UNPKG

@rohitninawe/chat-uikit-react-native

Version:

Ready-to-use Chat UI Components for React Native

1,009 lines (1,008 loc) 82.4 kB
import React, { forwardRef, useEffect, useRef, useState, useImperativeHandle, useContext, useCallback, memo, useLayoutEffect } from "react"; import { View, Text, Image, TouchableOpacity, ActivityIndicator, NativeModules, ScrollView, Dimensions, Keyboard } from "react-native"; //@ts-ignore import { CometChat } from "@cometchat/chat-sdk-react-native"; import { LeftArrowCurve } from "./resources"; import { BaseStyle, CometChatContext, CometChatMentionsFormatter, SuggestionItem } from "../shared"; import { MessageBubbleStyle } from "../shared/views/CometChatMessageBubble/MessageBubbleStyle"; import { AvatarStyle } from "../shared"; import { CometChatAvatar, CometChatDate, CometChatReceipt, DateStyle } from "../shared"; import { MessageListStyle } from "./MessageListStyle"; import { CometChatMessageBubble } from "../shared/views/CometChatMessageBubble"; import { CallTypeConstants, MessageCategoryConstants, MessageOptionConstants, MessageStatusConstants, MessageTypeConstants, ReceiverTypeConstants, ViewAlignment } from "../shared/constants/UIKitConstants"; import { localize } from "../shared"; import { downArrowIcon } from "./resources"; import { Style } from "./style"; import { CometChatSoundManager } from "../shared"; import { MessageEvents } from "../shared/events/messages"; import { ChatConfigurator } from "../shared/framework/ChatConfigurator"; import { CometChatActionSheet, ActionSheetStyles, CometChatBottomSheet } from "../shared"; import { getUnixTimestamp, messageStatus } from "../shared/utils/CometChatMessageHelper"; import { CometChatUIEventHandler } from "../shared/events/CometChatUIEventHandler/CometChatUIEventHandler"; import { CometChatMessageInformation } from "../CometChatMessageInformation/CometChatMessageInformation"; import { InteractiveMessageUtils } from "../shared/utils/InteractiveMessageUtils"; import { CometChatEmojiKeyboard } from "../shared/views/CometChatEmojiKeyboard"; import { CometChatReactionList } from "../shared/views/CometChatReactionList"; import { CometChatQuickReactions } from "../shared/views/CometChatQuickReactions"; import { CometChatReactions } from "../shared/views/CometChatReactions"; import { CommonUtils } from "../shared/utils/CommonUtils"; import Clipboard from "@react-native-clipboard/clipboard"; import { commonVars } from "../shared/base/vars"; let templatesMap = new Map(); let _defaultRequestBuilder; export const CometChatMessageList = memo(forwardRef((props, ref) => { const { parentMessageId, user, group, EmptyStateView, emptyStateText = localize("NO_MESSAGES_FOUND"), ErrorStateView, errorStateText = localize("SOMETHING_WRONG"), LoadingStateView, disableReceipt = false, disableSoundForMessages, customSoundForMessages, readIcon, deliveredIcon, sentIcon, waitIcon, errorIcon, alignment = "standard", showAvatar = true, datePattern, timeStampAlignment = "bottom", dateSeparatorPattern, templates = [], messageRequestBuilder, newMessageIndicatorText, scrollToBottomOnNewMessages, onThreadRepliesPress, HeaderView, FooterView, wrapperMessageBubbleStyle, avatarStyle, dateSeperatorStyle, actionSheetStyle, messageListStyle, onError, onBack, // forwardMessageConfiguration messageInformationConfiguration, hideActionSheetHeader, reactionsConfiguration, disableReactions, reactionListConfiguration, quickReactionConfiguration, emojiKeyboardStyle, disableMentions, textFormatters } = props; const callListenerId = "call_" + new Date().getTime(); const groupListenerId = "group_" + new Date().getTime(); const uiEventListener = "uiEvent_" + new Date().getTime(); const callEventListener = 'callEvent_' + new Date().getTime(); const uiEventListenerShow = "uiEvent_show_" + new Date().getTime(); const uiEventListenerHide = "uiEvent_hide_" + new Date().getTime(); const connectionListenerId = 'connectionListener_' + new Date().getTime(); useLayoutEffect(() => { if (user) { _defaultRequestBuilder = new CometChat.MessagesRequestBuilder() .setLimit(30) .setTags([]) .setUID(user["uid"]); } else if (group) { _defaultRequestBuilder = new CometChat.MessagesRequestBuilder() .setLimit(30) .setTags([]) .setGUID(group["guid"]); } _defaultRequestBuilder.setTypes(ChatConfigurator.dataSource.getAllMessageTypes()); _defaultRequestBuilder.setCategories(ChatConfigurator.dataSource.getAllMessageCategories()); //updating users request builder let _updatedCustomRequestBuilder = _defaultRequestBuilder; if (messageRequestBuilder) { _updatedCustomRequestBuilder = messageRequestBuilder; if (user) _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setUID(user["uid"]); if (group) _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setGUID(group["guid"]); } else { _updatedCustomRequestBuilder.hideReplies(true); if (user) _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setUID(user["uid"]); if (group) _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setGUID(group["guid"]); if (parentMessageId) _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setParentMessageId(parseInt(parentMessageId)); let types = [], categories = []; if (templates.length) { types = templates.map(template => template.type); categories = templates.map(template => template.category); } else { types = ChatConfigurator.dataSource.getAllMessageTypes(); categories = ChatConfigurator.dataSource.getAllMessageCategories(); } _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setTypes(types); _updatedCustomRequestBuilder = _updatedCustomRequestBuilder.setCategories(categories); } msgRequestBuilder.current = _updatedCustomRequestBuilder; }, []); const { theme } = useContext(CometChatContext); let Keyboard_Height = 0; let scrollPos = 0; // creating style based on styles from users const _messageListStyle = useRef(new MessageListStyle({ backgroundColor: theme?.palette.getBackgroundColor(), emptyStateTextColor: theme?.palette.getError(), emptyStateTextFont: theme?.typography.subtitle1, errorStateTextColor: theme?.palette.getAccent(), errorStateTextFont: theme?.typography.title1, loadingIconTint: theme?.palette.getAccent700(), nameTextColor: theme?.palette.getAccent(), nameTextFont: theme?.typography.name, threadReplyIconTint: theme?.palette.getAccent700(), threadReplySeparatorColor: theme?.palette.getAccent100(), threadReplyTextColor: theme?.palette.getPrimary(), threadReplyTextFont: theme?.typography.body, timestampTextColor: theme?.palette.getAccent500(), timestampTextFont: theme?.typography.caption1, ...messageListStyle })).current; const _avatarStyle = useRef(new AvatarStyle({ nameTextColor: _messageListStyle.nameTextColor, nameTextFont: _messageListStyle.nameTextFont, ...avatarStyle })).current; const _dateSeperatorStyle = useRef(new DateStyle({ textColor: _messageListStyle.timestampTextColor, textFont: theme?.typography.title2, ...dateSeperatorStyle })).current; const messageBubbleDateStyle = useRef(new DateStyle({ textColor: _messageListStyle.timestampTextColor, textFont: _messageListStyle.timestampTextFont })).current; const _actionStyle = useRef(new ActionSheetStyles({ actionSheetSeparatorTint: 'transparent', ...actionSheetStyle })).current; const _messageBubbleStyle = useRef(new MessageBubbleStyle({ ...wrapperMessageBubbleStyle })).current; // refs const currentScrollPosition = useRef({ y: null, scrollViewHeight: 0, layoutHeight: 0 }); const previousScrollPosition = useRef({ y: 0, scrollViewHeight: 0 }); const messagesLength = useRef(0); const prevMessagesLength = useRef(0); const messageListRef = useRef(null); const loggedInUser = useRef(null); const messageRequest = useRef(null); const messagesContentListRef = useRef([]); const msgRequestBuilder = useRef(); const lastMessageDate = useRef(new Date().getTime()); // states const [messagesList, setMessagesList] = useState([]); const [listState, setListState] = useState("loading"); const [loadingMessages, setLoadingMessages] = useState(false); const [unreadCount, setUnreadCount] = useState(0); const [showMessageOptions, setShowMessageOptions] = useState([]); const [ExtensionsComponent, setExtensionsComponent] = useState(null); const [CustomListHeader, setCustomListHeader] = useState(null); const [messageInfo, setMessageInfo] = useState(false); const [ongoingCallView, setOngoingCallView] = useState(null); const [selectedMessage, setSelectedMessage] = useState(null); const [showEmojiKeyboard, setShowEmojiKeyboard] = useState(false); const [showReactionList, setShowReactionList] = useState(false); const [selectedEmoji, setSelectedEmoji] = useState(null); // const [forwarding, setForwarding] = useState(false); const infoObject = useRef(); const inProgressMessages = useRef([]); // const messageToForward = useRef<CometChat.BaseMessage>(); const bottomSheetRef = useRef(null); const conversationId = useRef(null); let lastID = useRef(0); const scrollHandler = (event) => { const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; const screenHeight = Dimensions.get('window').height; // Calculate the scroll position const scrollPosition = layoutMeasurement.height + contentOffset.y; const scrollEndPosition = contentSize.height - screenHeight; currentScrollPosition.current.y = contentOffset.y; if (currentScrollPosition.current.layoutHeight != layoutMeasurement.height) { currentScrollPosition.current.layoutHeight = layoutMeasurement.height; } if (currentScrollPosition.current.scrollViewHeight !== contentSize.height) { currentScrollPosition.current.scrollViewHeight = contentSize.height; } // Check if the scroll position is at the end if (scrollPosition >= scrollEndPosition && unreadCount > 0) { markUnreadMessageAsRead(); } }; const markUnreadMessageAsRead = () => { for (let index = 0; index < unreadCount; index++) { const message = messagesContentListRef.current[messagesContentListRef.current.length - (index + 1)]; if (index == 0) CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageRead, { message }); CometChat.markAsRead(message); setUnreadCount(0); } }; const newMsgIndicatorPressed = () => { scrollToBottom(); markUnreadMessageAsRead(); }; const getPreviousMessages = async () => { if (messagesList.length == 0) setListState("loading"); else setLoadingMessages(true); // TODO: this condition is applied because somewhere from whiteboard extention group scope is set to undefined. if (group != undefined && group['scope'] == undefined) { let fetchedGroup = await CometChat.getGroup(group['guid']).catch(e => { console.log("Error: fetching group", e); onError && onError(e); }); group['scope'] = fetchedGroup['scope']; } messageRequest.current.fetchPrevious() .then(msgs => { let reversed = msgs.reverse(); if (messagesList.length === 0 && msgs?.length > 0) { CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccActiveChatChanged, { message: reversed[0], user: user, group: group, theme: theme, parentMessageId: parentMessageId }); if (conversationId.current == null) conversationId.current = reversed[0].getConversationId(); } else if (messagesList.length === 0 && !props?.parentMessageId) { CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccActiveChatChanged, { message: reversed[0], user: user, group: group, theme: theme, parentMessageId: parentMessageId }); } for (let index = 0; index < reversed.length; index++) { const message = reversed[index]; if (message && !disableReceipt && !message.hasOwnProperty("readAt") && loggedInUser.current.getUid() != message['sender']['uid']) { CometChat.markAsRead(message); if (index == 0) CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageRead, { message }); } else break; } reversed = reversed.map((item, index) => { if (item.getCategory() === MessageCategoryConstants.interactive) { return InteractiveMessageUtils.convertInteractiveMessage(item); } else { return item; } }); if (reversed.length > 0) { let reversedData = [...reversed.reverse()]; messagesContentListRef.current = [...reversedData, ...messagesContentListRef.current]; setMessagesList([...reversedData, ...messagesList]); } if (messagesList.length == 0) setListState(""); else setLoadingMessages(false); }) .catch(e => { if (messagesList.length == 0) setListState("error"); else setLoadingMessages(false); onError && onError(e); }); }; const getUpdatedPreviousMessages = () => { let messagesRequest = new CometChat.MessagesRequestBuilder().setLimit(50); if (user) messagesRequest = messagesRequest.setUID(user["uid"]); if (group) messagesRequest = messagesRequest.setGUID(group["guid"]); messagesRequest.setTypes([MessageCategoryConstants.message]); messagesRequest.setCategories([MessageCategoryConstants.action]); messagesRequest.setMessageId(lastID.current); messagesRequest.build() .fetchNext() .then(updatedMessages => { let tmpList = [...messagesList]; for (let i = 0; i < updatedMessages.length; i++) { let condition = (msg) => msg.getId() == updatedMessages[i]?.actionOn.getId(); let msgIndex = messagesList.findIndex(condition); if (msgIndex > -1) { tmpList[msgIndex] = updatedMessages[i]?.actionOn; } } // console.log("UPDATES LIST LENGTH", tmpList.length) // setMessagesList(tmpList); getNewMessages(tmpList); }) .catch(e => console.log("error while fetching updated msgs", e)); }; const getNewMessages = (updatedList) => { let newRequestBuilder = msgRequestBuilder.current; newRequestBuilder.setMessageId(lastID.current); newRequestBuilder.build() .fetchNext() .then(newMessages => { let cleanUpdatedList = [...updatedList]; for (let i = (cleanUpdatedList.length - 1); i >= 0; i--) { if (cleanUpdatedList[i].id == lastID.current) break; if (cleanUpdatedList[i].id == undefined || Number.isNaN(parseInt(cleanUpdatedList[i].id))) cleanUpdatedList.splice(i, 1); } // console.log("newMessages", newMessages.length, JSON.stringify(newMessages)) if (cleanUpdatedList?.length > 0 && cleanUpdatedList?.[cleanUpdatedList.length - 1]?.["muid"]) { let localFileExists = newMessages.findIndex(msg => msg?.["muid"] == cleanUpdatedList?.[cleanUpdatedList.length - 1]?.["muid"]); if (localFileExists > -1) { cleanUpdatedList.shift(); } } let tmpList = [...cleanUpdatedList, ...newMessages]; tmpList = tmpList.map((item, index) => { if (item.getCategory() === MessageCategoryConstants.interactive) { return InteractiveMessageUtils.convertInteractiveMessage(item); } else { return item; } }); if (inProgressMessages.current.length) { const filteredInProgressMessages = inProgressMessages.current.filter(secondItem => !tmpList.some(firstItem => firstItem.muid === secondItem.muid)); const combinedArray = tmpList.concat(filteredInProgressMessages); tmpList = combinedArray; } messagesContentListRef.current = tmpList; setMessagesList(tmpList); bottomHandler(tmpList[tmpList.length - 1], true); if (newMessages.length === 30) { getNewMessages(tmpList); } newRequestBuilder.setMessageId(undefined); }) .catch(e => console.log("error while fetching updated msgs", e)); }; const loadTemplates = useCallback(() => { let _formatters = textFormatters || []; let templates = ChatConfigurator.dataSource.getAllMessageTemplates(theme, { textFormatters: _formatters, disableMentions: disableMentions }); templates.forEach(template => { if (templatesMap.get(`${template.category}_${template.type}`)) return; templatesMap.set(`${template.category}_${template.type}`, template); }); }, []); const getPlainString = (underlyingText, messageObject) => { let _plainString = underlyingText; let regexes = []; let users = {}; let edits = []; let allFormatters = [...(textFormatters || [])]; let mentionsTextFormatter = ChatConfigurator.getDataSource().getMentionsFormatter(loggedInUser.current); allFormatters.push(mentionsTextFormatter); allFormatters.forEach((formatter, key) => { regexes.push(formatter.getRegexPattern()); let suggestionUsers = formatter.getSuggestionItems(); if (formatter instanceof CometChatMentionsFormatter) { let mentionUsers = (messageObject?.getMentionedUsers && messageObject?.getMentionedUsers()).map(item => new SuggestionItem({ id: item.getUid(), name: item.getName(), promptText: "@" + item.getName(), trackingCharacter: "@", underlyingText: `<@uid:${item.getUid()}>`, hideLeadingIcon: false })) || []; suggestionUsers = [...suggestionUsers, ...mentionUsers]; } suggestionUsers?.length > 0 && suggestionUsers.forEach(item => users[item.underlyingText] = item); }); regexes.forEach(regex => { let match; while ((match = regex.exec(_plainString)) !== null) { const user = users[match[0]]; if (user) { edits.push({ startIndex: match.index, endIndex: regex.lastIndex, replacement: user.promptText, user }); } } }); // Sort edits by startIndex to apply them in order edits.sort((a, b) => a.startIndex - b.startIndex); // Get an array of the entries in the map using the spread operator const entries = [...edits].reverse(); // Iterate over the array in reverse order entries.forEach(({ endIndex, replacement, startIndex, user }) => { let pre = _plainString.substring(0, startIndex); let post = _plainString.substring(endIndex); _plainString = pre + replacement + post; }); return _plainString; }; const playNotificationSound = useCallback((message) => { if (disableSoundForMessages) return; if (message?.category === MessageCategoryConstants.message) { if (customSoundForMessages) { CometChatSoundManager.play(loggedInUser.current.getUid() == message['sender']['uid'] ? "outgoingMessage" : "incomingMessage", customSoundForMessages); } else { CometChatSoundManager.play( // "incomingMessage" loggedInUser.current.getUid() == message['sender']['uid'] ? "outgoingMessage" : "incomingMessage"); } } }, []); const scrollToBottom = useCallback((scrollToFirstUnread = false) => { if (messageListRef.current && messagesContentListRef.current.length > 0) { let firstUnreadPosition = previousScrollPosition.current.scrollViewHeight; if (scrollToFirstUnread) { setTimeout(() => { messageListRef.current.scrollTo({ x: 0, y: firstUnreadPosition, animated: false }); }, 0); } else { setTimeout(() => { messageListRef.current.scrollToEnd({ animated: true }); }, 0); } } }, []); const markMessageAsRead = (message) => { if (!disableReceipt && !message?.readAt) { CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageRead, { message }); CometChat.markAsRead(message) .catch((error) => { console.log("Error", error); onError && onError(error); // errorHandler(error); }); } }; function checkMessageInSameConversation(message) { return (message?.getReceiverType() == ReceiverTypeConstants.user && user && user?.getUid() == message.getReceiver()?.['uid']) || (message?.getReceiverType() == ReceiverTypeConstants.group && message.getReceiverId() && group && group?.getGuid() == message.getReceiverId()); } function messageToSameConversation(message) { return (message?.getReceiverType() == ReceiverTypeConstants.user && user && user?.getUid() == message.getReceiverId()) || (message?.getReceiverType() == ReceiverTypeConstants.group && message.getReceiverId() && group && group?.getGuid() == message.getReceiverId()); } function checkSameConversation(message) { return conversationId.current != null && (message.getConversationId() == conversationId.current); } function isNearBottom() { const { layoutHeight, scrollViewHeight, y } = currentScrollPosition.current; let scrollPos = scrollViewHeight - (layoutHeight + y); let scrollPosFromBottomInPercentage = (scrollPos / layoutHeight) * 100; if (scrollPosFromBottomInPercentage <= 30) { // 30% from bottom return true; } return false; } const newMessage = (newMessage, isReceived = true) => { let baseMessage = newMessage; if (baseMessage.getCategory() === MessageCategoryConstants.interactive) { newMessage = InteractiveMessageUtils.convertInteractiveMessage(baseMessage); } if (checkSameConversation(baseMessage) || checkMessageInSameConversation(baseMessage) || messageToSameConversation(baseMessage)) { //need to add if (newMessage.getParentMessageId()) { if (newMessage.getParentMessageId() == parseInt(parentMessageId)) { // add to list messagesContentListRef.current = [...messagesContentListRef.current, newMessage]; inProgressMessages.current = [...inProgressMessages.current, newMessage]; setMessagesList(prev => [...prev, newMessage]); } else { //increase count let index = messagesList.findIndex(msg => msg.id === newMessage.parentMessageId); let oldMsg = CommonUtils.clone(messagesList[index]); oldMsg.setReplyCount(oldMsg.getReplyCount() ? oldMsg.getReplyCount() + 1 : 1); // if ((newMessage?.sender?.uid !== loggedInUser.current?.['uid'])) { // oldMsg.setUnreadReplyCount(oldMsg.getUnreadReplyCount() ? oldMsg.getUnreadReplyCount() + 1 : 1); // } let tmpList = [...messagesList]; tmpList[index] = oldMsg; messagesContentListRef.current = tmpList; setMessagesList(tmpList); inProgressMessages.current = [...inProgressMessages.current, newMessage]; } } else if (parentMessageId == undefined) { messagesContentListRef.current = [...messagesContentListRef.current, newMessage]; inProgressMessages.current = [...inProgressMessages.current, newMessage]; setMessagesList(prev => [...prev, newMessage]); } bottomHandler(newMessage, isReceived); } playNotificationSound(newMessage); }; const bottomHandler = (newMessage, isReceived) => { // if scroll is not at bottom if (!scrollToBottomOnNewMessages && currentScrollPosition.current.y && (Math.round(currentScrollPosition.current.y) <= currentScrollPosition.current.scrollViewHeight)) { if ((parentMessageId && newMessage.parentMessageId == parseInt(parentMessageId)) || (!parentMessageId && !newMessage.parentMessageId && (newMessage.getSender()?.getUid() || newMessage?.['sender']?.['uid']) == loggedInUser.current?.['uid'])) { scrollToBottom(); return; } CometChat.markAsDelivered(newMessage); if (newMessage?.getReceiverType() == ReceiverTypeConstants.user) { CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageDelivered, { message: newMessage }); } if (isNearBottom()) { scrollToBottom(); if (isReceived) { markMessageAsRead(newMessage); CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageRead, { message: newMessage }); } } else if ((!parentMessageId && !(newMessage.parentMessageId)) || (parentMessageId && newMessage.parentMessageId == parseInt(parentMessageId))) { setUnreadCount(unreadCount + 1); } } else { scrollToBottom(); if (isReceived) { markMessageAsRead(newMessage); CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageRead, { message: newMessage }); } } }; const markParentMessageAsRead = (message) => { let condition; condition = (msg) => msg.getId() == message?.['parentMessageId']; let msgIndex = messagesList.findIndex(condition); if (msgIndex > -1) { let tmpList = [...messagesList]; if (message.getCategory() === MessageCategoryConstants.interactive) { message = InteractiveMessageUtils.convertInteractiveMessage(message); } tmpList[msgIndex]?.setUnreadReplyCount(0); messagesContentListRef.current = tmpList; setMessagesList(tmpList); } }; const messageEdited = (editedMessage, withMuid = false) => { let condition; if (withMuid) { condition = (msg) => msg['muid'] == editedMessage['muid']; inProgressMessages.current = inProgressMessages.current.filter(item => item.muid !== editedMessage['muid']); } else condition = (msg) => msg.getId() == editedMessage.getId(); let msgIndex = messagesList.findIndex(condition); if (msgIndex > -1) { let tmpList = [...messagesList]; if (editedMessage.getCategory() === MessageCategoryConstants.interactive) { editedMessage = InteractiveMessageUtils.convertInteractiveMessage(editedMessage); } tmpList[msgIndex] = CommonUtils.clone(editedMessage); messagesContentListRef.current = tmpList; setMessagesList(tmpList); } }; const removeMessage = (message) => { let msgIndex = messagesList.findIndex(msg => msg.getId() == message.getId()); if (msgIndex == -1) return; let tmpList = [...messagesList]; tmpList.splice(msgIndex, 1); messagesContentListRef.current = tmpList; setMessagesList(tmpList); }; const deleteMessage = (message) => { CometChat.deleteMessage(message.getId().toString()) .then(res => { CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageDeleted, { message: res }); setShowMessageOptions([]); }) .catch(rej => { console.log(rej); onError && onError(rej); }); }; const createActionMessage = () => { }; const updateMessageReceipt = (receipt) => { let index = messagesList.findIndex((msg, index) => msg['id'] == receipt['messageId'] || msg['messageId'] == receipt['messageId']); if (index == -1) return; let tmpList = [...messagesList]; for (let i = index; i > 0; i--) { if (tmpList[i]?.getReadAt && tmpList[i]?.getReadAt()) break; let tmpMsg = tmpList[i]; if (tmpMsg?.getReceiverType() == ReceiverTypeConstants.group) return; if (tmpMsg.getCategory() === MessageCategoryConstants.interactive) { tmpMsg = InteractiveMessageUtils.convertInteractiveMessage(tmpMsg); } if (receipt.getDeliveredAt) { tmpMsg.setDeliveredAt(receipt.getDeliveredAt()); } if (receipt.getReadAt) { tmpMsg.setReadAt(receipt.getReadAt()); } tmpList[i] = CommonUtils.clone(tmpMsg); } messagesContentListRef.current = tmpList; setMessagesList(tmpList); }; const handlePannel = (item) => { if (item.alignment === ViewAlignment.messageListBottom && CommonUtils.checkIdBelongsToThisComponent(item.id, user, group, parentMessageId)) { if (item.child) setCustomListHeader(() => item.child); else setCustomListHeader(null); } }; useEffect(() => { const showSubscription = Keyboard.addListener('keyboardDidShow', (e) => onKeyboardVisibiltyChange(true, e?.endCoordinates?.height)); const hideSubscription = Keyboard.addListener('keyboardDidHide', () => onKeyboardVisibiltyChange(false)); CometChatUIEventHandler.addUIListener(uiEventListenerShow, { showPanel: (item) => handlePannel(item), // ccMentionClick: (item) => { // // console.log("item", item) // } }); CometChatUIEventHandler.addUIListener(uiEventListenerHide, { hidePanel: (item) => handlePannel(item) }); CometChatUIEventHandler.addUIListener(uiEventListener, { ccToggleBottomSheet: (item) => { bottomSheetRef.current?.togglePanel(); }, }); CometChatUIEventHandler.addCallListener(callEventListener, { ccShowOngoingCall: (CometChatOngoingComponent) => { //show ongoing call setOngoingCallView(CometChatOngoingComponent?.child); }, }); CometChat.getLoggedinUser() .then(u => { loggedInUser.current = u; messageRequest.current = msgRequestBuilder.current.build(); getPreviousMessages(); loadTemplates(); }) .catch(e => { console.log("Error while getting loggedInUser"); onError && onError(e); loggedInUser.current = null; }); return () => { showSubscription.remove(); hideSubscription.remove(); CometChatUIEventHandler.removeUIListener(uiEventListenerShow); CometChatUIEventHandler.removeUIListener(uiEventListenerHide); CometChatUIEventHandler.removeUIListener(uiEventListener); CometChatUIEventHandler.removeCallListener(callEventListener); onBack && onBack(); }; }, []); useEffect(() => { //add listeners let reactionListeners = disableReactions ? {} : { onMessageReactionAdded: (reaction) => { updateMessageReaction(reaction, true); }, onMessageReactionRemoved: (reaction) => { updateMessageReaction(reaction, false); } }; CometChat.addGroupListener(groupListenerId, new CometChat.GroupListener({ onGroupMemberScopeChanged: (message) => { newMessage(message); }, onGroupMemberLeft: (message) => { newMessage(message); }, onGroupMemberKicked: (message) => { newMessage(message); }, onGroupMemberBanned: (message) => { newMessage(message); }, onGroupMemberUnbanned: (message) => { }, onMemberAddedToGroup: (message) => { newMessage(message); }, onGroupMemberJoined: (message) => { newMessage(message); }, })); CometChatUIEventHandler.addMessageListener(uiEventListener, { ccMessageSent: ({ message, status }) => { if (status == MessageStatusConstants.inprogress) { newMessage(message, false); } if (status == MessageStatusConstants.success) { messageEdited(message, true); } if (status == MessageStatusConstants.error) { messageEdited(message, true); } }, ccMessageEdited: ({ message, status }) => { if (status == messageStatus.success) messageEdited(message, false); }, ccMessageDeleted: ({ message }) => { messageEdited(message, false); }, ccMessageRead: ({ message }) => { if (!parentMessageId && message.parentMessageId) { // markParentMessageAsRead(message); //NOTE: uncomment this when want unread count in thread view } }, onTextMessageReceived: (textMessage) => { newMessage(textMessage); }, onMediaMessageReceived: (mediaMessage) => { newMessage(mediaMessage); }, onCustomMessageReceived: (customMessage) => { newMessage(customMessage); }, onMessagesDelivered: (messageReceipt) => { updateMessageReceipt(messageReceipt); }, onMessagesRead: (messageReceipt) => { updateMessageReceipt(messageReceipt); }, onMessageDeleted: (deletedMessage) => { messageEdited(deletedMessage); }, onMessageEdited: (editedMessage) => { messageEdited(editedMessage); }, onFormMessageReceived: (formMessage) => { newMessage(formMessage); }, onCardMessageReceived: (cardMessage) => { newMessage(cardMessage); }, onSchedulerMessageReceived: (schedulerMessage) => { newMessage(schedulerMessage); }, onCustomInteractiveMessageReceived: (customInteractiveMessage) => { newMessage(customInteractiveMessage); }, onInteractionGoalCompleted: (interactionReceipt) => { if (loggedInUser?.current?.['uid'] === interactionReceipt?.getSender()?.getUid()) { // && String(message?.getId()) == String(interactionReceipt.getMessageId()) let msgIndex = messagesList.findIndex(item => { console.log("getId", item.getId(), interactionReceipt.getMessageId()); return item.getId() == interactionReceipt.getMessageId(); }); console.log("msgIndex", msgIndex); if (msgIndex === -1) return; const interaction = interactionReceipt.getInteractions() || []; let editedMessage = messagesList[msgIndex]; editedMessage = InteractiveMessageUtils.convertInteractiveMessage(editedMessage); editedMessage.setInteractions(interaction); messageEdited(editedMessage); } }, ...reactionListeners }); CometChatUIEventHandler.addGroupListener(uiEventListener, { ccGroupMemberUnBanned: ({ message }) => { newMessage(message, false); }, ccGroupMemberBanned: ({ message }) => { newMessage(message, false); }, ccGroupMemberAdded: ({ message, usersAdded, userAddedIn }) => { usersAdded.forEach(user => { message['message'] = `${loggedInUser.current['name']} added ${user.name}`; message['muid'] = String(getUnixTimestamp()); message['sentAt'] = getUnixTimestamp(); newMessage(message, false); }); }, ccGroupMemberKicked: ({ message }) => { newMessage(message, false); }, ccGroupMemberScopeChanged: ({ action, updatedUser, scopeChangedTo, scopeChangedFrom, group }) => { newMessage(action, false); }, ccOwnershipChanged: ({ group, message }) => { // newMessage(message, false); removed after discussion. } }); CometChat.addCallListener(callListenerId, new CometChat.CallListener({ onIncomingCallReceived: (call) => { newMessage(call); }, onOutgoingCallAccepted: (call) => { newMessage(call); }, onOutgoingCallRejected: (call) => { newMessage(call); }, onIncomingCallCancelled: (call) => { newMessage(call); } })); CometChatUIEventHandler.addCallListener(uiEventListener, { ccCallInitiated: ({ call }) => { if (call['type'] == CallTypeConstants.audio || call['type'] == CallTypeConstants.video) { newMessage(call); } }, ccOutgoingCall: ({ call }) => { if (call['type'] == CallTypeConstants.audio || call['type'] == CallTypeConstants.video) { newMessage(call); } }, ccCallAccepted: ({ call }) => { if (call['type'] == CallTypeConstants.audio || call['type'] == CallTypeConstants.video) { newMessage(call); } }, ccCallRejected: ({ call }) => { if (call['type'] == CallTypeConstants.audio || call['type'] == CallTypeConstants.video) { newMessage(call); } }, ccCallEnded: ({ call }) => { if (call['type'] == CallTypeConstants.audio || call['type'] == CallTypeConstants.video) { newMessage(call); } } }); CometChat.addConnectionListener(connectionListenerId, new CometChat.ConnectionListener({ onConnected: () => { console.log("ConnectionListener => On Connected - Message List", messagesList.length); getUpdatedPreviousMessages(); }, inConnecting: () => { console.log("ConnectionListener => In connecting"); }, onDisconnected: () => { console.log("ConnectionListener => On Disconnected"); if (!messagesList[messagesList.length - 1].id) { for (let i = (messagesList.length - 1); i >= 0; i--) { if (messagesList[i].id) { lastID.current = messagesList[i].id; break; } } } else { lastID.current = messagesList[messagesList.length - 1].id; } } })); return () => { // clean up code like removing listeners CometChatUIEventHandler.removeMessageListener(uiEventListener); CometChatUIEventHandler.removeGroupListener(uiEventListener); CometChatUIEventHandler.removeCallListener(uiEventListener); CometChat.removeGroupListener(groupListenerId); CometChat.removeCallListener(callListenerId); CometChat.removeConnectionListener(connectionListenerId); }; }, [messagesList, unreadCount, user, group]); useEffect(() => { prevMessagesLength.current = messagesLength.current || messagesContentListRef.current.length; messagesLength.current = messagesContentListRef.current.length; }, [messagesContentListRef.current]); useEffect(() => { if (selectedEmoji) { setShowReactionList(true); } }, [selectedEmoji]); useImperativeHandle(ref, () => { return { addMessage: newMessage, updateMessage: messageEdited, removeMessage, deleteMessage, scrollToBottom, /// todo: not handeled yet createActionMessage, updateMessageReceipt, }; }); const onKeyboardVisibiltyChange = (isVisible, keyboardHeight) => { if (messageListRef.current) { if (isVisible) { Keyboard_Height = keyboardHeight; scrollPos = (currentScrollPosition.current.y + Keyboard_Height) - (commonVars.safeAreaInsets.top / 2); messageListRef.current.scrollTo({ y: scrollPos, animated: false }); } else { messageListRef.current.scrollTo({ y: scrollPos - Keyboard_Height + (commonVars.safeAreaInsets.top / 2), animated: false }); } } }; const getMessageById = (messageId) => { const message = messagesList.find((message) => message.getId() === messageId); return message; }; function isReactionOfThisList(receipt) { const receiverId = receipt?.getReceiverId(); const receiverType = receipt?.getReceiverType(); const reactedById = receipt?.getReaction()?.getReactedBy()?.getUid(); const parentMessageId = receipt?.getParentMessageId(); const listParentMessageId = parentMessageId && String(parentMessageId); if (listParentMessageId) { if (parentMessageId === listParentMessageId) { return true; } else { return false; } } else { if (receipt.getParentMessageId()) { return false; } if (user) { if (receiverType === ReceiverTypeConstants.user && (receiverId === user.getUid() || reactedById === user.getUid())) { return true; } } else if (group) { if (receiverType === ReceiverTypeConstants.group && (receiverId === group.getGuid())) { return true; } } } return false; } const updateMessageReaction = (message, isAdded) => { let _isReactionOfThisList = isReactionOfThisList(message); if (!_isReactionOfThisList) return; const messageId = message?.getReaction()?.getMessageId(); const messageObject = getMessageById(messageId); if (!messageObject) return; let action; if (isAdded) { action = CometChat.REACTION_ACTION.REACTION_ADDED; } else { action = CometChat.REACTION_ACTION.REACTION_REMOVED; } const modifiedMessage = CometChat.CometChatHelper.updateMessageWithReactionInfo(messageObject, message.getReaction(), action); if (modifiedMessage instanceof CometChat.CometChatException) { onError && onError(modifiedMessage); return; } messageEdited(modifiedMessage, false); }; // functions returning view const getLeadingView = useCallback((item) => { if (showAvatar && (alignment === "leftAligned" || (item.getSender()?.getUid() || item?.['sender']?.['uid']) !== loggedInUser.current['uid'] && item['category'] != MessageCategoryConstants.action)) { return <CometChatAvatar image={(item?.getSender()?.getAvatar && item?.getSender()?.getAvatar()) ? { uri: item.getSender().getAvatar() } : undefined} name={(item?.getSender()?.getName && item?.getSender()?.getName()) ? item?.getSender()?.getName() : ""} style={_avatarStyle}/>; } return null; }, []); const getHeaderView = useCallback((item) => { let senderName = ""; if (alignment === "leftAligned" || ((item.getSender()?.getUid() || item?.['sender']?.['uid']) != loggedInUser.current['uid'])) senderName = item.getSender()?.getName ? item.getSender()?.getName() : ""; if (item.getCategory() == MessageCategoryConstants.action || item.getCategory() == MessageCategoryConstants.call) return null; return (<View style={{ flexDirection: "row" }}> {Boolean(senderName) && <Text style={[Style.nameStyle, { color: _messageListStyle.nameTextColor, ..._messageListStyle.nameTextFont, }]}>{senderName}</Text>} {timeStampAlignment == "bottom" || item['category'] == "action" ? null : <CometChatDate timeStamp={((item.getDeletedAt() || item.getReadAt() || item.getDeliveredAt() || item.getSentAt()) * 1000) || getSentAtTimestamp(item)} style={messageBubbleDateStyle} pattern={"timeFormat"} customDateString={datePattern && datePattern(item)} dateAlignment="center"/>} </View>); }, []); const getStatusInfoView = useCallback((item, bubbleAlignment) => { // return null if time alignment is top if (timeStampAlignment == "top" || item['category'] == "action" || item['deletedAt']) return null; let isSender = (item.getSender()?.getUid() || item?.['sender']?.['uid']) == loggedInUser.current['uid']; let messageState; if (item.getReadAt()) messageState = "READ"; else if (item.getDeliveredAt()) messageState = "DELIVERED"; else if (item.getSentAt()) messageState = "SENT"; else if (item?.data?.metaData?.error) messageState = "ERROR"; else if (isSender) messageState = "WAIT"; else messageState = "ERROR"; let isAudioVideo = (item?.getType() === "image" || item?.getType() === "video"); return <View style={[ isAudioVideo ? { flexDirection: "row", justifyContent: bubbleAlignment === "right" ? "flex-end" : "flex-start", alignSelf: "flex-end", paddingVertical: 2, paddingHorizontal: 5, position: "absolute", borderRadius: 10, backgroundColor: theme.palette.getAccent500("dark"), zIndex: 1, bottom: 5, right: 5, } : { flexDirection: "row", justifyContent: bubbleAlignment === "right" ? "flex-end" : "flex-start", alignSelf: "flex-end", padding: 5 } ]}> <CometChatDate timeStamp={((item.getDeletedAt() || item.getReadAt() || item.getDeliveredAt() || item.getSentAt()) * 1000) || getSentAtTimestamp(item)} style={{ ...messageBubbleDateStyle, textFont: isAudioVideo ? theme.typography.caption3 : messageBubbleDateStyle.textFont }} pattern={"timeFormat"} customDateString={datePattern && datePattern(item)} dateAlignment="center"/> {(!disableReceipt && alignment !== "leftAligned") && isSender ? <View style={{ marginLeft: 2, alignItems: "center", justifyContent: "center" }}> <CometChatReceipt receipt={messageState} deliveredIcon={deliveredIcon} readIcon={readIcon} sentIcon={sentIcon} waitIcon={waitIcon} errorIcon={errorIcon} style={{ height: isAudioVideo && 8, width: isAudioVideo && 10,