Skip to content

fix(send-link): keep submit-time errors visible through balance polls#2513

Merged
jjramirezn merged 3 commits into
devfrom
fix/send-link-error-swallowed
Jul 25, 2026
Merged

fix(send-link): keep submit-time errors visible through balance polls#2513
jjramirezn merged 3 commits into
devfrom
fix/send-link-error-swallowed

Conversation

@jjramirezn

@jjramirezn jjramirezn commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

QA repro: "Error message when creating sendlink disappears (found with signature cooldown)."

The send-link balance-gate useEffect and submit-time failures (Rain withdrawal-signature cooldown 425, balance-settling copy, passkey errors) share one errorState slot. Right after a collateral spend the ~30s-polled spendable balance oscillates around the typed amount, so the gate overwrote the submit-time error with INSUFFICIENT_BALANCE_MESSAGE on the dip, and its recovery branch then cleared that swapped-in message — the real error silently vanished while the user still couldn't spend for ~2 min. The 2026-06-24 guard (#4661a2b) protected the clear side but not the overwrite side.

Fix — a complete ownership model for the shared error slot:

  • The gate only claims the slot when it's free or already showing its own message; submit-time errors persist through background balance updates.
  • A momentarily-unavailable balance (query without data) releases only the gate's own error — never a submit-time failure the user hasn't acted on.
  • Editing the amount releases the slot back to the gate (a changed amount means the old failure no longer describes what's being submitted); the gate immediately re-flags a genuine shortfall on the new amount. An emptied amount clears everything — that's user input, and a Retry with no amount would be a dead button.

Evidence: PostHog session 019f8f8c-d4d5-775c-97ab-3e47c532a694 (2026-07-23 15:18) — $100 mixed-strategy link armed the signature lock, $20 retry hit the cooldown 425, error shown then swallowed; funds were oscillating right at the $20 boundary (two insufficient routings seconds earlier prove it).

Design notes / accepted trade-offs

  • Ownership is inferred from the message string (INSUFFICIENT_BALANCE_MESSAGE = gate-owned). A source-tagged error state (source: 'gate' | 'submit') would be more durable if a third owner ever appears — noted as a possible follow-up, not needed for two owners.
  • If funds genuinely drop while a submit error is displayed, the submit-side affordability check re-runs on the next Retry tap and surfaces the shortfall then.
  • Bundled boy-scout fix: the touched view's @/context barrel import → specific loadingStates.context file (same module instance; clears a no-restricted-imports error).

Risks / breaking changes

None cross-repo. FE-only, narrow behavior change in when an already-existing error string is shown/cleared. Gate-owned insufficient-balance errors keep their appear/clear behavior (covered by test).

QA

  • npm test -- --testPathPattern Initial.link.send.view — 4-test regression suite; the original repro test verified failing on dev without the fix.
  • Manual: create a collateral-funded send link, immediately create another → cooldown error must stay visible until retry/edit; editing the amount clears it; typing an amount above balance shows the insufficient-balance copy.

Screenshots

N/A — no visual change; the fix keeps the existing ErrorAlert mounted (copy: "A previous withdrawal signature is still active. Try again in about 2 min.") instead of it disappearing.

The balance-gate effect shares one errorState slot with submit-time failures (Rain signature-cooldown 425, settling copy). Right after a collateral spend the ~30s-polled spendable balance oscillates around the typed amount, so the gate overwrote the submit error with the insufficient-balance copy on the dip and then cleared it on the recovery - the user's real error silently vanished while they still couldn't spend (PostHog session 019f8f8c-d4d5-775c-97ab-3e47c532a694). The gate now only claims the error slot when it is free or already showing its own message; submit-time errors persist until the user retries or edits.
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jul 24, 2026 10:42pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b043c23e-eb88-4570-a566-4f0f53db0bad

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

LinkSendInitialView now avoids overwriting active submit errors with insufficient-balance messages. New tests cover error persistence during balance changes and clearing balance-gate errors after recovery.

Changes

Link send error ownership

Layer / File(s) Summary
Balance-gate error ownership
src/components/Send/link/views/Initial.link.send.view.tsx
The insufficient-balance gate only claims an unused error slot or replaces its own message, and the effect also tracks error visibility.
Error ownership regression coverage
src/components/Send/link/views/__tests__/Initial.link.send.view.test.tsx
Adds mocked rendering helpers and tests for preserving submit-time cooldown errors and clearing insufficient-balance errors after recovery.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: hugo0

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: preserving submit-time send-link errors during balance polling.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/send-link-error-swallowed

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 6267.93 → 6268.09 (+0.16)
Findings: 0 net (+3 new, -3 resolved)

🆕 New findings (3)

  • medium high-mdd — src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: MDD 61.8 (uses across many lines from declarations)
  • medium complexity — src/components/Send/link/views/Initial.link.send.view.tsx — CC 29, MI 52.06, SLOC 133
  • low high-dlt — src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: DLT 25 (calls 25 distinct functions — high context load)

✅ Resolved (3)

  • src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: MDD 57.1 (uses across many lines from declarations)
  • src/components/Send/link/views/Initial.link.send.view.tsx — CC 22, MI 51.54, SLOC 123
  • src/components/Send/link/views/Initial.link.send.view.tsx:25 — LinkSendInitialView: DLT 24 (calls 24 distinct functions — high context load)

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2146 ran, 0 failed, 0 skipped, 35.6s

📊 Coverage (unit)

metric %
statements 60.8%
branches 44.4%
functions 50.0%
lines 61.2%
⏱ 10 slowest test cases
time test
3.7s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.2s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › keeps stickers off the username pill (final pass respects the keep-out)
0.2s src/utils/__tests__/url.utils.test.ts › uses the public BASE_URL in Capacitor, not the localhost WebView origin
0.2s src/utils/__tests__/demo-balance.test.ts › keeps a spent-down balance across a cold start within the TTL
0.2s src/components/Request/__tests__/request-states.test.tsx › API failure shows error message and toast
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

…ched view

New code must lint clean: type the Button mock and drop the any-spread in the useWallet mock. The touched view's '@/context' barrel import is swapped for the specific loadingStates.context file (same module instance, restricted-imports rule).
@jjramirezn

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Send/link/views/Initial.link.send.view.tsx`:
- Around line 153-161: Complete error ownership in the balance polling and
amount editing flow: update the balance === undefined branch so it clears the
balance value without clearing an existing error, and replace the direct
AmountInput setTokenValue callback with a handler that clears the active error
before updating the amount. Add regressions covering both transitions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bd7b1704-db03-414a-9e1b-1ebabc8fc472

📥 Commits

Reviewing files that changed from the base of the PR and between 07bda55 and 5933177.

📒 Files selected for processing (2)
  • src/components/Send/link/views/Initial.link.send.view.tsx
  • src/components/Send/link/views/__tests__/Initial.link.send.view.test.tsx

Comment thread src/components/Send/link/views/Initial.link.send.view.tsx
Two more transitions could still eat a submit-time error: a momentarily-unavailable balance cleared every error (now releases only the gate's own message; an emptied amount still clears all - that IS user input), and editing the amount left a stale failure on screen (now released back to the gate, which re-flags a genuine shortfall on the new amount). Regression tests for both.
@jjramirezn
jjramirezn marked this pull request as ready for review July 24, 2026 22:43
@jjramirezn
jjramirezn requested a review from Hugo0 July 24, 2026 22:43
@jjramirezn
jjramirezn merged commit b806cdc into dev Jul 25, 2026
18 of 20 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.

1 participant