import React, {useCallback, useEffect, useRef} from 'react' import { Dimensions, FlatList, NativeScrollEvent, NativeSyntheticEvent, Platform, View, } from 'react-native' import {KeyboardAvoidingView} from 'react-native-keyboard-controller' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import {useChat} from '#/state/messages' import {ConvoItem, ConvoStatus} from '#/state/messages/convo' import {useSetMinimalShellMode} from '#/state/shell' import {MessageInput} from '#/screens/Messages/Conversation/MessageInput' import {MessageItem} from '#/screens/Messages/Conversation/MessageItem' import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' function MaybeLoader({isLoading}: {isLoading: boolean}) { return ( {isLoading && } ) } function RetryButton({onPress}: {onPress: () => unknown}) { const {_} = useLingui() return ( ) } function renderItem({item}: {item: ConvoItem}) { if (item.type === 'message' || item.type === 'pending-message') { return } else if (item.type === 'deleted-message') { return Deleted message } else if (item.type === 'pending-retry') { return } return null } function keyExtractor(item: ConvoItem) { return item.key } function onScrollToEndFailed() { // Placeholder function. You have to give FlatList something or else it will error. } export function MessagesList() { const chat = useChat() const flatListRef = useRef(null) // We use this to know if we should scroll after a new clop is added to the list const isAtBottom = useRef(false) const currentOffset = React.useRef(0) const onContentSizeChange = useCallback(() => { if (currentOffset.current <= 100) { flatListRef.current?.scrollToOffset({offset: 0, animated: true}) } }, []) const onEndReached = useCallback(() => { chat.service.fetchMessageHistory() }, [chat]) const onInputFocus = useCallback(() => { if (!isAtBottom.current) { flatListRef.current?.scrollToOffset({offset: 0, animated: true}) } }, []) const onInputBlur = useCallback(() => {}, []) const onSendMessage = useCallback( (text: string) => { chat.service.sendMessage({ text, }) }, [chat.service], ) const onScroll = React.useCallback( (e: NativeSyntheticEvent) => { currentOffset.current = e.nativeEvent.contentOffset.y }, [], ) const setMinShellMode = useSetMinimalShellMode() useFocusEffect( useCallback(() => { setMinShellMode(true) return () => setMinShellMode(false) }, [setMinShellMode]), ) const {bottom: bottomInset} = useSafeAreaInsets() const keyboardVerticalOffset = useKeyboardVerticalOffset() return ( } removeClippedSubviews={true} keyboardDismissMode="on-drag" /> ) } function useKeyboardVerticalOffset() { const {top: topInset} = useSafeAreaInsets() const [screenWindowDifference, setScreenWindowDifference] = React.useState( () => Dimensions.get('screen').height - Dimensions.get('window').height, ) useEffect(() => { const subscription = Dimensions.addEventListener( 'change', ({screen, window}) => { setScreenWindowDifference(screen.height - window.height) }, ) return () => subscription.remove() }, []) return Platform.select({ ios: topInset, android: screenWindowDifference, default: 0, }) }