Skip to content

feat(tokens): currency-to-currency Convert flow + v2 Get refresh - #1256

Merged
bmc08gt merged 9 commits into
code/cashfrom
feat/convert-flow
Aug 20, 2026
Merged

feat(tokens): currency-to-currency Convert flow + v2 Get refresh#1256
bmc08gt merged 9 commits into
code/cashfrom
feat/convert-flow

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Android port of iOS #604 — the currency-to-currency Convert flow, plus the Get refresh that shares its chrome. Last of the four iOS PRs in this batch (after #1253, #1254, #1255).

What's here

Convert flow (new). Currency → currency swap reachable from a token you hold: amount entry with a "Convert to [chip]" accessory, a Select Currency sheet for the destination, and a conversion-style receipt (TokenConvertReceiptScreen) with its own processing state. SwapViewModel grew the destination-selection and quote plumbing; SwapFlowScreen is the new flow host.

Get refresh. The buy path picks up the same shell — inline funding source in the accessory row, conversion-style receipt — so Get and Convert read as one component with two labels. TokenSelectorRow is the shared accessory ("Get with …" / "Convert to …").

Currency picker sheet.: its own purpose-config, corrected bottom edge fade (was starting too high and fading too fast, and dropping out mid-expand), and max(½ detent, wrap height) sizing — it opens at the half detent and only becomes expandable when the content actually needs more room.

Half-detent sheets (nav). ModalBottomSheetSceneStrategy learned a half detent + wrap-content mode, exposed through NavMetadata/Types and SheetExpansion. Generic, but the currency picker is the only caller today.

Slot-based AmountWithKeypad. Refactored to take accessory and amountField slots. V1 and every existing caller get the default centred header and no accessory — verified against origin/code/cash, not just against the pre-refactor commit. Get/Convert pass LargeAmountField: top-anchored, left-aligned, displayExtraLarge, with the small-screen padding behaviour iOS added.

Gating

Everything with a v1 counterpart — the Get (buy) path, the shared amount entry — is behind the newUI flag. Convert is new surface with no v1 side.

@github-actions github-actions Bot added type: feature New functionality area: ui Compose UI, theme, components, resources area: tokens Token accounts, balances, token info labels Aug 20, 2026
@bmc08gt
bmc08gt force-pushed the feat/convert-flow branch 3 times, most recently from e93e05e to 1ad008c Compare August 20, 2026 12:46
Convert moves value between two currencies the user already holds: a
destination picker on the amount-entry screen, a receipt that names both
sides, and the swap plumbing behind it.

The processing title reads a plain "Converting" rather than naming the
destination — the destination is already on the receipt the user just
confirmed, and long currency names truncated in the app bar. The receipt
shows token logos rather than currency flags, matching the withdrawal and
Get receipts; a flag can't tell two currencies apart when they share one
(or have none).
Bring the Convert flow in line with the iOS spec:

- New `displayExtraLarge` (74sp bold) type token, and map `textTertiary`
  to the real tertiary grey rather than White50.
- `AmountArea`/`AmountTextAnimated` gain a content colour and a horizontal
  alignment, so the amount can sit start-aligned with the prefix and the
  un-entered placeholder digits rendered in tertiary grey.
- `AmountWithKeypad` grows a `largeHeader` layout: the amount is
  top-anchored and left-aligned at display-extra-large over an
  "$X available" line, which simply turns red once the entry exceeds the
  balance instead of swapping in a separate over-limit sentence.
- The Convert destination picker is now a bottom sheet rather than a
  pushed step: `SwapStep.ConvertDestinationSelection` is marked as a
  `Sheet` and the swap `FlowHost` gets the modal-sheet scene strategy.
  Because that is an overlay scene, amount entry stays composed
  underneath and never re-runs its entry effects.
- Restrict Convert destinations to currencies the user already holds,
  matching iOS — acquiring something new is a Get, not a Convert.
Adds a half-detent option to the modal sheet scene and points the
select-currency step at it, so the picker opens at half height and sizes
its list to the detent instead of the full screen.
Brings the select-currency sheet to spec: row metrics, the list's edge
fade, and a height the sheet itself owns. Height is the sheet's business,
not the content's — restating a fraction of the screen in the content
capped the list short of the sheet's own bottom edge and stranded the
fade above it. The sheet now expands only when its content needs more
room than the detent gives.
Turns AmountWithKeypad into a slot-based component so callers supply the
accessory row rather than the component guessing at it, and gives the v2
layout the gap above the amount field that the spec calls for — including
on small screens, where a bare Spacer under heightIn(max=) measured to
the minimum and collapsed.
…tyle receipt

Reframes Get as a conversion between two currencies the user holds: the
funding source is picked inline on the amount-entry screen instead of on
a separate step, and the receipt reads "You Get / Amount to convert /
Conversion fee / You Pay". The accessory token chip cross-fades when the
selection changes rather than popping.
Get and Convert had separate receipt screens that had converged on the
same layout: the same bordered card, the same anchor rows, the same
scaffold and bottom bar. Everything still differing between them was
data — which side leads, the warning copy, the confirm label and its
progress state — so it now arrives as data, and the fee math the two
genuinely disagree on stays in its own adapter.

Two knock-on changes: Convert picks up Get's shimmer, so pending token or
amount data no longer blanks the screen instead of rendering it, and the
line items keep Convert's spacing (Get's was 5dp tighter). The token name
label under each amount is gone from both.

Fee lines test hasDisplayableValue rather than a hardcoded 0.01, which
was USD-shaped: a sub-unit fee in another currency now reads "~ ¥0"
instead of a bare "¥0".
AppNavHost defaulted predictivePopTransitionSpec to transitionSpec — the
*forward* spec — so dragging back out of a flow step slid the screen
being returned to in from the right instead of the left. Every other host
passes the parameter explicitly and so hid the bad default; FlowHost did
not, which is why backing out of the Get/Convert receipts ran the wrong
way.

Default it to popTransitionSpec and thread it through FlowHost. Hosts
that don't distinguish push from pop are unaffected: for them the two
specs are the same lambda.
@bmc08gt
bmc08gt force-pushed the feat/convert-flow branch from 1ad008c to 6c5dcb3 Compare August 20, 2026 13:00
The private `netTransferAmount` producer deferred to
`State.confirmedNetTransferAmount` — its own output from a previous trip
through the entry screen — while `enteredAmount` and `feeAmount` beside it
recomputed from the live amount delegate. All three feed the same
`OnAmountAccepted` event, so a second pass produced a receipt that mixed
the new entry with the old total: enter $1, go back, enter $0.50, and the
receipt paired a $0.50 debit and a $0.005 fee with a $0.99 "You Receive".

The cache-first read is a leftover from before #829, when a single getter
on `State` served both roles and the keypad lived on `State` too; there it
was what pinned the number once accepted. The delegate split gave that
freeze its own home in `State.netTransferAmount`, but the producer copy
kept the prefix. `enteredAmount` and `feeAmount` were rewritten during that
split and came out cache-free, which is the asymmetry behind the bug.

Affects v1's Sell path the same way, so it is left ungated — a wrong number
is not a v1 pixel worth preserving.
@bmc08gt
bmc08gt merged commit e9684a2 into code/cash Aug 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: tokens Token accounts, balances, token info area: ui Compose UI, theme, components, resources type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant