diff --git a/app/components/Onboarding/InitWallet.js b/app/components/Onboarding/InitWallet.js index 5069d0f6..ed3b80ce 100644 --- a/app/components/Onboarding/InitWallet.js +++ b/app/components/Onboarding/InitWallet.js @@ -4,28 +4,13 @@ import Login from './Login' import Signup from './Signup' import styles from './InitWallet.scss' -const InitWallet = ({ - password, - passwordIsValid, - hasSeed, - updatePassword, - createWallet, - unlockWallet, - unlockingWallet, - unlockWalletError -}) => ( +const InitWallet = ({ hasSeed, loginProps, signupProps }) => (
{ hasSeed ? - + : - + }
) diff --git a/app/components/Onboarding/NewAezeedPassword.js b/app/components/Onboarding/NewAezeedPassword.js new file mode 100644 index 00000000..7c796173 --- /dev/null +++ b/app/components/Onboarding/NewAezeedPassword.js @@ -0,0 +1,49 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Isvg from 'react-inlinesvg' +import eye from 'icons/eye.svg' +import styles from './NewAezeedPassword.scss' + +class NewAezeedPassword extends React.Component { + constructor(props) { + super(props) + this.state = { confirmPassword: '' } + } + + render() { + const { aezeedPassword, updateAezeedPassword } = this.props + const { confirmPassword } = this.state + + return ( +
+
+ updateAezeedPassword(event.target.value)} + /> +
+ +
+ this.setState({ confirmPassword: event.target.value })} + /> +
+
+ ) + } +} + +NewAezeedPassword.propTypes = { + aezeedPassword: PropTypes.string.isRequired, + updateAezeedPassword: PropTypes.func.isRequired +} + +export default NewAezeedPassword + diff --git a/app/components/Onboarding/NewAezeedPassword.scss b/app/components/Onboarding/NewAezeedPassword.scss new file mode 100644 index 00000000..1fd5fd6f --- /dev/null +++ b/app/components/Onboarding/NewAezeedPassword.scss @@ -0,0 +1,24 @@ +@import '../../variables.scss'; + +.input:nth-child(2) { + margin-top: 40px; +} + +.password { + background: transparent; + outline: none; + border: 0; + color: $gold; + -webkit-text-fill-color: $white; + font-size: 22px; + transition: all 0.25s; + + &.error { + border-bottom: 1px solid $red; + } +} + +.password::-webkit-input-placeholder { + text-shadow: none; + -webkit-text-fill-color: initial; +} diff --git a/app/components/Onboarding/NewWalletSeed.js b/app/components/Onboarding/NewWalletSeed.js index 82dc5b8a..b04207f7 100644 --- a/app/components/Onboarding/NewWalletSeed.js +++ b/app/components/Onboarding/NewWalletSeed.js @@ -4,12 +4,22 @@ import styles from './NewWalletSeed.scss' const NewWalletSeed = ({ seed }) => (
- { - seed.length > 0 ? - seed.join(', ') - : - 'loading' - } +
) diff --git a/app/components/Onboarding/NewWalletSeed.scss b/app/components/Onboarding/NewWalletSeed.scss index ed6e62f1..cc5b95b8 100644 --- a/app/components/Onboarding/NewWalletSeed.scss +++ b/app/components/Onboarding/NewWalletSeed.scss @@ -1,11 +1,49 @@ @import '../../variables.scss'; .container { - background: darken(#242833, 10%); - padding: 20px 40px; font-size: 14px; - line-height: 50px; color: $white; font-family: 'Roboto'; letter-spacing: 1.5px; + + li { + display: inline-block; + margin: 5px 0; + width: 25%; + font-family: 'Courier'; + + section { + display: inline-block; + vertical-align: middle; + color: $white; + + &:nth-child(1) { + width: 15%; + text-align: center; + opacity: 0.5; + } + + &:nth-child(2) { + width: calc(85% - 10px); + margin: 0 5px; + } + } + } + + .word { + margin: 0 3px; + background-color: #1c1e26; + outline: 0; + border: none; + padding: 10px; + color: $white; + + &.valid { + color: $green; + } + + &.invalid { + color: $red; + } + } } \ No newline at end of file diff --git a/app/components/Onboarding/Onboarding.js b/app/components/Onboarding/Onboarding.js index 20f08dde..1bb9e54b 100644 --- a/app/components/Onboarding/Onboarding.js +++ b/app/components/Onboarding/Onboarding.js @@ -7,8 +7,12 @@ import FormContainer from './FormContainer' import Alias from './Alias' import Autopilot from './Autopilot' import InitWallet from './InitWallet' +import Login from './Login' +import Signup from './Signup' import NewWalletSeed from './NewWalletSeed' +import ReEnterSeed from './ReEnterSeed' import NewWalletPassword from './NewWalletPassword' +import NewAezeedPassword from './NewAezeedPassword' import styles from './Onboarding.scss' const Onboarding = ({ @@ -18,7 +22,8 @@ const Onboarding = ({ autopilot, startingLnd, createWalletPassword, - seed + seed, + aezeedPassword }, changeStep, startLnd, @@ -27,7 +32,9 @@ const Onboarding = ({ initWalletProps, autopilotProps, newWalletSeedProps, - newWalletPasswordProps + newWalletPasswordProps, + newAezeedPasswordProps, + reEnterSeedProps }) => { const renderStep = () => { switch (step) { @@ -56,34 +63,67 @@ const Onboarding = ({ case 3: return ( changeStep(2)} + back={null} next={null} > - + ) case 4: return ( changeStep(3)} + title='Welcome!' + description='Looks like you are new here. Set a password to encrypt your wallet. This password will be needed to unlock Zap in the future' // eslint-disable-line + back={null} next={() => changeStep(5)} > - + ) case 5: return ( changeStep(4)} - next={() => submitNewWallet(createWalletPassword, seed)} + next={() => initWalletProps.signupProps.signupForm.create ? changeStep(6) : console.log('import')} > - + + + ) + case 6: + return ( + changeStep(5)} + next={() => changeStep(7)} + > + + + ) + case 7: + return ( + changeStep(6)} + next={() => changeStep(8)} + > + + + ) + case 8: + return ( + changeStep(6)} + next={() => submitNewWallet(createWalletPassword, seed, aezeedPassword)} + > + ) default: diff --git a/app/components/Onboarding/ReEnterSeed.js b/app/components/Onboarding/ReEnterSeed.js new file mode 100644 index 00000000..aab83657 --- /dev/null +++ b/app/components/Onboarding/ReEnterSeed.js @@ -0,0 +1,41 @@ +import React from 'react' +import PropTypes from 'prop-types' +import styles from './ReEnterSeed.scss' + +const ReEnterSeed = ({ seed, seedInput, updateSeedInput, reEnterSeedChecker, renderEnterSeedHtml }) => { + return ( +
+ +
+ ) +} + +ReEnterSeed.propTypes = { + seedInput: PropTypes.array.isRequired, + updateSeedInput: PropTypes.func.isRequired, + reEnterSeedChecker: PropTypes.array.isRequired +} + +export default ReEnterSeed diff --git a/app/components/Onboarding/ReEnterSeed.scss b/app/components/Onboarding/ReEnterSeed.scss new file mode 100644 index 00000000..4c794ac5 --- /dev/null +++ b/app/components/Onboarding/ReEnterSeed.scss @@ -0,0 +1,61 @@ +@import '../../variables.scss'; + +.seedContainer { + position: relative; + display: inline-block; + font-size: 12px; + + li { + display: inline-block; + margin: 5px 0; + width: 25%; + + section { + display: inline-block; + vertical-align: middle; + color: $white; + + &:nth-child(1) { + width: 15%; + text-align: center; + opacity: 0.5; + } + + &:nth-child(2) { + width: calc(85% - 10px); + margin: 0 5px; + } + } + } +} + + + +.word { + margin: 0 3px; + background-color: #1c1e26; + outline: 0; + border: none; + padding: 5px 10px; + color: $white; + font-family: courier; + font-family: 'Courier'; + + &.valid { + color: $green; + } + + &.invalid { + color: $red; + } +} + +.word::-webkit-input-placeholder { + text-shadow: none; + -webkit-text-fill-color: initial; +} + +.contentEditable { + width: 100px; + background: red; +} diff --git a/app/components/Onboarding/Signup.js b/app/components/Onboarding/Signup.js index 8ef1f5dd..3e28e6fd 100644 --- a/app/components/Onboarding/Signup.js +++ b/app/components/Onboarding/Signup.js @@ -1,16 +1,36 @@ import React from 'react' import PropTypes from 'prop-types' +import { FaCircle, FaCircleThin } from 'react-icons/lib/fa' import styles from './Signup.scss' -const Signup = ({ - password, - passwordIsValid, - hasSeed, - updatePassword, - createWallet -}) => ( +const Signup = ({ signupForm, setSignupCreate, setSignupImport }) => (
- signup yo +
+
+ { + signupForm.create ? + + : + + } + + Create new wallet + +
+
+
+
+ { + signupForm.import ? + + : + + } + + Import existing wallet + +
+
) diff --git a/app/components/Onboarding/Signup.scss b/app/components/Onboarding/Signup.scss index e69de29b..18d543b8 100644 --- a/app/components/Onboarding/Signup.scss +++ b/app/components/Onboarding/Signup.scss @@ -0,0 +1,52 @@ +@import '../../variables.scss'; + +.container { + color: $white; + + section { + margin: 20px 0; + + &.enable { + &.active { + div { + color: $green; + border-color: $green; + } + } + + div:hover { + color: $green; + border-color: $green; + } + } + + &.disable { + &.active { + div { + color: $red; + border-color: $red; + } + } + + div:hover { + color: $red; + border-color: $red; + } + } + + div { + width: 200px; + display: inline-block; + padding: 20px; + border: 1px solid $white; + border-radius: 5px; + font-weight: 200; + cursor: pointer; + transition: all 0.25s; + } + + .label { + margin-left: 15px; + } + } +} \ No newline at end of file diff --git a/app/containers/Root.js b/app/containers/Root.js index b6d2aad3..cf6530a6 100644 --- a/app/containers/Root.js +++ b/app/containers/Root.js @@ -15,9 +15,13 @@ import { startLnd, createWallet, updateCreateWalletPassword, + updateAezeedPassword, submitNewWallet, onboardingSelectors, - unlockWallet + unlockWallet, + setSignupCreate, + setSignupImport, + updateSeedInput } from '../reducers/onboarding' import { fetchBlockHeight, lndSelectors } from '../reducers/lnd' import Routes from '../routes' @@ -26,12 +30,16 @@ const mapDispatchToProps = { updateAlias, updatePassword, updateCreateWalletPassword, + updateAezeedPassword, setAutopilot, changeStep, startLnd, createWallet, submitNewWallet, unlockWallet, + setSignupCreate, + setSignupImport, + updateSeedInput, fetchBlockHeight } @@ -41,7 +49,9 @@ const mapStateToProps = state => ({ onboarding: state.onboarding, syncPercentage: lndSelectors.syncPercentage(state), - passwordIsValid: onboardingSelectors.passwordIsValid(state) + passwordIsValid: onboardingSelectors.passwordIsValid(state), + reEnterSeedChecker: onboardingSelectors.reEnterSeedChecker(state), + renderEnterSeedHtml: onboardingSelectors.renderEnterSeedHtml(state) }) const mergeProps = (stateProps, dispatchProps, ownProps) => { @@ -61,15 +71,26 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { } const initWalletProps = { - password: stateProps.onboarding.password, - passwordIsValid: stateProps.passwordIsValid, hasSeed: stateProps.onboarding.hasSeed, - unlockingWallet: stateProps.onboarding.unlockingWallet, - unlockWalletError: stateProps.onboarding.unlockWalletError, - updatePassword: dispatchProps.updatePassword, - createWallet: dispatchProps.createWallet, - unlockWallet: dispatchProps.unlockWallet + loginProps: { + password: stateProps.onboarding.password, + passwordIsValid: stateProps.passwordIsValid, + hasSeed: stateProps.onboarding.hasSeed, + unlockingWallet: stateProps.onboarding.unlockingWallet, + unlockWalletError: stateProps.onboarding.unlockWalletError, + + updatePassword: dispatchProps.updatePassword, + createWallet: dispatchProps.createWallet, + unlockWallet: dispatchProps.unlockWallet + }, + + signupProps: { + signupForm: stateProps.onboarding.signupForm, + + setSignupCreate: dispatchProps.setSignupCreate, + setSignupImport: dispatchProps.setSignupImport + } } const newWalletSeedProps = { @@ -81,6 +102,19 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { updateCreateWalletPassword: dispatchProps.updateCreateWalletPassword } + const newAezeedPasswordProps = { + aezeedPassword: stateProps.onboarding.aezeedPassword, + updateAezeedPassword: dispatchProps.updateAezeedPassword + } + + const reEnterSeedProps = { + seed: stateProps.onboarding.seed, + seedInput: stateProps.onboarding.seedInput, + reEnterSeedChecker: stateProps.reEnterSeedChecker, + renderEnterSeedHtml: stateProps.renderEnterSeedHtml, + updateSeedInput: dispatchProps.updateSeedInput + } + const onboardingProps = { onboarding: stateProps.onboarding, changeStep: dispatchProps.changeStep, @@ -90,7 +124,9 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { autopilotProps, initWalletProps, newWalletSeedProps, - newWalletPasswordProps + newWalletPasswordProps, + newAezeedPasswordProps, + reEnterSeedProps } return { diff --git a/app/lnd/methods/channelController.js b/app/lnd/methods/channelController.js index 5d841e97..9cb2c48f 100644 --- a/app/lnd/methods/channelController.js +++ b/app/lnd/methods/channelController.js @@ -1,10 +1,7 @@ -import bitcore from 'bitcore-lib' import find from 'lodash/find' import { listPeers, connectPeer } from './peersController' import pushopenchannel from '../push/openchannel' -const BufferUtil = bitcore.util.buffer - function ensurePeerConnected(lnd, meta, pubkey, host) { return listPeers(lnd, meta) .then(({ peers }) => { @@ -29,7 +26,7 @@ export function connectAndOpen(lnd, meta, event, payload) { return ensurePeerConnected(lnd, meta, pubkey, host) .then(() => { const call = lnd.openChannel({ - node_pubkey: BufferUtil.hexToBuffer(pubkey), + node_pubkey: Buffer.from(pubkey, 'hex'), local_funding_amount: Number(localamt) }, meta) @@ -54,7 +51,7 @@ export function connectAndOpen(lnd, meta, event, payload) { export function openChannel(lnd, meta, event, payload) { const { pubkey, localamt, pushamt } = payload const res = { - node_pubkey: BufferUtil.hexToBuffer(pubkey), + node_pubkey: Buffer.from(pubkey, 'hex'), local_funding_amount: Number(localamt), push_sat: Number(pushamt) } @@ -110,7 +107,7 @@ export function closeChannel(lnd, meta, event, payload) { const tx = payload.channel_point.funding_txid.match(/.{2}/g).reverse().join('') const res = { channel_point: { - funding_txid: BufferUtil.hexToBuffer(tx), + funding_txid: Buffer.from(tx, 'hex'), output_index: Number(payload.channel_point.output_index) }, force diff --git a/app/lnd/methods/walletController.js b/app/lnd/methods/walletController.js index 1dd84a74..934f0fe9 100644 --- a/app/lnd/methods/walletController.js +++ b/app/lnd/methods/walletController.js @@ -1,6 +1,3 @@ -import bitcore from 'bitcore-lib' -const BufferUtil = bitcore.util.buffer - /** * Returns the sum of all confirmed unspent outputs under control by the wallet * @param {[type]} lnd [description] @@ -129,9 +126,13 @@ export function unlockWallet(walletUnlocker, { wallet_password }) { * @param {[type]} password [description] * @param {[type]} cipher_seed_mnemonic [description] */ -export function initWallet(walletUnlocker, { wallet_password, cipher_seed_mnemonic }) { +export function initWallet(walletUnlocker, { wallet_password, cipher_seed_mnemonic, aezeed_passphrase }) { return new Promise((resolve, reject) => { - walletUnlocker.initWallet({ wallet_password, cipher_seed_mnemonic }, (err, data) => { + walletUnlocker.initWallet({ + wallet_password, + cipher_seed_mnemonic, + aezeed_passphrase: Buffer.from(aezeed_passphrase, 'hex') + }, (err, data) => { if (err) { reject(err) } resolve(data) diff --git a/app/reducers/onboarding.js b/app/reducers/onboarding.js index e967a21b..c628bf90 100644 --- a/app/reducers/onboarding.js +++ b/app/reducers/onboarding.js @@ -7,6 +7,8 @@ import { ipcRenderer } from 'electron' export const UPDATE_ALIAS = 'UPDATE_ALIAS' export const UPDATE_PASSWORD = 'UPDATE_PASSWORD' export const UPDATE_CREATE_WALLET_PASSWORD = 'UPDATE_CREATE_WALLET_PASSWORD' +export const UPDATE_AEZEED_PASSWORD = 'UPDATE_AEZEED_PASSWORD' +export const UPDATE_SEED_INPUT = 'UPDATE_SEED_INPUT' export const CHANGE_STEP = 'CHANGE_STEP' @@ -27,6 +29,9 @@ export const CREATING_NEW_WALLET = 'CREATING_NEW_WALLET' export const UNLOCKING_WALLET = 'UNLOCKING_WALLET' export const WALLET_UNLOCKED = 'WALLET_UNLOCKED' export const SET_UNLOCK_WALLET_ERROR = 'SET_UNLOCK_WALLET_ERROR' + +export const SET_SIGNUP_CREATE = 'SET_SIGNUP_CREATE' +export const SET_SIGNUP_IMPORT = 'SET_SIGNUP_IMPORT' // ------------------------------------ // Actions // ------------------------------------ @@ -51,6 +56,20 @@ export function updateCreateWalletPassword(createWalletPassword) { } } +export function updateAezeedPassword(aezeedPassword) { + return { + type: UPDATE_AEZEED_PASSWORD, + aezeedPassword + } +} + +export function updateSeedInput(inputSeedObj) { + return { + type: UPDATE_SEED_INPUT, + inputSeedObj + } +} + export function setAutopilot(autopilot) { return { type: SET_AUTOPILOT, @@ -58,6 +77,18 @@ export function setAutopilot(autopilot) { } } +export function setSignupCreate() { + return { + type: SET_SIGNUP_CREATE + } +} + +export function setSignupImport() { + return { + type: SET_SIGNUP_IMPORT + } +} + export function changeStep(step) { return { type: CHANGE_STEP, @@ -74,9 +105,9 @@ export function startLnd(alias, autopilot) { } } -export function submitNewWallet(wallet_password, cipher_seed_mnemonic) { +export const submitNewWallet = (wallet_password, cipher_seed_mnemonic, aezeed_passphrase) => (dispatch) => { // once the user submits the data needed to start LND we will alert the app that it should start LND - ipcRenderer.send('walletUnlocker', { msg: 'initWallet', data: { wallet_password, cipher_seed_mnemonic } }) + ipcRenderer.send('walletUnlocker', { msg: 'initWallet', data: { wallet_password, cipher_seed_mnemonic, aezeed_passphrase } }) dispatch({ type: CREATING_NEW_WALLET }) } @@ -87,7 +118,6 @@ export const startOnboarding = () => (dispatch) => { // Listener from after the LND walletUnlocker has started export const walletUnlockerStarted = () => (dispatch) => { dispatch({ type: LND_STARTED }) - dispatch({ type: CHANGE_STEP, step: 3 }) ipcRenderer.send('walletUnlocker', { msg: 'genSeed' }) } @@ -99,10 +129,18 @@ export const createWallet = () => (dispatch) => { export const successfullyCreatedWallet = (event) => (dispatch) => dispatch({ type: ONBOARDING_FINISHED }) // Listener for when LND creates and sends us a generated seed -export const receiveSeed = (event, { cipher_seed_mnemonic }) => (dispatch) => dispatch({ type: SET_SEED, seed: cipher_seed_mnemonic }) +export const receiveSeed = (event, { cipher_seed_mnemonic }) => (dispatch) => { + dispatch({ type: SET_SEED, seed: cipher_seed_mnemonic }) + // there was no seed and we just generated a new one, send user to the login component + dispatch({ type: CHANGE_STEP, step: 4 }) +} // Listener for when LND throws an error on seed creation -export const receiveSeedError = (event, error) => (dispatch) => dispatch({ type: SET_HAS_SEED, hasSeed: true }) +export const receiveSeedError = (event, error) => (dispatch) => { + dispatch({ type: SET_HAS_SEED, hasSeed: true }) + // there is already a seed, send user to the login component + dispatch({ type: CHANGE_STEP, step: 3 }) +} // Unlock an existing wallet with a wallet password export const unlockWallet = (wallet_password) => (dispatch) => { @@ -126,6 +164,13 @@ const ACTION_HANDLERS = { [UPDATE_ALIAS]: (state, { alias }) => ({ ...state, alias }), [UPDATE_PASSWORD]: (state, { password }) => ({ ...state, password }), [UPDATE_CREATE_WALLET_PASSWORD]: (state, { createWalletPassword }) => ({ ...state, createWalletPassword }), + [UPDATE_AEZEED_PASSWORD]: (state, { aezeedPassword }) => ({ ...state, aezeedPassword }), + [UPDATE_SEED_INPUT]: (state, { inputSeedObj }) => { + return { + ...state, + seedInput: Object.assign([], state.seedInput, { [inputSeedObj['index']]: inputSeedObj }) + } + }, [SET_AUTOPILOT]: (state, { autopilot }) => ({ ...state, autopilot }), @@ -144,17 +189,46 @@ const ACTION_HANDLERS = { [UNLOCKING_WALLET]: state => ({ ...state, unlockingWallet: true }), [WALLET_UNLOCKED]: state => ({ ...state, unlockingWallet: false, unlockWalletError: { isError: false, message: '' } }), - [SET_UNLOCK_WALLET_ERROR]: state => ({ ...state, unlockingWallet: false, unlockWalletError: { isError: true, message: 'Incorrect password' } }) + [SET_UNLOCK_WALLET_ERROR]: state => ({ ...state, unlockingWallet: false, unlockWalletError: { isError: true, message: 'Incorrect password' } }), + + [SET_SIGNUP_CREATE]: state => ({ ...state, signupForm: { create: true, import: false } }), + [SET_SIGNUP_IMPORT]: state => ({ ...state, signupForm: { create: false, import: true } }) } const onboardingSelectors = {} const passwordSelector = state => state.onboarding.password +const seedSelector = state => state.onboarding.seed +const seedInputSelector = state => state.onboarding.seedInput onboardingSelectors.passwordIsValid = createSelector( passwordSelector, password => password.length >= 8 ) +onboardingSelectors.reEnterSeedChecker = createSelector( + seedSelector, + seedInputSelector, + (seed, seedInput) => { + // console.log('seedInput: ', seedInput) + + // const seedInputArr = seedInput.split(' ').filter(n => true && n.length) + + // console.log('seedInputArr: ', seedInputArr) + + // return seedInputArr.map((word, index) => { return { valid: word === seed[index], word } }) + } +) + +onboardingSelectors.renderEnterSeedHtml = createSelector( + onboardingSelectors.reEnterSeedChecker, + (reEnterSeedChecker) => { + // console.log('reEnterSeedChecker: ', reEnterSeedChecker) + // if (!reEnterSeedChecker.length) { return 'gang' } + + // return reEnterSeedChecker.map( ({ valid, word }) => (`${word}`) ).join('') + } +) + export { onboardingSelectors } // ------------------------------------ @@ -171,15 +245,27 @@ const initialState = { hasSeed: false, seed: [], + // wallet password. password used to encrypt the wallet and is required to unlock the daemon after set createWalletPassword: '', creatingNewWallet: false, + // seed password. this is optional and used to encrypt the seed + aezeedPassword: '', + unlockingWallet: false, unlockWalletError: { isError: false, message: '' }, + // array of inputs for when the user re-enters their seed + seedInput: [], + // step where the user decides whether they want a newly created seed or to import an existing one + signupForm: { + create: false, + import: false + }, + autopilot: null }