From f64ebf99a186551f48f0fbac5138760885c27923 Mon Sep 17 00:00:00 2001 From: Fernando Date: Thu, 16 Jan 2020 14:28:28 -0300 Subject: [PATCH] Fix #381: notification shown after loading safe (#402) * Prevent notifications from showing after loading safe * Make a safe-based sessionStorage control * Show notifications for new Safes and prevent displaying oldest incoming tx for newly loaded Safe * Create `currentSession` state * Add `currentSession` to the Store * Initialize `currentSession` * Use `currentSession` to handle notifications for incoming Transactions * Remove `updateUserStatus` * Move the load of the current session from storage to app startup * Fix duplicated reducer id * Adapt notificationsMiddleware --- src/index.js | 7 ++-- .../store/actions/addViewedSafe.js | 10 ++++++ .../store/actions/loadCurrentSession.js | 8 +++++ .../actions/loadCurrentSessionFromStorage.js | 14 ++++++++ .../store/actions/updateViewedSafes.js | 8 +++++ .../store/model/currentSession.js | 14 ++++++++ .../store/reducer/currentSession.js | 32 +++++++++++++++++++ src/logic/currentSession/utils/index.js | 15 +++++++++ src/routes/safe/container/actions.js | 3 ++ src/routes/safe/container/index.jsx | 20 +++++++++--- .../middleware/notificationsMiddleware.js | 16 ++++------ src/routes/safe/store/models/safe.js | 2 ++ src/store/index.js | 7 +++- src/utils/verifyRecurringUser.js | 18 ----------- 14 files changed, 138 insertions(+), 36 deletions(-) create mode 100644 src/logic/currentSession/store/actions/addViewedSafe.js create mode 100644 src/logic/currentSession/store/actions/loadCurrentSession.js create mode 100644 src/logic/currentSession/store/actions/loadCurrentSessionFromStorage.js create mode 100644 src/logic/currentSession/store/actions/updateViewedSafes.js create mode 100644 src/logic/currentSession/store/model/currentSession.js create mode 100644 src/logic/currentSession/store/reducer/currentSession.js create mode 100644 src/logic/currentSession/utils/index.js delete mode 100644 src/utils/verifyRecurringUser.js diff --git a/src/index.js b/src/index.js index 4871d15d..f7d264ac 100644 --- a/src/index.js +++ b/src/index.js @@ -7,8 +7,8 @@ import Root from '~/components/Root' import loadActiveTokens from '~/logic/tokens/store/actions/loadActiveTokens' import loadDefaultSafe from '~/routes/safe/store/actions/loadDefaultSafe' import loadSafesFromStorage from '~/routes/safe/store/actions/loadSafesFromStorage' +import loadCurrentSessionFromStorage from '~/logic/currentSession/store/actions/loadCurrentSessionFromStorage' import { store } from '~/store' -import verifyRecurringUser from '~/utils/verifyRecurringUser' BigNumber.set({ EXPONENTIAL_AT: [-7, 255] }) @@ -20,9 +20,12 @@ if (process.env.NODE_ENV !== 'production') { // $FlowFixMe store.dispatch(loadActiveTokens()) +// $FlowFixMe store.dispatch(loadSafesFromStorage()) +// $FlowFixMe store.dispatch(loadDefaultSafe()) -verifyRecurringUser() +// $FlowFixMe +store.dispatch(loadCurrentSessionFromStorage()) const root = document.getElementById('root') diff --git a/src/logic/currentSession/store/actions/addViewedSafe.js b/src/logic/currentSession/store/actions/addViewedSafe.js new file mode 100644 index 00000000..d28529a4 --- /dev/null +++ b/src/logic/currentSession/store/actions/addViewedSafe.js @@ -0,0 +1,10 @@ +// @flow +import { type Dispatch as ReduxDispatch } from 'redux' +import { type GlobalState } from '~/store' +import updateViewedSafes from '~/logic/currentSession/store/actions/updateViewedSafes' + +const addViewedSafe = (safeAddress: string) => (dispatch: ReduxDispatch) => { + dispatch(updateViewedSafes(safeAddress)) +} + +export default addViewedSafe diff --git a/src/logic/currentSession/store/actions/loadCurrentSession.js b/src/logic/currentSession/store/actions/loadCurrentSession.js new file mode 100644 index 00000000..78dfbbb5 --- /dev/null +++ b/src/logic/currentSession/store/actions/loadCurrentSession.js @@ -0,0 +1,8 @@ +// @flow +import { createAction } from 'redux-actions' + +export const LOAD_CURRENT_SESSION = 'LOAD_CURRENT_SESSION' + +const loadCurrentSession = createAction(LOAD_CURRENT_SESSION) + +export default loadCurrentSession diff --git a/src/logic/currentSession/store/actions/loadCurrentSessionFromStorage.js b/src/logic/currentSession/store/actions/loadCurrentSessionFromStorage.js new file mode 100644 index 00000000..4ea6e474 --- /dev/null +++ b/src/logic/currentSession/store/actions/loadCurrentSessionFromStorage.js @@ -0,0 +1,14 @@ +// @flow +import { type Dispatch as ReduxDispatch } from 'redux' +import { type GlobalState } from '~/store' +import { getCurrentSessionFromStorage } from '~/logic/currentSession/utils' +import loadCurrentSession from '~/logic/currentSession/store/actions/loadCurrentSession' +import { makeCurrentSession } from '~/logic/currentSession/store/model/currentSession' + +const loadCurrentSessionFromStorage = () => async (dispatch: ReduxDispatch) => { + const currentSession = (await getCurrentSessionFromStorage()) + + dispatch(loadCurrentSession(makeCurrentSession(currentSession ? currentSession : {}))) +} + +export default loadCurrentSessionFromStorage diff --git a/src/logic/currentSession/store/actions/updateViewedSafes.js b/src/logic/currentSession/store/actions/updateViewedSafes.js new file mode 100644 index 00000000..2c4aeebf --- /dev/null +++ b/src/logic/currentSession/store/actions/updateViewedSafes.js @@ -0,0 +1,8 @@ +// @flow +import { createAction } from 'redux-actions' + +export const UPDATE_VIEWED_SAFES = 'UPDATE_VIEWED_SAFES' + +const updateViewedSafes = createAction(UPDATE_VIEWED_SAFES) + +export default updateViewedSafes diff --git a/src/logic/currentSession/store/model/currentSession.js b/src/logic/currentSession/store/model/currentSession.js new file mode 100644 index 00000000..51d82937 --- /dev/null +++ b/src/logic/currentSession/store/model/currentSession.js @@ -0,0 +1,14 @@ +// @flow +import { + Record, type RecordFactory, type RecordOf, +} from 'immutable' + +export type CurrentSessionProps = { + viewedSafes: Array, +} + +export const makeCurrentSession: RecordFactory = Record({ + viewedSafes: [], +}) + +export type CurrentSession = RecordOf diff --git a/src/logic/currentSession/store/reducer/currentSession.js b/src/logic/currentSession/store/reducer/currentSession.js new file mode 100644 index 00000000..ccc261df --- /dev/null +++ b/src/logic/currentSession/store/reducer/currentSession.js @@ -0,0 +1,32 @@ +// @flow +import { Map } from 'immutable' +import { handleActions, type ActionType } from 'redux-actions' +import { LOAD_CURRENT_SESSION } from '~/logic/currentSession/store/actions/loadCurrentSession' +import { UPDATE_VIEWED_SAFES } from '~/logic/currentSession/store/actions/updateViewedSafes' +import { saveCurrentSessionToStorage } from '~/logic/currentSession/utils' + +export const CURRENT_SESSION_REDUCER_ID = 'currentSession' + +export type State = Map + +export default handleActions( + { + [LOAD_CURRENT_SESSION]: ( + state: State, + action: ActionType, + ): State => state.merge(Map(action.payload)), + [UPDATE_VIEWED_SAFES]: (state: State, action: ActionType): State => { + const safeAddress = action.payload + + const newState = state.updateIn( + ['viewedSafes'], + (prev) => (prev.includes(safeAddress) ? prev : [...prev, safeAddress]), + ) + + saveCurrentSessionToStorage(newState) + + return newState + }, + }, + Map(), +) diff --git a/src/logic/currentSession/utils/index.js b/src/logic/currentSession/utils/index.js new file mode 100644 index 00000000..d0848a9b --- /dev/null +++ b/src/logic/currentSession/utils/index.js @@ -0,0 +1,15 @@ +// @flow +import type { CurrentSession, CurrentSessionProps } from '~/logic/currentSession/store/model/currentSession' +import { loadFromStorage, saveToStorage } from '~/utils/storage' + +const CURRENT_SESSION_STORAGE_KEY = 'CURRENT_SESSION' + +export const getCurrentSessionFromStorage = async (): Promise => loadFromStorage(CURRENT_SESSION_STORAGE_KEY) + +export const saveCurrentSessionToStorage = async (currentSession: CurrentSession): Promise<*> => { + try { + await saveToStorage(CURRENT_SESSION_STORAGE_KEY, currentSession.toJSON()) + } catch (err) { + console.error('Error storing currentSession in localStorage', err) + } +} diff --git a/src/routes/safe/container/actions.js b/src/routes/safe/container/actions.js index cb73d576..b1656d72 100644 --- a/src/routes/safe/container/actions.js +++ b/src/routes/safe/container/actions.js @@ -11,6 +11,7 @@ import fetchCurrencyValues from '~/logic/currencyValues/store/actions/fetchCurre import activateTokensByBalance from '~/logic/tokens/store/actions/activateTokensByBalance' import loadAddressBookFromStorage from '~/logic/addressBook/store/actions/loadAddressBookFromStorage' import { updateAddressBookEntry } from '~/logic/addressBook/store/actions/updateAddressBookEntry' +import addViewedSafe from '~/logic/currentSession/store/actions/addViewedSafe' export type Actions = { fetchSafe: typeof fetchSafe, @@ -26,6 +27,7 @@ export type Actions = { fetchCurrencyValues: typeof fetchCurrencyValues, loadAddressBook: typeof loadAddressBookFromStorage, updateAddressBookEntry: typeof updateAddressBookEntry, + addViewedSafe: typeof addViewedSafe, } export default { @@ -42,4 +44,5 @@ export default { checkAndUpdateSafeOwners: checkAndUpdateSafe, loadAddressBook: loadAddressBookFromStorage, updateAddressBookEntry, + addViewedSafe, } diff --git a/src/routes/safe/container/index.jsx b/src/routes/safe/container/index.jsx index db2c8b6b..f5248916 100644 --- a/src/routes/safe/container/index.jsx +++ b/src/routes/safe/container/index.jsx @@ -34,13 +34,23 @@ class SafeView extends React.Component { componentDidMount() { const { - fetchSafe, activeTokens, safeUrl, fetchTokenBalances, fetchTokens, fetchTransactions, fetchCurrencyValues, loadAddressBook, + fetchSafe, + activeTokens, + safeUrl, + fetchTokenBalances, + fetchTokens, + fetchTransactions, + fetchCurrencyValues, + loadAddressBook, + addViewedSafe, } = this.props - fetchSafe(safeUrl).then(() => { - // The safe needs to be loaded before fetching the transactions - fetchTransactions(safeUrl) - }) + fetchSafe(safeUrl) + .then(() => { + // The safe needs to be loaded before fetching the transactions + fetchTransactions(safeUrl) + addViewedSafe(safeUrl) + }) fetchTokenBalances(safeUrl, activeTokens) // fetch tokens there to get symbols for tokens in TXs list fetchTokens() diff --git a/src/routes/safe/store/middleware/notificationsMiddleware.js b/src/routes/safe/store/middleware/notificationsMiddleware.js index 4ccc0398..08555d56 100644 --- a/src/routes/safe/store/middleware/notificationsMiddleware.js +++ b/src/routes/safe/store/middleware/notificationsMiddleware.js @@ -12,9 +12,6 @@ import { enhanceSnackbarForAction, NOTIFICATIONS } from '~/logic/notifications' import closeSnackbarAction from '~/logic/notifications/store/actions/closeSnackbar' import { getIncomingTxAmount } from '~/routes/safe/components/Transactions/TxsTable/columns' import updateSafe from '~/routes/safe/store/actions/updateSafe' -import { loadFromStorage } from '~/utils/storage' -import { SAFES_KEY } from '~/logic/safe/utils' -import { RECURRING_USER_KEY } from '~/utils/verifyRecurringUser' import { safesMapSelector } from '~/routes/safe/store/selectors' import { isUserOwner } from '~/logic/wallets/ethAddresses' @@ -69,17 +66,16 @@ const notificationsMiddleware = (store: Store) => ( break } case ADD_INCOMING_TRANSACTIONS: { - action.payload.forEach(async (incomingTransactions, safeAddress) => { - const storedSafes = await loadFromStorage(SAFES_KEY) - const latestIncomingTxBlock = storedSafes - ? storedSafes[safeAddress].latestIncomingTxBlock - : 0 + action.payload.forEach((incomingTransactions, safeAddress) => { + const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress) + const viewedSafes = state.currentSession ? state.currentSession.get('viewedSafes') : [] + const recurringUser = viewedSafes.includes(safeAddress) const newIncomingTransactions = incomingTransactions.filter( (tx) => tx.blockNumber > latestIncomingTxBlock, ) + const { message, ...TX_INCOMING_MSG } = NOTIFICATIONS.TX_INCOMING_MSG - const recurringUser = await loadFromStorage(RECURRING_USER_KEY) if (recurringUser) { if (newIncomingTransactions.size > 3) { @@ -109,7 +105,7 @@ const notificationsMiddleware = (store: Store) => ( updateSafe({ address: safeAddress, latestIncomingTxBlock: newIncomingTransactions.size - ? newIncomingTransactions.last().blockNumber + ? newIncomingTransactions.first().blockNumber : latestIncomingTxBlock, }), ) diff --git a/src/routes/safe/store/models/safe.js b/src/routes/safe/store/models/safe.js index 63bebb1b..2f83e673 100644 --- a/src/routes/safe/store/models/safe.js +++ b/src/routes/safe/store/models/safe.js @@ -16,6 +16,7 @@ export type SafeProps = { ethBalance?: string, nonce: number, latestIncomingTxBlock: number, + recurringUser?: boolean, } const SafeRecord: RecordFactory = Record({ @@ -29,6 +30,7 @@ const SafeRecord: RecordFactory = Record({ balances: Map({}), nonce: 0, latestIncomingTxBlock: 0, + recurringUser: undefined, }) export type Safe = RecordOf diff --git a/src/store/index.js b/src/store/index.js index ae00a5b5..85b05395 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -27,7 +27,10 @@ import cookies, { COOKIES_REDUCER_ID } from '~/logic/cookies/store/reducer/cooki import notificationsMiddleware from '~/routes/safe/store/middleware/notificationsMiddleware' import addressBook, { ADDRESS_BOOK_REDUCER_ID } from '~/logic/addressBook/store/reducer/addressBook' import addressBookMiddleware from '~/logic/addressBook/store/middleware/addressBookMiddleware' - +import currentSession, { + CURRENT_SESSION_REDUCER_ID, + type State as CurrentSessionState, +} from '~/logic/currentSession/store/reducer/currentSession' export const history = createBrowserHistory() @@ -44,6 +47,7 @@ export type GlobalState = { transactions: TransactionsState, incomingTransactions: IncomingTransactionsState, notifications: NotificationsState, + currentSession: CurrentSessionState, } export type GetState = () => GlobalState @@ -59,6 +63,7 @@ const reducers: Reducer = combineReducers({ [CURRENCY_VALUES_KEY]: currencyValues, [COOKIES_REDUCER_ID]: cookies, [ADDRESS_BOOK_REDUCER_ID]: addressBook, + [CURRENT_SESSION_REDUCER_ID]: currentSession, }) export const store: Store = createStore(reducers, finalCreateStore) diff --git a/src/utils/verifyRecurringUser.js b/src/utils/verifyRecurringUser.js deleted file mode 100644 index 6c0456c9..00000000 --- a/src/utils/verifyRecurringUser.js +++ /dev/null @@ -1,18 +0,0 @@ -// @flow -import { loadFromStorage, saveToStorage } from '~/utils/storage' - -export const RECURRING_USER_KEY = 'RECURRING_USER' - -const verifyRecurringUser = async () => { - const recurringUser = await loadFromStorage(RECURRING_USER_KEY) - - if (recurringUser === undefined) { - await saveToStorage(RECURRING_USER_KEY, false) - } - - if (recurringUser === false) { - await saveToStorage(RECURRING_USER_KEY, true) - } -} - -export default verifyRecurringUser