2018-02-20 09:30:36 +00:00
|
|
|
/* @flow */
|
2018-12-18 13:33:01 +00:00
|
|
|
import { LOCATION_CHANGE } from 'connected-react-router';
|
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';
|
2019-02-20 11:17:28 +00:00
|
|
|
import { getVersion } from 'utils/device';
|
2018-12-17 17:33:55 +00:00
|
|
|
import { initialState } from 'reducers/SelectedAccountReducer';
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
import type { PayloadAction, Action, GetState, Dispatch, State } from 'flowtype';
|
2018-09-20 21:04:03 +00:00
|
|
|
|
2018-12-17 17:33:55 +00:00
|
|
|
import type {
|
|
|
|
State as SelectedAccountState,
|
|
|
|
Loader,
|
|
|
|
Notification,
|
|
|
|
ExceptionPage,
|
|
|
|
} from 'reducers/SelectedAccountReducer';
|
2018-09-26 12:11:37 +00:00
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
export type SelectedAccountAction =
|
|
|
|
| {
|
|
|
|
type: typeof ACCOUNT.DISPOSE,
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
|
|
|
payload: SelectedAccountState,
|
|
|
|
};
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-09-05 14:17:50 +00:00
|
|
|
export const dispose = (): Action => ({
|
|
|
|
type: ACCOUNT.DISPOSE,
|
|
|
|
});
|
|
|
|
|
2018-12-17 17:33:55 +00:00
|
|
|
// display exception page instead of component body
|
|
|
|
const getExceptionPage = (state: State, selectedAccount: SelectedAccountState): ?ExceptionPage => {
|
|
|
|
const device = state.wallet.selectedDevice;
|
|
|
|
const { discovery, network } = selectedAccount;
|
|
|
|
if (!device || !device.features || !network || !discovery) return null;
|
|
|
|
|
|
|
|
if (discovery.fwOutdated) {
|
2018-12-17 18:07:11 +00:00
|
|
|
// those values are not used because in this case views/Wallet/views/FirmwareUpdate component will be displayed and it already has text content
|
2018-12-17 17:33:55 +00:00
|
|
|
return {
|
2018-12-17 17:48:30 +00:00
|
|
|
type: 'fwOutdated',
|
|
|
|
title: 'not-used',
|
|
|
|
message: 'not-used',
|
|
|
|
shortcut: 'not-used',
|
2018-12-17 17:33:55 +00:00
|
|
|
};
|
|
|
|
}
|
2019-02-20 11:17:28 +00:00
|
|
|
|
2018-12-17 17:33:55 +00:00
|
|
|
if (discovery.fwNotSupported) {
|
|
|
|
return {
|
|
|
|
type: 'fwNotSupported',
|
2019-02-20 11:17:28 +00:00
|
|
|
title: `${network.name} is not supported with Trezor ${getVersion(device)}`,
|
2018-12-17 17:33:55 +00:00
|
|
|
message: 'Find more information on Trezor Wiki.',
|
|
|
|
shortcut: network.shortcut,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
// display loader instead of component body
|
|
|
|
const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): ?Loader => {
|
2018-09-26 12:11:37 +00:00
|
|
|
const device = state.wallet.selectedDevice;
|
2019-03-04 12:33:02 +00:00
|
|
|
const { account, discovery, network } = selectedAccount;
|
2018-09-26 12:11:37 +00:00
|
|
|
|
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 12:11:37 +00:00
|
|
|
};
|
|
|
|
}
|
2018-02-20 09:30:36 +00:00
|
|
|
|
2018-12-17 17:33:55 +00:00
|
|
|
// corner case: SelectedAccountState didn't change after LOCATION_CHANGE action
|
2018-11-21 14:24:07 +00:00
|
|
|
if (!network) {
|
2018-09-26 12:11:37 +00:00
|
|
|
return {
|
2018-11-21 14:24:07 +00:00
|
|
|
type: 'progress',
|
2018-12-17 17:33:55 +00:00
|
|
|
title: 'Loading account',
|
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
|
|
|
|
|
|
|
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 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 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',
|
|
|
|
};
|
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',
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// case default: account information isn't loaded yet
|
|
|
|
return {
|
|
|
|
type: 'progress',
|
|
|
|
title: 'Loading account',
|
|
|
|
};
|
2018-11-21 14:24:07 +00:00
|
|
|
};
|
|
|
|
|
2018-12-17 17:33:55 +00:00
|
|
|
// display notification above the component, with or without component body
|
2019-03-04 12:33:02 +00:00
|
|
|
const getAccountNotification = (
|
|
|
|
state: State,
|
|
|
|
selectedAccount: SelectedAccountState
|
|
|
|
): ?(Notification & { shouldRender: boolean }) => {
|
2018-11-21 14:24:07 +00:00
|
|
|
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',
|
2019-05-02 12:00:24 +00:00
|
|
|
variant: 'error',
|
2018-11-22 12:51:57 +00:00
|
|
|
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',
|
2019-05-02 12:00:24 +00:00
|
|
|
variant: 'info',
|
2018-11-22 12:51:57 +00:00
|
|
|
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',
|
2019-05-02 12:00:24 +00:00
|
|
|
variant: 'info',
|
2018-11-22 12:51:57 +00:00
|
|
|
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',
|
2019-05-02 12:00:24 +00:00
|
|
|
variant: 'info',
|
2018-11-22 12:51:57 +00:00
|
|
|
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,
|
2019-03-04 12:33:02 +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'),
|
|
|
|
];
|
|
|
|
|
|
|
|
/*
|
2019-03-04 12:33:02 +00:00
|
|
|
* Called from WalletService
|
|
|
|
*/
|
|
|
|
export const observe = (prevState: State, action: Action): PayloadAction<boolean> => (
|
|
|
|
dispatch: Dispatch,
|
|
|
|
getState: GetState
|
|
|
|
): boolean => {
|
2018-10-01 09:59:31 +00:00
|
|
|
// 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-21 14:24:07 +00:00
|
|
|
|
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);
|
2018-12-21 10:19:40 +00:00
|
|
|
const tokens = reducerUtils.getAccountTokens(state.tokens, account);
|
2018-09-26 12:11:37 +00:00
|
|
|
const pending = reducerUtils.getAccountPendingTx(state.pending, account);
|
|
|
|
|
|
|
|
// prepare new state for "selectedAccount" reducer
|
|
|
|
const newState: SelectedAccountState = {
|
2018-12-17 17:33:55 +00:00
|
|
|
...initialState,
|
2018-09-26 12:11:37 +00:00
|
|
|
location: state.router.location.pathname,
|
|
|
|
account,
|
|
|
|
network,
|
|
|
|
discovery,
|
|
|
|
tokens,
|
|
|
|
pending,
|
|
|
|
};
|
2018-05-25 09:26:36 +00:00
|
|
|
|
2018-09-26 12:11:37 +00:00
|
|
|
// get "selectedAccount" status from newState
|
2018-12-17 17:33:55 +00:00
|
|
|
const exceptionPage = getExceptionPage(state, newState);
|
|
|
|
const loader = getAccountLoader(state, newState);
|
|
|
|
const notification = getAccountNotification(state, newState);
|
|
|
|
|
|
|
|
if (exceptionPage) {
|
|
|
|
newState.exceptionPage = exceptionPage;
|
|
|
|
} else {
|
|
|
|
newState.loader = loader;
|
|
|
|
newState.notification = notification;
|
|
|
|
}
|
|
|
|
|
2019-03-04 12:33:02 +00:00
|
|
|
newState.shouldRender = !(
|
|
|
|
loader ||
|
|
|
|
exceptionPage ||
|
|
|
|
(notification && !notification.shouldRender)
|
|
|
|
);
|
2018-11-21 14:24:07 +00:00
|
|
|
|
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'],
|
2019-03-04 12:33:02 +00:00
|
|
|
discovery: [
|
|
|
|
'accountIndex',
|
|
|
|
'interrupted',
|
|
|
|
'completed',
|
|
|
|
'waitingForBlockchain',
|
|
|
|
'waitingForDevice',
|
|
|
|
],
|
2018-10-01 09:59:31 +00:00
|
|
|
});
|
|
|
|
|
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
|
|
|
};
|