diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx
index dbc669c3c8..69364f3fbc 100644
--- a/src/screens/Messages/ChatList.tsx
+++ b/src/screens/Messages/ChatList.tsx
@@ -1,4 +1,4 @@
-import {useCallback, useEffect, useMemo, useState} from 'react'
+import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {View} from 'react-native'
import {useAnimatedRef} from 'react-native-reanimated'
import {type ChatBskyConvoDefs} from '@atproto/api'
@@ -45,6 +45,7 @@ import {IS_NATIVE} from '#/env'
import {ChatListItem} from './components/ChatListItem'
import {InboxRequests} from './components/InboxRequests'
import {useIsWithinSplitView} from './components/splitView/context'
+import {splitViewLeftScroll} from './components/splitView/leftColumnScroll'
type ListItem = {
type: 'CONVERSATION'
@@ -255,12 +256,32 @@ export function ChatList({
animated: IS_NATIVE,
offset: 0,
})
+ if (isWithinSplitView) {
+ splitViewLeftScroll.current = 0
+ restoredRef.current = true
+ }
try {
await refetch()
} catch (err) {
logger.error('Failed to refresh conversations', {message: err})
}
- }, [scrollElRef, refetch])
+ }, [scrollElRef, refetch, isWithinSplitView])
+
+ // Restore the saved scroll offset once the list has rendered enough
+ // content to honor it. Module-level ref survives ChatList re-mounts that
+ // happen on in-splitview navigation (see leftColumnScroll.ts).
+ const restoredRef = useRef(false)
+ const onContentSizeChange = useCallback(
+ (_w: number, h: number) => {
+ if (!isWithinSplitView || restoredRef.current) return
+ const offset = splitViewLeftScroll.current
+ if (offset > 0 && h >= offset) {
+ scrollElRef.current?.scrollToOffset({offset, animated: false})
+ restoredRef.current = true
+ }
+ },
+ [isWithinSplitView, scrollElRef],
+ )
const isScreenFocused = useIsFocused()
useEffect(() => {
@@ -364,6 +385,7 @@ export function ChatList({
/>
}
onEndReachedThreshold={IS_NATIVE ? 1.5 : 0}
+ onContentSizeChange={onContentSizeChange}
initialNumToRender={initialNumToRender}
windowSize={11}
desktopFixedHeight
diff --git a/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx b/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx
index 00e57b4e6a..91428a08eb 100644
--- a/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx
+++ b/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx
@@ -1,8 +1,11 @@
+import {useCallback} from 'react'
import {View} from 'react-native'
+import {type ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes'
import {type ScreenLayoutArgs, useIsFocused} from '@react-navigation/native'
import {type NativeStackNavigationProp} from '@react-navigation/native-stack'
import {type FlatNavigatorParams} from '#/lib/routes/types'
+import {ScrollProvider} from '#/lib/ScrollContext'
import {type NativeStackNavigationOptionsWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth'
import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
@@ -13,6 +16,7 @@ import {useAgeAssurance} from '#/ageAssurance'
import {IS_WEB} from '#/env'
import {ChatList, Header as ChatListHeader} from '../../ChatList'
import {SplitViewProvider} from './context'
+import {splitViewLeftScroll} from './leftColumnScroll'
const CENTER_COLUMN_WIDTH = 600
const LEFT_NAV_FULL_WIDTH = 245
@@ -48,6 +52,11 @@ function MessagesSplitViewLayout({children, navigation, route}: LayoutProps) {
const aa = useAgeAssurance()
const isFocused = useIsFocused()
+ const onLeftColumnScroll = useCallback((e: ReanimatedScrollEvent) => {
+ 'worklet'
+ splitViewLeftScroll.current = e.contentOffset.y
+ }, [])
+
if (!IS_WEB || !rightNavVisible || aa.state.access !== aa.Access.Full) {
return children
}
@@ -106,10 +115,12 @@ function MessagesSplitViewLayout({children, navigation, route}: LayoutProps) {
{width: containerWidth - centerColumnWidth},
]}>
-
+
+
+
diff --git a/src/screens/Messages/components/splitView/leftColumnScroll.ts b/src/screens/Messages/components/splitView/leftColumnScroll.ts
new file mode 100644
index 0000000000..ca56c0e98b
--- /dev/null
+++ b/src/screens/Messages/components/splitView/leftColumnScroll.ts
@@ -0,0 +1,4 @@
+// Holds the splitview left column's scroll offset across re-mounts caused
+// by in-splitview navigation. Reset on full page reload, mirroring the
+// in-memory semantics of useWebScrollRestoration.
+export const splitViewLeftScroll = {current: 0}