Skip to content

fix(wallet): wait for the token set before drawing recent activity - #1285

Merged
bmc08gt merged 1 commit into
code/cashfrom
claude/recent-section-token-loading-bd8e92
Aug 20, 2026
Merged

fix(wallet): wait for the token set before drawing recent activity#1285
bmc08gt merged 1 commit into
code/cashfrom
claude/recent-section-token-loading-bd8e92

Conversation

@bmc08gt

Copy link
Copy Markdown
Collaborator

Post-login, the wallet's Recent Activity preview drew convert rows as "Converted" for a beat before re-titling them to "USDF → Dad Cash".

Cause

A race, not a mapping bug.

TokenCoordinator.hydrateFromPersistence() sets _hydrated = true immediately when Room is empty, so on a fresh login tokens goes from null to emptyList() before any network fetch. The wallet tab's single loading gate only checked tokens == null, so it released, and the feed and the token set then raced independently — the feed regularly won.

TransactionItemMapper.convertTitle needs both mints resolved out of the cache-only token map. With the map still empty it returns null, and the row falls back to the server's bare verb.

Fix

Mirror the pattern the activity feed already uses for exactly this ambiguity (FeedSyncState):

  • TokenCoordinator — new TokenSyncState (Unknown/Synced/Unavailable) exposed as syncState, maintained by updateTokens(): Synced on a successful fetchTokenAccounts, Unavailable on failure, never downgrading an already-successful sync. Reset to Unknown on login and on reset().
  • SelectTokenViewModel.State.isAwaitingTokens — true while tokens == null, or while the set is empty and nothing has fetched yet. A non-empty set short-circuits, so a returning user with cached tokens never waits.
  • WalletScreenContent — the gate becomes tokenState.isAwaitingTokens || balanceState.isAwaitingActivity, so the existing single spinner covers both sources.

Notes

The wait is bounded: a failed token fetch flips to Unavailable and releases the gate rather than spinning forever.

Gated on "a fetch completed" rather than "these specific mints resolved" on purpose — getTokenMetadata only caches metadata for mints the user holds an account for, so a per-row wait could hang indefinitely on a convert whose destination never lands.

The token-info screen's copy of recentActivitySection is untouched: you only reach it by tapping a card in the stack, so tokens are already resolved there.

On a fresh login `hydrateFromPersistence` flips `_hydrated` true immediately
when Room is empty, so the wallet's `tokens` went from null to an empty list
before any network fetch. The tab's loading gate only checked for null, so the
feed and the token set raced — and the feed regularly won. `convertTitle` needs
both mints resolved out of the cache-only token map, so convert rows fell back
to the server's bare "Converted" and re-titled themselves once tokens landed.
Mirror the pattern the feed already uses for this exact ambiguity
(`FeedSyncState`): add `TokenSyncState` to `TokenCoordinator`, set from
`updateTokens()`, and derive `State.isAwaitingTokens` from it — an empty token
set only counts as "settled" once a fetch has actually completed. A non-empty
set short-circuits the wait, so a returning user with cached tokens never
blocks, and a failed fetch flips to Unavailable rather than spinning forever.
Gated on "a fetch completed" rather than "these mints resolved" because
`getTokenMetadata` only caches metadata for mints the user holds an account
for, so a per-row wait could hang on a convert whose destination never lands.
@github-actionsgithub-actionsBot added type: fix Bug fix area: tokens Token accounts, balances, token info and removed type: fix Bug fix labels Aug 20, 2026
@bmc08gt
bmc08gt merged commit e28c557 into code/cashAug 20, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the claude/recent-section-token-loading-bd8e92 branch August 20, 2026 19:57
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: tokensToken accounts, balances, token info

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@bmc08gt