nodebb-plugin-admin-chats 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +89 -49
- package/library.js +20 -11
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -223,54 +223,92 @@ $(document).ready(function() {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
roomId = roomId || '';
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
// NodeBB 4.14.9+ (56c6115a61): destroyAutoComplete takes the input element, not a roomId
|
|
227
|
+
Chats.destroyAutoComplete($('[component="chat/main-wrapper"]').find('[component="chat/input"]'));
|
|
228
|
+
if (ajaxify.data.roomId) {
|
|
229
|
+
socket.emit('modules.chats.leave', ajaxify.data.roomId);
|
|
230
|
+
}
|
|
228
231
|
|
|
229
232
|
const url = getAdminChatsPageUrl(roomId);
|
|
233
|
+
|
|
234
|
+
async function renderAdminChatWindow(payload) {
|
|
235
|
+
const html = await app.parseAndTranslate('partials/chats/message-window', payload);
|
|
236
|
+
const mainWrapper = $('[component="chat/main-wrapper"]');
|
|
237
|
+
mainWrapper.html(html);
|
|
238
|
+
mainWrapper.attr('data-roomid', roomId);
|
|
239
|
+
html.find('.timeago').timeago();
|
|
240
|
+
ajaxify.data = { ...ajaxify.data, ...payload, roomId: roomId };
|
|
241
|
+
ajaxify.updateTitle(ajaxify.data.title);
|
|
242
|
+
$('body').toggleClass('chat-loaded', !!roomId);
|
|
243
|
+
$('[component="chat/nav-wrapper"]').attr('data-loaded', roomId ? '1' : '0');
|
|
244
|
+
mainWrapper.find('[data-bs-toggle="tooltip"]').tooltip({ trigger: 'hover', container: '#content' });
|
|
245
|
+
Chats.setActive(roomId);
|
|
246
|
+
Chats.addEventListeners();
|
|
247
|
+
// Pass the container like core does — listeners (e.g. the emoji
|
|
248
|
+
// plugin's chat button) resolve elements relative to it
|
|
249
|
+
$(window).trigger('action:chat.loaded', $('.chats-full'));
|
|
250
|
+
if (roomId) {
|
|
251
|
+
ChatsMessages.scrollToBottomAfterImageLoad(mainWrapper.find('[component="chat/message/content"]'));
|
|
252
|
+
}
|
|
253
|
+
if (history.pushState) {
|
|
254
|
+
const fullUrl = `${window.location.protocol}//${window.location.host}${config.relative_path || ''}/${url}`;
|
|
255
|
+
// When the address bar already shows the target (e.g. closing a chat
|
|
256
|
+
// after a back-gesture), replace instead of stacking a duplicate entry
|
|
257
|
+
const sameUrl = window.location.href.split('?')[0] === fullUrl.split('?')[0];
|
|
258
|
+
history[sameUrl ? 'replaceState' : 'pushState']({ url: url }, null, fullUrl);
|
|
259
|
+
}
|
|
260
|
+
bindAdminRecentChatsInfiniteScroll();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!roomId) {
|
|
264
|
+
// Closing a chat needs no server round-trip — render the empty
|
|
265
|
+
// window from the data we already have (instant back-gesture)
|
|
266
|
+
renderAdminChatWindow({ ...ajaxify.data, roomId: 0, title: '[[pages:chats]]' });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
230
270
|
fetch(getAdminChatsDataUrl(roomId), { credentials: 'include' })
|
|
231
271
|
.then(async function(response) {
|
|
232
272
|
if (!response.ok) {
|
|
233
273
|
throw new Error(`Received ${response.status}`);
|
|
234
274
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
html.find('.timeago').timeago();
|
|
242
|
-
ajaxify.data = { ...ajaxify.data, ...payload, roomId: roomId };
|
|
243
|
-
ajaxify.updateTitle(ajaxify.data.title);
|
|
244
|
-
$('body').toggleClass('chat-loaded', !!roomId);
|
|
245
|
-
mainWrapper.find('[data-bs-toggle="tooltip"]').tooltip({ trigger: 'hover', container: '#content' });
|
|
246
|
-
Chats.setActive(roomId);
|
|
247
|
-
Chats.addEventListeners();
|
|
248
|
-
$(window).trigger('action:chat.loaded');
|
|
249
|
-
if (roomId) {
|
|
250
|
-
ChatsMessages.scrollToBottomAfterImageLoad(mainWrapper.find('[component="chat/message/content"]'));
|
|
251
|
-
}
|
|
252
|
-
if (history.pushState) {
|
|
253
|
-
history.pushState({ url: url }, null, `${window.location.protocol}//${window.location.host}${config.relative_path || ''}/${url}`);
|
|
254
|
-
}
|
|
255
|
-
bindAdminRecentChatsInfiniteScroll();
|
|
275
|
+
await renderAdminChatWindow(await response.json());
|
|
276
|
+
})
|
|
277
|
+
.catch(function() {
|
|
278
|
+
// Never leave the user stuck on the list — fall back to a
|
|
279
|
+
// regular ajaxify navigation to the room
|
|
280
|
+
ajaxify.go(url);
|
|
256
281
|
});
|
|
257
282
|
};
|
|
258
283
|
|
|
259
284
|
Chats._adminAllChatsPatched = true;
|
|
260
285
|
}
|
|
261
286
|
|
|
262
|
-
// Auto-open chat if roomId is specified in URL
|
|
287
|
+
// Auto-open chat if roomId is specified in URL and the room is not
|
|
288
|
+
// already rendered (server-side render sets data-roomid on the wrapper —
|
|
289
|
+
// re-switching in that case caused a visible list->chat flash on mobile)
|
|
263
290
|
const currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
264
|
-
|
|
291
|
+
const activeChatId = $('[component="chat/main-wrapper"]').attr('data-roomid') || '';
|
|
292
|
+
if (currentRoomId && String(currentRoomId) !== String(activeChatId)) {
|
|
265
293
|
setTimeout(function() {
|
|
266
294
|
Chats.switchChat(currentRoomId);
|
|
267
295
|
}, 100);
|
|
296
|
+
} else {
|
|
297
|
+
syncChatLoadedState();
|
|
268
298
|
}
|
|
269
299
|
|
|
270
300
|
bindAdminRecentChatsInfiniteScroll();
|
|
271
301
|
});
|
|
272
302
|
}
|
|
273
303
|
|
|
304
|
+
// Keep the mobile layout state (body.chat-loaded / nav data-loaded) in sync with
|
|
305
|
+
// the room that is actually rendered, without re-fetching it
|
|
306
|
+
function syncChatLoadedState() {
|
|
307
|
+
const activeChatId = parseInt($('[component="chat/main-wrapper"]').attr('data-roomid'), 10) || 0;
|
|
308
|
+
$('body').toggleClass('chat-loaded', !!activeChatId);
|
|
309
|
+
$('[component="chat/nav-wrapper"]').attr('data-loaded', activeChatId ? '1' : '0');
|
|
310
|
+
}
|
|
311
|
+
|
|
274
312
|
function replaceAdminEmptyStateText() {
|
|
275
313
|
if (!hasAdminChatsAccess()) {
|
|
276
314
|
return;
|
|
@@ -353,7 +391,8 @@ $(document).ready(function() {
|
|
|
353
391
|
|
|
354
392
|
// Try admin endpoint first if user has access
|
|
355
393
|
if (hasAdminChatsAccess()) {
|
|
356
|
-
|
|
394
|
+
// switch=1: room data only — no need to rebuild the full room list here
|
|
395
|
+
const adminEndpoint = `${config.relative_path || ''}/api/admin-chats/page/${roomId}?switch=1`;
|
|
357
396
|
try {
|
|
358
397
|
const adminResponse = await fetch(adminEndpoint, {
|
|
359
398
|
headers: {
|
|
@@ -675,9 +714,10 @@ $(document).ready(function() {
|
|
|
675
714
|
patchForumChatsForAdminAll();
|
|
676
715
|
bindAdminRecentChatsInfiniteScroll();
|
|
677
716
|
|
|
678
|
-
// Auto-open chat if roomId is specified in URL and
|
|
717
|
+
// Auto-open chat if roomId is specified in URL and that room is not already rendered
|
|
679
718
|
const currentRoomId = ajaxify && ajaxify.data && ajaxify.data.roomId;
|
|
680
|
-
|
|
719
|
+
const activeChatId = $('[component="chat/main-wrapper"]').attr('data-roomid') || '';
|
|
720
|
+
if (currentRoomId && String(currentRoomId) !== String(activeChatId)) {
|
|
681
721
|
setTimeout(function() {
|
|
682
722
|
require(['forum/chats'], function(Chats) {
|
|
683
723
|
if (Chats && Chats.switchChat) {
|
|
@@ -685,6 +725,8 @@ $(document).ready(function() {
|
|
|
685
725
|
}
|
|
686
726
|
});
|
|
687
727
|
}, 200);
|
|
728
|
+
} else {
|
|
729
|
+
syncChatLoadedState();
|
|
688
730
|
}
|
|
689
731
|
}
|
|
690
732
|
|
|
@@ -718,6 +760,8 @@ $(document).ready(function() {
|
|
|
718
760
|
}
|
|
719
761
|
});
|
|
720
762
|
}, 100);
|
|
763
|
+
} else {
|
|
764
|
+
syncChatLoadedState();
|
|
721
765
|
}
|
|
722
766
|
}
|
|
723
767
|
refreshChatUi();
|
|
@@ -776,32 +820,28 @@ $(document).ready(function() {
|
|
|
776
820
|
}
|
|
777
821
|
});
|
|
778
822
|
|
|
779
|
-
// Handle URL changes for internal navigation
|
|
823
|
+
// Handle URL changes for internal navigation (including back/forward gestures).
|
|
824
|
+
// The URL is the source of truth here — ajaxify.data.roomId is stale after popstate.
|
|
780
825
|
function handleChatNavigation() {
|
|
781
826
|
if (!isAdminAllChatsPage()) {
|
|
782
827
|
return;
|
|
783
828
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if (
|
|
790
|
-
|
|
791
|
-
if (urlMatch) {
|
|
792
|
-
currentRoomId = parseInt(urlMatch[1], 10);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
const activeChatId = $('[component="chat/main-wrapper"]').attr('data-roomid');
|
|
797
|
-
|
|
798
|
-
if (currentRoomId && (!activeChatId || activeChatId !== String(currentRoomId))) {
|
|
799
|
-
require(['forum/chats'], function(Chats) {
|
|
800
|
-
if (Chats && Chats.switchChat) {
|
|
801
|
-
Chats.switchChat(currentRoomId);
|
|
802
|
-
}
|
|
803
|
-
});
|
|
829
|
+
|
|
830
|
+
const urlMatch = window.location.pathname.match(/\/chats\/(\d+)/);
|
|
831
|
+
const urlRoomId = urlMatch ? parseInt(urlMatch[1], 10) : 0;
|
|
832
|
+
const activeChatId = parseInt($('[component="chat/main-wrapper"]').attr('data-roomid'), 10) || 0;
|
|
833
|
+
|
|
834
|
+
if (urlRoomId === activeChatId) {
|
|
835
|
+
return;
|
|
804
836
|
}
|
|
837
|
+
|
|
838
|
+
require(['forum/chats'], function(Chats) {
|
|
839
|
+
if (Chats && Chats.switchChat) {
|
|
840
|
+
// urlRoomId 0 -> URL is the chat list: close the open chat and show the list
|
|
841
|
+
// (previously a back-gesture out of a chat left the chat stuck on screen)
|
|
842
|
+
Chats.switchChat(urlRoomId || '');
|
|
843
|
+
}
|
|
844
|
+
});
|
|
805
845
|
}
|
|
806
846
|
|
|
807
847
|
$(document).on('click', '[data-action="delete"], [data-action="restore"]', function() {
|
package/library.js
CHANGED
|
@@ -847,7 +847,9 @@ function registerRoutes(app, router, middleware) {
|
|
|
847
847
|
return;
|
|
848
848
|
}
|
|
849
849
|
|
|
850
|
-
const payload = await buildAdminChatsPayload(req
|
|
850
|
+
const payload = await buildAdminChatsPayload(req, {
|
|
851
|
+
isSwitch: parseInt(req.query.switch, 10) === 1,
|
|
852
|
+
});
|
|
851
853
|
res.json(payload);
|
|
852
854
|
} catch (err) {
|
|
853
855
|
res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
@@ -1115,7 +1117,11 @@ async function assertAdminChatsAccess(req, res) {
|
|
|
1115
1117
|
return true;
|
|
1116
1118
|
}
|
|
1117
1119
|
|
|
1118
|
-
async function buildAdminChatsPayload(req) {
|
|
1120
|
+
async function buildAdminChatsPayload(req, options = {}) {
|
|
1121
|
+
// Like core (controllers/accounts/chats.js): when the client only switches
|
|
1122
|
+
// rooms (?switch=1 on the JSON endpoint), skip rebuilding the room list —
|
|
1123
|
+
// scanning every chat room on each switch made room-opening slow on mobile
|
|
1124
|
+
const isSwitch = !!options.isSwitch;
|
|
1119
1125
|
const userslug = await User.getUserField(req.uid, 'userslug');
|
|
1120
1126
|
const [isAdmin, canView, canManage] = await Promise.all([
|
|
1121
1127
|
User.isAdministrator(req.uid),
|
|
@@ -1124,11 +1130,6 @@ async function buildAdminChatsPayload(req) {
|
|
|
1124
1130
|
]);
|
|
1125
1131
|
const adminChatsManage = isAdmin || canManage;
|
|
1126
1132
|
const adminChatsAccess = isAdmin || canView || canManage;
|
|
1127
|
-
const [recentChats, publicRooms, privateRoomCount] = await Promise.all([
|
|
1128
|
-
getAdminRecentChats(req.uid, 0, ADMIN_CHAT_PAGE_SIZE),
|
|
1129
|
-
Messaging.getPublicRooms(req.uid, req.uid),
|
|
1130
|
-
getPrivateRoomCount(),
|
|
1131
|
-
]);
|
|
1132
1133
|
|
|
1133
1134
|
const payload = {
|
|
1134
1135
|
title: '[[pages:chats]]',
|
|
@@ -1138,13 +1139,21 @@ async function buildAdminChatsPayload(req) {
|
|
|
1138
1139
|
adminChatsAccess,
|
|
1139
1140
|
adminChatsIsAdmin: isAdmin,
|
|
1140
1141
|
adminChatsCanManage: adminChatsManage,
|
|
1141
|
-
rooms: recentChats.rooms,
|
|
1142
|
-
nextStart: recentChats.nextStart,
|
|
1143
|
-
publicRooms: publicRooms || [],
|
|
1144
|
-
privateRoomCount,
|
|
1145
1142
|
bodyClasses: ['page-user-chats'],
|
|
1146
1143
|
};
|
|
1147
1144
|
|
|
1145
|
+
if (!isSwitch) {
|
|
1146
|
+
const [recentChats, publicRooms, privateRoomCount] = await Promise.all([
|
|
1147
|
+
getAdminRecentChats(req.uid, 0, ADMIN_CHAT_PAGE_SIZE),
|
|
1148
|
+
Messaging.getPublicRooms(req.uid, req.uid),
|
|
1149
|
+
getPrivateRoomCount(),
|
|
1150
|
+
]);
|
|
1151
|
+
payload.rooms = recentChats.rooms;
|
|
1152
|
+
payload.nextStart = recentChats.nextStart;
|
|
1153
|
+
payload.publicRooms = publicRooms || [];
|
|
1154
|
+
payload.privateRoomCount = privateRoomCount;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1148
1157
|
const roomId = parseInt(req.params.roomId, 10) || 0;
|
|
1149
1158
|
if (!roomId) {
|
|
1150
1159
|
return payload;
|