feat(tokens): currency-to-currency Convert flow + v2 Get refresh - #1256
Merged
Conversation
bmc08gt
force-pushed
the
feat/convert-flow
branch
3 times, most recently
from
August 20, 2026 12:46
e93e05e to
1ad008c
Compare
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
force-pushed
the
feat/convert-flow
branch
from
August 20, 2026 13:00
1ad008c to
6c5dcb3
Compare
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.SwapViewModelgrew the destination-selection and quote plumbing;SwapFlowScreenis 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.
TokenSelectorRowis 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).
ModalBottomSheetSceneStrategylearned a half detent + wrap-content mode, exposed throughNavMetadata/TypesandSheetExpansion. Generic, but the currency picker is the only caller today.Slot-based
AmountWithKeypad. Refactored to takeaccessoryandamountFieldslots. V1 and every existing caller get the default centred header and no accessory — verified againstorigin/code/cash, not just against the pre-refactor commit. Get/Convert passLargeAmountField: 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.