Skip to content

feat(activity): open a tapped activity row's details - #733

Merged
bmc08gt merged 2 commits into
mainfrom
feat/transaction-details
Sep 8, 2026
Merged

feat(activity): open a tapped activity row's details#733
bmc08gt merged 2 commits into
mainfrom
feat/transaction-details

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

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 an Activity to what the screen shows: which of the 13 TransactionKinds 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 from MessageMetadata, 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.

TransactionDetailsScreen is 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.

ActivityRow pushes 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 to ActivityAvatar with all its metrics derived from size; the details header draws the same view at 80pt from the same ActivityResolution, so the screen opens on exactly the avatar that was tapped. The "Rows are non-interactive" comment in ActivityHistoryScreen is gone.

TransactionDetailsTests mirrors TransactionDetailsMapperTest case 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:

  • No TransactionAccount row. The iOS activity feed carries no destination address, so a withdrawal has nothing to draw under its heading.
  • A conversion renders unsigned. Android asserts - on the source leg; the iOS row already shows it unsigned, and the row and the screen have to agree.
  • canViewInChat keys on the counterparty's UserID, 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.
  • Tokens reads the feed's recorded onChainAmount rather than re-estimating it from the bonding curve.

Scope note

The per-token TransactionHistoryScreen had its own cancel button and dialog on each row. That is now the details screen's, so the screen drops the dialog, the Session dependency and the row wrapper. This matches Android, where cancel lives only in details.

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.
@bmc08gt bmc08gt self-assigned this Sep 8, 2026
…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
bmc08gt merged commit b0ef271 into main Sep 8, 2026
1 check passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant