From 04094eb15636c1bb4cebd974cae4242564870d2a Mon Sep 17 00:00:00 2001 From: Kristof Csillag Date: Sat, 24 Sep 2022 00:34:05 +0200 Subject: [PATCH 1/4] Add a settings dialog, with "dangerous mode" setting Also, in modals, improve handling of danger, and add delays. --- src/app/components/Modal/index.tsx | 84 ++++++++++++++++--- src/app/components/SettingsButton/index.tsx | 22 +++++ src/app/components/SettingsDialog/index.tsx | 80 ++++++++++++++++++ .../components/SettingsDialog/slice/index.ts | 22 +++++ .../SettingsDialog/slice/selectors.ts | 8 ++ .../components/SettingsDialog/slice/types.ts | 3 + .../__snapshots__/index.test.tsx.snap | 33 ++++++++ src/app/components/Sidebar/index.tsx | 2 + src/locales/en/translation.json | 15 ++++ src/store/reducers.ts | 2 + src/types/RootState.ts | 2 + 11 files changed, 263 insertions(+), 10 deletions(-) create mode 100644 src/app/components/SettingsButton/index.tsx create mode 100644 src/app/components/SettingsDialog/index.tsx create mode 100644 src/app/components/SettingsDialog/slice/index.ts create mode 100644 src/app/components/SettingsDialog/slice/selectors.ts create mode 100644 src/app/components/SettingsDialog/slice/types.ts diff --git a/src/app/components/Modal/index.tsx b/src/app/components/Modal/index.tsx index 31ba5f9275..fd1d313c75 100644 --- a/src/app/components/Modal/index.tsx +++ b/src/app/components/Modal/index.tsx @@ -1,14 +1,30 @@ -import { createContext, useCallback, useContext, useState } from 'react' -import { Box, Button, Layer, Paragraph } from 'grommet' +import { createContext, useCallback, useContext, useEffect, useState } from 'react' +import { Box, Button, Layer, Heading, Paragraph } from 'grommet' import { useTranslation } from 'react-i18next' import { Alert, Checkmark, Close } from 'grommet-icons' import { ModalHeader } from 'app/components/Header' +import { AlertBox } from '../AlertBox' +import { selectAllowDangerousSetting } from '../SettingsDialog/slice/selectors' +import { useSelector } from 'react-redux' interface Modal { title: string description: string handleConfirm: () => void + + /** + * Is this a dangerous operation? + * + * If marked as such, it will only be possible to execute it if the wallet is configured to run in dangerous mode. + * + * It also automatically implies a mandatory waiting time of 10 sec, unless specified otherwise. + */ isDangerous: boolean + + /** + * How long does the user have to wait before he can actually confirm the action? + */ + mustWaitSecs?: number } interface ModalContainerProps { @@ -33,27 +49,75 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => { modal.handleConfirm() closeModal() }, [closeModal, modal]) + const { isDangerous, mustWaitSecs } = modal + const allowDangerous = useSelector(selectAllowDangerousSetting) + const forbidden = isDangerous && !allowDangerous + const waitingTime = forbidden + ? 0 // If the action is forbidden, there is nothing to wait for + : isDangerous + ? mustWaitSecs ?? 10 // For dangerous actions, we require 10 seconds of waiting, unless specified otherwise. + : mustWaitSecs ?? 0 // For normal, non-dangerous operations, just use what was specified + + const [secsLeft, setSecsLeft] = useState(0) + + useEffect(() => { + if (waitingTime) { + setSecsLeft(waitingTime) + const stopCounting = () => window.clearInterval(interval) + const interval = window.setInterval( + () => + setSecsLeft(seconds => { + const remains = seconds - 1 + if (!remains) stopCounting() + return remains + }), + 1000, + ) + return stopCounting + } + }, [waitingTime]) return ( {modal.title} {modal.description} + {forbidden && ( + + {t( + 'dangerMode.youDontWantThis', + "You most probably don't want to do this, so I won't allow it. If you really do, then please enable the 'dangerous mode' in wallet settings, and try again.", + )} + + )} + {isDangerous && allowDangerous && ( + + {t( + 'dangerMode.youCanButDoYouWant', + "You most probably shouldn't do this, but since you have specifically enabled 'dangerous mode' in wallet settings, we won't stop you.", + )} + + )} + {/* This is a placeholder, so that if we have only one valid button, + the Cancel, it should be on the right side, not the left side.*/} + {!!forbidden && } +
{ const size = useContext(ResponsiveContext) @@ -211,6 +212,7 @@ const SidebarFooter = (props: SidebarFooterProps) => { + } label="GitHub" diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 74123c7725..469999d02a 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -121,6 +121,13 @@ "newMnemonic": "Generate a new mnemonic", "thisIsYourPhrase": "This is your mnemonic" }, + "dangerMode": { + "description": "Dangerous mode: should the wallet let the user shoot himself in the foot?", + "off": "Off - Refuse to execute nonsensical actions", + "on": "On - Allow executing nonsensical actions. Don't blame Oasis!", + "youCanButDoYouWant": "You most probably shouldn't do this, but since you have specifically enabled 'dangerous mode' in wallet settings, we won't stop you.", + "youDontWantThis": "You most probably don't want to do this, so I won't allow it. If you really do, then please enable the 'dangerous mode' in wallet settings, and try again." + }, "delegations": { "activeDelegations": "Active delegations", "debondingDelegations": "Debonding delegations", @@ -198,6 +205,7 @@ "menu": { "closeWallet": "Close wallet", "home": "Home", + "settings": "Settings", "stake": "Stake", "wallet": "Wallet" }, @@ -234,6 +242,13 @@ "showPrivateKey": "Show private key" } }, + "settings": { + "dialog": { + "close": "Close", + "description": "This is where you can configure the behavior of the Oasis Wallet.", + "title": "Wallet settings" + } + }, "theme": { "darkMode": "Dark mode", "lightMode": "Light mode" diff --git a/src/store/reducers.ts b/src/store/reducers.ts index eb5435e3cb..9111fd363f 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -12,6 +12,7 @@ import stakingReducer from 'app/state/staking' import transactionReducer from 'app/state/transaction' import walletReducer from 'app/state/wallet' import themeReducer from 'styles/theme/slice' +import settingReducer from 'app/components/SettingsDialog/slice' export function createReducer() { const rootReducer = combineReducers({ @@ -24,6 +25,7 @@ export function createReducer() { theme: themeReducer, transaction: transactionReducer, wallet: walletReducer, + settings: settingReducer, }) return rootReducer diff --git a/src/types/RootState.ts b/src/types/RootState.ts index f3e356a3f7..7c554adcb4 100644 --- a/src/types/RootState.ts +++ b/src/types/RootState.ts @@ -8,6 +8,7 @@ import { TransactionState } from 'app/state/transaction/types' import { ImportAccountsState } from 'app/state/importaccounts/types' import { StakingState } from 'app/state/staking/types' import { FatalErrorState } from 'app/state/fatalerror/types' +import { SettingsState } from '../app/components/SettingsDialog/slice/types' // [IMPORT NEW CONTAINERSTATE ABOVE] < Needed for generating containers seamlessly export interface RootState { @@ -20,6 +21,7 @@ export interface RootState { importAccounts: ImportAccountsState staking: StakingState fatalError: FatalErrorState + settings: SettingsState // [INSERT NEW REDUCER KEY ABOVE] < Needed for generating containers seamlessly } From 72cb2c44fe64f0dd9f5ed53757599c4ba5c692a6 Mon Sep 17 00:00:00 2001 From: Csillag Kristof Date: Wed, 19 Oct 2022 22:01:49 +0200 Subject: [PATCH 2/4] Fix Grommet icon import --- src/app/components/SettingsButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/SettingsButton/index.tsx b/src/app/components/SettingsButton/index.tsx index 81248d8fde..621cdcc1a2 100644 --- a/src/app/components/SettingsButton/index.tsx +++ b/src/app/components/SettingsButton/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { SidebarButton } from '../Sidebar' -import { Configure } from 'grommet-icons/icons' +import { Configure } from 'grommet-icons' import { useTranslation } from 'react-i18next' import { SettingsDialog } from '../SettingsDialog' From 964bf8f51e193b515c20c57dd77af825c672f3b2 Mon Sep 17 00:00:00 2001 From: Csillag Kristof Date: Wed, 19 Oct 2022 23:20:16 +0200 Subject: [PATCH 3/4] Support opening the settings dialog directly from dangerous modal dialogs --- src/app/components/Modal/index.tsx | 60 ++++++++++++++++--- src/app/components/SettingsButton/index.tsx | 21 +++++-- .../components/SettingsDialog/slice/index.ts | 4 ++ .../SettingsDialog/slice/selectors.ts | 1 + .../components/SettingsDialog/slice/types.ts | 1 + .../__snapshots__/index.test.tsx.snap | 12 ++-- 6 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/app/components/Modal/index.tsx b/src/app/components/Modal/index.tsx index fd1d313c75..e30ee22540 100644 --- a/src/app/components/Modal/index.tsx +++ b/src/app/components/Modal/index.tsx @@ -1,11 +1,12 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'react' -import { Box, Button, Layer, Heading, Paragraph } from 'grommet' +import { Box, Button, Layer, Paragraph } from 'grommet' import { useTranslation } from 'react-i18next' -import { Alert, Checkmark, Close } from 'grommet-icons' +import { Alert, Checkmark, Close, Configure } from 'grommet-icons' import { ModalHeader } from 'app/components/Header' import { AlertBox } from '../AlertBox' import { selectAllowDangerousSetting } from '../SettingsDialog/slice/selectors' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' +import { settingsActions } from '../SettingsDialog/slice' interface Modal { title: string @@ -30,11 +31,29 @@ interface Modal { interface ModalContainerProps { modal: Modal closeModal: () => void + hideModal: () => void } interface ModalContextProps { + /** + * Show a new modal + */ launchModal: (modal: Modal) => void + + /** + * Close the current modal + */ closeModal: () => void + + /** + * Hide the current modal (with the intention of showing in again later) + */ + hideModal: () => void + + /** + * Show the previously hidden modal again + */ + showModal: () => void } interface ModalProviderProps { @@ -43,7 +62,8 @@ interface ModalProviderProps { const ModalContext = createContext({} as ModalContextProps) -const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => { +const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) => { + const dispatch = useDispatch() const { t } = useTranslation() const confirm = useCallback(() => { modal.handleConfirm() @@ -59,6 +79,10 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => { : mustWaitSecs ?? 0 // For normal, non-dangerous operations, just use what was specified const [secsLeft, setSecsLeft] = useState(0) + const openSettings = () => { + hideModal() + setTimeout(() => dispatch(settingsActions.setOpen(true)), 100) + } useEffect(() => { if (waitingTime) { @@ -99,9 +123,6 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => { )} - {/* This is a placeholder, so that if we have only one valid button, - the Cancel, it should be on the right side, not the left side.*/} - {!!forbidden && }
Date: Thu, 20 Oct 2022 13:24:11 +0200 Subject: [PATCH 4/4] Refactor Modal so that - The state is handled in Redux - No more ModalProvider - pop-up in pop-up is now supported (recursively) --- .../__snapshots__/index.test.tsx.snap | 5 +- .../AddEscrowForm/__tests__/index.test.tsx | 7 +- src/app/components/AddEscrowForm/index.tsx | 5 +- src/app/components/Modal/index.tsx | 123 +++--------------- src/app/components/Modal/slice/index.ts | 63 +++++++++ src/app/components/Modal/slice/selectors.ts | 8 ++ src/app/components/Modal/slice/types.ts | 24 ++++ src/app/components/SettingsButton/index.tsx | 7 +- src/app/index.tsx | 7 +- .../Features/SendTransaction/index.tsx | 5 +- src/store/configureStore.ts | 8 +- src/store/reducers.ts | 2 + src/types/RootState.ts | 2 + 13 files changed, 142 insertions(+), 124 deletions(-) create mode 100644 src/app/components/Modal/slice/index.ts create mode 100644 src/app/components/Modal/slice/selectors.ts create mode 100644 src/app/components/Modal/slice/types.ts diff --git a/src/app/__tests__/__snapshots__/index.test.tsx.snap b/src/app/__tests__/__snapshots__/index.test.tsx.snap index 00d2482a64..c5c4c86d5e 100644 --- a/src/app/__tests__/__snapshots__/index.test.tsx.snap +++ b/src/app/__tests__/__snapshots__/index.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` should render and match the snapshot 1`] = ` - + should render and match the snapshot 1`] = ` - + + `; diff --git a/src/app/components/AddEscrowForm/__tests__/index.test.tsx b/src/app/components/AddEscrowForm/__tests__/index.test.tsx index fbe3955cec..8c72486ee5 100644 --- a/src/app/components/AddEscrowForm/__tests__/index.test.tsx +++ b/src/app/components/AddEscrowForm/__tests__/index.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { ModalProvider } from 'app/components/Modal' +import { ModalContainer } from 'app/components/Modal' import { Validator } from 'app/state/staking/types' import * as React from 'react' import { Provider } from 'react-redux' @@ -12,9 +12,8 @@ import { AddEscrowForm } from '..' const renderComponent = (store: any, address: string, validatorStatus: Validator['status']) => render( - - - + + , ) diff --git a/src/app/components/AddEscrowForm/index.tsx b/src/app/components/AddEscrowForm/index.tsx index 54447266dc..b9738bf736 100644 --- a/src/app/components/AddEscrowForm/index.tsx +++ b/src/app/components/AddEscrowForm/index.tsx @@ -3,7 +3,8 @@ * AddEscrowForm * */ -import { useModal } from 'app/components/Modal' +import { Modal } from '../Modal/slice/types' +import { modalActions } from '../Modal/slice' import { parseRoseStringToBaseUnitString } from 'app/lib/helpers' import { selectMinStaking } from 'app/state/network/selectors' import { Validator } from 'app/state/staking/types' @@ -24,7 +25,7 @@ interface Props { export const AddEscrowForm = memo((props: Props) => { const { t } = useTranslation() - const { launchModal } = useModal() + const launchModal = (modal: Modal) => dispatch(modalActions.launch(modal)) const { error, success } = useSelector(selectTransaction) const isTop20 = props.validatorRank <= 20 const [showNotice, setShowNotice] = useState(isTop20) diff --git a/src/app/components/Modal/index.tsx b/src/app/components/Modal/index.tsx index e30ee22540..4182a9915f 100644 --- a/src/app/components/Modal/index.tsx +++ b/src/app/components/Modal/index.tsx @@ -1,4 +1,4 @@ -import { createContext, useCallback, useContext, useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { Box, Button, Layer, Paragraph } from 'grommet' import { useTranslation } from 'react-i18next' import { Alert, Checkmark, Close, Configure } from 'grommet-icons' @@ -7,70 +7,22 @@ import { AlertBox } from '../AlertBox' import { selectAllowDangerousSetting } from '../SettingsDialog/slice/selectors' import { useDispatch, useSelector } from 'react-redux' import { settingsActions } from '../SettingsDialog/slice' +import { selectCurrentModal } from './slice/selectors' +import { modalActions } from './slice' -interface Modal { - title: string - description: string - handleConfirm: () => void - - /** - * Is this a dangerous operation? - * - * If marked as such, it will only be possible to execute it if the wallet is configured to run in dangerous mode. - * - * It also automatically implies a mandatory waiting time of 10 sec, unless specified otherwise. - */ - isDangerous: boolean - - /** - * How long does the user have to wait before he can actually confirm the action? - */ - mustWaitSecs?: number -} - -interface ModalContainerProps { - modal: Modal - closeModal: () => void - hideModal: () => void -} - -interface ModalContextProps { - /** - * Show a new modal - */ - launchModal: (modal: Modal) => void - - /** - * Close the current modal - */ - closeModal: () => void - - /** - * Hide the current modal (with the intention of showing in again later) - */ - hideModal: () => void - - /** - * Show the previously hidden modal again - */ - showModal: () => void -} - -interface ModalProviderProps { - children: React.ReactNode -} - -const ModalContext = createContext({} as ModalContextProps) - -const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) => { +const ModalContainer = () => { const dispatch = useDispatch() const { t } = useTranslation() - const confirm = useCallback(() => { - modal.handleConfirm() - closeModal() - }, [closeModal, modal]) - const { isDangerous, mustWaitSecs } = modal + const modal = useSelector(selectCurrentModal) const allowDangerous = useSelector(selectAllowDangerousSetting) + const [secsLeft, setSecsLeft] = useState(0) + const closeModal = () => dispatch(modalActions.close()) + const confirm = () => { + modal?.handleConfirm() + dispatch(modalActions.close()) + } + const { isDangerous, mustWaitSecs } = modal || {} + const forbidden = isDangerous && !allowDangerous const waitingTime = forbidden ? 0 // If the action is forbidden, there is nothing to wait for @@ -78,10 +30,9 @@ const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) = ? mustWaitSecs ?? 10 // For dangerous actions, we require 10 seconds of waiting, unless specified otherwise. : mustWaitSecs ?? 0 // For normal, non-dangerous operations, just use what was specified - const [secsLeft, setSecsLeft] = useState(0) const openSettings = () => { - hideModal() - setTimeout(() => dispatch(settingsActions.setOpen(true)), 100) + dispatch(modalActions.stash()) + dispatch(settingsActions.setOpen(true)) } useEffect(() => { @@ -101,6 +52,7 @@ const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) = } }, [waitingTime]) + if (!modal) return null return ( @@ -148,45 +100,4 @@ const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) = ) } -const ModalProvider = (props: ModalProviderProps) => { - const [modal, setModal] = useState(null) - const [hiddenModal, setHiddenModal] = useState(null) - const closeModal = useCallback(() => { - setModal(null) - }, []) - const hideModal = useCallback(() => { - if (!modal) { - throw new Error("You can't call hideModal if no model is shown!") - } - setHiddenModal(modal) - setModal(null) - }, [modal]) - const showModal = useCallback(() => { - if (modal) { - throw new Error("You can't call showModal when a modal is already visible!") - } - if (!hiddenModal) { - return - } - setModal(hiddenModal) - setHiddenModal(null) - }, [modal, hiddenModal]) - - return ( - - {props.children} - {modal && } - - ) -} - -const useModal = () => { - const context = useContext(ModalContext) - if (context === undefined) { - throw new Error('useModal must be used within a ModalProvider') - } - - return context -} - -export { ModalProvider, useModal } +export { ModalContainer } diff --git a/src/app/components/Modal/slice/index.ts b/src/app/components/Modal/slice/index.ts new file mode 100644 index 0000000000..5305be0f5f --- /dev/null +++ b/src/app/components/Modal/slice/index.ts @@ -0,0 +1,63 @@ +import { PayloadAction } from '@reduxjs/toolkit' +import { createSlice } from 'utils/@reduxjs/toolkit' + +import { Modal, ModalState } from './types' + +export const initialState: ModalState = { + current: null, + stash: [], +} + +const slice = createSlice({ + name: 'modal', + initialState, + reducers: { + /** + * Show a new modal + */ + launch(state, action: PayloadAction) { + if (state.current) { + state.stash.push(state.current) + } + state.current = action.payload + }, + + /** + * Close the current modal + */ + close(state) { + state.current = state.stash.pop() || null + }, + + /** + * Hide the current modal (with the intention of showing in again later) + * + * The semantics is the same as with git stash. + */ + stash(state) { + if (!state.current) { + throw new Error("You can't call hideModal if no model is shown!") + } + state.stash.push(state.current) + state.current = null + }, + + /** + * Show the previously hidden modal again. + * + * The semantics is the same as with `git stash pop`. + */ + stashPop(state) { + if (state.current) { + throw new Error("You can't call showModal when a modal is already visible!") + } + const latest = state.stash.pop() + if (!latest) return + state.current = latest + }, + }, +}) + +export const { actions: modalActions } = slice + +export default slice.reducer diff --git a/src/app/components/Modal/slice/selectors.ts b/src/app/components/Modal/slice/selectors.ts new file mode 100644 index 0000000000..46aa32cb33 --- /dev/null +++ b/src/app/components/Modal/slice/selectors.ts @@ -0,0 +1,8 @@ +import { createSelector } from '@reduxjs/toolkit' + +import { RootState } from 'types' +import { initialState } from '.' + +const selectSlice = (state: RootState) => state.modal || initialState + +export const selectCurrentModal = createSelector([selectSlice], settings => settings.current) diff --git a/src/app/components/Modal/slice/types.ts b/src/app/components/Modal/slice/types.ts new file mode 100644 index 0000000000..fe56e5c438 --- /dev/null +++ b/src/app/components/Modal/slice/types.ts @@ -0,0 +1,24 @@ +export interface Modal { + title: string + description: string + handleConfirm: () => void + + /** + * Is this a dangerous operation? + * + * If marked as such, it will only be possible to execute it if the wallet is configured to run in dangerous mode. + * + * It also automatically implies a mandatory waiting time of 10 sec, unless specified otherwise. + */ + isDangerous: boolean + + /** + * How long does the user have to wait before he can actually confirm the action? + */ + mustWaitSecs?: number +} + +export interface ModalState { + current: Modal | null + stash: Modal[] +} diff --git a/src/app/components/SettingsButton/index.tsx b/src/app/components/SettingsButton/index.tsx index b26c65180b..08defcd70e 100644 --- a/src/app/components/SettingsButton/index.tsx +++ b/src/app/components/SettingsButton/index.tsx @@ -6,17 +6,16 @@ import { SettingsDialog } from '../SettingsDialog' import { selectIsSettingsDialogOpen } from '../SettingsDialog/slice/selectors' import { useDispatch, useSelector } from 'react-redux' import { settingsActions } from '../SettingsDialog/slice' -import { useModal } from '../Modal' +import { modalActions } from '../Modal/slice' export const SettingsButton = () => { const dispatch = useDispatch() const layerVisibility = useSelector(selectIsSettingsDialogOpen) const { t } = useTranslation() - const { showModal } = useModal() const setLayerVisibility = (value: boolean) => dispatch(settingsActions.setOpen(value)) const closeSettings = () => { - setLayerVisibility(false) - showModal() + dispatch(settingsActions.setOpen(false)) + dispatch(modalActions.stashPop()) } return ( <> diff --git a/src/app/index.tsx b/src/app/index.tsx index 20fb36d776..62c0f6efff 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -20,7 +20,7 @@ import { AccountPage } from './pages/AccountPage' import { CreateWalletPage } from './pages/CreateWalletPage' import { HomePage } from './pages/HomePage' import { OpenWalletPage } from './pages/OpenWalletPage' -import { ModalProvider } from './components/Modal' +import { ModalContainer } from './components/Modal' import { useRouteRedirects } from './useRouteRedirects' const AppMain = styled(Main)` @@ -33,7 +33,7 @@ export function App() { const size = useContext(ResponsiveContext) return ( - + <> - + + ) } diff --git a/src/app/pages/AccountPage/Features/SendTransaction/index.tsx b/src/app/pages/AccountPage/Features/SendTransaction/index.tsx index 3a2b2d1a12..2ac2cc17c7 100644 --- a/src/app/pages/AccountPage/Features/SendTransaction/index.tsx +++ b/src/app/pages/AccountPage/Features/SendTransaction/index.tsx @@ -1,5 +1,5 @@ import { TransactionStatus } from 'app/components/TransactionStatus' -import { useModal } from 'app/components/Modal' +import { modalActions } from '../../../../components/Modal/slice' import { transactionActions } from 'app/state/transaction' import { selectTransaction } from 'app/state/transaction/selectors' import { selectValidators } from 'app/state/staking/selectors' @@ -9,6 +9,7 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { parseRoseStringToBaseUnitString } from 'app/lib/helpers' +import { Modal } from '../../../../components/Modal/slice/types' export interface SendTransactionProps { isAddressInWallet: boolean @@ -21,7 +22,7 @@ export function SendTransaction(props: SendTransactionProps) { const { t } = useTranslation() const dispatch = useDispatch() - const { launchModal } = useModal() + const launchModal = (modal: Modal) => dispatch(modalActions.launch(modal)) const { error, success } = useSelector(selectTransaction) const validators = useSelector(selectValidators) const [recipient, setRecipient] = useState('') diff --git a/src/store/configureStore.ts b/src/store/configureStore.ts index 88a5a710a0..ede5a0e2b0 100644 --- a/src/store/configureStore.ts +++ b/src/store/configureStore.ts @@ -24,7 +24,13 @@ export function configureAppStore(state?: Partial) { const store = configureStore({ reducer: createReducer(), - middleware: getDefaultMiddleware => getDefaultMiddleware().concat(middlewares), + middleware: getDefaultMiddleware => + getDefaultMiddleware({ + serializableCheck: { + ignoredActionPaths: ['payload.handleConfirm'], + ignoredPaths: ['modal.current.handleConfirm', 'modal.stash'], + }, + }).concat(middlewares), devTools: /* istanbul ignore next line */ process.env.NODE_ENV !== 'production', diff --git a/src/store/reducers.ts b/src/store/reducers.ts index 9111fd363f..ccbd57890e 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -12,6 +12,7 @@ import stakingReducer from 'app/state/staking' import transactionReducer from 'app/state/transaction' import walletReducer from 'app/state/wallet' import themeReducer from 'styles/theme/slice' +import modalReducer from 'app/components/Modal/slice' import settingReducer from 'app/components/SettingsDialog/slice' export function createReducer() { @@ -25,6 +26,7 @@ export function createReducer() { theme: themeReducer, transaction: transactionReducer, wallet: walletReducer, + modal: modalReducer, settings: settingReducer, }) diff --git a/src/types/RootState.ts b/src/types/RootState.ts index 7c554adcb4..ed31727099 100644 --- a/src/types/RootState.ts +++ b/src/types/RootState.ts @@ -9,6 +9,7 @@ import { ImportAccountsState } from 'app/state/importaccounts/types' import { StakingState } from 'app/state/staking/types' import { FatalErrorState } from 'app/state/fatalerror/types' import { SettingsState } from '../app/components/SettingsDialog/slice/types' +import { ModalState } from '../app/components/Modal/slice/types' // [IMPORT NEW CONTAINERSTATE ABOVE] < Needed for generating containers seamlessly export interface RootState { @@ -21,6 +22,7 @@ export interface RootState { importAccounts: ImportAccountsState staking: StakingState fatalError: FatalErrorState + modal: ModalState settings: SettingsState // [INSERT NEW REDUCER KEY ABOVE] < Needed for generating containers seamlessly }