UNPKG

@cometchat/chat-uikit-react-native

Version:

Ready-to-use Chat UI Components for React Native

247 lines 11.5 kB
import { CometChat } from "@cometchat/chat-sdk-react-native"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { FlatList, ScrollView, Text, View } from "react-native"; import { ChatConfigurator, CometChatListItem, CometChatUIEventHandler, CometChatUiKitConstants, localize, } from "../shared"; import { MessageUtils } from "../shared/utils/MessageUtils"; import { useTheme } from "../theme"; import { CometChatDate } from "../shared/views/CometChatDate"; import { CometChatReceipt } from "../shared/views"; import { Skeleton } from "./Skeleton"; import { ErrorEmptyView } from "../shared/views/ErrorEmptyView/ErrorEmptyView"; import { MessageReceipt } from "../shared/constants/UIKitConstants"; import { deepMerge } from "../shared/helper/helperFunctions"; const listenerId = "uiEvents_" + new Date().getTime(); /** * CometChatMessageInformation component renders the message information view along with message receipts. * * @param props - Properties for configuring the component. * @returns A JSX.Element containing the rendered message information. */ export const CometChatMessageInformation = (props) => { const { title = localize("MESSAGE_INFORMATION"), message, template, BubbleView, ListItemView, receiptDatePattern, onBack, onError, EmptyStateView, emptyStateText, ErrorStateView, errorStateText, LoadingStateView, style, } = props; const defaultReceiptTimestampForUser = "----"; const theme = useTheme(); const mergedStyle = useMemo(() => { return deepMerge(theme.messageInformationStyles, style ?? {}); }, [theme, style]); const [receipts, setReceipts] = useState([]); const [listState, setListState] = useState("loading"); /** * Renders a receipt view for a given status and time. * Captures variables from the component scope (e.g. message, mergedStyle, theme). * * @param receipt - The receipt data or status identifier. * @param status - The receipt status (e.g. MessageReceipt.READ). * @param time - The timestamp for the receipt. * @returns A JSX.Element representing the receipt view. */ const receipt = (receipt, status, time) => { // If a custom ListItemView is provided in props, use it. if (ListItemView) { return ListItemView(message, receipt); } const receiverTypeIsUser = message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.user; const statusTextStyle = receiverTypeIsUser ? mergedStyle.receiptItemStyle.titleStyle : mergedStyle.receiptItemStyle.subtitleStyle; return (<View style={{ flexDirection: receiverTypeIsUser ? "column" : "row" }}> <View style={{ flexDirection: "row", flex: 1, gap: message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.user ? theme.spacing.padding.p1 : 0, }}> {receiverTypeIsUser && (<CometChatReceipt receipt={status} style={mergedStyle.receiptItemStyle.receiptStyles}/>)} <Text style={statusTextStyle}>{localize(status)}</Text> </View> {time && time !== defaultReceiptTimestampForUser ? (receiptDatePattern ? (receiptDatePattern(time)) : (<CometChatDate style={{ textStyle: mergedStyle.receiptItemStyle.subtitleStyle }} customDateString={new Date(time * 1000).toLocaleString("en-GB", { year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", hour12: true, })} timeStamp={time}/>)) : (<Text style={mergedStyle.receiptItemStyle.subtitleStyle}> {defaultReceiptTimestampForUser} </Text>)} </View>); }; /** * Renders a list of receipts for a given recipient item. * * @param item - The receipt item data. * @returns A JSX.Element containing the rendered receipts. */ const receiptsList = (item) => { if (message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.user) { item.readAt = item.readAt || defaultReceiptTimestampForUser; item.deliveredAt = item.deliveredAt || defaultReceiptTimestampForUser; } return (<View style={{ gap: message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.user ? theme.spacing.padding.p3 : 0, }}> {item.readAt && receipt("READ", MessageReceipt.READ, item.readAt)} {item.deliveredAt && receipt("DELIVERED", MessageReceipt.DELIVERED, item.deliveredAt)} </View>); }; /** * Renders each recipient as a list item. * * @param param0 - Object containing the current item and its index. * @returns A JSX.Element representing a single recipient list item. */ const renderReceipients = useCallback(({ item, index }) => { const { sender } = item; return (<CometChatListItem id={sender["uid"]} {...(message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.group ? { avatarName: sender["name"] } : {})} {...(message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.group ? { avatarURL: sender["avatar"] } : {})} SubtitleView={receiptsList(item)} {...(message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.group ? { title: sender["name"] } : {})} containerStyle={mergedStyle.receiptItemStyle.containerStyle} titleStyle={mergedStyle.receiptItemStyle.titleStyle} avatarStyle={mergedStyle.receiptItemStyle.avatarStyle}/>); }, [receipts, mergedStyle]); /** * Checks if the current message is for a group. * * @returns True if the receiver type is "group"; otherwise, false. */ const isGroup = () => { return message?.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.group; }; /** * Populates receipts for the current message. * For group messages, it fetches receipts from the server. * For user messages, it creates a single receipt object. */ const populateReceipts = () => { if (isGroup()) { setListState("loading"); CometChat.getMessageReceipts(message.getId()) .then((receiptsList) => { setReceipts(receiptsList); setListState("done"); }) .catch((er) => { onError && onError(er); setListState("error"); }); } else { const userReceipt = { sender: isGroup() ? message.getSender() : message.getReceiver(), sentAt: message.getSentAt(), readAt: message.getReadAt(), deliveredAt: message.getDeliveredAt(), }; setReceipts([userReceipt]); } }; /** * Updates the message receipt when a new receipt is received. * * @param newReceipt - The new message receipt. */ const updateMessageReceipt = useCallback((newReceipt) => { if (message.getId() === Number(newReceipt.getMessageId())) { if (message.getReceiverType() === CometChatUiKitConstants.ReceiverTypeConstants.user) { const udpatedReceipt = { sender: message.getReceiver(), sentAt: message.getSentAt(), readAt: message.getReadAt() || newReceipt.getReadAt(), deliveredAt: message.getDeliveredAt() || newReceipt.getDeliveredAt(), }; setReceipts([udpatedReceipt]); return; } const receiptIndex = receipts.findIndex((rec) => { return (rec.getSender().getUid() === newReceipt.getSender().getUid()); }); if (receiptIndex > -1) { let oldReceipt = receipts[receiptIndex]; oldReceipt.setDeliveredAt(oldReceipt.getDeliveredAt() || newReceipt.getDeliveredAt()); oldReceipt.setReadAt(oldReceipt.getReadAt() || newReceipt.getReadAt()); let receiptsList = [...receipts]; receiptsList.splice(receiptIndex, 1, oldReceipt); setReceipts(receiptsList); } else { setReceipts([newReceipt, ...receipts]); } } }, [receipts, message]); // Add listener for message delivery/read events. useEffect(() => { CometChatUIEventHandler.addMessageListener(listenerId, { onMessagesDelivered: (messageReceipt) => { updateMessageReceipt(messageReceipt); }, onMessagesRead: (messageReceipt) => { updateMessageReceipt(messageReceipt); }, }); return () => CometChatUIEventHandler.removeMessageListener(listenerId); }, [message, receipts, updateMessageReceipt]); // Populate receipts when the message changes. useEffect(() => { populateReceipts(); }, [message]); /** * Renders the loading state view. * * @returns A JSX.Element representing the loading view. */ const LoadingView = () => { if (LoadingStateView) return <LoadingStateView />; return <Skeleton />; }; /** * Renders the error state view. * * @returns A JSX.Element representing the error view. */ const ErrorView = () => { if (ErrorStateView) return <ErrorStateView />; return (<ErrorEmptyView subTitle={localize("WRONG_TEXT")} tertiaryTitle={localize("WRONG_TEXT_TRY_AGAIN")} containerStyle={mergedStyle.errorStateStyle?.containerStyle} titleStyle={mergedStyle.errorStateStyle?.titleStyle} subTitleStyle={mergedStyle.errorStateStyle?.subtitleStyle}/>); }; /** * Renders the empty state view. * * @returns A JSX.Element representing the empty view. */ const EmptyView = useCallback(() => { if (EmptyStateView) return <EmptyStateView />; return <></>; }, [message, receipts]); return (<View style={mergedStyle.containerStyle}> <View style={mergedStyle.titleContainerStyle}> <View style={{ flex: 1, alignItems: "center" }}> <Text style={mergedStyle.titleStyle}>{title}</Text> </View> </View> <View style={mergedStyle.messageBubbleContainerStyle}> <ScrollView> <View pointerEvents='none'> {BubbleView ? BubbleView(message) : MessageUtils.getMessageView({ message, templates: template ? [template] : ChatConfigurator.dataSource.getAllMessageTemplates(theme), alignment: "right", theme: theme, })} </View> </ScrollView> </View> {listState === "loading" && receipts.length === 0 ? (<LoadingView />) : listState === "error" && receipts.length === 0 ? (<ErrorView />) : receipts.length === 0 ? (<EmptyView />) : (<FlatList style={{ flex: 1, marginBottom: 50 }} data={receipts} renderItem={renderReceipients}/>)} </View>); }; //# sourceMappingURL=CometChatMessageInformation.js.map