feat: send form validation, pending states, claim-link success screen, and QR code - #519
Conversation
Strengthen amount validation on the single-recipient send form's
details step:
- Reject amounts below 0.0000001 (1 stroop), Stellar's smallest
indivisible unit, with a clear inline message.
- Reject amounts with more than 7 decimal places, since Stellar
cannot represent finer precision and the extra digits would
otherwise be silently dropped.
The issue's third criterion ("invalid Stellar destination format
rejected before submission") does not apply to this form: per the
product's own FRD (docs/bridgelet-frd-ui-ux.md) and the app's
ephemeral-account/claim-link model, the sender never enters a
recipient wallet address — that's collected from the recipient
during claim, where it is already validated (components/claim-status-card.tsx).
Client-side balance-vs-amount validation is also out of scope: the
frontend has no wallet-balance-fetching capability today.
Adds unit tests for the new validation rules.
Give the sender clear feedback during the gap between clicking "Confirm & Send" and reaching the success screen: - A distinct blue status banner (separate from the plain button-label change that existed before) shows while preparing, awaiting Freighter approval, submitting, and — after a few seconds in the submitting phase — pending confirmation on the Stellar network. - The submit button was already disabled during this window (existing behavior); it now stays disabled through the same distinct states shown in the banner. - If the wait stretches past ~12 seconds, a reassurance message appears so the sender doesn't think the app has frozen or needs a double-submit. The network layer already enforces a 15s request timeout (lib/create-bridgelet-client.ts) that surfaces as a retryable error; this change addresses the UX side of "unusually long" waits within that budget. Adds unit tests covering the pending banner and button disabling.
…rg#422 On the send flow's success screen: - Show the full claim URL prominently in a dedicated, monospace box (previously it was only embedded in the WhatsApp share link and the NFC-write payload, never displayed as text). - Add a one-click "Copy link" button with a "Copied!" confirmation, matching the existing copy-to-clipboard pattern used elsewhere in the app (components/share-prompt.tsx). - Show the claim link's absolute expiration deadline (e.g. "August 27, 2026, 5:23 PM") alongside the existing relative "7 days" phrasing, computed from the moment the account was created. Extends the confirm-step test suite to cover the new claim-link box, copy behavior, and expiry deadline text.
Adds a genuinely scannable QR code to the send flow's success screen for in-person or SMS-limited disbursement, encoding the exact claim URL: - New ClaimQrCode component (components/send-form/claim-qr-code.tsx) using the `qrcode` package to generate a spec-compliant PNG entirely client-side — no network requests, so the claim URL/token never leaves the browser. - Downloadable as a PNG image via a "Download QR code" link. - Descriptive accessible alt text ("QR code that opens your Bridgelet claim link when scanned with a phone camera") rather than exposing the raw URL as alt text. Note: an existing decorative QR component (components/qr-code.tsx, used only on the claim page's general app-referral share prompt) draws a QR-shaped pattern from a hash of the input and is not a real, scannable QR encoding. This issue's "verified round-trip via scan test" criterion is why a real encoder (`qrcode`) was used here instead of reusing that component; the existing one is left as-is since fixing it is a separate concern from this issue. package.json/package-lock.json: added `qrcode` (runtime) and `@types/qrcode` (dev). package-lock.json was intentionally NOT committed — see PR description for why. Adds unit tests for the new component and extends the confirm-step suite to assert the QR code receives the exact claim URL on success.
|
@A6dulmalik Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@A6dulmalik is attempting to deploy a commit to the aminubabafatima8-gmailcom's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Merged to \main\ as part of a consolidated integration (branch \merge-final, now pushed as commit \5155235\ on main). Validation on main:
Note: the Mobile CI Android/iOS native debug build jobs still fail on pre-existing repo issues unrelated to this PR (react-native-reanimated vs RN 0.81 compile error, and the macos-14 runner's Xcode 15.4 vs RN's required Xcode >= 16.1). Tracked as follow-up work, not blocked by this change. |
Summary
Implements four related send-flow improvements from the Stellar Wave Program batch (issues #420–#423), each as its own commit:
ClaimQrCodecomponent renders a genuinely scannable QR code (via theqrcodepackage, 100% client-side, no network calls) for the claim link, with a download button and descriptive accessible alt text.#420 — not fully closed, see below
The issue's third criterion ("invalid Stellar destination format rejected before submission") does not apply to this form. Per the product's own FRD (
docs/bridgelet-frd-ui-ux.md) and the app's ephemeral-account/claim-link model, the sender never enters a recipient wallet address on/send— that's collected from the recipient during claim, where it's already validated (components/claim-status-card.tsx,components/wallet-address-input.tsx). Client-side "amount vs. sender balance" validation is also out of scope: the frontend has no wallet-balance-fetching capability today (no Horizon/account-balance call anywhere inlib/), and implementing one would be a separate, larger feature. I implemented the parts of #420 that do apply (minimum amount, decimal precision) and am flagging the rest rather than guessing at a bigger redesign.Pre-existing issues found (not fixed here, out of scope)
While validating this branch I found
mainis currently broken independent of this PR:frontend/lib/create-bridgelet-client.ts,components/auto-sweep-retry.tsx,components/education-tooltip.tsx,components/mobile-deep-link.tsx, andcomponents/multi-step-progress.tsxcontain literal escaped-backtick sequences (e.g.\`${x}\`instead of an actual template literal) that fail to parse under both TypeScript and esbuild. This breaksnpm run typecheck,npm run build, and any test that transitively importscreate-bridgelet-client.ts(confirmed viagit diff origin/main— these files are untouched by this branch).frontend'spackage-lock.jsonis out of sync withpackage.json(a peer-dependency conflict between@storybook/nextjs-vite@^10.4.6and@chromatic-com/storybook@^3.2.0, which peer-depends on Storybook 8), so plainnpm ci/npm installfails withERESOLVE. This matches Frontend CI's current failing status onmain(confirmed viagh run list).Given the "no drive-by refactors" scope for this PR, I did not fix these — flagging for maintainer awareness since they block CI/build entirely regardless of this PR.
Dependency change
Added
qrcode(runtime) +@types/qrcode(dev) tofrontend/package.jsonfor #423.frontend/package-lock.jsonwas intentionally left unmodified — because of the pre-existing peer-dependency conflict above, regenerating it locally (even with--legacy-peer-deps) rewrites ~2,800 unrelated lines as npm re-resolves the whole tree differently, which would be a huge, unreviewable, out-of-scope diff. Once a maintainer resolves the underlying Storybook/Chromatic version conflict, regenerating the lockfile withqrcodeincluded will be a small, clean diff.Checks run
npx eslint <changed files>: 0 errors, 2 pre-existing-pattern warnings (no-img-elementon the new<img>, and a pre-existingexhaustive-depswarning on a hook I didn't modify).npm run lint(whole repo): 5 errors, all in the pre-existing broken files listed above — none in files this PR touches.npx tsc --noEmit(whole repo): same pre-existing files fail to parse; no errors involving any file this PR touches.npx vitest run components/send-form/: 61 passed, 2 failed. Both failures areconfirm-step.test.tsx(mine and a stale duplicate undertest-result/) failing to even transform, because they transitively import the corruptedcreate-bridgelet-client.tsabove — not a logic failure in this PR's code.details-step.test.tsx(10/10) andclaim-qr-code.test.tsx(3/3), which don't hit that import path, pass cleanly, using the same mocking patterns.npm run build: fails for the pre-existing reasons above (confirmed reproducible on a cleanorigin/maincheckout of the same file).npm ci/npm install: fails for the pre-existing lockfile conflict above; usednpm install --legacy-peer-depslocally to get a workingnode_modulesfor the checks above.Test plan
details-step.test.tsx)confirm-step.test.tsx)confirm-step.test.tsx)claim-qr-code.test.tsx)qrcodepackage is a widely-used, spec-compliant encoder, but a maintainer should verify a real scan on a device before mergeCloses #421
Closes #422
Closes #423
Closes #420