@antzsoft/chat-core 1.3.2 → 1.3.4
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 +292 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{storage-CNoA242G.d.cts → storage-DJh54pps.d.cts} +16 -1
- package/dist/{storage-CNoA242G.d.ts → storage-DJh54pps.d.ts} +16 -1
- package/docs/integration-guide.html +361 -3
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-
|
|
2
|
-
export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as
|
|
1
|
+
import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DJh54pps.cjs';
|
|
2
|
+
export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DJh54pps.cjs';
|
|
3
3
|
import { AxiosInstance } from 'axios';
|
|
4
4
|
import { Socket } from 'socket.io-client';
|
|
5
5
|
import * as zustand_middleware from 'zustand/middleware';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-
|
|
2
|
-
export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as
|
|
1
|
+
import { R as ResolvedCompressionConfig, L as LoginCredentials, A as AuthResponse, a as RegisterData, b as AuthTokens, U as User, C as CrossConversationSyncResponse, c as ConversationSyncResponse, d as AppConfig, S as SendMessagePayload, e as CursorPaginatedResponse, M as Message, f as MessageReactionsResponse, P as PaginatedResponse, g as MessageReceiptsResponse, h as ConversationListParams, i as Conversation, j as Participant, k as ConversationUnreadCount, l as UnreadSummary, m as UserPreferences, n as ResolvedConfig, o as PersistStorage, p as PresignedUrlRequest, q as PresignedUrlResponse, F as FileResponse, r as CompletedPart, s as FileType, t as AntzChatConfig, u as UploadableFile, B as BatchUploadResult } from './storage-DJh54pps.js';
|
|
2
|
+
export { v as Attachment, w as CompressedFile, x as CompressionAlgorithm, y as CompressionConfig, z as ConversationType, D as ConversationUpdatedEvent, E as FileSizeLimits, G as LastReaction, H as MessageAckEvent, I as MessageContent, J as MessageDeletedEvent, K as MessageDeletedForMeEvent, N as MessageDeliveredEvent, O as MessageMetadata, Q as MessageReaction, T as MessageReceiptEntry, V as MessageReplyReference, W as MessageStarUpdatedEvent, X as MessageUpdatedEvent, Y as MessagesDeliveredEvent, Z as MultipartPartUrl, _ as MultipartUploadInfo, $ as NewMessageEvent, a0 as OptimisticAttachment, a1 as PlatformCompressFn, a2 as PlatformUploadFn, a3 as PlatformUploadPartFn, a4 as QuietHours, a5 as ReactionGroup, a6 as ReactionUpdatedEvent, a7 as ReactionUser, a8 as ReadReceiptEvent, a9 as ReplyAttachmentSnapshot, aa as ResolvedFileSizeLimits, ab as SendMessageAttachment, ac as SyncDeletedForMe, ad as SyncDeliveredReceipt, ae as SyncParticipantChange, af as SyncReactionEntry, ag as SyncReactions, ah as SyncReadReceipt, ai as SyncStarEntry, aj as SystemMessageMetadata, ak as TypingIndicatorEvent, al as UploadConfig, am as UploadProgress, an as UserStatusEvent, ao as resolveConfig, ap as storageApi, aq as uploadBatch } from './storage-DJh54pps.js';
|
|
3
3
|
import { AxiosInstance } from 'axios';
|
|
4
4
|
import { Socket } from 'socket.io-client';
|
|
5
5
|
import * as zustand_middleware from 'zustand/middleware';
|
package/dist/internal.d.cts
CHANGED
package/dist/internal.d.ts
CHANGED
|
@@ -438,6 +438,12 @@ interface SyncReadReceipt {
|
|
|
438
438
|
userId: string;
|
|
439
439
|
readAt: string | null;
|
|
440
440
|
}
|
|
441
|
+
interface SyncDeliveredReceipt {
|
|
442
|
+
messageId: string;
|
|
443
|
+
conversationId: string;
|
|
444
|
+
userId: string;
|
|
445
|
+
deliveredAt: string | null;
|
|
446
|
+
}
|
|
441
447
|
interface SyncDeletedForMe {
|
|
442
448
|
messageId: string;
|
|
443
449
|
conversationId: string;
|
|
@@ -477,6 +483,14 @@ interface CrossConversationSyncResponse {
|
|
|
477
483
|
stars: SyncStarEntry[];
|
|
478
484
|
participantChanges: SyncParticipantChange[];
|
|
479
485
|
readReceipts: SyncReadReceipt[];
|
|
486
|
+
deliveredReceipts: SyncDeliveredReceipt[];
|
|
487
|
+
/**
|
|
488
|
+
* Conversation IDs the client should remove from its local list.
|
|
489
|
+
* Set when the user explicitly deleted their own copy of a conversation
|
|
490
|
+
* (DM or group) via the delete action. Scoped to this user only — other
|
|
491
|
+
* participants are not affected.
|
|
492
|
+
*/
|
|
493
|
+
deletedConversationIds: string[];
|
|
480
494
|
}
|
|
481
495
|
interface ConversationSyncResponse {
|
|
482
496
|
syncedAt: string;
|
|
@@ -490,6 +504,7 @@ interface ConversationSyncResponse {
|
|
|
490
504
|
stars: SyncStarEntry[];
|
|
491
505
|
participantChanges: SyncParticipantChange[];
|
|
492
506
|
readReceipts: SyncReadReceipt[];
|
|
507
|
+
deliveredReceipts: SyncDeliveredReceipt[];
|
|
493
508
|
}
|
|
494
509
|
interface ReactionUser {
|
|
495
510
|
userId: string;
|
|
@@ -802,4 +817,4 @@ declare function uploadBatchWithSlots(files: UploadableFile[], platformUploadFn:
|
|
|
802
817
|
slotToFile: Map<string, FileResponse>;
|
|
803
818
|
}>;
|
|
804
819
|
|
|
805
|
-
export { type NewMessageEvent as $, type AuthResponse as A, type BatchUploadResult as B, type CrossConversationSyncResponse as C, type ConversationUpdatedEvent as D, type FileSizeLimits as E, type FileResponse as F, type LastReaction as G, type MessageAckEvent as H, type MessageContent as I, type MessageDeletedEvent as J, type MessageDeletedForMeEvent as K, type LoginCredentials as L, type Message as M, type MessageDeliveredEvent as N, type MessageMetadata as O, type PaginatedResponse as P, type MessageReaction as Q, type ResolvedCompressionConfig as R, type SendMessagePayload as S, type MessageReceiptEntry as T, type User as U, type MessageReplyReference as V, type MessageStarUpdatedEvent as W, type MessageUpdatedEvent as X, type MessagesDeliveredEvent as Y, type MultipartPartUrl as Z, type MultipartUploadInfo as _, type RegisterData as a, type OptimisticAttachment as a0, type PlatformCompressFn as a1, type PlatformUploadFn as a2, type PlatformUploadPartFn as a3, type QuietHours as a4, type ReactionGroup as a5, type ReactionUpdatedEvent as a6, type ReactionUser as a7, type ReadReceiptEvent as a8, type ReplyAttachmentSnapshot as a9, type ResolvedFileSizeLimits as aa, type SendMessageAttachment as ab, type SyncDeletedForMe as ac, type
|
|
820
|
+
export { type NewMessageEvent as $, type AuthResponse as A, type BatchUploadResult as B, type CrossConversationSyncResponse as C, type ConversationUpdatedEvent as D, type FileSizeLimits as E, type FileResponse as F, type LastReaction as G, type MessageAckEvent as H, type MessageContent as I, type MessageDeletedEvent as J, type MessageDeletedForMeEvent as K, type LoginCredentials as L, type Message as M, type MessageDeliveredEvent as N, type MessageMetadata as O, type PaginatedResponse as P, type MessageReaction as Q, type ResolvedCompressionConfig as R, type SendMessagePayload as S, type MessageReceiptEntry as T, type User as U, type MessageReplyReference as V, type MessageStarUpdatedEvent as W, type MessageUpdatedEvent as X, type MessagesDeliveredEvent as Y, type MultipartPartUrl as Z, type MultipartUploadInfo as _, type RegisterData as a, type OptimisticAttachment as a0, type PlatformCompressFn as a1, type PlatformUploadFn as a2, type PlatformUploadPartFn as a3, type QuietHours as a4, type ReactionGroup as a5, type ReactionUpdatedEvent as a6, type ReactionUser as a7, type ReadReceiptEvent as a8, type ReplyAttachmentSnapshot as a9, type ResolvedFileSizeLimits as aa, type SendMessageAttachment as ab, type SyncDeletedForMe as ac, type SyncDeliveredReceipt as ad, type SyncParticipantChange as ae, type SyncReactionEntry as af, type SyncReactions as ag, type SyncReadReceipt as ah, type SyncStarEntry as ai, type SystemMessageMetadata as aj, type TypingIndicatorEvent as ak, type UploadConfig as al, type UploadProgress as am, type UserStatusEvent as an, resolveConfig as ao, storageApi as ap, uploadBatch as aq, uploadBatchWithSlots as ar, type AuthTokens as b, type ConversationSyncResponse as c, type AppConfig as d, type CursorPaginatedResponse as e, type MessageReactionsResponse as f, type MessageReceiptsResponse as g, type ConversationListParams as h, type Conversation as i, type Participant as j, type ConversationUnreadCount as k, type UnreadSummary as l, type UserPreferences as m, type ResolvedConfig as n, type PersistStorage as o, type PresignedUrlRequest as p, type PresignedUrlResponse as q, type CompletedPart as r, type FileType as s, type AntzChatConfig as t, type UploadableFile as u, type Attachment as v, type CompressedFile as w, type CompressionAlgorithm as x, type CompressionConfig as y, type ConversationType as z };
|
|
@@ -438,6 +438,12 @@ interface SyncReadReceipt {
|
|
|
438
438
|
userId: string;
|
|
439
439
|
readAt: string | null;
|
|
440
440
|
}
|
|
441
|
+
interface SyncDeliveredReceipt {
|
|
442
|
+
messageId: string;
|
|
443
|
+
conversationId: string;
|
|
444
|
+
userId: string;
|
|
445
|
+
deliveredAt: string | null;
|
|
446
|
+
}
|
|
441
447
|
interface SyncDeletedForMe {
|
|
442
448
|
messageId: string;
|
|
443
449
|
conversationId: string;
|
|
@@ -477,6 +483,14 @@ interface CrossConversationSyncResponse {
|
|
|
477
483
|
stars: SyncStarEntry[];
|
|
478
484
|
participantChanges: SyncParticipantChange[];
|
|
479
485
|
readReceipts: SyncReadReceipt[];
|
|
486
|
+
deliveredReceipts: SyncDeliveredReceipt[];
|
|
487
|
+
/**
|
|
488
|
+
* Conversation IDs the client should remove from its local list.
|
|
489
|
+
* Set when the user explicitly deleted their own copy of a conversation
|
|
490
|
+
* (DM or group) via the delete action. Scoped to this user only — other
|
|
491
|
+
* participants are not affected.
|
|
492
|
+
*/
|
|
493
|
+
deletedConversationIds: string[];
|
|
480
494
|
}
|
|
481
495
|
interface ConversationSyncResponse {
|
|
482
496
|
syncedAt: string;
|
|
@@ -490,6 +504,7 @@ interface ConversationSyncResponse {
|
|
|
490
504
|
stars: SyncStarEntry[];
|
|
491
505
|
participantChanges: SyncParticipantChange[];
|
|
492
506
|
readReceipts: SyncReadReceipt[];
|
|
507
|
+
deliveredReceipts: SyncDeliveredReceipt[];
|
|
493
508
|
}
|
|
494
509
|
interface ReactionUser {
|
|
495
510
|
userId: string;
|
|
@@ -802,4 +817,4 @@ declare function uploadBatchWithSlots(files: UploadableFile[], platformUploadFn:
|
|
|
802
817
|
slotToFile: Map<string, FileResponse>;
|
|
803
818
|
}>;
|
|
804
819
|
|
|
805
|
-
export { type NewMessageEvent as $, type AuthResponse as A, type BatchUploadResult as B, type CrossConversationSyncResponse as C, type ConversationUpdatedEvent as D, type FileSizeLimits as E, type FileResponse as F, type LastReaction as G, type MessageAckEvent as H, type MessageContent as I, type MessageDeletedEvent as J, type MessageDeletedForMeEvent as K, type LoginCredentials as L, type Message as M, type MessageDeliveredEvent as N, type MessageMetadata as O, type PaginatedResponse as P, type MessageReaction as Q, type ResolvedCompressionConfig as R, type SendMessagePayload as S, type MessageReceiptEntry as T, type User as U, type MessageReplyReference as V, type MessageStarUpdatedEvent as W, type MessageUpdatedEvent as X, type MessagesDeliveredEvent as Y, type MultipartPartUrl as Z, type MultipartUploadInfo as _, type RegisterData as a, type OptimisticAttachment as a0, type PlatformCompressFn as a1, type PlatformUploadFn as a2, type PlatformUploadPartFn as a3, type QuietHours as a4, type ReactionGroup as a5, type ReactionUpdatedEvent as a6, type ReactionUser as a7, type ReadReceiptEvent as a8, type ReplyAttachmentSnapshot as a9, type ResolvedFileSizeLimits as aa, type SendMessageAttachment as ab, type SyncDeletedForMe as ac, type
|
|
820
|
+
export { type NewMessageEvent as $, type AuthResponse as A, type BatchUploadResult as B, type CrossConversationSyncResponse as C, type ConversationUpdatedEvent as D, type FileSizeLimits as E, type FileResponse as F, type LastReaction as G, type MessageAckEvent as H, type MessageContent as I, type MessageDeletedEvent as J, type MessageDeletedForMeEvent as K, type LoginCredentials as L, type Message as M, type MessageDeliveredEvent as N, type MessageMetadata as O, type PaginatedResponse as P, type MessageReaction as Q, type ResolvedCompressionConfig as R, type SendMessagePayload as S, type MessageReceiptEntry as T, type User as U, type MessageReplyReference as V, type MessageStarUpdatedEvent as W, type MessageUpdatedEvent as X, type MessagesDeliveredEvent as Y, type MultipartPartUrl as Z, type MultipartUploadInfo as _, type RegisterData as a, type OptimisticAttachment as a0, type PlatformCompressFn as a1, type PlatformUploadFn as a2, type PlatformUploadPartFn as a3, type QuietHours as a4, type ReactionGroup as a5, type ReactionUpdatedEvent as a6, type ReactionUser as a7, type ReadReceiptEvent as a8, type ReplyAttachmentSnapshot as a9, type ResolvedFileSizeLimits as aa, type SendMessageAttachment as ab, type SyncDeletedForMe as ac, type SyncDeliveredReceipt as ad, type SyncParticipantChange as ae, type SyncReactionEntry as af, type SyncReactions as ag, type SyncReadReceipt as ah, type SyncStarEntry as ai, type SystemMessageMetadata as aj, type TypingIndicatorEvent as ak, type UploadConfig as al, type UploadProgress as am, type UserStatusEvent as an, resolveConfig as ao, storageApi as ap, uploadBatch as aq, uploadBatchWithSlots as ar, type AuthTokens as b, type ConversationSyncResponse as c, type AppConfig as d, type CursorPaginatedResponse as e, type MessageReactionsResponse as f, type MessageReceiptsResponse as g, type ConversationListParams as h, type Conversation as i, type Participant as j, type ConversationUnreadCount as k, type UnreadSummary as l, type UserPreferences as m, type ResolvedConfig as n, type PersistStorage as o, type PresignedUrlRequest as p, type PresignedUrlResponse as q, type CompletedPart as r, type FileType as s, type AntzChatConfig as t, type UploadableFile as u, type Attachment as v, type CompressedFile as w, type CompressionAlgorithm as x, type CompressionConfig as y, type ConversationType as z };
|
|
@@ -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.
|
|
158
|
+
<li><a href="#whats-new">v1.3.4 Release Notes</a></li>
|
|
159
159
|
</ul>
|
|
160
160
|
|
|
161
161
|
<div class="section-label">Getting Started</div>
|
|
@@ -250,12 +250,368 @@ section.sec>h2:hover{color:#fff}
|
|
|
250
250
|
<h2>What's New</h2>
|
|
251
251
|
<p style="color:var(--muted);font-size:13px;margin-bottom:20px">Version history and release notes. Click a version to expand.</p>
|
|
252
252
|
|
|
253
|
-
<!-- ── v1.
|
|
253
|
+
<!-- ── v1.3.4 (current) ── -->
|
|
254
|
+
<div class="wn-version open" id="wn-134">
|
|
255
|
+
<div class="wn-header" onclick="toggleVersion('wn-134')">
|
|
256
|
+
<div class="wn-title">
|
|
257
|
+
<span class="wn-ver">v1.3.4</span>
|
|
258
|
+
<span class="wn-badge current">Current</span>
|
|
259
|
+
<span class="wn-date">July 2026</span>
|
|
260
|
+
</div>
|
|
261
|
+
<span class="wn-chevron">▲</span>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="wn-body">
|
|
264
|
+
|
|
265
|
+
<div class="wn-item" id="wn-134-deleted-conv-ids">
|
|
266
|
+
<div class="wn-item-header" onclick="toggleItem('wn-134-deleted-conv-ids')">
|
|
267
|
+
<span class="wn-tag new">New</span>
|
|
268
|
+
<span class="wn-item-title"><code>deletedConversationIds</code> in <code>CrossConversationSyncResponse</code></span>
|
|
269
|
+
<span class="wn-chevron-sm">▾</span>
|
|
270
|
+
</div>
|
|
271
|
+
<div class="wn-item-body">
|
|
272
|
+
<p><code>syncApi.pull(since)</code> now returns <code>deletedConversationIds: string[]</code> — IDs of conversations this user explicitly deleted from their list (via the delete-conversation action) since the <code>since</code> cursor. The client should remove these from local storage and the UI conversation list.</p>
|
|
273
|
+
<table>
|
|
274
|
+
<thead><tr><th>Property</th><th>Type</th><th>Description</th></tr></thead>
|
|
275
|
+
<tbody>
|
|
276
|
+
<tr><td><code>deletedConversationIds</code></td><td><code>string[]</code></td><td>Conversation IDs to remove from local list. Empty array when none were deleted in the sync window.</td></tr>
|
|
277
|
+
</tbody>
|
|
278
|
+
</table>
|
|
279
|
+
<pre><code><span class="kw">const</span> result = <span class="kw">await</span> syncApi.<span class="fn">pull</span>(lastSyncedAt);
|
|
280
|
+
|
|
281
|
+
<span class="cm">// Remove conversations the user deleted from their list</span>
|
|
282
|
+
<span class="kw">for</span> (<span class="kw">const</span> convId <span class="kw">of</span> result.deletedConversationIds) {
|
|
283
|
+
<span class="kw">await</span> db.conversations.<span class="fn">delete</span>(convId);
|
|
284
|
+
}</code></pre>
|
|
285
|
+
<p><strong>Scoped to the requesting user only</strong> — if another user deletes their copy of a shared DM, it does not appear in your <code>deletedConversationIds</code>. <strong>Cursor-safe</strong> — once the client saves the returned <code>syncedAt</code> as the new cursor, the same IDs will not reappear on the next <code>pull()</code>. No acknowledged-receipt tracking needed.</p>
|
|
286
|
+
<p><strong>No integration changes required</strong> beyond consuming the new field. The field is always present (empty array when nothing was deleted).</p>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div class="wn-item" id="wn-134-sync-guards">
|
|
291
|
+
<div class="wn-item-header" onclick="toggleItem('wn-134-sync-guards')">
|
|
292
|
+
<span class="wn-tag fix">Fix</span>
|
|
293
|
+
<span class="wn-item-title">Sync access guard parity with message listing API</span>
|
|
294
|
+
<span class="wn-chevron-sm">▾</span>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="wn-item-body">
|
|
297
|
+
<p>All three sync paths now enforce the same access rules as <code>GET /messages</code>. Previously sync could return messages that message listing would not show.</p>
|
|
298
|
+
<table>
|
|
299
|
+
<thead><tr><th>Guard</th><th>Before</th><th>After</th></tr></thead>
|
|
300
|
+
<tbody>
|
|
301
|
+
<tr><td><code>membershipPeriods</code> windows</td><td>Not enforced — all messages returned regardless of when user was a member</td><td>Messages outside the user's active membership windows are excluded. Re-added users only see messages from their active periods.</td></tr>
|
|
302
|
+
<tr><td><code>status</code> filter</td><td>Only <code>deletedFor</code> checked — <code>failed</code>-status messages leaked through</td><td>Only <code>active</code> and <code>deleted</code> (tombstone) messages returned, matching message listing</td></tr>
|
|
303
|
+
<tr><td><code>isHidden</code> group check</td><td>Not checked — hidden group members could still sync</td><td>Hidden group participants (exit+delete) receive <code>403</code>. Hidden DM participants still allowed (consistent with message listing).</td></tr>
|
|
304
|
+
<tr><td>Removed-member read access</td><td><code>isActive: true</code> required — removed members blocked from syncing history</td><td>Removed members can sync read-only history, matching message listing <code>validateConversationAccess</code></td></tr>
|
|
305
|
+
</tbody>
|
|
306
|
+
</table>
|
|
307
|
+
<p><strong>No integration changes required.</strong> Response shape is unchanged — only the correctness of the returned message set improves.</p>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
</div>
|
|
312
|
+
</div><!-- /.wn-version -->
|
|
313
|
+
|
|
314
|
+
<!-- ── v1.3.3 ── -->
|
|
315
|
+
<div class="wn-version open" id="wn-133">
|
|
316
|
+
<div class="wn-header" onclick="toggleVersion('wn-133')">
|
|
317
|
+
<div class="wn-title">
|
|
318
|
+
<span class="wn-ver">v1.3.3</span>
|
|
319
|
+
<span class="wn-date">June 2026</span>
|
|
320
|
+
</div>
|
|
321
|
+
<span class="wn-chevron">▲</span>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="wn-body">
|
|
324
|
+
|
|
325
|
+
<div class="wn-item" id="wn-133-sync-metadata">
|
|
326
|
+
<div class="wn-item-header" onclick="toggleItem('wn-133-sync-metadata')">
|
|
327
|
+
<span class="wn-tag fix">Fix</span>
|
|
328
|
+
<span class="wn-item-title"><code>metadata</code> and <code>sender</code> fields now included in all sync message responses</span>
|
|
329
|
+
<span class="wn-chevron-sm">▾</span>
|
|
330
|
+
</div>
|
|
331
|
+
<div class="wn-item-body">
|
|
332
|
+
<p>Messages returned by all three sync paths (<code>crossConversationSync</code>, <code>conversationSync</code>, <code>conversationSyncBySeq</code>) now include <code>metadata</code> and <code>sender</code> fields. Both were already present in message list and socket responses but were missing from sync, causing system messages to render without actor/target context and sender avatars to appear blank after an offline sync.</p>
|
|
333
|
+
<ul>
|
|
334
|
+
<li><strong><code>metadata</code></strong> — <code>{ actorUserId, targetUserName, targetUserId, systemOperationType, ... }</code> used to render human-readable system message text (e.g. "Alice added Bob")</li>
|
|
335
|
+
<li><strong><code>sender</code></strong> — <code>{ displayName, avatarUrl }</code> snapshot at send time</li>
|
|
336
|
+
</ul>
|
|
337
|
+
<p>Sender profiles are batch-fetched server-side in a single <code>findByIds</code> call per sync request — one extra DB round trip per sync, not one per message.</p>
|
|
338
|
+
<p><strong>No integration changes required.</strong> Both fields were already optional on the <code>Message</code> type. Message normalizers and renderers pick them up automatically.</p>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
</div>
|
|
343
|
+
</div><!-- /.wn-version -->
|
|
344
|
+
|
|
345
|
+
<!-- ── v1.3.2 ── -->
|
|
346
|
+
<div class="wn-version open" id="wn-132">
|
|
347
|
+
<div class="wn-header" onclick="toggleVersion('wn-132')">
|
|
348
|
+
<div class="wn-title">
|
|
349
|
+
<span class="wn-ver">v1.3.2</span>
|
|
350
|
+
<span class="wn-date">June 2026</span>
|
|
351
|
+
</div>
|
|
352
|
+
<span class="wn-chevron">▲</span>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="wn-body">
|
|
355
|
+
|
|
356
|
+
<div class="wn-item" id="wn-132-bounded-reactions">
|
|
357
|
+
<div class="wn-item-header" onclick="toggleItem('wn-132-bounded-reactions')">
|
|
358
|
+
<span class="wn-tag new">New</span>
|
|
359
|
+
<span class="wn-item-title">Bounded sync reaction payloads — <code>SyncReactionEntry</code> replaces <code>MessageReaction[]</code></span>
|
|
360
|
+
<span class="wn-chevron-sm">▾</span>
|
|
361
|
+
</div>
|
|
362
|
+
<div class="wn-item-body">
|
|
363
|
+
<p>The <code>reactions</code> field in both <code>CrossConversationSyncResponse</code> and <code>ConversationSyncResponse</code> previously carried <code>MessageReaction[]</code> with an unbounded <code>userIds: string[]</code> per emoji. On messages with hundreds of reactions this could push payloads past the 4 MB adaptive page limit. The type is now <code>SyncReactionEntry[]</code> — each entry carries only <code>emoji</code>, <code>count</code>, and <code>didIReact</code>.</p>
|
|
364
|
+
<table>
|
|
365
|
+
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
366
|
+
<tbody>
|
|
367
|
+
<tr><td><code>emoji</code></td><td><code>string</code></td><td>The emoji character</td></tr>
|
|
368
|
+
<tr><td><code>count</code></td><td><code>number</code></td><td>Total users who reacted with this emoji</td></tr>
|
|
369
|
+
<tr><td><code>didIReact</code></td><td><code>boolean</code></td><td>Whether the calling user reacted with this emoji</td></tr>
|
|
370
|
+
</tbody>
|
|
371
|
+
</table>
|
|
372
|
+
<p><strong>Sync payload size is now O(messages × emojis)</strong> regardless of how many users have reacted. The full user list is available on demand via <code>messagesApi.getReactions(messageId)</code>.</p>
|
|
373
|
+
<p><strong>Migration</strong> — if you previously read <code>reactions[msgId][n].userIds</code>, switch to <code>count</code> and <code>didIReact</code>. For the full who-reacted list, call <code>messagesApi.getReactions()</code>.</p>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div class="wn-item" id="wn-132-reactions-endpoint">
|
|
378
|
+
<div class="wn-item-header" onclick="toggleItem('wn-132-reactions-endpoint')">
|
|
379
|
+
<span class="wn-tag new">New</span>
|
|
380
|
+
<span class="wn-item-title"><code>messagesApi.getReactions(messageId)</code> — reactions detail sheet API</span>
|
|
381
|
+
<span class="wn-chevron-sm">▾</span>
|
|
382
|
+
</div>
|
|
383
|
+
<div class="wn-item-body">
|
|
384
|
+
<p>New <code>POST /messages/:id/reactions/list</code> endpoint (GET mirror also available) returns the full per-emoji breakdown with resolved user profiles. Use as the data source for a "Who reacted" bottom sheet.</p>
|
|
385
|
+
<table>
|
|
386
|
+
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
387
|
+
<tbody>
|
|
388
|
+
<tr><td><code>messageId</code></td><td><code>string</code></td><td>The message ID</td></tr>
|
|
389
|
+
<tr><td><code>reactions</code></td><td><code>ReactionGroup[]</code></td><td>One entry per emoji</td></tr>
|
|
390
|
+
<tr><td><code>reactions[n].emoji</code></td><td><code>string</code></td><td>The emoji character</td></tr>
|
|
391
|
+
<tr><td><code>reactions[n].count</code></td><td><code>number</code></td><td>Total reactors</td></tr>
|
|
392
|
+
<tr><td><code>reactions[n].userIds</code></td><td><code>string[]</code></td><td>All user IDs who reacted</td></tr>
|
|
393
|
+
<tr><td><code>reactions[n].users</code></td><td><code>ReactionUser[]</code></td><td>Resolved <code>{ userId, displayName, avatarUrl? }</code></td></tr>
|
|
394
|
+
</tbody>
|
|
395
|
+
</table>
|
|
396
|
+
<pre><code><span class="kw">import</span> { messagesApi } <span class="kw">from</span> <span class="str">'@antzsoft/chat-core'</span>;
|
|
397
|
+
|
|
398
|
+
<span class="kw">const</span> data = <span class="kw">await</span> messagesApi.<span class="fn">getReactions</span>(messageId);
|
|
399
|
+
|
|
400
|
+
<span class="kw">for</span> (<span class="kw">const</span> group <span class="kw">of</span> data.reactions) {
|
|
401
|
+
<span class="cm">// group.emoji, group.count, group.users (with displayName + avatarUrl)</span>
|
|
402
|
+
renderReactionSheet(group);
|
|
403
|
+
}</code></pre>
|
|
404
|
+
<p><strong>New exported types:</strong> <code>MessageReactionsResponse</code>, <code>ReactionGroup</code>, <code>ReactionUser</code>.</p>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
</div>
|
|
409
|
+
</div><!-- /.wn-version -->
|
|
410
|
+
|
|
411
|
+
<!-- ── v1.3.1 ── -->
|
|
412
|
+
<div class="wn-version open" id="wn-131">
|
|
413
|
+
<div class="wn-header" onclick="toggleVersion('wn-131')">
|
|
414
|
+
<div class="wn-title">
|
|
415
|
+
<span class="wn-ver">v1.3.1</span>
|
|
416
|
+
<span class="wn-date">June 2026</span>
|
|
417
|
+
</div>
|
|
418
|
+
<span class="wn-chevron">▲</span>
|
|
419
|
+
</div>
|
|
420
|
+
<div class="wn-body">
|
|
421
|
+
|
|
422
|
+
<div class="wn-item" id="wn-131-bidir-sync">
|
|
423
|
+
<div class="wn-item-header" onclick="toggleItem('wn-131-bidir-sync')">
|
|
424
|
+
<span class="wn-tag new">New</span>
|
|
425
|
+
<span class="wn-item-title">Bidirectional seq pagination — <code>beforeSeq</code> for scroll-back history loading</span>
|
|
426
|
+
<span class="wn-chevron-sm">▾</span>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="wn-item-body">
|
|
429
|
+
<p>The per-conversation sync endpoint now supports two pagination directions:</p>
|
|
430
|
+
<table>
|
|
431
|
+
<thead><tr><th>Direction</th><th>Parameter</th><th>Order</th><th>Use case</th></tr></thead>
|
|
432
|
+
<tbody>
|
|
433
|
+
<tr><td>Forward</td><td><code>afterSeq</code></td><td>oldest → newest</td><td>Catch-up after reconnect or gap</td></tr>
|
|
434
|
+
<tr><td>Backward</td><td><code>beforeSeq</code></td><td>newest → oldest</td><td>Scroll-back / load older history</td></tr>
|
|
435
|
+
</tbody>
|
|
436
|
+
</table>
|
|
437
|
+
<p>Pass only one cursor per request — passing both is a <code>400 Bad Request</code>. The returned <code>nextSeq</code> is the cursor for the next page in the same direction as what you sent.</p>
|
|
438
|
+
<pre><code><span class="cm">// Catch-up (forward) — start from last known seq</span>
|
|
439
|
+
<span class="kw">let</span> afterSeq = conversation.lastSeq ?? 0;
|
|
440
|
+
<span class="kw">while</span> (<span class="kw">true</span>) {
|
|
441
|
+
<span class="kw">const</span> res = <span class="kw">await</span> syncApi.<span class="fn">pullConversation</span>(conversationId, { afterSeq });
|
|
442
|
+
<span class="kw">await</span> <span class="fn">upsertToSQLite</span>(res);
|
|
443
|
+
<span class="kw">if</span> (!res.hasMore || res.nextSeq == <span class="kw">null</span>) <span class="kw">break</span>;
|
|
444
|
+
afterSeq = res.nextSeq;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
<span class="cm">// Scroll-back (backward) — newest → oldest</span>
|
|
448
|
+
<span class="kw">const</span> res = <span class="kw">await</span> syncApi.<span class="fn">pullConversation</span>(conversationId, { beforeSeq: oldestLocalSeq });
|
|
449
|
+
<span class="kw">if</span> (res.hasMore) nextPage = <span class="kw">await</span> syncApi.<span class="fn">pullConversation</span>(conversationId, { beforeSeq: res.nextSeq });</code></pre>
|
|
450
|
+
<p><strong>RN SDK:</strong> <code>useConversationSync</code> accepts <code>direction: 'backward'</code> with <code>beforeSeq</code>. Forward is the default and unchanged.</p>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
<div class="wn-item" id="wn-131-crossconv-reactions">
|
|
455
|
+
<div class="wn-item-header" onclick="toggleItem('wn-131-crossconv-reactions')">
|
|
456
|
+
<span class="wn-tag new">New</span>
|
|
457
|
+
<span class="wn-item-title"><code>reactions</code> and <code>stars</code> added to <code>CrossConversationSyncResponse</code></span>
|
|
458
|
+
<span class="wn-chevron-sm">▾</span>
|
|
459
|
+
</div>
|
|
460
|
+
<div class="wn-item-body">
|
|
461
|
+
<p><code>syncApi.pull(since)</code> now returns <code>reactions: SyncReactions</code> and <code>stars: SyncStarEntry[]</code> alongside the existing cross-conversation fields. Previously these were only available from <code>pullConversation</code> — reactions and star changes that occurred during an offline gap were lost on cross-conversation catch-up. Cross-conversation sync is now fully symmetric with per-conversation sync.</p>
|
|
462
|
+
<p>Stale responses and empty-conversations early returns also now include <code>reactions: {}</code> and <code>stars: []</code> for a consistent response shape.</p>
|
|
463
|
+
<p><strong>No integration changes required.</strong> <code>SyncService.applyShared()</code> in the RN app already handles <code>reactions</code> and <code>stars</code> for both response types.</p>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
</div>
|
|
468
|
+
</div><!-- /.wn-version -->
|
|
469
|
+
|
|
470
|
+
<!-- ── v1.3.0 ── -->
|
|
471
|
+
<div class="wn-version open" id="wn-130">
|
|
472
|
+
<div class="wn-header" onclick="toggleVersion('wn-130')">
|
|
473
|
+
<div class="wn-title">
|
|
474
|
+
<span class="wn-ver">v1.3.0</span>
|
|
475
|
+
<span class="wn-date">June 2026</span>
|
|
476
|
+
</div>
|
|
477
|
+
<span class="wn-chevron">▲</span>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="wn-body">
|
|
480
|
+
|
|
481
|
+
<div class="wn-item" id="wn-130-seq">
|
|
482
|
+
<div class="wn-item-header" onclick="toggleItem('wn-130-seq')">
|
|
483
|
+
<span class="wn-tag new">New</span>
|
|
484
|
+
<span class="wn-item-title">Per-conversation message sequence numbers — <code>seq</code> on messages, <code>lastSeq</code> on conversations</span>
|
|
485
|
+
<span class="wn-chevron-sm">▾</span>
|
|
486
|
+
</div>
|
|
487
|
+
<div class="wn-item-body">
|
|
488
|
+
<p>Every message is now assigned a monotonically increasing <code>seq</code> integer per conversation at send time. The server maintains an atomic Redis counter per conversation (<code>chat:seq:{conversationId}</code>), flushed to MongoDB every 60 s and seeded on boot from the <code>chat_messages</code> collection.</p>
|
|
489
|
+
|
|
490
|
+
<p><code>seq</code> appears on:</p>
|
|
491
|
+
<ul>
|
|
492
|
+
<li><code>Message.seq</code> — returned by all REST message endpoints and the <code>new_message</code> socket event</li>
|
|
493
|
+
<li><code>MessageAckEvent.seq</code> — in the <code>message_ack</code> socket event so the sender can advance their local cursor immediately on send</li>
|
|
494
|
+
</ul>
|
|
495
|
+
|
|
496
|
+
<p><code>Conversation.lastSeq</code> is the highest seq in that conversation — returned by all conversation list/get endpoints and updated on every <code>conversation_updated</code> socket event (new <code>ConversationUpdatedEvent.lastSeq</code> field).</p>
|
|
497
|
+
|
|
498
|
+
<p><strong>Why seq?</strong> Timestamp-based sync has a race window: messages sent within the same millisecond collide. Seq cursors are collision-safe — <code>afterSeq: N</code> always returns exactly the messages with <code>seq > N</code>, no matter how close together they were sent.</p>
|
|
499
|
+
|
|
500
|
+
<table>
|
|
501
|
+
<thead><tr><th>New field</th><th>Type</th><th>Where</th><th>Description</th></tr></thead>
|
|
502
|
+
<tbody>
|
|
503
|
+
<tr><td><code>seq</code></td><td><code>number</code></td><td><code>Message</code></td><td>Monotonic counter for this conversation. Use as <code>afterSeq</code> cursor.</td></tr>
|
|
504
|
+
<tr><td><code>lastSeq</code></td><td><code>number</code></td><td><code>Conversation</code></td><td>Highest seq in this conversation. Store as your sync cursor.</td></tr>
|
|
505
|
+
<tr><td><code>seq</code></td><td><code>number</code></td><td><code>MessageAckEvent</code></td><td>Seq assigned to the message you just sent. Advance local cursor immediately.</td></tr>
|
|
506
|
+
<tr><td><code>lastSeq</code></td><td><code>number</code></td><td><code>ConversationUpdatedEvent</code></td><td>Updated on every message send. Keep cursor current without a sync call.</td></tr>
|
|
507
|
+
</tbody>
|
|
508
|
+
</table>
|
|
509
|
+
|
|
510
|
+
<p><strong>New exported type: <code>ConversationUpdatedEvent</code></strong> — previously the <code>conversation_updated</code> socket event was untyped. It is now <code>ConversationUpdatedEvent</code> with <code>id</code>, <code>lastMessage</code>, <code>unreadCount</code>, <code>updatedAt</code>, <code>lastSeq</code>, and <code>participants</code>.</p>
|
|
511
|
+
|
|
512
|
+
<p><strong>Migration 006</strong> — <code>006_backfill_message_seq</code> runs automatically on first deploy. Assigns sequential <code>seq</code> values to all existing messages ordered by <code>createdAt</code>. Idempotent.</p>
|
|
513
|
+
|
|
514
|
+
<p><strong>New <code>conversationSyncBySeq</code> endpoint</strong> — <code>GET /conversations/:id/sync?afterSeq=N</code> pages messages by seq (forward direction by default). This is the preferred sync method for clients that have <code>Conversation.lastSeq</code>.</p>
|
|
515
|
+
<pre><code><span class="cm">// On message_ack — advance cursor so next sync picks up exactly from here</span>
|
|
516
|
+
socket.<span class="fn">on</span>(<span class="str">'message_ack'</span>, (evt) => {
|
|
517
|
+
<span class="kw">if</span> (evt.seq) <span class="fn">setConvSeq</span>(evt.conversationId, evt.seq);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
<span class="cm">// On conversation_updated — keep lastSeq current</span>
|
|
521
|
+
socket.<span class="fn">on</span>(<span class="str">'conversation_updated'</span>, (evt: ConversationUpdatedEvent) => {
|
|
522
|
+
<span class="kw">if</span> (evt.lastSeq) <span class="fn">setLastSeq</span>(evt.id, evt.lastSeq);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
<span class="cm">// On reconnect — seq-based catch-up</span>
|
|
526
|
+
<span class="kw">const</span> lastSeq = <span class="kw">await</span> <span class="fn">getLastSeq</span>(conversationId);
|
|
527
|
+
<span class="kw">const</span> res = <span class="kw">await</span> syncApi.<span class="fn">pullConversation</span>(conversationId, { afterSeq: lastSeq });</code></pre>
|
|
528
|
+
<p><strong>RN SDK:</strong> <code>useConvSeq</code> / <code>getConvSeq</code> / <code>setConvSeq</code> functions exported from <code>@antzsoft/chat-rn-sdk</code> for persisting seq in AsyncStorage. <code>useConversationSync</code> automatically uses the stored seq for forward syncs.</p>
|
|
529
|
+
<p><strong>No breaking changes.</strong> <code>seq</code> and <code>lastSeq</code> are additive optional fields. Timestamp-based sync continues to work unchanged for clients that haven't adopted seq yet.</p>
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
</div>
|
|
534
|
+
</div><!-- /.wn-version -->
|
|
535
|
+
|
|
536
|
+
<!-- ── v1.2.9 (current) ── -->
|
|
537
|
+
<div class="wn-version open" id="wn-129">
|
|
538
|
+
<div class="wn-header" onclick="toggleVersion('wn-129')">
|
|
539
|
+
<div class="wn-title">
|
|
540
|
+
<span class="wn-ver">v1.2.9</span>
|
|
541
|
+
<span class="wn-date">June 2026</span>
|
|
542
|
+
</div>
|
|
543
|
+
<span class="wn-chevron">▲</span>
|
|
544
|
+
</div>
|
|
545
|
+
<div class="wn-body">
|
|
546
|
+
|
|
547
|
+
<div class="wn-item" id="wn-129-delivered-receipts">
|
|
548
|
+
<div class="wn-item-header" onclick="toggleItem('wn-129-delivered-receipts')">
|
|
549
|
+
<span class="wn-tag new">New</span>
|
|
550
|
+
<span class="wn-item-title"><code>deliveredReceipts[]</code> in sync responses — delivery tick marks survive offline gaps</span>
|
|
551
|
+
<span class="wn-chevron-sm">▾</span>
|
|
552
|
+
</div>
|
|
553
|
+
<div class="wn-item-body">
|
|
554
|
+
<p>Both <code>CrossConversationSyncResponse</code> (<code>syncApi.pull()</code>) and <code>ConversationSyncResponse</code> (<code>syncApi.pullConversation()</code>) now include a <code>deliveredReceipts: SyncDeliveredReceipt[]</code> field. This mirrors the existing <code>readReceipts[]</code> pattern. Delivery events that occurred while the device was offline are now replayed on reconnect, so single-tick → double-tick transitions are correctly applied to the local database.</p>
|
|
555
|
+
|
|
556
|
+
<table>
|
|
557
|
+
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
|
558
|
+
<tbody>
|
|
559
|
+
<tr><td><code>messageId</code></td><td><code>string</code></td><td>ID of the delivered message</td></tr>
|
|
560
|
+
<tr><td><code>conversationId</code></td><td><code>string</code></td><td>Conversation the message belongs to</td></tr>
|
|
561
|
+
<tr><td><code>userId</code></td><td><code>string</code></td><td>Recipient who received the message</td></tr>
|
|
562
|
+
<tr><td><code>deliveredAt</code></td><td><code>string | null</code></td><td>ISO timestamp of delivery</td></tr>
|
|
563
|
+
</tbody>
|
|
564
|
+
</table>
|
|
565
|
+
|
|
566
|
+
<p><strong>How the mobile app merges delivery receipts:</strong></p>
|
|
567
|
+
<ol>
|
|
568
|
+
<li>Group receipts by <code>messageId</code></li>
|
|
569
|
+
<li>For each message, merge new <code>{ userId, deliveredAt }</code> entries into <code>msg.deliveredTo[]</code> via <code>mergeByUserId</code> (deduplicates by <code>userId</code>)</li>
|
|
570
|
+
<li>Re-run <code>resolveMessageStatus(msg)</code> — if all recipients have delivered the message, status advances to <code>'delivered'</code></li>
|
|
571
|
+
<li>Upsert the updated message to SQLite</li>
|
|
572
|
+
</ol>
|
|
573
|
+
|
|
574
|
+
<p><strong>No integration changes required.</strong> The RN <code>SyncService.applyShared()</code> already calls <code>ChatRepository.applyDeliveredReceipts(res.deliveredReceipts)</code>. The field was absent in older server versions and is treated as an empty array by any client. <strong>New exported type: <code>SyncDeliveredReceipt</code>.</strong></p>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
|
|
578
|
+
<div class="wn-item" id="wn-129-signed-urls">
|
|
579
|
+
<div class="wn-item-header" onclick="toggleItem('wn-129-signed-urls')">
|
|
580
|
+
<span class="wn-tag fix">Fix</span>
|
|
581
|
+
<span class="wn-item-title">Attachment URLs in sync messages are now properly signed</span>
|
|
582
|
+
<span class="wn-chevron-sm">▾</span>
|
|
583
|
+
</div>
|
|
584
|
+
<div class="wn-item-body">
|
|
585
|
+
<p>Previously, messages returned by <code>syncApi.pull()</code> and <code>syncApi.pullConversation()</code> included raw <code>storageKey</code>, <code>provider</code>, and <code>bucket</code> fields for attachments. These are internal storage identifiers — clients cannot render images or play audio from them directly. The sync service now generates signed, short-lived <code>url</code>s for all attachment records before returning them, using the same Redis-cached signing logic as the message list API (2 400 s cache / 3 600 s expiry). The raw storage fields are stripped from the sync output.</p>
|
|
586
|
+
<p>The <code>replyTo.attachmentSnapshot.url</code> field (thumbnail shown in reply bubbles) is also now signed in sync responses. Previously it was absent, so quoted attachment previews were broken for offline-synced messages.</p>
|
|
587
|
+
<p><strong>No integration changes required.</strong> Clients that were silently ignoring <code>storageKey</code> now receive a working <code>url</code>. Attachment normalizers already read <code>a.url</code> directly.</p>
|
|
588
|
+
</div>
|
|
589
|
+
</div>
|
|
590
|
+
|
|
591
|
+
<div class="wn-item" id="wn-129-sync-fixes">
|
|
592
|
+
<div class="wn-item-header" onclick="toggleItem('wn-129-sync-fixes')">
|
|
593
|
+
<span class="wn-tag fix">Fix</span>
|
|
594
|
+
<span class="wn-item-title">Sync correctness fixes — <code>replyTo.messageId</code> serialization and read receipt status recompute</span>
|
|
595
|
+
<span class="wn-chevron-sm">▾</span>
|
|
596
|
+
</div>
|
|
597
|
+
<div class="wn-item-body">
|
|
598
|
+
<p>Two correctness fixes in the sync layer:</p>
|
|
599
|
+
<ul>
|
|
600
|
+
<li><strong><code>replyTo.messageId</code> now serialized as a string</strong> — was returned as a raw MongoDB <code>ObjectId</code> object in sync responses due to a missing <code>.toString()</code> call on the lean document. Now always a plain string, consistent with all other ID fields across the API.</li>
|
|
601
|
+
<li><strong><code>applyReadReceipts</code> now recomputes <code>status</code> after merge</strong> — the mobile <code>ChatRepository.applyReadReceipts()</code> was merging new read entries into <code>msg.readBy[]</code> but not re-running <code>resolveMessageStatus</code>. Messages read by all recipients during an offline gap stayed at double-tick (delivered) instead of advancing to triple-tick (read). Fixed: <code>resolveMessageStatus(updated)</code> is now called after every merge and the result is persisted.</li>
|
|
602
|
+
</ul>
|
|
603
|
+
<p><strong>No integration changes required.</strong></p>
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
</div>
|
|
608
|
+
</div><!-- /.wn-version -->
|
|
609
|
+
|
|
610
|
+
<!-- ── v1.2.8 ── -->
|
|
254
611
|
<div class="wn-version open" id="wn-128">
|
|
255
612
|
<div class="wn-header" onclick="toggleVersion('wn-128')">
|
|
256
613
|
<div class="wn-title">
|
|
257
614
|
<span class="wn-ver">v1.2.8</span>
|
|
258
|
-
<span class="wn-badge current">Current</span>
|
|
259
615
|
<span class="wn-date">June 2026</span>
|
|
260
616
|
</div>
|
|
261
617
|
<span class="wn-chevron">▲</span>
|
|
@@ -404,6 +760,7 @@ socket?.<span class="fn">on</span>(<span class="str">'message_star_updated'</spa
|
|
|
404
760
|
<tr><td><code>deletedForMe</code></td><td><code>SyncDeletedForMe[]</code></td><td>Messages this user hid for themselves — remove from local DB.</td></tr>
|
|
405
761
|
<tr><td><code>participantChanges</code></td><td><code>SyncParticipantChange[]</code></td><td>Role changes, mute/unmute, removals (<code>isActive: false</code>) across all conversations.</td></tr>
|
|
406
762
|
<tr><td><code>readReceipts</code></td><td><code>SyncReadReceipt[]</code></td><td>Read receipts received since <code>since</code>.</td></tr>
|
|
763
|
+
<tr><td><code>deliveredReceipts</code></td><td><code>SyncDeliveredReceipt[]</code></td><td>Delivery receipts received since <code>since</code> — merge into each message's <code>deliveredTo[]</code> and recompute tick state. Added in v1.2.9.</td></tr>
|
|
407
764
|
</tbody>
|
|
408
765
|
</table>
|
|
409
766
|
<p><strong>Stale handling</strong> — when <code>stale: true</code> the server still returns the new <code>syncedAt</code> cursor. Save it and mark every conversation <code>needs_refresh = true</code> in local SQLite; open <code>GET /conversations/:id/sync</code> lazily when the user navigates to each conversation.</p>
|
|
@@ -451,6 +808,7 @@ useSync({
|
|
|
451
808
|
<tr><td><code>stars</code></td><td><code>SyncStarEntry[]</code></td><td><code>isActive: true</code> = starred, <code>isActive: false</code> = unstarred (soft-delete). Delete local star record when <code>false</code>.</td></tr>
|
|
452
809
|
<tr><td><code>participantChanges</code></td><td><code>SyncParticipantChange[]</code></td><td>Role changes, mutes, removals for this conversation.</td></tr>
|
|
453
810
|
<tr><td><code>readReceipts</code></td><td><code>SyncReadReceipt[]</code></td><td>Read receipts received since <code>since</code>.</td></tr>
|
|
811
|
+
<tr><td><code>deliveredReceipts</code></td><td><code>SyncDeliveredReceipt[]</code></td><td>Delivery receipts for this page — merge into each message's <code>deliveredTo[]</code> and recompute tick state. Added in v1.2.9.</td></tr>
|
|
454
812
|
</tbody>
|
|
455
813
|
</table>
|
|
456
814
|
<p><strong>Reactions are always returned as full current state</strong> for the affected messages — not as a delta. Because reaction removes are hard-deleted (no timestamp trace), a delta is impossible. Upsert the reaction counts returned and discard any locally cached state for those message IDs.</p>
|
package/package.json
CHANGED