1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-11-15 12:59:09 +00:00
trezor-wallet/src/actions/LocalStorageActions.js

252 lines
8.6 KiB
JavaScript
Raw Normal View History

2018-02-20 09:30:36 +00:00
/* @flow */
2018-07-30 10:52:13 +00:00
2018-02-20 09:30:36 +00:00
2018-08-14 13:18:16 +00:00
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as ACCOUNT from 'actions/constants/account';
import * as TOKEN from 'actions/constants/token';
import * as DISCOVERY from 'actions/constants/discovery';
import * as STORAGE from 'actions/constants/localStorage';
import * as PENDING from 'actions/constants/pendingTx';
import * as WALLET from 'actions/constants/wallet';
2018-09-05 14:17:50 +00:00
import { httpRequest } from 'utils/networkUtils';
2018-10-11 16:04:25 +00:00
import * as buildUtils from 'utils/build';
import * as storageUtils from 'utils/storage';
2018-02-20 09:30:36 +00:00
import { findAccountTokens } from 'reducers/TokensReducer';
import type { Account } from 'reducers/AccountsReducer';
import type { Token } from 'reducers/TokensReducer';
import type { PendingTx } from 'reducers/PendingTxReducer';
import type { Discovery } from 'reducers/DiscoveryReducer';
2018-07-30 10:52:13 +00:00
import type {
TrezorDevice,
ThunkAction,
AsyncAction,
GetState,
Dispatch,
2018-08-14 12:56:47 +00:00
} from 'flowtype';
2018-10-15 13:44:10 +00:00
import type { Config, Network, TokensCollection } from 'reducers/LocalStorageReducer';
2018-04-16 21:19:50 +00:00
import Erc20AbiJSON from 'public/data/ERC20Abi.json';
import AppConfigJSON from 'public/data/appConfig.json';
2018-04-16 21:19:50 +00:00
export type StorageAction = {
type: typeof STORAGE.READY,
2018-04-23 10:20:15 +00:00
config: Config,
tokens: TokensCollection,
2018-09-21 10:41:59 +00:00
ERC20Abi: Array<TokensCollection>
2018-04-16 21:19:50 +00:00
} | {
type: typeof STORAGE.SAVE,
network: string,
} | {
type: typeof STORAGE.ERROR,
error: string,
};
const TYPE: 'local' = 'local';
const { STORAGE_PATH } = storageUtils;
const KEY_VERSION: string = `${STORAGE_PATH}version`;
const KEY_DEVICES: string = `${STORAGE_PATH}devices`;
const KEY_ACCOUNTS: string = `${STORAGE_PATH}accounts`;
const KEY_DISCOVERY: string = `${STORAGE_PATH}discovery`;
const KEY_TOKENS: string = `${STORAGE_PATH}tokens`;
const KEY_PENDING: string = `${STORAGE_PATH}pending`;
const KEY_BETA_MODAL: string = '/betaModalPrivacy'; // this key needs to be compatible with "parent" (old) wallet
2018-04-16 21:19:50 +00:00
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
// or
// https://www.npmjs.com/package/redux-react-session
2018-04-16 21:19:50 +00:00
const findAccounts = (devices: Array<TrezorDevice>, accounts: Array<Account>): Array<Account> => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []);
2018-04-16 21:19:50 +00:00
const findTokens = (accounts: Array<Account>, tokens: Array<Token>): Array<Token> => accounts.reduce((arr, account) => arr.concat(findAccountTokens(tokens, account)), []);
2018-04-16 21:19:50 +00:00
const findDiscovery = (devices: Array<TrezorDevice>, discovery: Array<Discovery>): Array<Discovery> => devices.reduce((arr, dev) => arr.concat(discovery.filter(a => a.deviceState === dev.state && a.publicKey.length > 0)), []);
2018-07-30 10:52:13 +00:00
const findPendingTxs = (accounts: Array<Account>, pending: Array<PendingTx>): Array<PendingTx> => accounts.reduce((result, account) => result.concat(pending.filter(p => p.address === account.address && p.network === account.network)), []);
2018-07-30 10:52:13 +00:00
export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const devices: Array<TrezorDevice> = getState().devices.filter(d => d.features && d.remember === true);
const accounts: Array<Account> = findAccounts(devices, getState().accounts);
const tokens: Array<Token> = findTokens(accounts, getState().tokens);
const pending: Array<PendingTx> = findPendingTxs(accounts, getState().pending);
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
2018-04-16 21:19:50 +00:00
// save devices
storageUtils.set(TYPE, KEY_DEVICES, JSON.stringify(devices));
// save already preloaded accounts
storageUtils.set(TYPE, KEY_ACCOUNTS, JSON.stringify(accounts));
// save discovery state
storageUtils.set(TYPE, KEY_DISCOVERY, JSON.stringify(discovery));
// tokens
storageUtils.set(TYPE, KEY_TOKENS, JSON.stringify(tokens));
// pending transactions
storageUtils.set(TYPE, KEY_PENDING, JSON.stringify(pending));
};
export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch): void => {
if (!event.newValue) return;
if (event.key === KEY_DEVICES) {
// check if device was added/ removed
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
// if (newDevices.length !== myDevices.length) {
// const diff = myDevices.filter(d => newDevices.indexOf(d) < 0)
// console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff)
// // check if difference is caused by local device which is not saved
// // or device which was saved in other tab
// }
// const diff = oldDevices.filter(d => newDevices.indexOf())
}
if (event.key === KEY_ACCOUNTS) {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
if (event.key === KEY_TOKENS) {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
if (event.key === KEY_PENDING) {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
if (event.key === KEY_DISCOVERY) {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
};
2018-04-16 21:19:50 +00:00
const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
if (typeof window.localStorage === 'undefined') return;
2018-05-17 12:40:38 +00:00
try {
const config: Config = await httpRequest(AppConfigJSON, 'json');
2018-10-11 16:04:25 +00:00
// remove ropsten testnet from config networks
if (!buildUtils.isDev()) {
const index = config.networks.findIndex(c => c.shortcut === 'trop');
delete config.networks[index];
}
2018-10-11 16:04:25 +00:00
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
window.addEventListener('storage', (event) => {
dispatch(update(event));
});
2018-05-17 12:40:38 +00:00
// load tokens
const tokens = await config.networks.reduce(async (promise: Promise<TokensCollection>, network: Network): Promise<TokensCollection> => {
const collection: TokensCollection = await promise;
const json = await httpRequest(network.tokens, 'json');
collection[network.shortcut] = json;
return collection;
}, Promise.resolve({}));
2018-02-20 09:30:36 +00:00
dispatch({
type: STORAGE.READY,
config,
tokens,
ERC20Abi,
});
} catch (error) {
dispatch({
type: STORAGE.ERROR,
error,
});
}
};
2018-02-20 09:30:36 +00:00
const VERSION: string = '1';
2018-02-20 09:30:36 +00:00
const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
// validate version
const version: ?string = storageUtils.get(TYPE, KEY_VERSION);
if (version && version !== VERSION) {
storageUtils.clearAll();
}
storageUtils.set(TYPE, KEY_VERSION, VERSION);
const devices: ?string = storageUtils.get(TYPE, KEY_DEVICES);
if (devices) {
dispatch({
type: CONNECT.DEVICE_FROM_STORAGE,
payload: JSON.parse(devices),
});
}
2018-03-08 16:10:53 +00:00
const accounts: ?string = storageUtils.get(TYPE, KEY_ACCOUNTS);
if (accounts) {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(accounts),
});
}
2018-07-30 10:52:13 +00:00
const userTokens: ?string = storageUtils.get(TYPE, KEY_TOKENS);
if (userTokens) {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(userTokens),
});
}
const pending: ?string = storageUtils.get(TYPE, KEY_PENDING);
if (pending) {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(pending),
});
}
const discovery: ?string = storageUtils.get(TYPE, KEY_DISCOVERY);
if (discovery) {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(discovery),
});
}
if (buildUtils.isDev() || buildUtils.isBeta()) {
const betaModal = Object.keys(window.localStorage).find(key => key.indexOf(KEY_BETA_MODAL) >= 0);
if (!betaModal) {
2018-02-20 09:30:36 +00:00
dispatch({
type: WALLET.SHOW_BETA_DISCLAIMER,
show: true,
2018-07-30 10:52:13 +00:00
});
2018-02-20 09:30:36 +00:00
}
}
2018-09-05 14:17:50 +00:00
};
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
dispatch(loadStorageData());
// stop loading resources and wait for user action
if (!getState().wallet.showBetaDisclaimer) {
dispatch(loadJSON());
2018-02-20 09:30:36 +00:00
}
};
export const hideBetaDisclaimer = (): ThunkAction => (dispatch: Dispatch): void => {
storageUtils.set(TYPE, KEY_BETA_MODAL, true);
dispatch(loadJSON());
};