Skip to content

fix(activity): show Convert transactions in the feed - #1281

Merged
bmc08gt merged 2 commits into
code/cashfrom
claude/swap-convert-display-issue-622b87
Aug 20, 2026
Merged

fix(activity): show Convert transactions in the feed#1281
bmc08gt merged 2 commits into
code/cashfrom
claude/swap-convert-display-issue-622b87

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Problem

A swap spans two mints, so the backend leaves payment_amount unset and carries both amounts in additional_metadata instead. ActivityFeedMessageMapper only ever read payment_amount, so every Convert was cached with a null amount, rate, and mint — which meant it matched no token's activity query and rendered blank in the global feed.

Changes

  • Map multi-mint amounts from additional_metadata when payment_amount is absent.
  • Attribute a Convert to both mints, so it shows on the source and destination token screens (new MintInvolvement, plus the metadata clause in observeRecentForMint).
  • Render it like iOS: overlapping dual-token avatar, from-amount on top, -<fee> Fee beneath.
  • Title the row with the two token names ("Moony → Jeffy") rather than the server's bare "Converted", falling back to the server text until both tokens resolve.

Cache repair (DB 28 → 29)

Rows cached before this can't be repaired in place — the amounts were never persisted — and they can't be re-fetched either, since fetchSinceLatest only ever walks forward from the newest cached id. The migration clears messages so the next sync falls into its descending re-seed and refetches from scratch; paging refills older history on demand. Same approach as the existing 1→2, 6→7, 7→8 and 9→10 migrations.

TransactionHistoryMigrationTest covers it, following the hand-rolled pattern in UserProfileMigrationTest: the feed is emptied, a neighbouring table survives, and it's a no-op on an already-empty feed.

The spec overrides onPostMigrate(SQLiteConnection) rather than the SupportSQLiteDatabase variant used by Migration22To23 — Room only calls the latter when the database is built without a driver, so a future .setDriver(...) would turn the delete into a silent no-op.

Silent data loss, found alongside

MessageDataSource.upsert no-oped when the per-user DB wasn't open yet, discarding a whole fetched page while ActivityFeedCoordinator still reported Synced. Because every sync path pages forward from the newest cached id, those notifications were never requested again.

It now fails loudly — matching observe(), which already errors on a missing DB. FeedRemoteMediator turns that into a retryable MediatorResult.Error, and the coordinator moved its write from onSuccess into mapCatching so Synced is only reported once the page has actually landed.

Note on missing history

Activity older than the backend's swap deploy is absent server-side — the widest query the client can issue (DESC, no paging token, pageSize=100) returns only post-deploy notifications, and all of them are persisted. That's expected data loss from the migration, not a client bug.

A swap spans two mints, so the backend leaves `payment_amount` unset and carries
both amounts in `additional_metadata` instead. `ActivityFeedMessageMapper` only
ever read `payment_amount`, so every Convert cached with a null amount, rate, and
mint — which meant it matched no token's activity query and rendered blank in the
global feed.

- Map multi-mint amounts from `additional_metadata` when `payment_amount` is absent.
- Attribute a Convert to *both* mints, so it appears on the source and destination
  token screens (`MintInvolvement` + the metadata clause in `observeRecentForMint`).
- Render it the way iOS does: overlapping dual-token avatar, from-amount on top,
  `-<fee> Fee` beneath.
- Title the row with the two token names ("Moony -> Jeffy") rather than the
  server's bare "Converted", falling back to the server text until both tokens
  resolve.

Rows cached before this can't be repaired in place (the amounts were never
persisted) and can't be re-fetched either, since sync only walks forward from the
newest cached id. DB 28 -> 29 clears `messages` so the next sync re-seeds from
scratch — the same approach as the existing 1->2, 6->7, 7->8 and 9->10 migrations.

Also fixes a silent data-loss path found alongside it: `MessageDataSource.upsert`
no-oped when the per-user DB wasn't open yet, discarding a whole fetched page
while the coordinator still reported `Synced`. Because every sync path pages
forward from the newest cached id, those notifications were never requested
again. It now fails loudly (matching `observe()`, which already errors on a
missing DB), the mediator turns that into a retryable error, and the coordinator
only reports `Synced` once the write has actually landed.
@github-actions github-actions Bot added type: fix Bug fix area: network gRPC, connectivity, API, exchange rates and removed type: fix Bug fix labels Aug 20, 2026
Folds in the standalone migration work from #1284, which duplicated the
28 -> 29 clear this branch already carries.

Adds TransactionHistoryMigrationTest, following the hand-rolled pattern
in UserProfileMigrationTest: asserts the feed is emptied, that a
neighbouring table survives, and that it's a no-op on an already-empty
feed.

Also switches Migration28To29 to the onPostMigrate(SQLiteConnection)
overload. Room's SupportSQLiteDatabase variant only fires when the
database is built without a driver, so a future setDriver would turn the
delete into a silent no-op.
@github-actions github-actions Bot added the type: fix Bug fix label Aug 20, 2026
@bmc08gt
bmc08gt merged commit fc73fb2 into code/cash Aug 20, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the claude/swap-convert-display-issue-622b87 branch August 20, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: network gRPC, connectivity, API, exchange rates type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant