@rohitninawe/chat-uikit-react-native
Version:
Ready-to-use Chat UI Components for React Native
1,009 lines (1,008 loc) • 82.4 kB
JavaScript
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,