import React, { ComponentType, useCallback, useEffect, useMemo, useRef, } from 'react'; import { StyleSheet, FlatList, View, ViewStyle, ActivityIndicator, } from 'react-native'; import { useStyles, useAutoScroll, useTheme, useChatRoomState, useChatMessagesEffect, useLoadReactionPacksEffect, useUserReactionsEffect, useReactionSpace, useLoadUserReactions, useChatRoomActions, } from '../../hooks'; import { LLChatMessageItem, LLChatMessageItemProps, } from '../LLChatMessageItem'; import { LLMessageListEmptyComponent, LLMessageListEmptyComponentProps, } from './LLMessageListEmptyComponent'; import { IChatMessage, ComponentStyleProp, LLComponentStyleFn, } from '../../types'; import { LLLoadActionButtonComponent } from '../LLLoadActionButton'; export type LLChatMessageListStyles = { rootContainer: ViewStyle; listLoadingIndicator: ViewStyle; }; export type LLChatMessageListProps = ComponentStyleProp & { roomId: string; MessageItemComponent?: typeof LLChatMessageItem; MessageItemComponentStyles?: LLChatMessageItemProps['styles']; MessageListComponent?: typeof FlatList; MessageListLoadingComponent?: ComponentType; MessageListEmptyComponent?: typeof LLMessageListEmptyComponent; LoadPrevButtonComponent?: typeof LLLoadActionButtonComponent; MessageListEmptyComponentStyles?: LLMessageListEmptyComponentProps['styles']; }; const listItemKeyExtractor = (message: IChatMessage) => message.id; export function LLChatMessageList({ roomId, styles: stylesProp, MessageListComponent = FlatList, MessageItemComponent = LLChatMessageItem, MessageListEmptyComponent = LLMessageListEmptyComponent, LoadPrevButtonComponent = LLLoadActionButtonComponent, MessageItemComponentStyles, MessageListLoadingComponent, MessageListEmptyComponentStyles, }: LLChatMessageListProps) { const listRef = useRef(null); const userReactionsLoadedRef = useRef(false); const { chatMessages, chatMessagesLoaded, messageListIterator } = useChatRoomState({ roomId, }); const { onContentSizeChangeHandler, onScrollEndDrag } = useAutoScroll({ ref: listRef, }); const messageListStyles = useStyles({ componentStylesFn: getMessageListStyles, stylesProp, }); const { theme } = useTheme(); const { reactionSpace } = useReactionSpace({ targetGroupId: roomId }); useChatMessagesEffect({ roomId }); useLoadReactionPacksEffect({ reactionSpaceId: reactionSpace?.id }); useUserReactionsEffect({ reactionSpaceId: reactionSpace?.id }); const { loadPrevMessages } = useChatRoomActions({ roomId }); const targetIds = useMemo( () => chatMessages?.map(({ id }) => id), [chatMessages] ); const { loadUserReactions } = useLoadUserReactions({ reactionSpaceId: reactionSpace?.id, }); useEffect(() => { if (!targetIds?.length || userReactionsLoadedRef.current) { return; } loadUserReactions({ targetIds }); userReactionsLoadedRef.current = true; }, [targetIds]); const renderLoadPrevMessagesButton = useCallback(() => { if (!messageListIterator) { return null; } return ( ); }, [messageListIterator, loadPrevMessages]); const renderListItem = useCallback( ({ item }) => ( ), [MessageItemComponentStyles] ); const renderListLoadingComponent = MessageListLoadingComponent ? ( ) : ( ); return ( {!chatMessagesLoaded ? ( renderListLoadingComponent ) : ( } data={chatMessages} renderItem={renderListItem} keyExtractor={listItemKeyExtractor} onContentSizeChange={onContentSizeChangeHandler} onScrollEndDrag={onScrollEndDrag} ListHeaderComponent={renderLoadPrevMessagesButton} maintainVisibleContentPosition={{ minIndexForVisible: 0, }} /> )} ); } const getMessageListStyles: LLComponentStyleFn = ({ theme, }) => StyleSheet.create({ rootContainer: { display: 'flex', flex: 1, justifyContent: 'center', marginBottom: 10, }, listLoadingIndicator: {}, });