feat: add toMid to chat messages

isekai-main
Barış Soner Uşaklı 2 years ago
parent 9a07cdbd51
commit 0316f324e7

@ -10,6 +10,7 @@
"chat.no_active": "You have no active chats.",
"chat.user_typing": "%1 is typing ...",
"chat.user_has_messaged_you": "%1 has messaged you.",
"chat.replying-to": "Replying to %1",
"chat.see_all": "All chats",
"chat.mark_all_read": "Mark all read",
"chat.no-messages": "Please select a recipient to view chat message history",

@ -72,16 +72,18 @@ define('forum/chats', [
Chats.addEventListeners = function () {
const { roomId } = ajaxify.data;
const mainWrapper = $('[component="chat/main-wrapper"]');
const chatMessageContent = $('[component="chat/message/content"]');
const chatControls = components.get('chat/controls');
Chats.addSendHandlers(roomId, $('.chat-input'), $('.expanded-chat button[data-action="send"]'));
Chats.addPopoutHandler();
Chats.addActionHandlers(components.get('chat/messages'), roomId);
Chats.addManageHandler(roomId, chatControls.find('[data-action="members"]'));
Chats.addManageHandler(roomId, chatControls.find('[data-action="manage"]'));
Chats.addRenameHandler(roomId, chatControls.find('[data-action="rename"]'));
Chats.addLeaveHandler(roomId, chatControls.find('[data-action="leave"]'));
Chats.addDeleteHandler(roomId, chatControls.find('[data-action="delete"]'));
Chats.addScrollHandler(roomId, ajaxify.data.uid, $('[component="chat/message/content"]'));
Chats.addScrollBottomHandler($('[component="chat/message/content"]'));
Chats.addScrollHandler(roomId, ajaxify.data.uid, chatMessageContent);
Chats.addScrollBottomHandler(chatMessageContent);
Chats.addParentHandler(chatMessageContent);
Chats.addCharactersLeftHandler(mainWrapper);
Chats.addTextareaResizeHandler(mainWrapper);
Chats.addIPHandler(mainWrapper);
@ -98,10 +100,10 @@ define('forum/chats', [
Chats.switchChat();
});
userList.init(roomId, mainWrapper);
messageSearch.init(roomId);
Chats.addNotificationSettingHandler(roomId, mainWrapper);
messageSearch.init(roomId, mainWrapper);
Chats.addPublicRoomSortHandler();
Chats.addTooltipHandler();
Chats.addNotificationSettingHandler();
};
Chats.addPublicRoomSortHandler = function () {
@ -141,20 +143,32 @@ define('forum/chats', [
});
};
Chats.addNotificationSettingHandler = function () {
const notifSettingEl = $('[component="chat/notification/setting"]');
Chats.addNotificationSettingHandler = function (roomId, containerEl) {
const notifSettingEl = containerEl.find('[component="chat/notification/setting"]');
notifSettingEl.find('[data-value]').on('click', async function () {
notifSettingEl.find('i.fa-check').addClass('hidden');
const $this = $(this);
$this.find('i.fa-check').removeClass('hidden');
$('[component="chat/notification/setting/icon"]').attr('class', `fa ${$this.attr('data-icon')}`);
notifSettingEl.find('[component="chat/notification/setting/icon"]').attr('class', `fa ${$this.attr('data-icon')}`);
await socket.emit('modules.chats.setNotificationSetting', {
roomId: ajaxify.data.roomId,
roomId: roomId,
value: $this.attr('data-value'),
});
});
};
Chats.addParentHandler = function (chatContent) {
chatContent.on('click', '[component="chat/message/parent"]', function () {
const parentEl = $(this);
parentEl.find('[component="chat/message/parent/content"]').toggleClass('line-clamp-1');
parentEl.find('.chat-timestamp').toggleClass('hidden');
parentEl.toggleClass('flex-column').toggleClass('flex-row');
if (chatContent.length && messages.isAtBottom(chatContent)) {
messages.scrollToBottom(chatContent);
}
});
};
Chats.addUploadHandler = function (options) {
uploadHelpers.init({
@ -285,14 +299,15 @@ define('forum/chats', [
const action = this.getAttribute('data-action');
switch (action) {
case 'edit': {
case 'reply':
messages.prepReplyTo(msgEl, roomId);
break;
case 'edit':
messages.prepEdit(msgEl, messageId, roomId);
break;
}
case 'delete':
messages.delete(messageId, roomId);
break;
case 'restore':
messages.restore(messageId, roomId);
break;

@ -11,17 +11,17 @@ define('forum/chats/message-search', [
let chatContent;
let clearEl;
let toggleEl;
let containerEl;
messageSearch.init = function (_roomId) {
let searchContainerEl;
messageSearch.init = function (_roomId, containerEl) {
roomId = _roomId;
resultListEl = $('[component="chat/message/search/results"]');
chatContent = $('[component="chat/message/content"]');
clearEl = $('[component="chat/room/search/clear"]');
containerEl = $('[component="chat/room/search/container"]');
toggleEl = $('[component="chat/room/search/toggle"');
resultListEl = containerEl.find('[component="chat/message/search/results"]');
chatContent = containerEl.find('[component="chat/message/content"]');
clearEl = containerEl.find('[component="chat/room/search/clear"]');
searchContainerEl = containerEl.find('[component="chat/room/search/container"]');
toggleEl = containerEl.find('[component="chat/room/search/toggle"');
searchInputEl = $('[component="chat/room/search"]');
searchInputEl = containerEl.find('[component="chat/room/search"]');
searchInputEl.on('keyup', utils.debounce(doSearch, 250))
.on('focus', () => {
if (searchInputEl.val()) {
@ -29,14 +29,14 @@ define('forum/chats/message-search', [
}
});
$('[component="chat/input"]').on('focus', () => {
containerEl.find('[component="chat/input"]').on('focus', () => {
resultListEl.addClass('hidden');
chatContent.removeClass('hidden');
});
clearEl.on('click', clearInputAndResults);
toggleEl.on('click', () => {
containerEl.removeClass('hidden');
searchContainerEl.removeClass('hidden');
toggleEl.addClass('hidden');
searchInputEl.trigger('focus');
});
@ -52,7 +52,7 @@ define('forum/chats/message-search', [
removeResults();
resultListEl.addClass('hidden');
clearEl.addClass('hidden');
containerEl.addClass('hidden');
searchContainerEl.addClass('hidden');
chatContent.removeClass('hidden');
toggleEl.removeClass('hidden');
}

@ -21,9 +21,13 @@ define('forum/chats/messages', [
messages.updateTextAreaHeight(chatContent);
const payload = { roomId, message };
({ roomId, message } = await hooks.fire('filter:chat.send', payload));
api.post(`/chats/${roomId}`, { message }).then(() => {
const replyToEl = inputEl.parents('[component="chat/composer"]')
.find('[component="chat/composer/replying-to"]');
const toMid = replyToEl.attr('data-tomid');
api.post(`/chats/${roomId}`, { message, toMid: toMid }).then(() => {
hooks.fire('action:chat.sent', { roomId, message });
replyToEl.addClass('hidden');
replyToEl.attr('data-tomid', '');
}).catch((err) => {
inputEl.val(message).trigger('input');
messages.updateRemainingLength(inputEl.parent());
@ -76,7 +80,7 @@ define('forum/chats/messages', [
const lastSpeaker = parseInt(lastMsgEl.attr('data-uid'), 10);
const lasttimestamp = parseInt(lastMsgEl.attr('data-timestamp'), 10);
if (!Array.isArray(data)) {
data.newSet = lastSpeaker !== parseInt(data.fromuid, 10) ||
data.newSet = data.toMid || lastSpeaker !== parseInt(data.fromuid, 10) ||
parseInt(data.timestamp, 10) > parseInt(lasttimestamp, 10) + (1000 * 60 * 3);
}
@ -156,6 +160,28 @@ define('forum/chats/messages', [
.toggleClass('hidden', isAtBottom);
};
messages.prepReplyTo = async function (msgEl, roomId) {
const chatMessages = msgEl.parents(`[component="chat/messages"][data-roomid="${roomId}"]`);
const chatContent = chatMessages.find('[component="chat/message/content"]');
const composerEl = chatMessages.find('[component="chat/composer"]');
const mid = msgEl.attr('data-mid');
const replyToEl = composerEl.find('[component="chat/composer/replying-to"]');
replyToEl.attr('data-tomid', mid)
.find('[component="chat/composer/replying-to-text"]')
.translateText(`[[modules:chat.replying-to, ${msgEl.attr('data-username')}]]`);
replyToEl.removeClass('hidden');
replyToEl.find('[component="chat/composer/replying-to-cancel"]').off('click')
.on('click', () => {
replyToEl.attr('data-tomid', '');
replyToEl.addClass('hidden');
});
if (chatContent.length && messages.isAtBottom(chatContent)) {
messages.scrollToBottom(chatContent);
}
composerEl.find('[component="chat/input"]').trigger('focus');
};
messages.prepEdit = async function (msgEl, mid, roomId) {
const raw = await socket.emit('modules.chats.getRaw', { mid: mid, roomId: roomId });
const editEl = await app.parseAndTranslate('partials/chats/edit-message', {

@ -190,7 +190,7 @@ define('chat', [
newMessage = data.self === 0;
}
data.message.self = data.self;
data.message.timestamp = Math.min(Date.now(), data.message.timetamp);
data.message.timestamp = Math.min(Date.now(), data.message.timestamp);
data.message.timestampISO = utils.toISOString(data.message.timestamp);
addMessageToModal(data);
}
@ -203,13 +203,13 @@ define('chat', [
require(['forum/chats/messages'], function (ChatsMessages) {
// don't add if already added
if (!modal.find('[data-mid="' + data.message.messageId + '"]').length) {
ChatsMessages.appendChatMessage(modal.find('.chat-content'), data.message);
ChatsMessages.appendChatMessage(modal.find('[component="chat/message/content"]'), data.message);
}
if (modal.is(':visible')) {
taskbar.updateActive(modal.attr('data-uuid'));
if (ChatsMessages.isAtBottom(modal.find('.chat-content'))) {
ChatsMessages.scrollToBottom(modal.find('.chat-content'));
if (ChatsMessages.isAtBottom(modal.find('[component="chat/message/content"]'))) {
ChatsMessages.scrollToBottom(modal.find('[component="chat/message/content"]'));
}
} else if (!ajaxify.data.template.chats) {
module.toggleNew(modal.attr('data-uuid'), true, true);
@ -254,17 +254,18 @@ define('chat', [
module.createModal = function (data, callback) {
callback = callback || function () {};
require([
'scrollStop', 'forum/chats', 'forum/chats/messages',
], function (scrollStop, Chats, ChatsMessages) {
'scrollStop', 'forum/chats', 'forum/chats/messages', 'forum/chats/message-search',
], function (scrollStop, Chats, ChatsMessages, messageSearch) {
app.parseAndTranslate('chat', data, function (chatModal) {
if (module.modalExists(data.roomId)) {
const roomId = data.roomId;
if (module.modalExists(roomId)) {
return callback(module.getModal(data.roomId));
}
const uuid = utils.generateUUID();
let dragged = false;
chatModal.attr('id', 'chat-modal-' + data.roomId);
chatModal.attr('data-roomid', data.roomId);
chatModal.attr('id', 'chat-modal-' + roomId);
chatModal.attr('data-roomid', roomId);
chatModal.attr('intervalId', 0);
chatModal.attr('data-uuid', uuid);
chatModal.css('position', 'fixed');
@ -313,7 +314,7 @@ define('chat', [
components.get('chat/input').val(text);
});
ajaxify.go('user/' + app.user.userslug + '/chats/' + chatModal.attr('data-roomid'));
ajaxify.go(`user/${app.user.userslug}/chats/${roomId}`);
module.close(uuid);
}
@ -340,23 +341,23 @@ define('chat', [
chatModal.on('mousemove keypress click', function () {
if (newMessage) {
api.del(`/chats/${data.roomId}/state`, {});
api.del(`/chats/${roomId}/state`, {});
newMessage = false;
}
});
Chats.addActionHandlers(chatModal.find('[component="chat/messages"]'), data.roomId);
Chats.addRenameHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="rename"]'), data.roomName);
Chats.addLeaveHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="leave"]'));
Chats.addDeleteHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="delete"]'));
Chats.addSendHandlers(chatModal.attr('data-roomid'), chatModal.find('.chat-input'), chatModal.find('[data-action="send"]'));
Chats.addManageHandler(chatModal.attr('data-roomid'), chatModal.find('[data-action="members"]'));
Chats.addActionHandlers(chatModal.find('[component="chat/messages"]'), roomId);
Chats.addRenameHandler(roomId, chatModal.find('[data-action="rename"]'), data.roomName);
Chats.addLeaveHandler(roomId, chatModal.find('[data-action="leave"]'));
Chats.addDeleteHandler(roomId, chatModal.find('[data-action="delete"]'));
Chats.addSendHandlers(roomId, chatModal.find('.chat-input'), chatModal.find('[data-action="send"]'));
Chats.addManageHandler(roomId, chatModal.find('[data-action="manage"]'));
Chats.createAutoComplete(chatModal.attr('data-roomid'), chatModal.find('[component="chat/input"]'));
Chats.addScrollHandler(chatModal.attr('data-roomid'), data.uid, chatModal.find('.chat-content'));
Chats.addScrollBottomHandler(chatModal.find('.chat-content'));
Chats.createAutoComplete(roomId, chatModal.find('[component="chat/input"]'));
Chats.addScrollHandler(roomId, data.uid, chatModal.find('[component="chat/message/content"]'));
Chats.addScrollBottomHandler(chatModal.find('[component="chat/message/content"]'));
Chats.addParentHandler(chatModal.find('[component="chat/message/content"]'));
Chats.addCharactersLeftHandler(chatModal);
Chats.addTextareaResizeHandler(chatModal);
Chats.addIPHandler(chatModal);
@ -370,6 +371,8 @@ define('chat', [
});
ChatsMessages.addSocketListeners();
messageSearch.init(roomId, chatModal);
Chats.addNotificationSettingHandler(roomId, chatModal);
taskbar.push('chat', chatModal.attr('data-uuid'), {
title: '[[modules:chat.chatting_with]] ' + (data.roomName || (data.users.length ? data.users[0].username : '')),

@ -87,6 +87,7 @@ chatsAPI.post = async (caller, data) => {
uid: caller.uid,
roomId: data.roomId,
content: data.message,
toMid: data.toMid,
timestamp: Date.now(),
ip: caller.ip,
});

@ -33,6 +33,7 @@ Chats.get = async (req, res) => {
Chats.post = async (req, res) => {
const messageObj = await api.chats.post(req, {
message: req.body.message,
toMid: req.body.toMid,
roomId: req.params.roomId,
});

@ -6,6 +6,7 @@ const meta = require('../meta');
const plugins = require('../plugins');
const db = require('../database');
const user = require('../user');
const utils = require('../utils');
module.exports = function (Messaging) {
Messaging.sendMessage = async (data) => {
@ -41,6 +42,9 @@ module.exports = function (Messaging) {
if (!roomData) {
throw new Error('[[error:no-room]]');
}
if (data.toMid && !utils.isNumber(data.toMid)) {
throw new Error('[[error:invalid-mid]]');
}
const mid = await db.incrObjectField('global', 'nextMid');
const timestamp = data.timestamp || Date.now();
let message = {
@ -50,7 +54,9 @@ module.exports = function (Messaging) {
fromuid: uid,
roomId: roomId,
};
if (data.toMid) {
message.toMid = data.toMid;
}
if (data.system) {
message.system = data.system;
}
@ -69,6 +75,9 @@ module.exports = function (Messaging) {
db.sortedSetAdd('messages:mid', timestamp, mid),
db.incrObjectField('global', 'messageCount'),
];
if (data.toMid) {
tasks.push(db.sortedSetAdd(`mid:${data.toMid}:replies`, timestamp, mid));
}
if (roomData.public) {
tasks.push(
db.sortedSetAdd('chat:rooms:public:lastpost', timestamp, roomId)

@ -1,5 +1,6 @@
'use strict';
const _ = require('lodash');
const validator = require('validator');
const db = require('../database');
@ -73,16 +74,7 @@ module.exports = function (Messaging) {
message.roomId = String(message.roomId || roomId);
});
messages = await Promise.all(messages.map(async (message) => {
if (message.system) {
message.content = validator.escape(String(message.content));
return message;
}
const result = await Messaging.parse(message.content, message.fromuid, uid, roomId, isNew);
message.content = result;
return message;
}));
await parseMessages(messages, uid, roomId, isNew);
if (messages.length > 1) {
// Add a spacer in between messages with time gaps between them
@ -96,7 +88,7 @@ module.exports = function (Messaging) {
message.newSet = true;
} else if (index > 0 && messages[index - 1].system) {
message.newSet = true;
} else if (index === 0) {
} else if (index === 0 || message.toMid) {
message.newSet = true;
}
@ -111,7 +103,7 @@ module.exports = function (Messaging) {
const fields = await Messaging.getMessageFields(mid, ['fromuid', 'timestamp']);
if ((messages[0].timestamp > fields.timestamp + Messaging.newMessageCutoff) ||
(messages[0].fromuid !== fields.fromuid) ||
messages[0].system) {
messages[0].system || messages[0].toMid) {
// If it's been 5 minutes, this is a new set of messages
messages[0].newSet = true;
}
@ -120,6 +112,8 @@ module.exports = function (Messaging) {
}
}
await addParentMessages(messages, uid, roomId);
const data = await plugins.hooks.fire('filter:messaging.getMessages', {
messages: messages,
uid: uid,
@ -130,6 +124,60 @@ module.exports = function (Messaging) {
return data && data.messages;
};
async function addParentMessages(messages, uid, roomId) {
let parentMids = messages.map(msg => (msg && msg.hasOwnProperty('toMid') ? parseInt(msg.toMid, 10) : null)).filter(Boolean);
if (!parentMids.length) {
return;
}
parentMids = _.uniq(parentMids);
const parentMessages = await Messaging.getMessagesFields(parentMids, [
'fromuid', 'content', 'timestamp',
]);
const parentUids = _.uniq(parentMessages.map(msg => msg && msg.fromuid));
const usersMap = _.zipObject(
parentUids,
await user.getUsersFields(parentUids, ['uid', 'username', 'userslug', 'picture'])
);
await Promise.all(parentMessages.map(async (parentMsg) => {
const foundMsg = messages.find(msg => parseInt(msg.mid, 10) === parseInt(parentMsg.mid, 10));
if (foundMsg) {
parentMsg.content = foundMsg.content;
return;
}
parentMsg.content = await parseMessage(parentMsg, uid, roomId, false);
}));
const parents = {};
parentMessages.forEach((msg, i) => {
if (usersMap[msg.fromuid]) {
msg.user = usersMap[msg.fromuid];
parents[parentMids[i]] = msg;
}
});
messages.forEach((msg) => {
if (parents[msg.toMid]) {
msg.parent = parents[msg.toMid];
msg.parent.mid = msg.toMid;
}
});
}
async function parseMessages(messages, uid, roomId, isNew) {
await Promise.all(messages.map(async (message) => {
message.content = await parseMessage(message, uid, roomId, isNew);
}));
}
async function parseMessage(message, uid, roomId, isNew) {
if (message.system) {
return validator.escape(String(message.content));
}
return await Messaging.parse(message.content, message.fromuid, uid, roomId, isNew);
}
};
async function modifyMessage(message, fields, mid) {

@ -189,14 +189,18 @@ module.exports = function (Topics) {
const usersMap = _.zipObject(parentUids, userData);
const parents = {};
parentPosts.forEach((post, i) => {
parents[parentPids[i]] = {
username: usersMap[post.uid].username,
displayname: usersMap[post.uid].displayname,
};
if (usersMap[post.uid]) {
parents[parentPids[i]] = {
username: usersMap[post.uid].username,
displayname: usersMap[post.uid].displayname,
};
}
});
postData.forEach((post) => {
post.parent = parents[post.toPid];
if (parents[post.toPid]) {
post.parent = parents[post.toPid];
}
});
};

Loading…
Cancel
Save