2017-07-12 20:42:16 +08:00
|
|
|
import Chats from '/imports/api/2.0/chat';
|
2017-06-28 06:14:53 +08:00
|
|
|
import Users from '/imports/api/2.0/users';
|
2016-06-14 01:00:38 +08:00
|
|
|
|
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';
|
2016-06-02 21:00:57 +08:00
|
|
|
|
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;
|
|
|
|
const PUBLIC_CHAT_USERID = CHAT_CONFIG.public_userid;
|
|
|
|
const PUBLIC_CHAT_USERNAME = CHAT_CONFIG.public_username;
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2016-07-01 01:10:36 +08:00
|
|
|
const ScrollCollection = new Mongo.Collection(null);
|
|
|
|
|
2017-03-17 23:27:37 +08:00
|
|
|
// session for closed chat list
|
|
|
|
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
|
|
|
|
2017-07-12 21:18:26 +08:00
|
|
|
const getUser = (userID) => {
|
|
|
|
const user = Users.findOne({ userId: userID });
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mapUser(user.user);
|
|
|
|
};
|
|
|
|
|
2016-07-12 01:07:35 +08:00
|
|
|
const mapMessage = (messagePayload) => {
|
2016-07-11 20:34:58 +08:00
|
|
|
const { message } = messagePayload;
|
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
const mappedMessage = {
|
2016-07-11 20:34:58 +08:00
|
|
|
id: messagePayload._id,
|
2016-07-12 01:07:35 +08:00
|
|
|
content: messagePayload.content,
|
2017-07-12 21:18:26 +08:00
|
|
|
time: message.fromTime, // + message.from_tz_offset,
|
2016-06-02 21:00:57 +08:00
|
|
|
sender: null,
|
|
|
|
};
|
2016-11-01 06:17:06 +08:00
|
|
|
|
2016-06-03 02:40:27 +08:00
|
|
|
if (message.chat_type !== SYSTEM_CHAT_TYPE) {
|
2017-07-13 00:47:58 +08:00
|
|
|
mappedMessage.sender = getUser(message.fromUserId, message.fromUsername);
|
2016-06-02 00:33:19 +08:00
|
|
|
}
|
2016-06-02 21:00:57 +08:00
|
|
|
|
|
|
|
return mappedMessage;
|
|
|
|
};
|
|
|
|
|
2017-07-12 21:18:26 +08:00
|
|
|
const reduceMessages = (previous, current) => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const lastMessage = previous[previous.length - 1];
|
|
|
|
const currentPayload = current.message;
|
2016-07-12 01:07:35 +08:00
|
|
|
|
2017-07-12 21:18:26 +08:00
|
|
|
const reducedMessages = current;
|
|
|
|
|
|
|
|
reducedMessages.content = [];
|
|
|
|
|
|
|
|
reducedMessages.content.push({
|
2016-07-12 04:10:55 +08:00
|
|
|
id: current._id,
|
2016-07-12 01:07:35 +08:00
|
|
|
text: currentPayload.message,
|
2017-07-12 20:42:16 +08:00
|
|
|
time: currentPayload.fromTime,
|
2016-07-12 01:07:35 +08:00
|
|
|
});
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2017-07-12 21:18:26 +08:00
|
|
|
if (!lastMessage || !reducedMessages.message.chatType === SYSTEM_CHAT_TYPE) {
|
|
|
|
return previous.concat(reducedMessages);
|
2016-06-02 21:00:57 +08:00
|
|
|
}
|
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
const lastPayload = lastMessage.message;
|
2016-07-12 01:07:35 +08:00
|
|
|
|
2016-06-02 21:00:57 +08:00
|
|
|
// 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
|
|
|
|
|
2017-07-13 00:47:58 +08:00
|
|
|
if (lastPayload.fromUserId === currentPayload.fromUserId
|
2017-07-12 20:42:16 +08:00
|
|
|
&& (currentPayload.fromTime - lastPayload.fromTime) <= GROUPING_MESSAGES_WINDOW) {
|
2017-07-12 21:18:26 +08:00
|
|
|
lastMessage.content.push(reducedMessages.content.pop());
|
2016-06-02 21:00:57 +08:00
|
|
|
return previous;
|
|
|
|
}
|
2017-07-12 21:18:26 +08:00
|
|
|
return previous.concat(reducedMessages);
|
2016-06-03 02:40:27 +08:00
|
|
|
};
|
|
|
|
|
2016-06-02 00:33:19 +08:00
|
|
|
const getPublicMessages = () => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const publicMessages = Chats.find({
|
2017-07-13 04:22:26 +08:00
|
|
|
'message.toUsername': { $in: [PUBLIC_CHAT_USERNAME, SYSTEM_CHAT_TYPE] },
|
2016-06-02 21:00:57 +08:00
|
|
|
}, {
|
2017-07-12 20:42:16 +08:00
|
|
|
sort: ['message.fromTime'],
|
2017-07-12 21:54:54 +08:00
|
|
|
}).fetch();
|
2016-06-02 21:00:57 +08:00
|
|
|
|
2016-07-12 03:42:54 +08:00
|
|
|
return publicMessages
|
|
|
|
.reduce(reduceMessages, [])
|
|
|
|
.map(mapMessage);
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const getPrivateMessages = (userID) => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const messages = Chats.find({
|
2017-07-13 04:22:26 +08:00
|
|
|
'message.toUsername': { $ne: PUBLIC_CHAT_USERNAME },
|
2016-06-02 21:00:57 +08:00
|
|
|
$or: [
|
2017-07-12 21:54:54 +08:00
|
|
|
{ 'message.toUserId': userID },
|
|
|
|
{ 'message.fromUserId': userID },
|
2016-06-02 21:00:57 +08:00
|
|
|
],
|
|
|
|
}, {
|
2017-07-12 20:42:16 +08:00
|
|
|
sort: ['message.fromTime'],
|
2017-06-03 03:25:02 +08:00
|
|
|
}).fetch();
|
2016-06-02 00:33:19 +08:00
|
|
|
|
2016-07-12 01:07:35 +08:00
|
|
|
return messages.reduce(reduceMessages, []).map(mapMessage);
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|
|
|
|
|
2016-06-14 01:00:38 +08:00
|
|
|
const isChatLocked = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2016-07-11 21:45:24 +08:00
|
|
|
const currentUser = getUser(Auth.userID);
|
2016-06-14 01:00:38 +08:00
|
|
|
|
2017-07-13 02:51:29 +08:00
|
|
|
const lockSettings = false;
|
2017-07-13 04:02:55 +08:00
|
|
|
|
|
|
|
// FIX ME
|
|
|
|
/* meeting.roomLockSettings || {
|
2016-06-14 01:00:38 +08:00
|
|
|
disablePublicChat: false,
|
|
|
|
disablePrivateChat: false,
|
2017-08-01 21:10:12 +08:00
|
|
|
}; */
|
2016-06-14 01:00:38 +08:00
|
|
|
|
|
|
|
if (!currentUser.isLocked || currentUser.isPresenter) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isPublic ? lockSettings.disablePublicChat : lockSettings.disablePrivateChat;
|
|
|
|
};
|
|
|
|
|
2016-07-05 02:53:47 +08:00
|
|
|
const hasUnreadMessages = (receiverID) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2017-07-12 21:18:26 +08:00
|
|
|
const chatType = isPublic ? PUBLIC_CHAT_USERID : receiverID;
|
2016-07-05 02:53:47 +08:00
|
|
|
|
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;
|
2017-07-12 21:18:26 +08:00
|
|
|
const chatType = isPublic ? PUBLIC_CHAT_USERID : 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-06-03 02:40:27 +08:00
|
|
|
const sendMessage = (receiverID, message) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
const sender = getUser(Auth.userID);
|
2016-06-03 02:40:27 +08:00
|
|
|
const receiver = !isPublic ? getUser(receiverID) : {
|
2016-07-05 02:53:47 +08:00
|
|
|
id: PUBLIC_CHAT_USERID,
|
|
|
|
name: PUBLIC_CHAT_USERNAME,
|
2016-06-03 02:40:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* FIX: Why we need all this payload to send a message?
|
|
|
|
* The server only really needs the message, from_userid, to_userid and from_lang
|
|
|
|
*/
|
2017-06-03 03:25:02 +08:00
|
|
|
const messagePayload = {
|
|
|
|
message,
|
2017-07-12 20:42:16 +08:00
|
|
|
fromUserId: sender.id,
|
|
|
|
fromUsername: sender.name,
|
|
|
|
fromTimezoneOffset: (new Date()).getTimezoneOffset(),
|
|
|
|
toUsername: receiver.name,
|
|
|
|
toUserId: receiver.id,
|
|
|
|
fromTime: Date.now(),
|
|
|
|
fromColor: 0,
|
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
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:29 +08:00
|
|
|
return makeCall('sendChat', messagePayload);
|
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;
|
|
|
|
};
|
|
|
|
|
2016-07-05 02:53:47 +08:00
|
|
|
const updateScrollPosition =
|
|
|
|
(receiverID, position) => ScrollCollection.upsert(
|
|
|
|
{ receiver: receiverID },
|
2017-06-03 03:25:02 +08:00
|
|
|
{ $set: { position } },
|
2016-07-05 02:53:47 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
const updateUnreadMessage = (receiverID, timestamp) => {
|
|
|
|
const isPublic = receiverID === PUBLIC_CHAT_ID;
|
2017-07-12 21:18:26 +08:00
|
|
|
const chatType = isPublic ? PUBLIC_CHAT_USERID : receiverID;
|
|
|
|
return UnreadMessages.update(chatType, timestamp);
|
2016-07-01 01:10:36 +08:00
|
|
|
};
|
|
|
|
|
2017-03-24 03:02:40 +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
|
|
|
|
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-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;
|
|
|
|
return e.childNodes[0].nodeValue;
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatTime = time => (time <= 9 ? `0${time}` : time);
|
|
|
|
|
2017-07-20 20:45:27 +08:00
|
|
|
// Export the chat as [Hour:Min] user : message
|
|
|
|
const exportChat = messageList => (
|
|
|
|
messageList.map(({ message }) => {
|
|
|
|
const date = new Date(message.fromTime);
|
2017-07-21 22:16:45 +08:00
|
|
|
const hour = formatTime(date.getHours());
|
|
|
|
const min = formatTime(date.getMinutes());
|
|
|
|
const hourMin = `${hour}:${min}`;
|
|
|
|
if (message.fromUserId === SYSTEM_CHAT_TYPE) {
|
2017-07-20 20:45:27 +08:00
|
|
|
return `[${hourMin}] ${message.message}`;
|
|
|
|
}
|
2017-07-21 22:16:45 +08:00
|
|
|
return `[${hourMin}] ${message.fromUsername}: ${htmlDecode(message.message)}`;
|
2017-07-20 20:45:27 +08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
2016-06-02 00:33:19 +08:00
|
|
|
export default {
|
|
|
|
getPublicMessages,
|
|
|
|
getPrivateMessages,
|
2016-06-07 22:19:19 +08:00
|
|
|
getUser,
|
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,
|
2016-06-02 00:33:19 +08:00
|
|
|
sendMessage,
|
2017-03-24 03:02:40 +08:00
|
|
|
closePrivateChat,
|
2017-07-20 20:45:27 +08:00
|
|
|
exportChat,
|
2016-06-02 00:33:19 +08:00
|
|
|
};
|