From 680a26ede4e6c944c19e81c2c77f51e8a8b9403f Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 19 Nov 2024 12:11:02 +0100 Subject: [PATCH 01/10] FIx the wallet window opening The transaction sign window appears despite the rejection of the deposit. Let's make sure that the user does not close the window when the wallet opening action has already been triggered. --- .../ActiveStakingStep/DepositBTCModal.tsx | 16 +++++++++++++--- .../TransactionModal/WalletInteractionModal.tsx | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx index 08926094e..bcb3d074a 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx @@ -12,7 +12,7 @@ import { eip1193, logPromiseFailure } from "#/utils" import { PROCESS_STATUSES } from "#/types" import { setStatus, setTxHash } from "#/store/action-flow" import { ONE_SEC_IN_MILLISECONDS, queryKeysFactory } from "#/constants" -import { useTimeout } from "@chakra-ui/react" +import { useBoolean, useTimeout } from "@chakra-ui/react" import { useMutation } from "@tanstack/react-query" import WalletInteractionModal from "../WalletInteractionModal" @@ -28,6 +28,8 @@ export default function DepositBTCModal() { queryKey: userKeys.balance(), }) + const [isTriggeredAction, setIsTriggeredAction] = useBoolean() + const onStakeBTCSuccess = useCallback(() => { handleBitcoinBalanceInvalidation() dispatch(setStatus(PROCESS_STATUSES.SUCCEEDED)) @@ -97,13 +99,21 @@ export default function DepositBTCModal() { ]) const handledDepositBTCWrapper = useCallback(() => { + setIsTriggeredAction.on() logPromiseFailure(handledDepositBTC()) - }, [handledDepositBTC]) + }, [handledDepositBTC, setIsTriggeredAction]) useTimeout(handledDepositBTCWrapper, ONE_SEC_IN_MILLISECONDS) if (status === "pending" || status === "success") return - return + return ( + + ) } diff --git a/dapp/src/components/TransactionModal/WalletInteractionModal.tsx b/dapp/src/components/TransactionModal/WalletInteractionModal.tsx index dbbf4c53b..8987dc500 100644 --- a/dapp/src/components/TransactionModal/WalletInteractionModal.tsx +++ b/dapp/src/components/TransactionModal/WalletInteractionModal.tsx @@ -44,8 +44,10 @@ const DATA: Record< export default function WalletInteractionModal({ step, + withCloseButton, }: { step: WalletInteractionStep + withCloseButton?: boolean }) { const actionType = useActionFlowType() const connector = useConnector() @@ -53,7 +55,7 @@ export default function WalletInteractionModal({ return ( <> - {step === "opening-wallet" && } + {withCloseButton && } {header} From 2c56e9b560d526c647d7107d443e3b77e0aebef0 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 19 Nov 2024 12:56:31 +0100 Subject: [PATCH 02/10] Do not close the Stake modal on esc button To be able to cancel the deposit/withdrawal flow when a user closes the modal. --- dapp/src/hooks/useTransactionModal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dapp/src/hooks/useTransactionModal.ts b/dapp/src/hooks/useTransactionModal.ts index e052ed131..7e4b9d0be 100644 --- a/dapp/src/hooks/useTransactionModal.ts +++ b/dapp/src/hooks/useTransactionModal.ts @@ -1,4 +1,4 @@ -import { ACTION_FLOW_TYPES, ActionFlowType, MODAL_TYPES } from "#/types" +import { ActionFlowType, MODAL_TYPES } from "#/types" import { useCallback } from "react" import { useModal } from "./useModal" @@ -8,7 +8,7 @@ export function useTransactionModal(type: ActionFlowType) { return useCallback(() => { openModal(MODAL_TYPES[type], { type, - closeOnEsc: type !== ACTION_FLOW_TYPES.UNSTAKE, + closeOnEsc: false, }) }, [openModal, type]) } From eee034b3e140221a543bc06ef97cd5971144b25f Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 19 Nov 2024 15:48:58 +0100 Subject: [PATCH 03/10] Cancel withdrawal/deposit Cancel the withdrawal/deposit transaction when the user closes the flow modal. --- .../ActiveStakingStep/DepositBTCModal.tsx | 25 ++++---- .../ActiveUnstakingStep/SignMessageModal.tsx | 51 ++++------------ .../WalletInteractionModal.tsx | 6 +- dapp/src/hooks/index.ts | 1 + dapp/src/hooks/useCancelPromise.ts | 58 +++++++++++++++++++ 5 files changed, 84 insertions(+), 57 deletions(-) create mode 100644 dapp/src/hooks/useCancelPromise.ts diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx index bcb3d074a..d55ee8f16 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx @@ -3,6 +3,7 @@ import { useActionFlowPause, useActionFlowTokenAmount, useAppDispatch, + useCancelPromise, useDepositBTCTransaction, useInvalidateQueries, useStakeFlowContext, @@ -12,7 +13,7 @@ import { eip1193, logPromiseFailure } from "#/utils" import { PROCESS_STATUSES } from "#/types" import { setStatus, setTxHash } from "#/store/action-flow" import { ONE_SEC_IN_MILLISECONDS, queryKeysFactory } from "#/constants" -import { useBoolean, useTimeout } from "@chakra-ui/react" +import { useTimeout } from "@chakra-ui/react" import { useMutation } from "@tanstack/react-query" import WalletInteractionModal from "../WalletInteractionModal" @@ -28,7 +29,8 @@ export default function DepositBTCModal() { queryKey: userKeys.balance(), }) - const [isTriggeredAction, setIsTriggeredAction] = useBoolean() + const { cancel, resolve, shouldOpenErrorModal } = + useCancelPromise("Deposit cancelled") const onStakeBTCSuccess = useCallback(() => { handleBitcoinBalanceInvalidation() @@ -60,13 +62,15 @@ export default function DepositBTCModal() { const onDepositBTCError = useCallback( (error: unknown) => { + if (!shouldOpenErrorModal) return + if (eip1193.didUserRejectRequest(error)) { handlePause() } else { onError(error) } }, - [onError, handlePause], + [shouldOpenErrorModal, handlePause, onError], ) const { mutate: sendBitcoinTransaction, status } = useDepositBTCTransaction({ @@ -81,6 +85,8 @@ export default function DepositBTCModal() { btcAddress, ) + await resolve() + if (verificationStatus === "valid") { sendBitcoinTransaction({ recipient: btcAddress, @@ -94,26 +100,19 @@ export default function DepositBTCModal() { btcAddress, depositReceipt, verifyDepositAddress, + resolve, sendBitcoinTransaction, onError, ]) const handledDepositBTCWrapper = useCallback(() => { - setIsTriggeredAction.on() logPromiseFailure(handledDepositBTC()) - }, [handledDepositBTC, setIsTriggeredAction]) + }, [handledDepositBTC]) useTimeout(handledDepositBTCWrapper, ONE_SEC_IN_MILLISECONDS) if (status === "pending" || status === "success") return - return ( - - ) + return } diff --git a/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx b/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx index 915a9436a..1cfaef56f 100644 --- a/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx @@ -1,8 +1,9 @@ -import React, { useCallback, useEffect, useRef, useState } from "react" +import React, { useCallback, useState } from "react" import { useActionFlowPause, useActionFlowTokenAmount, useAppDispatch, + useCancelPromise, useInvalidateQueries, useModal, useTimeout, @@ -22,15 +23,6 @@ const { userKeys } = queryKeysFactory type WithdrawalStatus = "building-data" | "built-data" | "signature" -const sessionIdToPromise: Record< - number, - { - promise: Promise - cancel: (reason: Error) => void - shouldOpenErrorModal: boolean - } -> = {} - export default function SignMessageModal() { const [status, setWaitingStatus] = useState("building-data") @@ -43,25 +35,14 @@ export default function SignMessageModal() { const handleBitcoinPositionInvalidation = useInvalidateQueries({ queryKey: userKeys.position(), }) - const sessionId = useRef(Math.random()) + const { cancel, resolve, shouldOpenErrorModal } = useCancelPromise( + "Withdrawal cancelled", + ) const { transactionFee } = useTransactionDetails( amount, ACTION_FLOW_TYPES.UNSTAKE, ) - useEffect(() => { - let cancel = (_: Error) => {} - const promise: Promise = new Promise((_, reject) => { - cancel = reject - }) - - sessionIdToPromise[sessionId.current] = { - cancel, - promise, - shouldOpenErrorModal: true, - } - }, []) - const dataBuiltStepCallback = useCallback(() => { setWaitingStatus("built-data") return Promise.resolve() @@ -69,11 +50,8 @@ export default function SignMessageModal() { const onSignMessageCallback = useCallback(async () => { setWaitingStatus("signature") - return Promise.race([ - sessionIdToPromise[sessionId.current].promise, - Promise.resolve(), - ]) - }, []) + return resolve() + }, [resolve]) const onSignMessageSuccess = useCallback(() => { handleBitcoinPositionInvalidation() @@ -90,7 +68,7 @@ export default function SignMessageModal() { const onError = useCallback( (error: unknown) => { - if (!sessionIdToPromise[sessionId.current].shouldOpenErrorModal) return + if (!shouldOpenErrorModal) return if (eip1193.didUserRejectRequest(error)) { handlePause() @@ -98,7 +76,7 @@ export default function SignMessageModal() { onSignMessageError(error) } }, - [onSignMessageError, handlePause], + [shouldOpenErrorModal, handlePause, onSignMessageError], ) const { mutate: handleSignMessage } = useMutation({ @@ -151,16 +129,7 @@ export default function SignMessageModal() { }) const onClose = () => { - const currentSessionId = sessionId.current - const sessionData = sessionIdToPromise[currentSessionId] - sessionIdToPromise[currentSessionId] = { - ...sessionData, - shouldOpenErrorModal: false, - } - - sessionIdToPromise[currentSessionId].cancel( - new Error("Withdrawal cancelled"), - ) + cancel() closeModal() } diff --git a/dapp/src/components/TransactionModal/WalletInteractionModal.tsx b/dapp/src/components/TransactionModal/WalletInteractionModal.tsx index 8987dc500..4eeb6e702 100644 --- a/dapp/src/components/TransactionModal/WalletInteractionModal.tsx +++ b/dapp/src/components/TransactionModal/WalletInteractionModal.tsx @@ -44,10 +44,10 @@ const DATA: Record< export default function WalletInteractionModal({ step, - withCloseButton, + onClose, }: { step: WalletInteractionStep - withCloseButton?: boolean + onClose?: () => void }) { const actionType = useActionFlowType() const connector = useConnector() @@ -55,7 +55,7 @@ export default function WalletInteractionModal({ return ( <> - {withCloseButton && } + {step === "opening-wallet" && } {header} diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 9584fa5eb..0f3213d35 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -39,3 +39,4 @@ export { default as useScrollbarVisibility } from "./useScrollbarVisibility" export { default as useAccessCode } from "./useAccessCode" export { default as useFormField } from "./useFormField" export { default as useDepositBTCTransaction } from "./useDepositBTCTransaction" +export { default as useCancelPromise } from "./useCancelPromise" diff --git a/dapp/src/hooks/useCancelPromise.ts b/dapp/src/hooks/useCancelPromise.ts new file mode 100644 index 000000000..e0cafdcde --- /dev/null +++ b/dapp/src/hooks/useCancelPromise.ts @@ -0,0 +1,58 @@ +import { useCallback, useEffect, useMemo, useRef } from "react" + +const sessionIdToPromise: Record< + number, + { + promise: Promise + cancel: (reason: Error) => void + shouldOpenErrorModal: boolean + } +> = {} + +export default function useCancelPromise(errorMsgText: string) { + const sessionId = useRef(Math.random()) + + useEffect(() => { + let cancel = (_: Error) => {} + const promise: Promise = new Promise((_, reject) => { + cancel = reject + }) + + sessionIdToPromise[sessionId.current] = { + cancel, + promise, + shouldOpenErrorModal: true, + } + }, []) + + const cancel = useCallback(() => { + const currentSessionId = sessionId.current + const sessionData = sessionIdToPromise[currentSessionId] + sessionIdToPromise[currentSessionId] = { + ...sessionData, + shouldOpenErrorModal: false, + } + + sessionIdToPromise[currentSessionId].cancel(new Error(errorMsgText)) + }, [errorMsgText]) + + const resolve = useCallback( + () => + Promise.race([ + sessionIdToPromise[sessionId.current].promise, + Promise.resolve(), + ]), + [], + ) + + const shouldOpenErrorModal = useMemo( + () => sessionIdToPromise[sessionId.current]?.shouldOpenErrorModal, + [], + ) + + return { + cancel, + resolve, + shouldOpenErrorModal, + } +} From fffdb6d548b029ab0783836c0ec93d8b7f10cd20 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Fri, 22 Nov 2024 10:19:18 +0100 Subject: [PATCH 04/10] Make sure the `shouldOpenErrorModal` is updated --- dapp/src/hooks/useCancelPromise.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/dapp/src/hooks/useCancelPromise.ts b/dapp/src/hooks/useCancelPromise.ts index e0cafdcde..26a15ad37 100644 --- a/dapp/src/hooks/useCancelPromise.ts +++ b/dapp/src/hooks/useCancelPromise.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef } from "react" +import { useCallback, useEffect, useRef } from "react" const sessionIdToPromise: Record< number, @@ -44,11 +44,8 @@ export default function useCancelPromise(errorMsgText: string) { ]), [], ) - - const shouldOpenErrorModal = useMemo( - () => sessionIdToPromise[sessionId.current]?.shouldOpenErrorModal, - [], - ) + const shouldOpenErrorModal = + sessionIdToPromise[sessionId.current]?.shouldOpenErrorModal return { cancel, From d1962002b2b3e19299b9047969e4a7c920bc9237 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Mon, 25 Nov 2024 11:39:53 +0100 Subject: [PATCH 05/10] Use current session data to check promise state Previously, `shouldOpenErrorModal` returned a wrong value. Let's check the shouldOpenErrorModal value using the current sessionId to make sure the data is up to date. --- .../ActiveStakingStep/DepositBTCModal.tsx | 13 +++++--- .../ActiveUnstakingStep/SignMessageModal.tsx | 10 +++--- dapp/src/hooks/useCancelPromise.ts | 33 ++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx index d55ee8f16..6a0bf6e5e 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react" +import React, { useCallback, useRef } from "react" import { useActionFlowPause, useActionFlowTokenAmount, @@ -29,8 +29,11 @@ export default function DepositBTCModal() { queryKey: userKeys.balance(), }) - const { cancel, resolve, shouldOpenErrorModal } = - useCancelPromise("Deposit cancelled") + const sessionId = useRef(Math.random()) + const { cancel, resolve, sessionIdToPromise } = useCancelPromise( + sessionId.current, + "Deposit cancelled", + ) const onStakeBTCSuccess = useCallback(() => { handleBitcoinBalanceInvalidation() @@ -62,7 +65,7 @@ export default function DepositBTCModal() { const onDepositBTCError = useCallback( (error: unknown) => { - if (!shouldOpenErrorModal) return + if (!sessionIdToPromise[sessionId.current].shouldOpenErrorModal) return if (eip1193.didUserRejectRequest(error)) { handlePause() @@ -70,7 +73,7 @@ export default function DepositBTCModal() { onError(error) } }, - [shouldOpenErrorModal, handlePause, onError], + [sessionIdToPromise, handlePause, onError], ) const { mutate: sendBitcoinTransaction, status } = useDepositBTCTransaction({ diff --git a/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx b/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx index 1cfaef56f..877f83264 100644 --- a/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveUnstakingStep/SignMessageModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react" +import React, { useCallback, useRef, useState } from "react" import { useActionFlowPause, useActionFlowTokenAmount, @@ -35,7 +35,9 @@ export default function SignMessageModal() { const handleBitcoinPositionInvalidation = useInvalidateQueries({ queryKey: userKeys.position(), }) - const { cancel, resolve, shouldOpenErrorModal } = useCancelPromise( + const sessionId = useRef(Math.random()) + const { cancel, resolve, sessionIdToPromise } = useCancelPromise( + sessionId.current, "Withdrawal cancelled", ) const { transactionFee } = useTransactionDetails( @@ -68,7 +70,7 @@ export default function SignMessageModal() { const onError = useCallback( (error: unknown) => { - if (!shouldOpenErrorModal) return + if (!sessionIdToPromise[sessionId.current].shouldOpenErrorModal) return if (eip1193.didUserRejectRequest(error)) { handlePause() @@ -76,7 +78,7 @@ export default function SignMessageModal() { onSignMessageError(error) } }, - [shouldOpenErrorModal, handlePause, onSignMessageError], + [sessionIdToPromise, handlePause, onSignMessageError], ) const { mutate: handleSignMessage } = useMutation({ diff --git a/dapp/src/hooks/useCancelPromise.ts b/dapp/src/hooks/useCancelPromise.ts index 26a15ad37..a6bb1666b 100644 --- a/dapp/src/hooks/useCancelPromise.ts +++ b/dapp/src/hooks/useCancelPromise.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef } from "react" +import { useCallback, useEffect } from "react" const sessionIdToPromise: Record< number, @@ -9,47 +9,42 @@ const sessionIdToPromise: Record< } > = {} -export default function useCancelPromise(errorMsgText: string) { - const sessionId = useRef(Math.random()) - +export default function useCancelPromise( + sessionId: number, + errorMsgText: string, +) { useEffect(() => { let cancel = (_: Error) => {} const promise: Promise = new Promise((_, reject) => { cancel = reject }) - sessionIdToPromise[sessionId.current] = { + sessionIdToPromise[sessionId] = { cancel, promise, shouldOpenErrorModal: true, } - }, []) + }, [sessionId]) const cancel = useCallback(() => { - const currentSessionId = sessionId.current - const sessionData = sessionIdToPromise[currentSessionId] - sessionIdToPromise[currentSessionId] = { + const sessionData = sessionIdToPromise[sessionId] + sessionIdToPromise[sessionId] = { ...sessionData, shouldOpenErrorModal: false, } - sessionIdToPromise[currentSessionId].cancel(new Error(errorMsgText)) - }, [errorMsgText]) + sessionIdToPromise[sessionId].cancel(new Error(errorMsgText)) + }, [errorMsgText, sessionId]) const resolve = useCallback( () => - Promise.race([ - sessionIdToPromise[sessionId.current].promise, - Promise.resolve(), - ]), - [], + Promise.race([sessionIdToPromise[sessionId].promise, Promise.resolve()]), + [sessionId], ) - const shouldOpenErrorModal = - sessionIdToPromise[sessionId.current]?.shouldOpenErrorModal return { cancel, resolve, - shouldOpenErrorModal, + sessionIdToPromise, } } From fd7159e9f8d9e610fceec93b3dbb98d4e6a15241 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 27 Nov 2024 13:23:02 +0100 Subject: [PATCH 06/10] Update wallet connection alert component --- dapp/src/DApp.tsx | 6 +- .../ConnectWalletModal/ConnectWalletAlert.tsx | 93 +++++++++++++++++++ .../ConnectWalletButton.tsx | 21 +++-- .../ConnectWalletErrorAlert.tsx | 50 ---------- .../components/ConnectWalletModal/index.tsx | 10 +- dapp/src/constants/errors.ts | 27 +----- .../contexts/WalletConnectionAlertContext.tsx | 54 +++++++++++ .../contexts/WalletConnectionErrorContext.tsx | 42 --------- dapp/src/contexts/index.tsx | 2 +- dapp/src/hooks/index.ts | 2 +- dapp/src/hooks/useWalletConnectionAlert.ts | 6 ++ dapp/src/hooks/useWalletConnectionError.ts | 6 -- dapp/src/types/error.ts | 4 - dapp/src/types/index.ts | 1 - dapp/src/utils/orangekit/index.ts | 23 ++--- 15 files changed, 183 insertions(+), 164 deletions(-) create mode 100644 dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx delete mode 100644 dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx create mode 100644 dapp/src/contexts/WalletConnectionAlertContext.tsx delete mode 100644 dapp/src/contexts/WalletConnectionErrorContext.tsx create mode 100644 dapp/src/hooks/useWalletConnectionAlert.ts delete mode 100644 dapp/src/hooks/useWalletConnectionError.ts delete mode 100644 dapp/src/types/error.ts diff --git a/dapp/src/DApp.tsx b/dapp/src/DApp.tsx index 31255b53d..587d248ef 100644 --- a/dapp/src/DApp.tsx +++ b/dapp/src/DApp.tsx @@ -10,7 +10,7 @@ import GlobalStyles from "./components/GlobalStyles" import { DocsDrawerContextProvider, SidebarContextProvider, - WalletConnectionErrorContextProvider, + WalletConnectionAlertContextProvider, } from "./contexts" import { useInitApp } from "./hooks" import { router } from "./router" @@ -67,11 +67,11 @@ function DAppProviders() { - + - + diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx new file mode 100644 index 000000000..70da999ff --- /dev/null +++ b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx @@ -0,0 +1,93 @@ +import React from "react" +import { Box, Link, VStack } from "@chakra-ui/react" +import { AnimatePresence, Variants, motion } from "framer-motion" +import { EXTERNAL_HREF } from "#/constants" +import { + Alert, + AlertDescription, + AlertTitle, + AlertIcon, + AlertProps, +} from "../shared/Alert" + +export enum ConnectionAlert { + Rejected = "REJECTED", + NotSupported = "NOT_SUPPORTED", + NetworkMismatch = "NETWORK_MISMATCH", + InvalidSIWWSignature = "INVALID_SIWW_SIGNATURE", + Default = "DEFAULT", +} + +const CONNECTION_ALERTS = { + [ConnectionAlert.Rejected]: { + title: "Wallet connection rejected.", + description: "If you encountered an error, please try again.", + }, + [ConnectionAlert.NotSupported]: { + title: "Not supported.", + description: + "Only Native SegWit, Nested SegWit or Legacy addresses supported at this time. Please try a different address or another wallet.", + }, + [ConnectionAlert.NetworkMismatch]: { + title: "Error!", + description: + "Incorrect network detected in your wallet. Please choose proper network and try again.", + }, + [ConnectionAlert.Default]: { + title: "Wallet connection error. Please try again.", + description: ( + + If the problem persists, contact{" "} + + Acre support + + + ), + }, + [ConnectionAlert.InvalidSIWWSignature]: { + title: "Invalid Sign In With Wallet signature", + description: "We encountered an error. Please try again.", + }, +} + +const collapseVariants: Variants = { + collapsed: { height: 0 }, + expanded: { height: "auto" }, +} + +type ConnectWalletAlertProps = AlertProps & { type?: ConnectionAlert } + +export default function ConnectWalletAlert(props: ConnectWalletAlertProps) { + const { type, ...restProps } = props + + const data = type ? CONNECTION_ALERTS[type] : undefined + + return ( + + {data && ( + + + + + {data.title} + {data.description} + + + + )} + + ) +} diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx index 0747fdc8e..e3250bea3 100644 --- a/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx +++ b/dapp/src/components/ConnectWalletModal/ConnectWalletButton.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useEffect, useRef, useState } from "react" -import { CONNECTION_ERRORS, ONE_SEC_IN_MILLISECONDS } from "#/constants" +import { ONE_SEC_IN_MILLISECONDS } from "#/constants" import { useAppDispatch, useIsEmbed, useModal, useSignMessageAndCreateSession, useWallet, - useWalletConnectionError, + useWalletConnectionAlert, } from "#/hooks" import { setIsSignedMessage } from "#/store/wallet" import { OrangeKitConnector, OrangeKitError, OnSuccessCallback } from "#/types" @@ -28,6 +28,7 @@ import ArrivingSoonTooltip from "../ArrivingSoonTooltip" import { TextLg, TextMd } from "../shared/Typography" import ConnectWalletStatusLabel from "./ConnectWalletStatusLabel" import Spinner from "../shared/Spinner" +import { ConnectionAlert } from "./ConnectWalletAlert" type ConnectWalletButtonProps = { label: string @@ -66,15 +67,15 @@ export default function ConnectWalletButton({ } = useWallet() const { signMessageStatus, resetMessageStatus, signMessageAndCreateSession } = useSignMessageAndCreateSession() - const { connectionError, setConnectionError, resetConnectionError } = - useWalletConnectionError() + const { type, setConnectionAlert, resetConnectionAlert } = + useWalletConnectionAlert() const { closeModal } = useModal() const dispatch = useAppDispatch() const isMounted = useRef(false) const [isLoading, setIsLoading] = useState(false) - const hasConnectionError = connectionError || connectionStatus === "error" + const hasConnectionError = type || connectionStatus === "error" const hasSignMessageErrorStatus = signMessageStatus === "error" const shouldShowStatuses = isSelected && !hasConnectionError const shouldShowRetryButton = address && hasSignMessageErrorStatus @@ -99,14 +100,14 @@ export default function ConnectWalletButton({ onDisconnect() console.error("Failed to sign siww message", error) - setConnectionError(CONNECTION_ERRORS.INVALID_SIWW_SIGNATURE) + setConnectionAlert(ConnectionAlert.InvalidSIWWSignature) } }, [ signMessageAndCreateSession, onSuccessSignMessage, onDisconnect, - setConnectionError, + setConnectionAlert, ], ) @@ -129,14 +130,14 @@ export default function ConnectWalletButton({ }, onError: (error: OrangeKitError) => { const errorData = orangeKit.parseOrangeKitConnectionError(error) - setConnectionError(errorData) + setConnectionAlert(errorData) }, }) }, [ onConnect, connector, onSuccessConnection, - setConnectionError, + setConnectionAlert, isReconnecting, ]) @@ -159,7 +160,7 @@ export default function ConnectWalletButton({ if (shouldShowStatuses) return if (!isReconnecting) onDisconnect() - resetConnectionError() + resetConnectionAlert() resetMessageStatus() const isInstalled = orangeKit.isWalletInstalled(connector) diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx deleted file mode 100644 index 2e97b59ea..000000000 --- a/dapp/src/components/ConnectWalletModal/ConnectWalletErrorAlert.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react" -import { Box, VStack } from "@chakra-ui/react" -import { AnimatePresence, Variants, motion } from "framer-motion" -import { ConnectionErrorData } from "#/types" -import { - Alert, - AlertDescription, - AlertTitle, - AlertIcon, - AlertProps, -} from "../shared/Alert" - -type ConnectWalletErrorAlertProps = AlertProps & Partial - -const collapseVariants: Variants = { - collapsed: { height: 0 }, - expanded: { height: "auto" }, -} - -export default function ConnectWalletErrorAlert( - props: ConnectWalletErrorAlertProps, -) { - const { title, description, ...restProps } = props - - const shouldRender = !!(title && description) - - return ( - - {shouldRender && ( - - - - - {title} - {description} - - - - )} - - ) -} diff --git a/dapp/src/components/ConnectWalletModal/index.tsx b/dapp/src/components/ConnectWalletModal/index.tsx index 66816f2be..1653d1e11 100644 --- a/dapp/src/components/ConnectWalletModal/index.tsx +++ b/dapp/src/components/ConnectWalletModal/index.tsx @@ -5,13 +5,13 @@ import { useIsEmbed, useIsSignedMessage, useWallet, - useWalletConnectionError, + useWalletConnectionAlert, } from "#/hooks" import { OrangeKitConnector, BaseModalProps, OnSuccessCallback } from "#/types" import { wallets } from "#/constants" import withBaseModal from "../ModalRoot/withBaseModal" import ConnectWalletButton from "./ConnectWalletButton" -import ConnectWalletErrorAlert from "./ConnectWalletErrorAlert" +import ConnectWalletAlert from "./ConnectWalletAlert" export function ConnectWalletModalBase({ onSuccess, @@ -30,7 +30,7 @@ export function ConnectWalletModalBase({ })) const [selectedConnectorId, setSelectedConnectorId] = useState() - const { connectionError, resetConnectionError } = useWalletConnectionError() + const { type, status, resetConnectionAlert } = useWalletConnectionAlert() const isSignedMessage = useIsSignedMessage() const handleButtonOnClick = (connector: OrangeKitConnector) => { @@ -48,7 +48,7 @@ export function ConnectWalletModalBase({ {withCloseButton && ( { - resetConnectionError() + resetConnectionAlert() if (!isSignedMessage) { onDisconnect() @@ -59,7 +59,7 @@ export function ConnectWalletModalBase({ {`Select your ${isEmbed ? "account" : "wallet"}`} - + {enabledConnectors.map((connector) => ( = { - REJECTED: { - title: "Wallet connection rejected.", - description: "If you encountered an error, please try again.", - }, - NOT_SUPPORTED: { - title: "Not supported.", - description: - "Only Native SegWit, Nested SegWit or Legacy addresses supported at this time. Please try a different address or another wallet.", - }, - NETWORK_MISMATCH: { - title: "Error!", - description: - "Incorrect network detected in your wallet. Please choose proper network and try again.", - }, - DEFAULT: { - title: "Something went wrong...", - description: "We encountered an error. Please try again.", - }, - INVALID_SIWW_SIGNATURE: { - title: "Invalid Sign In With Wallet signature", - description: "We encountered an error. Please try again.", - }, -} +import { ACTION_FLOW_TYPES } from "#/types" export const TOKEN_FORM_ERRORS = { REQUIRED: "Please enter an amount.", diff --git a/dapp/src/contexts/WalletConnectionAlertContext.tsx b/dapp/src/contexts/WalletConnectionAlertContext.tsx new file mode 100644 index 000000000..7a62ac050 --- /dev/null +++ b/dapp/src/contexts/WalletConnectionAlertContext.tsx @@ -0,0 +1,54 @@ +import { ConnectionAlert } from "#/components/ConnectWalletModal/ConnectWalletAlert" +import { AlertStatus } from "@chakra-ui/react" +import React, { createContext, useCallback, useMemo, useState } from "react" + +type WalletConnectionAlertContextValue = { + type?: ConnectionAlert + status: AlertStatus + setConnectionAlert: (type: ConnectionAlert, status?: AlertStatus) => void + resetConnectionAlert: () => void +} + +export const WalletConnectionAlertContext = + createContext( + {} as WalletConnectionAlertContextValue, + ) + +export function WalletConnectionAlertContextProvider({ + children, +}: { + children: React.ReactNode +}): React.ReactElement { + const [type, setType] = useState() + const [status, setStatus] = useState("error") + + const resetConnectionAlert = useCallback(() => { + setType(undefined) + setStatus("error") + }, [setType]) + + const setConnectionAlert = useCallback( + (connectionAlert: ConnectionAlert, alertStatus: AlertStatus = "error") => { + setType(connectionAlert) + setStatus(alertStatus) + }, + [setType], + ) + + const contextValue: WalletConnectionAlertContextValue = + useMemo( + () => ({ + type, + status, + setConnectionAlert, + resetConnectionAlert, + }), + [resetConnectionAlert, setConnectionAlert, status, type], + ) + + return ( + + {children} + + ) +} diff --git a/dapp/src/contexts/WalletConnectionErrorContext.tsx b/dapp/src/contexts/WalletConnectionErrorContext.tsx deleted file mode 100644 index 23838bac2..000000000 --- a/dapp/src/contexts/WalletConnectionErrorContext.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { ConnectionErrorData } from "#/types" -import React, { createContext, useCallback, useMemo, useState } from "react" - -type WalletConnectionErrorContextValue = { - connectionError: ConnectionErrorData | undefined - setConnectionError: (data: ConnectionErrorData) => void - resetConnectionError: () => void -} - -export const WalletConnectionErrorContext = - createContext( - {} as WalletConnectionErrorContextValue, - ) - -export function WalletConnectionErrorContextProvider({ - children, -}: { - children: React.ReactNode -}): React.ReactElement { - const [connectionError, setConnectionError] = useState() - - const resetConnectionError = useCallback( - () => setConnectionError(undefined), - [setConnectionError], - ) - - const contextValue: WalletConnectionErrorContextValue = - useMemo( - () => ({ - connectionError, - setConnectionError, - resetConnectionError, - }), - [connectionError, resetConnectionError, setConnectionError], - ) - - return ( - - {children} - - ) -} diff --git a/dapp/src/contexts/index.tsx b/dapp/src/contexts/index.tsx index 5781528dd..658feecbd 100644 --- a/dapp/src/contexts/index.tsx +++ b/dapp/src/contexts/index.tsx @@ -2,4 +2,4 @@ export * from "./DocsDrawerContext" export * from "./SidebarContext" export * from "./StakeFlowContext" export * from "./PaginationContext" -export * from "./WalletConnectionErrorContext" +export * from "./WalletConnectionAlertContext" diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index 28bc42703..c0ebd7792 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -20,7 +20,7 @@ export * from "./useTransactionModal" export * from "./useVerifyDepositAddress" export { default as useStatistics } from "./useStatistics" export * from "./useDisconnectWallet" -export * from "./useWalletConnectionError" +export { default as useWalletConnectionAlert } from "./useWalletConnectionAlert" export { default as useInvalidateQueries } from "./useInvalidateQueries" export { default as useResetWalletState } from "./useResetWalletState" export { default as useMobileMode } from "./useMobileMode" diff --git a/dapp/src/hooks/useWalletConnectionAlert.ts b/dapp/src/hooks/useWalletConnectionAlert.ts new file mode 100644 index 000000000..bab475294 --- /dev/null +++ b/dapp/src/hooks/useWalletConnectionAlert.ts @@ -0,0 +1,6 @@ +import { WalletConnectionAlertContext } from "#/contexts" +import { useContext } from "react" + +export default function useWalletConnectionAlert() { + return useContext(WalletConnectionAlertContext) +} diff --git a/dapp/src/hooks/useWalletConnectionError.ts b/dapp/src/hooks/useWalletConnectionError.ts deleted file mode 100644 index 55fae3a56..000000000 --- a/dapp/src/hooks/useWalletConnectionError.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { WalletConnectionErrorContext } from "#/contexts" -import { useContext } from "react" - -export function useWalletConnectionError() { - return useContext(WalletConnectionErrorContext) -} diff --git a/dapp/src/types/error.ts b/dapp/src/types/error.ts deleted file mode 100644 index 975af0f65..000000000 --- a/dapp/src/types/error.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ConnectionErrorData = { - title: string - description: string -} diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index 13bdda39c..aa1e0e3b0 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -10,7 +10,6 @@ export * from "./fee" export * from "./modal" export * from "./form" export * from "./eip1193" -export * from "./error" export * from "./status" export * from "./orangekit" export * from "./ledgerLive" diff --git a/dapp/src/utils/orangekit/index.ts b/dapp/src/utils/orangekit/index.ts index 18f355df4..973bcbf19 100644 --- a/dapp/src/utils/orangekit/index.ts +++ b/dapp/src/utils/orangekit/index.ts @@ -1,19 +1,12 @@ -import { - ACRE_SESSION_EXPIRATION_TIME, - CONNECTION_ERRORS, - wallets, -} from "#/constants" -import { - ConnectionErrorData, - OrangeKitError, - OrangeKitConnector, -} from "#/types" +import { ACRE_SESSION_EXPIRATION_TIME, wallets } from "#/constants" +import { OrangeKitError, OrangeKitConnector } from "#/types" import { isUnsupportedBitcoinAddressError, isWalletNetworkDoesNotMatchProviderChainError, } from "@orangekit/react" import { Connector } from "wagmi" import { SignInWithWalletMessage } from "@orangekit/sign-in-with-wallet" +import { ConnectionAlert } from "#/components/ConnectWalletModal/ConnectWalletAlert" import { getExpirationDate } from "../time" import { getOrangeKitLedgerLiveConnector } from "./ledger-live" @@ -71,22 +64,22 @@ const typeConversionToConnector = (connector?: OrangeKitConnector): Connector => const parseOrangeKitConnectionError = ( error: OrangeKitError, -): ConnectionErrorData => { +): ConnectionAlert => { const { cause } = error if (isWalletConnectionRejectedError(cause)) { - return CONNECTION_ERRORS.REJECTED + return ConnectionAlert.Rejected } if (isUnsupportedBitcoinAddressError(cause)) { - return CONNECTION_ERRORS.NOT_SUPPORTED + return ConnectionAlert.NotSupported } if (isWalletNetworkDoesNotMatchProviderChainError(cause)) { - return CONNECTION_ERRORS.NETWORK_MISMATCH + return ConnectionAlert.NetworkMismatch } - return CONNECTION_ERRORS.DEFAULT + return ConnectionAlert.Default } async function verifySignInWithWalletMessage( From 03cd31d498ef43d3d3d12dfb5f82734ccb36bdf8 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 27 Nov 2024 13:26:31 +0100 Subject: [PATCH 07/10] Add missing `status` for `ConnectWalletAlert` component --- dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx index 70da999ff..c1301912f 100644 --- a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx +++ b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx @@ -63,7 +63,7 @@ const collapseVariants: Variants = { type ConnectWalletAlertProps = AlertProps & { type?: ConnectionAlert } export default function ConnectWalletAlert(props: ConnectWalletAlertProps) { - const { type, ...restProps } = props + const { type, status, ...restProps } = props const data = type ? CONNECTION_ALERTS[type] : undefined @@ -79,7 +79,7 @@ export default function ConnectWalletAlert(props: ConnectWalletAlertProps) { overflow="hidden" w="full" > - + {data.title} From 13659b9a24778889c95aa98d290365dfdc8bfa11 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 27 Nov 2024 13:46:52 +0100 Subject: [PATCH 08/10] Add missing comment --- dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx index c1301912f..bdbc3eeb1 100644 --- a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx +++ b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx @@ -39,6 +39,7 @@ const CONNECTION_ALERTS = { If the problem persists, contact{" "} Date: Thu, 28 Nov 2024 08:58:47 +0100 Subject: [PATCH 09/10] Update content for connection alerts --- .../ConnectWalletModal/ConnectWalletAlert.tsx | 45 ++++++++++--------- dapp/src/theme/Alert.ts | 3 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx index bdbc3eeb1..553d9ff56 100644 --- a/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx +++ b/dapp/src/components/ConnectWalletModal/ConnectWalletAlert.tsx @@ -18,6 +18,24 @@ export enum ConnectionAlert { Default = "DEFAULT", } +function ContactSupport() { + return ( + + If the problem persists, contact{" "} + + Acre support + + . + + ) +} + const CONNECTION_ALERTS = { [ConnectionAlert.Rejected]: { title: "Wallet connection rejected.", @@ -26,33 +44,20 @@ const CONNECTION_ALERTS = { [ConnectionAlert.NotSupported]: { title: "Not supported.", description: - "Only Native SegWit, Nested SegWit or Legacy addresses supported at this time. Please try a different address or another wallet.", + "Only Native SegWit, Nested SegWit, or Legacy addresses are supported. Please use a compatible address or switch to a different wallet.", }, [ConnectionAlert.NetworkMismatch]: { - title: "Error!", + title: "Incorrect network detected in your wallet.", description: - "Incorrect network detected in your wallet. Please choose proper network and try again.", + "Please connect your wallet to the correct Bitcoin network and try again.", }, [ConnectionAlert.Default]: { - title: "Wallet connection error. Please try again.", - description: ( - - If the problem persists, contact{" "} - - Acre support - - - ), + title: "Wallet connection failed. Please try again.", + description: , }, [ConnectionAlert.InvalidSIWWSignature]: { - title: "Invalid Sign In With Wallet signature", - description: "We encountered an error. Please try again.", + title: "Invalid sign-in signature. Please try again.", + description: , }, } diff --git a/dapp/src/theme/Alert.ts b/dapp/src/theme/Alert.ts index 8b4aa91f9..005dbb32f 100644 --- a/dapp/src/theme/Alert.ts +++ b/dapp/src/theme/Alert.ts @@ -12,8 +12,7 @@ const baseStyle = multiStyleConfig.definePartsStyle({ borderWidth: 1, borderStyle: "solid", borderColor: $borderColor.reference, - px: 5, - py: 5, + p: 4, rounded: "xl", }, description: { From f7d8f5ecda57f59c4f0e76d749f13d66890b9a0e Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 3 Dec 2024 16:38:50 +0100 Subject: [PATCH 10/10] Remove `GrantedSeasonPassCard` component --- .../DashboardPage/GrantedSeasonPassCard.tsx | 31 ------------------- dapp/src/pages/DashboardPage/index.tsx | 4 --- 2 files changed, 35 deletions(-) delete mode 100644 dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx diff --git a/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx b/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx deleted file mode 100644 index 4e618844f..000000000 --- a/dapp/src/pages/DashboardPage/GrantedSeasonPassCard.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react" -import { - Card, - CardBody, - CardHeader, - CardProps, - HStack, - Icon, -} from "@chakra-ui/react" -import { IconDiscountCheckFilled, IconLock } from "@tabler/icons-react" -import { TextMd } from "#/components/shared/Typography" -import UserDataSkeleton from "#/components/shared/UserDataSkeleton" - -export default function GrantedSeasonPassCard(props: CardProps) { - return ( - - - - Season 2 arriving soon - - - - - - - Your seat is secured. - - - - ) -} diff --git a/dapp/src/pages/DashboardPage/index.tsx b/dapp/src/pages/DashboardPage/index.tsx index e0fb11bc2..b35928aa6 100644 --- a/dapp/src/pages/DashboardPage/index.tsx +++ b/dapp/src/pages/DashboardPage/index.tsx @@ -3,7 +3,6 @@ import { featureFlags } from "#/constants" import { useTriggerConnectWalletModal } from "#/hooks" import { Grid } from "@chakra-ui/react" import DashboardCard from "./DashboardCard" -// import GrantedSeasonPassCard from "./GrantedSeasonPassCard" import AcrePointsCard from "./AcrePointsCard" import AcrePointsTemplateCard from "./AcrePointsTemplateCard" import BeehiveCard from "./BeehiveCard" @@ -52,9 +51,6 @@ export default function DashboardPage() { - {/* TODO: Uncomment in post-launch phases + add `gridArea` and update `templateAreas` */} - {/* */} - {featureFlags.ACRE_POINTS_ENABLED ? ( ) : (