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",
"nodebb-plugin-2factor": "7.1.3",
"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-android": "4.0.0",
"nodebb-plugin-markdown": "12.1.7",
@ -101,10 +101,10 @@
"nodebb-plugin-ntfy": "1.1.0",
"nodebb-plugin-spam-be-gone": "2.1.1",
"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-peace": "2.1.4",
"nodebb-theme-persona": "13.2.8",
"nodebb-theme-peace": "2.1.5",
"nodebb-theme-persona": "13.2.9",
"nodebb-widget-essentials": "7.0.13",
"nodemailer": "6.9.4",
"nprogress": "0.2.0",

@ -9,6 +9,7 @@ define('forum/chats', [
'forum/chats/manage',
'forum/chats/messages',
'forum/chats/user-list',
'forum/chats/message-search',
'composer/autocomplete',
'hooks',
'bootbox',
@ -17,10 +18,9 @@ define('forum/chats', [
'api',
'uploadHelpers',
], function (
components, mousetrap,
recentChats, create, manage, messages,
userList, autocomplete, hooks, bootbox,
alerts, chatModule, api, uploadHelpers
components, mousetrap, recentChats, create,
manage, messages, userList, messageSearch, autocomplete,
hooks, bootbox, alerts, chatModule, api, uploadHelpers
) {
const Chats = {
initialised: false,
@ -62,8 +62,8 @@ define('forum/chats', [
}
Chats.initialised = true;
messages.scrollToBottom($('.expanded-chat ul.chat-content'));
messages.wrapImagesInLinks($('.expanded-chat ul.chat-content'));
messages.scrollToBottom($('[component="chat/message/content"]'));
messages.wrapImagesInLinks($('[component="chat/message/content"]'));
create.init();
hooks.fire('action:chat.loaded', $('.chats-full'));
@ -80,8 +80,8 @@ define('forum/chats', [
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, $('.chat-content'));
Chats.addScrollBottomHandler($('.chat-content'));
Chats.addScrollHandler(roomId, ajaxify.data.uid, $('[component="chat/message/content"]'));
Chats.addScrollBottomHandler($('[component="chat/message/content"]'));
Chats.addCharactersLeftHandler(mainWrapper);
Chats.addTextareaResizeHandler(mainWrapper);
Chats.addIPHandler(mainWrapper);
@ -98,6 +98,7 @@ define('forum/chats', [
Chats.switchChat();
});
userList.init(roomId, mainWrapper);
messageSearch.init(roomId);
Chats.addPublicRoomSortHandler();
Chats.addTooltipHandler();
Chats.addNotificationSettingHandler();
@ -268,24 +269,24 @@ define('forum/chats', [
// https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize
const textarea = parent.find('[component="chat/input"]');
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: messages.calcAutoTextAreaHeight(textarea) + 'px' });
if (isAtBottom) {
messages.scrollToBottom(parent.find('.chat-content'));
messages.scrollToBottom(parent.find('[component="chat/message/content"]'));
}
});
};
Chats.addActionHandlers = function (element, roomId) {
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');
switch (action) {
case 'edit': {
const inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]');
messages.prepEdit(inputEl, messageId, roomId);
messages.prepEdit(msgEl, messageId, roomId);
break;
}
case 'delete':
@ -509,7 +510,7 @@ define('forum/chats', [
Chats.setActive(roomId);
Chats.addEventListeners();
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) {
history.pushState({
url: url,
@ -543,7 +544,7 @@ define('forum/chats', [
data.message.self = data.self;
data.message.timestamp = Math.min(Date.now(), 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', [
'components', 'api', 'alerts', 'forum/chats/search',
], function (components, api, alerts, search) {
'components', 'api', 'alerts', 'forum/chats/user-search',
], function (components, api, alerts, userSearch) {
const create = {};
create.init = function () {
components.get('chat/create').on('click', handleCreate);
@ -65,7 +65,7 @@ define('forum/chats/create', [
const chatRoomUsersList = modal.find('[component="chat/room/users"]');
search.init({
userSearch.init({
onSelect: async function (user) {
const html = await app.parseAndTranslate('modals/create-room', 'selectedUsers', { selectedUsers: [user] });
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) {
messageEls.find('.timeago').timeago();
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) {
const tplData = {
messages: data,
isAdminOrGlobalMod: app.user.isAdmin || app.user.isGlobalMod,
};
if (Array.isArray(data)) {
app.parseAndTranslate('partials/chats/messages', tplData).then(callback);
@ -155,14 +156,14 @@ define('forum/chats/messages', [
.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 editEl = await app.parseAndTranslate('partials/chats/edit-message', {
rawContent: raw,
});
const messageBody = $(`[data-roomid="${roomId}"] [data-mid="${mid}"] [component="chat/message/body"]`);
const messageControls = $(`[data-roomid="${roomId}"] [data-mid="${mid}"] [component="chat/message/controls"]`);
const chatContent = messageBody.parents('.chat-content');
const messageBody = msgEl.find(`[component="chat/message/body"]`);
const messageControls = msgEl.find(`[component="chat/message/controls"]`);
const chatContent = messageBody.parents('[component="chat/message/content"]');
messageBody.addClass('hidden');
messageControls.addClass('hidden');
@ -173,7 +174,7 @@ define('forum/chats/messages', [
textarea.focus().putCursorAtEnd();
autoresizeTextArea(textarea);
if (messages.isAtBottom(chatContent)) {
if (chatContent.length && messages.isAtBottom(chatContent)) {
messages.scrollToBottom(chatContent);
}
@ -212,7 +213,7 @@ define('forum/chats/messages', [
});
hooks.fire('action:chat.prepEdit', {
inputEl: inputEl,
msgEl: msgEl,
messageId: mid,
roomId: roomId,
editEl: editEl,
@ -236,10 +237,10 @@ define('forum/chats/messages', [
const self = parseInt(message.fromuid, 10) === parseInt(app.user.uid, 10);
message.self = self ? 1 : 0;
messages.parseMessage(message, function (html) {
const body = components.get('chat/message', message.messageId);
if (body.length) {
body.replaceWith(html);
messages.onMessagesAddedToDom(html);
const msgEl = components.get('chat/message', message.mid);
if (msgEl.length) {
msgEl.replaceWith(html);
messages.onMessagesAddedToDom(components.get('chat/message', message.mid));
}
});
});

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

@ -6,6 +6,7 @@ const db = require('../database');
const Messaging = require('../messaging');
const utils = require('../utils');
const user = require('../user');
const plugins = require('../plugins');
const privileges = require('../privileges');
const groups = require('../groups');
@ -213,4 +214,41 @@ SocketModules.chats.setNotificationSetting = async (socket, data) => {
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);

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

Loading…
Cancel
Save