mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 11:35:55 +01:00
feat: #11850, chat msg search
This commit is contained in:
@@ -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);
|
||||
|
||||
82
public/src/client/chats/message-search.js
Normal file
82
public/src/client/chats/message-search.js
Normal file
@@ -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');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user