Compare commits

...

3 Commits

Author SHA1 Message Date
Samuel Newman f936749760 fix rebase error 2026-02-20 11:01:57 +02:00
Samuel Newman 501186cae6 clear when errorboundary catches 2026-02-20 10:41:07 +02:00
Samuel Newman f1a814c59d Persist navigation state 2026-02-20 10:41:07 +02:00
3 changed files with 61 additions and 7 deletions
+50 -7
View File
@@ -1,4 +1,4 @@
import {type JSX, useCallback, useRef} from 'react'
import {type JSX, useCallback, useRef, useState} from 'react'
import * as Linking from 'expo-linking'
import * as Notifications from 'expo-notifications'
import {i18n, type MessageDescriptor} from '@lingui/core'
@@ -14,6 +14,7 @@ import {
DefaultTheme,
type LinkingOptions,
NavigationContainer,
type NavigationState,
StackActions,
} from '@react-navigation/native'
@@ -140,6 +141,7 @@ import {useAnalytics} from '#/analytics'
import {setNavigationMetadata} from '#/analytics/metadata'
import {IS_NATIVE, IS_WEB} from '#/env'
import {router} from '#/routes'
import {device} from '#/storage'
import {Referrer} from '../modules/expo-bluesky-swiss-army'
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
@@ -885,6 +887,44 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
const emailDialogControl = useEmailDialogControl()
const closeAllActiveElements = useCloseAllActiveElements()
const linkingUrl = Linking.useLinkingURL()
const [initialNotificationResponse, setInitialNotificationResponse] =
useState(() => {
if (!IS_NATIVE) return null
return Notifications.getLastNotificationResponse()
})
const [initialState] = useState(() => {
if (!IS_NATIVE) return
const previousNavState = device.get(['navigationState'])
if (previousNavState) {
// we want to clear it asap - even if we don't use it
// if they're opening it via an intent, we obviously prioritize the intent
// but also the *subsequent* open after that, it would be weird if it restored to the
// nav state *before* that, hence the aggressive clearing -sfn
device.remove(['navigationState'])
// we only want to use it if the current state is more-or-less where they left off:
// - logged in to the same account
// - no intent
// - not handling a notification
if (linkingUrl) return
if (initialNotificationResponse) return
if (previousNavState.did !== currentAccount?.did) {
return
}
return previousNavState.state
}
})
const persistState = (state?: NavigationState) => {
if (!IS_NATIVE) return
if (!currentAccount) return
if (!state) return
device.set(['navigationState'], {did: currentAccount.did, state})
}
/**
* Handle navigation to a conversation, or prepares for account switch.
@@ -925,15 +965,14 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
// intent urls are handled by `useIntentHandler`
if (linkingUrl) return
const notificationResponse = Notifications.getLastNotificationResponse()
if (notificationResponse) {
if (initialNotificationResponse) {
notyLogger.debug(`handlePushNotificationEntry: response`, {
response: notificationResponse,
response: initialNotificationResponse,
})
// Clear the last notification response to ensure it's not used again
try {
setInitialNotificationResponse(null)
Notifications.clearLastNotificationResponse()
} catch (error) {
notyLogger.error(
@@ -942,7 +981,9 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
)
}
const payload = getNotificationPayload(notificationResponse.notification)
const payload = getNotificationPayload(
initialNotificationResponse.notification,
)
if (payload) {
ax.metric('notifications:openApp', {
@@ -1012,10 +1053,11 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
return (
<NavigationContainer
initialState={initialState}
ref={navigationRef}
linking={LINKING}
theme={theme}
onStateChange={() => {
onStateChange={state => {
const currentScreen = getCurrentRouteName()
// do this before metric
setNavigationMetadata({
@@ -1024,6 +1066,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
})
ax.metric('router:navigate', {from: previousScreen.current})
previousScreen.current = currentScreen
persistState(state)
}}
onReady={onNavigationReady}
// WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x
+7
View File
@@ -1,3 +1,5 @@
import {type InitialState} from '@react-navigation/native'
import {type ID as PolicyUpdate202508} from '#/components/PolicyUpdateOverlay/updates/202508/config'
import {type Geolocation} from '#/geolocation/types'
@@ -66,6 +68,11 @@ export type Device = {
*/
policyUpdateDebugOverride?: boolean
[PolicyUpdate202508]?: boolean
navigationState?: {
did: string
state: InitialState
}
}
export type Account = {
+4
View File
@@ -4,6 +4,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {device} from '#/storage'
import {ErrorScreen} from './error/ErrorScreen'
import {CenteredView} from './Views'
@@ -30,6 +31,9 @@ export class ErrorBoundary extends Component<Props, State> {
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
logger.error(error, {errorInfo})
// ensure the app doesn't re-open to the same screen, in case the screen is borked
device.remove(['navigationState'])
}
public render() {