From bb5773e79b3580a57098375ccff6b0c57b9ebb6d Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Mon, 14 Nov 2022 11:00:08 +0100 Subject: [PATCH 1/2] fix: Show error notifications in safe creation --- src/components/new-safe/steps/Step3/index.tsx | 4 +-- .../new-safe/steps/Step4/logic/index.ts | 27 ++++++++++++++++++- .../new-safe/steps/Step4/useSafeCreation.ts | 20 +++++++++++--- src/components/welcome/index.tsx | 3 ++- src/hooks/useTxNotifications.ts | 2 +- src/store/notificationsSlice.ts | 14 ++++++++-- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/components/new-safe/steps/Step3/index.tsx b/src/components/new-safe/steps/Step3/index.tsx index 13be28d7ca..ee1c328f26 100644 --- a/src/components/new-safe/steps/Step3/index.tsx +++ b/src/components/new-safe/steps/Step3/index.tsx @@ -135,8 +135,8 @@ const CreateSafeStep3 = ({ data, onSubmit, onBack, setStep }: StepRenderProps } /> - - + + You will have to confirm a transaction with your currently connected wallet. diff --git a/src/components/new-safe/steps/Step4/logic/index.ts b/src/components/new-safe/steps/Step4/logic/index.ts index 07e5716a54..77cc504164 100644 --- a/src/components/new-safe/steps/Step4/logic/index.ts +++ b/src/components/new-safe/steps/Step4/logic/index.ts @@ -26,6 +26,9 @@ import type { UrlObject } from 'url' import chains from '@/config/chains' import { AppRoutes } from '@/config/routes' import { SAFE_APPS_EVENTS, trackEvent } from '@/services/analytics' +import type { AppDispatch, AppThunk } from '@/store' +import { showNotification } from '@/store/notificationsSlice' +import { formatError } from '@/hooks/useTxNotifications' /** * Prepare data for creating a Safe for the Core SDK @@ -180,10 +183,24 @@ export const handleSafeCreationError = (error: EthersError) => { return SafeCreationStatus.TIMEOUT } +export const showSafeCreationError = (error: EthersError): AppThunk => { + return (dispatch) => { + dispatch( + showNotification({ + message: `Your transaction was unsuccessful. Reason: ${formatError(error)}`, + detailedMessage: error.message, + groupKey: 'create-safe-error', + variant: 'error', + }), + ) + } +} + export const checkSafeCreationTx = async ( provider: JsonRpcProvider, pendingTx: PendingSafeTx, txHash: string, + dispatch: AppDispatch, ): Promise => { const TIMEOUT_TIME = 6.5 * 60 * 1000 // 6.5 minutes @@ -196,7 +213,15 @@ export const checkSafeCreationTx = async ( return SafeCreationStatus.SUCCESS } catch (err) { - return handleSafeCreationError(err as EthersError) + const _err = err as EthersError + + const status = handleSafeCreationError(_err) + + if (status !== SafeCreationStatus.SUCCESS) { + dispatch(showSafeCreationError(_err)) + } + + return status } } diff --git a/src/components/new-safe/steps/Step4/useSafeCreation.ts b/src/components/new-safe/steps/Step4/useSafeCreation.ts index cc1b9cc9ba..4ef4d8b2f7 100644 --- a/src/components/new-safe/steps/Step4/useSafeCreation.ts +++ b/src/components/new-safe/steps/Step4/useSafeCreation.ts @@ -12,7 +12,10 @@ import { getSafeCreationTxInfo, getSafeDeployProps, handleSafeCreationError, + showSafeCreationError, } from '@/components/new-safe/steps/Step4/logic' +import { useAppDispatch } from '@/store' +import { closeAllNotifications } from '@/store/notificationsSlice' export enum SafeCreationStatus { AWAITING, @@ -34,6 +37,7 @@ export const useSafeCreation = ( ) => { const [isCreating, setIsCreating] = useState(false) const [isWatching, setIsWatching] = useState(false) + const dispatch = useAppDispatch() const wallet = useWallet() const provider = useWeb3() @@ -50,6 +54,7 @@ export const useSafeCreation = ( if (!pendingSafe || !provider || !chain || !wallet || isCreating) return setIsCreating(true) + dispatch(closeAllNotifications()) try { const tx = await getSafeCreationTxInfo(provider, pendingSafe, chain, pendingSafe.saltNonce, wallet) @@ -66,11 +71,18 @@ export const useSafeCreation = ( await createNewSafe(provider, safeParams) } catch (err) { - setStatus(handleSafeCreationError(err as EthersError)) + const _err = err as EthersError + const status = handleSafeCreationError(_err) + + setStatus(status) + + if (status !== SafeCreationStatus.SUCCESS) { + dispatch(showSafeCreationError(_err)) + } } setIsCreating(false) - }, [chain, createSafeCallback, isCreating, pendingSafe, provider, setStatus, wallet]) + }, [chain, createSafeCallback, dispatch, isCreating, pendingSafe, provider, setStatus, wallet]) const watchSafeTx = useCallback(async () => { if (!pendingSafe?.tx || !pendingSafe?.txHash || !provider || isWatching) return @@ -78,10 +90,10 @@ export const useSafeCreation = ( setStatus(SafeCreationStatus.PROCESSING) setIsWatching(true) - const txStatus = await checkSafeCreationTx(provider, pendingSafe.tx, pendingSafe.txHash) + const txStatus = await checkSafeCreationTx(provider, pendingSafe.tx, pendingSafe.txHash, dispatch) setStatus(txStatus) setIsWatching(false) - }, [isWatching, pendingSafe, provider, setStatus]) + }, [isWatching, pendingSafe, provider, setStatus, dispatch]) useEffect(() => { if (status !== SafeCreationStatus.AWAITING) return diff --git a/src/components/welcome/index.tsx b/src/components/welcome/index.tsx index 89b2be5346..89fca66488 100644 --- a/src/components/welcome/index.tsx +++ b/src/components/welcome/index.tsx @@ -28,7 +28,8 @@ const NewSafe = () => { for creating your new Safe. - diff --git a/src/hooks/useTxNotifications.ts b/src/hooks/useTxNotifications.ts index 02bfcacaf7..7b4d4cb7a5 100644 --- a/src/hooks/useTxNotifications.ts +++ b/src/hooks/useTxNotifications.ts @@ -37,7 +37,7 @@ enum Variant { } // Format the error message -const formatError = (error: Error & { reason?: string }): string => { +export const formatError = (error: Error & { reason?: string }): string => { let { reason } = error if (!reason) return '' if (!reason.endsWith('.')) reason += '.' diff --git a/src/store/notificationsSlice.ts b/src/store/notificationsSlice.ts index bf976cfb40..af81da11b9 100644 --- a/src/store/notificationsSlice.ts +++ b/src/store/notificationsSlice.ts @@ -31,6 +31,11 @@ export const notificationsSlice = createSlice({ return notification.id === payload.id ? { ...notification, isDismissed: true } : notification }) }, + closeAllNotifications: (state): NotificationState => { + return state.map((notification) => { + return { ...notification, isDismissed: true } + }) + }, deleteNotification: (state, { payload }: PayloadAction) => { return state.filter((notification) => notification.id !== payload.id) }, @@ -45,8 +50,13 @@ export const notificationsSlice = createSlice({ }, }) -export const { closeNotification, deleteNotification, deleteAllNotifications, readNotification } = - notificationsSlice.actions +export const { + closeNotification, + closeAllNotifications, + deleteNotification, + deleteAllNotifications, + readNotification, +} = notificationsSlice.actions export const showNotification = (payload: Omit): AppThunk => { return (dispatch) => { From 2f59cce0887388458e0767a1bf79c96efe595f2e Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Mon, 14 Nov 2022 14:12:02 +0100 Subject: [PATCH 2/2] fix: Add closeByGroupKey action to notification slice --- src/components/new-safe/steps/Step4/logic/index.ts | 3 ++- .../new-safe/steps/Step4/useSafeCreation.ts | 5 +++-- src/store/notificationsSlice.ts | 13 ++++--------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/components/new-safe/steps/Step4/logic/index.ts b/src/components/new-safe/steps/Step4/logic/index.ts index 77cc504164..e66070f030 100644 --- a/src/components/new-safe/steps/Step4/logic/index.ts +++ b/src/components/new-safe/steps/Step4/logic/index.ts @@ -183,13 +183,14 @@ export const handleSafeCreationError = (error: EthersError) => { return SafeCreationStatus.TIMEOUT } +export const SAFE_CREATION_ERROR_KEY = 'create-safe-error' export const showSafeCreationError = (error: EthersError): AppThunk => { return (dispatch) => { dispatch( showNotification({ message: `Your transaction was unsuccessful. Reason: ${formatError(error)}`, detailedMessage: error.message, - groupKey: 'create-safe-error', + groupKey: SAFE_CREATION_ERROR_KEY, variant: 'error', }), ) diff --git a/src/components/new-safe/steps/Step4/useSafeCreation.ts b/src/components/new-safe/steps/Step4/useSafeCreation.ts index 4ef4d8b2f7..11845e7c52 100644 --- a/src/components/new-safe/steps/Step4/useSafeCreation.ts +++ b/src/components/new-safe/steps/Step4/useSafeCreation.ts @@ -12,10 +12,11 @@ import { getSafeCreationTxInfo, getSafeDeployProps, handleSafeCreationError, + SAFE_CREATION_ERROR_KEY, showSafeCreationError, } from '@/components/new-safe/steps/Step4/logic' import { useAppDispatch } from '@/store' -import { closeAllNotifications } from '@/store/notificationsSlice' +import { closeByGroupKey } from '@/store/notificationsSlice' export enum SafeCreationStatus { AWAITING, @@ -54,7 +55,7 @@ export const useSafeCreation = ( if (!pendingSafe || !provider || !chain || !wallet || isCreating) return setIsCreating(true) - dispatch(closeAllNotifications()) + dispatch(closeByGroupKey({ groupKey: SAFE_CREATION_ERROR_KEY })) try { const tx = await getSafeCreationTxInfo(provider, pendingSafe, chain, pendingSafe.saltNonce, wallet) diff --git a/src/store/notificationsSlice.ts b/src/store/notificationsSlice.ts index af81da11b9..823f7fb512 100644 --- a/src/store/notificationsSlice.ts +++ b/src/store/notificationsSlice.ts @@ -31,9 +31,9 @@ export const notificationsSlice = createSlice({ return notification.id === payload.id ? { ...notification, isDismissed: true } : notification }) }, - closeAllNotifications: (state): NotificationState => { + closeByGroupKey: (state, { payload }: PayloadAction<{ groupKey: string }>): NotificationState => { return state.map((notification) => { - return { ...notification, isDismissed: true } + return notification.groupKey === payload.groupKey ? { ...notification, isDismissed: true } : notification }) }, deleteNotification: (state, { payload }: PayloadAction) => { @@ -50,13 +50,8 @@ export const notificationsSlice = createSlice({ }, }) -export const { - closeNotification, - closeAllNotifications, - deleteNotification, - deleteAllNotifications, - readNotification, -} = notificationsSlice.actions +export const { closeNotification, closeByGroupKey, deleteNotification, deleteAllNotifications, readNotification } = + notificationsSlice.actions export const showNotification = (payload: Omit): AppThunk => { return (dispatch) => {