add mechanism for running navigation actions when it's ready
This commit is contained in:
+7
-2
@@ -123,6 +123,7 @@ import {QuoteNotificationSettingsScreen} from './screens/Settings/NotificationSe
|
||||
import {ReplyNotificationSettingsScreen} from './screens/Settings/NotificationSettings/ReplyNotificationSettings'
|
||||
import {RepostNotificationSettingsScreen} from './screens/Settings/NotificationSettings/RepostNotificationSettings'
|
||||
import {RepostsOnRepostsNotificationSettingsScreen} from './screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings'
|
||||
import {NavigationAvailable} from './state/shell/navigation-available'
|
||||
|
||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||
|
||||
@@ -825,6 +826,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
const {currentAccount} = useSession()
|
||||
const prevLoggedRouteName = React.useRef<string | undefined>(undefined)
|
||||
const emailDialogControl = useEmailDialogControl()
|
||||
const navigationAvailableRef = React.useRef<{onReady: () => void}>(null)
|
||||
|
||||
function onReady() {
|
||||
prevLoggedRouteName.current = getCurrentRouteName()
|
||||
@@ -837,7 +839,9 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavigationAvailable
|
||||
ref={navigationAvailableRef}
|
||||
navigationRef={navigationRef}>
|
||||
<NavigationContainer
|
||||
ref={navigationRef}
|
||||
linking={LINKING}
|
||||
@@ -856,6 +860,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
attachRouteToLogEvents(getCurrentRouteName)
|
||||
logModuleInitTime()
|
||||
onReady()
|
||||
navigationAvailableRef.current?.onReady()
|
||||
logger.metric('router:navigate', {}, {statsig: false})
|
||||
}}
|
||||
// WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x
|
||||
@@ -867,7 +872,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
navigationInChildEnabled>
|
||||
{children}
|
||||
</NavigationContainer>
|
||||
</>
|
||||
</NavigationAvailable>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {invalidateCachedUnreadPage} from '#/state/queries/notifications/unread'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import {useRunWhenNavigationAvailable} from '#/state/shell/navigation-available'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
import {resetToTab} from '#/Navigation'
|
||||
|
||||
@@ -74,6 +75,7 @@ export function useNotificationsHandler() {
|
||||
const {currentConvoId} = useCurrentConvoId()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const runWhenNavigationAvailable = useRunWhenNavigationAvailable()
|
||||
const {_} = useLingui()
|
||||
|
||||
// On Android, we cannot control which sound is used for a notification on Android
|
||||
@@ -228,10 +230,17 @@ export function useNotificationsHandler() {
|
||||
}
|
||||
} else {
|
||||
switch (payload.reason) {
|
||||
case 'like':
|
||||
case 'repost':
|
||||
case 'reply':
|
||||
case 'quote':
|
||||
case 'mention':
|
||||
case 'like-via-repost':
|
||||
case 'repost-via-repost':
|
||||
case 'subscribed-post':
|
||||
const urip = new AtUri(payload.uri)
|
||||
if (urip.collection === 'app.bsky.feed.post') {
|
||||
setTimeout(() => {
|
||||
runWhenNavigationAvailable(() => {
|
||||
const urip = new AtUri(payload.uri)
|
||||
if (urip.collection === 'app.bsky.feed.post') {
|
||||
// @ts-expect-error types are weird here
|
||||
navigation.navigate('HomeTab', {
|
||||
screen: 'PostThread',
|
||||
@@ -240,51 +249,29 @@ export function useNotificationsHandler() {
|
||||
rkey: urip.rkey,
|
||||
},
|
||||
})
|
||||
}, 500)
|
||||
} else {
|
||||
resetToTab('NotificationsTab')
|
||||
}
|
||||
} else {
|
||||
resetToTab('NotificationsTab')
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'like':
|
||||
case 'repost':
|
||||
case 'follow':
|
||||
case 'mention':
|
||||
case 'quote':
|
||||
case 'reply':
|
||||
case 'starterpack-joined':
|
||||
case 'like-via-repost':
|
||||
case 'repost-via-repost':
|
||||
runWhenNavigationAvailable(() => {
|
||||
const urip = new AtUri(payload.uri)
|
||||
// @ts-expect-error types are weird here
|
||||
navigation.navigate('HomeTab', {
|
||||
screen: 'Profile',
|
||||
params: {
|
||||
name: urip.host,
|
||||
},
|
||||
})
|
||||
})
|
||||
break
|
||||
case 'verified':
|
||||
case 'unverified':
|
||||
default:
|
||||
resetToTab('NotificationsTab')
|
||||
break
|
||||
// TODO implement these after we have an idea of how to handle each individual case
|
||||
// case 'follow':
|
||||
// const uri = new AtUri(payload.uri)
|
||||
// setTimeout(() => {
|
||||
// // @ts-expect-error types are weird here
|
||||
// navigation.navigate('HomeTab', {
|
||||
// screen: 'Profile',
|
||||
// params: {
|
||||
// name: uri.host,
|
||||
// },
|
||||
// })
|
||||
// }, 500)
|
||||
// break
|
||||
// case 'mention':
|
||||
// case 'reply':
|
||||
// const urip = new AtUri(payload.uri)
|
||||
// setTimeout(() => {
|
||||
// // @ts-expect-error types are weird here
|
||||
// navigation.navigate('HomeTab', {
|
||||
// screen: 'PostThread',
|
||||
// params: {
|
||||
// name: urip.host,
|
||||
// rkey: urip.rkey,
|
||||
// },
|
||||
// })
|
||||
// }, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,5 +403,6 @@ export function useNotificationsHandler() {
|
||||
navigation,
|
||||
onPressSwitchAccount,
|
||||
setShowLoggedOut,
|
||||
runWhenNavigationAvailable,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import {createContext, useContext, useImperativeHandle, useState} from 'react'
|
||||
import {type NavigationContainerRefWithCurrent} from '@react-navigation/native'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {type AllNavigatorParams} from '#/lib/routes/types'
|
||||
|
||||
type QueueAction = () => void
|
||||
const NavigationAvailableContext = createContext<
|
||||
((cb: QueueAction) => void) | null
|
||||
>(null)
|
||||
|
||||
export function NavigationAvailable({
|
||||
children,
|
||||
ref,
|
||||
navigationRef,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
ref: React.Ref<{onReady: () => void}>
|
||||
navigationRef: NavigationContainerRefWithCurrent<AllNavigatorParams>
|
||||
}) {
|
||||
const [queue, setQueue] = useState<QueueAction[]>([])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
onReady: () => {
|
||||
for (const item of queue) {
|
||||
item()
|
||||
}
|
||||
setQueue([])
|
||||
},
|
||||
}))
|
||||
|
||||
const runWhenNavigationAvailable = useNonReactiveCallback(
|
||||
(cb: QueueAction) => {
|
||||
if (navigationRef.isReady()) {
|
||||
cb()
|
||||
} else {
|
||||
setQueue(prev => [...prev, cb])
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return (
|
||||
<NavigationAvailableContext.Provider value={runWhenNavigationAvailable}>
|
||||
{children}
|
||||
</NavigationAvailableContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useRunWhenNavigationAvailable() {
|
||||
const context = useContext(NavigationAvailableContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useRunWhenNavigationAvailable must be used within a NavigationAvailable component',
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
Reference in New Issue
Block a user