Move Chat to Model (#9703)

* Began moving group chat to separate model

* Fixed lint issue

* Updated delete chat with new model

* Updated flag chat to support model

* Updated like chat to use model

* Fixed duplicate code and chat messages

* Added note about concat chat

* Updated clear flags to user new model

* Updated more chat checks when loading get group

* Fixed spell test and back save

* Moved get chat to json method

* Updated flagging with new chat model

* Added missing await

* Fixed chat user styles. Fixed spell group test

* Added new model to quest chat and group plan chat

* Removed extra timestamps. Added limit check for group plans

* Updated tests

* Synced id fields

* Fixed id creation

* Add meta and fixed tests

* Fixed group quest accept test

* Updated puppeteer

* Added migration

* Export vars

* Updated comments
This commit is contained in:
Keith Holliday
2018-04-23 12:17:16 -05:00
committed by Sabe Jones
parent 0ec1a91774
commit 7d7fe6047c
23 changed files with 286 additions and 154 deletions

View File

@@ -1,12 +1,12 @@
import { authWithHeaders } from '../../middlewares/auth';
import { model as Group } from '../../models/group';
import { model as User } from '../../models/user';
import { model as Chat } from '../../models/chat';
import {
BadRequest,
NotFound,
NotAuthorized,
} from '../../libs/errors';
import _ from 'lodash';
import { removeFromArray } from '../../libs/collectionManipulators';
import { getUserInfo, getGroupUrl, sendTxn } from '../../libs/email';
import slack from '../../libs/slack';
@@ -70,10 +70,12 @@ api.getChat = {
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let group = await Group.getGroup({user, groupId: req.params.groupId, fields: 'chat'});
const groupId = req.params.groupId;
let group = await Group.getGroup({user, groupId, fields: 'chat'});
if (!group) throw new NotFound(res.t('groupNotFound'));
res.respond(200, Group.toJSONCleanChat(group, user).chat);
const groupChat = await Group.toJSONCleanChat(group, user);
res.respond(200, groupChat.chat);
},
};
@@ -164,35 +166,35 @@ api.postChat = {
}
}
let lastClientMsg = req.query.previousMsg;
const chatRes = await Group.toJSONCleanChat(group, user);
const lastClientMsg = req.query.previousMsg;
chatUpdated = lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg ? true : false;
if (group.checkChatSpam(user)) {
throw new NotAuthorized(res.t('messageGroupChatSpam'));
}
let newChatMessage = group.sendChat(req.body.message, user);
let toSave = [group.save()];
const newChatMessage = group.sendChat(req.body.message, user);
let toSave = [newChatMessage.save()];
if (group.type === 'party') {
user.party.lastMessageSeen = group.chat[0].id;
user.party.lastMessageSeen = newChatMessage.id;
toSave.push(user.save());
}
let [savedGroup] = await Promise.all(toSave);
await Promise.all(toSave);
// realtime chat is only enabled for private groups (for now only for parties)
if (savedGroup.privacy === 'private' && savedGroup.type === 'party') {
// @TODO: rethink if we want real-time
if (group.privacy === 'private' && group.type === 'party') {
// req.body.pusherSocketId is sent from official clients to identify the sender user's real time socket
// see https://pusher.com/docs/server_api_guide/server_excluding_recipients
pusher.trigger(`presencegroup${savedGroup._id}`, 'newchat', newChatMessage, req.body.pusherSocketId);
pusher.trigger(`presence-group-${group._id}`, 'new-chat', newChatMessage, req.body.pusherSocketId);
}
if (chatUpdated) {
res.respond(200, {chat: Group.toJSONCleanChat(savedGroup, user).chat});
res.respond(200, {chat: chatRes.chat});
} else {
res.respond(200, {message: savedGroup.chat[0]});
res.respond(200, {message: newChatMessage});
}
group.sendGroupChatReceivedWebhooks(newChatMessage);
@@ -233,22 +235,16 @@ api.likeChat = {
let group = await Group.getGroup({user, groupId});
if (!group) throw new NotFound(res.t('groupNotFound'));
let message = _.find(group.chat, {id: req.params.chatId});
let message = await Chat.findOne({id: req.params.chatId}).exec();
if (!message) throw new NotFound(res.t('messageGroupChatNotFound'));
// TODO correct this error type
// @TODO correct this error type
if (message.uuid === user._id) throw new NotFound(res.t('messageGroupChatLikeOwnMessage'));
let update = {$set: {}};
if (!message.likes) message.likes = {};
message.likes[user._id] = !message.likes[user._id];
update.$set[`chat.$.likes.${user._id}`] = message.likes[user._id];
message.markModified('likes');
await message.save();
await Group.update(
{_id: group._id, 'chat.id': message.id},
update
).exec();
res.respond(200, message); // TODO what if the message is flagged and shouldn't be returned?
},
};
@@ -334,15 +330,11 @@ api.clearChatFlags = {
});
if (!group) throw new NotFound(res.t('groupNotFound'));
let message = _.find(group.chat, {id: chatId});
let message = await Chat.findOne({id: chatId}).exec();
if (!message) throw new NotFound(res.t('messageGroupChatNotFound'));
message.flagCount = 0;
await Group.update(
{_id: group._id, 'chat.id': message.id},
{$set: {'chat.$.flagCount': message.flagCount}}
).exec();
await message.save();
let adminEmailContent = getUserInfo(user, ['email']).email;
let authorEmail = getAuthorEmailFromMessage(message);
@@ -466,25 +458,22 @@ api.deleteChat = {
let group = await Group.getGroup({user, groupId, fields: 'chat'});
if (!group) throw new NotFound(res.t('groupNotFound'));
let message = _.find(group.chat, {id: chatId});
let message = await Chat.findOne({id: chatId}).exec();
if (!message) throw new NotFound(res.t('messageGroupChatNotFound'));
if (user._id !== message.uuid && !user.contributor.admin) {
throw new NotAuthorized(res.t('onlyCreatorOrAdminCanDeleteChat'));
}
let lastClientMsg = req.query.previousMsg;
let chatUpdated = lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg ? true : false;
const chatRes = await Group.toJSONCleanChat(group, user);
const lastClientMsg = req.query.previousMsg;
const chatUpdated = lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg ? true : false;
await Group.update(
{_id: group._id},
{$pull: {chat: {id: chatId}}}
).exec();
await Chat.remove({_id: message._id}).exec();
if (chatUpdated) {
let chatRes = Group.toJSONCleanChat(group, user).chat;
removeFromArray(chatRes, {id: chatId});
res.respond(200, chatRes);
removeFromArray(chatRes.chat, {id: chatId});
res.respond(200, chatRes.chat);
} else {
res.respond(200, {});
}

View File

@@ -392,7 +392,7 @@ api.getGroup = {
throw new NotFound(res.t('groupNotFound'));
}
let groupJson = Group.toJSONCleanChat(group, user);
let groupJson = await Group.toJSONCleanChat(group, user);
if (groupJson.leader === user._id) {
groupJson.purchased.plan = group.purchased.plan.toObject();
@@ -456,7 +456,7 @@ api.updateGroup = {
_.assign(group, _.merge(group.toObject(), Group.sanitizeUpdate(req.body)));
let savedGroup = await group.save();
let response = Group.toJSONCleanChat(savedGroup, user);
let response = await Group.toJSONCleanChat(savedGroup, user);
// If the leader changed fetch new data, otherwise use authenticated user
if (response.leader !== user._id) {
@@ -625,7 +625,7 @@ api.joinGroup = {
promises = await Promise.all(promises);
let response = Group.toJSONCleanChat(promises[0], user);
let response = await Group.toJSONCleanChat(promises[0], user);
let leader = await User.findById(response.leader).select(nameFields).exec();
if (leader) {
response.leader = leader.toJSON({minimize: true});

View File

@@ -421,7 +421,8 @@ api.abortQuest = {
if (user._id !== group.leader && user._id !== group.quest.leader) throw new NotAuthorized(res.t('onlyLeaderAbortQuest'));
let questName = questScrolls[group.quest.key].text('en');
group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``);
const newChatMessage = group.sendChat(`\`${user.profile.name} aborted the party quest ${questName}.\``);
await newChatMessage.save();
let memberUpdates = User.update({
'party._id': groupId,

View File

@@ -195,13 +195,15 @@ api.assignTask = {
if (canNotEditTasks(group, user, assignedUserId)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
let promises = [];
// User is claiming the task
if (user._id === assignedUserId) {
let message = res.t('userIsClamingTask', {username: user.profile.name, task: task.text});
group.sendChat(message);
const newMessage = group.sendChat(message);
promises.push(newMessage.save());
}
let promises = [];
promises.push(group.syncTask(task, assignedUser));
promises.push(group.save());
await Promise.all(promises);

View File

@@ -130,8 +130,8 @@ api.castSpell = {
if (party && !spell.silent) {
let message = `\`${user.profile.name} casts ${spell.text()}${targetType === 'user' ? ` on ${partyMembers.profile.name}` : ' for the party'}.\``;
party.sendChat(message);
await party.save();
const newChatMessage = party.sendChat(message);
await newChatMessage.save();
}
}
},