feat: #11850, chat msg search

isekai-main
Barış Soner Uşaklı 2 years ago
parent 50fd7930ed
commit 11bfeaf130

@ -93,7 +93,7 @@
"nconf": "0.12.0", "nconf": "0.12.0",
"nodebb-plugin-2factor": "7.1.3", "nodebb-plugin-2factor": "7.1.3",
"nodebb-plugin-composer-default": "10.2.6", "nodebb-plugin-composer-default": "10.2.6",
"nodebb-plugin-dbsearch": "6.1.0", "nodebb-plugin-dbsearch": "6.2.0",
"nodebb-plugin-emoji": "5.1.3", "nodebb-plugin-emoji": "5.1.3",
"nodebb-plugin-emoji-android": "4.0.0", "nodebb-plugin-emoji-android": "4.0.0",
"nodebb-plugin-markdown": "12.1.7", "nodebb-plugin-markdown": "12.1.7",
@ -101,10 +101,10 @@
"nodebb-plugin-ntfy": "1.1.0", "nodebb-plugin-ntfy": "1.1.0",
"nodebb-plugin-spam-be-gone": "2.1.1", "nodebb-plugin-spam-be-gone": "2.1.1",
"nodebb-rewards-essentials": "0.2.3", "nodebb-rewards-essentials": "0.2.3",
"nodebb-theme-harmony": "1.1.16", "nodebb-theme-harmony": "1.1.17",
"nodebb-theme-lavender": "7.1.3", "nodebb-theme-lavender": "7.1.3",
"nodebb-theme-peace": "2.1.4", "nodebb-theme-peace": "2.1.5",
"nodebb-theme-persona": "13.2.8", "nodebb-theme-persona": "13.2.9",
"nodebb-widget-essentials": "7.0.13", "nodebb-widget-essentials": "7.0.13",
"nodemailer": "6.9.4", "nodemailer": "6.9.4",
"nprogress": "0.2.0", "nprogress": "0.2.0",

@ -9,6 +9,7 @@ define('forum/chats', [
'forum/chats/manage', 'forum/chats/manage',
'forum/chats/messages', 'forum/chats/messages',
'forum/chats/user-list', 'forum/chats/user-list',
'forum/chats/message-search',
'composer/autocomplete', 'composer/autocomplete',
'hooks', 'hooks',
'bootbox', 'bootbox',
@ -17,10 +18,9 @@ define('forum/chats', [
'api', 'api',
'uploadHelpers', 'uploadHelpers',
], function ( ], function (
components, mousetrap, components, mousetrap, recentChats, create,
recentChats, create, manage, messages, manage, messages, userList, messageSearch, autocomplete,
userList, autocomplete, hooks, bootbox, hooks, bootbox, alerts, chatModule, api, uploadHelpers
alerts, chatModule, api, uploadHelpers
) { ) {
const Chats = { const Chats = {
initialised: false, initialised: false,
@ -62,8 +62,8 @@ define('forum/chats', [
} }
Chats.initialised = true; Chats.initialised = true;
messages.scrollToBottom($('.expanded-chat ul.chat-content')); messages.scrollToBottom($('[component="chat/message/content"]'));
messages.wrapImagesInLinks($('.expanded-chat ul.chat-content')); messages.wrapImagesInLinks($('[component="chat/message/content"]'));
create.init(); create.init();
hooks.fire('action:chat.loaded', $('.chats-full')); hooks.fire('action:chat.loaded', $('.chats-full'));
@ -80,8 +80,8 @@ define('forum/chats', [
Chats.addRenameHandler(roomId, chatControls.find('[data-action="rename"]')); Chats.addRenameHandler(roomId, chatControls.find('[data-action="rename"]'));
Chats.addLeaveHandler(roomId, chatControls.find('[data-action="leave"]')); Chats.addLeaveHandler(roomId, chatControls.find('[data-action="leave"]'));
Chats.addDeleteHandler(roomId, chatControls.find('[data-action="delete"]')); Chats.addDeleteHandler(roomId, chatControls.find('[data-action="delete"]'));
Chats.addScrollHandler(roomId, ajaxify.data.uid, $('.chat-content')); Chats.addScrollHandler(roomId, ajaxify.data.uid, $('[component="chat/message/content"]'));
Chats.addScrollBottomHandler($('.chat-content')); Chats.addScrollBottomHandler($('[component="chat/message/content"]'));
Chats.addCharactersLeftHandler(mainWrapper); Chats.addCharactersLeftHandler(mainWrapper);
Chats.addTextareaResizeHandler(mainWrapper); Chats.addTextareaResizeHandler(mainWrapper);
Chats.addIPHandler(mainWrapper); Chats.addIPHandler(mainWrapper);
@ -98,6 +98,7 @@ define('forum/chats', [
Chats.switchChat(); Chats.switchChat();
}); });
userList.init(roomId, mainWrapper); userList.init(roomId, mainWrapper);
messageSearch.init(roomId);
Chats.addPublicRoomSortHandler(); Chats.addPublicRoomSortHandler();
Chats.addTooltipHandler(); Chats.addTooltipHandler();
Chats.addNotificationSettingHandler(); Chats.addNotificationSettingHandler();
@ -268,24 +269,24 @@ define('forum/chats', [
// https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize // https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize
const textarea = parent.find('[component="chat/input"]'); const textarea = parent.find('[component="chat/input"]');
textarea.on('input', function () { textarea.on('input', function () {
const isAtBottom = messages.isAtBottom(parent.find('.chat-content')); const isAtBottom = messages.isAtBottom(parent.find('[component="chat/message/content"]'));
textarea.css({ height: 0 }); textarea.css({ height: 0 });
textarea.css({ height: messages.calcAutoTextAreaHeight(textarea) + 'px' }); textarea.css({ height: messages.calcAutoTextAreaHeight(textarea) + 'px' });
if (isAtBottom) { if (isAtBottom) {
messages.scrollToBottom(parent.find('.chat-content')); messages.scrollToBottom(parent.find('[component="chat/message/content"]'));
} }
}); });
}; };
Chats.addActionHandlers = function (element, roomId) { Chats.addActionHandlers = function (element, roomId) {
element.on('click', '[data-mid] [data-action]', function () { element.on('click', '[data-mid] [data-action]', function () {
const messageId = $(this).parents('[data-mid]').attr('data-mid'); const msgEl = $(this).parents('[data-mid]');
const messageId = msgEl.attr('data-mid');
const action = this.getAttribute('data-action'); const action = this.getAttribute('data-action');
switch (action) { switch (action) {
case 'edit': { case 'edit': {
const inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]'); messages.prepEdit(msgEl, messageId, roomId);
messages.prepEdit(inputEl, messageId, roomId);
break; break;
} }
case 'delete': case 'delete':
@ -509,7 +510,7 @@ define('forum/chats', [
Chats.setActive(roomId); Chats.setActive(roomId);
Chats.addEventListeners(); Chats.addEventListeners();
hooks.fire('action:chat.loaded', $('.chats-full')); hooks.fire('action:chat.loaded', $('.chats-full'));
messages.scrollToBottom(mainWrapper.find('.expanded-chat ul.chat-content')); messages.scrollToBottom(mainWrapper.find('[component="chat/message/content"]'));
if (history.pushState) { if (history.pushState) {
history.pushState({ history.pushState({
url: url, url: url,
@ -543,7 +544,7 @@ define('forum/chats', [
data.message.self = data.self; data.message.self = data.self;
data.message.timestamp = Math.min(Date.now(), data.message.timestamp); data.message.timestamp = Math.min(Date.now(), data.message.timestamp);
data.message.timestampISO = utils.toISOString(data.message.timestamp); data.message.timestampISO = utils.toISOString(data.message.timestamp);
messages.appendChatMessage($('.expanded-chat .chat-content'), data.message); messages.appendChatMessage($('[component="chat/message/content"]'), data.message);
} }
}); });

@ -2,8 +2,8 @@
define('forum/chats/create', [ define('forum/chats/create', [
'components', 'api', 'alerts', 'forum/chats/search', 'components', 'api', 'alerts', 'forum/chats/user-search',
], function (components, api, alerts, search) { ], function (components, api, alerts, userSearch) {
const create = {}; const create = {};
create.init = function () { create.init = function () {
components.get('chat/create').on('click', handleCreate); components.get('chat/create').on('click', handleCreate);
@ -65,7 +65,7 @@ define('forum/chats/create', [
const chatRoomUsersList = modal.find('[component="chat/room/users"]'); const chatRoomUsersList = modal.find('[component="chat/room/users"]');
search.init({ userSearch.init({
onSelect: async function (user) { onSelect: async function (user) {
const html = await app.parseAndTranslate('modals/create-room', 'selectedUsers', { selectedUsers: [user] }); const html = await app.parseAndTranslate('modals/create-room', 'selectedUsers', { selectedUsers: [user] });
chatRoomUsersList.append(html); chatRoomUsersList.append(html);

@ -0,0 +1,82 @@
'use strict';
define('forum/chats/message-search', [
'components', 'alerts', 'forum/chats/messages',
], function (components, alerts, messages) {
const messageSearch = {};
let roomId = 0;
let resultListEl;
let chatContent;
let clearEl;
messageSearch.init = function (_roomId) {
roomId = _roomId;
const searchInput = $('[component="chat/room/search"]');
searchInput.on('keyup', utils.debounce(doSearch, 250))
.on('focus', () => {
if (searchInput.val()) {
doSearch();
}
});
resultListEl = $('[component="chat/message/search/results"]');
chatContent = $('[component="chat/message/content"]');
clearEl = $('[component="chat/room/search/clear"]');
$('[component="chat/input"]').on('focus', () => {
resultListEl.addClass('hidden');
chatContent.removeClass('hidden');
});
clearEl.on('click', clearInputAndResults);
};
function clearInputAndResults() {
components.get('chat/room/search').val('');
removeResults();
resultListEl.addClass('hidden');
chatContent.removeClass('hidden');
clearEl.addClass('hidden');
}
async function doSearch() {
const query = components.get('chat/room/search').val();
if (!query) {
return clearInputAndResults();
}
if (query.length <= 2) {
return;
}
clearEl.removeClass('hidden');
socket.emit('modules.chats.searchMessages', {
content: query,
roomId: roomId,
}).then(displayResults)
.catch(alerts.error);
}
function removeResults() {
resultListEl.children('[data-mid]').remove();
}
async function displayResults(data) {
removeResults();
if (!data.length) {
resultListEl.removeClass('hidden');
chatContent.addClass('hidden');
return resultListEl.find('[component="chat/message/search/no-results"]').removeClass('hidden');
}
resultListEl.find('[component="chat/message/search/no-results"]').addClass('hidden');
const html = await app.parseAndTranslate('partials/chats/messages', {
messages: data,
isAdminOrGlobalMod: app.user.isAdmin || app.user.isGlobalMod,
});
resultListEl.append(html);
messages.onMessagesAddedToDom(resultListEl.find('[component="chat/message"]'));
chatContent.addClass('hidden');
resultListEl.removeClass('hidden');
}
return messageSearch;
});

@ -108,14 +108,15 @@ define('forum/chats/messages', [
messages.onMessagesAddedToDom = function (messageEls) { messages.onMessagesAddedToDom = function (messageEls) {
messageEls.find('.timeago').timeago(); messageEls.find('.timeago').timeago();
messageEls.find('img:not(.not-responsive)').addClass('img-fluid'); messageEls.find('img:not(.not-responsive)').addClass('img-fluid');
messages.wrapImagesInLinks(messageEls.first().parent()); messageEls.find('img:not(.emoji)').each(function () {
images.wrapImageInLink($(this));
});
}; };
messages.parseMessage = function (data, callback) { messages.parseMessage = function (data, callback) {
const tplData = { const tplData = {
messages: data, messages: data,
isAdminOrGlobalMod: app.user.isAdmin || app.user.isGlobalMod, isAdminOrGlobalMod: app.user.isAdmin || app.user.isGlobalMod,
}; };
if (Array.isArray(data)) { if (Array.isArray(data)) {
app.parseAndTranslate('partials/chats/messages', tplData).then(callback); app.parseAndTranslate('partials/chats/messages', tplData).then(callback);
@ -155,14 +156,14 @@ define('forum/chats/messages', [
.toggleClass('hidden', isAtBottom); .toggleClass('hidden', isAtBottom);
}; };
messages.prepEdit = async function (inputEl, mid, roomId) { messages.prepEdit = async function (msgEl, mid, roomId) {
const raw = await socket.emit('modules.chats.getRaw', { mid: mid, roomId: roomId }); const raw = await socket.emit('modules.chats.getRaw', { mid: mid, roomId: roomId });
const editEl = await app.parseAndTranslate('partials/chats/edit-message', { const editEl = await app.parseAndTranslate('partials/chats/edit-message', {
rawContent: raw, rawContent: raw,
}); });
const messageBody = $(`[data-roomid="${roomId}"] [data-mid="${mid}"] [component="chat/message/body"]`); const messageBody = msgEl.find(`[component="chat/message/body"]`);
const messageControls = $(`[data-roomid="${roomId}"] [data-mid="${mid}"] [component="chat/message/controls"]`); const messageControls = msgEl.find(`[component="chat/message/controls"]`);
const chatContent = messageBody.parents('.chat-content'); const chatContent = messageBody.parents('[component="chat/message/content"]');
messageBody.addClass('hidden'); messageBody.addClass('hidden');
messageControls.addClass('hidden'); messageControls.addClass('hidden');
@ -173,7 +174,7 @@ define('forum/chats/messages', [
textarea.focus().putCursorAtEnd(); textarea.focus().putCursorAtEnd();
autoresizeTextArea(textarea); autoresizeTextArea(textarea);
if (messages.isAtBottom(chatContent)) { if (chatContent.length && messages.isAtBottom(chatContent)) {
messages.scrollToBottom(chatContent); messages.scrollToBottom(chatContent);
} }
@ -212,7 +213,7 @@ define('forum/chats/messages', [
}); });
hooks.fire('action:chat.prepEdit', { hooks.fire('action:chat.prepEdit', {
inputEl: inputEl, msgEl: msgEl,
messageId: mid, messageId: mid,
roomId: roomId, roomId: roomId,
editEl: editEl, editEl: editEl,
@ -236,10 +237,10 @@ define('forum/chats/messages', [
const self = parseInt(message.fromuid, 10) === parseInt(app.user.uid, 10); const self = parseInt(message.fromuid, 10) === parseInt(app.user.uid, 10);
message.self = self ? 1 : 0; message.self = self ? 1 : 0;
messages.parseMessage(message, function (html) { messages.parseMessage(message, function (html) {
const body = components.get('chat/message', message.messageId); const msgEl = components.get('chat/message', message.mid);
if (body.length) { if (msgEl.length) {
body.replaceWith(html); msgEl.replaceWith(html);
messages.onMessagesAddedToDom(html); messages.onMessagesAddedToDom(components.get('chat/message', message.mid));
} }
}); });
}); });

@ -1,13 +1,13 @@
'use strict'; 'use strict';
define('forum/chats/search', [ define('forum/chats/user-search', [
'components', 'api', 'alerts', 'components', 'api', 'alerts',
], function (components, api, alerts) { ], function (components, api, alerts) {
const search = {}; const userSearch = {};
let users = []; let users = [];
search.init = function (options) { userSearch.init = function (options) {
options = options || {}; options = options || {};
users.length = 0; users.length = 0;
components.get('chat/search').on('keyup', utils.debounce(doSearch, 250)); components.get('chat/search').on('keyup', utils.debounce(doSearch, 250));
@ -65,5 +65,5 @@ define('forum/chats/search', [
chatsListEl.parent().toggleClass('show', true); chatsListEl.parent().toggleClass('show', true);
} }
return search; return userSearch;
}); });

@ -6,6 +6,7 @@ const db = require('../database');
const Messaging = require('../messaging'); const Messaging = require('../messaging');
const utils = require('../utils'); const utils = require('../utils');
const user = require('../user'); const user = require('../user');
const plugins = require('../plugins');
const privileges = require('../privileges'); const privileges = require('../privileges');
const groups = require('../groups'); const groups = require('../groups');
@ -213,4 +214,41 @@ SocketModules.chats.setNotificationSetting = async (socket, data) => {
await Messaging.setUserNotificationSetting(socket.uid, data.roomId, data.value); await Messaging.setUserNotificationSetting(socket.uid, data.roomId, data.value);
}; };
SocketModules.chats.searchMessages = async (socket, data) => {
if (!data || !utils.isNumber(data.roomId) || !data.content) {
throw new Error('[[error:invalid-data]]');
}
const [roomData, inRoom] = await Promise.all([
Messaging.getRoomData(data.roomId),
Messaging.isUserInRoom(socket.uid, data.roomId),
]);
if (!roomData) {
throw new Error('[[error:no-room]]');
}
if (!inRoom) {
throw new Error('[[error:no-privileges]]');
}
const { ids } = await plugins.hooks.fire('filter:messaging.searchMessages', {
content: data.content,
roomId: [data.roomId],
uid: [data.uid],
matchWords: 'any',
ids: [],
});
let userjoinTimestamp = 0;
if (!roomData.public) {
userjoinTimestamp = await db.sortedSetScore(`chat:room:${data.roomId}:uids`, socket.uid);
}
const messageData = await Messaging.getMessagesData(ids, socket.uid, data.roomId, false);
messageData.forEach((msg) => {
if (msg) {
msg.newSet = true;
}
});
return messageData.filter(msg => msg && !msg.deleted && msg.timestamp > userjoinTimestamp);
};
require('../promisify')(SocketModules); require('../promisify')(SocketModules);

@ -154,7 +154,7 @@ describe('Messaging Library', () => {
const { body } = await callv3API('get', `/chats/${roomId}`, {}, 'foo'); const { body } = await callv3API('get', `/chats/${roomId}`, {}, 'foo');
const { messages } = body.response; const { messages } = body.response;
assert.equal(messages.length, 2); assert.equal(messages.length, 2);
assert.strictEqual(messages[0].system, true); assert.strictEqual(messages[0].system, 1);
assert.strictEqual(messages[0].content, 'user-join'); assert.strictEqual(messages[0].content, 'user-join');
const { statusCode, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, { const { statusCode, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, {
@ -233,7 +233,7 @@ describe('Messaging Library', () => {
const { body } = await callv3API('get', `/chats/${roomId}`, {}, 'foo'); const { body } = await callv3API('get', `/chats/${roomId}`, {}, 'foo');
const { messages } = body.response; const { messages } = body.response;
const message = messages.pop(); const message = messages.pop();
assert.strictEqual(message.system, true); assert.strictEqual(message.system, 1);
assert.strictEqual(message.content, 'user-leave'); assert.strictEqual(message.content, 'user-leave');
}); });
@ -244,12 +244,12 @@ describe('Messaging Library', () => {
assert.equal(messages.length, 4); assert.equal(messages.length, 4);
let message = messages.pop(); let message = messages.pop();
assert.strictEqual(message.system, true); assert.strictEqual(message.system, 1);
assert.strictEqual(message.content, 'user-leave'); assert.strictEqual(message.content, 'user-leave');
// The message before should still be a user-join // The message before should still be a user-join
message = messages.pop(); message = messages.pop();
assert.strictEqual(message.system, true); assert.strictEqual(message.system, 1);
assert.strictEqual(message.content, 'user-join'); assert.strictEqual(message.content, 'user-join');
}); });
@ -466,7 +466,7 @@ describe('Messaging Library', () => {
const { messages } = body.response; const { messages } = body.response;
const message = messages.pop(); const message = messages.pop();
assert.strictEqual(message.system, true); assert.strictEqual(message.system, 1);
assert.strictEqual(message.content, 'room-rename, new room name'); assert.strictEqual(message.content, 'room-rename, new room name');
}); });

Loading…
Cancel
Save