mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-01 03:40:53 +00:00
rewritten actions
This commit is contained in:
parent
2d4f3e8232
commit
0c0f9e7ac8
@ -1,12 +1,8 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
|
||||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
|
||||||
import * as ACCOUNT from 'actions/constants/account';
|
import * as ACCOUNT from 'actions/constants/account';
|
||||||
import * as NOTIFICATION from 'actions/constants/notification';
|
import * as reducerUtils from 'reducers/utils';
|
||||||
import * as PENDING from 'actions/constants/pendingTx';
|
import { initialState } from 'reducers/SelectedAccountReducer';
|
||||||
|
|
||||||
import * as stateUtils from 'reducers/utils';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AsyncAction,
|
AsyncAction,
|
||||||
@ -16,60 +12,191 @@ import type {
|
|||||||
State,
|
State,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
|
type SelectedAccountState = $ElementType<State, 'selectedAccount'>;
|
||||||
|
|
||||||
export type SelectedAccountAction = {
|
export type SelectedAccountAction = {
|
||||||
type: typeof ACCOUNT.DISPOSE,
|
type: typeof ACCOUNT.DISPOSE,
|
||||||
} | {
|
} | {
|
||||||
type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
||||||
payload: $ElementType<State, 'selectedAccount'>
|
payload: SelectedAccountState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AccountStatus = {
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
message?: string;
|
||||||
|
visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const dispose = (): Action => ({
|
export const dispose = (): Action => ({
|
||||||
type: ACCOUNT.DISPOSE,
|
type: ACCOUNT.DISPOSE,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): ?AccountStatus => {
|
||||||
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
const device = state.wallet.selectedDevice;
|
||||||
|
if (!device || !device.state) {
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: 'Loading device...',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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...',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockchain = state.blockchain.find(b => b.name === network.network);
|
||||||
|
if (blockchain && !blockchain.connected) {
|
||||||
|
return {
|
||||||
|
type: 'backend',
|
||||||
|
title: 'Backend is not connected',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// account not found (yet). checking why...
|
||||||
|
if (!account) {
|
||||||
|
if (!discovery || discovery.waitingForDevice) {
|
||||||
|
if (device.connected) {
|
||||||
|
// case 1: device is connected but discovery not started yet (probably waiting for auth)
|
||||||
|
if (device.available) {
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: 'Loading accounts...',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 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',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 3: device is disconnected
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: `Device ${device.instanceLabel} is disconnected`,
|
||||||
|
message: 'Connect device to load accounts',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discovery.completed) {
|
||||||
|
// case 4: account not found and discovery is completed
|
||||||
|
return {
|
||||||
|
type: 'warning',
|
||||||
|
title: 'Account does not exist',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 6: discovery is not completed yet
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: 'Loading accounts...',
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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`,
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!device.available) {
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: `Device ${device.instanceLabel} is unavailable`,
|
||||||
|
message: 'Change passphrase settings to use this device',
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional status: account does exists, but waiting for discovery to complete
|
||||||
|
if (discovery && !discovery.completed) {
|
||||||
|
return {
|
||||||
|
type: 'info',
|
||||||
|
title: 'Loading accounts...',
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
// ignore actions dispatched from this process
|
||||||
|
if (action.type === ACCOUNT.UPDATE_SELECTED_ACCOUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const state: State = getState();
|
const state: State = getState();
|
||||||
const { location } = state.router;
|
const { location } = state.router;
|
||||||
|
if (!location.state.account) {
|
||||||
|
// displayed route is not an account route
|
||||||
|
if (state.selectedAccount.location.length > 1) {
|
||||||
|
// reset "selectedAccount" reducer to default
|
||||||
|
dispatch({
|
||||||
|
type: ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
||||||
|
payload: initialState,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// handle devices state change (from trezor-connect events or location change)
|
// get new values for selected account
|
||||||
if (locationChange
|
const account = reducerUtils.getSelectedAccount(state);
|
||||||
|| prevState.accounts !== state.accounts
|
const network = reducerUtils.getSelectedNetwork(state);
|
||||||
|| prevState.discovery !== state.discovery
|
const discovery = reducerUtils.getDiscoveryProcess(state);
|
||||||
|| prevState.tokens !== state.tokens
|
const tokens = reducerUtils.getAccountTokens(state, account);
|
||||||
|| prevState.pending !== state.pending) {
|
const pending = reducerUtils.getAccountPendingTx(state.pending, account);
|
||||||
const account = stateUtils.getSelectedAccount(state);
|
|
||||||
const network = stateUtils.getSelectedNetwork(state);
|
|
||||||
const discovery = stateUtils.getDiscoveryProcess(state);
|
|
||||||
const tokens = stateUtils.getAccountTokens(state, account);
|
|
||||||
const pending = stateUtils.getAccountPendingTx(state.pending, account);
|
|
||||||
|
|
||||||
const payload: $ElementType<State, 'selectedAccount'> = {
|
// prepare new state for "selectedAccount" reducer
|
||||||
location: location.pathname,
|
const newState: SelectedAccountState = {
|
||||||
|
location: state.router.location.pathname,
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
discovery,
|
discovery,
|
||||||
tokens,
|
tokens,
|
||||||
pending,
|
pending,
|
||||||
|
notification: null,
|
||||||
|
visible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let needUpdate: boolean = false;
|
// get "selectedAccount" status from newState
|
||||||
Object.keys(payload).forEach((key) => {
|
const status = getAccountStatus(state, newState);
|
||||||
if (Array.isArray(payload[key])) {
|
newState.notification = status || null;
|
||||||
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
|
newState.visible = status ? status.visible : true;
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
} else if (payload[key] !== state.selectedAccount[key]) {
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (needUpdate) {
|
// check if newState is different than previous state
|
||||||
|
const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'status', 'visible']);
|
||||||
|
if (stateChanged) {
|
||||||
|
// update values in reducer
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
type: ACCOUNT.UPDATE_SELECTED_ACCOUNT,
|
||||||
payload,
|
payload: newState,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: move this to send account actions
|
||||||
|
/*
|
||||||
if (location.state.send) {
|
if (location.state.send) {
|
||||||
const rejectedTxs = pending.filter(tx => tx.rejected);
|
const rejectedTxs = pending.filter(tx => tx.rejected);
|
||||||
rejectedTxs.forEach((tx) => {
|
rejectedTxs.forEach((tx) => {
|
||||||
@ -95,6 +222,6 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
|
||||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
|
||||||
import * as WALLET from 'actions/constants/wallet';
|
import * as WALLET from 'actions/constants/wallet';
|
||||||
import * as stateUtils from 'reducers/utils';
|
import * as reducerUtils from 'reducers/utils';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Device,
|
Device,
|
||||||
@ -81,15 +79,20 @@ export const clearUnavailableDevicesData = (prevState: State, device: Device): T
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
// ignore actions dispatched from this process
|
||||||
|
if (action.type === WALLET.SET_SELECTED_DEVICE || action.type === WALLET.UPDATE_SELECTED_DEVICE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const state: State = getState();
|
const state: State = getState();
|
||||||
|
|
||||||
|
const locationChanged = reducerUtils.observeChanges(prevState.router.location, state.router.location, ['pathname']);
|
||||||
|
const device = reducerUtils.getSelectedDevice(state);
|
||||||
|
const selectedDeviceChanged = reducerUtils.observeChanges(state.wallet.selectedDevice, device);
|
||||||
|
|
||||||
// handle devices state change (from trezor-connect events or location change)
|
// handle devices state change (from trezor-connect events or location change)
|
||||||
if (locationChange || prevState.devices !== state.devices) {
|
if (locationChanged || selectedDeviceChanged) {
|
||||||
const device = stateUtils.getSelectedDevice(state);
|
if (device && reducerUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
||||||
if (state.wallet.selectedDevice !== device) {
|
|
||||||
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.UPDATE_SELECTED_DEVICE,
|
type: WALLET.UPDATE_SELECTED_DEVICE,
|
||||||
device,
|
device,
|
||||||
@ -101,5 +104,4 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
@ -11,12 +11,18 @@ import type {
|
|||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
location?: string;
|
location: string;
|
||||||
account: ?Account;
|
account: ?Account;
|
||||||
network: ?Coin;
|
network: ?Coin;
|
||||||
tokens: Array<Token>,
|
tokens: Array<Token>,
|
||||||
pending: Array<PendingTx>,
|
pending: Array<PendingTx>,
|
||||||
discovery: ?Discovery
|
discovery: ?Discovery,
|
||||||
|
notification: ?{
|
||||||
|
type: string,
|
||||||
|
title: string,
|
||||||
|
message?: string,
|
||||||
|
},
|
||||||
|
visible: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
@ -26,6 +32,8 @@ export const initialState: State = {
|
|||||||
tokens: [],
|
tokens: [],
|
||||||
pending: [],
|
pending: [],
|
||||||
discovery: null,
|
discovery: null,
|
||||||
|
notification: null,
|
||||||
|
visible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (state: State = initialState, action: Action): State => {
|
export default (state: State = initialState, action: Action): State => {
|
||||||
|
@ -128,6 +128,7 @@ export const observeChanges = (prev: ?(Object | Array<any>), current: ?(Object |
|
|||||||
if (prev instanceof Object && current instanceof Object) {
|
if (prev instanceof Object && current instanceof Object) {
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
const key = fields[i];
|
const key = fields[i];
|
||||||
|
if (Array.isArray(prev[key]) && Array.isArray(current[key])) return prev[key].length !== current[key].length;
|
||||||
if (prev[key] !== current[key]) return true;
|
if (prev[key] !== current[key]) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,10 +92,10 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
|||||||
api.dispatch(SendFormActionActions.observe(prevState, action));
|
api.dispatch(SendFormActionActions.observe(prevState, action));
|
||||||
|
|
||||||
// update common values in WallerReducer
|
// update common values in WallerReducer
|
||||||
api.dispatch(WalletActions.updateSelectedValues(prevState, action));
|
api.dispatch(WalletActions.observe(prevState, action));
|
||||||
|
|
||||||
// update common values in SelectedAccountReducer
|
// update common values in SelectedAccountReducer
|
||||||
api.dispatch(SelectedAccountActions.updateSelectedValues(prevState, action));
|
api.dispatch(SelectedAccountActions.observe(prevState, action));
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user