diff --git a/src/components/ConnectWalletButton/ConnectWalletButton.tsx b/src/components/ConnectWalletButton/ConnectWalletButton.tsx index 110f91f..10903f9 100644 --- a/src/components/ConnectWalletButton/ConnectWalletButton.tsx +++ b/src/components/ConnectWalletButton/ConnectWalletButton.tsx @@ -30,7 +30,11 @@ export default function ConnectWalletButton({ await connect(); toast.success("Wallet connected", "Freighter is now linked to SmartDrop"); } catch (error) { - toast.handleError(error, "Wallet Connection"); + // Issue #250: offer a Retry action right on the error toast, so a + // failed connect (wrong network, rejected prompt, timeout) doesn't + // force the user to dismiss the toast and click Connect all over + // again from scratch. + toast.handleError(error, "Wallet Connection", () => void handleConnect()); } finally { setIsConnecting(false); } diff --git a/src/components/UnlockModal/UnlockModal.tsx b/src/components/UnlockModal/UnlockModal.tsx index 75bc247..7818dba 100644 --- a/src/components/UnlockModal/UnlockModal.tsx +++ b/src/components/UnlockModal/UnlockModal.tsx @@ -261,7 +261,10 @@ export default function UnlockModal() { queryClient.setQueryData(positionKey, previousPosition); } - const normalizedError = toast.handleError(err, "Unlock Transaction"); + // Issue #250: let the user retry the same unlock straight from the + // error toast instead of having to reopen the modal and re-enter + // the amount. + const normalizedError = toast.handleError(err, "Unlock Transaction", () => void handleUnlock()); setError(normalizedError.userMessage); setStep("error"); trackEvent("unlock_failed", { diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.tsx similarity index 75% rename from src/hooks/useToast.ts rename to src/hooks/useToast.tsx index db25709..eef87fc 100644 --- a/src/hooks/useToast.ts +++ b/src/hooks/useToast.tsx @@ -11,7 +11,7 @@ import { normalizeError, withRetry } from "@/lib/error-handler"; -import { useToast as useChakraToast } from "@chakra-ui/react"; +import { Box, Button, Text, useToast as useChakraToast } from "@chakra-ui/react"; import { useCallback } from "react"; export type NotificationType = "success" | "error" | "info" | "warning"; @@ -98,20 +98,52 @@ export function useToast() { /** * Handle and display an error to the user. * Automatically logs the error and shows a user-friendly message. + * + * Pass `onRetry` (issue #250) to render a "Retry" action inside the toast + * itself, so the user can re-attempt the same operation without manually + * dismissing the toast and re-triggering the flow from scratch. Omit it + * for errors that genuinely aren't retryable (e.g. validation failures). */ const handleError = useCallback( - (error: unknown, context?: string) => { + (error: unknown, context?: string, onRetry?: () => void) => { const normalized = normalizeError(error, context); errorLogger.log(normalized, context); - // Show user-friendly message - chakraToast({ - title: "Error", - description: normalized.userMessage, - status: "error", - ...DEFAULT_TOAST_OPTIONS, - duration: 6000, // Slightly longer for errors - }); + if (onRetry) { + chakraToast({ + title: "Error", + status: "error", + ...DEFAULT_TOAST_OPTIONS, + duration: 8000, + description: ( + + {normalized.userMessage} + + + ), + }); + } else { + chakraToast({ + title: "Error", + description: normalized.userMessage, + status: "error", + ...DEFAULT_TOAST_OPTIONS, + duration: 6000, // Slightly longer for errors + }); + } return normalized; },