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-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-11-15 15:19:04 +00:00
type : ? string ; // notification type
title : ? string ; // notification title
message ? : ? string ; // notification message
2018-09-26 17:35:36 +00:00
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-11-21 14:24:07 +00:00
const getAccountLoader = ( state : State , selectedAccount : SelectedAccountState ) : ? AccountStatus => {
2018-09-26 12:11:37 +00:00
const device = state . wallet . selectedDevice ;
const {
account ,
discovery ,
network ,
} = selectedAccount ;
2018-11-21 14:24:07 +00:00
if ( ! device || ! device . state ) {
2018-09-26 12:11:37 +00:00
return {
2018-11-21 14:24:07 +00:00
type : 'progress' ,
title : 'Loading device...' ,
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-11-21 14:24:07 +00:00
// corner case: accountState didn't finish loading state after LOCATION_CHANGE action
if ( ! network ) {
2018-09-26 12:11:37 +00:00
return {
2018-11-21 14:24:07 +00:00
type : 'progress' ,
title : 'Loading account state...' ,
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-11-22 12:51:57 +00:00
if ( account ) return null ;
2018-09-26 12:11:37 +00:00
// account not found (yet). checking why...
2018-11-22 12:51:57 +00:00
2018-11-26 18:36:11 +00:00
if ( discovery && discovery . fwOutdated ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } firmware is outdated ` ,
message : 'TODO: update firmware explanation' ,
shouldRender : false ,
} ;
}
if ( discovery && discovery . fwNotSupported ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is not supported ` ,
message : 'TODO: model T1 not supported explanation' ,
shouldRender : false ,
} ;
}
2018-11-22 12:51:57 +00:00
if ( ! discovery || ( discovery . waitingForDevice || discovery . interrupted ) ) {
if ( device . connected ) {
// case 1: device is connected but discovery not started yet (probably waiting for auth)
if ( device . available ) {
2018-09-26 12:11:37 +00:00
return {
2018-11-22 12:51:57 +00:00
type : 'progress' ,
title : 'Authenticating 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-11-22 12:51:57 +00:00
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
// this is related to device instance in url, it's not used for now (device clones are disabled)
2018-09-26 12:11:37 +00:00
return {
2018-11-21 14:24:07 +00:00
type : 'info' ,
2018-11-22 12:51:57 +00:00
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-11-22 12:51:57 +00:00
// case 3: device is disconnected
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is disconnected ` ,
message : 'Connect device to load accounts' ,
shouldRender : false ,
} ;
2018-09-26 12:11:37 +00:00
}
2018-11-22 12:51:57 +00:00
if ( discovery . completed ) {
// case 4: account not found and discovery is completed
return {
type : 'info' ,
title : 'Account does not exist' ,
shouldRender : false ,
} ;
}
// case default: account information isn't loaded yet
return {
type : 'progress' ,
title : 'Loading account' ,
shouldRender : false ,
} ;
2018-11-21 14:24:07 +00:00
} ;
const getAccountNotification = ( state : State , selectedAccount : SelectedAccountState ) : ? AccountStatus => {
const device = state . wallet . selectedDevice ;
2018-11-22 12:51:57 +00:00
const { account , network , discovery } = selectedAccount ;
if ( ! device || ! network ) return null ;
2018-11-21 14:24:07 +00:00
2018-11-22 12:51:57 +00:00
// case 1: backend status
const blockchain = state . blockchain . find ( b => b . shortcut === network . shortcut ) ;
if ( blockchain && ! blockchain . connected ) {
return {
type : 'backend' ,
title : ` ${ network . name } backend is not connected ` ,
shouldRender : false ,
} ;
}
2018-11-21 14:24:07 +00:00
2018-11-22 12:51:57 +00:00
// case 2: account does exists and it's visible but shouldn't be active
if ( account && discovery && ! discovery . completed && ! discovery . waitingForDevice ) {
return {
type : 'info' ,
title : 'Loading other accounts...' ,
shouldRender : true ,
} ;
}
2018-11-21 14:38:32 +00:00
2018-11-22 12:51:57 +00:00
// case 3: account does exists and device is disconnected
if ( ! device . connected ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is disconnected ` ,
shouldRender : true ,
} ;
}
// case 4: account does exists and device is unavailable (created with different passphrase settings) account cannot be accessed
// this is related to device instance in url, it's not used for now (device clones are disabled)
if ( ! device . available ) {
return {
type : 'info' ,
title : ` Device ${ device . instanceLabel } is unavailable ` ,
message : 'Change passphrase settings to use this device' ,
shouldRender : true ,
} ;
2018-09-26 12:11:37 +00:00
}
2018-11-22 12:51:57 +00:00
// case default
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' ) ,
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 ( ) ;
2018-11-05 11:07:32 +00:00
const notification = {
type : null ,
message : null ,
title : null ,
} ;
2018-11-21 14:24:07 +00:00
const loader = {
type : null ,
message : null ,
title : null ,
} ;
2018-09-26 12:11:37 +00:00
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 ,
2018-11-05 11:07:32 +00:00
notification ,
2018-11-21 14:24:07 +00:00
loader ,
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
2018-11-21 14:24:07 +00:00
const statusNotification = getAccountNotification ( state , newState ) ;
const statusLoader = getAccountLoader ( state , newState ) ;
const shouldRender = ( statusNotification && statusLoader ) ? ( statusNotification . shouldRender || statusLoader . shouldRender ) : true ;
newState . notification = statusNotification || notification ;
newState . shouldRender = shouldRender ;
newState . loader = statusLoader || loader ;
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
} ;