From 79aa0b639ebf2be1f1255a86efbc203aee239c66 Mon Sep 17 00:00:00 2001 From: rejoicetukura-blip Date: Mon, 31 Aug 2026 10:51:02 +0000 Subject: [PATCH] fix: exponential backoff retry, masked amounts, session dismiss banner - useTransactionWithRetry: exponential backoff (1s base, 30s cap), configurable maxRetries (default 5), expose attemptCount - ConfidentialPaymentFlow: mask amounts by default with a show/hide toggle - UpgradeBanner: dismiss via sessionStorage flag so it reappears next session - DashboardClient already syncs filters to URL search params; no change needed Closes #679 Closes #669 Closes #668 Closes #667 --- src/components/ConfidentialPaymentFlow.tsx | 24 +++++++++++++++------- src/components/UpgradeBanner.tsx | 17 +++++++-------- src/hooks/useTransactionWithRetry.ts | 17 ++++++++++----- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/components/ConfidentialPaymentFlow.tsx b/src/components/ConfidentialPaymentFlow.tsx index a321caa..64922a0 100644 --- a/src/components/ConfidentialPaymentFlow.tsx +++ b/src/components/ConfidentialPaymentFlow.tsx @@ -62,6 +62,7 @@ export default function ConfidentialPaymentFlow({ const [step, setStep] = useState("commit"); const [txHash, setTxHash] = useState(null); const [error, setError] = useState(null); + const [amountsVisible, setAmountsVisible] = useState(false); useEffect(() => { if (!publicKey) return; @@ -178,15 +179,24 @@ export default function ConfidentialPaymentFlow({ )}
- +
+ + +
(null); const [mounted, setMounted] = useState(false); + const [dismissed, setDismissed] = useState(false); useEffect(() => { setMounted(true); + setDismissed(sessionStorage.getItem(DISMISSED_KEY) === "true"); let cleanup: (() => void) | undefined; try { cleanup = (splitClient as any).watchContractUpgrade((info: UpgradeInfo) => { - const key = `${STORAGE_KEY_PREFIX}${info.version}`; - const dismissed = localStorage.getItem(key) === "true"; - if (!dismissed) { - setUpgrade(info); - } + setUpgrade(info); }); } catch { // watchContractUpgrade not available in this environment — silently skip @@ -37,12 +35,11 @@ export default function UpgradeBanner() { }, []); const dismiss = () => { - if (!upgrade) return; - localStorage.setItem(`${STORAGE_KEY_PREFIX}${upgrade.version}`, "true"); - setUpgrade(null); + sessionStorage.setItem(DISMISSED_KEY, "true"); + setDismissed(true); }; - if (!mounted || !upgrade) return null; + if (!mounted || !upgrade || dismissed) return null; const changelogHref = upgrade.changelogUrl ?? "/changelog"; diff --git a/src/hooks/useTransactionWithRetry.ts b/src/hooks/useTransactionWithRetry.ts index 38c4acd..bd80ac2 100644 --- a/src/hooks/useTransactionWithRetry.ts +++ b/src/hooks/useTransactionWithRetry.ts @@ -1,4 +1,4 @@ -import { useCallback, useRef } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { useToast } from '@/contexts/ToastContext'; interface RetryOptions { @@ -7,15 +7,18 @@ interface RetryOptions { retryableStatusCodes?: number[]; } +const MAX_DELAY_MS = 30000; + const DEFAULT_OPTIONS = { - maxRetries: 3, - initialDelayMs: 3000, + maxRetries: 5, + initialDelayMs: 1000, retryableStatusCodes: [429], }; export function useTransactionWithRetry(options?: RetryOptions) { const toast = useToast(); const abortControllers = useRef>(new Map()); + const [attemptCount, setAttemptCount] = useState(0); const opts = { ...DEFAULT_OPTIONS, ...options }; const getStatusCode = (error: any): number | undefined => { @@ -48,6 +51,7 @@ export function useTransactionWithRetry(options?: RetryOptions) { let toastId: string | null = null; for (let attempt = 1; attempt <= opts.maxRetries; attempt++) { + setAttemptCount(attempt); try { // Check if user cancelled if (abortControllers.current.get(operationId)?.signal.aborted) { @@ -65,7 +69,10 @@ export function useTransactionWithRetry(options?: RetryOptions) { attempt < opts.maxRetries; if (isRetryable) { - const delayMs = opts.initialDelayMs * Math.pow(2, attempt - 1); + const delayMs = Math.min( + opts.initialDelayMs * Math.pow(2, attempt - 1), + MAX_DELAY_MS + ); const countdownSeconds = Math.ceil(delayMs / 1000); // Show countdown toast @@ -114,5 +121,5 @@ export function useTransactionWithRetry(options?: RetryOptions) { } }, []); - return { executeWithRetry, cancel }; + return { executeWithRetry, cancel, attemptCount }; }