load safes from localstorage on app load

This commit is contained in:
Mikhail Mikheev 2019-04-04 17:35:32 +04:00
parent 01baabc7cb
commit e33c90c230
16 changed files with 2197 additions and 31736 deletions

30184
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -142,7 +142,7 @@
"ethereumjs-abi": "^0.6.7", "ethereumjs-abi": "^0.6.7",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
"flow-bin": "^0.95.1", "flow-bin": "0.96.0",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"html-loader": "^0.5.5", "html-loader": "^0.5.5",
"html-webpack-plugin": "^3.0.4", "html-webpack-plugin": "^3.0.4",

View File

@ -4,5 +4,9 @@ import 'babel-polyfill'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import Root from '~/components/Root' import Root from '~/components/Root'
import { store } from '~/store'
import loadSafesFromStorage from '~/routes/safe/store/actions/loadSafesFromStorage'
store.dispatch(loadSafesFromStorage())
ReactDOM.render(<Root />, document.getElementById('root')) ReactDOM.render(<Root />, document.getElementById('root'))

View File

@ -1,7 +1,7 @@
// @flow // @flow
import { type Owner } from '~/routes/safe/store/model/owner' import { type Owner } from '~/routes/safe/store/model/owner'
import { List, Map } from 'immutable' import { List, Map } from 'immutable'
import { storage, loadFromStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage } from '~/utils/storage'
export const SAFES_KEY = 'SAFES' export const SAFES_KEY = 'SAFES'
export const TX_KEY = 'TX' export const TX_KEY = 'TX'
@ -19,8 +19,7 @@ export const getSafeName = async (safeAddress: string) => {
export const saveSafes = async (safes: Object) => { export const saveSafes = async (safes: Object) => {
try { try {
const serializedState = JSON.stringify(safes) await saveToStorage(SAFES_KEY, safes)
await storage.set(SAFES_KEY, serializedState)
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error storing safe info in localstorage') console.log('Error storing safe info in localstorage')
@ -30,8 +29,7 @@ export const saveSafes = async (safes: Object) => {
export const setOwners = async (safeAddress: string, owners: List<Owner>) => { export const setOwners = async (safeAddress: string, owners: List<Owner>) => {
try { try {
const ownersAsMap = Map(owners.map((owner: Owner) => [owner.get('address').toLowerCase(), owner.get('name')])) const ownersAsMap = Map(owners.map((owner: Owner) => [owner.get('address').toLowerCase(), owner.get('name')]))
const serializedState = JSON.stringify(ownersAsMap) await saveToStorage(`${OWNERS_KEY}-${safeAddress}`, ownersAsMap)
await storage.set(`${OWNERS_KEY}-${safeAddress}`, serializedState)
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error storing owners in localstorage') console.log('Error storing owners in localstorage')

View File

@ -1,7 +1,7 @@
// @flow // @flow
import { List } from 'immutable' import { List } from 'immutable'
import { type Token, type TokenProps } from '~/logic/tokens/store/model/token' import { type Token, type TokenProps } from '~/logic/tokens/store/model/token'
import { storage, loadFromStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage } from '~/utils/storage'
export const ACTIVE_TOKENS_KEY = 'ACTIVE_TOKENS' export const ACTIVE_TOKENS_KEY = 'ACTIVE_TOKENS'
export const TOKENS_KEY = 'TOKENS' export const TOKENS_KEY = 'TOKENS'
@ -11,9 +11,8 @@ const getTokensKey = (safeAddress: string) => `${TOKENS_KEY}-${safeAddress}`
export const setActiveTokens = async (safeAddress: string, tokens: List<TokenProps>) => { export const setActiveTokens = async (safeAddress: string, tokens: List<TokenProps>) => {
try { try {
const serializedState = JSON.stringify(tokens.toJS())
const key = getActiveTokensKey(safeAddress) const key = getActiveTokensKey(safeAddress)
await storage.set(key, serializedState) await saveToStorage(key, tokens.toJS())
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error storing tokens in localstorage') console.log('Error storing tokens in localstorage')
@ -38,9 +37,8 @@ export const setToken = async (safeAddress: string, token: Token) => {
const data: List<TokenProps> = await getTokens(safeAddress) const data: List<TokenProps> = await getTokens(safeAddress)
try { try {
const serializedState = JSON.stringify(data.push(token))
const key = getTokensKey(safeAddress) const key = getTokensKey(safeAddress)
await storage.set(key, serializedState) await saveToStorage(key, data.push(token))
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error adding token in localstorage') console.log('Error adding token in localstorage')
@ -52,9 +50,8 @@ export const removeTokenFromStorage = async (safeAddress: string, token: Token)
try { try {
const index = data.indexOf(token) const index = data.indexOf(token)
const serializedState = JSON.stringify(data.remove(index))
const key = getTokensKey(safeAddress) const key = getTokensKey(safeAddress)
await storage.set(key, serializedState) await saveToStorage(key, data.remove(index))
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error removing token in localstorage') console.log('Error removing token in localstorage')

View File

@ -20,7 +20,7 @@ type ActionReturn = {
safe: Safe, safe: Safe,
} }
const addSafe = createAction<string, *, *>( export const addSafe = createAction<string, *, *>(
ADD_SAFE, ADD_SAFE,
(safe: Safe): ActionReturn => ({ (safe: Safe): ActionReturn => ({
safe, safe,

View File

@ -8,7 +8,7 @@ import { makeTransaction, type Transaction } from '~/routes/safe/store/model/tra
import { makeConfirmation } from '~/routes/safe/store/model/confirmation' import { makeConfirmation } from '~/routes/safe/store/model/confirmation'
import { loadSafeSubjects } from '~/utils/storage/transactions' import { loadSafeSubjects } from '~/utils/storage/transactions'
import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory' import { buildTxServiceUrlFrom, type TxServiceType } from '~/logic/safe/safeTxHistory'
import { getOwners } from '~/utils/storage' import { getOwners } from '~/logic/safe/utils'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import addTransactions from './addTransactions' import addTransactions from './addTransactions'

View File

@ -0,0 +1,25 @@
// @flow
import type { Dispatch as ReduxDispatch } from 'redux'
import { type GlobalState } from '~/store/index'
import { SAFES_KEY } from '~/logic/safe/utils'
import { type SafeProps } from '~/routes/safe/store/model/safe'
import { loadFromStorage } from '~/utils/storage'
import { addSafe } from './addSafe'
import { buildSafe } from '../reducer/safe';
export default () => async (dispatch: ReduxDispatch<GlobalState>) => {
try {
const safes: ?{ [string]: SafeProps } = await loadFromStorage(SAFES_KEY)
if (safes) {
Object.values(safes).forEach((safeProps: SafeProps) => {
dispatch(addSafe(buildSafe(safeProps)))
})
}
} catch (err) {
// eslint-disable-next-line
console.error('Error while getting safes from storage:', err)
}
return Promise.resolve()
}

View File

@ -45,8 +45,8 @@ const buildSafesFrom = (loadedSafes: Object): Map<string, Safe> => {
} }
} }
export const safesInitialState = (): State => { export const safesInitialState = async (): Promise<State> => {
const storedSafes = loadFromStorage(SAFES_KEY) const storedSafes = await loadFromStorage(SAFES_KEY)
const safes = storedSafes ? buildSafesFrom(storedSafes) : Map() const safes = storedSafes ? buildSafesFrom(storedSafes) : Map()
return safes return safes

View File

@ -18,4 +18,4 @@ const SafeList = ({ safes, provider }: Props) => (
</Page> </Page>
) )
export default connect(selector)(SafeList) export default connect<*, *, *, *>(selector)(SafeList)

View File

@ -6,7 +6,7 @@ import {
} from 'redux' } from 'redux'
import thunk from 'redux-thunk' import thunk from 'redux-thunk'
import provider, { PROVIDER_REDUCER_ID, type State as ProviderState } from '~/logic/wallets/store/reducer/provider' import provider, { PROVIDER_REDUCER_ID, type State as ProviderState } from '~/logic/wallets/store/reducer/provider'
import safe, { SAFE_REDUCER_ID, type State as SafeState, safesInitialState } from '~/routes/safe/store/reducer/safe' import safe, { SAFE_REDUCER_ID, type State as SafeState } from '~/routes/safe/store/reducer/safe'
import tokens, { TOKEN_REDUCER_ID, type State as TokensState } from '~/logic/tokens/store/reducer/tokens' import tokens, { TOKEN_REDUCER_ID, type State as TokensState } from '~/logic/tokens/store/reducer/tokens'
import transactions, { import transactions, {
type State as TransactionsState, type State as TransactionsState,
@ -36,10 +36,6 @@ const reducers: Reducer<GlobalState> = combineReducers({
[TRANSACTIONS_REDUCER_ID]: transactions, [TRANSACTIONS_REDUCER_ID]: transactions,
}) })
const initialState = { export const store: Store<GlobalState> = createStore(reducers, finalCreateStore)
[SAFE_REDUCER_ID]: safesInitialState(),
}
export const store: Store<GlobalState> = createStore(reducers, initialState, finalCreateStore)
export const aNewStore = (localState?: Object): Store<GlobalState> => createStore(reducers, localState, finalCreateStore) export const aNewStore = (localState?: Object): Store<GlobalState> => createStore(reducers, localState, finalCreateStore)

View File

@ -40,7 +40,7 @@ describe('Safe - redux load safe', () => {
expect(safe.get('address')).toBe(safeAddress) expect(safe.get('address')).toBe(safeAddress)
expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'UNKNOWN', address: accounts[0] })])) expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'UNKNOWN', address: accounts[0] })]))
expect(safesInitialState()).toEqual(safes) expect(await safesInitialState()).toEqual(safes)
}) })
it('if safe is not present but owners, store and persist it with stored names', async () => { it('if safe is not present but owners, store and persist it with stored names', async () => {
@ -64,7 +64,7 @@ describe('Safe - redux load safe', () => {
expect(safe.get('address')).toBe(safeAddress) expect(safe.get('address')).toBe(safeAddress)
expect(safe.get('owners')).toEqual(List([makeOwner({ name: ownerName, address: accounts[0] })])) expect(safe.get('owners')).toEqual(List([makeOwner({ name: ownerName, address: accounts[0] })]))
expect(safesInitialState()).toEqual(safes) expect(await safesInitialState()).toEqual(safes)
}) })
it('if safe is present but no owners, store and persist it with default names', async () => { it('if safe is present but no owners, store and persist it with default names', async () => {
@ -86,7 +86,7 @@ describe('Safe - redux load safe', () => {
expect(safe.get('address')).toBe(safeAddress) expect(safe.get('address')).toBe(safeAddress)
expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'UNKNOWN', address: accounts[0] })])) expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'UNKNOWN', address: accounts[0] })]))
expect(safesInitialState()).toEqual(safes) expect(await safesInitialState()).toEqual(safes)
}) })
it('if safe is present but owners, store and persist it with stored names', async () => { it('if safe is present but owners, store and persist it with stored names', async () => {
@ -107,6 +107,6 @@ describe('Safe - redux load safe', () => {
expect(safe.get('address')).toBe(safeAddress) expect(safe.get('address')).toBe(safeAddress)
expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'Adol 1 Eth Account', address: accounts[0] })])) expect(safe.get('owners')).toEqual(List([makeOwner({ name: 'Adol 1 Eth Account', address: accounts[0] })]))
expect(safesInitialState()).toEqual(safes) expect(await safesInitialState()).toEqual(safes)
}) })
}) })

View File

@ -10,20 +10,22 @@ const PREFIX = 'v1'
export const loadFromStorage = async (key: string): Promise<*> => { export const loadFromStorage = async (key: string): Promise<*> => {
try { try {
const serializedState = await storage.get(`${PREFIX}__${key}`) const stringifiedValue = await storage.get(`${PREFIX}__${key}`)
if (serializedState === null || serializedState === undefined) { if (stringifiedValue === null || stringifiedValue === undefined) {
return undefined return undefined
} }
return JSON.parse(serializedState) return JSON.parse(stringifiedValue)
} catch (err) { } catch (err) {
console.error(`Failed to load ${key} from storage:`, err)
return undefined return undefined
} }
} }
export const saveInStorage = async (key: string, value: string): Promise<*> => { export const saveToStorage = async (key: string, value: *): Promise<*> => {
try { try {
await storage.set(`${PREFIX}__${key}`, value) const stringifiedValue = JSON.stringify(value)
await storage.set(`${PREFIX}__${key}`, stringifiedValue)
} catch (err) { } catch (err) {
console.error(`Failed to save ${key} in the storage:`, err) console.error(`Failed to save ${key} in the storage:`, err)
} }

View File

@ -1,6 +1,6 @@
// @flow // @flow
import { Map } from 'immutable' import { Map } from 'immutable'
import { loadFromStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage } from '~/utils/storage'
const getSignaturesKeyFrom = (safeAddress: string) => `TXS-SIGNATURES-${safeAddress}` const getSignaturesKeyFrom = (safeAddress: string) => `TXS-SIGNATURES-${safeAddress}`
@ -13,8 +13,7 @@ export const storeSignature = async (safeAddress: string, nonce: number, signatu
const existingSignatures = subjects.get(key) const existingSignatures = subjects.get(key)
const signatures = existingSignatures ? existingSignatures + signature : signature const signatures = existingSignatures ? existingSignatures + signature : signature
const updatedSubjects = subjects.set(key, signatures) const updatedSubjects = subjects.set(key, signatures)
const serializedState = JSON.stringify(updatedSubjects) await saveToStorage(signaturesKey, updatedSubjects)
localStorage.setItem(signaturesKey, serializedState)
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error storing signatures in localstorage') console.log('Error storing signatures in localstorage')

View File

@ -1,6 +1,6 @@
// @flow // @flow
import { Map } from 'immutable' import { Map } from 'immutable'
import { loadFromStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage } from '~/utils/storage'
const getSubjectKeyFrom = (safeAddress: string) => `TXS-SUBJECTS-${safeAddress}` const getSubjectKeyFrom = (safeAddress: string) => `TXS-SUBJECTS-${safeAddress}`
@ -10,8 +10,7 @@ export const storeSubject = async (safeAddress: string, nonce: number, subject:
try { try {
const updatedSubjects = subjects.set(nonce, subject) const updatedSubjects = subjects.set(nonce, subject)
const serializedState = JSON.stringify(updatedSubjects) saveToStorage(key, updatedSubjects)
localStorage.setItem(key, serializedState)
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
console.log('Error storing transaction subject in localstorage') console.log('Error storing transaction subject in localstorage')

3651
yarn.lock

File diff suppressed because it is too large Load Diff