From 57f20576fcb0a26721e451ef12e80c041962d189 Mon Sep 17 00:00:00 2001 From: energypantry Date: Sat, 7 Feb 2026 16:34:54 +0800 Subject: [PATCH 1/3] feat: allow dismissing permits from UI --- src/app-styles.css | 6 ++++++ src/components/dashboard-page.tsx | 9 +++++++++ src/components/permit-row.tsx | 18 ++++++++++++++++++ src/components/permits-table.tsx | 3 +++ 4 files changed, 36 insertions(+) diff --git a/src/app-styles.css b/src/app-styles.css index 8c1e2dec..5378e718 100644 --- a/src/app-styles.css +++ b/src/app-styles.css @@ -381,6 +381,12 @@ footer { width: 100%; } +.permit-cell .button-small { + height: 28px; + font-size: 12px; + padding-inline: 8px; + margin-top: 6px; +} .permit-cell button:not([disabled]):hover { opacity: 1; background-color: #ffffff1a; diff --git a/src/components/dashboard-page.tsx b/src/components/dashboard-page.tsx index b646030d..7c01fe26 100644 --- a/src/components/dashboard-page.tsx +++ b/src/components/dashboard-page.tsx @@ -233,6 +233,14 @@ export function DashboardPage() { [handleInvalidatePermit] ); + const onDismissPermit = useCallback( + (permit: PermitData) => { + // Local-only dismissal: mark as claimed/used so it gets filtered out. + updatePermitStatusCache(permit.signature, { status: "Claimed", isNonceUsed: true }); + }, + [updatePermitStatusCache] + ); + // --- UI Logic --- const toggleTableVisibility = () => { setIsTableVisible((prev) => !prev); @@ -550,6 +558,7 @@ export function DashboardPage() { onClaimPermit={handleClaimPermit} onClaimPermits={claimPermits} onInvalidatePermit={onInvalidatePermit} + onDismissPermit={onDismissPermit} isConnected={isConnected} chain={chain} isLoading={isLoading} diff --git a/src/components/permit-row.tsx b/src/components/permit-row.tsx index 593d276a..eb3be317 100644 --- a/src/components/permit-row.tsx +++ b/src/components/permit-row.tsx @@ -15,6 +15,7 @@ interface PermitRowProps { permit: PermitData; onClaimPermit: (permit: PermitData) => Promise<{ success: boolean; txHash: string }>; onInvalidatePermit?: (permit: PermitData) => Promise<{ success: boolean; txHash: string }>; + onDismissPermit?: (permit: PermitData) => void; isConnected: boolean; chain: Chain | undefined; isQuoting: boolean; @@ -29,6 +30,7 @@ export function PermitRow({ permit, onClaimPermit, onInvalidatePermit, + onDismissPermit, isConnected, chain, isQuoting, @@ -59,6 +61,8 @@ export function PermitRow({ const isOwner = !!address && permit.owner.toLowerCase() === address.toLowerCase(); const canInvalidate = isOwner && !isClaimed && !isInvalidating; + const isBeneficiary = !!address && permit.beneficiary.toLowerCase() === address.toLowerCase(); + const canDismiss = Boolean(isConnected && isBeneficiary && !isClaimed && !isClaimingThis); const rowClassName = (() => { if (!isReadyToClaim) return "row-invalid"; @@ -142,6 +146,15 @@ export function PermitRow({ } }; + const handleDismissClick = () => { + if (!canDismiss || !onDismissPermit) return; + const ok = window.confirm( + "Hide this permit from your pending list?\n\nThis only affects your current browser (stored locally) and can be undone by clearing site storage." + ); + if (!ok) return; + onDismissPermit(permit); + }; + const finalButtonText = networkMismatch ? (isSwitchingNetwork ? "Switching..." : `Switch to ${targetNetworkName}`) : buttonText; const formatGithubLink = (url: string | undefined): JSX.Element | string => { @@ -273,6 +286,11 @@ export function PermitRow({ {showButtonIcon && buttonIcon} {finalButtonText} + {canDismiss && ( + + )} {!networkMismatch && permit.claimError &&
Error: {permit.claimError}
} {!networkMismatch && permit.checkError &&
Check Failed: {permit.checkError}
} {!permit.claimError && !permit.checkError && permit.testError && ( diff --git a/src/components/permits-table.tsx b/src/components/permits-table.tsx index 4e4beb1c..0bd1c54c 100644 --- a/src/components/permits-table.tsx +++ b/src/components/permits-table.tsx @@ -8,6 +8,7 @@ interface PermitsTableProps { onClaimPermit: (permit: PermitData) => Promise<{ success: boolean; txHash: string }>; onClaimPermits: (permit: PermitData[]) => Promise; onInvalidatePermit?: (permit: PermitData) => Promise<{ success: boolean; txHash: string }>; + onDismissPermit?: (permit: PermitData) => void; isConnected: boolean; chain: Chain | undefined; isLoading: boolean; @@ -23,6 +24,7 @@ export function PermitsTable({ permits, onClaimPermit, onInvalidatePermit, + onDismissPermit, isConnected, chain, isLoading, @@ -62,6 +64,7 @@ export function PermitsTable({ permit={permit} onClaimPermit={onClaimPermit} onInvalidatePermit={onInvalidatePermit} + onDismissPermit={onDismissPermit} isConnected={isConnected} chain={chain} isQuoting={isQuoting} From 100741c9016c18a9d96b9805ec93e15575569b0d Mon Sep 17 00:00:00 2001 From: energypantry Date: Mon, 9 Feb 2026 09:33:22 +0800 Subject: [PATCH 2/3] fix: dismiss permits via local invalidation --- src/components/dashboard-page.tsx | 4 ++-- src/components/permit-row.tsx | 4 ++-- src/hooks/use-permit-data.ts | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/dashboard-page.tsx b/src/components/dashboard-page.tsx index 7c01fe26..dab07c53 100644 --- a/src/components/dashboard-page.tsx +++ b/src/components/dashboard-page.tsx @@ -235,8 +235,8 @@ export function DashboardPage() { const onDismissPermit = useCallback( (permit: PermitData) => { - // Local-only dismissal: mark as claimed/used so it gets filtered out. - updatePermitStatusCache(permit.signature, { status: "Claimed", isNonceUsed: true }); + // Local-only dismissal: mark as invalid so it naturally gets filtered out from the UI. + updatePermitStatusCache(permit.signature, { status: "Invalid" }); }, [updatePermitStatusCache] ); diff --git a/src/components/permit-row.tsx b/src/components/permit-row.tsx index eb3be317..e4993faa 100644 --- a/src/components/permit-row.tsx +++ b/src/components/permit-row.tsx @@ -149,7 +149,7 @@ export function PermitRow({ const handleDismissClick = () => { if (!canDismiss || !onDismissPermit) return; const ok = window.confirm( - "Hide this permit from your pending list?\n\nThis only affects your current browser (stored locally) and can be undone by clearing site storage." + "Dismiss this permit?\n\nThis will mark the permit as invalid and hide it from your pending list. It only affects your current browser (stored locally) and can be undone by clearing site storage." ); if (!ok) return; onDismissPermit(permit); @@ -287,7 +287,7 @@ export function PermitRow({ {finalButtonText} {canDismiss && ( - )} diff --git a/src/hooks/use-permit-data.ts b/src/hooks/use-permit-data.ts index 3543e765..f5d2a32a 100644 --- a/src/hooks/use-permit-data.ts +++ b/src/hooks/use-permit-data.ts @@ -42,7 +42,9 @@ export function usePermitData({ address, isConnected, preferredRewardTokenAddres const filtered: PermitData[] = []; permitsMap.forEach((permit) => { const nonceCheckFailed = !!(permit.checkError && permit.checkError.toLowerCase().includes("nonce")); - const shouldFilter = permit.isNonceUsed === true || nonceCheckFailed || permit.status === "Claimed"; + // Only show permits that are actionable to the current user. + // "Invalid" includes user-dismissed bogus permits (persisted locally) and other explicit invalid states. + const shouldFilter = permit.isNonceUsed === true || nonceCheckFailed || permit.status === "Claimed" || permit.status === "Invalid"; if (!shouldFilter) filtered.push(permit); }); setPermits(filtered); @@ -67,6 +69,7 @@ export function usePermitData({ address, isConnected, preferredRewardTokenAddres permit.tokenAddress && permit.type === "erc20-permit" && permit.status !== "Claimed" && + permit.status !== "Invalid" && permit.claimStatus !== "Success" && permit.claimStatus !== "Pending" ) { From e4aa94155fdc032fde061d60611d253118ef38ef Mon Sep 17 00:00:00 2001 From: energypantry Date: Wed, 11 Feb 2026 08:46:49 +0800 Subject: [PATCH 3/3] fix: gate dismiss action on callback --- src/components/permit-row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/permit-row.tsx b/src/components/permit-row.tsx index e4993faa..2cd6c786 100644 --- a/src/components/permit-row.tsx +++ b/src/components/permit-row.tsx @@ -62,7 +62,7 @@ export function PermitRow({ const isOwner = !!address && permit.owner.toLowerCase() === address.toLowerCase(); const canInvalidate = isOwner && !isClaimed && !isInvalidating; const isBeneficiary = !!address && permit.beneficiary.toLowerCase() === address.toLowerCase(); - const canDismiss = Boolean(isConnected && isBeneficiary && !isClaimed && !isClaimingThis); + const canDismiss = Boolean(onDismissPermit && isConnected && isBeneficiary && !isClaimed && !isClaimingThis); const rowClassName = (() => { if (!isReadyToClaim) return "row-invalid";