turbo_chat 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +178 -190
- data/app/assets/config/turbo_chat_manifest.js +3 -0
- data/app/assets/javascripts/turbo_chat/application.js +3 -0
- data/app/assets/javascripts/turbo_chat/invite_picker.js +19 -392
- data/app/assets/javascripts/turbo_chat/member_sync.js +426 -0
- data/app/assets/javascripts/turbo_chat/mentions.js +366 -0
- data/app/assets/javascripts/turbo_chat/messages.js +18 -370
- data/app/assets/javascripts/turbo_chat/realtime.js +3 -10
- data/app/assets/javascripts/turbo_chat/scroll_proxy.js +379 -0
- data/app/assets/javascripts/turbo_chat/shared.js +7 -383
- data/app/assets/stylesheets/turbo_chat/application.css +9 -1646
- data/app/assets/stylesheets/turbo_chat/base.css +84 -0
- data/app/assets/stylesheets/turbo_chat/components.css +193 -0
- data/app/assets/stylesheets/turbo_chat/composer.css +241 -0
- data/app/assets/stylesheets/turbo_chat/layout.css +307 -0
- data/app/assets/stylesheets/turbo_chat/members.css +264 -0
- data/app/assets/stylesheets/turbo_chat/menus.css +172 -0
- data/app/assets/stylesheets/turbo_chat/messages.css +430 -0
- data/app/controllers/turbo_chat/application_controller.rb +3 -7
- data/app/controllers/turbo_chat/chat_memberships_controller.rb +35 -1
- data/app/controllers/turbo_chat/chat_messages_controller.rb +4 -8
- data/app/controllers/turbo_chat/chats_controller.rb +10 -12
- data/app/helpers/turbo_chat/application_helper/config_support.rb +42 -32
- data/app/helpers/turbo_chat/application_helper/mention_support.rb +3 -3
- data/app/helpers/turbo_chat/application_helper/message_rendering.rb +24 -13
- data/app/models/turbo_chat/chat.rb +43 -20
- data/app/models/turbo_chat/chat_membership.rb +1 -1
- data/app/models/turbo_chat/chat_message/blocked_words_moderation.rb +9 -25
- data/app/models/turbo_chat/chat_message/body_length_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/broadcasting.rb +2 -6
- data/app/models/turbo_chat/chat_message/formatting.rb +3 -7
- data/app/models/turbo_chat/chat_message/mention_validation.rb +1 -1
- data/app/models/turbo_chat/chat_message/signals.rb +1 -1
- data/app/models/turbo_chat/chat_message.rb +3 -8
- data/app/views/turbo_chat/chat_messages/_form.html.erb +9 -9
- data/app/views/turbo_chat/chat_messages/_message.html.erb +2 -2
- data/app/views/turbo_chat/chat_messages/_signals.html.erb +11 -13
- data/app/views/turbo_chat/chat_messages/_system.html.erb +1 -1
- data/app/views/turbo_chat/chats/_invite_form.html.erb +1 -1
- data/app/views/turbo_chat/chats/_member_entries.html.erb +15 -1
- data/app/views/turbo_chat/chats/index.html.erb +1 -1
- data/app/views/turbo_chat/chats/new.html.erb +4 -7
- data/app/views/turbo_chat/chats/show.html.erb +29 -27
- data/config/routes.rb +6 -1
- data/db/migrate/20260325000016_add_chat_mode_to_turbo_chat_chats.rb +6 -0
- data/lib/generators/turbo_chat/install/templates/turbo_chat.rb +8 -0
- data/lib/turbo_chat/configuration/defaults.rb +21 -0
- data/lib/turbo_chat/configuration.rb +105 -0
- data/lib/turbo_chat/moderation/chat_actions.rb +2 -2
- data/lib/turbo_chat/moderation/member_actions.rb +2 -1
- data/lib/turbo_chat/moderation/support.rb +5 -9
- data/lib/turbo_chat/permission/support.rb +6 -2
- data/lib/turbo_chat/permission.rb +1 -5
- data/lib/turbo_chat/signals.rb +1 -1
- data/lib/turbo_chat/version.rb +1 -1
- metadata +13 -2
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
"use strict";
|
|
3
|
+
var namespace = (window.TurboChatUI = window.TurboChatUI || {});
|
|
4
|
+
var setupInvitePicker = namespace.setupInvitePicker;
|
|
5
|
+
|
|
6
|
+
function normalize(value) {
|
|
7
|
+
return String(value || "").trim().toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseMemberEntry(node) {
|
|
11
|
+
if (!node || !node.dataset || node.dataset.chatMemberEntry !== "true") {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
var chatId = String(node.dataset.chatId || "").trim();
|
|
16
|
+
var participantType = String(node.dataset.chatMemberParticipantType || "").trim();
|
|
17
|
+
var participantId = String(node.dataset.chatMemberParticipantId || "").trim();
|
|
18
|
+
var mentionToken = String(node.dataset.chatMemberMentionToken || "").trim();
|
|
19
|
+
var roleKey = String(node.dataset.chatMemberRoleKey || "").trim();
|
|
20
|
+
var roleName = String(node.dataset.chatMemberRoleName || "").trim();
|
|
21
|
+
var roleRank = parseInt(node.dataset.chatMemberRoleRank || "-1", 10);
|
|
22
|
+
var name = String(node.dataset.chatMemberName || "").trim();
|
|
23
|
+
var inviteLabel = String(node.dataset.chatMemberInviteLabel || "").trim();
|
|
24
|
+
var search = normalize(node.dataset.chatMemberSearch || inviteLabel || name);
|
|
25
|
+
|
|
26
|
+
if (!chatId || !participantType || !participantId) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
identity: participantType + ":" + participantId,
|
|
32
|
+
chatId: chatId,
|
|
33
|
+
participantType: participantType,
|
|
34
|
+
participantId: participantId,
|
|
35
|
+
mentionToken: mentionToken,
|
|
36
|
+
name: name,
|
|
37
|
+
roleKey: roleKey,
|
|
38
|
+
roleName: roleName,
|
|
39
|
+
roleRank: isNaN(roleRank) ? -1 : roleRank,
|
|
40
|
+
inviteOption: {
|
|
41
|
+
label: inviteLabel,
|
|
42
|
+
participantId: participantId,
|
|
43
|
+
search: search
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function collectMemberEntries(sourceNode, entryMap) {
|
|
49
|
+
if (!sourceNode || sourceNode.nodeType !== 1) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var sourceElement = sourceNode;
|
|
54
|
+
var sourceEntry = parseMemberEntry(sourceElement);
|
|
55
|
+
if (sourceEntry) {
|
|
56
|
+
entryMap[sourceEntry.identity] = sourceEntry;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof sourceElement.querySelectorAll !== "function") {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sourceElement.querySelectorAll("[data-chat-member-entry='true']").forEach(function (memberEntryNode) {
|
|
64
|
+
var memberEntry = parseMemberEntry(memberEntryNode);
|
|
65
|
+
if (!memberEntry) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
entryMap[memberEntry.identity] = memberEntry;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function findInvitePickerApis(chatId) {
|
|
74
|
+
var apis = [];
|
|
75
|
+
var normalizedChatId = String(chatId || "").trim();
|
|
76
|
+
if (!normalizedChatId) {
|
|
77
|
+
return apis;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
document.querySelectorAll("[data-chat-invite-form]").forEach(function (form) {
|
|
81
|
+
if (String(form.dataset.chatId || "").trim() !== normalizedChatId) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
var api = setupInvitePicker(form);
|
|
86
|
+
if (api) {
|
|
87
|
+
apis.push(api);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return apis;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function memberListForChat(chatId) {
|
|
95
|
+
var normalizedChatId = String(chatId || "").trim();
|
|
96
|
+
if (!normalizedChatId) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
var matchedList = null;
|
|
101
|
+
document.querySelectorAll("[data-chat-member-list]").forEach(function (memberList) {
|
|
102
|
+
if (!matchedList && String(memberList.dataset.chatId || "").trim() === normalizedChatId) {
|
|
103
|
+
matchedList = memberList;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return matchedList;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function shellForChat(chatId) {
|
|
110
|
+
var normalizedChatId = String(chatId || "").trim();
|
|
111
|
+
if (!normalizedChatId) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
var matchedShell = null;
|
|
116
|
+
document.querySelectorAll(".chat-shell[data-chat-id]").forEach(function (shell) {
|
|
117
|
+
if (!matchedShell && String(shell.dataset.chatId || "").trim() === normalizedChatId) {
|
|
118
|
+
matchedShell = shell;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return matchedShell;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function syncInviteOptionsFromMemberMutations(chatId, mutationRecords) {
|
|
125
|
+
var invitePickerApis = findInvitePickerApis(chatId);
|
|
126
|
+
if (!invitePickerApis.length) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var removedEntries = {};
|
|
131
|
+
var addedEntries = {};
|
|
132
|
+
|
|
133
|
+
mutationRecords.forEach(function (record) {
|
|
134
|
+
if (!record) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
record.removedNodes.forEach(function (removedNode) {
|
|
139
|
+
collectMemberEntries(removedNode, removedEntries);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
record.addedNodes.forEach(function (addedNode) {
|
|
143
|
+
collectMemberEntries(addedNode, addedEntries);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
Object.keys(removedEntries).forEach(function (entryKey) {
|
|
148
|
+
if (addedEntries[entryKey]) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
var removedEntry = removedEntries[entryKey];
|
|
153
|
+
invitePickerApis.forEach(function (invitePickerApi) {
|
|
154
|
+
invitePickerApi.upsertOption(removedEntry.inviteOption);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
Object.keys(addedEntries).forEach(function (entryKey) {
|
|
159
|
+
var addedEntry = addedEntries[entryKey];
|
|
160
|
+
invitePickerApis.forEach(function (invitePickerApi) {
|
|
161
|
+
invitePickerApi.removeParticipant(addedEntry.participantId);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function mentionOptionsForComposer(composer, memberEntries) {
|
|
167
|
+
if (!composer || composer.dataset.chatEnableMentions !== "true") {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
var canMentionMembers = composer.dataset.chatCanMentionMembers === "true";
|
|
172
|
+
var canMentionAll = composer.dataset.chatCanMentionAll === "true";
|
|
173
|
+
var canMentionRoles = composer.dataset.chatCanMentionRoles === "true";
|
|
174
|
+
var hideRoles = composer.dataset.chatMentionFilterHideRoles === "true";
|
|
175
|
+
var excludeSelf = composer.dataset.chatMentionFilterExcludeSelf === "true";
|
|
176
|
+
var selfType = String(composer.dataset.chatSelfParticipantType || "").trim();
|
|
177
|
+
var selfId = String(composer.dataset.chatSelfParticipantId || "").trim();
|
|
178
|
+
var mentionOptions = [];
|
|
179
|
+
var seenTokens = {};
|
|
180
|
+
|
|
181
|
+
function addMentionOption(optionData) {
|
|
182
|
+
if (!optionData || !optionData.token) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var dedupeToken = normalize(optionData.token);
|
|
187
|
+
if (!dedupeToken || seenTokens[dedupeToken]) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
seenTokens[dedupeToken] = true;
|
|
192
|
+
mentionOptions.push(optionData);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (canMentionAll) {
|
|
196
|
+
addMentionOption({ token: "@all", label: "All members", kind: "group" });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (canMentionMembers) {
|
|
200
|
+
memberEntries.forEach(function (memberEntry) {
|
|
201
|
+
if (excludeSelf && memberEntry.participantType === selfType && memberEntry.participantId === selfId) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!memberEntry.mentionToken) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
addMentionOption({
|
|
210
|
+
token: memberEntry.mentionToken,
|
|
211
|
+
label: memberEntry.name || memberEntry.mentionToken,
|
|
212
|
+
kind: "member",
|
|
213
|
+
participant_type: memberEntry.participantType,
|
|
214
|
+
participant_id: memberEntry.participantId
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (canMentionRoles && !hideRoles) {
|
|
220
|
+
memberEntries.forEach(function (memberEntry) {
|
|
221
|
+
if (!memberEntry.roleKey) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
var roleToken = "@" + memberEntry.roleKey.toUpperCase();
|
|
226
|
+
var roleLabel = (memberEntry.roleName || memberEntry.roleKey) + " role";
|
|
227
|
+
addMentionOption({
|
|
228
|
+
token: roleToken,
|
|
229
|
+
label: roleLabel,
|
|
230
|
+
kind: "role"
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return mentionOptions;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function syncComposerMentionOptionsForChat(chatId) {
|
|
239
|
+
var memberList = memberListForChat(chatId);
|
|
240
|
+
if (!memberList) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
var memberEntries = [];
|
|
245
|
+
memberList.querySelectorAll("[data-chat-member-entry='true']").forEach(function (memberNode) {
|
|
246
|
+
var memberEntry = parseMemberEntry(memberNode);
|
|
247
|
+
if (memberEntry) {
|
|
248
|
+
memberEntries.push(memberEntry);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
document.querySelectorAll("[data-chat-composer]").forEach(function (composer) {
|
|
253
|
+
if (String(composer.dataset.chatId || "").trim() !== String(chatId).trim()) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!composer.__chatComposerApi || typeof composer.__chatComposerApi.setMentionOptions !== "function") {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
composer.__chatComposerApi.setMentionOptions(mentionOptionsForComposer(composer, memberEntries));
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function setupMemberManageControlsForChat(chatId) {
|
|
266
|
+
var memberList = memberListForChat(chatId);
|
|
267
|
+
if (!memberList) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
memberList.querySelectorAll("[data-chat-member-entry='true']").forEach(function (memberNode) {
|
|
272
|
+
if (!memberNode || memberNode.dataset.chatMemberManageBound === "true") {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
var toggle = memberNode.querySelector("[data-chat-member-manage-toggle]");
|
|
277
|
+
var panel = memberNode.querySelector("[data-chat-member-manage-panel]");
|
|
278
|
+
if (!toggle || !panel) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
memberNode.dataset.chatMemberManageBound = "true";
|
|
283
|
+
panel.hidden = true;
|
|
284
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
285
|
+
|
|
286
|
+
toggle.addEventListener("click", function () {
|
|
287
|
+
if (toggle.disabled) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var expanded = toggle.getAttribute("aria-expanded") === "true";
|
|
292
|
+
var parentList = memberNode.closest("[data-chat-member-list]");
|
|
293
|
+
if (parentList) {
|
|
294
|
+
parentList.querySelectorAll("[data-chat-member-manage-toggle]").forEach(function (otherToggle) {
|
|
295
|
+
otherToggle.setAttribute("aria-expanded", "false");
|
|
296
|
+
});
|
|
297
|
+
parentList.querySelectorAll("[data-chat-member-manage-panel]").forEach(function (otherPanel) {
|
|
298
|
+
otherPanel.hidden = true;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (expanded) {
|
|
303
|
+
panel.hidden = true;
|
|
304
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
panel.hidden = false;
|
|
309
|
+
toggle.setAttribute("aria-expanded", "true");
|
|
310
|
+
var roleInput = panel.querySelector("[data-chat-member-role-input]");
|
|
311
|
+
if (roleInput && !roleInput.disabled) {
|
|
312
|
+
roleInput.focus();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function syncRoleFormAccessForChat(chatId) {
|
|
319
|
+
var memberList = memberListForChat(chatId);
|
|
320
|
+
var shell = shellForChat(chatId);
|
|
321
|
+
if (!memberList || !shell) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
var canManage = shell.dataset.chatCanManageMemberPermissions === "true";
|
|
326
|
+
var canGrant = shell.dataset.chatCanGrantMemberPermissions === "true";
|
|
327
|
+
var canMute = shell.dataset.chatCanMuteMember === "true";
|
|
328
|
+
var canBan = shell.dataset.chatCanBanMember === "true";
|
|
329
|
+
var selfType = String(shell.dataset.chatSelfParticipantType || "").trim();
|
|
330
|
+
var selfId = String(shell.dataset.chatSelfParticipantId || "").trim();
|
|
331
|
+
var selfRoleRank = -1;
|
|
332
|
+
|
|
333
|
+
memberList.querySelectorAll("[data-chat-member-entry='true']").forEach(function (memberNode) {
|
|
334
|
+
if (
|
|
335
|
+
String(memberNode.dataset.chatMemberParticipantType || "").trim() === selfType &&
|
|
336
|
+
String(memberNode.dataset.chatMemberParticipantId || "").trim() === selfId
|
|
337
|
+
) {
|
|
338
|
+
var rank = parseInt(memberNode.dataset.chatMemberRoleRank || "-1", 10);
|
|
339
|
+
selfRoleRank = isNaN(rank) ? -1 : rank;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
memberList.querySelectorAll("[data-chat-member-entry='true']").forEach(function (memberNode) {
|
|
344
|
+
var targetType = String(memberNode.dataset.chatMemberParticipantType || "").trim();
|
|
345
|
+
var targetId = String(memberNode.dataset.chatMemberParticipantId || "").trim();
|
|
346
|
+
var targetRoleRank = parseInt(memberNode.dataset.chatMemberRoleRank || "-1", 10);
|
|
347
|
+
if (isNaN(targetRoleRank)) {
|
|
348
|
+
targetRoleRank = -1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
var isSelf = targetType === selfType && targetId === selfId;
|
|
352
|
+
var targetManageableByRank = selfRoleRank < 0 || targetRoleRank < selfRoleRank;
|
|
353
|
+
var controlsEnabled = canManage && !isSelf && targetManageableByRank;
|
|
354
|
+
var toggle = memberNode.querySelector("[data-chat-member-manage-toggle]");
|
|
355
|
+
var rolePanel = memberNode.querySelector("[data-chat-member-manage-panel]");
|
|
356
|
+
var moderationPanel = memberNode.querySelector("[data-chat-member-moderation-panel]");
|
|
357
|
+
var actionsDiv = memberNode.querySelector(".chat-member-actions");
|
|
358
|
+
|
|
359
|
+
var roleFormVisible = canGrant && controlsEnabled;
|
|
360
|
+
var moderationVisible = (canMute || canBan) && controlsEnabled;
|
|
361
|
+
var anyActionVisible = roleFormVisible || moderationVisible;
|
|
362
|
+
|
|
363
|
+
if (isSelf) {
|
|
364
|
+
if (toggle) toggle.hidden = true;
|
|
365
|
+
if (actionsDiv) actionsDiv.style.display = "none";
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (actionsDiv) actionsDiv.style.display = "";
|
|
370
|
+
|
|
371
|
+
memberNode.querySelectorAll("[data-chat-member-role-input], [data-chat-member-role-submit]").forEach(function (control) {
|
|
372
|
+
control.disabled = !roleFormVisible;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
if (moderationPanel) {
|
|
376
|
+
moderationPanel.hidden = !moderationVisible;
|
|
377
|
+
var muteBtn = moderationPanel.querySelector("[data-chat-member-mute-action]");
|
|
378
|
+
var banBtn = moderationPanel.querySelector("[data-chat-member-ban-action]");
|
|
379
|
+
if (muteBtn) muteBtn.disabled = !(canMute && controlsEnabled);
|
|
380
|
+
if (banBtn) banBtn.disabled = !(canBan && controlsEnabled);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (toggle) {
|
|
384
|
+
toggle.hidden = !anyActionVisible;
|
|
385
|
+
toggle.disabled = !anyActionVisible;
|
|
386
|
+
if (!anyActionVisible) {
|
|
387
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (rolePanel && !roleFormVisible) {
|
|
392
|
+
rolePanel.hidden = true;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function setupMemberListSync(memberList) {
|
|
398
|
+
if (!memberList || memberList.dataset.chatMemberSyncBound === "true") {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
var chatId = String(memberList.dataset.chatId || "").trim();
|
|
403
|
+
if (!chatId) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
memberList.dataset.chatMemberSyncBound = "true";
|
|
408
|
+
setupMemberManageControlsForChat(chatId);
|
|
409
|
+
syncComposerMentionOptionsForChat(chatId);
|
|
410
|
+
syncRoleFormAccessForChat(chatId);
|
|
411
|
+
|
|
412
|
+
var observer = new MutationObserver(function (mutationRecords) {
|
|
413
|
+
syncInviteOptionsFromMemberMutations(chatId, mutationRecords);
|
|
414
|
+
setupMemberManageControlsForChat(chatId);
|
|
415
|
+
syncComposerMentionOptionsForChat(chatId);
|
|
416
|
+
syncRoleFormAccessForChat(chatId);
|
|
417
|
+
});
|
|
418
|
+
observer.observe(memberList, { childList: true });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function setupAllMemberListSync() {
|
|
422
|
+
document.querySelectorAll("[data-chat-member-list]").forEach(setupMemberListSync);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
namespace.setupAllMemberListSync = setupAllMemberListSync;
|
|
426
|
+
})();
|