Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/components/ConnectWalletButton/ConnectWalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/UnlockModal/UnlockModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,10 @@ export default function UnlockModal() {
queryClient.setQueryData<UserPosition>(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", {
Expand Down
52 changes: 42 additions & 10 deletions src/hooks/useToast.ts → src/hooks/useToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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: (
<Box>
<Text>{normalized.userMessage}</Text>
<Button
size="sm"
mt={2}
variant="outline"
borderColor="whiteAlpha.600"
color="inherit"
_hover={{ bg: "whiteAlpha.200" }}
onClick={() => {
chakraToast.closeAll();
onRetry();
}}
>
Retry
</Button>
</Box>
),
});
} else {
chakraToast({
title: "Error",
description: normalized.userMessage,
status: "error",
...DEFAULT_TOAST_OPTIONS,
duration: 6000, // Slightly longer for errors
});
}

return normalized;
},
Expand Down
Loading