@cometchat/chat-uikit-react-native
Version:
Ready-to-use Chat UI Components for React Native
247 lines • 11.5 kB
JavaScript
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