nodebb-plugin-admin-chats 3.4.1 → 3.4.3

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.
Files changed (3) hide show
  1. package/client.js +55 -28
  2. package/library.js +20 -11
  3. package/package.json +1 -1
package/client.js CHANGED
@@ -230,38 +230,64 @@ $(document).ready(function() {
230
230
  }
231
231
 
232
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
+ // Fire through the hooks module like core does — a raw jQuery
248
+ // trigger never reaches hooks.on() listeners (e.g. the reactions
249
+ // plugin), only legacy $(window).on() ones (e.g. the emoji plugin).
250
+ // hooks.fire invokes both kinds and passes the container.
251
+ require(['hooks'], function(hooks) {
252
+ hooks.fire('action:chat.loaded', $('.chats-full'));
253
+ });
254
+ if (roomId) {
255
+ ChatsMessages.scrollToBottomAfterImageLoad(mainWrapper.find('[component="chat/message/content"]'));
256
+ // Core only marks a room read when its list element carries the
257
+ // "unread" class — the admin list doesn't, so mark it explicitly
258
+ require(['api'], function(api) {
259
+ api.del(`/chats/${roomId}/state`, {}).catch(function() {});
260
+ });
261
+ $('[component="chat/nav-wrapper"]').find(`[data-roomid="${roomId}"]`).removeClass('unread');
262
+ }
263
+ if (history.pushState) {
264
+ const fullUrl = `${window.location.protocol}//${window.location.host}${config.relative_path || ''}/${url}`;
265
+ // When the address bar already shows the target (e.g. closing a chat
266
+ // after a back-gesture), replace instead of stacking a duplicate entry
267
+ const sameUrl = window.location.href.split('?')[0] === fullUrl.split('?')[0];
268
+ history[sameUrl ? 'replaceState' : 'pushState']({ url: url }, null, fullUrl);
269
+ }
270
+ bindAdminRecentChatsInfiniteScroll();
271
+ }
272
+
273
+ if (!roomId) {
274
+ // Closing a chat needs no server round-trip — render the empty
275
+ // window from the data we already have (instant back-gesture)
276
+ renderAdminChatWindow({ ...ajaxify.data, roomId: 0, title: '[[pages:chats]]' });
277
+ return;
278
+ }
279
+
233
280
  fetch(getAdminChatsDataUrl(roomId), { credentials: 'include' })
234
281
  .then(async function(response) {
235
282
  if (!response.ok) {
236
283
  throw new Error(`Received ${response.status}`);
237
284
  }
238
-
239
- const payload = await response.json();
240
- const html = await app.parseAndTranslate('partials/chats/message-window', payload);
241
- const mainWrapper = $('[component="chat/main-wrapper"]');
242
- mainWrapper.html(html);
243
- mainWrapper.attr('data-roomid', roomId);
244
- html.find('.timeago').timeago();
245
- ajaxify.data = { ...ajaxify.data, ...payload, roomId: roomId };
246
- ajaxify.updateTitle(ajaxify.data.title);
247
- $('body').toggleClass('chat-loaded', !!roomId);
248
- mainWrapper.find('[data-bs-toggle="tooltip"]').tooltip({ trigger: 'hover', container: '#content' });
249
- Chats.setActive(roomId);
250
- Chats.addEventListeners();
251
- // Pass the container like core does — listeners (e.g. the emoji
252
- // plugin's chat button) resolve elements relative to it
253
- $(window).trigger('action:chat.loaded', $('.chats-full'));
254
- if (roomId) {
255
- ChatsMessages.scrollToBottomAfterImageLoad(mainWrapper.find('[component="chat/message/content"]'));
256
- }
257
- if (history.pushState) {
258
- const fullUrl = `${window.location.protocol}//${window.location.host}${config.relative_path || ''}/${url}`;
259
- // When the address bar already shows the target (e.g. closing a chat
260
- // after a back-gesture), replace instead of stacking a duplicate entry
261
- const sameUrl = window.location.href.split('?')[0] === fullUrl.split('?')[0];
262
- history[sameUrl ? 'replaceState' : 'pushState']({ url: url }, null, fullUrl);
263
- }
264
- bindAdminRecentChatsInfiniteScroll();
285
+ await renderAdminChatWindow(await response.json());
286
+ })
287
+ .catch(function() {
288
+ // Never leave the user stuck on the list — fall back to a
289
+ // regular ajaxify navigation to the room
290
+ ajaxify.go(url);
265
291
  });
266
292
  };
267
293
 
@@ -375,7 +401,8 @@ $(document).ready(function() {
375
401
 
376
402
  // Try admin endpoint first if user has access
377
403
  if (hasAdminChatsAccess()) {
378
- const adminEndpoint = `${config.relative_path || ''}/api/admin-chats/page/${roomId}`;
404
+ // switch=1: room data only — no need to rebuild the full room list here
405
+ const adminEndpoint = `${config.relative_path || ''}/api/admin-chats/page/${roomId}?switch=1`;
379
406
  try {
380
407
  const adminResponse = await fetch(adminEndpoint, {
381
408
  headers: {
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-admin-chats",
3
- "version": "3.4.1",
3
+ "version": "3.4.3",
4
4
  "description": "A plugin that allows the administrator to view and manage user chats.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {