JimmyMow
7 years ago
committed by
GitHub
19 changed files with 462 additions and 364 deletions
@ -1,83 +0,0 @@ |
|||
import React, { Component } from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import styles from './LndSyncing.scss' |
|||
|
|||
|
|||
class LndSyncing extends Component { |
|||
constructor(props) { |
|||
super(props) |
|||
this.state = { |
|||
facts: [ |
|||
{ |
|||
title: 'The Lightning Network', |
|||
description: 'The Lightning Network is a second layer solution built on top of the Bitcoin block chain that attempts to increase Bitcoin\'s scalability and privacy' // eslint-disable-line max-len
|
|||
}, |
|||
{ |
|||
title: 'Payment Channel', |
|||
description: 'A payment channel is a class of techniques designed to allow users to make multiple Bitcoin transactions without commiting all of the transactions to the Bitcoin block chain. You can think of payment channels like tubes of money' // eslint-disable-line max-len
|
|||
}, |
|||
{ |
|||
title: 'HTLC', |
|||
description: 'Hashed TimeLock Contracts is a class of payments that use hashlocks and timelocks to require the receiver of a payment either acknowledge receiving the payment before a deadline or forfeit the ability to claim the payment. HTLCs are useful within the Lightning Network for routing payments across two or more payment channels' // eslint-disable-line max-len
|
|||
}, |
|||
{ |
|||
title: 'Onion Routing', |
|||
description: 'Onion routing is a technique for anonymous communication over a computer network. In an onion network, messages are encapsulated in layers of encryption, analogous to layers of an onion.' // eslint-disable-line max-len
|
|||
} |
|||
], |
|||
currentFact: 0 |
|||
} |
|||
} |
|||
|
|||
componentWillMount() { |
|||
this.props.fetchBlockHeight() |
|||
} |
|||
|
|||
render() { |
|||
const { syncPercentage } = this.props |
|||
const { facts, currentFact } = this.state |
|||
const renderCurrentFact = facts[currentFact] |
|||
|
|||
return ( |
|||
<div className={styles.container}> |
|||
<header> |
|||
<section> |
|||
<h3>zap</h3> |
|||
</section> |
|||
<section className={`${styles.loading} hint--left`} data-hint='Syncing your Lightning Network node to the blockchain'> |
|||
<h4>{syncPercentage.toString().length > 0 && `${syncPercentage}%`}</h4> |
|||
<div className={styles.spinner} /> |
|||
</section> |
|||
</header> |
|||
|
|||
<div className={styles.facts}> |
|||
<div className={styles.fact}> |
|||
<h2>{renderCurrentFact.title}</h2> |
|||
<p>{renderCurrentFact.description}</p> |
|||
</div> |
|||
<ul className={styles.factButtons}> |
|||
{ |
|||
facts.map((fact, index) => ( |
|||
<li |
|||
className={`${styles.factButton} ${currentFact === index && styles.active}`} |
|||
key={index} |
|||
onClick={() => this.setState({ currentFact: index })} |
|||
/> |
|||
)) |
|||
} |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
LndSyncing.propTypes = { |
|||
fetchBlockHeight: PropTypes.func.isRequired, |
|||
syncPercentage: PropTypes.oneOfType([ |
|||
PropTypes.number, |
|||
PropTypes.string |
|||
]).isRequired |
|||
} |
|||
|
|||
export default LndSyncing |
@ -1,242 +0,0 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.container { |
|||
height: 100vh; |
|||
background: $secondary; |
|||
|
|||
header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: top; |
|||
padding: 100px; |
|||
} |
|||
|
|||
h3 { |
|||
font-size: 50px; |
|||
color: $white; |
|||
} |
|||
|
|||
.loading { |
|||
text-align: center; |
|||
position: relative; |
|||
|
|||
.spinner, h1 { |
|||
display: inline-block; |
|||
vertical-align: top; |
|||
} |
|||
|
|||
h4 { |
|||
position: absolute; |
|||
min-width: 100px; |
|||
top: calc(50% - 5px); |
|||
left: calc(50% - 48px); |
|||
color: $main; |
|||
font-size: 10px; |
|||
} |
|||
|
|||
h1 { |
|||
color: $main; |
|||
line-height: 50px; |
|||
margin-left: 20px; |
|||
} |
|||
} |
|||
|
|||
.facts { |
|||
color: $white; |
|||
|
|||
.fact { |
|||
transition: all 0.25s; |
|||
width: 50%; |
|||
margin: 0 auto; |
|||
text-align: center; |
|||
line-height: 1.5; |
|||
letter-spacing: 1.1px; |
|||
height: 250px; |
|||
|
|||
h2 { |
|||
font-size: 50px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
p { |
|||
margin-bottom: 20px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.factButtons { |
|||
text-align: center; |
|||
} |
|||
|
|||
.factButton { |
|||
cursor: pointer; |
|||
display: inline-block; |
|||
width: 15px; |
|||
height: 15px; |
|||
background: $white; |
|||
opacity: 0.5; |
|||
border-radius: 50%; |
|||
margin: 0 5px; |
|||
|
|||
&:hover { |
|||
opacity: 0.75; |
|||
} |
|||
|
|||
&.active { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.aliasForm { |
|||
width: 50%; |
|||
margin: 0 auto; |
|||
|
|||
h1 { |
|||
text-align: center; |
|||
font-size: 32px; |
|||
color: $white; |
|||
} |
|||
|
|||
p { |
|||
color: $darkgrey; |
|||
text-align: center; |
|||
margin-top: 20px; |
|||
font-weight: 100; |
|||
} |
|||
|
|||
.inputContainer { |
|||
text-align: center; |
|||
margin-top: 50px; |
|||
} |
|||
|
|||
.input { |
|||
padding: 20px; |
|||
font-size: 18px; |
|||
color: $darkestgrey; |
|||
background: lighten($black, 15%); |
|||
border: none; |
|||
outline: 0; |
|||
-webkit-appearance: none; |
|||
font-weight: 200; |
|||
width: calc(100% - 20px); |
|||
} |
|||
|
|||
.submit { |
|||
background: $main; |
|||
color: $white; |
|||
font-size: 18px; |
|||
cursor: pointer; |
|||
width: 10%; |
|||
margin: 50px auto 0 auto; |
|||
padding: 20px 60px; |
|||
opacity: 0.5; |
|||
} |
|||
} |
|||
|
|||
.footer { |
|||
position: absolute; |
|||
bottom: 0; |
|||
width: 100%; |
|||
background: darken($secondary, 5%); |
|||
white-space: nowrap; |
|||
|
|||
section { |
|||
display: inline-block; |
|||
vertical-align: top; |
|||
width: 50%; |
|||
white-space: normal; |
|||
padding: 30px 0; |
|||
|
|||
h2 { |
|||
color: $white; |
|||
font-size: 22px; |
|||
letter-spacing: 1.2px; |
|||
font-weight: bold; |
|||
margin-bottom: 20px; |
|||
padding: 0 30px; |
|||
} |
|||
|
|||
p { |
|||
color: $white; |
|||
padding: 0 30px; |
|||
line-height: 1.5; |
|||
} |
|||
} |
|||
|
|||
.address { |
|||
display: flex; |
|||
flex-direction: row; |
|||
font-family: 'Roboto'; |
|||
font-size: 14px; |
|||
font-weight: 200; |
|||
background: lighten($black, 15%); |
|||
color: $darkestgrey; |
|||
width: 75%; |
|||
margin: 0 auto; |
|||
|
|||
span { |
|||
padding: 20px; |
|||
} |
|||
|
|||
span:nth-child(1) { |
|||
flex: 9; |
|||
overflow-x: scroll; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
span:nth-child(2) { |
|||
background: $darkestgrey; |
|||
color: $white; |
|||
cursor: pointer; |
|||
transition: all 0.25s; |
|||
|
|||
&:hover { |
|||
background: $darkestgrey; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.spinner { |
|||
border: 1px solid rgba(235, 184, 100, 0.1); |
|||
border-left-color: rgba(235, 184, 100, 0.4); |
|||
-webkit-border-radius: 999px; |
|||
-moz-border-radius: 999px; |
|||
border-radius: 999px; |
|||
} |
|||
|
|||
.spinner { |
|||
margin: 0 auto; |
|||
height: 50px; |
|||
width: 50px; |
|||
-webkit-animation: animation-rotate 1000ms linear infinite; |
|||
-moz-animation: animation-rotate 1000ms linear infinite; |
|||
-o-animation: animation-rotate 1000ms linear infinite; |
|||
animation: animation-rotate 1000ms linear infinite; |
|||
} |
|||
|
|||
@-webkit-keyframes animation-rotate { |
|||
100% { |
|||
-webkit-transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
@-moz-keyframes animation-rotate { |
|||
100% { |
|||
-moz-transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
@-o-keyframes animation-rotate { |
|||
100% { |
|||
-o-transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
@keyframes animation-rotate { |
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
@ -1,3 +0,0 @@ |
|||
import LndSyncing from './LndSyncing' |
|||
|
|||
export default LndSyncing |
@ -0,0 +1,23 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import styles from './Alias.scss' |
|||
|
|||
const Alias = ({ alias, updateAlias }) => ( |
|||
<div className={styles.container}> |
|||
<input |
|||
type='text' |
|||
placeholder='Satoshi' |
|||
className={styles.alias} |
|||
ref={input => input && input.focus()} |
|||
value={alias} |
|||
onChange={event => updateAlias(event.target.value)} |
|||
/> |
|||
</div> |
|||
) |
|||
|
|||
Alias.propTypes = { |
|||
alias: PropTypes.string.isRequired, |
|||
updateAlias: PropTypes.func.isRequired |
|||
} |
|||
|
|||
export default Alias |
@ -0,0 +1,15 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.alias { |
|||
background: transparent; |
|||
outline: none; |
|||
border: 0; |
|||
color: $gold; |
|||
-webkit-text-fill-color: $white; |
|||
font-size: 22px; |
|||
} |
|||
|
|||
.alias::-webkit-input-placeholder { |
|||
text-shadow: none; |
|||
-webkit-text-fill-color: initial; |
|||
} |
@ -0,0 +1,55 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import Isvg from 'react-inlinesvg' |
|||
import zapLogo from 'icons/zap_logo.svg' |
|||
import styles from './FormContainer.scss' |
|||
|
|||
const FormContainer = ({ title, description, back, next, children }) => ( |
|||
<div className={styles.container}> |
|||
<div className={styles.titleBar} /> |
|||
|
|||
<header className={styles.header}> |
|||
<section> |
|||
<Isvg src={zapLogo} /> |
|||
</section> |
|||
<section /> |
|||
</header> |
|||
|
|||
<div className={styles.info}> |
|||
<h1>{title}</h1> |
|||
<p>{description}</p> |
|||
</div> |
|||
|
|||
<div className={styles.content}> |
|||
{children} |
|||
</div> |
|||
|
|||
<footer className={styles.footer}> |
|||
<div className={styles.buttonsContainer}> |
|||
<section> |
|||
{ |
|||
back && <div onClick={back}>Back</div> |
|||
} |
|||
</section> |
|||
<section> |
|||
{ |
|||
next && <div onClick={next}>Next</div> |
|||
} |
|||
</section> |
|||
</div> |
|||
</footer> |
|||
</div> |
|||
) |
|||
|
|||
|
|||
FormContainer.propTypes = { |
|||
title: PropTypes.string.isRequired, |
|||
description: PropTypes.string.isRequired, |
|||
|
|||
back: PropTypes.func, |
|||
next: PropTypes.func, |
|||
|
|||
children: PropTypes.object.isRequired |
|||
} |
|||
|
|||
export default FormContainer |
@ -0,0 +1,67 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.container { |
|||
position: relative; |
|||
height: 100vh; |
|||
background: $darkspaceblue; |
|||
} |
|||
|
|||
.titleBar { |
|||
background: $spacegrey; |
|||
height: 20px; |
|||
-webkit-user-select: none; |
|||
-webkit-app-region: drag; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
padding: 20px 40px; |
|||
} |
|||
|
|||
.info { |
|||
color: $white; |
|||
margin: 20px 0 20px 0; |
|||
padding: 20px 40px; |
|||
|
|||
h1 { |
|||
font-size: 22px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
p { |
|||
font-size: 12px; |
|||
} |
|||
} |
|||
|
|||
.content { |
|||
position: relative; |
|||
background: $spaceblue; |
|||
height: 100vh; |
|||
padding: 60px 40px; |
|||
} |
|||
|
|||
.footer { |
|||
position: absolute; |
|||
bottom: 0; |
|||
padding: 20px 40px; |
|||
color: $white; |
|||
width: calc(100% - 80px); |
|||
|
|||
.buttonsContainer { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
|
|||
div { |
|||
cursor: pointer; |
|||
transition: all 0.25s; |
|||
|
|||
&:hover { |
|||
opacity: 0.5; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,49 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
|
|||
import LoadingBolt from 'components/LoadingBolt' |
|||
|
|||
import FormContainer from './FormContainer' |
|||
import Alias from './Alias' |
|||
import styles from './Onboarding.scss' |
|||
|
|||
const Onboarding = ({ |
|||
onboarding: { |
|||
step, |
|||
alias |
|||
}, |
|||
submit, |
|||
aliasProps |
|||
}) => { |
|||
const renderStep = () => { |
|||
switch (step) { |
|||
case 1: |
|||
return ( |
|||
<FormContainer |
|||
title={'1. What should we call you?'} |
|||
description={'Set your nickname to help others connect with you on the Lightning Network'} |
|||
back={null} |
|||
next={() => submit(alias)} |
|||
> |
|||
<Alias {...aliasProps} /> |
|||
</FormContainer> |
|||
) |
|||
default: |
|||
return <LoadingBolt /> |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div className={styles.container}> |
|||
{renderStep()} |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
Onboarding.propTypes = { |
|||
onboarding: PropTypes.object.isRequired, |
|||
aliasProps: PropTypes.object.isRequired, |
|||
submit: PropTypes.func.isRequired |
|||
} |
|||
|
|||
export default Onboarding |
@ -0,0 +1,45 @@ |
|||
import React, { Component } from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import Isvg from 'react-inlinesvg' |
|||
import zapLogo from 'icons/zap_logo.svg' |
|||
import styles from './Syncing.scss' |
|||
|
|||
|
|||
class Syncing extends Component { |
|||
componentWillMount() { |
|||
this.props.fetchBlockHeight() |
|||
} |
|||
|
|||
render() { |
|||
const { syncPercentage } = this.props |
|||
|
|||
return ( |
|||
<div className={styles.container}> |
|||
<div className={styles.titleBar} /> |
|||
|
|||
<div className={styles.content}> |
|||
<header> |
|||
<Isvg className={styles.bitcoinLogo} src={zapLogo} /> |
|||
</header> |
|||
<section className={styles.progressContainer}> |
|||
<h1>Syncing to the blockchain...</h1> |
|||
<div className={styles.progressBar}> |
|||
<div className={styles.progress} style={{ width: isNaN(syncPercentage) ? 0 : `${syncPercentage}%` }} /> |
|||
</div> |
|||
<h4>{isNaN(parseInt(syncPercentage, 10)) || syncPercentage.toString().length === 0 ? '' : `${syncPercentage}%`}</h4> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
Syncing.propTypes = { |
|||
fetchBlockHeight: PropTypes.func.isRequired, |
|||
syncPercentage: PropTypes.oneOfType([ |
|||
PropTypes.number, |
|||
PropTypes.string |
|||
]).isRequired |
|||
} |
|||
|
|||
export default Syncing |
@ -0,0 +1,50 @@ |
|||
@import '../../variables.scss'; |
|||
|
|||
.container { |
|||
position: relative; |
|||
height: 100vh; |
|||
background: $spaceblue; |
|||
} |
|||
|
|||
.titleBar { |
|||
background: $spacegrey; |
|||
height: 20px; |
|||
-webkit-user-select: none; |
|||
-webkit-app-region: drag; |
|||
} |
|||
|
|||
.content { |
|||
padding: 20px 40px; |
|||
} |
|||
|
|||
.progressContainer { |
|||
color: $white; |
|||
text-align: center; |
|||
margin-top: 20%; |
|||
|
|||
h1 { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.progressBar { |
|||
width: 75%; |
|||
max-width: 700px; |
|||
margin: 0 auto; |
|||
height: 10px; |
|||
border-radius: 5px; |
|||
background: $spaceborder; |
|||
} |
|||
|
|||
.progress { |
|||
background: $gold; |
|||
background: #DEA326; |
|||
height: 10px; |
|||
border-radius: 5px; |
|||
transition: all 0.25s; |
|||
} |
|||
|
|||
h4 { |
|||
color: $gold; |
|||
margin-top: 10px; |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
import Onboarding from './Onboarding' |
|||
|
|||
export default Onboarding |
@ -0,0 +1,69 @@ |
|||
import { ipcRenderer } from 'electron' |
|||
|
|||
// ------------------------------------
|
|||
// Constants
|
|||
// ------------------------------------
|
|||
export const UPDATE_ALIAS = 'UPDATE_ALIAS' |
|||
|
|||
export const CHANGE_STEP = 'CHANGE_STEP' |
|||
|
|||
export const ONBOARDING_STARTED = 'ONBOARDING_STARTED' |
|||
export const ONBOARDING_FINISHED = 'ONBOARDING_FINISHED' |
|||
|
|||
// ------------------------------------
|
|||
// Actions
|
|||
// ------------------------------------
|
|||
export function updateAlias(alias) { |
|||
return { |
|||
type: UPDATE_ALIAS, |
|||
alias |
|||
} |
|||
} |
|||
|
|||
export function changeStep(step) { |
|||
return { |
|||
type: CHANGE_STEP, |
|||
step |
|||
} |
|||
} |
|||
|
|||
export function submit(alias) { |
|||
// alert the app we're done onboarding and it's cool to start LND
|
|||
ipcRenderer.send('onboardingFinished', { alias }) |
|||
|
|||
return { |
|||
type: ONBOARDING_FINISHED |
|||
} |
|||
} |
|||
|
|||
export const startOnboarding = () => (dispatch) => { |
|||
dispatch({ type: ONBOARDING_STARTED }) |
|||
} |
|||
|
|||
// ------------------------------------
|
|||
// Action Handlers
|
|||
// ------------------------------------
|
|||
const ACTION_HANDLERS = { |
|||
[UPDATE_ALIAS]: (state, { alias }) => ({ ...state, alias }), |
|||
[CHANGE_STEP]: (state, { step }) => ({ ...state, step }), |
|||
[ONBOARDING_STARTED]: state => ({ ...state, onboarded: false }), |
|||
[ONBOARDING_FINISHED]: state => ({ ...state, onboarded: true }) |
|||
} |
|||
|
|||
// ------------------------------------
|
|||
// Reducer
|
|||
// ------------------------------------
|
|||
const initialState = { |
|||
onboarded: true, |
|||
step: 1, |
|||
alias: '' |
|||
} |
|||
|
|||
// ------------------------------------
|
|||
// Reducer
|
|||
// ------------------------------------
|
|||
export default function lndReducer(state = initialState, action) { |
|||
const handler = ACTION_HANDLERS[action.type] |
|||
|
|||
return handler ? handler(state, action) : state |
|||
} |
Loading…
Reference in new issue