import React, {useCallback, useRef} from 'react' import { 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 {isIOS} from '#/platform/detection' import {useChat} from '#/state/messages' import {ConvoItem, ConvoStatus} from '#/state/messages/convo' import {MessageInput} from '#/screens/Messages/Conversation/MessageInput' import {MessageListError} from '#/screens/Messages/Conversation/MessageListError' import {atoms as a, useBreakpoints} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {MessageItem} from '#/components/dms/MessageItem' 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 } else if (item.type === 'error-recoverable') { 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(() => { if (chat.status === ConvoStatus.Ready) { chat.fetchMessageHistory() } }, [chat]) const onInputFocus = useCallback(() => { if (!isAtBottom.current) { flatListRef.current?.scrollToOffset({offset: 0, animated: true}) } }, []) const onInputBlur = useCallback(() => {}, []) const onSendMessage = useCallback( (text: string) => { if (chat.status === ConvoStatus.Ready) { chat.sendMessage({ text, }) } }, [chat], ) const onScroll = React.useCallback( (e: NativeSyntheticEvent) => { currentOffset.current = e.nativeEvent.contentOffset.y }, [], ) const {bottom: bottomInset} = useSafeAreaInsets() const {gtMobile} = useBreakpoints() const bottomBarHeight = gtMobile ? 0 : isIOS ? 40 : 60 const keyboardVerticalOffset = useKeyboardVerticalOffset() return ( } removeClippedSubviews={true} keyboardDismissMode="on-drag" /> ) } function useKeyboardVerticalOffset() { const {top: topInset} = useSafeAreaInsets() return Platform.select({ ios: topInset, // I thought this might be the navigation bar height, but not sure // 25 is just trial and error android: 25, default: 0, }) }