2017-10-12 10:00:28 +08:00
|
|
|
import Users from '/imports/api/users';
|
2021-10-20 04:35:39 +08:00
|
|
|
import Meetings from '/imports/api/meetings';
|
2016-06-02 21:46:35 +08:00
|
|
|
import Auth from '/imports/ui/services/auth';
|
2016-07-05 02:53:47 +08:00
|
|
|
import UnreadMessages from '/imports/ui/services/unread-messages';
|
2017-03-01 06:40:16 +08:00
|
|
|
import Storage from '/imports/ui/services/storage/session';
|
2022-05-24 01:09:50 +08:00
|
|
|
import { stripTags, unescapeHtml } from '/imports/utils/string-utils';
|
2020-05-01 05:50:34 +08:00
|
|
|
import { meetingIsBreakout } from '/imports/ui/components/app/service';
|
2021-04-13 19:43:08 +08:00
|
|
|
import { defineMessages } from 'react-intl';
|
2021-05-29 08:28:47 +08:00
|
|
|
import PollService from '/imports/ui/components/poll/service';
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2024-03-07 01:28:18 +08:00
|
|
|
const APP = window.meetingClientSettings.public.app;
|
|
|
|
const CHAT_CONFIG = window.meetingClientSettings.public.chat;
|
2016-08-17 23:48:03 +08:00
|
|
|
const GROUPING_MESSAGES_WINDOW = CHAT_CONFIG.grouping_messages_window;
|
2016-06-02 00:33:19 +08:00
|
|
|
|
2016-08-17 23:48:03 +08:00
|
|
|
const SYSTEM_CHAT_TYPE = CHAT_CONFIG.type_system;
|
2016-06-02 00:33:19 +08:00
|
|
|
|
2016-08-17 23:48:03 +08:00
|
|
|
const PUBLIC_CHAT_ID = CHAT_CONFIG.public_id;
|
2018-07-27 21:44:21 +08:00
|
|
|
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2022-06-01 20:49:27 +08:00
|
|
|
const PUBLIC_CHAT_CLEAR = CHAT_CONFIG.system_messages_keys.chat_clear;
|
2021-05-20 22:53:52 +08:00
|
|
|
const CHAT_POLL_RESULTS_MESSAGE = CHAT_CONFIG.system_messages_keys.chat_poll_result;
|
|
|
|
|
2024-03-07 01:28:18 +08:00
|
|
|
const ROLE_MODERATOR = window.meetingClientSettings.public.user.role_moderator;
|
2019-02-26 07:31:33 +08:00
|
|
|
|
2016-07-01 01:10:36 +08:00
|
|
|
const ScrollCollection = new Mongo.Collection(null);
|
|
|
|
|
2019-02-05 20:24:45 +08:00
|
|
|
const UnsentMessagesCollection = new Mongo.Collection(null);
|
|
|
|
|
2021-01-20 01:06:32 +08:00
|
|
|
export const UserSentMessageCollection = new Mongo.Collection(null);
|
|
|
|
|
2017-03-17 23:27:37 +08:00
|
|
|
// session for closed chat list
|
|
|
|
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
|
|
|
|
2021-04-12 20:13:42 +08:00
|
|
|
const intlMessages = defineMessages({
|
|
|
|
publicChatClear: {
|
|
|
|
id: 'app.chat.clearPublicChatMessage',
|
|
|
|
description: 'message of when clear the public chat',
|
|
|
|
},
|
2021-05-29 08:28:47 +08:00
|
|
|
pollResult: {
|
|
|
|
id: 'app.chat.pollResult',
|
|
|
|
description: 'used in place of user name who published poll to chat',
|
|
|
|
},
|
2021-04-12 20:13:42 +08:00
|
|
|
});
|
|
|
|
|
2021-01-20 01:06:32 +08:00
|
|
|
const setUserSentMessage = (bool) => {
|
|
|
|
UserSentMessageCollection.upsert(
|
|
|
|
{ userId: Auth.userID },
|
|
|
|
{ $set: { sent: bool } },
|
|
|
|
);
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2021-01-20 01:06:32 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const getUser = (userId) => Users.findOne({ userId });
|
2017-07-12 21:18:26 +08:00
|
|
|
|
2019-08-22 20:05:06 +08:00
|
|
|
const getWelcomeProp = () => Meetings.findOne({ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { welcomeProp: 1 } });
|
2018-10-08 22:22:45 +08:00
|
|
|
|
2018-07-26 22:56:26 +08:00
|
|
|
const mapGroupMessage = (message) => {
|
|
|
|
const mappedMessage = {
|
2020-01-22 05:08:48 +08:00
|
|
|
id: message._id || message.id,
|
2018-07-26 22:56:26 +08:00
|
|
|
content: message.content,
|
2020-01-22 05:08:48 +08:00
|
|
|
time: message.timestamp || message.time,
|
2018-07-26 22:56:26 +08:00
|
|
|
sender: null,
|
2021-05-18 04:25:07 +08:00
|
|
|
key: message.key,
|
2024-01-10 21:53:29 +08:00
|
|
|
chatId: message.chatId,
|
2018-07-26 22:56:26 +08:00
|
|
|
};
|
|
|
|
|
2021-04-10 04:35:05 +08:00
|
|
|
if (message.sender && message.sender !== SYSTEM_CHAT_TYPE) {
|
2021-05-18 04:25:07 +08:00
|
|
|
const sender = Users.findOne(
|
|
|
|
{ userId: message.sender },
|
|
|
|
{
|
|
|
|
fields: { avatar: 1, role: 1, name: 1 },
|
|
|
|
},
|
|
|
|
);
|
2018-12-20 02:51:03 +08:00
|
|
|
|
|
|
|
const mappedSender = {
|
2020-10-13 21:49:12 +08:00
|
|
|
avatar: sender?.avatar,
|
2020-08-20 00:22:43 +08:00
|
|
|
color: message.color,
|
2022-09-20 20:57:08 +08:00
|
|
|
isModerator: message.senderRole === ROLE_MODERATOR,
|
|
|
|
name: message.senderName,
|
2020-08-20 00:22:43 +08:00
|
|
|
isOnline: !!sender,
|
2018-12-20 02:51:03 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
mappedMessage.sender = mappedSender;
|
2018-07-26 22:56:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return mappedMessage;
|
|
|
|
};
|
|
|
|
|
|
|
|
const reduceGroupMessages = (previous, current) => {
|
|
|
|
const lastMessage = previous[previous.length - 1];
|
|
|
|
const currentMessage = current;
|
2018-07-27 21:44:21 +08:00
|
|
|
currentMessage.content = [{
|
2018-07-26 22:56:26 +08:00
|
|
|
id: current.id,
|
|
|
|
text: current.message,
|
|
|
|
time: current.timestamp,
|
2020-08-20 00:22:43 +08:00
|
|
|
color: current.color,
|
2018-07-26 22:56:26 +08:00
|
|
|
}];
|
2021-01-20 01:06:32 +08:00
|
|
|
if (!lastMessage) {
|
2018-07-26 22:56:26 +08:00
|
|
|
return previous.concat(currentMessage);
|
|
|
|
}
|
|
|
|
// Check if the last message is from the same user and time discrepancy
|
2020-07-28 20:55:21 +08:00
|
|
|
// between the two messages exceeds window and then group current
|
|
|
|
// message with the last one
|
2018-07-26 22:56:26 +08:00
|
|
|
const timeOfLastMessage = lastMessage.content[lastMessage.content.length - 1].time;
|
2021-05-20 22:53:52 +08:00
|
|
|
const isOrWasPoll = currentMessage.id.includes(CHAT_POLL_RESULTS_MESSAGE)
|
|
|
|
|| lastMessage.id.includes(CHAT_POLL_RESULTS_MESSAGE);
|
2020-07-28 20:55:21 +08:00
|
|
|
const groupingWindow = isOrWasPoll ? 0 : GROUPING_MESSAGES_WINDOW;
|
|
|
|
|
2020-08-20 00:22:43 +08:00
|
|
|
if (lastMessage.sender.id === currentMessage.sender.id
|
2020-07-28 20:55:21 +08:00
|
|
|
&& (currentMessage.timestamp - timeOfLastMessage) <= groupingWindow) {
|
2018-07-26 22:56:26 +08:00
|
|
|
lastMessage.content.push(currentMessage.content.pop());
|
|
|
|
return previous;
|
|
|
|
}
|
|
|
|
|
|
|
|
return previous.concat(currentMessage);
|
|
|
|
};
|
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const reduceAndMapGroupMessages = (messages) => (messages
|
2019-01-14 21:23:35 +08:00
|
|
|
.reduce(reduceGroupMessages, []).map(mapGroupMessage));
|
2018-07-27 21:44:21 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const reduceAndDontMapGroupMessages = (messages) => (messages
|
2020-01-22 05:08:48 +08:00
|
|
|
.reduce(reduceGroupMessages, []));
|
|
|
|
|
2016-06-14 01:00:38 +08:00
|
|
|
const isChatLocked = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2019-08-22 20:05:06 +08:00
|
|
|
const meeting = Meetings.findOne({ meetingId: Auth.meetingID },
|
2024-03-26 19:57:28 +08:00
|
|
|
{ fields: { 'lockSettings.disablePublicChat': 1, 'lockSettings.disablePrivateChat': 1 } });
|
2019-08-22 20:05:06 +08:00
|
|
|
const user = Users.findOne({ meetingId: Auth.meetingID, userId: Auth.userID },
|
|
|
|
{ fields: { locked: 1, role: 1 } });
|
2019-09-07 04:28:02 +08:00
|
|
|
const receiver = Users.findOne({ meetingId: Auth.meetingID, userId: receiverID },
|
|
|
|
{ fields: { role: 1 } });
|
|
|
|
const isReceiverModerator = receiver && receiver.role === ROLE_MODERATOR;
|
2017-07-13 04:02:55 +08:00
|
|
|
|
2020-05-01 05:50:34 +08:00
|
|
|
// disable private chat in breakouts
|
|
|
|
if (meetingIsBreakout()) {
|
|
|
|
return !isPublic;
|
|
|
|
}
|
|
|
|
|
2024-03-26 19:57:28 +08:00
|
|
|
if (meeting.lockSettings !== undefined) {
|
2019-08-09 02:53:08 +08:00
|
|
|
if (user.locked && user.role !== ROLE_MODERATOR) {
|
2019-02-26 07:31:33 +08:00
|
|
|
if (isPublic) {
|
2024-03-26 19:57:28 +08:00
|
|
|
return meeting.lockSettings.disablePublicChat;
|
2019-02-26 07:31:33 +08:00
|
|
|
}
|
2019-09-07 04:28:02 +08:00
|
|
|
return !isReceiverModerator
|
2024-03-26 19:57:28 +08:00
|
|
|
&& meeting.lockSettings.disablePrivateChat;
|
2019-02-26 07:31:33 +08:00
|
|
|
}
|
2016-06-14 01:00:38 +08:00
|
|
|
}
|
|
|
|
|
2017-09-07 03:57:24 +08:00
|
|
|
return false;
|
2016-06-14 01:00:38 +08:00
|
|
|
};
|
|
|
|
|
2023-07-11 22:32:15 +08:00
|
|
|
const isChatClosed = (chatId) => {
|
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY) || [];
|
2024-01-10 21:53:29 +08:00
|
|
|
return !!currentClosedChats.find((closedChat) => closedChat.chatId === chatId);
|
2023-07-11 22:32:15 +08:00
|
|
|
};
|
|
|
|
|
2016-07-12 03:42:54 +08:00
|
|
|
const lastReadMessageTime = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2018-07-27 21:44:21 +08:00
|
|
|
const chatType = isPublic ? PUBLIC_GROUP_CHAT_ID : receiverID;
|
2016-07-12 03:42:54 +08:00
|
|
|
|
2017-07-12 21:18:26 +08:00
|
|
|
return UnreadMessages.get(chatType);
|
2016-07-12 03:42:54 +08:00
|
|
|
};
|
|
|
|
|
2016-07-01 01:10:36 +08:00
|
|
|
const getScrollPosition = (receiverID) => {
|
2019-08-22 20:05:06 +08:00
|
|
|
const scroll = ScrollCollection.findOne({ receiver: receiverID },
|
|
|
|
{ fields: { position: 1 } }) || { position: null };
|
2016-07-01 01:10:36 +08:00
|
|
|
return scroll.position;
|
|
|
|
};
|
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const updateScrollPosition = (position, idChatOpen) => ScrollCollection.upsert(
|
|
|
|
{ receiver: idChatOpen },
|
2019-01-14 21:23:35 +08:00
|
|
|
{ $set: { position } },
|
|
|
|
);
|
2016-07-05 02:53:47 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const updateUnreadMessage = (timestamp, idChatOpen) => {
|
|
|
|
const chatID = idChatOpen;
|
2018-10-16 04:03:17 +08:00
|
|
|
const isPublic = chatID === PUBLIC_CHAT_ID;
|
|
|
|
const chatType = isPublic ? PUBLIC_GROUP_CHAT_ID : chatID;
|
2017-07-12 21:18:26 +08:00
|
|
|
return UnreadMessages.update(chatType, timestamp);
|
2016-07-01 01:10:36 +08:00
|
|
|
};
|
|
|
|
|
2021-02-12 22:07:53 +08:00
|
|
|
const closePrivateChat = (chatId) => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY) || [];
|
2017-03-03 04:33:49 +08:00
|
|
|
|
2023-07-11 22:32:15 +08:00
|
|
|
if (!isChatClosed(chatId)) {
|
|
|
|
currentClosedChats.push({ chatId, timestamp: Date.now() });
|
2017-03-03 04:33:49 +08:00
|
|
|
|
2017-03-17 23:27:37 +08:00
|
|
|
Storage.setItem(CLOSED_CHAT_LIST_KEY, currentClosedChats);
|
2017-03-07 01:25:35 +08:00
|
|
|
}
|
2017-03-03 04:33:49 +08:00
|
|
|
};
|
|
|
|
|
2017-12-19 01:38:46 +08:00
|
|
|
// if this private chat has been added to the list of closed ones, remove it
|
2021-05-18 04:25:07 +08:00
|
|
|
const removeFromClosedChatsSession = (idChatOpen) => {
|
|
|
|
const chatID = idChatOpen;
|
2017-12-19 01:38:46 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY);
|
2023-07-11 22:32:15 +08:00
|
|
|
|
|
|
|
if (isChatClosed(chatID)) {
|
2024-01-10 21:53:29 +08:00
|
|
|
const closedChats = currentClosedChats.filter((closedChat) => closedChat.chatId !== chatID);
|
|
|
|
Storage.setItem(CLOSED_CHAT_LIST_KEY, closedChats);
|
2017-12-19 01:38:46 +08:00
|
|
|
}
|
2017-12-19 01:41:26 +08:00
|
|
|
};
|
2017-12-19 01:38:46 +08:00
|
|
|
|
2017-07-21 22:16:45 +08:00
|
|
|
const htmlDecode = (input) => {
|
2022-05-24 01:09:50 +08:00
|
|
|
const replacedBRs = input.replaceAll('<br/>', '\n');
|
|
|
|
return unescapeHtml(stripTags(replacedBRs));
|
2017-07-21 22:16:45 +08:00
|
|
|
};
|
|
|
|
|
2017-08-08 22:52:26 +08:00
|
|
|
// Export the chat as [Hour:Min] user: message
|
2022-05-20 02:53:48 +08:00
|
|
|
const exportChat = (timeWindowList, intl) => {
|
2021-04-12 20:13:42 +08:00
|
|
|
const messageList = timeWindowList.reduce((acc, timeWindow) => {
|
2021-05-18 04:25:07 +08:00
|
|
|
const msgs = timeWindow.content.map((message) => {
|
2021-03-25 04:20:33 +08:00
|
|
|
const date = new Date(message.time);
|
|
|
|
const hour = date.getHours().toString().padStart(2, 0);
|
|
|
|
const min = date.getMinutes().toString().padStart(2, 0);
|
|
|
|
const hourMin = `[${hour}:${min}]`;
|
2021-07-22 00:57:49 +08:00
|
|
|
|
2024-01-10 21:53:29 +08:00
|
|
|
// Skip the reduce aggregation for the sync messages because they aren't localized
|
|
|
|
// (causing an error in line 268)
|
2021-07-22 00:57:49 +08:00
|
|
|
// Also they're temporary (preliminary) messages, so it doesn't make sense export them
|
2021-06-18 02:34:07 +08:00
|
|
|
if (['SYSTEM_MESSAGE-sync-msg', 'synced'].includes(message.id)) return acc;
|
2021-07-22 00:57:49 +08:00
|
|
|
|
2021-05-29 08:28:47 +08:00
|
|
|
let userName = message.id.startsWith(SYSTEM_CHAT_TYPE)
|
2021-03-25 04:20:33 +08:00
|
|
|
? ''
|
2022-05-20 02:53:48 +08:00
|
|
|
: `${timeWindow.senderName}: `;
|
2021-05-29 08:28:47 +08:00
|
|
|
let messageText = '';
|
|
|
|
if (message.text === PUBLIC_CHAT_CLEAR) {
|
2022-06-01 20:49:27 +08:00
|
|
|
messageText = intl.formatMessage(intlMessages.publicChatClear);
|
2021-05-29 08:28:47 +08:00
|
|
|
} else if (message.id.includes(CHAT_POLL_RESULTS_MESSAGE)) {
|
|
|
|
userName = `${intl.formatMessage(intlMessages.pollResult)}:\n`;
|
|
|
|
const { pollResultData } = timeWindow.extra;
|
2021-05-29 08:56:25 +08:00
|
|
|
const pollText = htmlDecode(PollService.getPollResultString(pollResultData, intl).split('<br/>').join('\n'));
|
2021-05-29 08:28:47 +08:00
|
|
|
// remove last \n to avoid empty line
|
|
|
|
messageText = pollText.slice(0, -1);
|
|
|
|
} else {
|
|
|
|
messageText = message.text;
|
|
|
|
}
|
2021-04-12 20:13:42 +08:00
|
|
|
return `${hourMin} ${userName}${htmlDecode(messageText)}`;
|
2021-03-25 04:20:33 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
return [...acc, ...msgs];
|
2021-05-18 04:25:07 +08:00
|
|
|
}, []);
|
2021-03-25 04:20:33 +08:00
|
|
|
|
|
|
|
return messageList.join('\n');
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2021-02-13 03:13:21 +08:00
|
|
|
|
2021-04-13 22:38:44 +08:00
|
|
|
const getAllMessages = (chatID, messages) => {
|
2021-05-18 04:25:07 +08:00
|
|
|
if (!messages[chatID]) {
|
2021-04-13 22:38:44 +08:00
|
|
|
return [];
|
2018-05-30 00:43:11 +08:00
|
|
|
}
|
2021-04-13 22:38:44 +08:00
|
|
|
|
|
|
|
return (chatID === PUBLIC_GROUP_CHAT_ID)
|
2021-05-18 04:25:07 +08:00
|
|
|
? Object.values(messages[chatID].posJoinMessages)
|
|
|
|
: Object.values(messages[chatID].messageGroups);
|
2018-05-30 00:43:11 +08:00
|
|
|
};
|
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
const maxTimestampReducer = (max, el) => ((el.timestamp > max) ? el.timestamp : max);
|
|
|
|
|
|
|
|
const maxNumberReducer = (max, el) => ((el > max) ? el : max);
|
|
|
|
|
2021-04-13 22:38:44 +08:00
|
|
|
const getLastMessageTimestampFromChatList = (activeChats, messages) => activeChats
|
|
|
|
.map((chat) => ((chat.userId === 'public') ? 'MAIN-PUBLIC-GROUP-CHAT' : chat.chatId))
|
|
|
|
.map((chatId) => getAllMessages(chatId, messages).reduce(maxTimestampReducer, 0))
|
2019-01-14 21:23:35 +08:00
|
|
|
.reduce(maxNumberReducer, 0);
|
2018-05-30 00:43:11 +08:00
|
|
|
|
2021-03-29 10:12:58 +08:00
|
|
|
const removePackagedClassAttribute = (classnames, attribute) => {
|
2021-05-18 04:25:07 +08:00
|
|
|
classnames.forEach((c) => {
|
2021-03-29 10:12:58 +08:00
|
|
|
const elements = document.getElementsByClassName(c);
|
2021-05-18 04:25:07 +08:00
|
|
|
if (elements) {
|
|
|
|
// eslint-disable-next-line
|
|
|
|
for (const [, v] of Object.entries(elements)) {
|
|
|
|
v.removeAttribute(attribute);
|
2021-03-29 10:12:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2021-03-29 10:12:58 +08:00
|
|
|
|
2016-06-02 00:33:19 +08:00
|
|
|
export default {
|
2021-01-20 01:06:32 +08:00
|
|
|
setUserSentMessage,
|
2020-01-22 05:08:48 +08:00
|
|
|
mapGroupMessage,
|
2018-07-26 22:56:26 +08:00
|
|
|
reduceAndMapGroupMessages,
|
2020-01-22 05:08:48 +08:00
|
|
|
reduceAndDontMapGroupMessages,
|
2016-06-07 22:19:19 +08:00
|
|
|
getUser,
|
2019-08-22 20:05:06 +08:00
|
|
|
getWelcomeProp,
|
2016-07-01 01:10:36 +08:00
|
|
|
getScrollPosition,
|
2016-07-12 03:42:54 +08:00
|
|
|
lastReadMessageTime,
|
2016-06-14 01:00:38 +08:00
|
|
|
isChatLocked,
|
2023-07-11 22:32:15 +08:00
|
|
|
isChatClosed,
|
2016-07-05 02:53:47 +08:00
|
|
|
updateScrollPosition,
|
|
|
|
updateUnreadMessage,
|
2017-03-24 03:02:40 +08:00
|
|
|
closePrivateChat,
|
2017-12-19 01:38:46 +08:00
|
|
|
removeFromClosedChatsSession,
|
2017-07-20 20:45:27 +08:00
|
|
|
exportChat,
|
2019-01-14 21:23:35 +08:00
|
|
|
maxTimestampReducer,
|
|
|
|
getLastMessageTimestampFromChatList,
|
2019-02-05 20:24:45 +08:00
|
|
|
UnsentMessagesCollection,
|
2021-03-29 10:12:58 +08:00
|
|
|
removePackagedClassAttribute,
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|