@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.
package/dist/index.d.cts CHANGED
@@ -25,6 +25,7 @@ interface UploadableFile {
25
25
  }
26
26
  interface User {
27
27
  id: string;
28
+ externalId?: string;
28
29
  tenantId: string;
29
30
  email: string;
30
31
  username: string;
@@ -154,7 +155,6 @@ interface Conversation {
154
155
  conversationType: ConversationType;
155
156
  name?: string;
156
157
  description?: string;
157
- icon?: string;
158
158
  iconUrl?: string;
159
159
  participants: Participant[];
160
160
  participantCount?: number;
@@ -172,6 +172,38 @@ interface Conversation {
172
172
  isEncryptionEnabled?: boolean;
173
173
  encryptionKey?: string;
174
174
  }
175
+ interface ConversationUnreadCount {
176
+ conversationId: string;
177
+ unreadCount: number;
178
+ }
179
+ interface UnreadSummary {
180
+ /** Total unread messages across all conversations */
181
+ totalUnread: number;
182
+ /** Per-conversation breakdown — only includes conversations with unread > 0 */
183
+ byConversation: ConversationUnreadCount[];
184
+ }
185
+ interface ConversationListParams {
186
+ page?: number;
187
+ limit?: number;
188
+ /** Filter by conversation type */
189
+ type?: ConversationType;
190
+ /** Only pinned (true) or unpinned (false) conversations */
191
+ isPinned?: boolean;
192
+ /** Only muted (true) or unmuted (false) conversations */
193
+ isMuted?: boolean;
194
+ /** Only conversations with unread messages */
195
+ hasUnread?: boolean;
196
+ /** Search by group name / description (text index) */
197
+ search?: string;
198
+ /** Filter by current user's role in the conversation */
199
+ role?: ParticipantRole;
200
+ /** Filter by whether the last message has attachments */
201
+ hasAttachments?: boolean;
202
+ /** Filter by last message attachment type */
203
+ attachmentType?: FileType;
204
+ /** Filter by notification setting */
205
+ notificationsEnabled?: boolean;
206
+ }
175
207
  interface PaginationMeta {
176
208
  total: number;
177
209
  page: number;
@@ -326,6 +358,37 @@ interface SendMessagePayload {
326
358
  encryptedContent?: EncryptedContent;
327
359
  isEncrypted?: boolean;
328
360
  }
361
+ interface QuietHours {
362
+ enabled: boolean;
363
+ /** HH:MM — e.g. "22:00" */
364
+ start: string;
365
+ /** HH:MM — e.g. "08:00" */
366
+ end: string;
367
+ /** IANA timezone — e.g. "Asia/Kolkata" */
368
+ timezone: string;
369
+ }
370
+ /**
371
+ * User notification preferences stored in chat_user_prefs.
372
+ * All fields are optional on update — only send what changed.
373
+ * Any future preference field is added here and to the server schema;
374
+ * no new collections or API endpoints needed.
375
+ */
376
+ interface UserPreferences {
377
+ /** Master switch — false disables all push notifications */
378
+ notificationsEnabled?: boolean;
379
+ /** Play sound with notifications */
380
+ soundEnabled?: boolean;
381
+ /** Show message text in notification body (false = show "New message" only) */
382
+ messagePreview?: boolean;
383
+ /** Notify when @mentioned in a group */
384
+ notifyOnMention?: boolean;
385
+ /** Notify when someone reacts to your message */
386
+ notifyOnReaction?: boolean;
387
+ /** Notify when added to a group */
388
+ notifyOnGroupInvite?: boolean;
389
+ /** Quiet hours window — no push delivered during this period */
390
+ quietHours?: QuietHours;
391
+ }
329
392
 
330
393
  interface FileSizeLimits {
331
394
  image?: number;
@@ -406,6 +469,21 @@ interface AntzChatConfig {
406
469
  /** Must match server ENCRYPTION_MODE env var. Default: 'none' */
407
470
  encryptionMode?: 'none' | 'server';
408
471
  upload?: UploadConfig;
472
+ /**
473
+ * Number of messages fetched per page when loading chat history.
474
+ * Default: 40
475
+ */
476
+ messagePageSize?: number;
477
+ /**
478
+ * Number of starred messages fetched per page.
479
+ * Default: 30
480
+ */
481
+ starredMessagePageSize?: number;
482
+ /**
483
+ * Number of search results fetched per request.
484
+ * Default: 50
485
+ */
486
+ searchPageSize?: number;
409
487
  /**
410
488
  * Platform-specific binary upload implementation.
411
489
  * Required — each SDK (web, RN) provides its own.
@@ -447,6 +525,9 @@ interface ResolvedConfig {
447
525
  };
448
526
  platformUploadFn: PlatformUploadFn;
449
527
  persistStorage: PersistStorage;
528
+ messagePageSize: number;
529
+ starredMessagePageSize: number;
530
+ searchPageSize: number;
450
531
  }
451
532
  declare function resolveConfig(config: AntzChatConfig): ResolvedConfig;
452
533
 
@@ -503,6 +584,10 @@ declare const messagesApi: {
503
584
  conversationId?: string;
504
585
  }): Promise<PaginatedResponse<Message>>;
505
586
  search(params: SearchParams): Promise<PaginatedResponse<Message>>;
587
+ getLastRead(conversationId: string): Promise<{
588
+ lastReadMessageId: string | null;
589
+ lastReadAt: string | null;
590
+ }>;
506
591
  markAsRead(conversationId: string, messageId?: string): Promise<void>;
507
592
  pin(messageId: string): Promise<Message>;
508
593
  unpin(messageId: string): Promise<Message>;
@@ -513,7 +598,6 @@ declare function normalizeConversation(conv: any): Conversation;
513
598
  interface CreateGroupData {
514
599
  name: string;
515
600
  description?: string;
516
- icon?: string;
517
601
  participantIds: string[];
518
602
  }
519
603
  interface CreateDirectData {
@@ -522,13 +606,9 @@ interface CreateDirectData {
522
606
  interface UpdateConversationData {
523
607
  name?: string;
524
608
  description?: string;
525
- icon?: string;
526
609
  }
527
610
  declare const conversationsApi: {
528
- list(params?: {
529
- page?: number;
530
- limit?: number;
531
- }): Promise<PaginatedResponse<Conversation>>;
611
+ list(params?: ConversationListParams): Promise<PaginatedResponse<Conversation>>;
532
612
  get(conversationId: string): Promise<Conversation>;
533
613
  createGroup(payload: CreateGroupData): Promise<Conversation>;
534
614
  createDirect(payload: CreateDirectData): Promise<Conversation>;
@@ -543,7 +623,29 @@ declare const conversationsApi: {
543
623
  unpin(conversationId: string): Promise<void>;
544
624
  leave(conversationId: string): Promise<void>;
545
625
  getMembers(conversationId: string): Promise<User[]>;
546
- searchUsers(query: string): Promise<User[]>;
626
+ /**
627
+ * Get unread message count for a single conversation.
628
+ * Use this after app foreground or socket reconnect to refresh a specific count.
629
+ */
630
+ getUnreadCount(conversationId: string): Promise<ConversationUnreadCount>;
631
+ /**
632
+ * Get total unread count across all conversations + per-conversation breakdown.
633
+ * Use on app cold start, foreground resume, or after socket reconnect.
634
+ * The socket keeps counts live while connected — this is the source of truth
635
+ * when the socket was down.
636
+ */
637
+ getUnreadSummary(): Promise<UnreadSummary>;
638
+ /**
639
+ * Upload or replace the group icon (admin only).
640
+ * Accepts a File (web) or { uri, name, type } object (React Native).
641
+ * The server stores the image in its own storage, deletes the previous icon,
642
+ * and returns a fresh signed iconUrl on every subsequent response.
643
+ */
644
+ uploadIcon(conversationId: string, file: File | {
645
+ uri: string;
646
+ name: string;
647
+ type: string;
648
+ }): Promise<Conversation>;
547
649
  };
548
650
 
549
651
  declare const storageApi: {
@@ -631,6 +733,30 @@ declare const devicesApi: {
631
733
  remove(deviceId: string): Promise<void>;
632
734
  };
633
735
 
736
+ declare const usersApi: {
737
+ list(params?: {
738
+ query?: string;
739
+ page?: number;
740
+ limit?: number;
741
+ }): Promise<PaginatedResponse<User>>;
742
+ getById(userId: string): Promise<User>;
743
+ getLastSeen(userId: string): Promise<{
744
+ lastSeenAt: string | null;
745
+ }>;
746
+ /**
747
+ * Update notification preferences for the current user.
748
+ * Partial update — only send fields you want to change.
749
+ * A prefs record is automatically created with defaults when a device
750
+ * token is first registered, so this never fails with "not found".
751
+ */
752
+ updatePreferences(prefs: UserPreferences): Promise<User>;
753
+ /**
754
+ * Fetch current notification preferences for the current user.
755
+ * Returns null if no prefs record exists yet (all defaults apply).
756
+ */
757
+ getPreferences(): Promise<UserPreferences | null>;
758
+ };
759
+
634
760
  type TokenStore = {
635
761
  getAccessToken: () => string | null | undefined;
636
762
  getRefreshToken: () => string | null | undefined;
@@ -649,7 +775,7 @@ declare function getSocket(): Socket;
649
775
  declare function tryGetSocket(): Socket | null;
650
776
  declare function getSocketStatus(): SocketStatus;
651
777
  declare function onSocketStatus(listener: StatusListener): () => void;
652
- declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined, userId?: string, tenantId?: string): Promise<Socket>;
778
+ declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined): Promise<Socket>;
653
779
  declare function disconnectSocket(): void;
654
780
  declare function reconnectSocket(token: string, userId?: string, tenantId?: string): void;
655
781
  /**
@@ -769,6 +895,10 @@ interface TypingUser {
769
895
  displayName: string;
770
896
  avatarUrl?: string;
771
897
  }
898
+ interface LastReadEntry {
899
+ messageId: string;
900
+ readAt: string;
901
+ }
772
902
  interface ChatState {
773
903
  activeConversationId: string | null;
774
904
  pendingTarget: {
@@ -777,6 +907,10 @@ interface ChatState {
777
907
  } | null;
778
908
  typingUsers: Record<string, TypingUser[]>;
779
909
  onlineUsers: string[];
910
+ /** keyed by conversationId — current user's last read pointer per conversation */
911
+ lastRead: Record<string, LastReadEntry>;
912
+ /** keyed by userId — last seen timestamp for each user */
913
+ lastSeen: Record<string, string>;
780
914
  replyingTo: Message | null;
781
915
  editingMessage: Message | null;
782
916
  isSidebarOpen: boolean;
@@ -792,6 +926,8 @@ interface ChatState {
792
926
  setUserOnline: (userId: string) => void;
793
927
  setUserOffline: (userId: string) => void;
794
928
  setOnlineUsers: (userIds: string[]) => void;
929
+ setLastRead: (conversationId: string, messageId: string, readAt: string) => void;
930
+ setLastSeen: (userId: string, lastSeenAt: string) => void;
795
931
  setReplyingTo: (message: Message | null) => void;
796
932
  setEditingMessage: (message: Message | null) => void;
797
933
  toggleSidebar: () => void;
@@ -851,16 +987,17 @@ declare class AntzChatClient {
851
987
  conversationId?: string;
852
988
  }): Promise<PaginatedResponse<Message>>;
853
989
  search(params: SearchParams): Promise<PaginatedResponse<Message>>;
990
+ getLastRead(conversationId: string): Promise<{
991
+ lastReadMessageId: string | null;
992
+ lastReadAt: string | null;
993
+ }>;
854
994
  markAsRead(conversationId: string, messageId?: string): Promise<void>;
855
995
  pin(messageId: string): Promise<Message>;
856
996
  unpin(messageId: string): Promise<Message>;
857
997
  getPinned(conversationId: string): Promise<Message[]>;
858
998
  };
859
999
  readonly conversations: {
860
- list(params?: {
861
- page?: number;
862
- limit?: number;
863
- }): Promise<PaginatedResponse<Conversation>>;
1000
+ list(params?: ConversationListParams): Promise<PaginatedResponse<Conversation>>;
864
1001
  get(conversationId: string): Promise<Conversation>;
865
1002
  createGroup(payload: CreateGroupData): Promise<Conversation>;
866
1003
  createDirect(payload: CreateDirectData): Promise<Conversation>;
@@ -875,7 +1012,13 @@ declare class AntzChatClient {
875
1012
  unpin(conversationId: string): Promise<void>;
876
1013
  leave(conversationId: string): Promise<void>;
877
1014
  getMembers(conversationId: string): Promise<User[]>;
878
- searchUsers(query: string): Promise<User[]>;
1015
+ getUnreadCount(conversationId: string): Promise<ConversationUnreadCount>;
1016
+ getUnreadSummary(): Promise<UnreadSummary>;
1017
+ uploadIcon(conversationId: string, file: File | {
1018
+ uri: string;
1019
+ name: string;
1020
+ type: string;
1021
+ }): Promise<Conversation>;
879
1022
  };
880
1023
  readonly storage: {
881
1024
  requestPresignedUrl(payload: PresignedUrlRequest): Promise<PresignedUrlResponse>;
@@ -903,6 +1046,19 @@ declare class AntzChatClient {
903
1046
  limit?: number;
904
1047
  }): Promise<PaginatedResponse<FileResponse>>;
905
1048
  };
1049
+ readonly users: {
1050
+ list(params?: {
1051
+ query?: string;
1052
+ page?: number;
1053
+ limit?: number;
1054
+ }): Promise<PaginatedResponse<User>>;
1055
+ getById(userId: string): Promise<User>;
1056
+ getLastSeen(userId: string): Promise<{
1057
+ lastSeenAt: string | null;
1058
+ }>;
1059
+ updatePreferences(prefs: UserPreferences): Promise<User>;
1060
+ getPreferences(): Promise<UserPreferences | null>;
1061
+ };
906
1062
  readonly socket: ClientSocketHandle;
907
1063
  constructor(rawConfig: AntzChatConfig);
908
1064
  connect(): Promise<void>;
@@ -910,4 +1066,4 @@ declare class AntzChatClient {
910
1066
  uploadFiles(files: UploadableFile[], conversationId?: string): Promise<BatchUploadResult>;
911
1067
  }
912
1068
 
913
- export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type Conversation, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore };
1069
+ export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type Conversation, type ConversationListParams, type ConversationUnreadCount, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type LastReadEntry, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type QuietHours, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UnreadSummary, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserPreferences, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore, usersApi };
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ interface UploadableFile {
25
25
  }
26
26
  interface User {
27
27
  id: string;
28
+ externalId?: string;
28
29
  tenantId: string;
29
30
  email: string;
30
31
  username: string;
@@ -154,7 +155,6 @@ interface Conversation {
154
155
  conversationType: ConversationType;
155
156
  name?: string;
156
157
  description?: string;
157
- icon?: string;
158
158
  iconUrl?: string;
159
159
  participants: Participant[];
160
160
  participantCount?: number;
@@ -172,6 +172,38 @@ interface Conversation {
172
172
  isEncryptionEnabled?: boolean;
173
173
  encryptionKey?: string;
174
174
  }
175
+ interface ConversationUnreadCount {
176
+ conversationId: string;
177
+ unreadCount: number;
178
+ }
179
+ interface UnreadSummary {
180
+ /** Total unread messages across all conversations */
181
+ totalUnread: number;
182
+ /** Per-conversation breakdown — only includes conversations with unread > 0 */
183
+ byConversation: ConversationUnreadCount[];
184
+ }
185
+ interface ConversationListParams {
186
+ page?: number;
187
+ limit?: number;
188
+ /** Filter by conversation type */
189
+ type?: ConversationType;
190
+ /** Only pinned (true) or unpinned (false) conversations */
191
+ isPinned?: boolean;
192
+ /** Only muted (true) or unmuted (false) conversations */
193
+ isMuted?: boolean;
194
+ /** Only conversations with unread messages */
195
+ hasUnread?: boolean;
196
+ /** Search by group name / description (text index) */
197
+ search?: string;
198
+ /** Filter by current user's role in the conversation */
199
+ role?: ParticipantRole;
200
+ /** Filter by whether the last message has attachments */
201
+ hasAttachments?: boolean;
202
+ /** Filter by last message attachment type */
203
+ attachmentType?: FileType;
204
+ /** Filter by notification setting */
205
+ notificationsEnabled?: boolean;
206
+ }
175
207
  interface PaginationMeta {
176
208
  total: number;
177
209
  page: number;
@@ -326,6 +358,37 @@ interface SendMessagePayload {
326
358
  encryptedContent?: EncryptedContent;
327
359
  isEncrypted?: boolean;
328
360
  }
361
+ interface QuietHours {
362
+ enabled: boolean;
363
+ /** HH:MM — e.g. "22:00" */
364
+ start: string;
365
+ /** HH:MM — e.g. "08:00" */
366
+ end: string;
367
+ /** IANA timezone — e.g. "Asia/Kolkata" */
368
+ timezone: string;
369
+ }
370
+ /**
371
+ * User notification preferences stored in chat_user_prefs.
372
+ * All fields are optional on update — only send what changed.
373
+ * Any future preference field is added here and to the server schema;
374
+ * no new collections or API endpoints needed.
375
+ */
376
+ interface UserPreferences {
377
+ /** Master switch — false disables all push notifications */
378
+ notificationsEnabled?: boolean;
379
+ /** Play sound with notifications */
380
+ soundEnabled?: boolean;
381
+ /** Show message text in notification body (false = show "New message" only) */
382
+ messagePreview?: boolean;
383
+ /** Notify when @mentioned in a group */
384
+ notifyOnMention?: boolean;
385
+ /** Notify when someone reacts to your message */
386
+ notifyOnReaction?: boolean;
387
+ /** Notify when added to a group */
388
+ notifyOnGroupInvite?: boolean;
389
+ /** Quiet hours window — no push delivered during this period */
390
+ quietHours?: QuietHours;
391
+ }
329
392
 
330
393
  interface FileSizeLimits {
331
394
  image?: number;
@@ -406,6 +469,21 @@ interface AntzChatConfig {
406
469
  /** Must match server ENCRYPTION_MODE env var. Default: 'none' */
407
470
  encryptionMode?: 'none' | 'server';
408
471
  upload?: UploadConfig;
472
+ /**
473
+ * Number of messages fetched per page when loading chat history.
474
+ * Default: 40
475
+ */
476
+ messagePageSize?: number;
477
+ /**
478
+ * Number of starred messages fetched per page.
479
+ * Default: 30
480
+ */
481
+ starredMessagePageSize?: number;
482
+ /**
483
+ * Number of search results fetched per request.
484
+ * Default: 50
485
+ */
486
+ searchPageSize?: number;
409
487
  /**
410
488
  * Platform-specific binary upload implementation.
411
489
  * Required — each SDK (web, RN) provides its own.
@@ -447,6 +525,9 @@ interface ResolvedConfig {
447
525
  };
448
526
  platformUploadFn: PlatformUploadFn;
449
527
  persistStorage: PersistStorage;
528
+ messagePageSize: number;
529
+ starredMessagePageSize: number;
530
+ searchPageSize: number;
450
531
  }
451
532
  declare function resolveConfig(config: AntzChatConfig): ResolvedConfig;
452
533
 
@@ -503,6 +584,10 @@ declare const messagesApi: {
503
584
  conversationId?: string;
504
585
  }): Promise<PaginatedResponse<Message>>;
505
586
  search(params: SearchParams): Promise<PaginatedResponse<Message>>;
587
+ getLastRead(conversationId: string): Promise<{
588
+ lastReadMessageId: string | null;
589
+ lastReadAt: string | null;
590
+ }>;
506
591
  markAsRead(conversationId: string, messageId?: string): Promise<void>;
507
592
  pin(messageId: string): Promise<Message>;
508
593
  unpin(messageId: string): Promise<Message>;
@@ -513,7 +598,6 @@ declare function normalizeConversation(conv: any): Conversation;
513
598
  interface CreateGroupData {
514
599
  name: string;
515
600
  description?: string;
516
- icon?: string;
517
601
  participantIds: string[];
518
602
  }
519
603
  interface CreateDirectData {
@@ -522,13 +606,9 @@ interface CreateDirectData {
522
606
  interface UpdateConversationData {
523
607
  name?: string;
524
608
  description?: string;
525
- icon?: string;
526
609
  }
527
610
  declare const conversationsApi: {
528
- list(params?: {
529
- page?: number;
530
- limit?: number;
531
- }): Promise<PaginatedResponse<Conversation>>;
611
+ list(params?: ConversationListParams): Promise<PaginatedResponse<Conversation>>;
532
612
  get(conversationId: string): Promise<Conversation>;
533
613
  createGroup(payload: CreateGroupData): Promise<Conversation>;
534
614
  createDirect(payload: CreateDirectData): Promise<Conversation>;
@@ -543,7 +623,29 @@ declare const conversationsApi: {
543
623
  unpin(conversationId: string): Promise<void>;
544
624
  leave(conversationId: string): Promise<void>;
545
625
  getMembers(conversationId: string): Promise<User[]>;
546
- searchUsers(query: string): Promise<User[]>;
626
+ /**
627
+ * Get unread message count for a single conversation.
628
+ * Use this after app foreground or socket reconnect to refresh a specific count.
629
+ */
630
+ getUnreadCount(conversationId: string): Promise<ConversationUnreadCount>;
631
+ /**
632
+ * Get total unread count across all conversations + per-conversation breakdown.
633
+ * Use on app cold start, foreground resume, or after socket reconnect.
634
+ * The socket keeps counts live while connected — this is the source of truth
635
+ * when the socket was down.
636
+ */
637
+ getUnreadSummary(): Promise<UnreadSummary>;
638
+ /**
639
+ * Upload or replace the group icon (admin only).
640
+ * Accepts a File (web) or { uri, name, type } object (React Native).
641
+ * The server stores the image in its own storage, deletes the previous icon,
642
+ * and returns a fresh signed iconUrl on every subsequent response.
643
+ */
644
+ uploadIcon(conversationId: string, file: File | {
645
+ uri: string;
646
+ name: string;
647
+ type: string;
648
+ }): Promise<Conversation>;
547
649
  };
548
650
 
549
651
  declare const storageApi: {
@@ -631,6 +733,30 @@ declare const devicesApi: {
631
733
  remove(deviceId: string): Promise<void>;
632
734
  };
633
735
 
736
+ declare const usersApi: {
737
+ list(params?: {
738
+ query?: string;
739
+ page?: number;
740
+ limit?: number;
741
+ }): Promise<PaginatedResponse<User>>;
742
+ getById(userId: string): Promise<User>;
743
+ getLastSeen(userId: string): Promise<{
744
+ lastSeenAt: string | null;
745
+ }>;
746
+ /**
747
+ * Update notification preferences for the current user.
748
+ * Partial update — only send fields you want to change.
749
+ * A prefs record is automatically created with defaults when a device
750
+ * token is first registered, so this never fails with "not found".
751
+ */
752
+ updatePreferences(prefs: UserPreferences): Promise<User>;
753
+ /**
754
+ * Fetch current notification preferences for the current user.
755
+ * Returns null if no prefs record exists yet (all defaults apply).
756
+ */
757
+ getPreferences(): Promise<UserPreferences | null>;
758
+ };
759
+
634
760
  type TokenStore = {
635
761
  getAccessToken: () => string | null | undefined;
636
762
  getRefreshToken: () => string | null | undefined;
@@ -649,7 +775,7 @@ declare function getSocket(): Socket;
649
775
  declare function tryGetSocket(): Socket | null;
650
776
  declare function getSocketStatus(): SocketStatus;
651
777
  declare function onSocketStatus(listener: StatusListener): () => void;
652
- declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined, userId?: string, tenantId?: string): Promise<Socket>;
778
+ declare function connectSocket(config: ResolvedConfig, getToken: () => string | null | undefined): Promise<Socket>;
653
779
  declare function disconnectSocket(): void;
654
780
  declare function reconnectSocket(token: string, userId?: string, tenantId?: string): void;
655
781
  /**
@@ -769,6 +895,10 @@ interface TypingUser {
769
895
  displayName: string;
770
896
  avatarUrl?: string;
771
897
  }
898
+ interface LastReadEntry {
899
+ messageId: string;
900
+ readAt: string;
901
+ }
772
902
  interface ChatState {
773
903
  activeConversationId: string | null;
774
904
  pendingTarget: {
@@ -777,6 +907,10 @@ interface ChatState {
777
907
  } | null;
778
908
  typingUsers: Record<string, TypingUser[]>;
779
909
  onlineUsers: string[];
910
+ /** keyed by conversationId — current user's last read pointer per conversation */
911
+ lastRead: Record<string, LastReadEntry>;
912
+ /** keyed by userId — last seen timestamp for each user */
913
+ lastSeen: Record<string, string>;
780
914
  replyingTo: Message | null;
781
915
  editingMessage: Message | null;
782
916
  isSidebarOpen: boolean;
@@ -792,6 +926,8 @@ interface ChatState {
792
926
  setUserOnline: (userId: string) => void;
793
927
  setUserOffline: (userId: string) => void;
794
928
  setOnlineUsers: (userIds: string[]) => void;
929
+ setLastRead: (conversationId: string, messageId: string, readAt: string) => void;
930
+ setLastSeen: (userId: string, lastSeenAt: string) => void;
795
931
  setReplyingTo: (message: Message | null) => void;
796
932
  setEditingMessage: (message: Message | null) => void;
797
933
  toggleSidebar: () => void;
@@ -851,16 +987,17 @@ declare class AntzChatClient {
851
987
  conversationId?: string;
852
988
  }): Promise<PaginatedResponse<Message>>;
853
989
  search(params: SearchParams): Promise<PaginatedResponse<Message>>;
990
+ getLastRead(conversationId: string): Promise<{
991
+ lastReadMessageId: string | null;
992
+ lastReadAt: string | null;
993
+ }>;
854
994
  markAsRead(conversationId: string, messageId?: string): Promise<void>;
855
995
  pin(messageId: string): Promise<Message>;
856
996
  unpin(messageId: string): Promise<Message>;
857
997
  getPinned(conversationId: string): Promise<Message[]>;
858
998
  };
859
999
  readonly conversations: {
860
- list(params?: {
861
- page?: number;
862
- limit?: number;
863
- }): Promise<PaginatedResponse<Conversation>>;
1000
+ list(params?: ConversationListParams): Promise<PaginatedResponse<Conversation>>;
864
1001
  get(conversationId: string): Promise<Conversation>;
865
1002
  createGroup(payload: CreateGroupData): Promise<Conversation>;
866
1003
  createDirect(payload: CreateDirectData): Promise<Conversation>;
@@ -875,7 +1012,13 @@ declare class AntzChatClient {
875
1012
  unpin(conversationId: string): Promise<void>;
876
1013
  leave(conversationId: string): Promise<void>;
877
1014
  getMembers(conversationId: string): Promise<User[]>;
878
- searchUsers(query: string): Promise<User[]>;
1015
+ getUnreadCount(conversationId: string): Promise<ConversationUnreadCount>;
1016
+ getUnreadSummary(): Promise<UnreadSummary>;
1017
+ uploadIcon(conversationId: string, file: File | {
1018
+ uri: string;
1019
+ name: string;
1020
+ type: string;
1021
+ }): Promise<Conversation>;
879
1022
  };
880
1023
  readonly storage: {
881
1024
  requestPresignedUrl(payload: PresignedUrlRequest): Promise<PresignedUrlResponse>;
@@ -903,6 +1046,19 @@ declare class AntzChatClient {
903
1046
  limit?: number;
904
1047
  }): Promise<PaginatedResponse<FileResponse>>;
905
1048
  };
1049
+ readonly users: {
1050
+ list(params?: {
1051
+ query?: string;
1052
+ page?: number;
1053
+ limit?: number;
1054
+ }): Promise<PaginatedResponse<User>>;
1055
+ getById(userId: string): Promise<User>;
1056
+ getLastSeen(userId: string): Promise<{
1057
+ lastSeenAt: string | null;
1058
+ }>;
1059
+ updatePreferences(prefs: UserPreferences): Promise<User>;
1060
+ getPreferences(): Promise<UserPreferences | null>;
1061
+ };
906
1062
  readonly socket: ClientSocketHandle;
907
1063
  constructor(rawConfig: AntzChatConfig);
908
1064
  connect(): Promise<void>;
@@ -910,4 +1066,4 @@ declare class AntzChatClient {
910
1066
  uploadFiles(files: UploadableFile[], conversationId?: string): Promise<BatchUploadResult>;
911
1067
  }
912
1068
 
913
- export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type Conversation, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore };
1069
+ export { AntzChatClient, type AntzChatConfig, type Attachment, type AuthResponse, type AuthTokens, type BatchUploadResult, type Conversation, type ConversationListParams, type ConversationUnreadCount, type CreateDirectData, type CreateGroupData, type CursorPaginatedResponse, type EncryptedContent, type EncryptionKeyInfo, type EncryptionMode, type FileResponse, type FileSizeLimits, type FileType, type LastReadEntry, type ListMessagesParams, type LoginCredentials, type Message, type MessageAckEvent, type MessageContent, type MessageDeletedEvent, type MessageDeletedForMeEvent, type MessageDeliveredEvent, type MessageReaction, type MessageReplyReference, type MessageUpdatedEvent, type MessagesDeliveredEvent, type MobileDeviceToken, type NewMessageEvent, type OptimisticAttachment, type PaginatedResponse, type Participant, type PersistStorage, type PlatformUploadFn, type PresignedUrlRequest, type PresignedUrlResponse, type QuietHours, type ReactionUpdatedEvent, type ReadReceiptEvent, type RegisterData, type RegisterDeviceTokenPayload, type ResolvedConfig, type ResolvedFileSizeLimits, type SearchParams, type SendData, type SendMessageAttachment, type SendMessagePayload, type SocketStatus, type StatusListener, type TokenStore, type TypingIndicatorEvent, type UnreadSummary, type UpdateConversationData, type UploadConfig, type UploadProgress, type UploadableFile, type User, type UserPreferences, type UserStatusEvent, type WebPushDeviceToken, authApi, connectSocket, conversationsApi, createAuthStore, devicesApi, disconnectSocket, getApiClient, getAuthStore, getSocket, getSocketStatus, initApiClient, initAuthStore, messagesApi, normalizeConversation, onSocketStatus, reconnectSocket, refreshSocketAuth, resetAuthStore, resolveConfig, setApiClientInstance, socketEmit, storageApi, tryGetSocket, uploadBatch, useChatStore, usersApi };