import { AnyAction, Action } from 'redux'; import { PayloadAction } from '@reduxjs/toolkit'; import { WebHIDConnectedStatuses, HardwareTransportStates, } from '../../../shared/constants/hardware-wallets'; import * as actionConstants from '../../store/actionConstants'; interface AppState { shouldClose: boolean; menuOpen: boolean; modal: { open: boolean; modalState: { name: string | null; props: Record; }; previousModalState: { name: string | null; }; }; alertOpen: boolean; alertMessage: string | null; qrCodeData: { type?: string | null; values?: { address?: string | null }; } | null; networkDropdownOpen: boolean; accountDetail: { subview?: string; accountExport?: string; privateKey?: string; }; isLoading: boolean; loadingMessage: string | null; scrollToBottom: boolean; warning: string | null | undefined; buyView: Record; isMouseUser: boolean; defaultHdPaths: { trezor: string; ledger: string; lattice: string; }; networksTabSelectedRpcUrl: string | null; requestAccountTabs: Record; // [url.origin]: tab.id openMetaMaskTabs: Record; // openMetamaskTabsIDs[tab.id]): true/false currentWindowTab: Record; // tabs.tab https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab showWhatsNewPopup: boolean; showTermsOfUsePopup: boolean; singleExceptions: { testKey: string | null; }; gasLoadingAnimationIsShowing: boolean; smartTransactionsError: string | null; smartTransactionsErrorMessageDismissed: boolean; ledgerWebHidConnectedStatus: WebHIDConnectedStatuses; ledgerTransportStatus: HardwareTransportStates; newNftAddedMessage: string; removeNftMessage: string; newNetworkAddedName: string; newNetworkAddedConfigurationId: string; selectedNetworkConfigurationId: string; sendInputCurrencySwitched: boolean; newTokensImported: string; onboardedInThisUISession: boolean; customTokenAmount: string; txId: number | null; accountDetailsAddress: string; ///: BEGIN:ONLY_INCLUDE_IN(snaps) snapsInstallPrivacyWarningShown: boolean; ///: END:ONLY_INCLUDE_IN } interface AppSliceState { appState: AppState; } // default state const initialState: AppState = { shouldClose: false, menuOpen: false, modal: { open: false, modalState: { name: null, props: {}, }, previousModalState: { name: null, }, }, alertOpen: false, alertMessage: null, qrCodeData: null, networkDropdownOpen: false, accountDetail: { privateKey: '', }, // Used to display loading indicator isLoading: false, loadingMessage: null, // Used to display error text warning: null, buyView: {}, isMouseUser: false, defaultHdPaths: { trezor: `m/44'/60'/0'/0`, ledger: `m/44'/60'/0'/0/0`, lattice: `m/44'/60'/0'/0`, }, networksTabSelectedRpcUrl: '', requestAccountTabs: {}, openMetaMaskTabs: {}, currentWindowTab: {}, showWhatsNewPopup: true, showTermsOfUsePopup: true, singleExceptions: { testKey: null, }, gasLoadingAnimationIsShowing: false, smartTransactionsError: null, smartTransactionsErrorMessageDismissed: false, ledgerWebHidConnectedStatus: WebHIDConnectedStatuses.unknown, ledgerTransportStatus: HardwareTransportStates.none, newNftAddedMessage: '', removeNftMessage: '', newNetworkAddedName: '', newNetworkAddedConfigurationId: '', selectedNetworkConfigurationId: '', sendInputCurrencySwitched: false, newTokensImported: '', onboardedInThisUISession: false, customTokenAmount: '', scrollToBottom: true, txId: null, accountDetailsAddress: '', ///: BEGIN:ONLY_INCLUDE_IN(snaps) snapsInstallPrivacyWarningShown: false, ///: END:ONLY_INCLUDE_IN }; export default function reduceApp( state: AppState, action: AnyAction, ): AppState { const appState: AppState = { ...initialState, ...state, }; switch (action.type) { // dropdown methods case actionConstants.NETWORK_DROPDOWN_OPEN: return { ...appState, networkDropdownOpen: true, }; case actionConstants.NETWORK_DROPDOWN_CLOSE: return { ...appState, networkDropdownOpen: false, }; // alert methods case actionConstants.ALERT_OPEN: return { ...appState, alertOpen: true, alertMessage: action.payload, }; case actionConstants.ALERT_CLOSE: return { ...appState, alertOpen: false, alertMessage: null, }; case actionConstants.SET_ACCOUNT_DETAILS_ADDRESS: { return { ...appState, accountDetailsAddress: action.payload, }; } // qr scanner methods case actionConstants.QR_CODE_DETECTED: return { ...appState, qrCodeData: action.value, }; // Smart Transactions errors. case actionConstants.SET_SMART_TRANSACTIONS_ERROR: return { ...appState, smartTransactionsError: action.payload, }; case actionConstants.DISMISS_SMART_TRANSACTIONS_ERROR_MESSAGE: return { ...appState, smartTransactionsErrorMessageDismissed: true, }; // modal methods: case actionConstants.MODAL_OPEN: { const { name, ...modalProps } = action.payload; return { ...appState, modal: { open: true, modalState: { name, props: { ...modalProps }, }, previousModalState: { ...appState.modal.modalState }, }, }; } case actionConstants.MODAL_CLOSE: return { ...appState, modal: Object.assign( appState.modal, { open: false }, { modalState: { name: null, props: {} } }, { previousModalState: appState.modal.modalState }, ), }; case actionConstants.CLEAR_ACCOUNT_DETAILS: return { ...appState, accountDetail: { privateKey: '', }, }; case actionConstants.SHOW_SEND_TOKEN_PAGE: return { ...appState, warning: null, }; case actionConstants.LOCK_METAMASK: return { ...appState, warning: null, }; // accounts case actionConstants.GO_HOME: return { ...appState, accountDetail: { privateKey: '', }, warning: null, }; case actionConstants.SHOW_ACCOUNTS_PAGE: return { ...appState, isLoading: false, warning: null, scrollToBottom: false, }; case actionConstants.SHOW_CONF_TX_PAGE: return { ...appState, txId: action.id, warning: null, isLoading: false, }; case actionConstants.COMPLETED_TX: return { ...appState, warning: null, txId: null, }; case actionConstants.UNLOCK_FAILED: return { ...appState, warning: action.value || 'Incorrect password. Try again.', }; case actionConstants.UNLOCK_SUCCEEDED: return { ...appState, warning: '', }; case actionConstants.SET_HARDWARE_WALLET_DEFAULT_HD_PATH: { const { device, path } = action.payload; const newDefaults = { ...appState.defaultHdPaths } as any; newDefaults[device] = path; return { ...appState, defaultHdPaths: newDefaults, }; } case actionConstants.SHOW_LOADING: return { ...appState, isLoading: true, loadingMessage: action.payload, }; case actionConstants.HIDE_LOADING: return { ...appState, isLoading: false, }; case actionConstants.DISPLAY_WARNING: return { ...appState, warning: action.payload, isLoading: false, }; case actionConstants.HIDE_WARNING: return { ...appState, warning: undefined, }; case actionConstants.SHOW_PRIVATE_KEY: return { ...appState, accountDetail: { privateKey: action.payload, }, }; case actionConstants.SET_MOUSE_USER_STATE: return { ...appState, isMouseUser: action.payload, }; case actionConstants.SET_SELECTED_NETWORK_CONFIGURATION_ID: return { ...appState, selectedNetworkConfigurationId: action.payload, }; case actionConstants.SET_NEW_NETWORK_ADDED: { const { networkConfigurationId, nickname } = action.payload; return { ...appState, newNetworkAddedName: nickname, newNetworkAddedConfigurationId: networkConfigurationId, }; } case actionConstants.SET_NEW_TOKENS_IMPORTED: return { ...appState, newTokensImported: action.payload, }; case actionConstants.SET_NEW_NFT_ADDED_MESSAGE: return { ...appState, newNftAddedMessage: action.payload, }; case actionConstants.SET_REMOVE_NFT_MESSAGE: return { ...appState, removeNftMessage: action.payload, }; case actionConstants.SET_REQUEST_ACCOUNT_TABS: return { ...appState, requestAccountTabs: action.value, }; case actionConstants.SET_OPEN_METAMASK_TAB_IDS: return { ...appState, openMetaMaskTabs: action.payload, }; case actionConstants.HIDE_WHATS_NEW_POPUP: return { ...appState, showWhatsNewPopup: false, }; case actionConstants.CAPTURE_SINGLE_EXCEPTION: return { ...appState, singleExceptions: { ...appState.singleExceptions, [action.value]: null, }, }; case actionConstants.TOGGLE_GAS_LOADING_ANIMATION: return { ...appState, gasLoadingAnimationIsShowing: action.payload, }; case actionConstants.SET_WEBHID_CONNECTED_STATUS: return { ...appState, ledgerWebHidConnectedStatus: action.payload, }; case actionConstants.SET_LEDGER_TRANSPORT_STATUS: return { ...appState, ledgerTransportStatus: action.payload, }; case actionConstants.TOGGLE_CURRENCY_INPUT_SWITCH: return { ...appState, sendInputCurrencySwitched: !appState.sendInputCurrencySwitched, }; case actionConstants.ONBOARDED_IN_THIS_UI_SESSION: return { ...appState, onboardedInThisUISession: action.payload, }; case actionConstants.SET_CUSTOM_TOKEN_AMOUNT: return { ...appState, customTokenAmount: action.payload, }; default: return appState; } } // Action Creators export function hideWhatsNewPopup(): Action { return { type: actionConstants.HIDE_WHATS_NEW_POPUP, }; } export function toggleGasLoadingAnimation( payload: boolean, ): PayloadAction { return { type: actionConstants.TOGGLE_GAS_LOADING_ANIMATION, payload }; } export function setLedgerWebHidConnectedStatus( payload: WebHIDConnectedStatuses, ): PayloadAction { return { type: actionConstants.SET_WEBHID_CONNECTED_STATUS, payload }; } export function setLedgerTransportStatus( payload: HardwareTransportStates, ): PayloadAction { return { type: actionConstants.SET_LEDGER_TRANSPORT_STATUS, payload }; } export function toggleCurrencySwitch(): Action { return { type: actionConstants.TOGGLE_CURRENCY_INPUT_SWITCH }; } export function setOnBoardedInThisUISession( payload: boolean, ): PayloadAction { return { type: actionConstants.ONBOARDED_IN_THIS_UI_SESSION, payload }; } export function setCustomTokenAmount(payload: string): PayloadAction { return { type: actionConstants.SET_CUSTOM_TOKEN_AMOUNT, payload }; } // Selectors export function getQrCodeData(state: AppSliceState): { type?: string | null; values?: { address?: string | null }; } | null { return state.appState.qrCodeData; } export function getGasLoadingAnimationIsShowing(state: AppSliceState): boolean { return state.appState.gasLoadingAnimationIsShowing; } export function getLedgerWebHidConnectedStatus( state: AppSliceState, ): string | null { return state.appState.ledgerWebHidConnectedStatus; } export function getLedgerTransportStatus(state: AppSliceState): string | null { return state.appState.ledgerTransportStatus; }