Factor out navigation stuff

This commit is contained in:
Dan Abramov
2023-12-23 00:59:32 +00:00
parent af254768a4
commit 85fcff8f7f
3 changed files with 58 additions and 29 deletions
+4 -29
View File
@@ -26,7 +26,7 @@ import {BottomBar} from './view/shell/bottom-bar/BottomBar'
import {buildStateObject} from 'lib/routes/helpers'
import {State, RouteParams} from 'lib/routes/types'
import {colors} from 'lib/styles'
import {isNative, isWeb} from 'platform/detection'
import {isNative} from 'platform/detection'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {router} from './routes'
import {usePalette} from 'lib/hooks/usePalette'
@@ -41,6 +41,7 @@ import {
setEmailConfirmationRequested,
} from './state/shell/reminders'
import {init as initAnalytics} from './lib/analytics/analytics'
import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration'
import {HomeScreen} from './view/screens/Home'
import {SearchScreen} from './view/screens/Search'
@@ -385,9 +386,6 @@ function MyProfileTabNavigator() {
)
}
const webScrollPositions = new Map()
let webFocusedScreen = null
/**
* The FlatNavigator is used by Web to represent the routes
* in a single ("flat") stack.
@@ -395,24 +393,11 @@ let webFocusedScreen = null
const FlatNavigator = () => {
const pal = usePalette('default')
const numUnread = useUnreadNotifications()
const title = (page: string) => bskyTitle(page, numUnread)
const screenListeners = useWebScrollRestoration()
return (
<Flat.Navigator
screenListeners={{
beforeRemove(e) {
if (isWeb) {
webScrollPositions.delete(e.target)
}
},
focus(e) {
if (isWeb) {
const scrollY = webScrollPositions.get(e.target) ?? 0
window.scrollTo(0, scrollY)
webFocusedScreen = e.target
}
},
}}
screenListeners={screenListeners}
screenOptions={{
gestureEnabled: true,
fullScreenGestureEnabled: true,
@@ -515,16 +500,6 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
onReady={() => {
logModuleInitTime()
onReady()
if (isWeb) {
history.scrollRestoration = 'manual'
// TODO: Clean up?
navigationRef.current?.addListener('__unsafe_action__', e => {
if (webFocusedScreen) {
webScrollPositions.set(webFocusedScreen, window.scrollY)
}
})
}
}}>
{children}
</NavigationContainer>
@@ -0,0 +1,3 @@
export function useWebScrollRestoration() {
return undefined
}
+51
View File
@@ -0,0 +1,51 @@
import {useMemo, useState, useEffect} from 'react'
import {EventArg, useNavigation} from '@react-navigation/core'
if ('scrollRestoration' in history) {
// Tell the brower not to mess with the scroll.
// We're doing that manuall below.
history.scrollRestoration = 'manual'
}
function createInitialScrollState() {
return {
scrollYs: new Map(),
focusedKey: null as string | null,
}
}
export function useWebScrollRestoration() {
const [state] = useState(createInitialScrollState)
const navigation = useNavigation()
useEffect(() => {
function onDispatch() {
if (state.focusedKey) {
// Remember where we were for later.
state.scrollYs.set(state.focusedKey, window.scrollY)
}
}
// We want to intercept any push/pop/replace *before* the re-render.
// There is no official way to do this yet, but this works okay for now.
// https://twitter.com/satya164/status/1737301243519725803
navigation.addListener('__unsafe_action__' as any, onDispatch)
return () => {
navigation.removeListener('__unsafe_action__' as any, onDispatch)
}
}, [state, navigation])
const screenListeners = useMemo(
() => ({
beforeRemove(e: EventArg<'beforeRemove', boolean | undefined, unknown>) {
state.scrollYs.delete(e.target)
},
focus(e: EventArg<'focus', boolean | undefined, unknown>) {
const scrollY = state.scrollYs.get(e.target) ?? 0
window.scrollTo(0, scrollY)
state.focusedKey = e.target ?? null
},
}),
[state],
)
return screenListeners
}