Fix deep link URL re-processing on account switch and back gesture reliability
Two fixes for initialUrl/deep link handling:
1. Prevent initial URL from being re-processed on account switch. The
key={currentAccount?.did} in App.native.tsx remounts NavigationContainer,
which re-calls getInitialURL() and navigates to the deep link again.
Fixed by tracking consumption in a module-level flag and providing a
custom getInitialURL that returns null after first call.
2. Fix back swipe gesture not working reliably from deep links. Two changes:
- Changed isStateAtTabRoot to return false when state is undefined
(during init), preventing the Drawer gesture from activating during
the async window before the initial URL resolves.
- Added custom subscribe handler that navigates (pushes) on native
instead of letting React Navigation reset the entire state, ensuring
proper native back gesture setup.
https://claude.ai/code/session_01NCogJsPSFXu8YJG8dWwEbC
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
} from '@react-navigation/native'
|
||||
|
||||
import {timeout} from '#/lib/async/timeout'
|
||||
import {parseLinkingUrl} from '#/lib/parseLinkingUrl'
|
||||
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
|
||||
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
@@ -833,11 +834,60 @@ const FlatNavigator = ({
|
||||
* to the navigation context.
|
||||
*/
|
||||
|
||||
// Prevents re-processing the initial deep link URL on remount
|
||||
// (e.g. when account switch changes the key in App.native.tsx)
|
||||
let initialUrlConsumed = false
|
||||
|
||||
const LINKING = {
|
||||
// TODO figure out what we are going to use
|
||||
// note: `bluesky://` is what is used in app.config.js
|
||||
prefixes: ['bsky://', 'bluesky://', 'https://bsky.app'],
|
||||
|
||||
async getInitialURL(): Promise<string | null> {
|
||||
if (initialUrlConsumed) {
|
||||
return null
|
||||
}
|
||||
initialUrlConsumed = true
|
||||
return Linking.getInitialURL()
|
||||
},
|
||||
|
||||
subscribe(listener: (url: string) => void) {
|
||||
const sub = Linking.addEventListener('url', ({url}: {url: string}) => {
|
||||
if (IS_NATIVE && navigationRef.isReady()) {
|
||||
const urlp = parseLinkingUrl(url)
|
||||
const path = urlp.pathname
|
||||
|
||||
// Intent URLs are handled by useIntentHandler
|
||||
if (path.includes('intent/')) {
|
||||
return
|
||||
}
|
||||
|
||||
const [name, params] = router.matchPath(path)
|
||||
|
||||
// For tab roots, switch to the tab
|
||||
if (name === 'Search') {
|
||||
resetToTab('SearchTab')
|
||||
} else if (name === 'Notifications') {
|
||||
resetToTab('NotificationsTab')
|
||||
} else if (name === 'Home') {
|
||||
resetToTab('HomeTab')
|
||||
} else if (name === 'Messages') {
|
||||
resetToTab('MessagesTab')
|
||||
} else {
|
||||
// Navigate (push) instead of resetting state, so that the
|
||||
// native back gesture is properly set up
|
||||
// @ts-ignore nested navigators aren't typed -sfn
|
||||
navigate('HomeTab', {screen: name, params})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Web or navigation not ready: use default React Navigation behavior
|
||||
listener(url)
|
||||
})
|
||||
return () => sub.remove()
|
||||
},
|
||||
|
||||
getPathFromState(state: State) {
|
||||
// find the current node in the navigation tree
|
||||
let node = state.routes[state.index || 0]
|
||||
|
||||
@@ -25,11 +25,11 @@ export function getCurrentRoute(state?: State) {
|
||||
|
||||
export function isStateAtTabRoot(state: State | undefined) {
|
||||
if (!state) {
|
||||
// NOTE
|
||||
// if state is not defined it's because init is occurring
|
||||
// and therefore we can safely assume we're at root
|
||||
// -prf
|
||||
return true
|
||||
// During initialization (before navigation state is set), default to
|
||||
// not-at-root. This prevents the drawer gesture from activating during
|
||||
// the async window before the initial URL is resolved, which would
|
||||
// compete with the native stack's back gesture on deep link opens.
|
||||
return false
|
||||
}
|
||||
const currentRoute = getCurrentRoute(state)
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user