mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
New inbox client (#10644)
* new inbox client * add tests for sendPrivateMessage returning the message * update DELETE user message tests * port v3 GET-inbox_messages * use v4 delete message route * sendPrivateMessage: return sent message * fix
This commit is contained in:
committed by
Sabe Jones
parent
64507a161e
commit
84329e5fad
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../helpers/api-integration/v4';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('GET /inbox/messages', () => {
|
describe('GET /inbox/messages', () => {
|
||||||
let user;
|
let user;
|
||||||
@@ -22,17 +22,26 @@ describe('GET /inbox/messages', () => {
|
|||||||
message: 'third',
|
message: 'third',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// message to yourself
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'fourth',
|
||||||
|
});
|
||||||
|
|
||||||
await user.sync();
|
await user.sync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the user inbox messages as an array of ordered messages (from most to least recent)', async () => {
|
it('returns the user inbox messages as an array of ordered messages (from most to least recent)', async () => {
|
||||||
const messages = await user.get('/inbox/messages');
|
const messages = await user.get('/inbox/messages');
|
||||||
|
|
||||||
expect(messages.length).to.equal(3);
|
expect(messages.length).to.equal(4);
|
||||||
expect(messages.length).to.equal(Object.keys(user.inbox.messages).length);
|
|
||||||
|
|
||||||
expect(messages[0].text).to.equal('third');
|
// message to yourself
|
||||||
expect(messages[1].text).to.equal('second');
|
expect(messages[0].text).to.equal('fourth');
|
||||||
expect(messages[2].text).to.equal('first');
|
expect(messages[0].uuid).to.equal(user._id);
|
||||||
|
|
||||||
|
expect(messages[1].text).to.equal('third');
|
||||||
|
expect(messages[2].text).to.equal('second');
|
||||||
|
expect(messages[3].text).to.equal('first');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -100,7 +100,7 @@ describe('POST /members/send-private-message', () => {
|
|||||||
let receiver = await generateUser();
|
let receiver = await generateUser();
|
||||||
// const initialNotifications = receiver.notifications.length;
|
// const initialNotifications = receiver.notifications.length;
|
||||||
|
|
||||||
await userToSendMessage.post('/members/send-private-message', {
|
const response = await userToSendMessage.post('/members/send-private-message', {
|
||||||
message: messageToSend,
|
message: messageToSend,
|
||||||
toUserId: receiver._id,
|
toUserId: receiver._id,
|
||||||
});
|
});
|
||||||
@@ -116,6 +116,9 @@ describe('POST /members/send-private-message', () => {
|
|||||||
return message.uuid === receiver._id && message.text === messageToSend;
|
return message.uuid === receiver._id && message.text === messageToSend;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(response.message.text).to.deep.equal(sendersMessageInSendersInbox.text);
|
||||||
|
expect(response.message.uuid).to.deep.equal(sendersMessageInSendersInbox.uuid);
|
||||||
|
|
||||||
// @TODO waiting for mobile support
|
// @TODO waiting for mobile support
|
||||||
// expect(updatedReceiver.notifications.length).to.equal(initialNotifications + 1);
|
// expect(updatedReceiver.notifications.length).to.equal(initialNotifications + 1);
|
||||||
// const notification = updatedReceiver.notifications[updatedReceiver.notifications.length - 1];
|
// const notification = updatedReceiver.notifications[updatedReceiver.notifications.length - 1];
|
||||||
|
|||||||
@@ -3,25 +3,41 @@ import {
|
|||||||
} from '../../../../helpers/api-integration/v3';
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
|
||||||
describe('DELETE user message', () => {
|
describe('DELETE user message', () => {
|
||||||
let user;
|
let user, messagesId, otherUser;
|
||||||
|
|
||||||
beforeEach(async () => {
|
before(async () => {
|
||||||
user = await generateUser({ inbox: { messages: { first: 'message', second: 'message' } } });
|
[user, otherUser] = await Promise.all([generateUser(), generateUser()]);
|
||||||
expect(user.inbox.messages.first).to.eql('message');
|
await user.post('/members/send-private-message', {
|
||||||
expect(user.inbox.messages.second).to.eql('message');
|
toUserId: otherUser.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: otherUser.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
|
||||||
|
let userRes = await user.get('/user');
|
||||||
|
|
||||||
|
messagesId = Object.keys(userRes.inbox.messages);
|
||||||
|
expect(messagesId.length).to.eql(2);
|
||||||
|
expect(userRes.inbox.messages[messagesId[0]].text).to.eql('first');
|
||||||
|
expect(userRes.inbox.messages[messagesId[1]].text).to.eql('second');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('one message', async () => {
|
it('one message', async () => {
|
||||||
let result = await user.del('/user/messages/first');
|
let result = await user.del(`/user/messages/${messagesId[0]}`);
|
||||||
await user.sync();
|
messagesId = Object.keys(result);
|
||||||
expect(result).to.eql({ second: 'message' });
|
expect(messagesId.length).to.eql(1);
|
||||||
expect(user.inbox.messages).to.eql({ second: 'message' });
|
|
||||||
|
let userRes = await user.get('/user');
|
||||||
|
expect(Object.keys(userRes.inbox.messages).length).to.eql(1);
|
||||||
|
expect(userRes.inbox.messages[messagesId[0]].text).to.eql('second');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clear all', async () => {
|
it('clear all', async () => {
|
||||||
let result = await user.del('/user/messages');
|
let result = await user.del('/user/messages');
|
||||||
await user.sync();
|
let userRes = await user.get('/user');
|
||||||
expect(user.inbox.messages).to.eql({});
|
expect(userRes.inbox.messages).to.eql({});
|
||||||
expect(result).to.eql({});
|
expect(result).to.eql({});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
62
test/api/v4/inbox/DELETE-inbox_messages_messageId.test.js
Normal file
62
test/api/v4/inbox/DELETE-inbox_messages_messageId.test.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
describe('DELETE /inbox/messages/:messageId', () => {
|
||||||
|
let user;
|
||||||
|
let otherUser;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[user, otherUser] = await Promise.all([generateUser(), generateUser()]);
|
||||||
|
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'first',
|
||||||
|
});
|
||||||
|
await user.post('/members/send-private-message', {
|
||||||
|
toUserId: otherUser.id,
|
||||||
|
message: 'second',
|
||||||
|
});
|
||||||
|
await otherUser.post('/members/send-private-message', {
|
||||||
|
toUserId: user.id,
|
||||||
|
message: 'third',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if the messageId parameter is not an UUID', async () => {
|
||||||
|
await expect(user.del('/inbox/messages/123'))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: 'Invalid request parameters.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error if the message does not exist', async () => {
|
||||||
|
await expect(user.del(`/inbox/messages/${generateUUID()}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('messageGroupChatNotFound'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletes one message', async () => {
|
||||||
|
const messages = await user.get('/inbox/messages');
|
||||||
|
|
||||||
|
expect(messages.length).to.equal(3);
|
||||||
|
|
||||||
|
expect(messages[0].text).to.equal('third');
|
||||||
|
expect(messages[1].text).to.equal('second');
|
||||||
|
expect(messages[2].text).to.equal('first');
|
||||||
|
|
||||||
|
await user.del(`/inbox/messages/${messages[1]._id}`);
|
||||||
|
const updatedMessages = await user.get('/inbox/messages');
|
||||||
|
expect(updatedMessages.length).to.equal(2);
|
||||||
|
|
||||||
|
expect(updatedMessages[0].text).to.equal('third');
|
||||||
|
expect(updatedMessages[1].text).to.equal('first');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -255,8 +255,7 @@ export default {
|
|||||||
this.$emit('message-removed', message);
|
this.$emit('message-removed', message);
|
||||||
|
|
||||||
if (this.inbox) {
|
if (this.inbox) {
|
||||||
axios.delete(`/api/v4/user/messages/${message.id}`);
|
await axios.delete(`/api/v4/inbox/messages/${message.id}`);
|
||||||
this.$delete(this.user.inbox.messages, message.id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,11 @@ export default {
|
|||||||
this.chat.splice(chatIndex, 1, message);
|
this.chat.splice(chatIndex, 1, message);
|
||||||
},
|
},
|
||||||
messageRemoved (message) {
|
messageRemoved (message) {
|
||||||
|
if (this.inbox) {
|
||||||
|
this.$emit('message-removed', message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const chatIndex = findIndex(this.chat, chatMessage => {
|
const chatIndex = findIndex(this.chat, chatMessage => {
|
||||||
return chatMessage.id === message.id;
|
return chatMessage.id === message.id;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -478,11 +478,6 @@ export default {
|
|||||||
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
|
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteAllMessages () {
|
|
||||||
if (confirm(this.$t('confirmDeleteAllMessages'))) {
|
|
||||||
// User.clearPMs();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkForAchievements () {
|
checkForAchievements () {
|
||||||
// Checks if user's party has reached 2 players for the first time.
|
// Checks if user's party has reached 2 players for the first time.
|
||||||
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
|
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
b-modal#inbox-modal(title="", :hide-footer="true", size='lg')
|
b-modal#inbox-modal(title="", :hide-footer="true", size='lg', @shown="onModalShown", @hide="onModalHide")
|
||||||
.header-wrap.container.align-items-center(slot="modal-header")
|
.header-wrap.container.align-items-center(slot="modal-header")
|
||||||
.row.align-items-center
|
.row.align-items-center
|
||||||
.col-4
|
.col-4
|
||||||
@@ -34,13 +34,19 @@
|
|||||||
span.timeago {{conversation.date | timeAgo}}
|
span.timeago {{conversation.date | timeAgo}}
|
||||||
div {{conversation.lastMessageText ? conversation.lastMessageText.substring(0, 30) : ''}}
|
div {{conversation.lastMessageText ? conversation.lastMessageText.substring(0, 30) : ''}}
|
||||||
.col-8.messages.d-flex.flex-column.justify-content-between
|
.col-8.messages.d-flex.flex-column.justify-content-between
|
||||||
.empty-messages.text-center(v-if='activeChat.length === 0 && !selectedConversation.key')
|
.empty-messages.text-center(v-if='!selectedConversation.key')
|
||||||
.svg-icon.envelope(v-html="icons.messageIcon")
|
.svg-icon.envelope(v-html="icons.messageIcon")
|
||||||
h4 {{placeholderTexts.title}}
|
h4 {{placeholderTexts.title}}
|
||||||
p(v-html="placeholderTexts.description")
|
p(v-html="placeholderTexts.description")
|
||||||
.empty-messages.text-center(v-if='activeChat.length === 0 && selectedConversation.key')
|
.empty-messages.text-center(v-if='selectedConversation.key && selectedConversationMessages.length === 0')
|
||||||
p {{ $t('beginningOfConversation', {userName: selectedConversation.name})}}
|
p {{ $t('beginningOfConversation', {userName: selectedConversation.name})}}
|
||||||
chat-message.message-scroll(v-if="activeChat.length > 0", :chat.sync='activeChat', :inbox='true', ref="chatscroll")
|
chat-messages.message-scroll(
|
||||||
|
v-if="selectedConversation.messages && selectedConversationMessages.length > 0",
|
||||||
|
:chat='selectedConversationMessages',
|
||||||
|
:inbox='true',
|
||||||
|
@message-removed='messageRemoved',
|
||||||
|
ref="chatscroll"
|
||||||
|
)
|
||||||
.pm-disabled-caption.text-center(v-if="user.inbox.optOut && selectedConversation.key")
|
.pm-disabled-caption.text-center(v-if="user.inbox.optOut && selectedConversation.key")
|
||||||
h4 {{$t('PMDisabledCaptionTitle')}}
|
h4 {{$t('PMDisabledCaptionTitle')}}
|
||||||
p {{$t('PMDisabledCaptionText')}}
|
p {{$t('PMDisabledCaptionText')}}
|
||||||
@@ -196,43 +202,46 @@ import moment from 'moment';
|
|||||||
import filter from 'lodash/filter';
|
import filter from 'lodash/filter';
|
||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import groupBy from 'lodash/groupBy';
|
import groupBy from 'lodash/groupBy';
|
||||||
import findIndex from 'lodash/findIndex';
|
|
||||||
import { mapState } from 'client/libs/store';
|
import { mapState } from 'client/libs/store';
|
||||||
import styleHelper from 'client/mixins/styleHelper';
|
import styleHelper from 'client/mixins/styleHelper';
|
||||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import messageIcon from 'assets/svg/message.svg';
|
import messageIcon from 'assets/svg/message.svg';
|
||||||
import chatMessage from '../chat/chatMessages';
|
import chatMessages from '../chat/chatMessages';
|
||||||
import svgClose from 'assets/svg/close.svg';
|
import svgClose from 'assets/svg/close.svg';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [styleHelper],
|
mixins: [styleHelper],
|
||||||
components: {
|
components: {
|
||||||
chatMessage,
|
chatMessages,
|
||||||
toggleSwitch,
|
toggleSwitch,
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.$root.$on('habitica::new-inbox-message', (data) => {
|
this.$root.$on('habitica::new-inbox-message', (data) => {
|
||||||
this.$root.$emit('bv::show::modal', 'inbox-modal');
|
this.$root.$emit('bv::show::modal', 'inbox-modal');
|
||||||
|
|
||||||
const conversation = this.conversations.find(convo => {
|
// Wait for messages to be loaded
|
||||||
return convo.key === data.userIdToMessage;
|
const unwatchLoaded = this.$watch('loaded', (loaded) => {
|
||||||
});
|
if (!loaded) return;
|
||||||
|
|
||||||
|
const conversation = this.conversations.find(convo => {
|
||||||
|
return convo.key === data.userIdToMessage;
|
||||||
|
});
|
||||||
|
if (loaded) setImmediate(() => unwatchLoaded());
|
||||||
|
|
||||||
|
if (conversation) {
|
||||||
|
this.selectConversation(data.userIdToMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initiatedConversation = {
|
||||||
|
user: data.userName,
|
||||||
|
uuid: data.userIdToMessage,
|
||||||
|
};
|
||||||
|
|
||||||
if (conversation) {
|
|
||||||
this.selectConversation(data.userIdToMessage);
|
this.selectConversation(data.userIdToMessage);
|
||||||
return;
|
}, {immediate: true});
|
||||||
}
|
|
||||||
|
|
||||||
const newMessage = {
|
|
||||||
text: '',
|
|
||||||
timestamp: new Date(),
|
|
||||||
user: data.userName,
|
|
||||||
uuid: data.userIdToMessage,
|
|
||||||
id: '',
|
|
||||||
};
|
|
||||||
this.$set(this.user.inbox.messages, data.userIdToMessage, newMessage);
|
|
||||||
this.selectConversation(data.userIdToMessage);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
@@ -248,8 +257,10 @@ export default {
|
|||||||
selectedConversation: {},
|
selectedConversation: {},
|
||||||
search: '',
|
search: '',
|
||||||
newMessage: '',
|
newMessage: '',
|
||||||
activeChat: [],
|
|
||||||
showPopover: false,
|
showPopover: false,
|
||||||
|
messages: [],
|
||||||
|
loaded: false,
|
||||||
|
initiatedConversation: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
@@ -260,8 +271,18 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({user: 'user.data'}),
|
...mapState({user: 'user.data'}),
|
||||||
conversations () {
|
conversations () {
|
||||||
const inboxGroup = groupBy(this.user.inbox.messages, 'uuid');
|
const inboxGroup = groupBy(this.messages, 'uuid');
|
||||||
|
|
||||||
|
// Add placeholder for new conversations
|
||||||
|
if (this.initiatedConversation && this.initiatedConversation.uuid) {
|
||||||
|
inboxGroup[this.initiatedConversation.uuid] = [{
|
||||||
|
id: '',
|
||||||
|
text: '',
|
||||||
|
user: this.initiatedConversation.user,
|
||||||
|
uuid: this.initiatedConversation.uuid,
|
||||||
|
timestamp: new Date(),
|
||||||
|
}];
|
||||||
|
}
|
||||||
// Create conversation objects
|
// Create conversation objects
|
||||||
const convos = [];
|
const convos = [];
|
||||||
for (let key in inboxGroup) {
|
for (let key in inboxGroup) {
|
||||||
@@ -283,12 +304,9 @@ export default {
|
|||||||
return newChat;
|
return newChat;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// In case the last message is a placeholder, remove it
|
||||||
const recentMessage = newChatModels[newChatModels.length - 1];
|
const recentMessage = newChatModels[newChatModels.length - 1];
|
||||||
|
if (!recentMessage.text) newChatModels.splice(newChatModels.length - 1, 1);
|
||||||
// Special case where we have placeholder message because conversations are just grouped messages for now
|
|
||||||
if (!recentMessage.text) {
|
|
||||||
newChatModels.splice(newChatModels.length - 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const convoModel = {
|
const convoModel = {
|
||||||
name: recentMessage.toUser ? recentMessage.toUser : recentMessage.user, // Handles case where from user sent the only message or the to user sent the only message
|
name: recentMessage.toUser ? recentMessage.toUser : recentMessage.user, // Handles case where from user sent the only message or the to user sent the only message
|
||||||
@@ -308,6 +326,12 @@ export default {
|
|||||||
|
|
||||||
return conversations.reverse();
|
return conversations.reverse();
|
||||||
},
|
},
|
||||||
|
// Separate from selectedConversation which is not coputed so messages don't update automatically
|
||||||
|
selectedConversationMessages () {
|
||||||
|
const selectedConversationKey = this.selectedConversation.key;
|
||||||
|
const selectedConversation = this.conversations.find(c => c.key === selectedConversationKey);
|
||||||
|
return selectedConversation ? selectedConversation.messages : [];
|
||||||
|
},
|
||||||
filtersConversations () {
|
filtersConversations () {
|
||||||
if (!this.search) return this.conversations;
|
if (!this.search) return this.conversations;
|
||||||
return filter(this.conversations, (conversation) => {
|
return filter(this.conversations, (conversation) => {
|
||||||
@@ -343,6 +367,25 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async onModalShown () {
|
||||||
|
this.loaded = false;
|
||||||
|
const res = await axios.get('/api/v4/inbox/messages');
|
||||||
|
this.messages = res.data.data;
|
||||||
|
this.loaded = true;
|
||||||
|
},
|
||||||
|
onModalHide () {
|
||||||
|
this.messages = [];
|
||||||
|
this.loaded = false;
|
||||||
|
this.initiatedConversation = null;
|
||||||
|
},
|
||||||
|
messageRemoved (message) {
|
||||||
|
const messageIndex = this.messages.findIndex(msg => msg.id === message.id);
|
||||||
|
if (messageIndex !== -1) this.messages.splice(messageIndex, 1);
|
||||||
|
if (this.selectedConversationMessages.length === 0) this.initiatedConversation = {
|
||||||
|
user: this.selectedConversation.name,
|
||||||
|
uuid: this.selectedConversation.key,
|
||||||
|
};
|
||||||
|
},
|
||||||
toggleClick () {
|
toggleClick () {
|
||||||
this.displayCreate = !this.displayCreate;
|
this.displayCreate = !this.displayCreate;
|
||||||
},
|
},
|
||||||
@@ -354,14 +397,7 @@ export default {
|
|||||||
return conversation.key === key;
|
return conversation.key === key;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selectedConversation = convoFound;
|
this.selectedConversation = convoFound || {};
|
||||||
let activeChat = convoFound.messages;
|
|
||||||
|
|
||||||
activeChat = sortBy(activeChat, [(o) => {
|
|
||||||
return moment(o.timestamp).toDate();
|
|
||||||
}]);
|
|
||||||
|
|
||||||
this.$set(this, 'activeChat', activeChat);
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
if (!this.$refs.chatscroll) return;
|
if (!this.$refs.chatscroll) return;
|
||||||
@@ -372,22 +408,22 @@ export default {
|
|||||||
sendPrivateMessage () {
|
sendPrivateMessage () {
|
||||||
if (!this.newMessage) return;
|
if (!this.newMessage) return;
|
||||||
|
|
||||||
const convoFound = this.conversations.find((conversation) => {
|
this.messages.push({
|
||||||
return conversation.key === this.selectedConversation.key;
|
sent: true,
|
||||||
});
|
|
||||||
|
|
||||||
convoFound.messages.push({
|
|
||||||
text: this.newMessage,
|
text: this.newMessage,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
user: this.user.profile.name,
|
user: this.selectedConversation.name,
|
||||||
uuid: this.user._id,
|
uuid: this.selectedConversation.key,
|
||||||
contributor: this.user.contributor,
|
contributor: this.user.contributor,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activeChat = convoFound.messages;
|
// Remove the placeholder message
|
||||||
|
if (this.initiatedConversation && this.initiatedConversation.uuid === this.selectedConversation.key) {
|
||||||
|
this.initiatedConversation = null;
|
||||||
|
}
|
||||||
|
|
||||||
convoFound.lastMessageText = this.newMessage;
|
this.selectedConversation.lastMessageText = this.newMessage;
|
||||||
convoFound.date = new Date();
|
this.selectedConversation.date = new Date();
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
if (!this.$refs.chatscroll) return;
|
if (!this.$refs.chatscroll) return;
|
||||||
@@ -400,8 +436,7 @@ export default {
|
|||||||
message: this.newMessage,
|
message: this.newMessage,
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
const newMessage = response.data.data.message;
|
const newMessage = response.data.data.message;
|
||||||
const messageIndex = findIndex(convoFound.messages, msg => !msg.id);
|
Object.assign(this.messages[this.messages.length - 1], newMessage);
|
||||||
convoFound.messages.splice(convoFound.messages.length - 1, messageIndex, newMessage);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.newMessage = '';
|
this.newMessage = '';
|
||||||
|
|||||||
@@ -22,4 +22,6 @@ module.exports = {
|
|||||||
missingCustomerId: 'Missing "req.query.customerId"',
|
missingCustomerId: 'Missing "req.query.customerId"',
|
||||||
missingPaypalBlock: 'Missing "req.session.paypalBlock"',
|
missingPaypalBlock: 'Missing "req.session.paypalBlock"',
|
||||||
missingSubKey: 'Missing "req.query.sub"',
|
missingSubKey: 'Missing "req.query.sub"',
|
||||||
|
|
||||||
|
messageIdRequired: '\"messageId\" must be a valid UUID.",',
|
||||||
};
|
};
|
||||||
|
|||||||
29
website/server/controllers/api-v3/inbox.js
Normal file
29
website/server/controllers/api-v3/inbox.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
|
import { toArray, orderBy } from 'lodash';
|
||||||
|
|
||||||
|
let api = {};
|
||||||
|
|
||||||
|
/* NOTE most inbox routes are either in the user or members controller */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /api/v3/inbox/messages Get inbox messages for a user
|
||||||
|
* @apiPrivate
|
||||||
|
* @apiName GetInboxMessages
|
||||||
|
* @apiGroup Inbox
|
||||||
|
* @apiDescription Get inbox messages for a user
|
||||||
|
*
|
||||||
|
* @apiSuccess {Array} data An array of inbox messages
|
||||||
|
*/
|
||||||
|
api.getInboxMessages = {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/inbox/messages',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
async handler (req, res) {
|
||||||
|
const messagesObj = res.locals.user.inbox.messages;
|
||||||
|
const messagesArray = orderBy(toArray(messagesObj), ['timestamp'], ['desc']);
|
||||||
|
|
||||||
|
res.respond(200, messagesArray);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = api;
|
||||||
@@ -1,28 +1,49 @@
|
|||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import { toArray, orderBy } from 'lodash';
|
import apiError from '../../libs/apiError';
|
||||||
|
import * as inboxLib from '../../libs/inbox';
|
||||||
|
import {
|
||||||
|
NotFound,
|
||||||
|
} from '../../libs/errors';
|
||||||
|
|
||||||
let api = {};
|
const api = {};
|
||||||
|
|
||||||
/* NOTE most inbox routes are either in the user or members controller */
|
/* NOTE most inbox routes are either in the user or members controller */
|
||||||
|
|
||||||
/**
|
/* NOTE the getInboxMessages route is implemented in v3 only */
|
||||||
* @api {get} /api/v3/inbox/messages Get inbox messages for a user
|
|
||||||
* @apiPrivate
|
|
||||||
* @apiName GetInboxMessages
|
|
||||||
* @apiGroup Inbox
|
|
||||||
* @apiDescription Get inbox messages for a user
|
|
||||||
*
|
|
||||||
* @apiSuccess {Array} data An array of inbox messages
|
|
||||||
*/
|
|
||||||
api.getInboxMessages = {
|
|
||||||
method: 'GET',
|
|
||||||
url: '/inbox/messages',
|
|
||||||
middlewares: [authWithHeaders()],
|
|
||||||
async handler (req, res) {
|
|
||||||
const messagesObj = res.locals.user.inbox.messages;
|
|
||||||
const messagesArray = orderBy(toArray(messagesObj), ['timestamp'], ['desc']);
|
|
||||||
|
|
||||||
res.respond(200, messagesArray);
|
/* NOTE this route has also an API v3 version */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {delete} /api/v4/inbox/messages/:messageId Delete a message
|
||||||
|
* @apiName deleteMessage
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
* @apiParam (Path) {UUID} messageId The id of the message to delete
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data Empty object
|
||||||
|
* @apiSuccessExample {json}
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "data": {}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
api.deleteMessage = {
|
||||||
|
method: 'DELETE',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
url: '/inbox/messages/:messageId',
|
||||||
|
async handler (req, res) {
|
||||||
|
req.checkParams('messageId', apiError('messageIdRequired')).notEmpty().isUUID();
|
||||||
|
|
||||||
|
const validationErrors = req.validationErrors();
|
||||||
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
|
const messageId = req.params.messageId;
|
||||||
|
const user = res.locals.user;
|
||||||
|
|
||||||
|
const deleted = await inboxLib.deleteMessage(user, messageId);
|
||||||
|
if (!deleted) throw new NotFound(res.t('messageGroupChatNotFound'));
|
||||||
|
|
||||||
|
res.respond(200);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
11
website/server/libs/inbox/index.js
Normal file
11
website/server/libs/inbox/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export async function deleteMessage (user, messageId) {
|
||||||
|
if (user.inbox.messages[messageId]) {
|
||||||
|
delete user.inbox.messages[messageId];
|
||||||
|
user.markModified(`inbox.messages.${messageId}`);
|
||||||
|
await user.save();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ module.exports.walkControllers = function walkControllers (router, filePath, ove
|
|||||||
.readdirSync(filePath)
|
.readdirSync(filePath)
|
||||||
.forEach(fileName => {
|
.forEach(fileName => {
|
||||||
if (!fs.statSync(filePath + fileName).isFile()) {
|
if (!fs.statSync(filePath + fileName).isFile()) {
|
||||||
walkControllers(router, `${filePath}${fileName}/`);
|
walkControllers(router, `${filePath}${fileName}/`, overrides);
|
||||||
} else if (fileName.match(/\.js$/)) {
|
} else if (fileName.match(/\.js$/)) {
|
||||||
let controller = require(filePath + fileName); // eslint-disable-line global-require
|
let controller = require(filePath + fileName); // eslint-disable-line global-require
|
||||||
module.exports.readController(router, controller, overrides);
|
module.exports.readController(router, controller, overrides);
|
||||||
|
|||||||
@@ -106,8 +106,8 @@ schema.methods.sendMessage = async function sendMessage (userToReceiveMessage, o
|
|||||||
// whether to save users after sending the message, defaults to true
|
// whether to save users after sending the message, defaults to true
|
||||||
let saveUsers = options.save === false ? false : true;
|
let saveUsers = options.save === false ? false : true;
|
||||||
|
|
||||||
const newMessage = chatDefaults(options.receiverMsg, sender);
|
const newMessageReceiver = chatDefaults(options.receiverMsg, sender);
|
||||||
common.refPush(userToReceiveMessage.inbox.messages, newMessage);
|
common.refPush(userToReceiveMessage.inbox.messages, newMessageReceiver);
|
||||||
userToReceiveMessage.inbox.newMessages++;
|
userToReceiveMessage.inbox.newMessages++;
|
||||||
userToReceiveMessage._v++;
|
userToReceiveMessage._v++;
|
||||||
userToReceiveMessage.markModified('inbox.messages');
|
userToReceiveMessage.markModified('inbox.messages');
|
||||||
@@ -134,7 +134,8 @@ schema.methods.sendMessage = async function sendMessage (userToReceiveMessage, o
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
common.refPush(sender.inbox.messages, defaults({sent: true}, chatDefaults(senderMsg, userToReceiveMessage)));
|
const newMessage = defaults({sent: true}, chatDefaults(senderMsg, userToReceiveMessage));
|
||||||
|
common.refPush(sender.inbox.messages, newMessage);
|
||||||
sender.markModified('inbox.messages');
|
sender.markModified('inbox.messages');
|
||||||
|
|
||||||
if (saveUsers) {
|
if (saveUsers) {
|
||||||
|
|||||||
Reference in New Issue
Block a user