clay-server 2.27.0-beta.9 → 2.27.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/README.md +10 -0
- package/lib/daemon-projects.js +164 -0
- package/lib/daemon.js +13 -126
- package/lib/mates-identity.js +132 -0
- package/lib/mates-knowledge.js +113 -0
- package/lib/mates-prompts.js +398 -0
- package/lib/mates.js +40 -599
- package/lib/project-connection.js +2 -0
- package/lib/project-http.js +4 -2
- package/lib/project-loop.js +110 -48
- package/lib/project-mate-interaction.js +4 -0
- package/lib/project-notifications.js +210 -0
- package/lib/project-sessions.js +5 -2
- package/lib/project-user-message.js +2 -1
- package/lib/project.js +26 -2
- package/lib/public/app.js +1193 -8517
- package/lib/public/css/command-palette.css +14 -0
- package/lib/public/css/loop.css +301 -0
- package/lib/public/css/notifications-center.css +190 -0
- package/lib/public/css/rewind.css +6 -0
- package/lib/public/index.html +89 -35
- package/lib/public/modules/app-connection.js +160 -0
- package/lib/public/modules/app-cursors.js +473 -0
- package/lib/public/modules/app-debate-ui.js +389 -0
- package/lib/public/modules/app-dm.js +627 -0
- package/lib/public/modules/app-favicon.js +212 -0
- package/lib/public/modules/app-header.js +229 -0
- package/lib/public/modules/app-home-hub.js +600 -0
- package/lib/public/modules/app-loop-ui.js +589 -0
- package/lib/public/modules/app-loop-wizard.js +439 -0
- package/lib/public/modules/app-messages.js +1560 -0
- package/lib/public/modules/app-misc.js +299 -0
- package/lib/public/modules/app-notifications.js +372 -0
- package/lib/public/modules/app-panels.js +888 -0
- package/lib/public/modules/app-projects.js +798 -0
- package/lib/public/modules/app-rate-limit.js +451 -0
- package/lib/public/modules/app-rendering.js +597 -0
- package/lib/public/modules/app-skills-install.js +234 -0
- package/lib/public/modules/command-palette.js +27 -4
- package/lib/public/modules/input.js +31 -20
- package/lib/public/modules/scheduler-config.js +1532 -0
- package/lib/public/modules/scheduler-history.js +79 -0
- package/lib/public/modules/scheduler.js +33 -1554
- package/lib/public/modules/session-search.js +13 -1
- package/lib/public/modules/sidebar-mates.js +812 -0
- package/lib/public/modules/sidebar-mobile.js +1269 -0
- package/lib/public/modules/sidebar-projects.js +1449 -0
- package/lib/public/modules/sidebar-sessions.js +986 -0
- package/lib/public/modules/sidebar.js +232 -4591
- package/lib/public/modules/store.js +27 -0
- package/lib/public/modules/ws-ref.js +7 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +96 -717
- package/lib/sdk-message-processor.js +587 -0
- package/lib/sdk-message-queue.js +42 -0
- package/lib/sdk-skill-discovery.js +131 -0
- package/lib/server-admin.js +712 -0
- package/lib/server-auth.js +737 -0
- package/lib/server-dm.js +221 -0
- package/lib/server-mates.js +281 -0
- package/lib/server-palette.js +110 -0
- package/lib/server-settings.js +479 -0
- package/lib/server-skills.js +280 -0
- package/lib/server.js +246 -2755
- package/lib/sessions.js +11 -4
- package/lib/users-auth.js +146 -0
- package/lib/users-permissions.js +118 -0
- package/lib/users-preferences.js +210 -0
- package/lib/users.js +48 -398
- package/lib/ws-schema.js +498 -0
- package/package.json +1 -1
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
// sidebar-mates.js - User/mate strip, DM picker, context menus, tooltips, presence
|
|
2
|
+
// Extracted from sidebar.js (PR-37)
|
|
3
|
+
|
|
4
|
+
import { userAvatarUrl, mateAvatarUrl } from './avatar.js';
|
|
5
|
+
import { escapeHtml } from './utils.js';
|
|
6
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
7
|
+
import { showMateProfilePopover } from './profile.js';
|
|
8
|
+
|
|
9
|
+
var _ctx = null;
|
|
10
|
+
|
|
11
|
+
// --- User strip state ---
|
|
12
|
+
var cachedAllUsers = [];
|
|
13
|
+
var cachedOnlineUserIds = [];
|
|
14
|
+
var cachedDmFavorites = [];
|
|
15
|
+
var cachedDmConversations = [];
|
|
16
|
+
var cachedDmUnread = {};
|
|
17
|
+
var cachedMyUserId = null;
|
|
18
|
+
var currentDmUserId = null;
|
|
19
|
+
var dmPickerOpen = false;
|
|
20
|
+
var cachedDmRemovedUsers = {};
|
|
21
|
+
var cachedMates = [];
|
|
22
|
+
|
|
23
|
+
// --- Icon strip tooltip ---
|
|
24
|
+
var iconStripTooltip = null;
|
|
25
|
+
|
|
26
|
+
// --- DM user context menu ---
|
|
27
|
+
var userCtxMenu = null;
|
|
28
|
+
|
|
29
|
+
export function initSidebarMates(ctx) {
|
|
30
|
+
_ctx = ctx;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function showIconTooltip(el, text) {
|
|
34
|
+
hideIconTooltip();
|
|
35
|
+
var tip = document.createElement("div");
|
|
36
|
+
tip.className = "icon-strip-tooltip";
|
|
37
|
+
tip.textContent = text;
|
|
38
|
+
document.body.appendChild(tip);
|
|
39
|
+
iconStripTooltip = tip;
|
|
40
|
+
|
|
41
|
+
requestAnimationFrame(function () {
|
|
42
|
+
var rect = el.getBoundingClientRect();
|
|
43
|
+
tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
|
|
44
|
+
tip.classList.add("visible");
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function showIconTooltipHtml(el, html) {
|
|
49
|
+
hideIconTooltip();
|
|
50
|
+
var tip = document.createElement("div");
|
|
51
|
+
tip.className = "icon-strip-tooltip";
|
|
52
|
+
tip.style.whiteSpace = "normal";
|
|
53
|
+
tip.style.maxWidth = "260px";
|
|
54
|
+
tip.innerHTML = html;
|
|
55
|
+
document.body.appendChild(tip);
|
|
56
|
+
iconStripTooltip = tip;
|
|
57
|
+
|
|
58
|
+
requestAnimationFrame(function () {
|
|
59
|
+
var rect = el.getBoundingClientRect();
|
|
60
|
+
tip.style.top = (rect.top + rect.height / 2 - tip.offsetHeight / 2) + "px";
|
|
61
|
+
tip.classList.add("visible");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function hideIconTooltip() {
|
|
66
|
+
if (iconStripTooltip) {
|
|
67
|
+
iconStripTooltip.remove();
|
|
68
|
+
iconStripTooltip = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function closeUserCtxMenu() {
|
|
73
|
+
if (userCtxMenu) {
|
|
74
|
+
userCtxMenu.remove();
|
|
75
|
+
userCtxMenu = null;
|
|
76
|
+
}
|
|
77
|
+
document.removeEventListener("click", handleUserCtxOutsideClick, true);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function showUserCtxMenu(anchorEl, user) {
|
|
81
|
+
closeUserCtxMenu();
|
|
82
|
+
if (_ctx.closeProjectCtxMenu) _ctx.closeProjectCtxMenu();
|
|
83
|
+
|
|
84
|
+
var menu = document.createElement("div");
|
|
85
|
+
menu.className = "project-ctx-menu";
|
|
86
|
+
|
|
87
|
+
var removeItem = document.createElement("button");
|
|
88
|
+
removeItem.className = "project-ctx-item project-ctx-delete";
|
|
89
|
+
removeItem.innerHTML = iconHtml("user-minus") + " <span>Remove from favorites</span>";
|
|
90
|
+
removeItem.addEventListener("click", function (e) {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
// Spawn dust particles at the user icon position
|
|
93
|
+
var iconRect = anchorEl.getBoundingClientRect();
|
|
94
|
+
if (_ctx.spawnDustParticles) _ctx.spawnDustParticles(iconRect.left + iconRect.width / 2, iconRect.top + iconRect.height / 2);
|
|
95
|
+
closeUserCtxMenu();
|
|
96
|
+
// Immediately mark as removed so strip re-render hides the icon,
|
|
97
|
+
// even if the user was only visible via cachedDmConversations (not favorites)
|
|
98
|
+
cachedDmRemovedUsers[user.id] = true;
|
|
99
|
+
if (_ctx.onDmRemoveUser) _ctx.onDmRemoveUser(user.id);
|
|
100
|
+
renderUserStrip(cachedAllUsers, cachedOnlineUserIds, cachedMyUserId, cachedDmFavorites, cachedDmConversations, cachedDmUnread, cachedDmRemovedUsers, cachedMates);
|
|
101
|
+
if (_ctx.sendWs) {
|
|
102
|
+
_ctx.sendWs({ type: "dm_remove_favorite", targetUserId: user.id });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
menu.appendChild(removeItem);
|
|
106
|
+
|
|
107
|
+
document.body.appendChild(menu);
|
|
108
|
+
userCtxMenu = menu;
|
|
109
|
+
refreshIcons();
|
|
110
|
+
|
|
111
|
+
requestAnimationFrame(function () {
|
|
112
|
+
var rect = anchorEl.getBoundingClientRect();
|
|
113
|
+
menu.style.position = "fixed";
|
|
114
|
+
menu.style.left = (rect.right + 6) + "px";
|
|
115
|
+
menu.style.top = rect.top + "px";
|
|
116
|
+
var menuRect = menu.getBoundingClientRect();
|
|
117
|
+
if (menuRect.right > window.innerWidth - 8) {
|
|
118
|
+
menu.style.left = (rect.left - menuRect.width - 6) + "px";
|
|
119
|
+
}
|
|
120
|
+
if (menuRect.bottom > window.innerHeight - 8) {
|
|
121
|
+
menu.style.top = (window.innerHeight - menuRect.height - 8) + "px";
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Close on outside click
|
|
126
|
+
setTimeout(function () {
|
|
127
|
+
document.addEventListener("click", handleUserCtxOutsideClick, true);
|
|
128
|
+
}, 0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function handleUserCtxOutsideClick(e) {
|
|
132
|
+
if (userCtxMenu && !userCtxMenu.contains(e.target)) {
|
|
133
|
+
closeUserCtxMenu();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function showMateCtxMenu(anchorEl, mate) {
|
|
138
|
+
// Primary mates cannot be edited or removed
|
|
139
|
+
if (mate.primary) return;
|
|
140
|
+
|
|
141
|
+
closeUserCtxMenu();
|
|
142
|
+
if (_ctx.closeProjectCtxMenu) _ctx.closeProjectCtxMenu();
|
|
143
|
+
|
|
144
|
+
var menu = document.createElement("div");
|
|
145
|
+
menu.className = "project-ctx-menu";
|
|
146
|
+
|
|
147
|
+
// Edit Profile item
|
|
148
|
+
var editItem = document.createElement("button");
|
|
149
|
+
editItem.className = "project-ctx-item";
|
|
150
|
+
editItem.innerHTML = iconHtml("edit-2") + " <span>Edit Profile</span>";
|
|
151
|
+
editItem.addEventListener("click", function (e) {
|
|
152
|
+
e.stopPropagation();
|
|
153
|
+
closeUserCtxMenu();
|
|
154
|
+
showMateProfilePopover(anchorEl, mate, function (updates) {
|
|
155
|
+
if (_ctx.sendWs) {
|
|
156
|
+
_ctx.sendWs({ type: "mate_update", mateId: mate.id, updates: updates });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
menu.appendChild(editItem);
|
|
161
|
+
|
|
162
|
+
var removeItem = document.createElement("button");
|
|
163
|
+
removeItem.className = "project-ctx-item";
|
|
164
|
+
removeItem.innerHTML = iconHtml("star-off") + " <span>Remove from favorites</span>";
|
|
165
|
+
removeItem.addEventListener("click", function (e) {
|
|
166
|
+
e.stopPropagation();
|
|
167
|
+
closeUserCtxMenu();
|
|
168
|
+
// Spawn dust particles at the mate icon position
|
|
169
|
+
var iconRect = anchorEl.getBoundingClientRect();
|
|
170
|
+
if (_ctx.spawnDustParticles) _ctx.spawnDustParticles(iconRect.left + iconRect.width / 2, iconRect.top + iconRect.height / 2);
|
|
171
|
+
if (_ctx.sendWs) {
|
|
172
|
+
_ctx.sendWs({ type: "dm_remove_favorite", targetUserId: mate.id });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
menu.appendChild(removeItem);
|
|
176
|
+
|
|
177
|
+
document.body.appendChild(menu);
|
|
178
|
+
userCtxMenu = menu;
|
|
179
|
+
refreshIcons();
|
|
180
|
+
|
|
181
|
+
requestAnimationFrame(function () {
|
|
182
|
+
var rect = anchorEl.getBoundingClientRect();
|
|
183
|
+
menu.style.position = "fixed";
|
|
184
|
+
menu.style.left = (rect.right + 6) + "px";
|
|
185
|
+
menu.style.top = rect.top + "px";
|
|
186
|
+
var menuRect = menu.getBoundingClientRect();
|
|
187
|
+
if (menuRect.right > window.innerWidth - 8) {
|
|
188
|
+
menu.style.left = (rect.left - menuRect.width - 6) + "px";
|
|
189
|
+
}
|
|
190
|
+
if (menuRect.bottom > window.innerHeight - 8) {
|
|
191
|
+
menu.style.top = (window.innerHeight - menuRect.height - 8) + "px";
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
setTimeout(function () {
|
|
196
|
+
document.addEventListener("click", handleUserCtxOutsideClick, true);
|
|
197
|
+
}, 0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var _lastSidebarPresenceIds = [];
|
|
201
|
+
export function renderSidebarPresence(onlineUsers) {
|
|
202
|
+
var container = document.getElementById("sidebar-presence");
|
|
203
|
+
if (!container) return;
|
|
204
|
+
if (!onlineUsers || onlineUsers.length < 2) {
|
|
205
|
+
if (_lastSidebarPresenceIds.length > 0) {
|
|
206
|
+
_lastSidebarPresenceIds = [];
|
|
207
|
+
container.innerHTML = "";
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Skip re-render if same users
|
|
212
|
+
var newIds = onlineUsers.map(function (u) { return u.id; }).sort();
|
|
213
|
+
if (newIds.length === _lastSidebarPresenceIds.length && newIds.every(function (id, i) { return id === _lastSidebarPresenceIds[i]; })) return;
|
|
214
|
+
_lastSidebarPresenceIds = newIds;
|
|
215
|
+
container.innerHTML = "";
|
|
216
|
+
var maxShow = 4;
|
|
217
|
+
for (var i = 0; i < Math.min(onlineUsers.length, maxShow); i++) {
|
|
218
|
+
var ou = onlineUsers[i];
|
|
219
|
+
var img = document.createElement("img");
|
|
220
|
+
img.className = "sidebar-presence-avatar";
|
|
221
|
+
img.src = presenceAvatarUrl(ou);
|
|
222
|
+
img.alt = ou.displayName;
|
|
223
|
+
img.dataset.tip = ou.displayName + " (@" + ou.username + ")";
|
|
224
|
+
container.appendChild(img);
|
|
225
|
+
}
|
|
226
|
+
if (onlineUsers.length > maxShow) {
|
|
227
|
+
var more = document.createElement("span");
|
|
228
|
+
more.className = "sidebar-presence-more";
|
|
229
|
+
more.textContent = "+" + (onlineUsers.length - maxShow);
|
|
230
|
+
container.appendChild(more);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Presence avatar URL helper
|
|
235
|
+
function presenceAvatarUrl(userOrStyle) {
|
|
236
|
+
if (userOrStyle && typeof userOrStyle === "object") return userAvatarUrl(userOrStyle, 24);
|
|
237
|
+
return userAvatarUrl({ avatarStyle: userOrStyle || "thumbs" }, 24);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites, dmConversations, dmUnread, dmRemovedUsers, matesList) {
|
|
241
|
+
cachedMates = matesList || cachedMates || [];
|
|
242
|
+
cachedAllUsers = allUsers || [];
|
|
243
|
+
cachedOnlineUserIds = onlineUserIds || [];
|
|
244
|
+
cachedDmFavorites = dmFavorites || [];
|
|
245
|
+
cachedDmConversations = dmConversations || [];
|
|
246
|
+
cachedDmUnread = dmUnread || {};
|
|
247
|
+
cachedDmRemovedUsers = dmRemovedUsers || {};
|
|
248
|
+
cachedMyUserId = myUserId;
|
|
249
|
+
var container = document.getElementById("icon-strip-users");
|
|
250
|
+
if (!container) return;
|
|
251
|
+
|
|
252
|
+
// All other users
|
|
253
|
+
var allOthers = cachedAllUsers.filter(function (u) { return u.id !== myUserId; });
|
|
254
|
+
|
|
255
|
+
// Hide section if no other users and no mates
|
|
256
|
+
if (allOthers.length === 0 && cachedMates.length === 0) {
|
|
257
|
+
container.innerHTML = "";
|
|
258
|
+
container.classList.add("hidden");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Filter to show only: favorites + users with unread + users with DM conversations
|
|
263
|
+
// But exclude users explicitly removed from favorites
|
|
264
|
+
var others = allOthers.filter(function (u) {
|
|
265
|
+
if (cachedDmRemovedUsers[u.id]) return false;
|
|
266
|
+
if (cachedDmFavorites.indexOf(u.id) !== -1) return true;
|
|
267
|
+
if (cachedDmUnread[u.id] && cachedDmUnread[u.id] > 0) return true;
|
|
268
|
+
if (cachedDmConversations.indexOf(u.id) !== -1) return true;
|
|
269
|
+
return false;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
container.classList.remove("hidden");
|
|
273
|
+
container.innerHTML = "";
|
|
274
|
+
|
|
275
|
+
for (var i = 0; i < others.length; i++) {
|
|
276
|
+
(function (u) {
|
|
277
|
+
var el = document.createElement("div");
|
|
278
|
+
el.className = "icon-strip-user";
|
|
279
|
+
el.dataset.userId = u.id;
|
|
280
|
+
if (u.id === currentDmUserId) el.classList.add("active");
|
|
281
|
+
if (onlineUserIds.indexOf(u.id) !== -1) el.classList.add("online");
|
|
282
|
+
|
|
283
|
+
var pill = document.createElement("span");
|
|
284
|
+
pill.className = "icon-strip-pill";
|
|
285
|
+
el.appendChild(pill);
|
|
286
|
+
|
|
287
|
+
var avatar = document.createElement("img");
|
|
288
|
+
avatar.className = "icon-strip-user-avatar";
|
|
289
|
+
avatar.src = userAvatarUrl(u, 34);
|
|
290
|
+
avatar.alt = u.displayName;
|
|
291
|
+
el.appendChild(avatar);
|
|
292
|
+
|
|
293
|
+
var onlineDot = document.createElement("span");
|
|
294
|
+
onlineDot.className = "icon-strip-user-online";
|
|
295
|
+
el.appendChild(onlineDot);
|
|
296
|
+
|
|
297
|
+
var badge = document.createElement("span");
|
|
298
|
+
badge.className = "icon-strip-user-badge";
|
|
299
|
+
badge.dataset.userId = u.id;
|
|
300
|
+
el.appendChild(badge);
|
|
301
|
+
|
|
302
|
+
// Tooltip
|
|
303
|
+
el.addEventListener("mouseenter", function () { showIconTooltip(el, u.displayName); });
|
|
304
|
+
el.addEventListener("mouseleave", hideIconTooltip);
|
|
305
|
+
|
|
306
|
+
// Click: open DM
|
|
307
|
+
el.addEventListener("click", function () {
|
|
308
|
+
if (_ctx.openDm) _ctx.openDm(u.id);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Right-click: show context menu
|
|
312
|
+
el.addEventListener("contextmenu", function (e) {
|
|
313
|
+
e.preventDefault();
|
|
314
|
+
e.stopPropagation();
|
|
315
|
+
showUserCtxMenu(el, u);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
container.appendChild(el);
|
|
319
|
+
})(others[i]);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Build mate project status lookup from project list
|
|
323
|
+
var mateProjectStatus = {};
|
|
324
|
+
if (_ctx && _ctx.projectList) {
|
|
325
|
+
var allProjects = _ctx.projectList;
|
|
326
|
+
for (var pi = 0; pi < allProjects.length; pi++) {
|
|
327
|
+
if (allProjects[pi].isMate) {
|
|
328
|
+
mateProjectStatus[allProjects[pi].slug] = allProjects[pi];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Render mates (only favorites, built-in first, then user-created)
|
|
334
|
+
var favoriteMates = cachedMates.filter(function (m) {
|
|
335
|
+
if (cachedDmRemovedUsers[m.id]) return false;
|
|
336
|
+
if (cachedDmFavorites.indexOf(m.id) !== -1) return true;
|
|
337
|
+
if (cachedDmUnread[m.id] && cachedDmUnread[m.id] > 0) return true;
|
|
338
|
+
return false;
|
|
339
|
+
});
|
|
340
|
+
var sortedMates = favoriteMates.sort(function (a, b) {
|
|
341
|
+
var aBuiltin = a.builtinKey ? 1 : 0;
|
|
342
|
+
var bBuiltin = b.builtinKey ? 1 : 0;
|
|
343
|
+
if (aBuiltin !== bBuiltin) return bBuiltin - aBuiltin;
|
|
344
|
+
return (a.createdAt || 0) - (b.createdAt || 0);
|
|
345
|
+
});
|
|
346
|
+
for (var mi = 0; mi < sortedMates.length; mi++) {
|
|
347
|
+
(function (mate) {
|
|
348
|
+
var mp = mate.profile || {};
|
|
349
|
+
var mateSlug = "mate-" + mate.id;
|
|
350
|
+
var mateProj = mateProjectStatus[mateSlug] || {};
|
|
351
|
+
var isActive = mate.id === currentDmUserId;
|
|
352
|
+
var el = document.createElement("div");
|
|
353
|
+
el.className = "icon-strip-user icon-strip-mate";
|
|
354
|
+
el.dataset.userId = mate.id;
|
|
355
|
+
el.dataset.mateSlug = mateSlug;
|
|
356
|
+
if (isActive) el.classList.add("active");
|
|
357
|
+
|
|
358
|
+
// Pending permission shake
|
|
359
|
+
if (mateProj.pendingPermissions > 0 && !isActive) {
|
|
360
|
+
el.classList.add("has-pending-perm");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
var pill = document.createElement("span");
|
|
364
|
+
pill.className = "icon-strip-pill";
|
|
365
|
+
el.appendChild(pill);
|
|
366
|
+
|
|
367
|
+
var avatar = document.createElement("img");
|
|
368
|
+
avatar.className = "icon-strip-user-avatar" + (mate.primary ? " icon-strip-primary-mate" : "");
|
|
369
|
+
avatar.src = mateAvatarUrl(mate, 34);
|
|
370
|
+
avatar.alt = mp.displayName || mate.name || "Mate";
|
|
371
|
+
var mateColor = (mp.avatarColor) || mate.avatarColor || "#7c3aed";
|
|
372
|
+
avatar.style.background = mateColor + "30";
|
|
373
|
+
el.appendChild(avatar);
|
|
374
|
+
|
|
375
|
+
// Processing status dot (IO blink)
|
|
376
|
+
var statusDot = document.createElement("span");
|
|
377
|
+
statusDot.className = "icon-strip-status";
|
|
378
|
+
if (mateProj.isProcessing) statusDot.classList.add("processing");
|
|
379
|
+
el.appendChild(statusDot);
|
|
380
|
+
|
|
381
|
+
// Mate badge (bot icon)
|
|
382
|
+
var mateBadge = document.createElement("span");
|
|
383
|
+
mateBadge.className = "icon-strip-user-mate-badge";
|
|
384
|
+
mateBadge.innerHTML = iconHtml("bot");
|
|
385
|
+
el.appendChild(mateBadge);
|
|
386
|
+
|
|
387
|
+
var badge = document.createElement("span");
|
|
388
|
+
badge.className = "icon-strip-user-badge";
|
|
389
|
+
badge.dataset.userId = mate.id;
|
|
390
|
+
el.appendChild(badge);
|
|
391
|
+
|
|
392
|
+
// Restore unread badge if cached
|
|
393
|
+
var unreadCount = cachedDmUnread[mate.id] || 0;
|
|
394
|
+
if (unreadCount > 0 && !isActive) {
|
|
395
|
+
badge.textContent = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
396
|
+
badge.classList.add("has-unread");
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Tooltip
|
|
400
|
+
var displayName = mp.displayName || mate.name || "New Mate";
|
|
401
|
+
el.addEventListener("mouseenter", function () {
|
|
402
|
+
var html = '<div style="font-weight:600">' + escapeHtml(displayName);
|
|
403
|
+
if (mate.primary) {
|
|
404
|
+
html += ' <span style="font-size:10px;font-weight:600;color:#00b894;background:rgba(0,184,148,0.1);padding:1px 5px;border-radius:3px;margin-left:4px">SYSTEM</span>';
|
|
405
|
+
}
|
|
406
|
+
html += '</div>';
|
|
407
|
+
if (mate.bio) {
|
|
408
|
+
html += '<div style="font-weight:400;font-size:12px;color:var(--text-secondary);margin-top:2px">' + escapeHtml(mate.bio) + '</div>';
|
|
409
|
+
}
|
|
410
|
+
showIconTooltipHtml(el, html);
|
|
411
|
+
});
|
|
412
|
+
el.addEventListener("mouseleave", hideIconTooltip);
|
|
413
|
+
|
|
414
|
+
// Click: open DM with mate
|
|
415
|
+
el.addEventListener("click", function () {
|
|
416
|
+
if (_ctx.openDm) _ctx.openDm(mate.id);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Right-click: context menu for mate
|
|
420
|
+
el.addEventListener("contextmenu", function (e) {
|
|
421
|
+
e.preventDefault();
|
|
422
|
+
e.stopPropagation();
|
|
423
|
+
showMateCtxMenu(el, mate);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
container.appendChild(el);
|
|
427
|
+
})(sortedMates[mi]);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Show container if we have mates even with no other users
|
|
431
|
+
if (cachedMates.length > 0) {
|
|
432
|
+
container.classList.remove("hidden");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Add user (+) button
|
|
436
|
+
var addBtn = document.createElement("button");
|
|
437
|
+
addBtn.className = "icon-strip-invite";
|
|
438
|
+
addBtn.innerHTML = iconHtml("user-plus");
|
|
439
|
+
addBtn.addEventListener("click", function (e) {
|
|
440
|
+
e.stopPropagation();
|
|
441
|
+
toggleDmUserPicker(addBtn);
|
|
442
|
+
});
|
|
443
|
+
addBtn.addEventListener("mouseenter", function () { showIconTooltip(addBtn, "Add user or create mate"); });
|
|
444
|
+
addBtn.addEventListener("mouseleave", hideIconTooltip);
|
|
445
|
+
container.appendChild(addBtn);
|
|
446
|
+
refreshIcons();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function toggleDmUserPicker(anchorEl) {
|
|
450
|
+
if (dmPickerOpen) {
|
|
451
|
+
closeDmUserPicker();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
dmPickerOpen = true;
|
|
455
|
+
|
|
456
|
+
var picker = document.createElement("div");
|
|
457
|
+
picker.className = "dm-user-picker";
|
|
458
|
+
picker.id = "dm-user-picker";
|
|
459
|
+
|
|
460
|
+
// Search input
|
|
461
|
+
var searchInput = document.createElement("input");
|
|
462
|
+
searchInput.className = "dm-user-picker-search";
|
|
463
|
+
searchInput.type = "text";
|
|
464
|
+
searchInput.placeholder = "Search mates and users...";
|
|
465
|
+
picker.appendChild(searchInput);
|
|
466
|
+
|
|
467
|
+
// User list element (appended later, after USERS label)
|
|
468
|
+
var listEl = document.createElement("div");
|
|
469
|
+
listEl.className = "dm-user-picker-list";
|
|
470
|
+
|
|
471
|
+
// Position the picker above the + button
|
|
472
|
+
document.body.appendChild(picker);
|
|
473
|
+
var rect = anchorEl.getBoundingClientRect();
|
|
474
|
+
picker.style.left = (rect.right + 8) + "px";
|
|
475
|
+
picker.style.bottom = (window.innerHeight - rect.bottom) + "px";
|
|
476
|
+
|
|
477
|
+
function renderPickerList(filter) {
|
|
478
|
+
listEl.innerHTML = "";
|
|
479
|
+
var allOthers = cachedAllUsers.filter(function (u) { return u.id !== cachedMyUserId; });
|
|
480
|
+
// Exclude already-favorited users
|
|
481
|
+
var available = allOthers.filter(function (u) {
|
|
482
|
+
return cachedDmFavorites.indexOf(u.id) === -1;
|
|
483
|
+
});
|
|
484
|
+
if (filter) {
|
|
485
|
+
var lf = filter.toLowerCase();
|
|
486
|
+
available = available.filter(function (u) {
|
|
487
|
+
return (u.displayName && u.displayName.toLowerCase().indexOf(lf) !== -1) ||
|
|
488
|
+
(u.username && u.username.toLowerCase().indexOf(lf) !== -1);
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
if (available.length === 0) {
|
|
492
|
+
var emptyEl = document.createElement("div");
|
|
493
|
+
emptyEl.className = "dm-user-picker-empty";
|
|
494
|
+
emptyEl.textContent = filter ? "No users found" : "No more users to add";
|
|
495
|
+
listEl.appendChild(emptyEl);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
for (var i = 0; i < available.length; i++) {
|
|
499
|
+
(function (u) {
|
|
500
|
+
var item = document.createElement("div");
|
|
501
|
+
item.className = "dm-user-picker-item";
|
|
502
|
+
|
|
503
|
+
var av = document.createElement("img");
|
|
504
|
+
av.className = "dm-user-picker-avatar";
|
|
505
|
+
av.src = userAvatarUrl(u, 28);
|
|
506
|
+
av.alt = u.displayName;
|
|
507
|
+
item.appendChild(av);
|
|
508
|
+
|
|
509
|
+
var name = document.createElement("span");
|
|
510
|
+
name.className = "dm-user-picker-name";
|
|
511
|
+
name.textContent = u.displayName;
|
|
512
|
+
item.appendChild(name);
|
|
513
|
+
|
|
514
|
+
item.addEventListener("click", function () {
|
|
515
|
+
if (_ctx.sendWs) {
|
|
516
|
+
_ctx.sendWs({ type: "dm_add_favorite", targetUserId: u.id });
|
|
517
|
+
}
|
|
518
|
+
closeDmUserPicker();
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
listEl.appendChild(item);
|
|
522
|
+
})(available[i]);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// --- MATES section ---
|
|
527
|
+
var matesSectionLabel = document.createElement("div");
|
|
528
|
+
matesSectionLabel.className = "dm-user-picker-section";
|
|
529
|
+
matesSectionLabel.textContent = "Mates";
|
|
530
|
+
picker.appendChild(matesSectionLabel);
|
|
531
|
+
|
|
532
|
+
var matesListEl = document.createElement("div");
|
|
533
|
+
matesListEl.className = "dm-user-picker-list dm-mates-list";
|
|
534
|
+
picker.appendChild(matesListEl);
|
|
535
|
+
|
|
536
|
+
// Update scroll gradient hint
|
|
537
|
+
function updateMatesScrollHint() {
|
|
538
|
+
var isOverflow = matesListEl.scrollHeight > matesListEl.clientHeight + 2;
|
|
539
|
+
if (!isOverflow) {
|
|
540
|
+
matesListEl.classList.add("no-overflow");
|
|
541
|
+
matesListEl.classList.remove("scrolled-bottom");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
matesListEl.classList.remove("no-overflow");
|
|
545
|
+
var atBottom = matesListEl.scrollTop + matesListEl.clientHeight >= matesListEl.scrollHeight - 4;
|
|
546
|
+
if (atBottom) {
|
|
547
|
+
matesListEl.classList.add("scrolled-bottom");
|
|
548
|
+
} else {
|
|
549
|
+
matesListEl.classList.remove("scrolled-bottom");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
matesListEl.addEventListener("scroll", updateMatesScrollHint);
|
|
553
|
+
|
|
554
|
+
function renderMatesList(filter) {
|
|
555
|
+
matesListEl.innerHTML = "";
|
|
556
|
+
var allMates = cachedMates || [];
|
|
557
|
+
if (filter) {
|
|
558
|
+
var lf = filter.toLowerCase();
|
|
559
|
+
allMates = allMates.filter(function (m) {
|
|
560
|
+
var name = (m.profile && m.profile.displayName) || m.name || "";
|
|
561
|
+
return name.toLowerCase().indexOf(lf) !== -1;
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// Build unified list: installed builtins, deleted builtins, user-created
|
|
565
|
+
var availBuiltins = (_ctx.availableBuiltins && _ctx.availableBuiltins()) || [];
|
|
566
|
+
var entries = [];
|
|
567
|
+
// 1. Installed builtin mates
|
|
568
|
+
for (var si = 0; si < allMates.length; si++) {
|
|
569
|
+
if (allMates[si].builtinKey) entries.push({ type: "mate", data: allMates[si] });
|
|
570
|
+
}
|
|
571
|
+
// 2. Deleted builtins (only when not filtering)
|
|
572
|
+
if (!filter) {
|
|
573
|
+
for (var di = 0; di < availBuiltins.length; di++) {
|
|
574
|
+
entries.push({ type: "deleted", data: availBuiltins[di] });
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// 3. User-created mates
|
|
578
|
+
var userMates = allMates.filter(function (m) { return !m.builtinKey; });
|
|
579
|
+
userMates.sort(function (a, b) { return (a.createdAt || 0) - (b.createdAt || 0); });
|
|
580
|
+
for (var ui = 0; ui < userMates.length; ui++) {
|
|
581
|
+
entries.push({ type: "mate", data: userMates[ui] });
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
for (var i = 0; i < entries.length; i++) {
|
|
585
|
+
var entry = entries[i];
|
|
586
|
+
if (entry.type === "deleted") {
|
|
587
|
+
// Deleted builtin: show with "+ Add" button
|
|
588
|
+
(function (b) {
|
|
589
|
+
var bItem = document.createElement("div");
|
|
590
|
+
bItem.className = "dm-user-picker-item dm-user-picker-builtin-item";
|
|
591
|
+
bItem.style.opacity = "0.7";
|
|
592
|
+
var bAv = document.createElement("img");
|
|
593
|
+
bAv.className = "dm-user-picker-avatar";
|
|
594
|
+
bAv.src = mateAvatarUrl({ avatarCustom: b.avatarCustom, avatarStyle: b.avatarStyle || "bottts", avatarSeed: b.displayName, id: b.key }, 28);
|
|
595
|
+
bAv.alt = b.displayName;
|
|
596
|
+
bItem.appendChild(bAv);
|
|
597
|
+
var bNameWrap = document.createElement("div");
|
|
598
|
+
bNameWrap.style.cssText = "flex:1;min-width:0;";
|
|
599
|
+
var bName = document.createElement("span");
|
|
600
|
+
bName.className = "dm-user-picker-name";
|
|
601
|
+
bName.textContent = b.displayName;
|
|
602
|
+
bNameWrap.appendChild(bName);
|
|
603
|
+
var bBio = document.createElement("div");
|
|
604
|
+
bBio.style.cssText = "font-size:11px;color:var(--text-dimmer);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
|
|
605
|
+
bBio.textContent = b.bio || b.displayName;
|
|
606
|
+
bNameWrap.appendChild(bBio);
|
|
607
|
+
bItem.appendChild(bNameWrap);
|
|
608
|
+
var bAddBtn = document.createElement("button");
|
|
609
|
+
bAddBtn.style.cssText = "border:none;background:none;cursor:pointer;padding:2px 6px;color:var(--accent, #6366f1);font-size:12px;font-weight:600;white-space:nowrap;";
|
|
610
|
+
bAddBtn.textContent = "+ Add";
|
|
611
|
+
bAddBtn.title = "Re-add " + b.displayName;
|
|
612
|
+
bAddBtn.addEventListener("click", function (e) {
|
|
613
|
+
e.stopPropagation();
|
|
614
|
+
if (_ctx.sendWs) _ctx.sendWs({ type: "mate_readd_builtin", builtinKey: b.key });
|
|
615
|
+
closeDmUserPicker();
|
|
616
|
+
});
|
|
617
|
+
bItem.appendChild(bAddBtn);
|
|
618
|
+
bItem.addEventListener("click", function () {
|
|
619
|
+
if (_ctx.sendWs) _ctx.sendWs({ type: "mate_readd_builtin", builtinKey: b.key });
|
|
620
|
+
closeDmUserPicker();
|
|
621
|
+
});
|
|
622
|
+
matesListEl.appendChild(bItem);
|
|
623
|
+
})(entry.data);
|
|
624
|
+
} else {
|
|
625
|
+
// Normal mate
|
|
626
|
+
(function (m) {
|
|
627
|
+
var mp = m.profile || {};
|
|
628
|
+
var isFav = cachedDmFavorites.indexOf(m.id) !== -1;
|
|
629
|
+
var item = document.createElement("div");
|
|
630
|
+
item.className = "dm-user-picker-item";
|
|
631
|
+
if (isFav) item.classList.add("dm-picker-fav");
|
|
632
|
+
var av = document.createElement("img");
|
|
633
|
+
av.className = "dm-user-picker-avatar";
|
|
634
|
+
av.src = mateAvatarUrl(m, 28);
|
|
635
|
+
av.alt = mp.displayName || m.name || "Mate";
|
|
636
|
+
item.appendChild(av);
|
|
637
|
+
var nameWrap = document.createElement("div");
|
|
638
|
+
nameWrap.style.cssText = "flex:1;min-width:0;";
|
|
639
|
+
var name = document.createElement("span");
|
|
640
|
+
name.className = "dm-user-picker-name";
|
|
641
|
+
name.textContent = mp.displayName || m.name || "Mate";
|
|
642
|
+
nameWrap.appendChild(name);
|
|
643
|
+
if (m.bio) {
|
|
644
|
+
var bio = document.createElement("div");
|
|
645
|
+
bio.style.cssText = "font-size:11px;color:var(--text-dimmer);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
|
|
646
|
+
bio.textContent = m.bio;
|
|
647
|
+
nameWrap.appendChild(bio);
|
|
648
|
+
}
|
|
649
|
+
item.appendChild(nameWrap);
|
|
650
|
+
// Delete button with inline confirm
|
|
651
|
+
var delBtn = document.createElement("button");
|
|
652
|
+
delBtn.className = "dm-picker-del-btn";
|
|
653
|
+
delBtn.innerHTML = m.builtinKey ? iconHtml("minus-circle") : iconHtml("trash-2");
|
|
654
|
+
delBtn.title = m.builtinKey ? "Remove mate" : "Delete mate";
|
|
655
|
+
delBtn.addEventListener("click", function (e) {
|
|
656
|
+
e.stopPropagation();
|
|
657
|
+
var origHtml = item.innerHTML;
|
|
658
|
+
item.innerHTML = "";
|
|
659
|
+
item.style.justifyContent = "center";
|
|
660
|
+
item.style.gap = "6px";
|
|
661
|
+
var confirmMsg = document.createElement("span");
|
|
662
|
+
confirmMsg.style.cssText = "font-size:12px;color:var(--text-dimmer);";
|
|
663
|
+
confirmMsg.textContent = m.builtinKey ? "Remove? You can add back anytime." : "Delete permanently?";
|
|
664
|
+
item.appendChild(confirmMsg);
|
|
665
|
+
var yesBtn = document.createElement("button");
|
|
666
|
+
yesBtn.style.cssText = "border:none;background:var(--danger,#e74c3c);color:#fff;padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;";
|
|
667
|
+
yesBtn.textContent = m.builtinKey ? "Remove" : "Delete";
|
|
668
|
+
yesBtn.addEventListener("click", function (e2) {
|
|
669
|
+
e2.stopPropagation();
|
|
670
|
+
if (_ctx.sendWs) _ctx.sendWs({ type: "mate_delete", mateId: m.id });
|
|
671
|
+
closeDmUserPicker();
|
|
672
|
+
});
|
|
673
|
+
item.appendChild(yesBtn);
|
|
674
|
+
var noBtn = document.createElement("button");
|
|
675
|
+
noBtn.style.cssText = "border:1px solid var(--border);background:none;color:var(--text);padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;";
|
|
676
|
+
noBtn.textContent = "Cancel";
|
|
677
|
+
noBtn.addEventListener("click", function (e2) {
|
|
678
|
+
e2.stopPropagation();
|
|
679
|
+
item.innerHTML = origHtml;
|
|
680
|
+
item.style.justifyContent = "";
|
|
681
|
+
item.style.gap = "";
|
|
682
|
+
refreshIcons();
|
|
683
|
+
});
|
|
684
|
+
item.appendChild(noBtn);
|
|
685
|
+
});
|
|
686
|
+
item.appendChild(delBtn);
|
|
687
|
+
item.addEventListener("click", function () {
|
|
688
|
+
if (_ctx.openDm) _ctx.openDm(m.id);
|
|
689
|
+
if (!isFav && _ctx.sendWs) _ctx.sendWs({ type: "dm_add_favorite", targetUserId: m.id });
|
|
690
|
+
closeDmUserPicker();
|
|
691
|
+
});
|
|
692
|
+
matesListEl.appendChild(item);
|
|
693
|
+
})(entry.data);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (entries.length === 0 && filter) {
|
|
698
|
+
var emptyEl = document.createElement("div");
|
|
699
|
+
emptyEl.className = "dm-user-picker-empty";
|
|
700
|
+
emptyEl.textContent = "No mates found";
|
|
701
|
+
matesListEl.appendChild(emptyEl);
|
|
702
|
+
}
|
|
703
|
+
refreshIcons();
|
|
704
|
+
requestAnimationFrame(updateMatesScrollHint);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Create Mate option
|
|
708
|
+
var createMateEl = document.createElement("div");
|
|
709
|
+
createMateEl.className = "dm-user-picker-create-mate";
|
|
710
|
+
var hasCustomMates = (cachedMates || []).some(function (m) { return !m.builtinKey; });
|
|
711
|
+
var createMateLabel = hasCustomMates ? "Create a Mate" : "Create a Mate for what you're doing";
|
|
712
|
+
createMateEl.innerHTML = iconHtml("bot") + " <span>" + createMateLabel + "</span>";
|
|
713
|
+
createMateEl.addEventListener("click", function () {
|
|
714
|
+
closeDmUserPicker();
|
|
715
|
+
if (_ctx.openMateWizard) _ctx.openMateWizard();
|
|
716
|
+
});
|
|
717
|
+
picker.appendChild(createMateEl);
|
|
718
|
+
|
|
719
|
+
// Divider
|
|
720
|
+
var divider = document.createElement("div");
|
|
721
|
+
divider.style.borderTop = "1px solid var(--border, #333)";
|
|
722
|
+
divider.style.margin = "4px 0";
|
|
723
|
+
picker.appendChild(divider);
|
|
724
|
+
|
|
725
|
+
// Section label for users
|
|
726
|
+
var sectionLabel = document.createElement("div");
|
|
727
|
+
sectionLabel.className = "dm-user-picker-section";
|
|
728
|
+
sectionLabel.textContent = "Users";
|
|
729
|
+
picker.appendChild(sectionLabel);
|
|
730
|
+
picker.appendChild(listEl);
|
|
731
|
+
|
|
732
|
+
renderMatesList("");
|
|
733
|
+
renderPickerList("");
|
|
734
|
+
searchInput.addEventListener("input", function () {
|
|
735
|
+
var val = searchInput.value;
|
|
736
|
+
renderMatesList(val);
|
|
737
|
+
renderPickerList(val);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// Focus search
|
|
741
|
+
setTimeout(function () { searchInput.focus(); }, 50);
|
|
742
|
+
|
|
743
|
+
// Close on click outside
|
|
744
|
+
function onDocClick(e) {
|
|
745
|
+
if (!picker.contains(e.target) && e.target !== anchorEl && !anchorEl.contains(e.target)) {
|
|
746
|
+
closeDmUserPicker();
|
|
747
|
+
document.removeEventListener("click", onDocClick, true);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
setTimeout(function () {
|
|
751
|
+
document.addEventListener("click", onDocClick, true);
|
|
752
|
+
}, 10);
|
|
753
|
+
picker._docClickHandler = onDocClick;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function closeDmUserPicker() {
|
|
757
|
+
dmPickerOpen = false;
|
|
758
|
+
var picker = document.getElementById("dm-user-picker");
|
|
759
|
+
if (picker) {
|
|
760
|
+
if (picker._docClickHandler) {
|
|
761
|
+
document.removeEventListener("click", picker._docClickHandler, true);
|
|
762
|
+
}
|
|
763
|
+
picker.remove();
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export function setCurrentDmUser(userId) {
|
|
768
|
+
currentDmUserId = userId;
|
|
769
|
+
// Update active state on user icons immediately
|
|
770
|
+
var container = document.getElementById("icon-strip-users");
|
|
771
|
+
if (!container) return;
|
|
772
|
+
var items = container.querySelectorAll(".icon-strip-user");
|
|
773
|
+
for (var i = 0; i < items.length; i++) {
|
|
774
|
+
if (items[i].dataset.userId === userId) {
|
|
775
|
+
items[i].classList.add("active");
|
|
776
|
+
} else {
|
|
777
|
+
items[i].classList.remove("active");
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
export function updateDmBadge(userId, count) {
|
|
783
|
+
var badge = document.querySelector('.icon-strip-user-badge[data-user-id="' + userId + '"]');
|
|
784
|
+
if (!badge) return;
|
|
785
|
+
if (count > 0) {
|
|
786
|
+
badge.textContent = count > 99 ? "99+" : String(count);
|
|
787
|
+
badge.classList.add("has-unread");
|
|
788
|
+
} else {
|
|
789
|
+
badge.textContent = "";
|
|
790
|
+
badge.classList.remove("has-unread");
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export function getCurrentDmUserId() {
|
|
795
|
+
return currentDmUserId;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function getCachedMates() {
|
|
799
|
+
return cachedMates;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
export function getCachedDmFavorites() {
|
|
803
|
+
return cachedDmFavorites;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export function getCachedDmUnread() {
|
|
807
|
+
return cachedDmUnread;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export function getCachedDmRemovedUsers() {
|
|
811
|
+
return cachedDmRemovedUsers;
|
|
812
|
+
}
|