bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/chat/service.js

350 lines
10 KiB
JavaScript
Raw Normal View History

import Users from '/imports/api/users';
import Meetings from '/imports/api/meetings';
2019-08-03 02:18:33 +08:00
import { GroupChatMsg } from '/imports/api/group-chat-msg';
import GroupChat from '/imports/api/group-chat';
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-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';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
const CHAT_CONFIG = Meteor.settings.public.chat;
const GROUPING_MESSAGES_WINDOW = CHAT_CONFIG.grouping_messages_window;
2016-06-02 00:33:19 +08:00
const SYSTEM_CHAT_TYPE = CHAT_CONFIG.type_system;
2016-06-02 00:33:19 +08:00
const PUBLIC_CHAT_ID = CHAT_CONFIG.public_id;
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
const PRIVATE_CHAT_TYPE = CHAT_CONFIG.type_private;
const PUBLIC_CHAT_USER_ID = CHAT_CONFIG.system_userid;
const PUBLIC_CHAT_CLEAR = CHAT_CONFIG.system_messages_keys.chat_clear;
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
const CONNECTION_STATUS_ONLINE = 'online';
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);
// session for closed chat list
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
const getUser = userId => Users.findOne({ userId });
2017-07-12 21:18:26 +08:00
const getWelcomeProp = () => Meetings.findOne({ meetingId: Auth.meetingID },
{ fields: { welcomeProp: 1 } });
const mapGroupMessage = (message) => {
const mappedMessage = {
id: message._id,
content: message.content,
time: message.timestamp,
sender: null,
};
if (message.sender !== SYSTEM_CHAT_TYPE) {
const sender = Users.findOne({ userId: message.sender },
{
fields: {
color: 1, role: 1, name: 1, connectionStatus: 1,
},
});
const {
color,
role,
name,
connectionStatus,
} = sender;
const mappedSender = {
color,
isModerator: role === ROLE_MODERATOR,
name,
isOnline: connectionStatus === CONNECTION_STATUS_ONLINE,
};
mappedMessage.sender = mappedSender;
}
return mappedMessage;
};
const reduceGroupMessages = (previous, current) => {
const lastMessage = previous[previous.length - 1];
const currentMessage = current;
currentMessage.content = [{
id: current.id,
text: current.message,
time: current.timestamp,
}];
if (!lastMessage || !currentMessage.chatId === PUBLIC_GROUP_CHAT_ID) {
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));
const getPublicGroupMessages = () => {
const publicGroupMessages = GroupChatMsg.find({
meetingId: Auth.meetingID,
chatId: PUBLIC_GROUP_CHAT_ID,
2019-01-14 21:23:35 +08:00
}, { sort: ['timestamp'] }).fetch();
return publicGroupMessages;
};
const getPrivateGroupMessages = () => {
const chatID = Session.get('idChatOpen');
const senderId = Auth.userID;
const privateChat = GroupChat.findOne({
meetingId: Auth.meetingID,
users: { $all: [chatID, senderId] },
access: PRIVATE_CHAT_TYPE,
2019-08-23 03:14:36 +08:00
});
let messages = [];
if (privateChat) {
const {
chatId,
} = privateChat;
messages = GroupChatMsg.find({
meetingId: Auth.meetingID,
chatId,
2019-01-14 21:23:35 +08:00
}, { sort: ['timestamp'] }).fetch();
}
return reduceAndMapGroupMessages(messages, []);
};
2016-06-14 01:00:38 +08:00
const isChatLocked = (receiverID) => {
const isPublic = receiverID === PUBLIC_CHAT_ID;
const meeting = Meetings.findOne({ meetingId: Auth.meetingID },
{ fields: { 'lockSettingsProps.disablePublicChat': 1, 'lockSettingsProps.disablePrivateChat': 1 } });
const user = Users.findOne({ meetingId: Auth.meetingID, userId: Auth.userID },
{ fields: { locked: 1, role: 1 } });
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
// disable private chat in breakouts
if (meetingIsBreakout()) {
return !isPublic;
}
if (meeting.lockSettingsProps !== undefined) {
if (user.locked && user.role !== ROLE_MODERATOR) {
if (isPublic) {
2019-04-10 21:44:34 +08:00
return meeting.lockSettingsProps.disablePublicChat;
}
return !isReceiverModerator
&& meeting.lockSettingsProps.disablePrivateChat;
}
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;
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;
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
};
const sendGroupMessage = (message) => {
const chatID = Session.get('idChatOpen');
const isPublicChat = chatID === PUBLIC_CHAT_ID;
let destinationChatId = PUBLIC_GROUP_CHAT_ID;
const { fullname: senderName, userID: senderUserId } = Auth;
const receiverId = { id: chatID };
if (!isPublicChat) {
const privateChat = GroupChat.findOne({ users: { $all: [chatID, senderUserId] } },
{ fields: { chatId: 1 } });
if (privateChat) {
const { chatId: privateChatId } = privateChat;
destinationChatId = privateChatId;
}
}
const payload = {
color: '0',
correlationId: `${senderUserId}-${Date.now()}`,
sender: {
id: senderUserId,
name: senderName,
},
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-03 05:42:39 +08:00
// Remove the chat that user send messages from the session.
if (_.indexOf(currentClosedChats, receiverId.id) > -1) {
Storage.setItem(CLOSED_CHAT_LIST_KEY, _.without(currentClosedChats, receiverId.id));
2017-03-01 06:40:16 +08:00
}
return makeCall('sendGroupChatMsg', destinationChatId, payload);
2016-06-02 00:33:19 +08:00
};
2016-07-01 01:10:36 +08:00
const getScrollPosition = (receiverID) => {
const scroll = ScrollCollection.findOne({ receiver: receiverID },
{ fields: { position: 1 } }) || { 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
const updateUnreadMessage = (timestamp) => {
const chatID = Session.get('idChatOpen');
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'));
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);
Storage.setItem(CLOSED_CHAT_LIST_KEY, currentClosedChats);
}
2017-03-03 04:33:49 +08:00
};
// if this private chat has been added to the list of closed ones, remove it
const removeFromClosedChatsSession = () => {
const chatID = Session.get('idChatOpen');
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY);
2017-12-19 01:41:26 +08:00
if (_.indexOf(currentClosedChats, chatID) > -1) {
Storage.setItem(CLOSED_CHAT_LIST_KEY, _.without(currentClosedChats, chatID));
}
2017-12-19 01:41:26 +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('');
};
// Export the chat as [Hour:Min] user: message
const exportChat = (messageList) => {
const { welcomeProp } = getWelcomeProp();
const { loginTime } = Users.findOne({ userId: Auth.userID }, { fields: { loginTime: 1 } });
2019-01-09 19:41:52 +08:00
const { welcomeMsg } = welcomeProp;
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)) {
messageList.push({
2019-01-14 21:23:35 +08:00
timestamp: loginTime,
message: welcomeMsg,
type: SYSTEM_CHAT_TYPE,
sender: PUBLIC_CHAT_USER_ID,
});
}
messageList.sort((a, b) => a.timestamp - b.timestamp);
return messageList.map((message) => {
const date = new Date(message.timestamp);
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}`;
}
const userName = message.sender === PUBLIC_CHAT_USER_ID
? ''
: `${getUser(message.sender).name} :`;
return `${hourMin} ${userName} ${htmlDecode(message.message)}`;
}).join('\n');
};
2019-01-14 21:23:35 +08:00
const getAllMessages = (chatID) => {
const filter = {
sender: { $ne: Auth.userID },
};
if (chatID === PUBLIC_GROUP_CHAT_ID) {
filter.chatId = { $eq: chatID };
} else {
2019-08-23 03:14:36 +08:00
const privateChat = GroupChat.findOne({ users: { $all: [chatID, Auth.userID] } });
2019-01-14 21:23:35 +08:00
filter.chatId = { $ne: PUBLIC_GROUP_CHAT_ID };
if (privateChat) {
filter.chatId = privateChat.chatId;
}
}
2019-01-14 21:23:35 +08:00
const messages = GroupChatMsg.find(filter).fetch();
return messages;
};
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);
const getLastMessageTimestampFromChatList = activeChats => activeChats
.map(chat => ((chat.userId === 'public') ? 'MAIN-PUBLIC-GROUP-CHAT' : chat.userId))
2019-01-14 21:23:35 +08:00
.map(chatId => getAllMessages(chatId).reduce(maxTimestampReducer, 0))
.reduce(maxNumberReducer, 0);
2016-06-02 00:33:19 +08:00
export default {
reduceAndMapGroupMessages,
getPublicGroupMessages,
getPrivateGroupMessages,
2016-06-07 22:19:19 +08:00
getUser,
getWelcomeProp,
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,
sendGroupMessage,
closePrivateChat,
removeFromClosedChatsSession,
exportChat,
2017-08-03 01:05:20 +08:00
clearPublicChatHistory,
2019-01-14 21:23:35 +08:00
maxTimestampReducer,
getLastMessageTimestampFromChatList,
2019-02-05 20:24:45 +08:00
UnsentMessagesCollection,
2016-06-02 00:33:19 +08:00
};