feat(activity): open a tapped activity row's details - #733
Merged
Conversation
An activity row was a label everywhere it was drawn, so the only way to read an entry's rate, token quantity or id was not to have one, and the only way to cancel a pending cash link was to find it again in the per-token history. Android closed this in code-payments/code-android-app#1417 and code-payments/code-android-app#1418; this is the same screen on iOS. `TransactionDetails` maps an `Activity` to what the screen shows: which of the 13 kinds it was, its heading and subtitle, whether it can be cancelled, and whether it has a conversation to open. The three cash kinds stay distinct because `MessageMetadata` already distinguishes them, and there is no received-cash-link case because a claimed link arrives as a plain receive. `TransactionDetailsTests` mirrors Android's `TransactionDetailsMapperTest` case for case. `ActivityRow` now pushes `transactionDetails` itself rather than each of its three call sites wiring a tap, so a row opens the same screen in the wallet's Recent section, the currency info screen, the cross-token history and a token's own history. The avatar the row draws moves to `ActivityAvatar`, which the details header draws at 80pt from the same `ActivityResolution`, so the screen opens on exactly the avatar that was tapped. `TransactionHistoryScreen` drops its own cancel dialog; cancel now lives in the details screen's toolbar, as it does on Android. Layout follows Figma node 9708:105260, and the kind headings node 9708:118186, including the four places Android's implementation is still waiting on design: amount type size, line-item value size, gutter inset, and the header avatar's ring.
…ching tabs "View in Chat" called `navigate(to:)`, which reaches the Chat tab's stack by bringing the tab forward. That swaps the tab out from under the transition: the tab bar and the conversation list are both visible for a beat before the chat pushes in over them. Push it onto the stack the details screen is already on. The chat slides in from the entry it belongs to, back returns to the entry, and nothing changes underneath. `ConversationScreen` names no stack of its own, and the pushes it makes — a profile, a currency — resolve against whichever stack is topmost, so it carries over unchanged.
bmc08gt
added a commit
to code-payments/code-android-app
that referenced
this pull request
Sep 8, 2026
* fix(activity): read the transaction's recorded token quantity #1417 shipped the details screen's Tokens row as an estimate, and said so: it priced the entry's value against the mint's current circulating supply, so a historical entry stated what that value buys today rather than what moved at the time. The feed has carried the real number all along. Every entry's amount comes from the proto's `CryptoPaymentAmount`, and `ActivityFeedMessageMapper` puts `quarks` straight into `LocalFiat.underlyingTokenAmount` — USD quarks for the reserve, that mint's own quarks for anything else. It survives persistence intact as `MessageEntity.amountUsdc`. `tokenAmountOf` now scales that by the mint's decimals and formats it, with no curve in the path. Dropping the round trip also drops two scale bugs it carried. It sold token quarks to a USD value at `token.decimals` and bought them back at a hardcoded six, which is wrong for every launchpad mint at ten; and it priced against `launchpadMetadata?.currentCirculatingSupplyQuarks ?: 0`, so a mint whose metadata had not resolved was valued against a supply of zero. Trailing zeros are now trimmed rather than padded to the mint's decimals, since `1,204.9050000000` states no more than `1,204.905`. A USDF entry that read `20.000000` reads `20`. iOS reads the same field for the same row in code-payments/code-ios-app#733, so the two screens state one number. The screenshot fixtures declared a six-decimal Jeffy, which no launchpad mint is; it is ten now, and the hand-written USDF quantities match what the mapper actually produces. * refactor(activity): move the token quantity formatting onto Token The details mapper was holding the BigDecimal shift and the DecimalFormat itself, which is scale arithmetic rather than mapping. `Token.formattedQuantity` takes both, next to the `decimals` the shift reads. FeedItemDetails needs the same reading, so having one place for it is the point.
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.
Tapping an activity row did nothing, anywhere it was drawn. An entry's exchange rate, token quantity, fee and id had no surface at all, and cancelling a pending cash link meant leaving the wallet and finding the entry again in the per-token history. Android closed this in code-payments/code-android-app#1417 and code-payments/code-android-app#1418; this is the same screen on iOS.
What's here
TransactionDetails(FlipcashCore) maps anActivityto what the screen shows: which of the 13TransactionKinds it was, the heading and subtitle, the sign, whether it can be cancelled and whether it has a conversation to open. Android's reasoning carries over unchanged because it comes fromMessageMetadata, which iOS has too: the three cash kinds stay distinct, and there is no received-cash-link case because a claimed link arrives as a plain receive.TransactionDetailsScreenis pushed, not presented, and reads: header avatar, who or what it was, the amount in the viewer's currency with what actually moved underneath, then a receipt card (currency, rate, date, tokens, fee and received for a conversion, status) and the id with a copy control. Cancel is the bar's trailing action rather than a control at the foot of a variable-length card. "View in Chat" hands off cross-stack to the existing conversation screen.ActivityRowpushes the destination itself rather than each call site wiring a tap, so the row behaves identically in the wallet's Recent section, the currency info screen, the cross-token history and a token's own history. The avatar moves out toActivityAvatarwith all its metrics derived fromsize; the details header draws the same view at 80pt from the sameActivityResolution, so the screen opens on exactly the avatar that was tapped. The "Rows are non-interactive" comment inActivityHistoryScreenis gone.TransactionDetailsTestsmirrorsTransactionDetailsMapperTestcase for case: 15 tests over kind, status, cancellability and the receipt fields.Where this deviates from Android
Four differences, each because the iOS feed or an iOS screen is shaped differently:
TransactionAccountrow. The iOS activity feed carries no destination address, so a withdrawal has nothing to draw under its heading.-on the source leg; the iOS row already shows it unsigned, and the row and the screen have to agree.canViewInChatkeys on the counterparty'sUserID, not on a resolved profile. The conversation screen derives its own header from the id, so a name that hasn't landed yet doesn't withhold the action.Tokensreads the feed's recordedonChainAmountrather than re-estimating it from the bonding curve.Scope note
The per-token
TransactionHistoryScreenhad its own cancel button and dialog on each row. That is now the details screen's, so the screen drops the dialog, theSessiondependency and the row wrapper. This matches Android, where cancel lives only in details.