@ -7,11 +7,23 @@ 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' ;
import { httpRequest } from 'utils/networkUtils' ;
import * as buildUtils from 'utils/build' ;
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' ;
import type {
ThunkAction , AsyncAction , /* GetState, */ Dispatch ,
TrezorDevice ,
ThunkAction ,
AsyncAction ,
GetState ,
Dispatch ,
} from 'flowtype' ;
import type { Config , Network , TokensCollection } from 'reducers/LocalStorageReducer' ;
@ -31,7 +43,7 @@ export type StorageAction = {
error : string ,
} ;
export const get = ( key : string ) : ? string => {
const get = ( key : string ) : ? string => {
try {
return window . localStorage . getItem ( key ) ;
} catch ( error ) {
@ -40,168 +52,212 @@ export const get = (key: string): ?string => {
}
} ;
export function update ( event : StorageEvent ) : AsyncAction {
return async ( dispatch : Dispatch /* , getState: GetState */ ) : Promise < void > => {
if ( ! event . newValue ) return ;
const set = ( key : string , value : string | boolean ) : void => {
try {
window . localStorage . setItem ( key , value ) ;
} catch ( error ) {
console . error ( ` Local Storage ERROR: ${ error } ` ) ;
}
} ;
if ( event . 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);
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
// or
// https://www.npmjs.com/package/redux-react-session
// 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 findAccounts = ( devices : Array < TrezorDevice > , accounts : Array < Account > ) : Array < Account > => devices . reduce ( ( arr , dev ) => arr . concat ( accounts . filter ( a => a . deviceState === dev . state ) ) , [ ] ) ;
// const diff = oldDevices.filter(d => newDevices.indexOf())
}
const findTokens = ( accounts : Array < Account > , tokens : Array < Token > ) : Array < Token > => accounts . reduce ( ( arr , account ) => arr . concat ( findAccountTokens ( tokens , account ) ) , [ ] ) ;
if ( event . key === 'accounts' ) {
dispatch ( {
type : ACCOUNT . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
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 ) ) , [ ] ) ;
if ( event . key === 'tokens' ) {
dispatch ( {
type : TOKEN . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
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 ) ) , [ ] ) ;
if ( event . key === 'pending' ) {
dispatch ( {
type : PENDING . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
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 ) ;
if ( event . key === 'discovery' ) {
dispatch ( {
type : DISCOVERY . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
} ;
}
// save devices
set ( 'devices' , JSON . stringify ( devices ) ) ;
// save already preloaded accounts
set ( 'accounts' , JSON . stringify ( accounts ) ) ;
// save discovery state
set ( 'discovery' , JSON . stringify ( discovery ) ) ;
// tokens
set ( 'tokens' , JSON . stringify ( tokens ) ) ;
// pending transactions
set ( 'pending' , JSON . stringify ( pending ) ) ;
} ;
export const update = ( event : StorageEvent ) : ThunkAction => ( dispatch : Dispatch ) : void => {
if ( ! event . newValue ) return ;
if ( event . 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 === 'accounts' ) {
dispatch ( {
type : ACCOUNT . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
if ( event . key === 'tokens' ) {
dispatch ( {
type : TOKEN . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
if ( event . key === 'pending' ) {
dispatch ( {
type : PENDING . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
if ( event . key === 'discovery' ) {
dispatch ( {
type : DISCOVERY . FROM _STORAGE ,
payload : JSON . parse ( event . newValue ) ,
} ) ;
}
} ;
const VERSION : string = '1' ;
export function loadTokensFromJSON ( ) : AsyncAction {
return async ( dispatch : Dispatch ) : Promise < void > => {
if ( typeof window . localStorage === 'undefined' ) return ;
const loadJSON = ( ) : AsyncAction => async ( dispatch : Dispatch ) : Promise < void > => {
if ( typeof window . localStorage === 'undefined' ) return ;
try {
const config : Config = await httpRequest ( AppConfigJSON , 'json' ) ;
try {
const config : Config = await httpRequest ( AppConfigJSON , 'json' ) ;
if ( ! buildUtils . isDev ( ) ) {
const index = config . networks . findIndex ( c => c . shortcut === 'trop' ) ;
delete config . networks [ index ] ;
}
if ( ! buildUtils . isDev ( ) ) {
const index = config . networks . findIndex ( c => c . shortcut === 'trop' ) ;
delete config . networks [ index ] ;
}
const ERC20Abi = await httpRequest ( Erc20AbiJSON , 'json' ) ;
const ERC20Abi = await httpRequest ( Erc20AbiJSON , 'json' ) ;
window . addEventListener ( 'storage' , ( event ) => {
dispatch ( update ( event ) ) ;
} ) ;
window . addEventListener ( 'storage' , ( event ) => {
dispatch ( update ( event ) ) ;
} ) ;
// validate version
const version : ? string = get ( 'version' ) ;
if ( version !== VERSION ) {
// validate version
const version : ? string = get ( 'version' ) ;
if ( version !== VERSION ) {
try {
window . localStorage . clear ( ) ;
dispatch ( save ( 'version' , VERSION ) ) ;
window . sessionStorage . clear ( ) ;
} catch ( error ) {
// empty
}
set ( 'version' , VERSION ) ;
}
// 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 ( { } ) ) ;
const devices : ? string = get ( 'devices' ) ;
if ( devices ) {
dispatch ( {
type : CONNECT . DEVICE _FROM _STORAGE ,
payload : JSON . parse ( devices ) ,
} ) ;
}
// 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 ( { } ) ) ;
const accounts : ? string = get ( 'accounts' ) ;
if ( accounts ) {
dispatch ( {
type : ACCOUNT . FROM _STORAGE ,
payload : JSON . parse ( accounts ) ,
} ) ;
}
dispatch ( {
type : STORAGE . READY ,
config ,
tokens ,
ERC20Abi ,
} ) ;
} catch ( error ) {
dispatch ( {
type : STORAGE . ERROR ,
error ,
} ) ;
}
} ;
const userTokens : ? string = get ( 'tokens' ) ;
if ( userTokens ) {
dispatch ( {
type : TOKEN . FROM _STORAGE ,
payload : JSON . parse ( userTokens ) ,
} ) ;
}
const pending : ? string = get ( 'pending' ) ;
if ( pending ) {
dispatch ( {
type : PENDING . FROM _STORAGE ,
payload : JSON . parse ( pending ) ,
} ) ;
}
const loadStorageData = ( ) : ThunkAction => ( dispatch : Dispatch ) : void => {
const devices : ? string = get ( 'devices' ) ;
if ( devices ) {
dispatch ( {
type : CONNECT . DEVICE _FROM _STORAGE ,
payload : JSON . parse ( devices ) ,
} ) ;
}
const discovery : ? string = get ( 'discovery ') ;
if ( discovery ) {
dispatch ( {
type : DISCOVERY . FROM _STORAGE ,
payload : JSON . parse ( discovery ) ,
} ) ;
}
const accounts : ? string = get ( 'accounts ') ;
if ( accounts ) {
dispatch ( {
type : ACCOUNT . FROM _STORAGE ,
payload : JSON . parse ( accounts ) ,
} ) ;
}
const userTokens : ? string = get ( 'tokens' ) ;
if ( userTokens ) {
dispatch ( {
type : TOKEN . FROM _STORAGE ,
payload : JSON . parse ( userTokens ) ,
} ) ;
}
const pending : ? string = get ( 'pending' ) ;
if ( pending ) {
dispatch ( {
type : PENDING . FROM _STORAGE ,
payload : JSON . parse ( pending ) ,
} ) ;
}
const discovery : ? string = get ( 'discovery' ) ;
if ( discovery ) {
dispatch ( {
type : DISCOVERY . FROM _STORAGE ,
payload : JSON . parse ( discovery ) ,
} ) ;
}
if ( buildUtils . isDev ( ) || buildUtils . isBeta ( ) ) {
const betaModal = get ( '/betaModalPrivacy' ) ;
if ( ! betaModal ) {
dispatch ( {
type : STORAGE . READY ,
config ,
tokens ,
ERC20Abi ,
} ) ;
} catch ( error ) {
dispatch ( {
type : STORAGE . ERROR ,
error ,
type : WALLET . SHOW _BETA _DISCLAIMER ,
show : true ,
} ) ;
}
} ;
}
export const loadData = ( ) : ThunkAction => ( dispatch : Dispatch ) : void => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
dispatch ( loadTokensFromJSON ( ) ) ;
}
} ;
export const save = ( key : string , value : string ) : ThunkAction => ( ) : void => {
if ( typeof window . localStorage !== 'undefined' ) {
try {
window . localStorage . setItem ( key , value ) ;
} catch ( error ) {
// available = false;
console . error ( ` Local Storage ERROR: ${ error } ` ) ;
}
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 ( ) ) ;
}
} ;
} ;
export const hideBetaDisclaimer = ( ) : ThunkAction => ( dispatch : Dispatch ) : void => {
set ( '/betaModalPrivacy' , true ) ;
dispatch ( loadJSON ( ) ) ;
} ;