@antzsoft/chat-core 1.0.9 → 1.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/README.md +45 -11
- package/dist/index.cjs +11 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/docs/integration-guide.html +170 -26
- package/package.json +1 -1
|
@@ -155,7 +155,7 @@ section.sec>h2:hover{color:#fff}
|
|
|
155
155
|
|
|
156
156
|
<div class="section-label">What's New</div>
|
|
157
157
|
<ul>
|
|
158
|
-
<li><a href="#whats-new">v1.0
|
|
158
|
+
<li><a href="#whats-new">v1.1.0 Release Notes</a></li>
|
|
159
159
|
</ul>
|
|
160
160
|
|
|
161
161
|
<div class="section-label">Getting Started</div>
|
|
@@ -242,12 +242,125 @@ section.sec>h2:hover{color:#fff}
|
|
|
242
242
|
<h2>What's New</h2>
|
|
243
243
|
<p style="color:var(--muted);font-size:13px;margin-bottom:20px">Version history and release notes. Click a version to expand.</p>
|
|
244
244
|
|
|
245
|
-
<!-- ── v1.0
|
|
246
|
-
<div class="wn-version open" id="wn-
|
|
245
|
+
<!-- ── v1.1.0 (current) ── -->
|
|
246
|
+
<div class="wn-version open" id="wn-110">
|
|
247
|
+
<div class="wn-header" onclick="toggleVersion('wn-110')">
|
|
248
|
+
<div class="wn-title">
|
|
249
|
+
<span class="wn-ver">v1.1.0</span>
|
|
250
|
+
<span class="wn-badge current">Current</span>
|
|
251
|
+
<span class="wn-date">May 2026</span>
|
|
252
|
+
</div>
|
|
253
|
+
<span class="wn-chevron">▲</span>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="wn-body">
|
|
256
|
+
|
|
257
|
+
<div class="wn-item" id="wn-110-1">
|
|
258
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-1')">
|
|
259
|
+
<span class="wn-tag fix">Fix</span>
|
|
260
|
+
<span class="wn-item-title">500 error on device token registration after reinstall or account switch</span>
|
|
261
|
+
<span class="wn-chevron-sm">▾</span>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="wn-item-body">
|
|
264
|
+
<p>Registering a push token (Expo, FCM, APNs) that was previously registered under a different user or device ID returned a 500. This happened because the global <code>token_unique</code> sparse index rejected the duplicate before the upsert could complete.</p>
|
|
265
|
+
<p>Common triggers: app reinstall (new <code>deviceId</code> UUID generated), user switches account on same device, or token reuse after logout/login.</p>
|
|
266
|
+
<p>The server now removes any stale record holding the same token under a different owner before performing the upsert. Registration always succeeds for the new owner.</p>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<div class="wn-item" id="wn-110-2">
|
|
271
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-2')">
|
|
272
|
+
<span class="wn-tag fix">Fix</span>
|
|
273
|
+
<span class="wn-item-title">500 error when removing a participant from a conversation</span>
|
|
274
|
+
<span class="wn-chevron-sm">▾</span>
|
|
275
|
+
</div>
|
|
276
|
+
<div class="wn-item-body">
|
|
277
|
+
<p>Removing a participant caused a server error in certain cases. Message delivery stops and socket room eviction now correctly target only the remaining active members.</p>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<div class="wn-item" id="wn-110-3">
|
|
282
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-3')">
|
|
283
|
+
<span class="wn-tag fix">Fix</span>
|
|
284
|
+
<span class="wn-item-title">Leave group now works for removed members</span>
|
|
285
|
+
<span class="wn-chevron-sm">▾</span>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="wn-item-body">
|
|
288
|
+
<p>After an admin removed a user from a group, that user still saw the conversation in their list (read-only, by design since v1.0.8). However, calling <code>leave()</code> to dismiss it returned a <strong>403 Forbidden</strong> because the active-participant guard rejected inactive users.</p>
|
|
289
|
+
<p><code>leave()</code> now uses read-access validation instead. If the caller is already inactive (removed), the conversation is hidden from their list immediately — no removal step is re-executed. If the caller is still active, the existing leave flow runs unchanged.</p>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div class="wn-item" id="wn-110-4">
|
|
294
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-4')">
|
|
295
|
+
<span class="wn-tag fix">Fix</span>
|
|
296
|
+
<span class="wn-item-title">Group auto-disbands when last active member leaves or is removed</span>
|
|
297
|
+
<span class="wn-chevron-sm">▾</span>
|
|
298
|
+
</div>
|
|
299
|
+
<div class="wn-item-body">
|
|
300
|
+
<p>When the last active participant left or was removed, the group conversation persisted in the database as an orphan with zero active members. It could never be accessed or cleaned up.</p>
|
|
301
|
+
<p>The server now marks the conversation <code>isActive: false</code> automatically when <code>remainingParticipantIds</code> drops to zero after a leave or remove operation.</p>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div class="wn-item" id="wn-110-5">
|
|
306
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-5')">
|
|
307
|
+
<span class="wn-tag fix">Fix</span>
|
|
308
|
+
<span class="wn-item-title"><code>lastMessage</code> content not updating after message edit</span>
|
|
309
|
+
<span class="wn-chevron-sm">▾</span>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="wn-item-body">
|
|
312
|
+
<p>Editing a message that was also the conversation's last message did not update <code>conversation.lastMessage.contentPreview</code>. The conversation list kept showing the original text until a new message was sent.</p>
|
|
313
|
+
<p>The server now refreshes <code>lastMessage.contentPreview</code> on edit when the edited message ID matches <code>conversation.lastMessage.messageId</code>.</p>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div class="wn-item" id="wn-110-6">
|
|
318
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-6')">
|
|
319
|
+
<span class="wn-tag fix">Fix</span>
|
|
320
|
+
<span class="wn-item-title">Reply-to attachment preview shows filename or file type instead of <code>[Attachment]</code></span>
|
|
321
|
+
<span class="wn-chevron-sm">▾</span>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="wn-item-body">
|
|
324
|
+
<p>When replying to a message that contained an attachment, the quoted preview always showed the generic string <code>[Attachment]</code>. The server now returns the filename if available, otherwise the file type (e.g. <code>image</code>, <code>video</code>, <code>document</code>).</p>
|
|
325
|
+
<p>For previewable types (images, video), clients should use the parent message's attachment data (already included in the reply payload) to render a thumbnail or inline preview.</p>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div class="wn-item" id="wn-110-7">
|
|
330
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-7')">
|
|
331
|
+
<span class="wn-tag fix">Fix</span>
|
|
332
|
+
<span class="wn-item-title">Remove reaction no longer errors when reaction does not exist</span>
|
|
333
|
+
<span class="wn-chevron-sm">▾</span>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="wn-item-body">
|
|
336
|
+
<p>Removing a reaction that had already been removed (or never existed) returned a 500. The operation is now idempotent — removing a non-existent reaction returns success.</p>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<div class="wn-item" id="wn-110-8">
|
|
341
|
+
<div class="wn-item-header" onclick="toggleItem('wn-110-8')">
|
|
342
|
+
<span class="wn-tag new">New</span>
|
|
343
|
+
<span class="wn-item-title">Pin limit — max 5 pinned conversations</span>
|
|
344
|
+
<span class="wn-chevron-sm">▾</span>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="wn-item-body">
|
|
347
|
+
<p>The server now enforces a maximum of <strong>5 pinned conversations</strong> per user. Attempting to pin a sixth returns a <code>400</code> error.</p>
|
|
348
|
+
<p>A new <code>GET /app/config</code> endpoint exposes server-side limits to clients. Use <code>appConfigApi.get()</code> to fetch them:</p>
|
|
349
|
+
<pre><code><span class="kw">const</span> config = <span class="kw">await</span> appConfigApi.<span class="fn">get</span>();
|
|
350
|
+
<span class="cm">// { maxPinnedConversations: 5 }</span></code></pre>
|
|
351
|
+
<p><code>useConversations()</code> (Web + RN) now fetches config automatically on mount, blocks the <code>pin</code> mutation client-side before it hits the API when the limit is reached, and exposes <code>maxPinnedConversations</code> for UI gating (e.g. hiding the pin option).</p>
|
|
352
|
+
<p>The RN <code>ConversationList</code> component now supports long-press to pin/unpin — this action was previously missing on React Native.</p>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
</div>
|
|
357
|
+
</div><!-- /.wn-version -->
|
|
358
|
+
|
|
359
|
+
<!-- ── v1.0.9 ── -->
|
|
360
|
+
<div class="wn-version" id="wn-109">
|
|
247
361
|
<div class="wn-header" onclick="toggleVersion('wn-109')">
|
|
248
362
|
<div class="wn-title">
|
|
249
363
|
<span class="wn-ver">v1.0.9</span>
|
|
250
|
-
<span class="wn-badge current">Current</span>
|
|
251
364
|
<span class="wn-date">May 2026</span>
|
|
252
365
|
</div>
|
|
253
366
|
<span class="wn-chevron">▲</span>
|
|
@@ -263,7 +376,6 @@ section.sec>h2:hover{color:#fff}
|
|
|
263
376
|
<div class="wn-item-body">
|
|
264
377
|
<p>On Android, the OS suspends idle WebSocket connections during long operations such as audio recording. When <code>sendMessage</code> was called immediately after a file upload completed, the socket was still reconnecting and the call failed immediately with "Socket not connected".</p>
|
|
265
378
|
<p><code>sendMessage</code> now waits up to <strong>15 seconds</strong> for Socket.IO to auto-reconnect before attempting to send. Once the connection is restored the message goes through normally — no error, no retry logic needed in the app.</p>
|
|
266
|
-
<p><strong>Action required:</strong> None. Behavior change is inside the SDK. No API or type changes.</p>
|
|
267
379
|
</div>
|
|
268
380
|
</div>
|
|
269
381
|
|
|
@@ -306,9 +418,8 @@ section.sec>h2:hover{color:#fff}
|
|
|
306
418
|
<span class="wn-chevron-sm">▾</span>
|
|
307
419
|
</div>
|
|
308
420
|
<div class="wn-item-body">
|
|
309
|
-
<p>When sending a message with an attachment and <code>text: ""</code>, the empty string was
|
|
310
|
-
<p>
|
|
311
|
-
<p><strong>Action required:</strong> None. Server-side fix only. No SDK or type changes.</p>
|
|
421
|
+
<p>When sending a message with an attachment and <code>text: ""</code>, the empty string was stored and returned instead of being treated as absent. The conversation-list preview for attachment-only messages showed blank instead of the filename or "Attachment". Both the HTTP and socket paths now trim and treat empty strings as absent before saving.</p>
|
|
422
|
+
<p><strong>Action required:</strong> None. Server-side fix only.</p>
|
|
312
423
|
</div>
|
|
313
424
|
</div>
|
|
314
425
|
|
|
@@ -319,9 +430,30 @@ section.sec>h2:hover{color:#fff}
|
|
|
319
430
|
<span class="wn-chevron-sm">▾</span>
|
|
320
431
|
</div>
|
|
321
432
|
<div class="wn-item-body">
|
|
322
|
-
<p>The <code>participant_left</code> socket event was only broadcast to the <code>conversation:<id></code> room. Users only join that room when they actively open that specific conversation — so
|
|
323
|
-
<p>
|
|
324
|
-
|
|
433
|
+
<p>The <code>participant_left</code> socket event was only broadcast to the <code>conversation:<id></code> room. Users only join that room when they actively open that specific conversation — so users on the conversation-list screen never received the event and saw a stale participant count. The server now also emits directly to each remaining participant's personal <code>user:<tenantId>:<userId></code> room, skipping users already in the conversation room to avoid double-delivery.</p>
|
|
434
|
+
<p><strong>Action required:</strong> None. Server-side fix only.</p>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<div class="wn-item" id="wn-109-6">
|
|
439
|
+
<div class="wn-item-header" onclick="toggleItem('wn-109-6')">
|
|
440
|
+
<span class="wn-tag new">New</span>
|
|
441
|
+
<span class="wn-item-title">Pin limit — max 5 pinned conversations + <code>appConfigApi</code></span>
|
|
442
|
+
<span class="wn-chevron-sm">▾</span>
|
|
443
|
+
</div>
|
|
444
|
+
<div class="wn-item-body">
|
|
445
|
+
<p>The server now enforces a maximum of <strong>5 pinned conversations</strong> per user. Attempting to pin a 6th returns <code>400 Bad Request: "You can only pin up to 5 conversations"</code>.</p>
|
|
446
|
+
<p>A new <code>GET /app/config</code> endpoint returns server-side limits so clients can gate the UI without hardcoding values:</p>
|
|
447
|
+
<pre><code><span class="kw">import</span> { appConfigApi } <span class="kw">from</span> <span class="str">'@antzsoft/chat-core'</span>;
|
|
448
|
+
<span class="kw">const</span> config = <span class="kw">await</span> appConfigApi.<span class="fn">get</span>();
|
|
449
|
+
<span class="cm">// { maxPinnedConversations: 5 }</span></code></pre>
|
|
450
|
+
<ul>
|
|
451
|
+
<li><code>useConversations()</code> (Web + RN) now fetches this automatically and exposes <code>maxPinnedConversations</code></li>
|
|
452
|
+
<li>The <code>pin</code> mutation in <code>useConversations()</code> throws before hitting the network if the limit is already reached</li>
|
|
453
|
+
<li>RN SDK <code>ConversationList</code> now shows pin/unpin on long-press (was missing previously)</li>
|
|
454
|
+
</ul>
|
|
455
|
+
<p><strong>Action required:</strong> None for SDK users — built-in check handles it. Raw API users should call <code>appConfigApi.get()</code> on init and gate the pin button using <code>config.maxPinnedConversations</code>.</p>
|
|
456
|
+
<p class="wn-ref">→ <a href="#step-conversations">Step 6 — Conversations</a></p>
|
|
325
457
|
</div>
|
|
326
458
|
</div>
|
|
327
459
|
|
|
@@ -329,7 +461,7 @@ section.sec>h2:hover{color:#fff}
|
|
|
329
461
|
</div>
|
|
330
462
|
|
|
331
463
|
<!-- ── v1.0.8 ── -->
|
|
332
|
-
<div class="wn-version
|
|
464
|
+
<div class="wn-version" id="wn-108">
|
|
333
465
|
<div class="wn-header" onclick="toggleVersion('wn-108')">
|
|
334
466
|
<div class="wn-title">
|
|
335
467
|
<span class="wn-ver">v1.0.8</span>
|
|
@@ -375,7 +507,6 @@ section.sec>h2:hover{color:#fff}
|
|
|
375
507
|
<li>Cannot send, react, type, edit, or delete — <code>validateWriteAccess</code> blocks all write operations</li>
|
|
376
508
|
</ul>
|
|
377
509
|
<p>This is implemented via a new <code>isHidden</code> field on the <code>Participant</code> subdocument (schema change, server only). The conversation list filter now shows conversations where the user is not hidden, regardless of <code>isActive</code>.</p>
|
|
378
|
-
<p><strong>Action required:</strong> None. Behavior change is entirely server-side. SDK types and API calls are unchanged.</p>
|
|
379
510
|
</div>
|
|
380
511
|
</div>
|
|
381
512
|
|
|
@@ -388,7 +519,6 @@ section.sec>h2:hover{color:#fff}
|
|
|
388
519
|
<div class="wn-item-body">
|
|
389
520
|
<p>Previously, when an admin called <code>deleteConversation</code> it set <code>conversation.isActive = false</code>, making it disappear for <em>all</em> participants. This was wrong — only the admin who deleted it should lose access.</p>
|
|
390
521
|
<p>Now, <code>deleteConversation</code> sets <code>participant.isHidden = true</code> for the requesting user only. All other participants continue to see the conversation normally.</p>
|
|
391
|
-
<p><strong>Action required:</strong> None. Server-side change only.</p>
|
|
392
522
|
</div>
|
|
393
523
|
</div>
|
|
394
524
|
|
|
@@ -401,7 +531,6 @@ section.sec>h2:hover{color:#fff}
|
|
|
401
531
|
<div class="wn-item-body">
|
|
402
532
|
<p>When a message was deleted, <code>lastMessage.status</code> on the conversation was set to <code>'deleted'</code>. Sending a new message afterwards never reset it — so the conversation list kept showing <code>deleted</code> as the last message state indefinitely.</p>
|
|
403
533
|
<p>Root cause: two separate code paths update <code>lastMessage</code> (REST and WebSocket) and neither was explicitly setting <code>status: 'active'</code> when writing a new message. Fixed in both paths.</p>
|
|
404
|
-
<p><strong>Action required:</strong> None. Server-side fix only.</p>
|
|
405
534
|
</div>
|
|
406
535
|
</div>
|
|
407
536
|
|
|
@@ -431,7 +560,7 @@ section.sec>h2:hover{color:#fff}
|
|
|
431
560
|
</div>
|
|
432
561
|
|
|
433
562
|
<!-- ── v1.0.7 ── -->
|
|
434
|
-
<div class="wn-version
|
|
563
|
+
<div class="wn-version" id="wn-107">
|
|
435
564
|
<div class="wn-header" onclick="toggleVersion('wn-107')">
|
|
436
565
|
<div class="wn-title">
|
|
437
566
|
<span class="wn-ver">v1.0.7</span>
|
|
@@ -468,7 +597,7 @@ section.sec>h2:hover{color:#fff}
|
|
|
468
597
|
</div>
|
|
469
598
|
<div class="wn-item-body">
|
|
470
599
|
<p>The server now accepts <code>audio/m4a</code> and <code>audio/x-m4a</code> MIME types for audio attachments. Previously only <code>audio/mpeg</code>, <code>audio/ogg</code>, and <code>audio/wav</code> were accepted — uploads of <code>.m4a</code> files returned a <code>400</code> unsupported type error.</p>
|
|
471
|
-
<p
|
|
600
|
+
<p>If you were filtering <code>.m4a</code> files out in the client before upload, remove that restriction.</p>
|
|
472
601
|
</div>
|
|
473
602
|
</div>
|
|
474
603
|
|
|
@@ -1412,10 +1541,27 @@ chatClient.<span class="fn">disconnect</span>();
|
|
|
1412
1541
|
<span class="kw">await</span> conversationsApi.<span class="fn">updateParticipantRole</span>(conversationId, <span class="str">'user-1'</span>, <span class="str">'admin'</span>);
|
|
1413
1542
|
|
|
1414
1543
|
<span class="cm">// Pin · Mute · Leave · Delete</span>
|
|
1415
|
-
<span class="kw">await</span> conversationsApi.<span class="fn">pin</span>(conversationId);
|
|
1544
|
+
<span class="kw">await</span> conversationsApi.<span class="fn">pin</span>(conversationId); <span class="cm">// 400 if already at 5 pinned</span>
|
|
1416
1545
|
<span class="kw">await</span> conversationsApi.<span class="fn">mute</span>(conversationId, <span class="str">'2025-12-31T23:59:59Z'</span>); <span class="cm">// omit date = indefinite</span>
|
|
1417
1546
|
<span class="kw">await</span> conversationsApi.<span class="fn">leave</span>(conversationId);
|
|
1418
1547
|
<span class="kw">await</span> conversationsApi.<span class="fn">delete</span>(conversationId); <span class="cm">// admin only</span></code></pre>
|
|
1548
|
+
|
|
1549
|
+
<div class="callout info">
|
|
1550
|
+
<strong>Pin limit — max 5</strong> — The server enforces a maximum of 5 pinned conversations per user. Attempting to pin a 6th returns <code>400 Bad Request</code>. Gate your UI before calling the API:
|
|
1551
|
+
<pre style="margin-top:8px"><code><span class="kw">import</span> { appConfigApi } <span class="kw">from</span> <span class="str">'@antzsoft/chat-core'</span>;
|
|
1552
|
+
|
|
1553
|
+
<span class="cm">// Fetch once on init — SDK caches under ['app-config'] with staleTime: Infinity</span>
|
|
1554
|
+
<span class="kw">const</span> config = <span class="kw">await</span> appConfigApi.<span class="fn">get</span>(); <span class="cm">// { maxPinnedConversations: 5 }</span>
|
|
1555
|
+
|
|
1556
|
+
<span class="cm">// Before calling pin:</span>
|
|
1557
|
+
<span class="kw">const</span> pinnedCount = conversations.<span class="fn">filter</span>(c => c.isPinned).length;
|
|
1558
|
+
<span class="kw">if</span> (pinnedCount >= config.maxPinnedConversations) {
|
|
1559
|
+
<span class="cm">// show error toast — do NOT call the API</span>
|
|
1560
|
+
<span class="kw">return</span>;
|
|
1561
|
+
}
|
|
1562
|
+
<span class="kw">await</span> conversationsApi.<span class="fn">pin</span>(conversationId);</code></pre>
|
|
1563
|
+
<p>If you use <code>useConversations()</code> (Web/RN SDK), this check is built in — the <code>pin</code> mutation throws before hitting the network if the limit is reached. The hook also exposes <code>maxPinnedConversations</code> to disable the pin button in your UI.</p>
|
|
1564
|
+
</div>
|
|
1419
1565
|
</section>
|
|
1420
1566
|
|
|
1421
1567
|
<!-- ─── STEP 7: ROOMS ──────────────────────────────────────────────────── -->
|
|
@@ -1957,11 +2103,11 @@ socket?.<span class="fn">on</span>(<span class="str">'read_receipt'</span>, ({ c
|
|
|
1957
2103
|
<pre><code><span class="kw">await</span> socketEmit.<span class="fn">updateMessage</span>(messageId, <span class="str">'Updated text'</span>);
|
|
1958
2104
|
<span class="cm">// REST fallback: await messagesApi.update(messageId, 'Updated text')</span></code></pre>
|
|
1959
2105
|
<div class="callout info">
|
|
1960
|
-
<strong>Edit window</strong> —
|
|
2106
|
+
<strong>Edit window</strong> — server default is <strong>900 s (15 minutes)</strong>. <code>conversation.settings.messageConfig.editWindowSeconds</code> is always populated — use it directly:
|
|
1961
2107
|
<pre style="margin-top:8px"><code><span class="kw">function</span> <span class="fn">canEdit</span>(msg: <span class="tp">Message</span>, conv: <span class="tp">Conversation</span>, userId: <span class="tp">string</span>) {
|
|
1962
2108
|
<span class="kw">if</span> (msg.senderId !== userId) <span class="kw">return false</span>;
|
|
1963
|
-
<span class="kw">const</span> w = conv.settings?.messageConfig?.editWindowSeconds
|
|
1964
|
-
<span class="kw">return</span>
|
|
2109
|
+
<span class="kw">const</span> w = conv.settings?.messageConfig?.editWindowSeconds ?? <span class="num">900</span>;
|
|
2110
|
+
<span class="kw">return</span> (Date.<span class="fn">now</span>() - <span class="kw">new</span> <span class="fn">Date</span>(msg.sentAt).<span class="fn">getTime</span>()) < w * <span class="num">1000</span>;
|
|
1965
2111
|
}</code></pre>
|
|
1966
2112
|
</div>
|
|
1967
2113
|
|
|
@@ -1975,20 +2121,18 @@ socket?.<span class="fn">on</span>(<span class="str">'read_receipt'</span>, ({ c
|
|
|
1975
2121
|
</table>
|
|
1976
2122
|
|
|
1977
2123
|
<div class="callout info">
|
|
1978
|
-
<strong>Default delete window</strong> —
|
|
2124
|
+
<strong>Default delete window</strong> — server default is <strong>216,000 seconds (60 hours)</strong>. <code>conversation.settings.messageConfig.deleteWindowSeconds</code> is always populated — use it directly. Your UI helper must use the same fallback, otherwise you may show "Delete for everyone" after the window has already expired on the server and get a <code>403 Forbidden</code> response.
|
|
1979
2125
|
</div>
|
|
1980
2126
|
|
|
1981
2127
|
<div class="callout info">
|
|
1982
2128
|
<strong>DMs have no admins</strong> — in a direct message conversation <code>conv.type === 'direct'</code>, participants have no <code>admin</code> role. <code>isAdmin</code> will always be <code>false</code>, so "Delete for everyone" is only available to the message sender within the window. Do not show an admin-based delete option in DMs.
|
|
1983
2129
|
</div>
|
|
1984
2130
|
|
|
1985
|
-
<pre><code><span class="kw">
|
|
1986
|
-
|
|
1987
|
-
<span class="kw">function</span> <span class="fn">getDeleteOptions</span>(msg: <span class="tp">Message</span>, conv: <span class="tp">Conversation</span>, userId: <span class="tp">string</span>) {
|
|
2131
|
+
<pre><code><span class="kw">function</span> <span class="fn">getDeleteOptions</span>(msg: <span class="tp">Message</span>, conv: <span class="tp">Conversation</span>, userId: <span class="tp">string</span>) {
|
|
1988
2132
|
<span class="kw">const</span> isMine = msg.senderId === userId;
|
|
1989
2133
|
<span class="kw">const</span> isAdmin = conv.type !== <span class="str">'direct'</span> &&
|
|
1990
2134
|
conv.participants.<span class="fn">find</span>(p => p.userId === userId)?.role === <span class="str">'admin'</span>;
|
|
1991
|
-
<span class="kw">const</span> w = conv.settings?.messageConfig?.deleteWindowSeconds ??
|
|
2135
|
+
<span class="kw">const</span> w = conv.settings?.messageConfig?.deleteWindowSeconds ?? <span class="num">216000</span>;
|
|
1992
2136
|
<span class="kw">const</span> inWindow = (Date.<span class="fn">now</span>() - <span class="kw">new</span> <span class="fn">Date</span>(msg.sentAt).<span class="fn">getTime</span>()) < w * <span class="num">1000</span>;
|
|
1993
2137
|
<span class="kw">return</span> { <span class="at">canDeleteForEveryone</span>: (isMine && inWindow) || isAdmin, <span class="at">canDeleteForMe</span>: <span class="kw">true</span> };
|
|
1994
2138
|
}
|
package/package.json
CHANGED