Files
bsky-social-app/src/screens/Messages/components/splitView/MessagesSplitViewLayout.tsx
T
2026-05-29 22:46:15 +03:00

148 lines
4.4 KiB
TypeScript

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 {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
import {type NativeStackNavigationOptionsWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth'
import {LEFT_NAV_MINIMAL_WIDTH} from '#/view/shell/desktop/LeftNav'
import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {NewChat} from '#/components/dms/dialogs/NewChatDialog'
import {CENTER_COLUMN_WIDTH, SCROLLBAR_OFFSET} from '#/components/Layout'
import {LockScroll} from '#/components/LockScroll'
import {useAgeAssurance} from '#/ageAssurance'
import {IS_WEB} from '#/env'
import {ChatList, Header as ChatListHeader} from '../../ChatList'
import {SplitViewProvider} from './context'
import {splitViewLeftScroll} from './leftColumnScroll'
type MessageScreens =
| 'Messages'
| 'MessagesConversation'
| 'MessagesConversationSettings'
| 'MessagesInbox'
| 'MessagesSettings'
type LayoutProps = ScreenLayoutArgs<
FlatNavigatorParams,
MessageScreens,
NativeStackNavigationOptionsWithAuth,
NativeStackNavigationProp<
FlatNavigatorParams,
MessageScreens,
string | undefined
>
>
export function renderMessagesSplitViewLayout(props: LayoutProps) {
return <MessagesSplitViewLayout {...props} />
}
function MessagesSplitViewLayout({children, ...props}: LayoutProps) {
const {rightNavVisible} = useLayoutBreakpoints()
const aa = useAgeAssurance()
if (!IS_WEB || !rightNavVisible || aa.state.access !== aa.Access.Full) {
return children
}
return (
<MessagesSplitViewLayoutInner {...props}>
{children}
</MessagesSplitViewLayoutInner>
)
}
function MessagesSplitViewLayoutInner({
children,
navigation,
route,
}: LayoutProps) {
const {centerColumnOffset} = useLayoutBreakpoints()
const newChatControl = useDialogControl()
const t = useTheme()
const isFocused = useIsFocused()
const {data: chatStatus} = useChatActorStatusQuery()
const onLeftColumnScroll = useCallback((e: ReanimatedScrollEvent) => {
'worklet'
splitViewLeftScroll.current = e.contentOffset.y
}, [])
const onNewChat = (conversation: string) =>
navigation.navigate('MessagesConversation', {conversation})
const selectedChat =
(route.name === 'MessagesConversation' ||
route.name === 'MessagesConversationSettings') &&
route.params &&
'conversation' in route.params
? route.params.conversation
: undefined
const halfLeftNavWidth = LEFT_NAV_MINIMAL_WIDTH / 2
const leftColumnWidth = 360
const rightColumnWidth =
CENTER_COLUMN_WIDTH - (centerColumnOffset ? halfLeftNavWidth + 30 : 0)
const containerWidth = leftColumnWidth + rightColumnWidth
return (
<View
style={[
a.flex_1,
a.flex_row,
a.mx_auto,
{maxWidth: containerWidth},
{
transform: [
{
translateX: centerColumnOffset
? halfLeftNavWidth
: halfLeftNavWidth / 2,
},
{translateX: web(SCROLLBAR_OFFSET) ?? 0},
],
},
]}>
{isFocused && <LockScroll />}
<SplitViewProvider side="left">
<View
style={[
a.border_l,
t.atoms.border_contrast_low,
{width: leftColumnWidth},
]}>
<ChatListHeader
newChatControl={newChatControl}
chatStatus={chatStatus}
/>
<ScrollProvider onScroll={onLeftColumnScroll}>
<ChatList
newChatControl={newChatControl}
selectedChat={selectedChat}
chatStatus={chatStatus}
/>
</ScrollProvider>
<NewChat onNewChat={onNewChat} control={newChatControl} />
</View>
</SplitViewProvider>
<SplitViewProvider side="right">
<View
style={[
a.border_x,
t.atoms.border_contrast_low,
{width: rightColumnWidth},
]}>
{children}
</View>
</SplitViewProvider>
</View>
)
}