nodebb-plugin-admin-chats 1.1.0 → 2.1.0
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/.vscode/settings.json +3 -0
- package/client.js +398 -103
- package/library.js +503 -39
- package/package.json +1 -1
- package/plugin.json +66 -17
- package/public/languages/en-GB/admin-chats.json +9 -0
- package/public/languages/he/admin-chats.json +9 -0
package/client.js
CHANGED
|
@@ -1,150 +1,349 @@
|
|
|
1
1
|
$(document).ready(function() {
|
|
2
2
|
const LOCK_PREFIX = '[admin-chat-lock]';
|
|
3
|
-
const
|
|
3
|
+
const roomStateCache = new Map();
|
|
4
|
+
const TEXT = {
|
|
5
|
+
he: {
|
|
6
|
+
lockBanner: '🔒 חדר זה ננעל ע"י המנהלים.',
|
|
7
|
+
menuLock: 'נעל חדר',
|
|
8
|
+
menuRelease: 'שחרר נעילה',
|
|
9
|
+
updateError: 'לא ניתן לעדכן את נעילת החדר.',
|
|
10
|
+
viewChats: "צפיה בצ'אטים",
|
|
11
|
+
emptyState: "אנא בחר צ'אט מסרגל הצד.",
|
|
12
|
+
participants: 'משתתפים',
|
|
13
|
+
},
|
|
14
|
+
en: {
|
|
15
|
+
lockBanner: '🔒 This room was locked by the administrators.',
|
|
16
|
+
menuLock: 'Lock Room',
|
|
17
|
+
menuRelease: 'Release Lock',
|
|
18
|
+
updateError: 'Unable to update room lock.',
|
|
19
|
+
viewChats: 'View Chats',
|
|
20
|
+
emptyState: 'Please select a chat from the sidebar.',
|
|
21
|
+
participants: 'Participants',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
4
24
|
|
|
5
25
|
function isEnglishSystem() {
|
|
6
26
|
return $('html').attr('lang') && $('html').attr('lang').startsWith('en');
|
|
7
27
|
}
|
|
8
28
|
|
|
9
|
-
function
|
|
10
|
-
|
|
29
|
+
function t(key) {
|
|
30
|
+
const dict = isEnglishSystem() ? TEXT.en : TEXT.he;
|
|
31
|
+
return dict[key];
|
|
32
|
+
}
|
|
11
33
|
|
|
12
|
-
|
|
13
|
-
|
|
34
|
+
// Get lockedAction message from server translations only (based on forum settings)
|
|
35
|
+
function getLockedActionMessage() {
|
|
36
|
+
if (window.adminChatsTranslations && window.adminChatsTranslations.lockedAction) {
|
|
37
|
+
return window.adminChatsTranslations.lockedAction;
|
|
38
|
+
}
|
|
39
|
+
// If server translation not available, default to English
|
|
40
|
+
return 'This action cannot be performed in a locked room.';
|
|
41
|
+
}
|
|
14
42
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
function isAdminAllChatsPage() {
|
|
44
|
+
return !!(ajaxify && ajaxify.data && ajaxify.data.adminAllChats);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getAdminChatsPageUrl(roomId) {
|
|
48
|
+
return roomId ? `chats/${roomId}` : 'chats';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getAdminChatsDataUrl(roomId) {
|
|
52
|
+
const params = new URL(document.location).searchParams;
|
|
53
|
+
params.set('switch', 1);
|
|
54
|
+
const query = params.toString();
|
|
55
|
+
return `${config.relative_path || ''}/api/admin-chats/page${roomId ? `/${roomId}` : ''}${query ? `?${query}` : ''}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function loadMoreAdminChats() {
|
|
59
|
+
const recentChats = $('[component="chat/recent"]');
|
|
60
|
+
if (!isAdminAllChatsPage() || !recentChats.length || recentChats.attr('loading')) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
recentChats.attr('loading', 1);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const start = parseInt(recentChats.attr('data-nextstart'), 10) || 0;
|
|
68
|
+
const response = await fetch(`${config.relative_path || ''}/api/admin-chats?start=${start}`, {
|
|
69
|
+
headers: {
|
|
70
|
+
'x-csrf-token': config.csrf_token,
|
|
71
|
+
},
|
|
72
|
+
credentials: 'same-origin',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error('Unable to load chats');
|
|
18
77
|
}
|
|
19
78
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
79
|
+
const payload = await response.json();
|
|
80
|
+
if (payload.rooms && payload.rooms.length) {
|
|
81
|
+
payload.loadingMore = true;
|
|
82
|
+
const html = await app.parseAndTranslate('chats', 'rooms', payload);
|
|
83
|
+
recentChats.append(html);
|
|
84
|
+
html.find('.timeago').timeago();
|
|
85
|
+
recentChats.attr('data-nextstart', payload.nextStart);
|
|
23
86
|
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
app.alertError(err);
|
|
89
|
+
} finally {
|
|
90
|
+
recentChats.removeAttr('loading');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function bindAdminRecentChatsInfiniteScroll() {
|
|
95
|
+
if (!isAdminAllChatsPage()) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$('[component="chat/recent"]').off('scroll').on('scroll', utils.debounce(function() {
|
|
100
|
+
const $this = $(this);
|
|
101
|
+
const bottom = ($this[0].scrollHeight - $this.height()) * 0.9;
|
|
102
|
+
if ($this.scrollTop() > bottom) {
|
|
103
|
+
loadMoreAdminChats();
|
|
104
|
+
}
|
|
105
|
+
}, 100));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function patchForumChatsForAdminAll() {
|
|
109
|
+
if (!isAdminAllChatsPage()) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
require(['forum/chats', 'forum/chats/messages'], function(Chats, ChatsMessages) {
|
|
114
|
+
if (!Chats) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!Chats._adminAllChatsPatched) {
|
|
119
|
+
const originalSwitchChat = Chats.switchChat;
|
|
120
|
+
|
|
121
|
+
Chats.switchChat = function(roomId) {
|
|
122
|
+
if (!isAdminAllChatsPage()) {
|
|
123
|
+
return originalSwitchChat.call(this, roomId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
roomId = roomId || '';
|
|
127
|
+
Chats.destroyAutoComplete(ajaxify.data.roomId);
|
|
128
|
+
socket.emit('modules.chats.leave', ajaxify.data.roomId);
|
|
129
|
+
|
|
130
|
+
const url = getAdminChatsPageUrl(roomId);
|
|
131
|
+
fetch(getAdminChatsDataUrl(roomId), { credentials: 'include' })
|
|
132
|
+
.then(async function(response) {
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
throw new Error(`Received ${response.status}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const payload = await response.json();
|
|
138
|
+
const html = await app.parseAndTranslate('partials/chats/message-window', payload);
|
|
139
|
+
const mainWrapper = $('[component="chat/main-wrapper"]');
|
|
140
|
+
mainWrapper.html(html);
|
|
141
|
+
mainWrapper.attr('data-roomid', roomId);
|
|
142
|
+
html.find('.timeago').timeago();
|
|
143
|
+
ajaxify.data = { ...ajaxify.data, ...payload, roomId: roomId };
|
|
144
|
+
ajaxify.updateTitle(ajaxify.data.title);
|
|
145
|
+
$('body').toggleClass('chat-loaded', !!roomId);
|
|
146
|
+
mainWrapper.find('[data-bs-toggle="tooltip"]').tooltip({ trigger: 'hover', container: '#content' });
|
|
147
|
+
Chats.setActive(roomId);
|
|
148
|
+
Chats.addEventListeners();
|
|
149
|
+
$(window).trigger('action:chat.loaded');
|
|
150
|
+
if (roomId) {
|
|
151
|
+
ChatsMessages.scrollToBottomAfterImageLoad(mainWrapper.find('[component="chat/message/content"]'));
|
|
152
|
+
}
|
|
153
|
+
if (history.pushState) {
|
|
154
|
+
history.pushState({ url: url }, null, `${window.location.protocol}//${window.location.host}${config.relative_path || ''}/${url}`);
|
|
155
|
+
}
|
|
156
|
+
bindAdminRecentChatsInfiniteScroll();
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
Chats._adminAllChatsPatched = true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
bindAdminRecentChatsInfiniteScroll();
|
|
24
164
|
});
|
|
25
165
|
}
|
|
26
166
|
|
|
27
|
-
function
|
|
28
|
-
if (!
|
|
29
|
-
return
|
|
167
|
+
function replaceAdminEmptyStateText() {
|
|
168
|
+
if (!app.user.isAdmin) {
|
|
169
|
+
return;
|
|
30
170
|
}
|
|
31
171
|
|
|
32
|
-
|
|
172
|
+
$('span.text-muted.text-sm').each(function() {
|
|
173
|
+
const currentText = $(this).text().trim();
|
|
174
|
+
if (
|
|
175
|
+
currentText.includes("אין לכם צ'אטים פעילים") ||
|
|
176
|
+
currentText === "אין לכם צ'אטים פעילים." ||
|
|
177
|
+
currentText.includes('You have no active chats') ||
|
|
178
|
+
currentText === 'You have no active chats.'
|
|
179
|
+
) {
|
|
180
|
+
$(this).text(t('emptyState'));
|
|
181
|
+
$(this).removeClass('text-muted');
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getChatWindows() {
|
|
187
|
+
return $('[component="chat/message/window"]');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getWindowRoomId($window) {
|
|
191
|
+
const roomId = $window.find('[component="chat/messages"]').first().attr('data-roomid') ||
|
|
192
|
+
$window.closest('.chat-modal').attr('data-roomid') ||
|
|
193
|
+
(ajaxify && ajaxify.data && ajaxify.data.roomId);
|
|
194
|
+
return parseInt(roomId, 10) || 0;
|
|
33
195
|
}
|
|
34
196
|
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
197
|
+
function getCachedRoomData(roomId) {
|
|
198
|
+
const cached = roomStateCache.get(String(roomId));
|
|
199
|
+
if (!cached) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
if (Date.now() - cached.timestamp > 10000) {
|
|
203
|
+
roomStateCache.delete(String(roomId));
|
|
204
|
+
return null;
|
|
38
205
|
}
|
|
206
|
+
return cached.data;
|
|
207
|
+
}
|
|
39
208
|
|
|
40
|
-
|
|
209
|
+
function setCachedRoomData(roomId, data) {
|
|
210
|
+
roomStateCache.set(String(roomId), {
|
|
211
|
+
data,
|
|
212
|
+
timestamp: Date.now(),
|
|
213
|
+
});
|
|
41
214
|
}
|
|
42
215
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
216
|
+
async function fetchRoomData(roomId) {
|
|
217
|
+
if (!roomId) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
47
220
|
|
|
48
|
-
|
|
49
|
-
|
|
221
|
+
const currentRoom = ajaxify && ajaxify.data && (ajaxify.data.room || ajaxify.data);
|
|
222
|
+
if (currentRoom && parseInt(currentRoom.roomId, 10) === roomId && currentRoom.adminChatLock) {
|
|
223
|
+
setCachedRoomData(roomId, currentRoom);
|
|
224
|
+
return currentRoom;
|
|
50
225
|
}
|
|
51
226
|
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
return;
|
|
227
|
+
const cached = getCachedRoomData(roomId);
|
|
228
|
+
if (cached) {
|
|
229
|
+
return cached;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const endpoint = isAdminAllChatsPage() ?
|
|
233
|
+
`${config.relative_path || ''}/api/admin-chats/page/${roomId}` :
|
|
234
|
+
`${config.relative_path || ''}/api/chats/${roomId}`;
|
|
235
|
+
const response = await fetch(endpoint, {
|
|
236
|
+
headers: {
|
|
237
|
+
'x-csrf-token': config.csrf_token,
|
|
238
|
+
},
|
|
239
|
+
credentials: 'same-origin',
|
|
240
|
+
});
|
|
241
|
+
if (!response.ok) {
|
|
242
|
+
return null;
|
|
55
243
|
}
|
|
56
244
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
245
|
+
const payload = await response.json();
|
|
246
|
+
const roomData = payload && payload.roomId ? payload : (payload && payload.room ? payload.room : null);
|
|
247
|
+
if (roomData) {
|
|
248
|
+
setCachedRoomData(roomId, roomData);
|
|
249
|
+
}
|
|
250
|
+
return roomData;
|
|
60
251
|
}
|
|
61
252
|
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'[component="chat/replies"]',
|
|
66
|
-
'[component="chat/input"]',
|
|
67
|
-
'[component="chat/textarea"]',
|
|
68
|
-
'.chat-input',
|
|
69
|
-
'.composer',
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const seen = new Set();
|
|
73
|
-
selectors.forEach(function(selector) {
|
|
74
|
-
$(selector).each(function() {
|
|
75
|
-
const $element = $(this);
|
|
76
|
-
const $container = $element.closest('[component="chat/composer"], [component="chat/replies"], .chat-input, .composer').length ?
|
|
77
|
-
$element.closest('[component="chat/composer"], [component="chat/replies"], .chat-input, .composer').first() :
|
|
78
|
-
$element;
|
|
79
|
-
const domNode = $container.get(0);
|
|
80
|
-
|
|
81
|
-
if (!domNode || seen.has(domNode)) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
253
|
+
function isLockedForUser(roomData) {
|
|
254
|
+
return !!(roomData && roomData.adminChatLock && roomData.adminChatLock.isLocked && !(app.user && app.user.isAdmin));
|
|
255
|
+
}
|
|
84
256
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
257
|
+
function setComposerHidden($window, hidden) {
|
|
258
|
+
$window.find('[component="chat/composer"]').each(function() {
|
|
259
|
+
const $composer = $(this);
|
|
260
|
+
if (hidden) {
|
|
261
|
+
$composer.addClass('hidden').hide();
|
|
262
|
+
} else {
|
|
263
|
+
$composer.removeClass('hidden').show();
|
|
264
|
+
}
|
|
88
265
|
});
|
|
266
|
+
|
|
267
|
+
$window.find('[component="chat/input"], [component="chat/send"], button[data-action="send"], textarea.chat-input')
|
|
268
|
+
.prop('disabled', hidden)
|
|
269
|
+
.attr('disabled', hidden ? 'disabled' : null);
|
|
89
270
|
}
|
|
90
271
|
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
272
|
+
function renderLockBanner($window, hidden) {
|
|
273
|
+
$window.find('.admin-chat-lock-banner').remove();
|
|
274
|
+
if (!hidden) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const target = $window.find('[component="chat/messages"]').first();
|
|
279
|
+
if (!target.length) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
95
282
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
283
|
+
const positionStyle = isEnglishSystem() ? 'float:right; clear:both;' : 'float:left; clear:both;';
|
|
284
|
+
target.prepend(`<div class="admin-chat-lock-banner alert alert-warning mb-2 text-start" style="${positionStyle} max-width: fit-content;">${t('lockBanner')}</div>`);
|
|
285
|
+
}
|
|
99
286
|
|
|
100
|
-
|
|
287
|
+
function updateLockedActionVisibility($window, hidden) {
|
|
288
|
+
[
|
|
289
|
+
'[data-action="reply"]',
|
|
290
|
+
'[data-action="edit"]',
|
|
291
|
+
'[data-action="delete"]',
|
|
292
|
+
'[data-action="restore"]',
|
|
293
|
+
'[data-action="kick"]',
|
|
294
|
+
'[data-action="toggleOwner"]'
|
|
295
|
+
].forEach(function(selector) {
|
|
296
|
+
$window.find(selector).toggleClass('hidden', hidden).toggle(!hidden);
|
|
297
|
+
});
|
|
101
298
|
|
|
102
|
-
|
|
103
|
-
|
|
299
|
+
$window.find('[component="chat/controlsToggle"]').closest('.dropdown').toggleClass('hidden', hidden).toggle(!hidden);
|
|
300
|
+
$window.find('[component="chat/manage/user/add/search"], [component="chat/manage/user/list/search"], [component="chat/manage/save"]')
|
|
301
|
+
.toggleClass('hidden', hidden)
|
|
302
|
+
.toggle(!hidden)
|
|
303
|
+
.prop('disabled', hidden)
|
|
304
|
+
.attr('disabled', hidden ? 'disabled' : null);
|
|
305
|
+
$window.find('[component="chat/manage-modal"] .form-text, [component="chat/manage-modal"] .text-danger')
|
|
306
|
+
.toggleClass('hidden', hidden);
|
|
104
307
|
}
|
|
105
308
|
|
|
106
|
-
function normalizeLockMessages() {
|
|
107
|
-
$('[component="chat/message
|
|
309
|
+
function normalizeLockMessages($window) {
|
|
310
|
+
$window.find('[component="chat/system-message"] > div').each(function() {
|
|
108
311
|
const $el = $(this);
|
|
109
312
|
const text = $el.text().trim();
|
|
110
|
-
if (!text.
|
|
313
|
+
if (!text.includes(LOCK_PREFIX.slice(1, -1)) && !text.includes('admin-chat-lock')) {
|
|
111
314
|
return;
|
|
112
315
|
}
|
|
113
|
-
|
|
114
|
-
$el.text(text.replace(LOCK_PREFIX, '').trim());
|
|
316
|
+
$el.text(t('lockBanner'));
|
|
115
317
|
});
|
|
116
318
|
}
|
|
117
319
|
|
|
118
|
-
function getRoomMenuTargets() {
|
|
119
|
-
|
|
120
|
-
|
|
320
|
+
function getRoomMenuTargets($window) {
|
|
321
|
+
const scoped = $window.find('[component="chat/controls"]');
|
|
322
|
+
if (scoped.length) {
|
|
323
|
+
return scoped;
|
|
324
|
+
}
|
|
325
|
+
return $window.find('[component="chat/header"], .chat-header, .modal-header, [component="chat/nav"]').find('.dropdown-menu');
|
|
121
326
|
}
|
|
122
327
|
|
|
123
|
-
function renderAdminLockControl() {
|
|
124
|
-
$('.admin-chat-lock-toggle-item, .admin-chat-lock-divider').remove();
|
|
328
|
+
function renderAdminLockControl($window, roomData) {
|
|
329
|
+
$window.find('.admin-chat-lock-toggle-item, .admin-chat-lock-divider, .admin-chat-lock-item-wrap').remove();
|
|
125
330
|
|
|
126
|
-
if (!app.user.isAdmin) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const roomData = getRoomData();
|
|
131
|
-
const roomId = roomData && roomData.roomId;
|
|
132
|
-
if (!roomId) {
|
|
331
|
+
if (!app.user.isAdmin || !roomData || !roomData.roomId) {
|
|
133
332
|
return;
|
|
134
333
|
}
|
|
135
334
|
|
|
136
|
-
const menus = getRoomMenuTargets();
|
|
335
|
+
const menus = getRoomMenuTargets($window);
|
|
137
336
|
if (!menus.length) {
|
|
138
337
|
return;
|
|
139
338
|
}
|
|
140
339
|
|
|
141
340
|
const lockData = roomData.adminChatLock || {};
|
|
142
341
|
const isLocked = !!lockData.isLocked;
|
|
143
|
-
const itemText =
|
|
342
|
+
const itemText = isLocked ? t('menuRelease') : t('menuLock');
|
|
144
343
|
const iconClass = isLocked ? 'fa-lock-open' : 'fa-lock';
|
|
145
344
|
const menuItemHtml = `
|
|
146
345
|
<li role="presentation" class="admin-chat-lock-item-wrap">
|
|
147
|
-
<a href="#" role="menuitem" class="dropdown-item rounded-1 d-flex align-items-center gap-2 admin-chat-lock-toggle-item" data-room-id="${roomId}" data-locked="${isLocked}">
|
|
346
|
+
<a href="#" role="menuitem" class="dropdown-item rounded-1 d-flex align-items-center gap-2 admin-chat-lock-toggle-item" data-room-id="${roomData.roomId}" data-locked="${isLocked}">
|
|
148
347
|
<i class="fa fa-fw ${iconClass}"></i>
|
|
149
348
|
<span>${itemText}</span>
|
|
150
349
|
</a>
|
|
@@ -157,11 +356,75 @@ $(document).ready(function() {
|
|
|
157
356
|
if ($menu.find('.admin-chat-lock-toggle-item').length) {
|
|
158
357
|
return;
|
|
159
358
|
}
|
|
160
|
-
|
|
161
359
|
$menu.prepend(menuItemHtml);
|
|
162
360
|
});
|
|
163
361
|
}
|
|
164
362
|
|
|
363
|
+
async function applyUiToWindow($window) {
|
|
364
|
+
const roomId = getWindowRoomId($window);
|
|
365
|
+
if (!roomId) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const roomData = await fetchRoomData(roomId);
|
|
370
|
+
if (!roomData) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const hidden = isLockedForUser(roomData);
|
|
375
|
+
setComposerHidden($window, hidden);
|
|
376
|
+
updateLockedActionVisibility($window, hidden);
|
|
377
|
+
renderLockBanner($window, hidden);
|
|
378
|
+
renderAdminLockControl($window, roomData);
|
|
379
|
+
normalizeLockMessages($window);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
function getAdminRoomsMap() {
|
|
384
|
+
const rooms = ajaxify && ajaxify.data && Array.isArray(ajaxify.data.rooms) ? ajaxify.data.rooms : [];
|
|
385
|
+
return new Map(rooms.map(room => [String(room.roomId), room]));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function renderAdminParticipants() {
|
|
389
|
+
if (!isAdminAllChatsPage()) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const roomsMap = getAdminRoomsMap();
|
|
394
|
+
$('[component="chat/recent/room"]').each(function() {
|
|
395
|
+
const $room = $(this);
|
|
396
|
+
const roomId = String($room.attr('data-roomid') || '');
|
|
397
|
+
const roomData = roomsMap.get(roomId);
|
|
398
|
+
const $container = $room.find('[component="chat/room/title"]').closest('.d-flex.flex-grow-1.flex-column.w-100');
|
|
399
|
+
|
|
400
|
+
$container.find('.admin-chat-participants').remove();
|
|
401
|
+
if (!$container.length || !roomData || !roomData.participantsLabel) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const html = `<div class="admin-chat-participants text-muted text-xs text-break line-clamp-2"><span class="fw-semibold">${t('participants')}:</span> ${utils.escapeHTML(roomData.participantsLabel)}</div>`;
|
|
406
|
+
$container.find('[component="chat/room/title"]').after(html);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function applyAdminAllPageTweaks() {
|
|
411
|
+
if (!isAdminAllChatsPage()) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
$('[component="chat/recent"] .mark-read').addClass('hidden');
|
|
416
|
+
$('[data-action="leave"]').closest('li').addClass('hidden');
|
|
417
|
+
renderAdminParticipants();
|
|
418
|
+
}
|
|
419
|
+
async function refreshChatUi() {
|
|
420
|
+
replaceAdminEmptyStateText();
|
|
421
|
+
applyAdminAllPageTweaks();
|
|
422
|
+
const windows = getChatWindows();
|
|
423
|
+
await Promise.all(windows.map(function() {
|
|
424
|
+
return applyUiToWindow($(this));
|
|
425
|
+
}).get());
|
|
426
|
+
}
|
|
427
|
+
|
|
165
428
|
async function toggleRoomLock(roomId, nextState) {
|
|
166
429
|
const response = await fetch(`${config.relative_path || ''}/api/admin-chats/${roomId}/lock`, {
|
|
167
430
|
method: 'POST',
|
|
@@ -170,6 +433,7 @@ $(document).ready(function() {
|
|
|
170
433
|
'x-csrf-token': config.csrf_token,
|
|
171
434
|
},
|
|
172
435
|
body: JSON.stringify({ locked: nextState }),
|
|
436
|
+
credentials: 'same-origin',
|
|
173
437
|
});
|
|
174
438
|
|
|
175
439
|
if (!response.ok) {
|
|
@@ -179,13 +443,6 @@ $(document).ready(function() {
|
|
|
179
443
|
return await response.json();
|
|
180
444
|
}
|
|
181
445
|
|
|
182
|
-
function refreshChatUi() {
|
|
183
|
-
replaceAdminEmptyStateText();
|
|
184
|
-
renderAdminLockControl();
|
|
185
|
-
updateComposerState();
|
|
186
|
-
normalizeLockMessages();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
446
|
$(window).on('action:ajaxify.end', function(ev, data) {
|
|
190
447
|
const templateName = ajaxify && ajaxify.data && ajaxify.data.template ? ajaxify.data.template.name : '';
|
|
191
448
|
|
|
@@ -193,14 +450,12 @@ $(document).ready(function() {
|
|
|
193
450
|
const userSlug = ajaxify.data.userslug || (ajaxify.data.user && ajaxify.data.user.userslug);
|
|
194
451
|
|
|
195
452
|
if (userSlug) {
|
|
196
|
-
const buttonText = isEnglishSystem() ? 'View Chats' : "צפיה בצ'אטים";
|
|
197
453
|
const relativePath = config.relative_path || '';
|
|
198
|
-
|
|
199
454
|
const btnHtml = `
|
|
200
455
|
<li role="presentation">
|
|
201
456
|
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="${relativePath}/user/${userSlug}/chats" role="menuitem">
|
|
202
457
|
<i class="far fa-fw fa-comments"></i>
|
|
203
|
-
<span>${
|
|
458
|
+
<span>${t('viewChats')}</span>
|
|
204
459
|
</a>
|
|
205
460
|
</li>
|
|
206
461
|
<li role="presentation" class="dropdown-divider"></li>
|
|
@@ -213,23 +468,37 @@ $(document).ready(function() {
|
|
|
213
468
|
}
|
|
214
469
|
}
|
|
215
470
|
|
|
216
|
-
|
|
471
|
+
if (isAdminAllChatsPage()) {
|
|
472
|
+
patchForumChatsForAdminAll();
|
|
473
|
+
bindAdminRecentChatsInfiniteScroll();
|
|
474
|
+
}
|
|
217
475
|
|
|
218
|
-
|
|
476
|
+
const url = data && data.url ? data.url : '';
|
|
477
|
+
if (url.match(/^user\/.+\/chats/) || url.match(/^chats(\/|$)/)) {
|
|
219
478
|
refreshChatUi();
|
|
220
479
|
setTimeout(refreshChatUi, 500);
|
|
480
|
+
setTimeout(refreshChatUi, 1200);
|
|
221
481
|
}
|
|
222
482
|
});
|
|
223
483
|
|
|
224
484
|
$(window).on('action:chat.loaded', function() {
|
|
485
|
+
if (isAdminAllChatsPage()) {
|
|
486
|
+
patchForumChatsForAdminAll();
|
|
487
|
+
bindAdminRecentChatsInfiniteScroll();
|
|
488
|
+
}
|
|
225
489
|
refreshChatUi();
|
|
226
490
|
setTimeout(refreshChatUi, 200);
|
|
491
|
+
setTimeout(refreshChatUi, 1000);
|
|
227
492
|
});
|
|
228
493
|
|
|
229
494
|
$(window).on('action:chat.closed', function() {
|
|
230
495
|
setTimeout(refreshChatUi, 200);
|
|
231
496
|
});
|
|
232
497
|
|
|
498
|
+
$(window).on('action:chat.onMessagesAddedToDom action:chat.edited action:chat.renamed', function() {
|
|
499
|
+
setTimeout(refreshChatUi, 0);
|
|
500
|
+
});
|
|
501
|
+
|
|
233
502
|
$(document).on('click', '.admin-chat-lock-toggle-item', async function(ev) {
|
|
234
503
|
ev.preventDefault();
|
|
235
504
|
|
|
@@ -245,16 +514,42 @@ $(document).ready(function() {
|
|
|
245
514
|
|
|
246
515
|
try {
|
|
247
516
|
const result = await toggleRoomLock(roomId, !isLocked);
|
|
248
|
-
|
|
249
|
-
|
|
517
|
+
const currentRoom = ajaxify && ajaxify.data && (ajaxify.data.room || ajaxify.data);
|
|
518
|
+
if (currentRoom && parseInt(currentRoom.roomId, 10) === roomId) {
|
|
519
|
+
currentRoom.adminChatLock = result.lockData;
|
|
520
|
+
currentRoom.canReply = !result.lockData.isLocked || (app.user && app.user.isAdmin);
|
|
521
|
+
currentRoom.showUserInput = currentRoom.canReply;
|
|
250
522
|
if (ajaxify.data.room) {
|
|
251
523
|
ajaxify.data.room.adminChatLock = result.lockData;
|
|
524
|
+
ajaxify.data.room.canReply = currentRoom.canReply;
|
|
525
|
+
ajaxify.data.room.showUserInput = currentRoom.showUserInput;
|
|
252
526
|
}
|
|
253
527
|
}
|
|
254
|
-
|
|
528
|
+
|
|
529
|
+
const cached = getCachedRoomData(roomId) || { roomId };
|
|
530
|
+
cached.adminChatLock = result.lockData;
|
|
531
|
+
cached.canReply = !result.lockData.isLocked || (app.user && app.user.isAdmin);
|
|
532
|
+
cached.showUserInput = cached.canReply;
|
|
533
|
+
setCachedRoomData(roomId, cached);
|
|
534
|
+
await refreshChatUi();
|
|
255
535
|
} catch (err) {
|
|
256
|
-
app.alertError(
|
|
536
|
+
app.alertError(t('updateError'));
|
|
257
537
|
$button.removeClass('disabled').removeAttr('aria-disabled');
|
|
258
538
|
}
|
|
259
539
|
});
|
|
540
|
+
|
|
541
|
+
// Intercept and translate error messages from API - only for lockedAction
|
|
542
|
+
if (app.alertError && !app.alertError._adminChatsWrapped) {
|
|
543
|
+
const originalAlertError = app.alertError;
|
|
544
|
+
app.alertError = function(message) {
|
|
545
|
+
if (typeof message === 'string') {
|
|
546
|
+
if (message.includes('[[admin-chats:errors.lockedAction]]') || message.includes('errors.lockedAction')) {
|
|
547
|
+
message = getLockedActionMessage();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return originalAlertError.call(this, message);
|
|
551
|
+
};
|
|
552
|
+
app.alertError._adminChatsWrapped = true;
|
|
553
|
+
}
|
|
260
554
|
});
|
|
555
|
+
|