@antzsoft/chat-core 1.2.7 → 1.2.9

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 CHANGED
@@ -2518,6 +2518,12 @@ interface Message {
2518
2518
  replyTo?: MessageReplyReference;
2519
2519
  reactions: MessageReaction[];
2520
2520
  status: 'sent' | 'delivered' | 'read' | 'failed' | 'deleted';
2521
+ /**
2522
+ * Tick status for the current user's perspective.
2523
+ * On full message objects: computed from readBy / deliveredTo at fetch time.
2524
+ * On Conversation.lastMessage (v1.2.8+): stored on the conversation and kept
2525
+ * up-to-date by the server on every read/delivery event. Defaults to 'sent'.
2526
+ */
2521
2527
  deliveryStatus?: 'sending' | 'sent' | 'delivered' | 'read' | 'failed';
2522
2528
  isEdited: boolean;
2523
2529
  editedAt?: string;
@@ -2668,7 +2674,7 @@ interface Conversation {
2668
2674
  participants: Participant[];
2669
2675
  participantCount?: number;
2670
2676
  settings?: ConversationSettings;
2671
- lastMessage?: Message;
2677
+ lastMessage?: Message; // deliveryStatus populated as of v1.2.8 — tick state of the last message
2672
2678
  createdBy?: string;
2673
2679
  isActive: boolean;
2674
2680
  createdAt: string;
@@ -2950,6 +2956,40 @@ document.querySelectorAll('[data-conv-id]').forEach((el) => {
2950
2956
 
2951
2957
  ## Changelog
2952
2958
 
2959
+ ### v1.2.8
2960
+
2961
+ - **New: `deliveryStatus` on `Conversation.lastMessage`** — The conversation list API (`GET /conversations`) and the `conversation_updated` socket event now include `deliveryStatus` on the `lastMessage` object. This is the tick state (`'sent'` | `'delivered'` | `'read'`) of the last message as seen by the sender. It is computed and stored directly on the conversation document — no extra queries at list time.
2962
+
2963
+ | Value | Meaning |
2964
+ |---|---|
2965
+ | `'sent'` | Message written to the server — not yet delivered to all recipients |
2966
+ | `'delivered'` | All active recipients have received the message on at least one device |
2967
+ | `'read'` | All active recipients have opened and read the message |
2968
+
2969
+ **Usage — conversation list tick marks (custom UI):**
2970
+
2971
+ ```typescript
2972
+ const { data: conversations } = await conversationsApi.list();
2973
+
2974
+ for (const conv of conversations) {
2975
+ const lm = conv.lastMessage;
2976
+ if (!lm) continue;
2977
+
2978
+ // Only show ticks on messages sent by the current user
2979
+ const isMyMessage = lm.senderId === currentUserId;
2980
+ if (!isMyMessage) continue;
2981
+
2982
+ // lm.deliveryStatus: 'sent' | 'delivered' | 'read'
2983
+ renderTick(conv.id, lm.deliveryStatus ?? 'sent');
2984
+ }
2985
+ ```
2986
+
2987
+ **Live updates** — the `read_receipt` socket event continues to carry real-time tick updates while a conversation room is open. `deliveryStatus` on `lastMessage` is the cold-start / list-load source of truth; the socket events keep it live after that.
2988
+
2989
+ **Backward compatibility** — `deliveryStatus` defaults to `'sent'` when absent (`lm.deliveryStatus ?? 'sent'`). Existing conversations are backfilled automatically by migration `005_backfill_last_message_delivery_status` which runs on first deploy. No integration changes required.
2990
+
2991
+ **`Message` type** — `deliveryStatus` was already defined as an optional field on the `Message` interface. It is now also populated on the `lastMessage` snapshot returned by the conversation list.
2992
+
2953
2993
  ### v1.2.7
2954
2994
  - **New: `syncApi` — offline delta sync for mobile** — Two new REST endpoints let the mobile app pull all changes since a given timestamp without a full refetch. Call `syncApi.pull(since)` on every socket reconnect to get cross-conversation deltas; call `syncApi.pullConversation(convId, since?)` when opening a stale conversation (or on fresh install with no `since`). When the gap exceeds the stale threshold (default 30 days, env: `CHAT_SYNC_STALE_DAYS`) the server sets `stale: true` and returns no data — the app lazy-syncs each conversation as the user opens it. See [`syncApi`](#sync-api-syncapi) for full reference.
2955
2995
  - **New: `message_star_updated` socket event — real-time star sync across devices** — When the current user stars or unstars a message, the server now emits `message_star_updated` to their personal socket room. All other connected devices of the same user receive the event and update their local message cache automatically — no refetch needed. Payload: `{ messageId: string; conversationId: string; isStarred: boolean }`. New exported type: `MessageStarUpdatedEvent`. Web and RN `SocketProvider` handle this event internally.
package/dist/index.cjs CHANGED
@@ -152,7 +152,13 @@ module.exports = __toCommonJS(src_exports);
152
152
 
153
153
  // src/config/types.ts
154
154
  function resolveConfig(config) {
155
- const socketUrl = config.socketUrl ?? config.apiUrl.replace(/\/api\/v\d+\/?$/, "").replace(/\/$/, "");
155
+ const socketUrl = config.socketUrl ?? (() => {
156
+ try {
157
+ return new URL(config.apiUrl).origin;
158
+ } catch {
159
+ return config.apiUrl;
160
+ }
161
+ })();
156
162
  let socketOrigin = socketUrl;
157
163
  let socketPath = "/socket.io";
158
164
  try {
@@ -854,14 +860,22 @@ var syncApi = {
854
860
  },
855
861
  /**
856
862
  * Per-conversation delta sync — call when a conversation has `needs_refresh = true`
857
- * or on fresh install (omit `since` to receive the full message history).
863
+ * or on fresh install (omit both since and afterSeq to receive full history).
858
864
  * Covers messages, deletedForMe, full reaction state, stars, participant changes,
859
865
  * and read receipts — everything needed to make local SQLite authoritative.
866
+ *
867
+ * Prefer `afterSeq` when available (new server) — paginated, collision-safe.
868
+ * Fall back to `since` timestamp for old servers or first sync on a new install.
869
+ * When `hasMore` is true in the response, call again with the returned `nextSeq`.
860
870
  */
861
- pullConversation(conversationId, since) {
862
- return getApiClient().get(`/conversations/${conversationId}/sync`, {
863
- params: since ? { since } : {}
864
- }).then((r) => r.data);
871
+ pullConversation(conversationId, opts) {
872
+ const params = {};
873
+ if (opts?.afterSeq !== void 0) {
874
+ params.afterSeq = opts.afterSeq;
875
+ } else if (opts?.since) {
876
+ params.since = opts.since;
877
+ }
878
+ return getApiClient().get(`/conversations/${conversationId}/sync`, { params }).then((r) => r.data);
865
879
  }
866
880
  };
867
881
 
@@ -1118,7 +1132,8 @@ function normalizeLastMessage(lastMsg) {
1118
1132
  },
1119
1133
  reactions: [],
1120
1134
  lastReaction: lastMsg.lastReaction ?? null,
1121
- status: lastMsg.status ?? "sent",
1135
+ status: lastMsg.status ?? "active",
1136
+ deliveryStatus: lastMsg.deliveryStatus ?? "sent",
1122
1137
  isEdited: false,
1123
1138
  sentAt: lastMsg.sentAt ?? "",
1124
1139
  createdAt: lastMsg.sentAt ?? "",