Skip to content

Commit

Permalink
feat(TransactionStatusModal): handle TX timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny committed Nov 12, 2024
1 parent d4e64af commit 5ba5762
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ import {
useState,
} from "react"

const TransactionStatusContext = createContext<{
type TransactionStatusContextType = {
isTxModalOpen: boolean
onTxModalOpen: () => void
onTxModalClose: () => void
txHash: string
setTxHash: Dispatch<SetStateAction<string>>
txError: boolean
setTxError: Dispatch<SetStateAction<boolean>>
txError: Error | null
setTxError: Dispatch<SetStateAction<Error | null>>
txSuccess: boolean
setTxSuccess: Dispatch<SetStateAction<boolean>>
}>(undefined)
}

const TransactionStatusContext = createContext<TransactionStatusContextType>(
undefined as unknown as TransactionStatusContextType
)

const TransactionStatusProvider = ({
children,
Expand All @@ -32,7 +36,7 @@ const TransactionStatusProvider = ({
} = useDisclosure()

const [txHash, setTxHash] = useState("")
const [txError, setTxError] = useState(false)
const [txError, setTxError] = useState<Error | null>(null)
const [txSuccess, setTxSuccess] = useState(false)

const { confettiPlayer } = useConfetti()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Anchor } from "@/components/ui/Anchor"
import { FuelProvider } from "@fuels/react"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { fuelConfig } from "fuelConfig"
import { ThemeProvider } from "next-themes"
import { useEffect } from "react"
import { WaitForTransactionReceiptTimeoutError } from "viem"
import { WagmiProvider } from "wagmi"
import { wagmiConfig } from "wagmiConfig"
import {
Expand All @@ -17,6 +17,8 @@ import { TransactionStatusModal } from "./TransactionStatusModal"

const queryClient = new QueryClient()

const HASH = "0xbb2da2efbfc465f63c100036d25c626ac96a1167d48f80646e91be3361179160"

const TransactionStatusDialogStory = () => (
<>
<TransactionStatusModal
Expand All @@ -25,7 +27,7 @@ const TransactionStatusDialogStory = () => (
successText={"This is the success text!"}
successLinkComponent={
<Anchor
href="https://sepolia.etherscan.io/tx/0xbb2da2efbfc465f63c100036d25c626ac96a1167d48f80646e91be3361179160"
href={`https://sepolia.etherscan.io/tx/${HASH}`}
target="_blank"
showExternal
variant="muted"
Expand Down Expand Up @@ -55,19 +57,17 @@ const meta: Meta<typeof TransactionStatusDialogStory> = {
component: TransactionStatusDialogStory,
decorators: [
(Story) => (
<ThemeProvider>
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<FuelProvider ui={false} fuelConfig={fuelConfig}>
<TransactionStatusProvider>
<ConfettiProvider>
<Story />
</ConfettiProvider>
</TransactionStatusProvider>
</FuelProvider>
</QueryClientProvider>
</WagmiProvider>
</ThemeProvider>
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<FuelProvider ui={false} fuelConfig={fuelConfig}>
<TransactionStatusProvider>
<ConfettiProvider>
<Story />
</ConfettiProvider>
</TransactionStatusProvider>
</FuelProvider>
</QueryClientProvider>
</WagmiProvider>
),
],
}
Expand All @@ -82,9 +82,23 @@ export const Progress: Story = {
const { onTxModalOpen, setTxHash } = useTransactionStatusContext()

useEffect(() => {
setTxHash(
"0xbb2da2efbfc465f63c100036d25c626ac96a1167d48f80646e91be3361179160"
)
setTxHash(HASH)
onTxModalOpen()
}, [])

return <Story />
},
],
}

export const Error_: Story = {
name: "Error",
decorators: [
(Story) => {
const { onTxModalOpen, setTxError } = useTransactionStatusContext()

useEffect(() => {
setTxError(new Error("TX ERROR"))
onTxModalOpen()
}, [])

Expand All @@ -93,13 +107,13 @@ export const Progress: Story = {
],
}

export const Error: Story = {
export const Timeout: Story = {
decorators: [
(Story) => {
const { onTxModalOpen, setTxError } = useTransactionStatusContext()

useEffect(() => {
setTxError(true)
setTxError(new WaitForTransactionReceiptTimeoutError({ hash: HASH }))
onTxModalOpen()
}, [])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/Dialog"
import { WaitForTransactionReceiptTimeoutError } from "viem"
import { useTransactionStatusContext } from "../TransactionStatusContext"
import { TxError } from "./components/TxError"
import { TxInProgress } from "./components/TxInProgress"
Expand Down Expand Up @@ -55,13 +56,15 @@ const TransactionStatusModal = ({
<DialogContent>
<DialogHeader>
<DialogTitle>
{txError
? "Transaction failed"
: txSuccess
? (successTitle ?? "Successful payment")
: txHash
? "Transaction is processing..."
: title}
{txError instanceof WaitForTransactionReceiptTimeoutError
? "Timeout"
: txError
? "Transaction failed"
: txSuccess
? (successTitle ?? "Successful payment")
: txHash
? "Transaction is processing..."
: title}
</DialogTitle>
</DialogHeader>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
import { DialogBody, DialogFooter } from "@/components/ui/Dialog"
import { XCircle } from "@phosphor-icons/react/dist/ssr"
import { Timer, XCircle } from "@phosphor-icons/react/dist/ssr"
import { PropsWithChildren } from "react"
import { WaitForTransactionReceiptTimeoutError } from "viem"
import { useTransactionStatusContext } from "../../TransactionStatusContext"
import { TransactionModalCloseButton } from "./TransactionModalCloseButton"

const TxError = ({ children }: PropsWithChildren<unknown>): JSX.Element => (
<>
<DialogBody>
<div className="mb-10 flex items-center justify-center">
<XCircle className="size-36 text-destructive [&>*]:stroke-[6px]" />
</div>
const TxError = ({ children }: PropsWithChildren<unknown>): JSX.Element => {
const { txError } = useTransactionStatusContext()

{children}
</DialogBody>
const isTimeout = txError instanceof WaitForTransactionReceiptTimeoutError

<DialogFooter>
<TransactionModalCloseButton />
</DialogFooter>
</>
)
return (
<>
<DialogBody>
<div className="mb-10 flex items-center justify-center">
{isTimeout ? (
<Timer className="size-36 text-muted-foreground [&>*]:stroke-[6px]" />
) : (
<XCircle className="size-36 text-destructive [&>*]:stroke-[6px]" />
)}
</div>

{isTimeout ? (
<p>
Your transaction is processing. Due to high network traffic, it may take
longer than usual. Check your wallet later for status updates. For
persistent issues, please contact our support.
</p>
) : (
children
)}
</DialogBody>

<DialogFooter>
<TransactionModalCloseButton />
</DialogFooter>
</>
)
}

export { TxError }
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const useMintGuildPin = () => {
const { triggerMembershipUpdate } = useMembershipUpdate()

const mintGuildPin = async () => {
setTxError?.(false)
setTxError?.(null)
setTxSuccess?.(false)

setLoadingText("Uploading metadata")
Expand Down Expand Up @@ -205,7 +205,7 @@ const useMintGuildPin = () => {
...useSubmit(mintGuildPin, {
onError: (error) => {
setLoadingText("")
setTxError?.(true)
setTxError?.(error)

const prettyError = error.correlationId
? error
Expand Down
11 changes: 8 additions & 3 deletions src/components/[guild]/collect/hooks/useCollectNft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ const useCollectNft = () => {
const claimAmount = claimAmountFromForm ?? 1

const mint = async () => {
setTxError(false)
setTxError(null)
setTxSuccess(false)

if (!publicClient) return Promise.reject("Couldn't find publicClient")
if (!walletClient) return Promise.reject("Couldn't find walletClient")

if (shouldSwitchChain)
return Promise.reject("Please switch network before minting")

Expand Down Expand Up @@ -189,7 +192,7 @@ const useCollectNft = () => {

mutateTopCollectors(
(prevValue) => {
const lowerCaseUserAddress = userAddress.toLowerCase()
const lowerCaseUserAddress = userAddress?.toLowerCase()
const alreadyCollected = !!prevValue?.topCollectors?.find(
(collector) => collector.address.toLowerCase() === lowerCaseUserAddress
)
Expand Down Expand Up @@ -241,13 +244,15 @@ const useCollectNft = () => {
},
onError: (error) => {
setLoadingText("")
setTxError(true)
setTxError(error)

const prettyError = error.correlationId
? error
: processViemContractError(error, (errorName) => {
if (errorName === "AlreadyClaimed")
return "You've already collected this NFT"

return errorName
})

if (isUserRejectedError(prettyError)) {
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/useSubmitTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const useSubmitTransaction = (
)

const setTxError = useCallback(
(newState: boolean) => {
(newState: Error | null) => {
if (options?.setContext === false || typeof setTxErrorInContext !== "function")
return
setTxErrorInContext(newState)
Expand Down Expand Up @@ -162,8 +162,7 @@ const useSubmitTransaction = (
}

if (error) {
setTxError(true)

setTxError(rawError)
onError?.(error, rawError)
reset()
}
Expand All @@ -174,7 +173,7 @@ const useSubmitTransaction = (
return {
onSubmitTransaction: () => {
setTxHash("")
setTxError(false)
setTxError(null)
setTxSuccess(false)

if (!writeContract && error) {
Expand Down

0 comments on commit 5ba5762

Please sign in to comment.