import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; import { ReactionSortBase } from 'stream-chat'; import { EmojiPickerList } from './EmojiPickerList'; import { useFetchReactions } from './hooks/useFetchReactions'; import { ReactionButton } from './ReactionButton'; import { useBottomSheetContext } from '../../contexts'; import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext'; import { MessageContextValue, useMessageContext, } from '../../contexts/messageContext/MessageContext'; import { MessagesContextValue, useMessagesContext, } from '../../contexts/messagesContext/MessagesContext'; import { useTheme } from '../../contexts/themeContext/ThemeContext'; import { useTranslationContext } from '../../contexts/translationContext/TranslationContext'; import { useStableCallback } from '../../hooks'; import { IconProps } from '../../icons'; import { MoreEmojis } from '../../icons/emoji-add-1'; import { primitives } from '../../theme'; import { Reaction } from '../../types/types'; import { ReactionData } from '../../utils/utils'; import { Button } from '../ui'; import { StreamBottomSheetModalFlatList } from '../UIComponents'; const ITEM_WIDTH = 60; // @ts-ignore const getItemLayout = (_, index: number) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index, }); export type MessageUserReactionsProps = Partial> & Partial> & { /** * An array of reactions */ reactions?: Reaction[]; /** * The selected reaction */ selectedReaction?: string; }; const sort: ReactionSortBase = { created_at: -1, }; export type ReactionSelectorItemType = ReactionData & { onSelectReaction: (type: string) => void; selectedReaction?: string; count: string; }; export const MessageUserReactionsSelectorItem = (props: ReactionSelectorItemType) => { const { Icon, type, onSelectReaction, selectedReaction, count } = props; const SelectorIcon = useCallback(({ ...props }) => , [Icon]); return ( ); }; const renderSelectorItem = ({ index, item }: { index: number; item: ReactionSelectorItemType }) => ( ); const reactionsKeyExtractor = (item: Reaction) => `${item.id}-${item.type}`; const reactionSelectorKeyExtractor = (item: ReactionSelectorItemType) => item.type; export const MessageUserReactions = (props: MessageUserReactionsProps) => { const styles = useStyles(); const [showMoreReactions, setShowMoreReactions] = useState(false); const { message, reactions: propReactions, supportedReactions: propSupportedReactions } = props; const selectorListRef = useRef(null); const { close } = useBottomSheetContext(); const reactionTypes = useMemo( () => Object.keys(message?.reaction_groups ?? {}), [message?.reaction_groups], ); const [selectedReaction, setSelectedReaction] = useState(undefined); const { supportedReactions: contextSupportedReactions } = useMessagesContext(); const { MessageUserReactionsItem } = useComponentsContext(); const { handleReaction } = useMessageContext(); const supportedReactions = propSupportedReactions ?? contextSupportedReactions; const onSelectReaction = useStableCallback((reactionType: string) => { setSelectedReaction((currentReaction) => currentReaction === reactionType ? undefined : reactionType, ); }); useEffect(() => { if (selectedReaction && reactionTypes.length > 0 && !reactionTypes.includes(selectedReaction)) { setSelectedReaction(undefined); } }, [reactionTypes, selectedReaction]); const selectorReactions: ReactionSelectorItemType[] = useMemo( () => reactionTypes.reduce((acc, reaction) => { const reactionData = supportedReactions?.find( (supportedReaction) => supportedReaction.type === reaction, ); if (reactionData) { acc.push({ ...reactionData, onSelectReaction, selectedReaction, count: (message?.reaction_counts?.[reactionData.type] ?? 0).toString(), }); } return acc; }, []), [ message?.reaction_counts, onSelectReaction, reactionTypes, selectedReaction, supportedReactions, ], ); const selectedIndex = useMemo(() => { if (!selectedReaction) { return -1; } return selectorReactions.findIndex((reaction) => reaction.type === selectedReaction); }, [selectedReaction, selectorReactions]); useEffect(() => { if (selectedIndex !== -1 && selectorListRef.current) { selectorListRef.current?.scrollToIndex({ index: selectedIndex, animated: true, }); } }, [selectedIndex]); const { loading, loadNextPage, reactions: fetchedReactions, } = useFetchReactions({ message, reactionType: selectedReaction, sort, }); const { theme: { messageMenu: { userReactions: { container, contentContainer, flatlistContainer, reactionSelectorContainer, reactionsText, }, }, }, } = useTheme(); const { t } = useTranslationContext(); const totalReactionCount = useMemo( () => Object.values(message?.reaction_counts ?? {}).reduce( (acc, reactionCount) => acc + reactionCount, 0, ), [message?.reaction_counts], ); const reactions = useMemo( () => propReactions || (fetchedReactions.map((reaction) => ({ id: reaction.user?.id, image: reaction.user?.image, name: reaction.user?.name, type: reaction.type, })) as Reaction[]), [propReactions, fetchedReactions], ); const renderItem = useCallback( ({ item }: { item: Reaction }) => ( ), [MessageUserReactionsItem, supportedReactions], ); const handleSelectReaction = useStableCallback((emoji: string) => { if (handleReaction) { handleReaction(emoji); } close(); }); const onShowMoreReactionsPress = useStableCallback(() => setShowMoreReactions(true)); const MoreEmojisIcon = useCallback( (props: IconProps) => ( ), [styles.showMoreReactionsButton], ); const ShowMoreReactionsButton = useCallback( () => (