feat(home): long-press the You tab to open the account switcher - #749
Open
bmc08gt wants to merge 4 commits into
Open
feat(home): long-press the You tab to open the account switcher#749bmc08gt wants to merge 4 commits into
bmc08gt wants to merge 4 commits into
Conversation
A root-only `.switchAccount` sheet and stack on AppRouter, wrapping AccountSelectionScreen in its own NavigationStack with a close button. The stack is not tab-hosted, so the cross-stack invariants hold. The dismiss-then-switch closure the Settings push already used moves to `AccountSelectionScreen.switchAccountAction(router:sessionAuthenticator:)` so the push and the sheet run the same code.
Behind `BetaFlags.canSwitchAccounts`, the same gate as the Switch Accounts row in Settings. On both bars the press still selects the You tab on release. Legacy pill (iOS 18 to 25): a simultaneous LongPressGesture on each tab button. A high-priority gesture held the button's tap until the press failed, which made quick taps flaky. iOS 26 TabView: the TabBarSelectedIcons probe already finds the UITabBar, so it adds one UILongPressGestureRecognizer per bar with cancelsTouchesInView off. TabBarItemLocator maps the press point back to a HomeTab from the bar's button frames. iOS 26 draws every item twice at the same frame (a plain copy and the one the glass lens reveals), so coinciding frames collapse to one before the count is compared against the tabs. The locator test drives a real windowed UITabBarController so that layout is what gets checked.
A `UILongPressGestureRecognizer` added to the native `UITabBar` never began: the bar's own `_UIContinuousSelectionGestureRecognizer` — the one tracking a finger sliding across the items — won the conflict. Give the recognizer a delegate that allows simultaneous recognition, so it runs alongside the bar's own recognizers and holding an item still selects it on release. The same press also exposed a latent locator bug. iOS 26 lays the items out wider than their pitch (114pt buttons every 85pt on a 402pt bar), so adjacent frames overlap and a press near a boundary falls inside two; taking the first containing frame could resolve to the wrong tab. The nearest centre now wins.
`Haptics.medium()` from the one place both bars converge, `handleLongPress`, placed after the `canSwitchAccounts` gate so a hold that opens nothing stays silent.
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.
Holding the You tab opens the account switcher as a sheet. It is behind the same gate as the Switch Accounts row in Settings,
BetaFlags.canSwitchAccounts, which isaccessGrantedtoday, so nobody outside that group sees a difference. The tap still lands: on both bars the press selects the You tab on release and the switcher slides up over it.Changes:
.switchAccountsheet and stack onAppRouter. The stack is root-only and not tab-hosted, so the existing cross-stack invariants hold.SwitchAccountSheetRootwrapsAccountSelectionScreenin its ownNavigationStackwith a close button.AccountSelectionScreen.switchAccountAction(router:sessionAuthenticator:)so the push and the sheet run the same code.HomeTabBar, iOS 18 to 25): asimultaneousGesturelong press on each tab button. A high-priority gesture held the button's tap until the press failed, which made quick taps flaky, so it is simultaneous.TabView: the existingTabBarSelectedIcons.Probealready finds theUITabBar; it now adds oneUILongPressGestureRecognizerper bar withcancelsTouchesInView = false. The recognizer also needs a delegate allowing simultaneous recognition — without one the bar's_UIContinuousSelectionGestureRecognizer, which tracks a finger sliding across the items, wins the conflict and the long press never begins.TabBarItemLocatormaps the press point back to aHomeTabfrom the bar's button frames. Two things about the iOS 26 bar shape it has to absorb: every item is drawn twice at the same frame (a plain copy and the one the glass lens reveals), so coincident frames collapse to one before the count is compared against the tabs; and the buttons are laid out wider than their pitch — 114pt buttons every 85pt on a 402pt bar — so neighbours overlap and a press near a boundary falls inside two frames. The nearer centre wins, which is the item the press looks like it is on.Haptics.medium()fires fromhandleLongPress, the one place both bars converge, and after thecanSwitchAccountsgate — a hold that opens nothing stays silent.SwitchAccountSheetTestspins the sheet and stack mapping, root-sheet presentation, and that dismissing restores the You stack's path.TabBarItemLocatorTestsbuilds a real windowedUITabBarControllerand checks one frame per tab in display order, that the last frame resolves to You, that a point in an overlap resolves to the nearer item, and that an off-bar point resolves to nil.Android has no long press on its bottom nav, so this is iOS-only for now.