Skip to content

feat(activity): lead activity rows with the viewer's own currency - #709

Merged
bmc08gt merged 1 commit into
mainfrom
feat/activity-row-viewer-currency
Sep 1, 2026
Merged

feat(activity): lead activity rows with the viewer's own currency#709
bmc08gt merged 1 commit into
mainfrom
feat/activity-row-viewer-currency

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

An activity row showed nativeAmount alone — the currency the payment was denominated in — so a 7,500 peso tip read "-$7,500.00" to someone who reads money in dollars, with nothing saying it was $5.

The row now leads with the amount in the viewer's currency and, only when the transferred currency differs, shows what actually moved beneath it with that currency's flag in the secondary style. Both lines carry the same sign; a payment already in the viewer's currency renders one line as before.

The conversion

ExchangedFiat.forViewer(preferredRate:rates:) in FlipcashCore, with unit tests.

A USDF payment converts from the USD it settled at (onChainAmount), so a $5 tip keeps reading $5 however far the peso moves afterwards. Any other mint has no such anchor — onChainAmount holds that mint's own quarks — so it crosses through today's rates instead, and falls back to a single line when the source currency has no rate to cross with.

Deliberately not usdfValue: an activity's rate is synthesized as nativeAmount / onChainAmount for a bonded mint, so dividing back through it returns the token count rather than dollars. This is the same trap as Android's underlyingTokenAmount.

Where it runs

At render time, not in the feed mapping. Reading RatesController in the row's body is what subscribes the rows to it, so a currency changed on a screen stacked over the list reaches them. Android needed the same placement for a different reason — its feed only re-maps when a profile or token cache lands.

Signs

Feed amounts are magnitudes with the direction carried alongside, which is why the row supplies the sign at all. FiatAmount.formatted(signPrefix:) leaves a value that already carries its own "-" alone rather than printing "--$5.00".

Swap rows keep the second line for the swap fee, unchanged.

Difference from Android

Ports code-payments/code-android-app#1388. Android updated two row implementations and flagged both lines in the per-token history, which had always flagged the one amount it showed. iOS has a single ActivityRow across the Recent previews, the cross-token history and the per-token history, and none of them flag today, so only the transferred line gains a flag.

One guard has no Android counterpart: a viewer rate of zero falls back to the transferred amount rather than rendering the row as zero. iOS rehydrates its rate table from SQLite at launch, so a bad row is reachable.

An activity row showed `nativeAmount` alone — the currency the payment was
denominated in — so a 7,500 peso tip read "-$7,500.00" to someone who reads
money in dollars, with nothing saying it was $5.

The row now leads with the amount in the viewer's currency and, only when the
transferred currency differs, shows what actually moved beneath it with that
currency's flag in the secondary style. Both lines carry the same sign; a
payment already in the viewer's currency renders one line as before.

`ExchangedFiat.forViewer(preferredRate:rates:)` is the conversion. A USDF
payment converts from the USD it settled at (`onChainAmount`), so a $5 tip keeps
reading $5 however far the peso moves afterwards. Any other mint has no such
anchor — `onChainAmount` holds that mint's own quarks — so it crosses through
today's rates instead and falls back to a single line when the source currency
has no rate to cross with. Deliberately not `usdfValue`: an activity's rate is
synthesized as `nativeAmount / onChainAmount` for a bonded mint, so dividing
back through it returns the token count rather than dollars.

The conversion runs at render time rather than where the feed is mapped.
Reading `RatesController` in the row's body is what subscribes the rows to it,
so a currency changed on a screen stacked over the list reaches them.

Feed amounts are magnitudes with the direction carried alongside, which is why
the row supplies the sign at all. `FiatAmount.formatted(signPrefix:)` leaves a
value that already carries its own "-" alone rather than printing "--$5.00".

Swap rows keep the second line for the swap fee, unchanged.

Ports code-payments/code-android-app#1388. Android updated two row
implementations and flagged both lines in the per-token history, which had
always flagged the one amount it showed. iOS has a single `ActivityRow` across
the Recent previews, the cross-token history and the per-token history, and
none of them flag today, so only the transferred line gains a flag.
@bmc08gt bmc08gt self-assigned this Sep 1, 2026
@bmc08gt
bmc08gt merged commit 9f9c0d0 into main Sep 1, 2026
@bmc08gt
bmc08gt deleted the feat/activity-row-viewer-currency branch September 1, 2026 16:12
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