Skip to content

feat(frontend): optimistic UI for register/claim with rollback (#627)#663

Merged
joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
anuoluwaponiorimi:feat/optimistic-register-claim
Jun 19, 2026
Merged

feat(frontend): optimistic UI for register/claim with rollback (#627)#663
joelpeace48-cell merged 1 commit into
FinesseStudioLab:mainfrom
anuoluwaponiorimi:feat/optimistic-register-claim

Conversation

@anuoluwaponiorimi

Copy link
Copy Markdown
Contributor

Summary

Adds optimistic UI to the register and claim flows (#627): the expected state is reflected the instant the user submits, then reconciled with chain truth on success or rolled back on failure with a clear, class-specific error. Successful actions feel instant; failed actions recover cleanly.

What changed

useOptimisticAction hook — src/hooks/useOptimisticAction.js

A small, reusable lifecycle engine: idle → pending → success | error.

  • Applies an optimistic update immediately, awaits the real action, reconciles on success, rolls back on failure.
  • Double-submit guard: a synchronous in-flight latch (ref) ignores a second run while one is in flight — a double-click can never fire two transactions.
  • Rollback only runs if the optimistic update was actually applied (idempotent, correct even if the failure happens before the optimistic step).

Error classification — src/lib/errorMapping.js

  • classifyError / mapError bucket every failure into a class — contract | wallet | network | rpc | validation | unknown — with distinct messaging:
    • network/RPC outages → "not submitted / temporarily unavailable, try again" (retryable),
    • contract reverts → the precise existing per-code copy (e.g. "This campaign has reached its participant limit"),
    • wallet rejection → "Signing was cancelled" (not a system error).
  • Directly addresses the issue's "Network/RPC error vs contract revert → distinct messaging" edge case.

Components

  • RegisterCampaign.jsx: participant status flips to Registered optimistically on submit; rolls back to the prior status on failure; shows a pending card then a confirmed/error state.
  • ClaimRewards.jsx: clears the input + shows a pending indicator immediately; restores the entered amount on failure; reconciles the on-chain balance via onClaimSuccess.
  • TransactionStatus.jsx: new variant of pending | success | error (backward compatible — default success), so a pending card can render before a hash exists and rolled-back errors render inline.

Acceptance criteria

  • ✅ Successful actions feel instant (optimistic state on submit).
  • ✅ Failed actions roll back to the correct state with a clear, mapped error.
  • ✅ Double-submit guarded; pending indicators shown.
  • ✅ Distinct messaging for network/RPC vs contract revert.

Testing

src/hooks/useOptimisticAction.test.jsx (under the vitest include glob, so it runs in CI):

  • optimistic-apply then reconcile on success;
  • rollback on failure + mapped error class/code/message;
  • no rollback when the optimistic step itself throws;
  • double-submit guard (second submit skipped, action called once);
  • reset;
  • error classification — contract vs wallet vs network vs RPC messaging.

Local: vitest (new suite) ✅, eslint . ✅ (0 errors), prettier --check ✅, vite build ✅.

Note on E2E

The task list includes an E2E that forces a failure to assert rollback. The existing Playwright lifecycle suite (tests/e2e/campaign-lifecycle.test.ts) test.skips itself when no backend is reachable, and the CI Playwright run has no backend — so a register/claim failure E2E needs the documented docker-compose environment. Rollback is therefore asserted here at the hook/integration level; the live E2E can follow in that environment.

)

Register and claim now reflect the expected state the instant the user
submits, then reconcile with chain truth on success or roll back on
failure — so successful actions feel instant and failures recover cleanly.

- useOptimisticAction hook (src/hooks): drives the
  idle → pending → success | error lifecycle. Applies an optimistic update,
  awaits the action, reconciles on success, and rolls back on failure. A
  synchronous in-flight latch guards against double-submit (a second click
  is ignored, never fires a second transaction). Rollback only runs if the
  optimistic update was actually applied.
- errorMapping: add classifyError + mapError giving each failure a class
  (contract | wallet | network | rpc | validation | unknown) with distinct
  messaging — a network/RPC outage now reads differently from a contract
  revert, and contract reverts keep their precise per-code copy.
- RegisterCampaign: status flips to "Registered" optimistically on submit;
  rolls back to the prior status on failure with a mapped error.
- ClaimRewards: clears the input optimistically + shows a pending indicator;
  restores the amount on failure; reconciles the balance via onClaimSuccess.
- TransactionStatus: new pending/success/error variant (backward compatible)
  so the pending state shows before a hash exists and errors render inline.

Tests (src/hooks/useOptimisticAction.test.jsx, runs under the vitest
include): optimistic-apply + reconcile, rollback on failure, no rollback
when the optimistic step itself throws, double-submit guard, reset, and
error classification (contract vs wallet vs network vs rpc messaging).

A live register/claim E2E that forces a failure needs the docker-compose
backend (the CI playwright run has none and skips the lifecycle suite), so
rollback is covered here at the hook/integration level; the E2E can follow
in that environment.
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

@anuoluwaponiorimi is attempting to deploy a commit to the joelpeace48-cell's projects Team on Vercel.

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
trivela-frontend Ready Ready Preview, Comment Jun 19, 2026 7:36pm

@joelpeace48-cell joelpeace48-cell merged commit b759dc1 into FinesseStudioLab:main Jun 19, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants