2018-02-20 09:30:36 +00:00
/* @flow */
2018-10-01 09:59:31 +00:00
import { LOCATION _CHANGE } from 'react-router-redux' ;
2018-10-03 12:44:19 +00:00
import { BLOCKCHAIN } from 'trezor-connect' ;
2018-10-01 09:59:31 +00:00
import * as WALLET from 'actions/constants/wallet' ;
2018-08-14 13:18:16 +00:00
import * as ACCOUNT from 'actions/constants/account' ;
2018-10-01 09:59:31 +00:00
import * as DISCOVERY from 'actions/constants/discovery' ;
import * as TOKEN from 'actions/constants/token' ;
import * as PENDING from 'actions/constants/pendingTx' ;
2018-10-03 12:44:19 +00:00
import * as SEND from 'actions/constants/send' ;
2018-10-01 09:59:31 +00:00
2018-09-26 12:11:37 +00:00
import * as reducerUtils from 'reducers/utils' ;
2018-02-20 09:30:36 +00:00
2018-05-25 09:26:36 +00:00
import type {
2018-10-01 09:59:31 +00:00
PayloadAction ,
2018-07-30 10:52:13 +00:00
Action ,
GetState ,
2018-05-25 09:26:36 +00:00
Dispatch ,
State ,
2018-08-14 12:56:47 +00:00
} from 'flowtype' ;
2018-09-20 21:04:03 +00:00
2018-09-26 12:11:37 +00:00
type SelectedAccountState = $ElementType < State , 'selectedAccount' > ;
2018-05-18 17:26:46 +00:00
export type SelectedAccountAction = {
2018-04-16 21:19:50 +00:00
type : typeof ACCOUNT . DISPOSE ,
2018-05-25 09:26:36 +00:00
} | {
type : typeof ACCOUNT . UPDATE _SELECTED _ACCOUNT ,
2018-09-26 12:11:37 +00:00
payload : SelectedAccountState ,
2018-04-16 21:19:50 +00:00
} ;
2018-02-20 09:30:36 +00:00
2018-09-26 12:11:37 +00:00
type AccountStatus = {
2018-09-26 17:35:36 +00:00
type : string ; // notification type
title : string ; // notification title
message ? : string ; // notification message
shouldRender : boolean ; // should render account page
2018-09-26 12:11:37 +00:00
}
2018-09-05 14:17:50 +00:00
export const dispose = ( ) : Action => ( {
type : ACCOUNT . DISPOSE ,
} ) ;
2018-09-26 12:11:37 +00:00
const getAccountStatus = ( state : State , selectedAccount : SelectedAccountState ) : ? AccountStatus => {
const device = state . wallet . selectedDevice ;
if ( ! device || ! device . state ) {
return {
type : 'info' ,
title : 'Loading device...' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
}
const {
account ,
discovery ,
network ,
} = selectedAccount ;
// corner case: accountState didn't finish loading state after LOCATION_CHANGE action
if ( ! network ) {
return {
type : 'info' ,
title : 'Loading account state...' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
}
2018-02-20 09:30:36 +00:00
2018-09-26 12:11:37 +00:00
const blockchain = state . blockchain . find ( b => b . name === network . network ) ;
if ( blockchain && ! blockchain . connected ) {
return {
type : 'backend' ,
2018-10-01 09:59:31 +00:00
title : ` ${ network . name } backend is not connected ` ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-07-30 10:52:13 +00:00
} ;
2018-09-26 12:11:37 +00:00
}
2018-05-28 17:48:07 +00:00
2018-09-26 12:11:37 +00:00
// account not found (yet). checking why...
if ( ! account ) {
2018-10-04 08:51:06 +00:00
if ( ! discovery || ( discovery . waitingForDevice || discovery . interrupted ) ) {
2018-09-26 12:11:37 +00:00
if ( device . connected ) {
// case 1: device is connected but discovery not started yet (probably waiting for auth)
if ( device . available ) {
return {
type : 'info' ,
2018-10-03 17:55:30 +00:00
title : 'Authenticating device...' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
2018-05-25 09:26:36 +00:00
}
2018-09-26 12:11:37 +00:00
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is unavailable ` ,
message : 'Change passphrase settings to use this device' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
2018-07-30 10:52:13 +00:00
}
2018-05-25 09:26:36 +00:00
2018-09-26 12:11:37 +00:00
// case 3: device is disconnected
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is disconnected ` ,
message : 'Connect device to load accounts' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
}
if ( discovery . completed ) {
// case 4: account not found and discovery is completed
return {
type : 'warning' ,
title : 'Account does not exist' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
}
// case 6: discovery is not completed yet
return {
type : 'info' ,
title : 'Loading accounts...' ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
}
// Additional status: account does exists and it's visible but shouldn't be active
if ( ! device . connected ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is disconnected ` ,
2018-09-26 17:35:36 +00:00
shouldRender : true ,
2018-09-26 12:11:37 +00:00
} ;
}
if ( ! device . available ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is unavailable ` ,
message : 'Change passphrase settings to use this device' ,
2018-09-26 17:35:36 +00:00
shouldRender : true ,
2018-09-26 12:11:37 +00:00
} ;
}
// Additional status: account does exists, but waiting for discovery to complete
if ( discovery && ! discovery . completed ) {
return {
type : 'info' ,
title : 'Loading accounts...' ,
2018-09-26 17:35:36 +00:00
shouldRender : true ,
2018-09-26 12:11:37 +00:00
} ;
}
return null ;
} ;
2018-10-01 09:59:31 +00:00
// list of all actions which has influence on "selectedAccount" reducer
// other actions will be ignored
const actions = [
LOCATION _CHANGE ,
2018-10-03 12:44:19 +00:00
... Object . values ( BLOCKCHAIN ) . filter ( v => typeof v === 'string' ) ,
SEND . TX _COMPLETE ,
2018-10-01 09:59:31 +00:00
WALLET . SET _SELECTED _DEVICE ,
WALLET . UPDATE _SELECTED _DEVICE ,
2018-10-04 08:51:06 +00:00
... Object . values ( ACCOUNT ) . filter ( v => typeof v === 'string' && v !== ACCOUNT . UPDATE _SELECTED _ACCOUNT && v !== ACCOUNT . DISPOSE ) , // exported values got unwanted "__esModule: true" as first element
2018-10-01 09:59:31 +00:00
... Object . values ( DISCOVERY ) . filter ( v => typeof v === 'string' ) ,
... Object . values ( TOKEN ) . filter ( v => typeof v === 'string' ) ,
... Object . values ( PENDING ) . filter ( v => typeof v === 'string' ) ,
] ;
/ *
* Called from WalletService
* /
export const observe = ( prevState : State , action : Action ) : PayloadAction < boolean > => ( dispatch : Dispatch , getState : GetState ) : boolean => {
// ignore not listed actions
if ( actions . indexOf ( action . type ) < 0 ) return false ;
2018-09-26 12:11:37 +00:00
const state : State = getState ( ) ;
const { location } = state . router ;
2018-10-01 09:59:31 +00:00
// displayed route is not an account route
if ( ! location . state . account ) return false ;
2018-09-26 12:11:37 +00:00
// get new values for selected account
const account = reducerUtils . getSelectedAccount ( state ) ;
const network = reducerUtils . getSelectedNetwork ( state ) ;
const discovery = reducerUtils . getDiscoveryProcess ( state ) ;
const tokens = reducerUtils . getAccountTokens ( state , account ) ;
const pending = reducerUtils . getAccountPendingTx ( state . pending , account ) ;
// prepare new state for "selectedAccount" reducer
const newState : SelectedAccountState = {
location : state . router . location . pathname ,
account ,
network ,
discovery ,
tokens ,
pending ,
notification : null ,
2018-09-26 17:35:36 +00:00
shouldRender : false ,
2018-09-26 12:11:37 +00:00
} ;
2018-05-25 09:26:36 +00:00
2018-09-26 12:11:37 +00:00
// get "selectedAccount" status from newState
const status = getAccountStatus ( state , newState ) ;
newState . notification = status || null ;
2018-09-26 17:35:36 +00:00
newState . shouldRender = status ? status . shouldRender : true ;
2018-09-26 12:11:37 +00:00
// check if newState is different than previous state
2018-10-01 09:59:31 +00:00
const stateChanged = reducerUtils . observeChanges ( prevState . selectedAccount , newState , {
account : [ 'balance' , 'nonce' ] ,
discovery : [ 'accountIndex' , 'interrupted' , 'completed' , 'waitingForBlockchain' , 'waitingForDevice' ] ,
} ) ;
2018-09-26 12:11:37 +00:00
if ( stateChanged ) {
// update values in reducer
dispatch ( {
type : ACCOUNT . UPDATE _SELECTED _ACCOUNT ,
payload : newState ,
} ) ;
2018-02-20 09:30:36 +00:00
}
2018-10-01 09:59:31 +00:00
return stateChanged ;
2018-07-30 10:52:13 +00:00
} ;