Skip to content

feat(buy-sell): align quote card with the DFX web widget - #255

Open
joshuakrueger-dfx wants to merge 16 commits into
DFXswiss:developfrom
joshuakrueger-dfx:feat/buysell-quote-card-redesign
Open

feat(buy-sell): align quote card with the DFX web widget#255
joshuakrueger-dfx wants to merge 16 commits into
DFXswiss:developfrom
joshuakrueger-dfx:feat/buysell-quote-card-redesign

Conversation

@joshuakrueger-dfx

@joshuakrueger-dfx joshuakrueger-dfx commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Not symptom-driven: proactive redesign requested by JK, phase 2 of aligning the mobile Buy/Sell screens with the DFX web widget (dfx.swiss). Not a reported bug.

Scale: 2 screens (Buy, Sell), affects every user going through the buy/sell flow — this is the primary conversion path of the app.

Smaller fix considered: N/A — this is already scoped down to layout/data-plumbing plus the required state coverage for this phase. Explicitly out of scope for this PR: wallet-switcher bottom sheet, swap icon between buy/sell, hamburger-menu restructuring, and live rate countdown — each is its own follow-up phase.

Summary

  • FeeDto (wallet-side type) synced with the backend's actual response shape — it was missing bank, bankFixed, bankVariable, platform, networkStart, so the bank fee was silently unreadable despite being in every API response (verified against ~/DFXswiss/api's fee.dto.ts, not guessed).
  • Buy presets fixed to 50/100/250/500 (was 100/500/1000/5000) and moved above the amount input.
  • Quote card is now collapsed by default: shield icon + effective rate headline + fee-total badge + chevron toggle, matching the widget's summary-first layout.
  • Bank fee row added (uses the now-visible fees.bank).
  • "You receive" row gets the accent color.
  • Static SEPA payment-method row above the CTA (Buy only — Sell has no payment-method choice at this step).
  • CTA now names the target asset plus a trailing arrow icon (PrimaryButton got an additive, optional icon slot).
  • Collapsed Buy/Sell quote cards retain visible loading, continue, unsupported, and rejected-quote states; the static payment-method row is no longer presented as an action.

Caught and fixed my own spec mistake before landing: the first pass had the quote-card headline backwards (1 {currency} ≈ {crypto amount} {asset} instead of the widget's 1 {asset} ≈ {price} {currency}). Verified the correct source field (exchangeRate, used directly — not rate, not inverted) against the actual amount/estimatedAmount ratio in the existing test fixtures before landing, not just against the field name.

Test plan

  • npm run typecheck — 0 errors
  • npm run lint — 0 errors (existing warnings only)
  • npm run format — passed
  • npx jest --coverage --runInBand — 107/107 suites, 1395/1395 tests; 99.83% lines, 99% floor
  • npm run check:matrix — 63 referenced paths, 17 flags, 37 screens
  • npm run check:coverage-floor and per-file floors — passed
  • visual coverage manifest and self-test — passed; committed buy-amount and sell-no-balance PNGs generated from rendered iOS simulator screens
  • iOS release build — passed
  • isolated iOS Detox Buy/Sell smoke — 2/2 passed (Buy amount and Sell no-balance); the full-feature authenticated attempt was separately reproduced as an ErrorBoundary Maximum update depth exceeded before Buy/Sell navigation, an existing full-variant simulator/environment blocker outside this diff
  • TaprootFreak mechanical gate rerun on the pushed head after every fix commit; current mechanical findings are limited to the external audit advisory and the manually triggered visual/Maestro checks while they run

Final pass (9b586c1):
Coherent: The diff is one Buy/Sell quote-card redesign: shared quote presentation, amount-entry affordances, explicit collapsed error/loading states, and focused screen/E2E/visual coverage; the duplicate locale keys fixed in the final commit are part of that same surface.
Nothing extra: No permanent feature activation, encryption-key changes, real swap implementation, wallet-switcher, menu restructuring, or live-rate countdown was added; those are separate product/security scopes and are explicitly out of this PR.
Sources closed: PR scope and web-widget reference are implemented; all 16 commits were rechecked; the three GitHub review channels and review list are empty; local Jest/typecheck/lint/format/matrix/coverage/visual/build gates passed; the remaining browserslist audit failure is present on develop unchanged and is reported as external.

The wallet's local FeeDto type only declared rate/fixed/network/min/dfx/
total, missing bank/bankFixed/bankVariable/platform/networkStart that
the DFX API's FeeDto already sends (api/.../fee.dto.ts). The bank fee
was silently unreadable in the app despite being in every response.
Purely additive: PrimaryButton's new icon prop is optional, existing
callers are unaffected.
- Presets fixed to 50/100/250/500, moved above the amount input
- Quote card collapsed by default: shield icon, effective rate
  ('1 {asset} ~ {price} {currency} incl. fees', using exchangeRate
  directly - verified against the amount/estimatedAmount ratio in the
  quote fixtures, not just the field name), fee-total badge, chevron
- Bank fee row added now that FeeDto carries it
- 'You receive' row highlighted in the accent color
- Static SEPA payment-method row above the CTA (Buy only)
- CTA now names the target asset with a trailing arrow icon

Buy and Sell mirror each other except presets and the payment-method
row, which are Buy-only by design (Sell trades a crypto amount, not a
fiat preset, and has no payment-method choice before confirming).
BuyScreenImpl's CTA test now expects 'buy.title BTC' instead of the old
common.continue placeholder; Icon and PrimaryButton get coverage for
the new arrow-right variant and icon slot.
The wallet's local FeeDto type only declared rate/fixed/network/min/dfx/
total, missing bank/bankFixed/bankVariable/platform/networkStart that
the DFX API's FeeDto already sends (api/.../fee.dto.ts). The bank fee
was silently unreadable in the app despite being in every response.
Purely additive: PrimaryButton's new icon prop is optional, existing
callers are unaffected.
- Presets fixed to 50/100/250/500, moved above the amount input
- Quote card collapsed by default: shield icon, effective rate
  ('1 {asset} ~ {price} {currency} incl. fees', using exchangeRate
  directly - verified against the amount/estimatedAmount ratio in the
  quote fixtures, not just the field name), fee-total badge, chevron
- Bank fee row added now that FeeDto carries it
- 'You receive' row highlighted in the accent color
- Static SEPA payment-method row above the CTA (Buy only)
- CTA now names the target asset with a trailing arrow icon

Buy and Sell mirror each other except presets and the payment-method
row, which are Buy-only by design (Sell trades a crypto amount, not a
fiat preset, and has no payment-method choice before confirming).
BuyScreenImpl's CTA test now expects 'buy.title BTC' instead of the old
common.continue placeholder; Icon and PrimaryButton get coverage for
the new arrow-right variant and icon slot.
Verifies the icon prop is truly additive (absent by default, hidden
during loading) instead of only asserting both texts render.
@joshuakrueger-dfx
joshuakrueger-dfx marked this pull request as ready for review September 1, 2026 14:07
@joshuakrueger-dfx joshuakrueger-dfx added the needs-visual Re-run Visual Regression on this PR label Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-visual Re-run Visual Regression on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant