Browse Source

Integrate core account sync (without ops) in the account import process

master
meriadec 7 years ago
parent
commit
8200523b5d
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 7
      src/actions/accounts.js
  2. 75
      src/components/modals/AddAccount/03-step-import.js
  3. 172
      src/components/modals/AddAccount/index.js
  4. 6
      src/internals/usb/wallet/index.js
  5. 2
      src/internals/usb/wallet/scanAccountsOnDevice.js
  6. 4
      src/reducers/accounts.js

7
src/actions/accounts.js

@ -43,16 +43,9 @@ export type AddAccount = Account => (Function, Function) => void
export const addAccount: AddAccount = payload => (dispatch, getState) => { export const addAccount: AddAccount = payload => (dispatch, getState) => {
const { const {
settings: { orderAccounts }, settings: { orderAccounts },
accounts,
} = getState() } = getState()
dispatch({ type: 'ADD_ACCOUNT', payload }) dispatch({ type: 'ADD_ACCOUNT', payload })
dispatch(updateOrderAccounts(orderAccounts)) dispatch(updateOrderAccounts(orderAccounts))
// Start sync accounts the first time you add an account
if (accounts.length === 0) {
const accounts = [payload]
startSyncAccounts(accounts)
}
} }
export type RemoveAccount = Account => { type: string, payload: Account } export type RemoveAccount = Account => { type: string, payload: Account }

75
src/components/modals/AddAccount/03-step-import.js

@ -25,74 +25,31 @@ const AccountItem = styled(AccountCard)`
` `
type Props = { type Props = {
accountsImport: Object, scannedAccounts: Account[],
archivedAccounts: Account[], selectedAccounts: Account[],
currency?: ?CryptoCurrency, existingAccounts: Account[],
importProgress: boolean, onToggleAccount: Function,
onSelectAccount?: Function,
selectedAccounts?: Array<number>,
} }
function StepImport(props: Props) { function StepImport(props: Props) {
const hasAccountsImports = Object.keys(props.accountsImport).length > 0 const { scannedAccounts, selectedAccounts, existingAccounts, onToggleAccount } = props
const unit = props.currency && props.currency.units[0]
return ( return (
<Box> <Box flow={4}>
{props.importProgress ? ( {scannedAccounts.map(account => {
<Box alignItems="center">In progress...</Box> const isSelected = selectedAccounts.find(a => a.id === account.id)
) : ( const isExisting = existingAccounts.find(a => a.id === account.id && a.archived === false)
hasAccountsImports && <Box mb={-2}>Accounts</Box>
)}
{hasAccountsImports && (
<AccountsContainer pt={5}>
{Object.keys(props.accountsImport).map(k => {
const a = props.accountsImport[k]
return ( return (
<AccountItemWrapper key={a.id}> <Box
<AccountItem bg="lightgrey"
selected={props.selectedAccounts && props.selectedAccounts.includes(a.id)} key={account.id}
onClick={props.onSelectAccount && props.onSelectAccount(a.id)} onClick={onToggleAccount && !isExisting ? () => onToggleAccount(account) : undefined}
account={{ >
...a, {isSelected && `[SELECTED]`} {isExisting && `[ALREADY IMPORTED]`} {account.name}
currencyId: props.currency && props.currency.id, </Box>
name: `Account ${a.accountIndex}`,
currency: props.currency,
unit,
}}
counterValue="USD"
daysCount={365}
/>
</AccountItemWrapper>
) )
})} })}
</AccountsContainer>
)}
{!props.importProgress &&
props.archivedAccounts.length > 0 && (
<Fragment>
<Box pb={3}>Archived accounts</Box>
<AccountsContainer>
{props.archivedAccounts.map(a => (
<AccountItemWrapper key={a.id}>
<AccountItem
selected={props.selectedAccounts && props.selectedAccounts.includes(a.id)}
onClick={props.onSelectAccount && props.onSelectAccount(a.id)}
account={a}
counterValue="USD"
daysCount={365}
/>
</AccountItemWrapper>
))}
</AccountsContainer>
</Fragment>
)}
</Box> </Box>
) )
} }
StepImport.defaultProps = {
onSelectAccount: undefined,
selectedAccounts: [],
}
export default StepImport export default StepImport

172
src/components/modals/AddAccount/index.js

@ -13,8 +13,14 @@ import type { Device, T } from 'types/common'
import { MODAL_ADD_ACCOUNT } from 'config/constants' import { MODAL_ADD_ACCOUNT } from 'config/constants'
import { closeModal } from 'reducers/modals' import { closeModal } from 'reducers/modals'
import { canCreateAccount, getAccounts, getArchivedAccounts } from 'reducers/accounts' import {
import { sendEvent } from 'renderer/events' canCreateAccount,
getAccounts,
getArchivedAccounts,
decodeAccount,
} from 'reducers/accounts'
import { runJob } from 'renderer/events'
import { addAccount, updateAccount } from 'actions/accounts' import { addAccount, updateAccount } from 'actions/accounts'
@ -35,7 +41,7 @@ const GET_STEPS = t => [
] ]
const mapStateToProps = state => ({ const mapStateToProps = state => ({
accounts: getAccounts(state), existingAccounts: getAccounts(state),
archivedAccounts: getArchivedAccounts(state), archivedAccounts: getArchivedAccounts(state),
canCreateAccount: canCreateAccount(state), canCreateAccount: canCreateAccount(state),
}) })
@ -47,7 +53,7 @@ const mapDispatchToProps = {
} }
type Props = { type Props = {
accounts: Account[], existingAccounts: Account[],
addAccount: Function, addAccount: Function,
archivedAccounts: Account[], archivedAccounts: Account[],
canCreateAccount: boolean, canCreateAccount: boolean,
@ -57,23 +63,29 @@ type Props = {
} }
type State = { type State = {
accountsImport: Object, stepIndex: number,
currency: ?CryptoCurrency, currency: ?CryptoCurrency,
deviceSelected: ?Device, deviceSelected: ?Device,
selectedAccounts: Account[],
scannedAccounts: Account[],
// TODO: what's that.
fetchingCounterValues: boolean, fetchingCounterValues: boolean,
selectedAccounts: Array<number>,
appStatus: ?string, appStatus: ?string,
stepIndex: number,
} }
const INITIAL_STATE = { const INITIAL_STATE = {
accountsImport: {}, stepIndex: 0,
currency: null, currency: null,
deviceSelected: null, deviceSelected: null,
fetchingCounterValues: false,
selectedAccounts: [], selectedAccounts: [],
scannedAccounts: [],
fetchingCounterValues: false,
appStatus: null, appStatus: null,
stepIndex: 0,
} }
class AddAccountModal extends PureComponent<Props, State> { class AddAccountModal extends PureComponent<Props, State> {
@ -88,29 +100,41 @@ class AddAccountModal extends PureComponent<Props, State> {
const { stepIndex: nextStepIndex } = nextState const { stepIndex: nextStepIndex } = nextState
if (!fetchingCounterValues && stepIndex === 0 && nextStepIndex === 1) { if (!fetchingCounterValues && stepIndex === 0 && nextStepIndex === 1) {
// TODO: seems shady to do this here..............
await this.fetchCounterValues() await this.fetchCounterValues()
} }
} }
componentWillUnmount() { componentWillUnmount() {
this.killProcess() this.handleReset()
ipcRenderer.removeListener('msg', this.handleMsgEvent) ipcRenderer.removeListener('msg', this.handleMsgEvent)
clearTimeout(this._timeout)
} }
importsAccounts() { async startScanAccountsDevice() {
const { accounts } = this.props
const { deviceSelected, currency } = this.state const { deviceSelected, currency } = this.state
if (!deviceSelected || !currency) { if (!deviceSelected || !currency) {
return return
} }
sendEvent('usb', 'wallet.getAccounts', { try {
pathDevice: deviceSelected.path, // scan every account for given currency and device
await runJob({
channel: 'usb',
job: 'wallet.scanAccountsOnDevice',
successResponse: 'wallet.scanAccountsOnDevice.success',
errorResponse: 'wallet.scanAccountsOnDevice.fail',
data: {
devicePath: deviceSelected.path,
currencyId: currency.id, currencyId: currency.id,
currentAccounts: accounts.map(acc => acc.id), },
}) })
// go to final step
this.setState({ stepIndex: 3 })
} catch (err) {
console.log(err)
}
} }
async fetchCounterValues() { async fetchCounterValues() {
@ -160,75 +184,46 @@ class AddAccountModal extends PureComponent<Props, State> {
_steps = GET_STEPS(this.props.t) _steps = GET_STEPS(this.props.t)
handleMsgEvent = (e, { data, type }) => { handleMsgEvent = (e, { data, type }) => {
const { accountsImport, currency } = this.state const { addAccount, existingAccounts } = this.props
const { addAccount } = this.props
if (type === 'wallet.getAccounts.start') {
this._pid = data.pid
}
if (type === 'wallet.getAccounts.progress') {
this.setState(prev => ({
stepIndex: 2,
accountsImport: {
...(data !== null
? {
[data.id]: {
...data,
name: `Account ${data.accountIndex + 1}`,
},
}
: {}),
...prev.accountsImport,
},
}))
if (currency && data && data.finish) { if (type === 'wallet.scanAccountsOnDevice.accountScanned') {
const { accountIndex, finish, ...account } = data // create Account from AccountRaw account scanned on device
addAccount({ const account = {
...account, ...decodeAccount(data),
// As data is passed inside electron event system,
// dates are converted to their string equivalent
//
// so, quick & dirty way to put back Date objects
operations: account.operations.map(op => ({
...op,
date: new Date(op.date),
})),
name: `Account ${accountIndex + 1}`,
archived: true, archived: true,
currency,
unit: currency.units[0],
})
} }
// add it to the reducer if needed, archived
if (!existingAccounts.find(a => a.id === account.id)) {
addAccount(account)
} }
if (type === 'wallet.getAccounts.success') { this.setState(state => ({
this.setState({ scannedAccounts: [...state.scannedAccounts, account],
selectedAccounts: Object.keys(accountsImport).map(k => accountsImport[k].id), }))
stepIndex: 3,
})
} }
} }
handleChangeDevice = d => this.setState({ deviceSelected: d }) handleChangeDevice = d => this.setState({ deviceSelected: d })
handleSelectAccount = a => () => handleToggleAccount = account => {
this.setState(prev => ({ const { selectedAccounts } = this.state
selectedAccounts: prev.selectedAccounts.includes(a) const isSelected = selectedAccounts.find(a => a === account)
? prev.selectedAccounts.filter(x => x !== a) this.setState({
: [a, ...prev.selectedAccounts], selectedAccounts: isSelected
})) ? selectedAccounts.filter(a => a !== account)
: [...selectedAccounts, account],
})
}
handleChangeCurrency = (currency: CryptoCurrency) => this.setState({ currency }) handleChangeCurrency = (currency: CryptoCurrency) => this.setState({ currency })
handleChangeStatus = (deviceStatus, appStatus) => this.setState({ appStatus }) handleChangeStatus = (deviceStatus, appStatus) => this.setState({ appStatus })
handleImportAccount = () => { handleImportAccount = () => {
const { archivedAccounts, updateAccount, closeModal } = this.props const { updateAccount } = this.props
const { selectedAccounts } = this.state const { selectedAccounts } = this.state
const accounts = archivedAccounts.filter(a => selectedAccounts.includes(a.id)) selectedAccounts.forEach(a => updateAccount({ ...a, archived: false }))
accounts.forEach(a => updateAccount({ ...a, archived: false }))
this.setState({ selectedAccounts: [] }) this.setState({ selectedAccounts: [] })
closeModal(MODAL_ADD_ACCOUNT) closeModal(MODAL_ADD_ACCOUNT)
} }
@ -242,22 +237,12 @@ class AddAccountModal extends PureComponent<Props, State> {
} }
handleReset = () => { handleReset = () => {
this.killProcess()
clearTimeout(this._timeout)
this.setState(INITIAL_STATE) this.setState(INITIAL_STATE)
} }
killProcess = () =>
sendEvent('msg', 'kill.process', {
pid: this._pid,
})
_timeout = undefined
_pid = null
renderStep() { renderStep() {
const { accounts, archivedAccounts, t } = this.props const { t, existingAccounts } = this.props
const { stepIndex, currency, accountsImport, deviceSelected, selectedAccounts } = this.state const { stepIndex, scannedAccounts, currency, deviceSelected, selectedAccounts } = this.state
const step = this._steps[stepIndex] const step = this._steps[stepIndex]
if (!step) { if (!step) {
return null return null
@ -269,31 +254,28 @@ class AddAccountModal extends PureComponent<Props, State> {
const stepProps = { const stepProps = {
t, t,
currency, currency,
// STEP CURRENCY
...props(stepIndex === 0, { ...props(stepIndex === 0, {
onChangeCurrency: this.handleChangeCurrency, onChangeCurrency: this.handleChangeCurrency,
}), }),
// STEP CONNECT DEVICE
...props(stepIndex === 1, { ...props(stepIndex === 1, {
deviceSelected, deviceSelected,
onStatusChange: this.handleChangeStatus, onStatusChange: this.handleChangeStatus,
onChangeDevice: this.handleChangeDevice, onChangeDevice: this.handleChangeDevice,
}), }),
// STEP ACCOUNT IMPORT PROGRESS
...props(stepIndex === 2, { ...props(stepIndex === 2, {
accountsImport, selectedAccounts,
importProgress: true, scannedAccounts,
existingAccounts,
}), }),
// STEP FINISH AND SELECT ACCOUNTS
...props(stepIndex === 3, { ...props(stepIndex === 3, {
accountsImport: Object.keys(accountsImport).reduce((result, k) => { onToggleAccount: this.handleToggleAccount,
const account = accountsImport[k]
const existingAccount = accounts.find(a => a.id === account.id)
if (!existingAccount || (existingAccount && existingAccount.archived)) {
result[account.id] = account
}
return result
}, {}),
archivedAccounts: archivedAccounts.filter(a => !accountsImport[a.id]),
importProgress: false,
onSelectAccount: this.handleSelectAccount,
selectedAccounts, selectedAccounts,
scannedAccounts,
existingAccounts,
}), }),
} }
@ -310,7 +292,7 @@ class AddAccountModal extends PureComponent<Props, State> {
case 1: case 1:
onClick = () => { onClick = () => {
this.handleNextStep() this.handleNextStep()
this.importsAccounts() this.startScanAccountsDevice()
} }
break break

6
src/internals/usb/wallet/index.js

@ -18,11 +18,13 @@ export default (sendEvent: Function) => ({
currencyId: string, currencyId: string,
}) => { }) => {
try { try {
sendEvent('wallet.scanAccountsOnDevice.start', { pid: process.pid }, { kill: false })
const accounts = await scanAccountsOnDevice({ const accounts = await scanAccountsOnDevice({
devicePath, devicePath,
currencyId, currencyId,
onAccountScanned: account => onAccountScanned: account => {
sendEvent('wallet.scanAccountsOnDevice.accountScanned', account), sendEvent('wallet.scanAccountsOnDevice.accountScanned', account, { kill: false })
},
}) })
sendEvent('wallet.scanAccountsOnDevice.success', accounts) sendEvent('wallet.scanAccountsOnDevice.success', accounts)
} catch (err) { } catch (err) {

2
src/internals/usb/wallet/scanAccountsOnDevice.js

@ -173,7 +173,7 @@ async function buildRawAccount({
xpub, xpub,
path: accountPath, // TODO: this should be called `accountPath` in Account/AccountRaw types path: accountPath, // TODO: this should be called `accountPath` in Account/AccountRaw types
rootPath: walletPath, // TODO: this should be `walletPath` in Account/AccountRaw types rootPath: walletPath, // TODO: this should be `walletPath` in Account/AccountRaw types
name: '', // TODO: placeholder name? name: `Account ${accountIndex}`, // TODO: placeholder name?
address: bitcoinAddress, // TODO: discuss about the utility of storing it here address: bitcoinAddress, // TODO: discuss about the utility of storing it here
addresses, addresses,
balance: 0, balance: 0,

4
src/reducers/accounts.js

@ -115,6 +115,10 @@ export function canCreateAccount(state: State): boolean {
return every(getAccounts(state), a => get(a, 'operations.length', 0) > 0) return every(getAccounts(state), a => get(a, 'operations.length', 0) > 0)
} }
export function decodeAccount(account: AccountRaw): Account {
return accountModel.decode({ data: account })
}
// Yeah. `any` should be `AccountRaw[]` but it can also be a map // Yeah. `any` should be `AccountRaw[]` but it can also be a map
// of wrapped accounts. And as flow is apparently incapable of doing // of wrapped accounts. And as flow is apparently incapable of doing
// such a simple thing, let's put any, right? I don't care. // such a simple thing, let's put any, right? I don't care.

Loading…
Cancel
Save