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..dab07c53 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 invalid so it naturally gets filtered out from the UI. + updatePermitStatusCache(permit.signature, { status: "Invalid" }); + }, + [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..2cd6c786 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(onDismissPermit && 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( + "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); + }; + 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} 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" ) {