feat(ui): v2 tab-bar nav shell + full-screen wallet (behind BetaFlags.newUI) - #559
Merged
Merged
Conversation
The topmost-stack mutators (push, pushAny, popTopmost, replaceTopmostAny, popToRoot) targeted presentedSheet?.stack and no-op'd when no sheet was up. In the v2 tab UI a tab is the active surface without being a sheet, so those pushes would silently drop. Add an activeTabStack fallback (set by HomeTabView per selected tab) via a topmostStack helper. No behaviour change in v1, where activeTabStack is nil and a sheet is always the target.
Extract the app-level router.rootSheet presentation (RoutedSheet swap + tip-resume + dismiss-on-bill hooks) into RootSheetHostModifier so exactly one live view can own it. Add ScanScreen(isEmbedded:) — default false keeps the v1 root owning the host unchanged; when embedded as a v2 tab, HomeTabView owns it and ScanScreen suppresses its own to avoid double-presentation.
Add BalanceScreen(isEmbedded:) which suppresses the sheet-only close button so the screen can serve as the full-screen v2 Wallet tab, keeping its own NavigationStack(path: $router[.balance]) and every existing push destination. Defaults to false — the v1 sheet presentation is unchanged.
Add the v2 tab-bar UI, gated by the new developer BetaFlags.newUI (default off; v1 stays the default). ContainerScreen branches the logged-in root between HomeTabView (v2) and ScanScreen (v1). HomeTabView hosts four tabs — Scan, Wallet, Chat, Tip Card — behind a floating pill HomeTabBar (sliding indicator, ported from Android's NavigationBarV2), launches on Wallet, and owns the app-level rootSheet host so present() works from any tab. Only the selected tab is mounted (a switch, not TabView — the deployment target predates the tab-bar-hiding APIs) so the Scan camera stops via onDisappear. Tab icons are SF Symbols for now — close stand-ins for the Figma nav glyphs, swappable for exported assets. First slice of the Android v2 UI port (PR #1209); token cards, onboarding funnel, and header polish follow.
Replace the SF Symbol placeholders with the Figma tab-bar glyphs (Nav* template imagesets — scan/wallet/chat/tipcard — converted from the same vectors as Android's ic_nav_*, tinted white at the call site), and swap the flat black capsule for the app's Liquid Glass surface (glassEffect on iOS 26, ultraThinMaterial below), matching the design.
Hide the scanner's v1 chrome when embedded in the tab-bar UI — the Flipcash brand + settings top bar and the bottom navigation are replaced by the app-level tab bar, so the Scan tab is just the camera. v1 (non-embedded) is unchanged.
When embedded in the tab-bar UI, the permission prompt reads 'Start your camera to scan a Tip Card' instead of the v1 cash-grab wording. v1 (non-embedded) copy is unchanged.
Extend the tip-card framing to the other two prompts when embedded in the v2 tab-bar UI: denied → 'Turn on Camera in Settings to scan a Tip Card'; granted but paused → 'Start your camera to scan a Tip Card'. v1 copy unchanged.
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.
What
First slice of porting the Android v2 tab-bar UI (code-android-app#1209) to iOS, per the Figma (nav / card stack). This PR is the navigation shell + full-screen wallet, gated behind a new dev flag so v1 is untouched by default.
Moves the logged-in root from the scanner-first, sheet-centric model to a tab-bar-centric one: a floating Liquid Glass pill tab bar (Scan · Wallet · Chat · Tip Card) with a sliding selection indicator, wallet-first launch, and the Wallet screen promoted from a sheet to a full-screen tab.
How it's wired
BetaFlags.newUI(default off).ContainerScreenbranches the logged-in root betweenHomeTabView(v2) andScanScreen(v1).HomeTabView— hosts the four tabs behindHomeTabBar, launches on Wallet, and owns the app-levelrouter.rootSheethost sorouter.present(_:)works from any tab. Only the selected tab is mounted (aswitch, notTabView— the iOS 15 target predates the tab-bar-hiding APIs), so the Scan camera stops via its ownonDisappear.push/pop/…) no-op'd with no sheet presented; a tab is the active surface without being a sheet, so anactiveTabStackfallback (set byHomeTabViewper tab) routes in-tab pushes. No v1 behaviour change (activeTabStackis nil there).ScanScreen(isEmbedded:)suppresses its own sheet host when embedded;BalanceScreen(isEmbedded:)drops its sheet close button to serve as the full-screen Wallet tab (same$router[.balance]stack, all push destinations intact). Chat embedsTipsScreen($router[.tips]); Tip Card showsTipcardScreen, prompting over to Chat before the profile is tippable.RootSheetHostModifier— the app-levelrootSheethost (RoutedSheet swap + tip-resume + dismiss-on-bill) extracted so exactly one live view owns it:ScanScreenin v1,HomeTabViewin v2.glassEffecton iOS 26,.ultraThinMaterialbelow), white-20% sliding indicator; icons are the Figma nav glyphs as tintable template imagesets (converted from the same vectors as Android'sic_nav_*).Not in this PR (follow-ups, per the phased plan)
TokenCardStack(populated wallet)Testing
xcodebuild build(device + simulator): SUCCEEDED.newUI(Settings ▸ 9-tap ▸ Beta, or launch with--beta-flags=newUI) on a logged-in build.