feat: add Settings as a fourth bottom-navigation destination - #807
feat: add Settings as a fourth bottom-navigation destination#807BrianSeong99 wants to merge 2 commits into
Conversation
|
This is clean and well-thought-out. A few things I checked:
One tiny thing, not blocking: the |
|
Thanks — and you're right that it's worth acting on, though the branch is slightly more alive than it looks, so let me be precise about what changed.
So rather than keep a condition whose extra reach is unreachable, I made it exact ( // Exact, unlike the segment matches above: `/history/:programId` renders
// inside this shell, so Activity has sub-paths to stay lit for, whereas the
// only Settings route that mounts TabLayout is the bare root — every
// `/settings/<slug>` sub-page renders in FullScreenPage (see PageRouter).
if (pathname === '/settings') return 'settings';The inconsistency with the Two tests went with it, because both asserted a state the app cannot produce:
150 tests pass across |
Research participants rarely found Settings from the Home balance-card gear, and several went hunting for it under Activity.
/settingssat outside the persistent tab layout —PageRouterwrapped it inFullScreenPage, which callsuseHideNavbarWhileOpen()unconditionally and therefore hid the bottom nav on the very screen you had navigated to.Settings is now the fourth bottom-nav destination, beside Home, Explore and Activity.
Two findings that changed the shape of this
The native mobile navbar is dead code. The JS↔native bridge (
showNativeNavbar,setNavbarSecondaryRow,nativeNavbarTap, …) was deleted by5fd95811"v0 UI Revamp (#248)". There are zero call sites insrc/on bothmainandnext, andsrc/main.css:335says so outright: "Wallet navigation is React-only: BottomNav is mounted inside TabLayout." The Swift/Java overlay is intact but unreachable.CLAUDE.md:172,AGENTS.mdand the issue all still describe it as live — they are stale.So the React
BottomNavis the mobile navbar, and changing it aligns extension, mobile and desktop by construction rather than by keeping three lists in sync. The issue's "the native mobile navbar contains the same four primary destinations as the React footer" is satisfied because there is only one navbar.The "mobile secondary quick-action row" no longer exists. It was the native Send/Receive/Settings row, deleted with the bridge. The live duplicate was
TabHeader's gear, which rendered on Activity and Explore — exactly the two screens that now carry a Settings tab in the same bar. That gear is gone.What
Routing — one new exact-matched route puts the Settings root in the tab shell, above the existing
/settings/:tabSlug?. Sub-pages keep theirFullScreenPagedrill-in, so opening a section and backing out is byte-for-byte the behaviour it was, and so is hardware back from either place (MobileBackBridgealready listed/settingsinTAB_PAGESand is untouched).The tab —
TAB_ROUTESgainssettings,activeTabFromPathmatches on the path segment so/settings/<slug>keeps the tab lit, and the item reuses the already-wiredIconName.Settings. Tab labels are now localized:settings,home,exploreandactivityall already exist inen.json, so no new keys and nothing for the DeepL job to translate.Header — the Settings root wears the same
TabHeaderas Activity and Explore: a plain title, no back chevron, because a tab destination is not something you back out of. Its sub-pages keepNavigationHeader, whose chevron is their only way out. This also retires dead space —Settings.tsxalready carriedpb-22"so the last menu item can scroll above the React BottomNav", for a nav that did not render there.Balance-card icon — the account-options button on the Home balance card was a gear. It opens account options, not Settings, and a gear sitting a thumb away from a Settings tab reads as the tab. It is now a pencil (new
IconName.Edit).Fit, not restyle — the pill's
px-13.5 gap-8was tuned for three items;TabLayout.tsx:241-244already noted its min-content was ~367px against a 375px viewport. A fourth item pushed that to ~470px and overflowed every phone. Reduced topx-4 gap-2. Colours, radius, shadow, typography and structure are untouched — #803 still owns the visual treatment.One latent native bug fixed —
NavbarView.java:345hardcodesnavStack.setWeightSum(3)whilerebuildMainRow()adds each button at weight1fand never recomputes it, so a fourth button would claim 1/3 of the width and clip off the right edge. NowsetWeightSum(items.size()), mirroring what the secondary row already does correctly at:929. Dormant code today; correct whenever #803 revives it.Testing
yarn test, full suite).yarn ts,yarn lint:i18n,yarn lint:e2eclean; ESLint clean on every changed file./settingsand/settings/<slug>→ settings tab active; tapping the tab navigates and fires exactly onehapticSelection; re-tapping on-route is a silent no-op; tapping from a sub-page returns to the root; tab order and the localization of all four labels; four-destination rendering and single-active-tab inBottomNav;/settingsinsideTabLayoutand sub-pages outside it inPageRouter;TabHeaderrenders no settings-labelled control and no empty actions row; the balance card renders the edit glyph.TabLayout.test.tsx:185asserted/settings→ home tab. That test encoded the old behaviour and is inverted, not deleted. Same for the threeSettings.test.tsxcases that asserted the root'sNavigationHeaderand its back-to-home chevron — the back-button test is replaced by a positive assertion that the root has no back affordance and never callsnavigate./settings, list scrolls above the pill, Home ↔ Settings switching correct, four tabs fit without overflow.Reviewers
main, deliberately. Most work here lands onnextfirst, butnextcurrently pins@miden-sdk/miden-sdk0.16.0-rc.5 (miden-client 0.16.0-rc.3) and testnet rejects that client at the handshake — "accept header validation failed … (client version: 0.16.0-rc.3)" — so nothing onnextcan create a wallet locally to verify against. It will need porting tonextalongside the rest.TabHeaderis imported from its module, not thecomponents/uibarrel. The barrel pulls in siblings that calllib/platformpredicates at module scope, andSettings.test.tsxmocks that module with onlyisMobile; going through the barrel took the whole suite down on a TDZ error. Fixing the mock instead meant editing an unrelated suite's mocking strategy, so I took the smaller route. Happy to flip it if you'd rather fix the mock.weightSumfix in code nothing currently calls, and neither platform has navbar tests.Welcome.tsx'sconfirmationhandler swallows registration failures for the Create + passcode path — thecatchonly surfaces an error for Import+Guardian and hardware-only-biometric, so any other failure leaves a dead "Open wallet" button with no message. Worth its own issue.closes #802