@antzsoft/chat-core 1.0.2 → 1.0.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.
@@ -0,0 +1,7 @@
1
+ import {
2
+ useChatStore
3
+ } from "./chunk-TB52RCSF.js";
4
+ export {
5
+ useChatStore
6
+ };
7
+ //# sourceMappingURL=chat.store-PY3YVYGN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,54 @@
1
+ // src/stores/chat.store.ts
2
+ import { create } from "zustand";
3
+ var useChatStore = create((set) => ({
4
+ activeConversationId: null,
5
+ pendingTarget: null,
6
+ typingUsers: {},
7
+ onlineUsers: [],
8
+ lastRead: {},
9
+ lastSeen: {},
10
+ replyingTo: null,
11
+ editingMessage: null,
12
+ isSidebarOpen: true,
13
+ isGroupInfoOpen: false,
14
+ isStarredPanelOpen: false,
15
+ setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
16
+ setPendingTarget: (target) => set({ pendingTarget: target }),
17
+ addTypingUser: (conversationId, user) => set((state) => {
18
+ const existing = state.typingUsers[conversationId] ?? [];
19
+ const deduped = existing.filter((u) => u.userId !== user.userId);
20
+ return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };
21
+ }),
22
+ removeTypingUser: (conversationId, userId) => set((state) => ({
23
+ typingUsers: {
24
+ ...state.typingUsers,
25
+ [conversationId]: (state.typingUsers[conversationId] ?? []).filter(
26
+ (u) => u.userId !== userId
27
+ )
28
+ }
29
+ })),
30
+ setUserOnline: (userId) => set((state) => ({
31
+ onlineUsers: state.onlineUsers.includes(userId) ? state.onlineUsers : [...state.onlineUsers, userId]
32
+ })),
33
+ setUserOffline: (userId) => set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),
34
+ setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),
35
+ setLastRead: (conversationId, messageId, readAt) => set((state) => ({
36
+ lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } }
37
+ })),
38
+ setLastSeen: (userId, lastSeenAt) => set((state) => ({
39
+ lastSeen: { ...state.lastSeen, [userId]: lastSeenAt }
40
+ })),
41
+ setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
42
+ setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
43
+ toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
44
+ setSidebarOpen: (open) => set({ isSidebarOpen: open }),
45
+ toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
46
+ setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
47
+ toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
48
+ setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
49
+ }));
50
+
51
+ export {
52
+ useChatStore
53
+ };
54
+ //# sourceMappingURL=chunk-TB52RCSF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stores/chat.store.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type { Message } from '../types/index.js';\n\ninterface TypingUser {\n userId: string;\n displayName: string;\n avatarUrl?: string;\n}\n\nexport interface LastReadEntry {\n messageId: string;\n readAt: string;\n}\n\ninterface ChatState {\n activeConversationId: string | null;\n pendingTarget: { conversationId: string; messageId: string } | null;\n typingUsers: Record<string, TypingUser[]>;\n onlineUsers: string[];\n /** keyed by conversationId — current user's last read pointer per conversation */\n lastRead: Record<string, LastReadEntry>;\n /** keyed by userId — last seen timestamp for each user */\n lastSeen: Record<string, string>;\n replyingTo: Message | null;\n editingMessage: Message | null;\n isSidebarOpen: boolean;\n isGroupInfoOpen: boolean;\n isStarredPanelOpen: boolean;\n\n setActiveConversation: (id: string | null) => void;\n setPendingTarget: (target: { conversationId: string; messageId: string } | null) => void;\n addTypingUser: (conversationId: string, user: TypingUser) => void;\n removeTypingUser: (conversationId: string, userId: string) => void;\n setUserOnline: (userId: string) => void;\n setUserOffline: (userId: string) => void;\n setOnlineUsers: (userIds: string[]) => void;\n setLastRead: (conversationId: string, messageId: string, readAt: string) => void;\n setLastSeen: (userId: string, lastSeenAt: string) => void;\n setReplyingTo: (message: Message | null) => void;\n setEditingMessage: (message: Message | null) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n toggleGroupInfo: () => void;\n setGroupInfoOpen: (open: boolean) => void;\n toggleStarredPanel: () => void;\n setStarredPanelOpen: (open: boolean) => void;\n}\n\nexport const useChatStore = create<ChatState>((set) => ({\n activeConversationId: null,\n pendingTarget: null,\n typingUsers: {},\n onlineUsers: [],\n lastRead: {},\n lastSeen: {},\n replyingTo: null,\n editingMessage: null,\n isSidebarOpen: true,\n isGroupInfoOpen: false,\n isStarredPanelOpen: false,\n\n setActiveConversation: (id) =>\n set({ activeConversationId: id, replyingTo: null, editingMessage: null }),\n\n setPendingTarget: (target) => set({ pendingTarget: target }),\n\n addTypingUser: (conversationId, user) =>\n set((state) => {\n const existing = state.typingUsers[conversationId] ?? [];\n const deduped = existing.filter((u) => u.userId !== user.userId);\n return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };\n }),\n\n removeTypingUser: (conversationId, userId) =>\n set((state) => ({\n typingUsers: {\n ...state.typingUsers,\n [conversationId]: (state.typingUsers[conversationId] ?? []).filter(\n (u) => u.userId !== userId,\n ),\n },\n })),\n\n setUserOnline: (userId) =>\n set((state) => ({\n onlineUsers: state.onlineUsers.includes(userId)\n ? state.onlineUsers\n : [...state.onlineUsers, userId],\n })),\n\n setUserOffline: (userId) =>\n set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),\n\n setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),\n\n setLastRead: (conversationId, messageId, readAt) =>\n set((state) => ({\n lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } },\n })),\n\n setLastSeen: (userId, lastSeenAt) =>\n set((state) => ({\n lastSeen: { ...state.lastSeen, [userId]: lastSeenAt },\n })),\n\n setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),\n\n setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),\n\n toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),\n setSidebarOpen: (open) => set({ isSidebarOpen: open }),\n\n toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),\n setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),\n\n toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),\n setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open }),\n}));\n"],"mappings":";AAAA,SAAS,cAAc;AAgDhB,IAAM,eAAe,OAAkB,CAAC,SAAS;AAAA,EACtD,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,aAAa,CAAC;AAAA,EACd,aAAa,CAAC;AAAA,EACd,UAAU,CAAC;AAAA,EACX,UAAU,CAAC;AAAA,EACX,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EAEpB,uBAAuB,CAAC,OACtB,IAAI,EAAE,sBAAsB,IAAI,YAAY,MAAM,gBAAgB,KAAK,CAAC;AAAA,EAE1E,kBAAkB,CAAC,WAAW,IAAI,EAAE,eAAe,OAAO,CAAC;AAAA,EAE3D,eAAe,CAAC,gBAAgB,SAC9B,IAAI,CAAC,UAAU;AACb,UAAM,WAAW,MAAM,YAAY,cAAc,KAAK,CAAC;AACvD,UAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,KAAK,MAAM;AAC/D,WAAO,EAAE,aAAa,EAAE,GAAG,MAAM,aAAa,CAAC,cAAc,GAAG,CAAC,GAAG,SAAS,IAAI,EAAE,EAAE;AAAA,EACvF,CAAC;AAAA,EAEH,kBAAkB,CAAC,gBAAgB,WACjC,IAAI,CAAC,WAAW;AAAA,IACd,aAAa;AAAA,MACX,GAAG,MAAM;AAAA,MACT,CAAC,cAAc,IAAI,MAAM,YAAY,cAAc,KAAK,CAAC,GAAG;AAAA,QAC1D,CAAC,MAAM,EAAE,WAAW;AAAA,MACtB;AAAA,IACF;AAAA,EACF,EAAE;AAAA,EAEJ,eAAe,CAAC,WACd,IAAI,CAAC,WAAW;AAAA,IACd,aAAa,MAAM,YAAY,SAAS,MAAM,IAC1C,MAAM,cACN,CAAC,GAAG,MAAM,aAAa,MAAM;AAAA,EACnC,EAAE;AAAA,EAEJ,gBAAgB,CAAC,WACf,IAAI,CAAC,WAAW,EAAE,aAAa,MAAM,YAAY,OAAO,CAAC,OAAO,OAAO,MAAM,EAAE,EAAE;AAAA,EAEnF,gBAAgB,CAAC,YAAY,IAAI,EAAE,aAAa,QAAQ,CAAC;AAAA,EAEzD,aAAa,CAAC,gBAAgB,WAAW,WACvC,IAAI,CAAC,WAAW;AAAA,IACd,UAAU,EAAE,GAAG,MAAM,UAAU,CAAC,cAAc,GAAG,EAAE,WAAW,OAAO,EAAE;AAAA,EACzE,EAAE;AAAA,EAEJ,aAAa,CAAC,QAAQ,eACpB,IAAI,CAAC,WAAW;AAAA,IACd,UAAU,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,GAAG,WAAW;AAAA,EACtD,EAAE;AAAA,EAEJ,eAAe,CAAC,YAAY,IAAI,EAAE,YAAY,SAAS,gBAAgB,KAAK,CAAC;AAAA,EAE7E,mBAAmB,CAAC,YAAY,IAAI,EAAE,gBAAgB,SAAS,YAAY,KAAK,CAAC;AAAA,EAEjF,eAAe,MAAM,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,MAAM,cAAc,EAAE;AAAA,EAC7E,gBAAgB,CAAC,SAAS,IAAI,EAAE,eAAe,KAAK,CAAC;AAAA,EAErD,iBAAiB,MAAM,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,MAAM,gBAAgB,EAAE;AAAA,EACnF,kBAAkB,CAAC,SAAS,IAAI,EAAE,iBAAiB,KAAK,CAAC;AAAA,EAEzD,oBAAoB,MAAM,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,MAAM,mBAAmB,EAAE;AAAA,EAC5F,qBAAqB,CAAC,SAAS,IAAI,EAAE,oBAAoB,KAAK,CAAC;AACjE,EAAE;","names":[]}
package/dist/index.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,66 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/stores/chat.store.ts
34
+ var chat_store_exports = {};
35
+ __export(chat_store_exports, {
36
+ useChatStore: () => useChatStore
37
+ });
38
+ var import_zustand, useChatStore;
39
+ var init_chat_store = __esm({
40
+ "src/stores/chat.store.ts"() {
41
+ "use strict";
42
+ import_zustand = require("zustand");
43
+ useChatStore = (0, import_zustand.create)((set) => ({
44
+ activeConversationId: null,
45
+ pendingTarget: null,
46
+ typingUsers: {},
47
+ onlineUsers: [],
48
+ lastRead: {},
49
+ lastSeen: {},
50
+ replyingTo: null,
51
+ editingMessage: null,
52
+ isSidebarOpen: true,
53
+ isGroupInfoOpen: false,
54
+ isStarredPanelOpen: false,
55
+ setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
56
+ setPendingTarget: (target) => set({ pendingTarget: target }),
57
+ addTypingUser: (conversationId, user) => set((state) => {
58
+ const existing = state.typingUsers[conversationId] ?? [];
59
+ const deduped = existing.filter((u) => u.userId !== user.userId);
60
+ return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };
61
+ }),
62
+ removeTypingUser: (conversationId, userId) => set((state) => ({
63
+ typingUsers: {
64
+ ...state.typingUsers,
65
+ [conversationId]: (state.typingUsers[conversationId] ?? []).filter(
66
+ (u) => u.userId !== userId
67
+ )
68
+ }
69
+ })),
70
+ setUserOnline: (userId) => set((state) => ({
71
+ onlineUsers: state.onlineUsers.includes(userId) ? state.onlineUsers : [...state.onlineUsers, userId]
72
+ })),
73
+ setUserOffline: (userId) => set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),
74
+ setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),
75
+ setLastRead: (conversationId, messageId, readAt) => set((state) => ({
76
+ lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } }
77
+ })),
78
+ setLastSeen: (userId, lastSeenAt) => set((state) => ({
79
+ lastSeen: { ...state.lastSeen, [userId]: lastSeenAt }
80
+ })),
81
+ setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
82
+ setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
83
+ toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
84
+ setSidebarOpen: (open) => set({ isSidebarOpen: open }),
85
+ toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
86
+ setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
87
+ toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
88
+ setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
89
+ }));
90
+ }
91
+ });
92
+
30
93
  // src/index.ts
31
94
  var src_exports = {};
32
95
  __export(src_exports, {
@@ -55,7 +118,8 @@ __export(src_exports, {
55
118
  storageApi: () => storageApi,
56
119
  tryGetSocket: () => tryGetSocket,
57
120
  uploadBatch: () => uploadBatch,
58
- useChatStore: () => useChatStore
121
+ useChatStore: () => useChatStore,
122
+ usersApi: () => usersApi
59
123
  });
60
124
  module.exports = __toCommonJS(src_exports);
61
125
 
@@ -104,7 +168,10 @@ function resolveConfig(config) {
104
168
  onProgress: config.upload?.onProgress
105
169
  },
106
170
  platformUploadFn: config.platformUploadFn,
107
- persistStorage: config.persistStorage
171
+ persistStorage: config.persistStorage,
172
+ messagePageSize: config.messagePageSize ?? 40,
173
+ starredMessagePageSize: config.starredMessagePageSize ?? 30,
174
+ searchPageSize: config.searchPageSize ?? 50
108
175
  };
109
176
  }
110
177
 
@@ -238,9 +305,14 @@ var authApi = {
238
305
  // src/api/messages.ts
239
306
  var messagesApi = {
240
307
  async list(conversationId, params = {}) {
308
+ const { cursor, direction, ...rest } = params;
309
+ const serverParams = { ...rest };
310
+ if (cursor) {
311
+ serverParams[direction === "after" ? "after" : "before"] = cursor;
312
+ }
241
313
  const { data } = await getApiClient().get(
242
314
  `/conversations/${conversationId}/messages`,
243
- { params }
315
+ { params: serverParams }
244
316
  );
245
317
  return data;
246
318
  },
@@ -289,6 +361,12 @@ var messagesApi = {
289
361
  const { data } = await getApiClient().get("/messages/search", { params });
290
362
  return data;
291
363
  },
364
+ async getLastRead(conversationId) {
365
+ const { data } = await getApiClient().get(
366
+ `/conversations/${conversationId}/read-receipt`
367
+ );
368
+ return data;
369
+ },
292
370
  async markAsRead(conversationId, messageId) {
293
371
  await getApiClient().post(`/conversations/${conversationId}/read`, messageId ? { messageId } : {});
294
372
  },
@@ -417,9 +495,45 @@ var conversationsApi = {
417
495
  const { data } = await getApiClient().get(`/conversations/${conversationId}/participants`);
418
496
  return data;
419
497
  },
420
- async searchUsers(query) {
421
- const { data } = await getApiClient().get("/users/search", { params: { query } });
422
- return data.data;
498
+ /**
499
+ * Get unread message count for a single conversation.
500
+ * Use this after app foreground or socket reconnect to refresh a specific count.
501
+ */
502
+ async getUnreadCount(conversationId) {
503
+ const { data } = await getApiClient().get(
504
+ `/conversations/${conversationId}/unread`
505
+ );
506
+ return data;
507
+ },
508
+ /**
509
+ * Get total unread count across all conversations + per-conversation breakdown.
510
+ * Use on app cold start, foreground resume, or after socket reconnect.
511
+ * The socket keeps counts live while connected — this is the source of truth
512
+ * when the socket was down.
513
+ */
514
+ async getUnreadSummary() {
515
+ const { data } = await getApiClient().get("/conversations/unread");
516
+ return data;
517
+ },
518
+ /**
519
+ * Upload or replace the group icon (admin only).
520
+ * Accepts a File (web) or { uri, name, type } object (React Native).
521
+ * The server stores the image in its own storage, deletes the previous icon,
522
+ * and returns a fresh signed iconUrl on every subsequent response.
523
+ */
524
+ async uploadIcon(conversationId, file) {
525
+ const formData = new FormData();
526
+ if (file instanceof File) {
527
+ formData.append("icon", file);
528
+ } else {
529
+ formData.append("icon", { uri: file.uri, name: file.name, type: file.type });
530
+ }
531
+ const { data } = await getApiClient().put(
532
+ `/conversations/${conversationId}/icon`,
533
+ formData,
534
+ { headers: { "Content-Type": "multipart/form-data" } }
535
+ );
536
+ return normalizeConversation(data);
423
537
  }
424
538
  };
425
539
 
@@ -524,6 +638,44 @@ var devicesApi = {
524
638
  }
525
639
  };
526
640
 
641
+ // src/api/users.ts
642
+ var usersApi = {
643
+ async list(params = {}) {
644
+ const { data } = await getApiClient().get("/users", { params });
645
+ return data;
646
+ },
647
+ async getById(userId) {
648
+ const { data } = await getApiClient().get(`/users/${userId}`);
649
+ return data;
650
+ },
651
+ async getLastSeen(userId) {
652
+ const { data } = await getApiClient().get(`/users/${userId}`);
653
+ return { lastSeenAt: data.lastSeenAt ?? null };
654
+ },
655
+ /**
656
+ * Update notification preferences for the current user.
657
+ * Partial update — only send fields you want to change.
658
+ * A prefs record is automatically created with defaults when a device
659
+ * token is first registered, so this never fails with "not found".
660
+ */
661
+ async updatePreferences(prefs) {
662
+ const { data } = await getApiClient().put("/users/me/preferences", prefs);
663
+ return data;
664
+ },
665
+ /**
666
+ * Fetch current notification preferences for the current user.
667
+ * Returns null if no prefs record exists yet (all defaults apply).
668
+ */
669
+ async getPreferences() {
670
+ try {
671
+ const { data } = await getApiClient().get("/users/me/preferences");
672
+ return data;
673
+ } catch {
674
+ return null;
675
+ }
676
+ }
677
+ };
678
+
527
679
  // src/socket/socket.ts
528
680
  var import_socket = require("socket.io-client");
529
681
  var _socket = null;
@@ -550,17 +702,17 @@ function onSocketStatus(listener) {
550
702
  _statusListeners.add(listener);
551
703
  return () => _statusListeners.delete(listener);
552
704
  }
553
- async function connectSocket(config, getToken, userId, tenantId) {
554
- if (_socket?.connected) return _socket;
705
+ async function connectSocket(config, getToken) {
706
+ if (_socket && !_socket.disconnected) return _socket;
555
707
  _getToken = getToken;
556
- _userId = userId;
557
- _tenantId = tenantId;
708
+ _userId = config.userId;
709
+ _tenantId = config.tenantId;
558
710
  const token = getToken();
559
711
  _socket = (0, import_socket.io)(`${config.socketOrigin}/chat`, {
560
712
  auth: {
561
713
  token: token ? `Bearer ${token}` : "",
562
- ...userId && { userId },
563
- ...tenantId && { tenantId },
714
+ ...config.userId && { userId: config.userId },
715
+ ...config.tenantId && { tenantId: config.tenantId },
564
716
  ...config.avatar?.url && { avatarUrl: config.avatar.url },
565
717
  ...config.avatar?.base64 && { avatarBase64: config.avatar.base64 }
566
718
  },
@@ -578,9 +730,30 @@ async function connectSocket(config, getToken, userId, tenantId) {
578
730
  setStatus("connecting");
579
731
  _socket.on("connect", () => setStatus("connected"));
580
732
  _socket.on("disconnect", () => setStatus("disconnected"));
581
- _socket.on("connect_error", () => setStatus("error"));
733
+ _socket.on("connect_error", (err) => {
734
+ console.error("[AntzChat] Socket connect_error:", err?.message, err?.data);
735
+ setStatus("error");
736
+ });
582
737
  _socket.on("reconnecting", () => setStatus("reconnecting"));
583
738
  _socket.on("reconnect", () => setStatus("connected"));
739
+ _socket.on("read_receipt", (event) => {
740
+ Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
741
+ useChatStore2.getState().setLastRead(event.conversationId, event.messageId, event.readAt);
742
+ });
743
+ });
744
+ _socket.on("user_online", (event) => {
745
+ Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
746
+ const store = useChatStore2.getState();
747
+ store.setUserOnline(event.userId);
748
+ });
749
+ });
750
+ _socket.on("user_offline", (event) => {
751
+ Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
752
+ const store = useChatStore2.getState();
753
+ store.setUserOffline(event.userId);
754
+ if (event.lastSeenAt) store.setLastSeen(event.userId, event.lastSeenAt);
755
+ });
756
+ });
584
757
  return _socket;
585
758
  }
586
759
  function disconnectSocket() {
@@ -695,12 +868,12 @@ var socketEmit = {
695
868
  };
696
869
 
697
870
  // src/stores/auth.store.ts
698
- var import_zustand = require("zustand");
871
+ var import_zustand2 = require("zustand");
699
872
  var import_middleware = require("zustand/middleware");
700
873
  function createAuthStore(storage) {
701
874
  if (!storage) throw new Error("[AntzChat] createAuthStore requires a valid PersistStorage \u2014 received undefined. Make sure the SDK config is fully resolved before initializing the store.");
702
875
  const ref = { store: null };
703
- const store = (0, import_zustand.create)()(
876
+ const store = (0, import_zustand2.create)()(
704
877
  (0, import_middleware.persist)(
705
878
  (set) => ({
706
879
  user: null,
@@ -781,47 +954,8 @@ function resetAuthStore() {
781
954
  _authStore = null;
782
955
  }
783
956
 
784
- // src/stores/chat.store.ts
785
- var import_zustand2 = require("zustand");
786
- var useChatStore = (0, import_zustand2.create)((set) => ({
787
- activeConversationId: null,
788
- pendingTarget: null,
789
- typingUsers: {},
790
- onlineUsers: [],
791
- replyingTo: null,
792
- editingMessage: null,
793
- isSidebarOpen: true,
794
- isGroupInfoOpen: false,
795
- isStarredPanelOpen: false,
796
- setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
797
- setPendingTarget: (target) => set({ pendingTarget: target }),
798
- addTypingUser: (conversationId, user) => set((state) => {
799
- const existing = state.typingUsers[conversationId] ?? [];
800
- const deduped = existing.filter((u) => u.userId !== user.userId);
801
- return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };
802
- }),
803
- removeTypingUser: (conversationId, userId) => set((state) => ({
804
- typingUsers: {
805
- ...state.typingUsers,
806
- [conversationId]: (state.typingUsers[conversationId] ?? []).filter(
807
- (u) => u.userId !== userId
808
- )
809
- }
810
- })),
811
- setUserOnline: (userId) => set((state) => ({
812
- onlineUsers: state.onlineUsers.includes(userId) ? state.onlineUsers : [...state.onlineUsers, userId]
813
- })),
814
- setUserOffline: (userId) => set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),
815
- setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),
816
- setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
817
- setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
818
- toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
819
- setSidebarOpen: (open) => set({ isSidebarOpen: open }),
820
- toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
821
- setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
822
- toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
823
- setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
824
- }));
957
+ // src/index.ts
958
+ init_chat_store();
825
959
 
826
960
  // src/client-facade.ts
827
961
  var AntzChatClient = class {
@@ -830,6 +964,7 @@ var AntzChatClient = class {
830
964
  this.messages = messagesApi;
831
965
  this.conversations = conversationsApi;
832
966
  this.storage = storageApi;
967
+ this.users = usersApi;
833
968
  this.socket = {
834
969
  emit: socketEmit,
835
970
  on: (event, handler) => getSocket().on(event, handler),
@@ -885,6 +1020,7 @@ var AntzChatClient = class {
885
1020
  storageApi,
886
1021
  tryGetSocket,
887
1022
  uploadBatch,
888
- useChatStore
1023
+ useChatStore,
1024
+ usersApi
889
1025
  });
890
1026
  //# sourceMappingURL=index.cjs.map