2017-10-12 10:00:28 +08:00
|
|
|
import Users from '/imports/api/users';
|
|
|
|
import Meetings from '/imports/api/meetings';
|
2018-07-27 21:44:21 +08:00
|
|
|
import GroupChatMsg from '/imports/api/group-chat-msg';
|
2018-07-26 22:56:26 +08:00
|
|
|
import GroupChat from '/imports/api/group-chat';
|
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';
|
2017-08-01 21:10:12 +08:00
|
|
|
import mapUser from '/imports/ui/services/user/mapUser';
|
2017-04-26 21:47:44 +08:00
|
|
|
import { makeCall } from '/imports/ui/services/api';
|
2017-03-22 05:52:10 +08:00
|
|
|
import _ from 'lodash';
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2016-08-17 23:48:03 +08:00
|
|
|
const CHAT_CONFIG = Meteor.settings.public.chat;
|
|
|
|
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;
|
|
|
|
const PRIVATE_CHAT_TYPE = CHAT_CONFIG.type_private;
|
2018-08-03 22:03:16 +08:00
|
|
|
const PUBLIC_CHAT_USER_ID = CHAT_CONFIG.system_userid;
|
2019-01-09 19:36:14 +08:00
|
|
|
const PUBLIC_CHAT_CLEAR = CHAT_CONFIG.system_messages_keys.chat_clear;
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2019-02-26 07:31:33 +08:00
|
|
|
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
|
|
|
|
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);
|
|
|
|
|
2017-03-17 23:27:37 +08:00
|
|
|
// session for closed chat list
|
|
|
|
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
|
|
|
|
2017-08-05 01:58:55 +08:00
|
|
|
const getUser = (userId) => {
|
|
|
|
const user = Users.findOne({ userId });
|
2017-07-12 21:18:26 +08:00
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-07-26 22:09:07 +08:00
|
|
|
return mapUser(user);
|
2017-07-12 21:18:26 +08:00
|
|
|
};
|
|
|
|
|
2018-10-08 22:22:45 +08:00
|
|
|
const getMeeting = () => Meetings.findOne({});
|
|
|
|
|
2018-07-26 22:56:26 +08:00
|
|
|
const mapGroupMessage = (message) => {
|
|
|
|
const mappedMessage = {
|
|
|
|
id: message._id,
|
|
|
|
content: message.content,
|
2018-07-27 21:44:21 +08:00
|
|
|
time: message.timestamp,
|
2018-07-26 22:56:26 +08:00
|
|
|
sender: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (message.sender !== SYSTEM_CHAT_TYPE) {
|
2018-12-20 02:51:03 +08:00
|
|
|
const sender = getUser(message.sender);
|
|
|
|
|
|
|
|
const {
|
|
|
|
color,
|
|
|
|
isModerator,
|
|
|
|
name,
|
|
|
|
isOnline,
|
|
|
|
} = sender;
|
|
|
|
|
|
|
|
const mappedSender = {
|
|
|
|
color,
|
|
|
|
isModerator,
|
|
|
|
name,
|
|
|
|
isOnline,
|
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
}];
|
2018-07-27 21:44:21 +08:00
|
|
|
if (!lastMessage || !currentMessage.chatId === PUBLIC_GROUP_CHAT_ID) {
|
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
|
|
|
|
// between the two messages exceeds window and then group current message
|
|
|
|
// with the last one
|
|
|
|
const timeOfLastMessage = lastMessage.content[lastMessage.content.length - 1].time;
|
|
|
|
if (lastMessage.sender === currentMessage.sender
|
|
|
|
&& (currentMessage.timestamp - timeOfLastMessage) <= GROUPING_MESSAGES_WINDOW) {
|
|
|
|
lastMessage.content.push(currentMessage.content.pop());
|
|
|
|
return previous;
|
|
|
|
}
|
|
|
|
|
|
|
|
return previous.concat(currentMessage);
|
|
|
|
};
|
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
const reduceAndMapGroupMessages = messages => (messages
|
|
|
|
.reduce(reduceGroupMessages, []).map(mapGroupMessage));
|
2018-07-27 21:44:21 +08:00
|
|
|
|
2018-07-26 22:56:26 +08:00
|
|
|
const getPublicGroupMessages = () => {
|
|
|
|
const publicGroupMessages = GroupChatMsg.find({
|
2018-07-27 21:44:21 +08:00
|
|
|
chatId: PUBLIC_GROUP_CHAT_ID,
|
2019-01-14 21:23:35 +08:00
|
|
|
}, { sort: ['timestamp'] }).fetch();
|
2018-07-26 22:56:26 +08:00
|
|
|
return publicGroupMessages;
|
|
|
|
};
|
|
|
|
|
2018-10-16 04:03:17 +08:00
|
|
|
const getPrivateGroupMessages = () => {
|
|
|
|
const chatID = Session.get('idChatOpen');
|
2018-07-26 22:56:26 +08:00
|
|
|
const sender = getUser(Auth.userID);
|
|
|
|
|
2018-07-27 21:44:21 +08:00
|
|
|
const privateChat = GroupChat.findOne({
|
|
|
|
users: { $all: [chatID, sender.id] },
|
2018-08-01 01:13:36 +08:00
|
|
|
access: PRIVATE_CHAT_TYPE,
|
2018-07-27 21:44:21 +08:00
|
|
|
});
|
2018-07-26 22:56:26 +08:00
|
|
|
|
|
|
|
let messages = [];
|
|
|
|
|
|
|
|
if (privateChat) {
|
|
|
|
const {
|
2018-07-27 21:44:21 +08:00
|
|
|
chatId,
|
2018-07-26 22:56:26 +08:00
|
|
|
} = privateChat;
|
|
|
|
|
|
|
|
messages = GroupChatMsg.find({
|
2018-07-27 21:44:21 +08:00
|
|
|
chatId,
|
2019-01-14 21:23:35 +08:00
|
|
|
}, { sort: ['timestamp'] }).fetch();
|
2018-07-26 22:56:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return reduceAndMapGroupMessages(messages, []);
|
|
|
|
};
|
|
|
|
|
2016-06-14 01:00:38 +08:00
|
|
|
const isChatLocked = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
|
|
|
|
2017-09-01 05:29:18 +08:00
|
|
|
const meeting = Meetings.findOne({});
|
2018-08-30 00:00:53 +08:00
|
|
|
const user = Users.findOne({ userId: Auth.userID });
|
2017-07-13 04:02:55 +08:00
|
|
|
|
2017-09-13 01:01:07 +08:00
|
|
|
if (meeting.lockSettingsProp !== undefined) {
|
2019-02-26 07:31:33 +08:00
|
|
|
if (mapUser(user).isLocked) {
|
|
|
|
if (isPublic) {
|
|
|
|
return meeting.lockSettingsProp.disablePubChat;
|
|
|
|
}
|
|
|
|
const receivingUser = Users.findOne({ userId: receiverID });
|
|
|
|
const receiverIsMod = receivingUser && receivingUser.role === ROLE_MODERATOR;
|
|
|
|
return !receiverIsMod && meeting.lockSettingsProp.disablePrivChat;
|
|
|
|
}
|
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
|
|
|
};
|
|
|
|
|
2016-07-05 02:53:47 +08:00
|
|
|
const hasUnreadMessages = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2018-07-27 21:44:21 +08:00
|
|
|
const chatType = isPublic ? PUBLIC_GROUP_CHAT_ID : receiverID;
|
2017-07-12 21:18:26 +08:00
|
|
|
return UnreadMessages.count(chatType) > 0;
|
2016-07-05 02:53:47 +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
|
|
|
};
|
|
|
|
|
2018-10-16 04:03:17 +08:00
|
|
|
const sendGroupMessage = (message) => {
|
2018-12-19 22:39:32 +08:00
|
|
|
const chatID = Session.get('idChatOpen') || PUBLIC_CHAT_ID;
|
2018-07-27 21:44:21 +08:00
|
|
|
const isPublicChat = chatID === PUBLIC_CHAT_ID;
|
2018-07-26 22:56:26 +08:00
|
|
|
|
2018-07-27 21:44:21 +08:00
|
|
|
let chatId = PUBLIC_GROUP_CHAT_ID;
|
2018-07-26 22:56:26 +08:00
|
|
|
|
|
|
|
const sender = getUser(Auth.userID);
|
|
|
|
|
2018-07-27 21:44:21 +08:00
|
|
|
const receiver = !isPublicChat ? getUser(chatID) : { id: chatID };
|
|
|
|
|
2018-07-26 22:56:26 +08:00
|
|
|
if (!isPublicChat) {
|
2018-07-27 21:44:21 +08:00
|
|
|
const privateChat = GroupChat.findOne({ users: { $all: [chatID, sender.id] } });
|
2018-07-26 22:56:26 +08:00
|
|
|
|
|
|
|
if (privateChat) {
|
2018-07-27 21:44:21 +08:00
|
|
|
const { chatId: privateChatId } = privateChat;
|
|
|
|
|
|
|
|
chatId = privateChatId;
|
2018-07-26 22:56:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const payload = {
|
2018-07-27 21:44:21 +08:00
|
|
|
color: '0',
|
2018-07-26 22:56:26 +08:00
|
|
|
correlationId: `${sender.id}-${Date.now()}`,
|
|
|
|
sender: {
|
|
|
|
id: sender.id,
|
2018-07-27 21:44:21 +08:00
|
|
|
name: sender.name,
|
2018-07-26 22:56:26 +08:00
|
|
|
},
|
2017-06-03 03:25:02 +08:00
|
|
|
message,
|
2016-06-03 02:40:27 +08:00
|
|
|
};
|
2016-06-02 00:33:19 +08:00
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY);
|
2017-03-02 05:27:05 +08:00
|
|
|
|
2017-03-03 05:42:39 +08:00
|
|
|
// Remove the chat that user send messages from the session.
|
2017-03-22 05:52:10 +08:00
|
|
|
if (_.indexOf(currentClosedChats, receiver.id) > -1) {
|
2017-03-17 23:27:37 +08:00
|
|
|
Storage.setItem(CLOSED_CHAT_LIST_KEY, _.without(currentClosedChats, receiver.id));
|
2017-03-01 06:40:16 +08:00
|
|
|
}
|
|
|
|
|
2018-07-27 21:44:21 +08:00
|
|
|
return makeCall('sendGroupChatMsg', chatId, payload);
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|
|
|
|
|
2016-07-01 01:10:36 +08:00
|
|
|
const getScrollPosition = (receiverID) => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const scroll = ScrollCollection.findOne({ receiver: receiverID }) || { position: null };
|
2016-07-01 01:10:36 +08:00
|
|
|
return scroll.position;
|
|
|
|
};
|
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
const updateScrollPosition = position => ScrollCollection.upsert(
|
|
|
|
{ receiver: Session.get('idChatOpen') },
|
|
|
|
{ $set: { position } },
|
|
|
|
);
|
2016-07-05 02:53:47 +08:00
|
|
|
|
2018-10-16 04:03:17 +08:00
|
|
|
const updateUnreadMessage = (timestamp) => {
|
2018-12-20 03:25:13 +08:00
|
|
|
const chatID = Session.get('idChatOpen') || PUBLIC_CHAT_ID;
|
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
|
|
|
};
|
|
|
|
|
2017-08-03 01:05:20 +08:00
|
|
|
const clearPublicChatHistory = () => (makeCall('clearPublicChatHistory'));
|
|
|
|
|
2018-10-16 04:03:17 +08:00
|
|
|
const closePrivateChat = () => {
|
|
|
|
const chatID = Session.get('idChatOpen');
|
2017-06-03 03:25:02 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY) || [];
|
2017-03-03 04:33:49 +08:00
|
|
|
|
2017-03-22 05:52:10 +08:00
|
|
|
if (_.indexOf(currentClosedChats, chatID) < 0) {
|
2017-03-03 04:33:49 +08:00
|
|
|
currentClosedChats.push(chatID);
|
|
|
|
|
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
|
2018-10-16 04:03:17 +08:00
|
|
|
const removeFromClosedChatsSession = () => {
|
|
|
|
const chatID = Session.get('idChatOpen');
|
2017-12-19 01:38:46 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY);
|
2017-12-19 01:41:26 +08:00
|
|
|
if (_.indexOf(currentClosedChats, chatID) > -1) {
|
2017-12-19 01:38:46 +08:00
|
|
|
Storage.setItem(CLOSED_CHAT_LIST_KEY, _.without(currentClosedChats, chatID));
|
|
|
|
}
|
2017-12-19 01:41:26 +08:00
|
|
|
};
|
2017-12-19 01:38:46 +08:00
|
|
|
|
2017-07-21 22:16:45 +08:00
|
|
|
// We decode to prevent HTML5 escaped characters.
|
|
|
|
const htmlDecode = (input) => {
|
|
|
|
const e = document.createElement('div');
|
|
|
|
e.innerHTML = input;
|
2019-03-06 02:21:33 +08:00
|
|
|
const messages = Array.from(e.childNodes);
|
2019-02-26 05:38:57 +08:00
|
|
|
const message = messages.map(chatMessage => chatMessage.textContent);
|
|
|
|
return message.join('');
|
2017-07-21 22:16:45 +08:00
|
|
|
};
|
|
|
|
|
2017-08-08 22:52:26 +08:00
|
|
|
// Export the chat as [Hour:Min] user: message
|
2019-01-09 19:36:14 +08:00
|
|
|
const exportChat = (messageList) => {
|
|
|
|
const { welcomeProp } = getMeeting();
|
2019-01-14 21:23:35 +08:00
|
|
|
const { loginTime } = getUser(Auth.userID);
|
2019-01-09 19:41:52 +08:00
|
|
|
const { welcomeMsg } = welcomeProp;
|
2019-01-09 19:36:14 +08:00
|
|
|
|
|
|
|
const clearMessage = messageList.filter(message => message.message === PUBLIC_CHAT_CLEAR);
|
|
|
|
|
|
|
|
const hasClearMessage = clearMessage.length;
|
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
if (!hasClearMessage || (hasClearMessage && clearMessage[0].timestamp < loginTime)) {
|
2019-01-09 19:36:14 +08:00
|
|
|
messageList.push({
|
2019-01-14 21:23:35 +08:00
|
|
|
timestamp: loginTime,
|
2019-01-09 19:36:14 +08:00
|
|
|
message: welcomeMsg,
|
|
|
|
type: SYSTEM_CHAT_TYPE,
|
|
|
|
sender: PUBLIC_CHAT_USER_ID,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
messageList.sort((a, b) => a.timestamp - b.timestamp);
|
|
|
|
|
|
|
|
return messageList.map((message) => {
|
2018-08-03 22:03:16 +08:00
|
|
|
const date = new Date(message.timestamp);
|
2017-08-08 22:52:26 +08:00
|
|
|
const hour = date.getHours().toString().padStart(2, 0);
|
|
|
|
const min = date.getMinutes().toString().padStart(2, 0);
|
|
|
|
const hourMin = `[${hour}:${min}]`;
|
|
|
|
if (message.type === SYSTEM_CHAT_TYPE) {
|
|
|
|
return `${hourMin} ${message.message}`;
|
2017-07-20 20:45:27 +08:00
|
|
|
}
|
2018-08-03 22:03:16 +08:00
|
|
|
const userName = message.sender === PUBLIC_CHAT_USER_ID ? '' : `${getUser(message.sender).name} :`;
|
|
|
|
return `${hourMin} ${userName} ${htmlDecode(message.message)}`;
|
2019-01-09 19:36:14 +08:00
|
|
|
}).join('\n');
|
|
|
|
};
|
2017-07-20 20:45:27 +08:00
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
const getUnreadMessagesFromChatId = chatId => UnreadMessages.getUnreadMessages(chatId);
|
|
|
|
|
|
|
|
const getAllMessages = (chatID) => {
|
|
|
|
const filter = {
|
|
|
|
sender: { $ne: Auth.userID },
|
|
|
|
};
|
|
|
|
if (chatID === PUBLIC_GROUP_CHAT_ID) {
|
|
|
|
filter.chatId = { $eq: chatID };
|
|
|
|
} else {
|
|
|
|
const privateChat = GroupChat.findOne({ users: { $all: [chatID, Auth.userID] } });
|
|
|
|
|
|
|
|
filter.chatId = { $ne: PUBLIC_GROUP_CHAT_ID };
|
|
|
|
|
|
|
|
if (privateChat) {
|
|
|
|
filter.chatId = privateChat.chatId;
|
|
|
|
}
|
2018-05-30 00:43:11 +08:00
|
|
|
}
|
2019-01-14 21:23:35 +08:00
|
|
|
const messages = GroupChatMsg.find(filter).fetch();
|
|
|
|
return messages;
|
2018-05-30 00:43:11 +08:00
|
|
|
};
|
|
|
|
|
2019-01-14 21:23:35 +08:00
|
|
|
const getlastMessage = lastMessages => lastMessages.sort((a,
|
|
|
|
b) => a.timestamp - b.timestamp).pop();
|
|
|
|
|
|
|
|
const maxTimestampReducer = (max, el) => ((el.timestamp > max) ? el.timestamp : max);
|
|
|
|
|
|
|
|
const maxNumberReducer = (max, el) => ((el > max) ? el : max);
|
|
|
|
|
|
|
|
const getLastMessageTimestampFromChatList = activeChats => activeChats
|
|
|
|
.map(chat => ((chat.id === 'public') ? 'MAIN-PUBLIC-GROUP-CHAT' : chat.id))
|
|
|
|
.map(chatId => getAllMessages(chatId).reduce(maxTimestampReducer, 0))
|
|
|
|
.reduce(maxNumberReducer, 0);
|
2018-05-30 00:43:11 +08:00
|
|
|
|
2016-06-02 00:33:19 +08:00
|
|
|
export default {
|
2018-07-26 22:56:26 +08:00
|
|
|
reduceAndMapGroupMessages,
|
|
|
|
getPublicGroupMessages,
|
|
|
|
getPrivateGroupMessages,
|
2016-06-07 22:19:19 +08:00
|
|
|
getUser,
|
2018-10-08 22:22:45 +08:00
|
|
|
getMeeting,
|
2016-07-01 01:10:36 +08:00
|
|
|
getScrollPosition,
|
2016-07-05 02:53:47 +08:00
|
|
|
hasUnreadMessages,
|
2016-07-12 03:42:54 +08:00
|
|
|
lastReadMessageTime,
|
2016-06-14 01:00:38 +08:00
|
|
|
isChatLocked,
|
2016-07-05 02:53:47 +08:00
|
|
|
updateScrollPosition,
|
|
|
|
updateUnreadMessage,
|
2018-07-26 22:56:26 +08:00
|
|
|
sendGroupMessage,
|
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,
|
2017-08-03 01:05:20 +08:00
|
|
|
clearPublicChatHistory,
|
2019-01-14 21:23:35 +08:00
|
|
|
getlastMessage,
|
|
|
|
getUnreadMessagesFromChatId,
|
|
|
|
getAllMessages,
|
|
|
|
maxTimestampReducer,
|
|
|
|
maxNumberReducer,
|
|
|
|
getLastMessageTimestampFromChatList,
|
2019-02-05 20:24:45 +08:00
|
|
|
UnsentMessagesCollection,
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|