fix: nameless-account parity on the You and Chats tabs, and add-money from a held balance - #1315
Merged
Merged
Conversation
The "Add Money" onboarding milestone read only `MessageDao.hasEverReceivedMoney()` — an EXISTS over the *local* message cache. An account funded before this install, or on another device, has money but no local row to prove it, so the wallet tab told it to add money it already had and kept the action tiles gated behind that same flag. Feed the milestone `hasEverReceivedMoney || holdsBalance`, sourced from a new `TokenCoordinator.hasAnyBalance`. That flow uses `Fiat.isPositive` rather than `hasDisplayableValue` — the question is whether the account holds anything at all, so a dust balance that rounds away in the UI still counts. A held balance also short-circuits `isAwaitingActivity`: the balance is a live read of the account, not of the cache, so there is nothing left to mistake for a new account and no reason to keep spinning on the first sync.
The tipping flow doubles as the v2 "Chats" root tab, and it opened on TipStep.Intro whenever the account had no display name. As a sheet step that was fine; as a root tab it replaced the whole tab — the NoChatsYet empty state was unreachable for a nameless account, and TipInfoScreen's Close had no sheet to dismiss. Under NewUi the flow always opens on TipStep.Tips, whatever the profile or tip-card state looks like. The claim-your-tip-card prompt belongs on the You tab. The v1 sheet keeps Intro and the post-setup TipCard handoff unchanged. Extracts the step derivation to `stepsFor` so both chromes are covered by tests.
… tab The You tab modelled the tip card as a nullable, so `tipCard == null` meant both "still resolving" and "this account has no display name". YouHeader opened with `if (card == null) return`, which drew nothing for either — a nameless account got a page with no card, no prompt, and no way to claim one from the tab that owns it. Replaces the nullable with a Unknown / Unclaimed / Claimed tri-state. Unclaimed draws the card the account *would* have, blurred out behind the claim prompt and its CTA into display-name entry, mirroring iOS `YouScreen.setupPrompt`. The stand-in is the account's real scannable payload over a placeholder name, built locally, so an account whose profile the server has never returned still gets it — with the card's own fill turned off so the 8% ground is what's blurred. Below API 31 the ground and outline stand alone rather than falling back to a sharp code. The tip link and the Share / Download tiles are absent while unclaimed, expansion is disabled, and the Sharesheet preview is no longer warmed for a card that can't be shared. Resolution is gated on AuthState.Ready so a named account, which restores its cached profile before auth completes, never flashes the prompt.
Haze only blurs on API 31+, and minSdk is 29. Below that it falls through to a scrim delegate that draws nothing unless given a colour effect — so the stand-in's fallbackColorEffect is the only thing standing between an API 29/30 device and a fully legible tip code behind the claim prompt. Nothing exercised that branch. Surfaces haze's own blurEnabled gate as a parameter on UnclaimedTipCardPrompt, defaulted to HazeBlurDefaults.isBlurEnabledByDefault() so app code is unchanged, and adds a preview pair: the blurred API 31+ rendering, and the scrim fallback an older device draws. The fallback preview is the one that matters — if the tint ever comes off, the code shows through it.
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.
Three iOS→Android parity fixes for an account that hasn't set a display name, plus an
add-money milestone that never completed.
fix(balance): complete the add-money milestone from a held balanceThe "Add Money" milestone only cleared on a deposit event, so an account that arrived
with a balance already held — restored on another device, or funded before the milestone
existed — was stuck being told to add money it already had. It now completes off the held
balance itself, matching iOS.
fix(tipping): keep the Chats tab on the list for a nameless accountThe Chats tab was hidden until the account had a display name, which is backwards: a
nameless account can still receive DMs and had no way to read them. iOS shows the tab
unconditionally. The name gate stays on tipping itself, where it belongs.
fix(menu): prompt a nameless account to claim its tip card on the You tabThe You tab modelled the tip card as a nullable, so
tipCard == nullmeant both "stillresolving" and "this account has no display name" —
YouHeaderopened withif (card == null) returnand drew nothing for either. A nameless account got a page withno card, no prompt, and no way to claim one from the tab that owns it.
Replaced with an
Unknown/Unclaimed/Claimedtri-state.Unclaimeddraws the cardthe account would have, frosted out behind the claim prompt and its CTA into display-name
entry, mirroring iOS
YouScreen.setupPrompt:so the 8% ground is what gets blurred. A placeholder name stands in for the empty one so
the blurred name line has the width a real card's would (iOS
placeholderName).Modifier.blur. Haze draws the bluras a layer in front of its source, so the effect carries an opaque
backgroundColor—the flattened card ground — or the sharp code would read straight through its own
frosting. Below API 31 (minSdk is 29) haze can't blur, and its scrim fallback draws
nothing unless told to, so a
fallbackColorEffecttint covers the same ground. Filmgrain is off: over a scannable figure it reads as noise in the code, and iOS frosts
with a plain
blur(radius: 12).disabled, and the Sharesheet preview is no longer warmed for a card that can't be shared.
AuthState.Ready, so a named account — which restores its cachedprofile before auth completes — never flashes the prompt.
settings row versus the claimed card's 19dp
(
.padding(.top, displayName == nil ? 48 : 19)). The claimed card pays part of itsclearance in the Share / Download tiles the unclaimed state doesn't draw. Row insets
already match —
ListItempadsgrid.x5= 25dp, the same as iOS'srowInsets.test(menu): preview the unclaimed tip card's API 29/30 blur fallbackHaze only blurs on API 31+, and minSdk is 29 — below that it falls through to a scrim
delegate that draws nothing unless given a colour effect. That makes the stand-in's
fallbackColorEffectthe only thing between an API 29/30 device and a fully legible tipcode behind the claim prompt, and nothing exercised the branch.
Haze's own
blurEnabledgate is now a parameter onUnclaimedTipCardPrompt, defaulted toHazeBlurDefaults.isBlurEnabledByDefault()so app code is unchanged, with a preview pair:the blurred API 31+ rendering and the scrim fallback an older device draws. The fallback
preview is the one that matters — if the tint ever comes off, the code shows through it.