Browse Source

Merge pull request #527 from mrfelton/feat/remember-wallet-address

Remember last unused wallet address
renovate/lint-staged-8.x
Ben Woosley 7 years ago
committed by GitHub
parent
commit
9d3c0b4668
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 78
      app/components/Onboarding/Syncing.js
  2. 2
      app/components/Wallet/ReceiveModal.js
  3. 7
      app/containers/Root.js
  4. 2
      app/lnd/methods/index.js
  5. 39
      app/reducers/address.js
  6. 29
      app/reducers/info.js
  7. 18
      app/reducers/lnd.js
  8. 14
      app/reducers/transaction.js
  9. 6
      app/routes/activity/containers/ActivityContainer.js
  10. 4
      app/routes/app/components/App.js
  11. 4
      app/routes/app/containers/AppContainer.js
  12. 3
      test/reducers/__snapshots__/info.spec.js.snap

78
app/components/Onboarding/Syncing.js

@ -9,23 +9,26 @@ import styles from './Syncing.scss'
class Syncing extends Component {
componentWillMount() {
const { fetchBlockHeight, blockHeight, newAddress } = this.props
const { fetchBlockHeight, blockHeight } = this.props
// If we don't already know the target block height, fetch it now.
if (!blockHeight) {
fetchBlockHeight()
}
newAddress('np2wkh')
}
render() {
const { syncPercentage, address, blockHeight, lndBlockHeight } = this.props
const { hasSynced, syncPercentage, address, blockHeight, lndBlockHeight } = this.props
const copyClicked = () => {
copy(address)
showNotification('Noice', 'Successfully copied to clipboard')
}
if (typeof hasSynced === 'undefined') {
return null
}
return (
<div className={styles.container}>
<div className={styles.titleBar} />
@ -35,32 +38,49 @@ class Syncing extends Component {
<Isvg className={styles.bitcoinLogo} src={zapLogo} />
</header>
<div className={styles.title}>
<h1>Fund your Zap wallet</h1>
<p>Might as well fund your wallet while you&apos;re waiting to sync.</p>
</div>
{address.length ? (
<div className={styles.address}>
<div className={styles.qrConatiner}>
<QRCode
value={address}
renderAs="svg"
size={100}
bgColor="transparent"
fgColor="white"
level="L"
className={styles.qrcode}
/>
{hasSynced === true && (
<div>
<div className={styles.title}>
<h1>Welcome back to your Zap wallet!</h1>
<p>
Please wait a while whilst we fetch all of your latest data from the blockchain.
</p>
</div>
<div className={styles.loading}>
<div className={styles.spinner} />
</div>
<section className={styles.textAddress}>
<span>{address}</span>
<span onClick={copyClicked}>copy</span>
</section>
</div>
) : (
<div className={styles.loading}>
<div className={styles.spinner} />
)}
{hasSynced === false && (
<div>
<div className={styles.title}>
<h1>Fund your Zap wallet</h1>
<p>Might as well fund your wallet while you&apos;re waiting to sync.</p>
</div>
{address && address.length ? (
<div className={styles.address}>
<div className={styles.qrConatiner}>
<QRCode
value={address}
renderAs="svg"
size={100}
bgColor="transparent"
fgColor="white"
level="L"
className={styles.qrcode}
/>
</div>
<section className={styles.textAddress}>
<span>{address}</span>
<span onClick={copyClicked}>copy</span>
</section>
</div>
) : (
<div className={styles.loading}>
<div className={styles.spinner} />
</div>
)}
</div>
)}
@ -93,8 +113,8 @@ class Syncing extends Component {
Syncing.propTypes = {
fetchBlockHeight: PropTypes.func.isRequired,
newAddress: PropTypes.func.isRequired,
address: PropTypes.string.isRequired,
hasSynced: PropTypes.bool,
syncPercentage: PropTypes.number,
blockHeight: PropTypes.number,
lndBlockHeight: PropTypes.number

2
app/components/Wallet/ReceiveModal.js

@ -117,7 +117,7 @@ ReceiveModal.propTypes = {
}).isRequired,
isOpen: PropTypes.bool.isRequired,
pubkey: PropTypes.string,
address: PropTypes.string.isRequired,
address: PropTypes.string,
alias: PropTypes.string,
closeReceiveModal: PropTypes.func.isRequired
}

7
app/containers/Root.js

@ -32,7 +32,7 @@ import {
setReEnterSeedIndexes
} from '../reducers/onboarding'
import { fetchBlockHeight, lndSelectors } from '../reducers/lnd'
import { newAddress } from '../reducers/address'
import { walletAddress } from '../reducers/address'
import Routes from '../routes'
const mapDispatchToProps = {
@ -54,7 +54,7 @@ const mapDispatchToProps = {
unlockWallet,
setSignupCreate,
setSignupImport,
newAddress,
walletAddress,
updateReEnterSeedInput,
updateRecoverSeedInput,
setReEnterSeedIndexes,
@ -66,6 +66,7 @@ const mapStateToProps = state => ({
lnd: state.lnd,
onboarding: state.onboarding,
address: state.address,
info: state.info,
syncPercentage: lndSelectors.syncPercentage(state),
passwordIsValid: onboardingSelectors.passwordIsValid(state),
@ -84,7 +85,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
fetchBlockHeight: dispatchProps.fetchBlockHeight,
blockHeight: stateProps.lnd.blockHeight,
lndBlockHeight: stateProps.lnd.lndBlockHeight,
newAddress: dispatchProps.newAddress,
hasSynced: stateProps.info.hasSynced,
syncPercentage: stateProps.syncPercentage,
address: stateProps.address.address
}

2
app/lnd/methods/index.js

@ -60,7 +60,7 @@ export default function(lnd, log, event, msg, data) {
// Data looks like { address: '' }
walletController
.newAddress(lnd, data.type)
.then(({ address }) => event.sender.send('receiveAddress', address))
.then(({ address }) => event.sender.send('receiveAddress', { type: data.type, address }))
.catch(error => log.error('newaddress:', error))
break
case 'setAlias':

39
app/reducers/address.js

@ -1,4 +1,6 @@
import { ipcRenderer } from 'electron'
import Store from 'electron-store'
// ------------------------------------
// Constants
// ------------------------------------
@ -36,15 +38,46 @@ export function closeWalletModal() {
}
}
// Get our existing address if there is one, otherwise generate a new one.
export const walletAddress = type => (dispatch, getState) => {
let address
// Wallet addresses are keyed under the node pubKey in our store.
const state = getState()
const pubKey = state.info.data.identity_pubkey
if (pubKey) {
const store = new Store({ name: 'wallet' })
address = store.get(`${pubKey}.${type}`, null)
}
// If we have an address already, use that. Otherwise, generate a new address.
if (address) {
dispatch({ type: RECEIVE_ADDRESS, address })
} else {
dispatch(newAddress(type))
}
}
// Send IPC event for getinfo
export const newAddress = type => async dispatch => {
export const newAddress = type => dispatch => {
dispatch(getAddress())
ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } })
}
// Receive IPC event for info
export const receiveAddress = (event, address) => dispatch =>
dispatch({ type: RECEIVE_ADDRESS, address })
export const receiveAddress = (event, data) => (dispatch, getState) => {
const state = getState()
const pubKey = state.info.data.identity_pubkey
// If we know the node's public key, store the address for reuse.
if (pubKey) {
const type = Object.keys(addressTypes).find(key => addressTypes[key] === data.type)
const store = new Store({ name: 'wallet' })
store.set(`${pubKey}.${type}`, data.address)
}
dispatch({ type: RECEIVE_ADDRESS, address: data.address })
}
// ------------------------------------
// Action Handlers

29
app/reducers/info.js

@ -1,6 +1,8 @@
import Store from 'electron-store'
import bitcoin from 'bitcoinjs-lib'
import { ipcRenderer } from 'electron'
import { walletAddress } from './address'
// ------------------------------------
// Constants
@ -8,6 +10,7 @@ import { ipcRenderer } from 'electron'
export const GET_INFO = 'GET_INFO'
export const RECEIVE_INFO = 'RECEIVE_INFO'
export const SET_WALLET_CURRENCY_FILTERS = 'SET_WALLET_CURRENCY_FILTERS'
export const SET_HAS_SYNCED = 'SET_HAS_SYNCED'
// ------------------------------------
// Actions
@ -25,6 +28,13 @@ export function setWalletCurrencyFilters(showWalletCurrencyFilters) {
}
}
export const setHasSynced = hasSynced => {
return {
type: SET_HAS_SYNCED,
hasSynced
}
}
// Send IPC event for getinfo
export const fetchInfo = () => async dispatch => {
dispatch(getInfo())
@ -32,8 +42,20 @@ export const fetchInfo = () => async dispatch => {
}
// Receive IPC event for info
export const receiveInfo = (event, data) => dispatch => {
export const receiveInfo = (event, data) => (dispatch, getState) => {
dispatch({ type: RECEIVE_INFO, data })
// Now that we have the node info, get the current wallet address.
dispatch(walletAddress('np2wkh'))
// Determine the node's current sync state.
const state = getState()
if (typeof state.info.hasSynced === 'undefined') {
const store = new Store({ name: 'wallet' })
const hasSynced = store.get(`${data.identity_pubkey}.hasSynced`, false)
store.set(`${data.identity_pubkey}.hasSynced`, hasSynced)
dispatch(setHasSynced(hasSynced))
}
}
const networks = {
@ -57,6 +79,10 @@ const networks = {
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[SET_HAS_SYNCED]: (state, { hasSynced }) => ({
...state,
hasSynced
}),
[GET_INFO]: state => ({ ...state, infoLoading: true }),
[RECEIVE_INFO]: (state, { data }) => ({
...state,
@ -75,6 +101,7 @@ const ACTION_HANDLERS = {
// ------------------------------------
const initialState = {
infoLoading: false,
hasSynced: undefined,
network: {},
data: {},
showWalletCurrencyFilters: false

18
app/reducers/lnd.js

@ -1,7 +1,8 @@
import Store from 'electron-store'
import { createSelector } from 'reselect'
import { fetchTicker } from './ticker'
import { fetchBalance } from './balance'
import { fetchInfo } from './info'
import { fetchInfo, setHasSynced } from './info'
import { requestBlockHeight } from '../api'
import { showNotification } from '../notifications'
// ------------------------------------
@ -25,14 +26,23 @@ export const GRPC_CONNECTED = 'GRPC_CONNECTED'
export const lndSyncing = () => dispatch => dispatch({ type: START_SYNCING })
// Receive IPC event for LND stoping sync
export const lndSynced = () => dispatch => {
export const lndSynced = () => (dispatch, getState) => {
// Persist the fact that the wallet has been synced at least once.
const state = getState()
const pubKey = state.info.data.identity_pubkey
if (pubKey) {
const store = new Store({ name: 'wallet' })
store.set(`${pubKey}.hasSynced`, true)
}
dispatch({ type: STOP_SYNCING })
dispatch(setHasSynced(true))
// Fetch data now that we know LND is synced
dispatch(fetchTicker())
dispatch(fetchBalance())
dispatch(fetchInfo())
dispatch({ type: STOP_SYNCING })
// HTML 5 desktop notification for the new transaction
const notifTitle = 'Lightning Node Synced'
const notifBody = "Visa who? You're your own payment processor now!"

14
app/reducers/transaction.js

@ -58,9 +58,21 @@ export const fetchTransactions = () => dispatch => {
}
// Receive IPC event for payments
export const receiveTransactions = (event, { transactions }) => dispatch =>
export const receiveTransactions = (event, { transactions }) => (dispatch, getState) => {
dispatch({ type: RECEIVE_TRANSACTIONS, transactions })
// If our current wallet address has been used, generate a new one.
const state = getState()
const currentAddress = state.address.address
let usedAddresses = []
transactions.forEach(transaction => {
usedAddresses = usedAddresses.concat(transaction.dest_addresses)
})
if (usedAddresses.includes(currentAddress)) {
dispatch(newAddress('np2wkh'))
}
}
export const sendCoins = ({ value, addr, currency }) => dispatch => {
// backend needs amount in satoshis no matter what currency we are using
const amount = btc.convert(currency, 'sats', value)

6
app/routes/activity/containers/ActivityContainer.js

@ -13,7 +13,7 @@ import {
updateSearchActive,
updateSearchText
} from 'reducers/activity'
import { newAddress, openWalletModal } from 'reducers/address'
import { walletAddress, openWalletModal } from 'reducers/address'
import { setFormType } from 'reducers/form'
import { payFormSelectors } from 'reducers/payform'
@ -33,7 +33,7 @@ const mapDispatchToProps = {
hideActivityModal,
changeFilter,
toggleFilterPulldown,
newAddress,
walletAddress,
openWalletModal,
fetchBalance,
updateSearchActive,
@ -93,7 +93,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
setCurrency: dispatchProps.setCurrency,
setWalletCurrencyFilters: dispatchProps.setWalletCurrencyFilters,
newAddress: dispatchProps.newAddress,
walletAddress: dispatchProps.walletAddress,
openReceiveModal: dispatchProps.openWalletModal,
openPayForm: () => dispatchProps.setFormType('PAY_FORM'),
openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM')

4
app/routes/app/components/App.js

@ -21,7 +21,6 @@ class App extends Component {
const {
fetchTicker,
fetchInfo,
newAddress,
fetchChannels,
fetchSuggestedNodes,
fetchBalance,
@ -32,8 +31,6 @@ class App extends Component {
fetchTicker()
// fetch node info
fetchInfo()
// fetch new address for wallet
newAddress('np2wkh')
// fetch nodes channels
fetchChannels()
// fetch suggested nodes list from zap.jackmallers.com/suggested-peers
@ -108,7 +105,6 @@ App.propTypes = {
receiveModalProps: PropTypes.object,
channelFormProps: PropTypes.object,
newAddress: PropTypes.func.isRequired,
fetchInfo: PropTypes.func.isRequired,
fetchTicker: PropTypes.func.isRequired,
clearError: PropTypes.func.isRequired,

4
app/routes/app/containers/AppContainer.js

@ -5,7 +5,7 @@ import { btc } from 'utils'
import { fetchTicker, setCurrency, tickerSelectors } from 'reducers/ticker'
import { newAddress, closeWalletModal } from 'reducers/address'
import { closeWalletModal } from 'reducers/address'
import { fetchInfo } from 'reducers/info'
@ -79,7 +79,6 @@ const mapDispatchToProps = {
fetchTicker,
setCurrency,
newAddress,
closeWalletModal,
fetchInfo,
@ -396,7 +395,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
pubkey: stateProps.info.data.identity_pubkey,
address: stateProps.address.address,
alias: stateProps.info.data.alias,
newAddress: dispatchProps.newAddress,
closeReceiveModal: dispatchProps.closeWalletModal
}

3
test/reducers/__snapshots__/info.spec.js.snap

@ -3,6 +3,7 @@
exports[`reducers infoReducer should correctly getInfo 1`] = `
Object {
"data": Object {},
"hasSynced": undefined,
"infoLoading": true,
"network": Object {},
"showWalletCurrencyFilters": false,
@ -12,6 +13,7 @@ Object {
exports[`reducers infoReducer should correctly receiveInfo 1`] = `
Object {
"data": "foo",
"hasSynced": undefined,
"infoLoading": false,
"network": Object {
"bitcoinJsNetwork": Object {
@ -37,6 +39,7 @@ Object {
exports[`reducers infoReducer should handle initial state 1`] = `
Object {
"data": Object {},
"hasSynced": undefined,
"infoLoading": false,
"network": Object {},
"showWalletCurrencyFilters": false,

Loading…
Cancel
Save