@antzsoft/chat-core 1.2.2 → 1.2.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.cjs CHANGED
@@ -35,12 +35,12 @@ var chat_store_exports = {};
35
35
  __export(chat_store_exports, {
36
36
  useChatStore: () => useChatStore
37
37
  });
38
- var import_zustand, useChatStore;
38
+ var import_zustand2, useChatStore;
39
39
  var init_chat_store = __esm({
40
40
  "src/stores/chat.store.ts"() {
41
41
  "use strict";
42
- import_zustand = require("zustand");
43
- useChatStore = (0, import_zustand.create)((set) => ({
42
+ import_zustand2 = require("zustand");
43
+ useChatStore = (0, import_zustand2.create)((set) => ({
44
44
  activeConversationId: null,
45
45
  pendingTarget: null,
46
46
  typingUsers: {},
@@ -52,6 +52,7 @@ var init_chat_store = __esm({
52
52
  isSidebarOpen: true,
53
53
  isGroupInfoOpen: false,
54
54
  isStarredPanelOpen: false,
55
+ messageInfoId: null,
55
56
  setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
56
57
  setPendingTarget: (target) => set({ pendingTarget: target }),
57
58
  addTypingUser: (conversationId, user) => set((state) => {
@@ -89,7 +90,8 @@ var init_chat_store = __esm({
89
90
  toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
90
91
  setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
91
92
  toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
92
- setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
93
+ setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open }),
94
+ setMessageInfoId: (id) => set({ messageInfoId: id })
93
95
  }));
94
96
  }
95
97
  });
@@ -119,6 +121,7 @@ __export(src_exports, {
119
121
  refreshSocketAuth: () => refreshSocketAuth,
120
122
  resetAuthStore: () => resetAuthStore,
121
123
  resolveConfig: () => resolveConfig,
124
+ resolveSystemMessageText: () => resolveSystemMessageText,
122
125
  setApiClientInstance: () => setApiClientInstance,
123
126
  setTransitSession: () => setTransitSession,
124
127
  socketEmit: () => socketEmit,
@@ -175,6 +178,7 @@ function resolveConfig(config) {
175
178
  onProgress: config.upload?.onProgress
176
179
  },
177
180
  platformUploadFn: config.platformUploadFn,
181
+ platformUploadPartFn: config.platformUploadPartFn,
178
182
  platformCompressFn: config.platformCompressFn,
179
183
  compression: {
180
184
  enabled: config.compression?.enabled ?? config.platformCompressFn != null,
@@ -574,6 +578,123 @@ var appConfigApi = {
574
578
  }
575
579
  };
576
580
 
581
+ // src/stores/auth.store.ts
582
+ var import_zustand = require("zustand");
583
+ var import_middleware = require("zustand/middleware");
584
+ function createAuthStore(storage) {
585
+ 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.");
586
+ const ref = { store: null };
587
+ const store = (0, import_zustand.create)()(
588
+ (0, import_middleware.persist)(
589
+ (set) => ({
590
+ user: null,
591
+ tokens: null,
592
+ isAuthenticated: false,
593
+ isLoading: false,
594
+ isHydrated: false,
595
+ setAuth: (user, tokens) => set({ user, tokens, isAuthenticated: true, isLoading: false }),
596
+ setTokens: (tokens) => set({ tokens }),
597
+ setUser: (user) => set({ user }),
598
+ logout: () => set({ user: null, tokens: null, isAuthenticated: false, isLoading: false }),
599
+ setLoading: (isLoading) => set({ isLoading }),
600
+ setHydrated: (isHydrated) => set({ isHydrated })
601
+ }),
602
+ {
603
+ name: "antz-chat-auth",
604
+ storage: {
605
+ getItem: (name) => {
606
+ const result = storage.getItem(name);
607
+ if (result instanceof Promise) {
608
+ return result.then((str) => str ? JSON.parse(str) : null);
609
+ }
610
+ return result ? JSON.parse(result) : null;
611
+ },
612
+ setItem: (name, value) => {
613
+ storage.setItem(name, JSON.stringify(value));
614
+ },
615
+ removeItem: (name) => storage.removeItem(name)
616
+ },
617
+ partialize: (state) => ({
618
+ user: state.user,
619
+ tokens: state.tokens,
620
+ isAuthenticated: state.isAuthenticated
621
+ }),
622
+ onRehydrateStorage: () => (state, error) => {
623
+ if (error) {
624
+ console.warn("[AntzChat] Auth store rehydration failed:", error);
625
+ }
626
+ ref.store?.setState({ isHydrated: true });
627
+ }
628
+ }
629
+ )
630
+ );
631
+ ref.store = store;
632
+ if (!store.getState().isHydrated) {
633
+ const hydrationTimeout = setTimeout(() => {
634
+ if (!store.getState().isHydrated) {
635
+ store.setState({ isHydrated: true });
636
+ }
637
+ }, 3e3);
638
+ const unsub = store.subscribe((s) => {
639
+ if (s.isHydrated) {
640
+ clearTimeout(hydrationTimeout);
641
+ unsub();
642
+ }
643
+ });
644
+ }
645
+ const tokenStore = {
646
+ getAccessToken: () => store.getState().tokens?.accessToken,
647
+ getRefreshToken: () => store.getState().tokens?.refreshToken,
648
+ setTokens: (tokens) => store.getState().setTokens(tokens),
649
+ clearTokens: () => store.getState().logout()
650
+ };
651
+ return { useAuthStore: store, authTokenStore: tokenStore };
652
+ }
653
+ var _authStore = null;
654
+ function initAuthStore(storage) {
655
+ if (!_authStore) {
656
+ _authStore = createAuthStore(storage);
657
+ }
658
+ return _authStore;
659
+ }
660
+ function getAuthStore() {
661
+ if (!_authStore) throw new Error("[AntzChat] Auth store not initialized. Call initAuthStore first.");
662
+ return _authStore;
663
+ }
664
+ function resetAuthStore() {
665
+ _authStore = null;
666
+ }
667
+
668
+ // src/utils/resolveSystemMessageText.ts
669
+ function resolveSystemMessageText(message, currentUserId) {
670
+ const m = message.metadata;
671
+ if (!m?.systemOperationType || !m.actorUserId) {
672
+ return message.content.text ?? "";
673
+ }
674
+ const iActor = m.actorUserId === currentUserId;
675
+ const iTarget = m.targetUserId === currentUserId;
676
+ const actor = iActor ? "You" : m.actorUserName ?? "Someone";
677
+ const target = iTarget ? "you" : m.targetUserName ?? "a member";
678
+ switch (m.systemOperationType) {
679
+ case "group_created":
680
+ return iActor ? "You created the group" : `${actor} created the group`;
681
+ case "user_added":
682
+ return iActor ? `You added ${target}` : `${actor} added ${target}`;
683
+ case "user_removed":
684
+ return iActor ? `You removed ${target}` : `${actor} removed ${target}`;
685
+ case "user_left":
686
+ return iActor ? "You left the group" : `${actor} left the group`;
687
+ case "admin_promoted":
688
+ return iActor ? `You made ${target} an admin` : `${actor} made ${target} an admin`;
689
+ case "admin_demoted":
690
+ return iActor ? `You removed ${target} as admin` : `${actor} removed ${target} as admin`;
691
+ case "group_updated":
692
+ return iActor ? "You updated the group" : `${actor} updated the group`;
693
+ default:
694
+ return message.content.text ?? "";
695
+ }
696
+ }
697
+
577
698
  // src/api/messages.ts
578
699
  var messagesApi = {
579
700
  async list(conversationId, params = {}) {
@@ -586,7 +707,14 @@ var messagesApi = {
586
707
  `/conversations/${conversationId}/messages`,
587
708
  { params: serverParams }
588
709
  );
589
- return data;
710
+ const currentUserId = getAuthStore().useAuthStore.getState().user?.id;
711
+ if (!currentUserId) return data;
712
+ return {
713
+ ...data,
714
+ data: data.data.map(
715
+ (m) => m.content.type === "system" ? { ...m, content: { ...m.content, text: resolveSystemMessageText(m, currentUserId) } } : m
716
+ )
717
+ };
590
718
  },
591
719
  async get(messageId) {
592
720
  const { data } = await getApiClient().get(`/messages/${messageId}`);
@@ -653,6 +781,10 @@ var messagesApi = {
653
781
  async getPinned(conversationId) {
654
782
  const { data } = await getApiClient().get(`/conversations/${conversationId}/pinned-messages`);
655
783
  return data;
784
+ },
785
+ async getReceipts(messageId) {
786
+ const { data } = await getApiClient().get(`/messages/${messageId}/receipts`);
787
+ return data;
656
788
  }
657
789
  };
658
790
 
@@ -684,7 +816,7 @@ function normalizeLastMessage(lastMsg) {
684
816
  id: lastMsg.messageId ?? "",
685
817
  tenantId: "",
686
818
  conversationId: "",
687
- senderId: "",
819
+ senderId: lastMsg.senderId ?? "",
688
820
  content: {
689
821
  type: lastMsg.hasAttachments ? "attachment" : "text",
690
822
  text: lastMsg.contentPreview
@@ -849,6 +981,13 @@ var storageApi = {
849
981
  async deleteFile(fileId) {
850
982
  await getApiClient().post(`/storage/files/${fileId}/delete`);
851
983
  },
984
+ async completeMultipartUpload(fileId, uploadId, parts) {
985
+ const { data } = await getApiClient().post(
986
+ `/storage/multipart/complete/${fileId}`,
987
+ { uploadId, parts }
988
+ );
989
+ return data;
990
+ },
852
991
  async getConversationFiles(conversationId, params = {}) {
853
992
  const { data } = await getApiClient().get(
854
993
  `/storage/conversations/${conversationId}/files`,
@@ -861,7 +1000,52 @@ var storageApi = {
861
1000
  return data;
862
1001
  }
863
1002
  };
864
- async function runUploadBatch(files, platformUploadFn, slotIds, conversationId, onProgress, platformCompressFn, compressionConfig) {
1003
+ async function runMultipartUpload(presigned, file, platformUploadPartFn, onProgress) {
1004
+ const { multipart } = presigned;
1005
+ if (!multipart) throw new Error("No multipart info on presigned response");
1006
+ const CONCURRENCY = 3;
1007
+ const completedParts = [];
1008
+ const partProgress = {};
1009
+ multipart.partUrls.forEach(({ partNumber }) => {
1010
+ partProgress[partNumber] = 0;
1011
+ });
1012
+ const reportProgress = () => {
1013
+ if (!onProgress) return;
1014
+ const vals = Object.values(partProgress);
1015
+ const avg = vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);
1016
+ onProgress(Math.round(avg * 0.95));
1017
+ };
1018
+ const uploadPart = async (partNumber, uploadUrl, method) => {
1019
+ const offset = (partNumber - 1) * multipart.chunkSize;
1020
+ const end = Math.min(offset + multipart.chunkSize, file.size);
1021
+ const blob = await fetch(file.uri).then((r) => r.blob());
1022
+ const slice = blob.slice(offset, end);
1023
+ const etag = await platformUploadPartFn(uploadUrl, slice, (pct) => {
1024
+ partProgress[partNumber] = pct;
1025
+ reportProgress();
1026
+ }, method);
1027
+ completedParts.push({ partNumber, etag });
1028
+ partProgress[partNumber] = 100;
1029
+ reportProgress();
1030
+ };
1031
+ for (let i = 0; i < multipart.partUrls.length; i += CONCURRENCY) {
1032
+ const batch = multipart.partUrls.slice(i, i + CONCURRENCY);
1033
+ const results = await Promise.allSettled(
1034
+ batch.map(({ partNumber, uploadUrl, method }) => uploadPart(partNumber, uploadUrl, method ?? "PUT"))
1035
+ );
1036
+ const failed = results.find((r) => r.status === "rejected");
1037
+ if (failed) throw failed.reason;
1038
+ }
1039
+ completedParts.sort((a, b) => a.partNumber - b.partNumber);
1040
+ const fileResponse = await storageApi.completeMultipartUpload(
1041
+ presigned.fileId,
1042
+ multipart.uploadId,
1043
+ completedParts
1044
+ );
1045
+ onProgress?.(100);
1046
+ return fileResponse;
1047
+ }
1048
+ async function runUploadBatch(files, platformUploadFn, slotIds, conversationId, onProgress, platformCompressFn, compressionConfig, platformUploadPartFn) {
865
1049
  const compressedFiles = await Promise.all(
866
1050
  files.map((f) => compressFile(f, platformCompressFn, compressionConfig ?? { enabled: false, imageQuality: 0.85, imageMaxDimension: 1920, compressDocuments: true }))
867
1051
  );
@@ -903,11 +1087,19 @@ async function runUploadBatch(files, platformUploadFn, slotIds, conversationId,
903
1087
  const { file, slotId } = slotted[originalIdx];
904
1088
  progressMap[originalIdx] = 0;
905
1089
  try {
906
- await platformUploadFn(presigned, file, (pct) => {
907
- progressMap[originalIdx] = Math.round(pct * 0.9);
908
- reportProgress();
909
- });
910
- const fileResponse = await storageApi.confirmUpload(presigned.fileId);
1090
+ let fileResponse;
1091
+ if (presigned.multipart && platformUploadPartFn) {
1092
+ fileResponse = await runMultipartUpload(presigned, file, platformUploadPartFn, (pct) => {
1093
+ progressMap[originalIdx] = pct;
1094
+ reportProgress();
1095
+ });
1096
+ } else {
1097
+ await platformUploadFn(presigned, file, (pct) => {
1098
+ progressMap[originalIdx] = Math.round(pct * 0.9);
1099
+ reportProgress();
1100
+ });
1101
+ fileResponse = await storageApi.confirmUpload(presigned.fileId);
1102
+ }
911
1103
  progressMap[originalIdx] = 100;
912
1104
  reportProgress();
913
1105
  successful.push(fileResponse);
@@ -919,9 +1111,9 @@ async function runUploadBatch(files, platformUploadFn, slotIds, conversationId,
919
1111
  );
920
1112
  return { result: { successful, failed }, slotToFile };
921
1113
  }
922
- async function uploadBatch(files, platformUploadFn, conversationId, onProgress, platformCompressFn, compressionConfig) {
1114
+ async function uploadBatch(files, platformUploadFn, conversationId, onProgress, platformCompressFn, compressionConfig, platformUploadPartFn) {
923
1115
  const slotIds = files.map(() => generateUUID());
924
- const { result } = await runUploadBatch(files, platformUploadFn, slotIds, conversationId, onProgress, platformCompressFn, compressionConfig);
1116
+ const { result } = await runUploadBatch(files, platformUploadFn, slotIds, conversationId, onProgress, platformCompressFn, compressionConfig, platformUploadPartFn);
925
1117
  return result;
926
1118
  }
927
1119
 
@@ -1500,93 +1692,6 @@ var socketEmit = {
1500
1692
  }
1501
1693
  };
1502
1694
 
1503
- // src/stores/auth.store.ts
1504
- var import_zustand2 = require("zustand");
1505
- var import_middleware = require("zustand/middleware");
1506
- function createAuthStore(storage) {
1507
- 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.");
1508
- const ref = { store: null };
1509
- const store = (0, import_zustand2.create)()(
1510
- (0, import_middleware.persist)(
1511
- (set) => ({
1512
- user: null,
1513
- tokens: null,
1514
- isAuthenticated: false,
1515
- isLoading: false,
1516
- isHydrated: false,
1517
- setAuth: (user, tokens) => set({ user, tokens, isAuthenticated: true, isLoading: false }),
1518
- setTokens: (tokens) => set({ tokens }),
1519
- setUser: (user) => set({ user }),
1520
- logout: () => set({ user: null, tokens: null, isAuthenticated: false, isLoading: false }),
1521
- setLoading: (isLoading) => set({ isLoading }),
1522
- setHydrated: (isHydrated) => set({ isHydrated })
1523
- }),
1524
- {
1525
- name: "antz-chat-auth",
1526
- storage: {
1527
- getItem: (name) => {
1528
- const result = storage.getItem(name);
1529
- if (result instanceof Promise) {
1530
- return result.then((str) => str ? JSON.parse(str) : null);
1531
- }
1532
- return result ? JSON.parse(result) : null;
1533
- },
1534
- setItem: (name, value) => {
1535
- storage.setItem(name, JSON.stringify(value));
1536
- },
1537
- removeItem: (name) => storage.removeItem(name)
1538
- },
1539
- partialize: (state) => ({
1540
- user: state.user,
1541
- tokens: state.tokens,
1542
- isAuthenticated: state.isAuthenticated
1543
- }),
1544
- onRehydrateStorage: () => (state, error) => {
1545
- if (error) {
1546
- console.warn("[AntzChat] Auth store rehydration failed:", error);
1547
- }
1548
- ref.store?.setState({ isHydrated: true });
1549
- }
1550
- }
1551
- )
1552
- );
1553
- ref.store = store;
1554
- if (!store.getState().isHydrated) {
1555
- const hydrationTimeout = setTimeout(() => {
1556
- if (!store.getState().isHydrated) {
1557
- store.setState({ isHydrated: true });
1558
- }
1559
- }, 3e3);
1560
- const unsub = store.subscribe((s) => {
1561
- if (s.isHydrated) {
1562
- clearTimeout(hydrationTimeout);
1563
- unsub();
1564
- }
1565
- });
1566
- }
1567
- const tokenStore = {
1568
- getAccessToken: () => store.getState().tokens?.accessToken,
1569
- getRefreshToken: () => store.getState().tokens?.refreshToken,
1570
- setTokens: (tokens) => store.getState().setTokens(tokens),
1571
- clearTokens: () => store.getState().logout()
1572
- };
1573
- return { useAuthStore: store, authTokenStore: tokenStore };
1574
- }
1575
- var _authStore = null;
1576
- function initAuthStore(storage) {
1577
- if (!_authStore) {
1578
- _authStore = createAuthStore(storage);
1579
- }
1580
- return _authStore;
1581
- }
1582
- function getAuthStore() {
1583
- if (!_authStore) throw new Error("[AntzChat] Auth store not initialized. Call initAuthStore first.");
1584
- return _authStore;
1585
- }
1586
- function resetAuthStore() {
1587
- _authStore = null;
1588
- }
1589
-
1590
1695
  // src/index.ts
1591
1696
  init_chat_store();
1592
1697
 
@@ -1663,6 +1768,7 @@ var AntzChatClient = class {
1663
1768
  refreshSocketAuth,
1664
1769
  resetAuthStore,
1665
1770
  resolveConfig,
1771
+ resolveSystemMessageText,
1666
1772
  setApiClientInstance,
1667
1773
  setTransitSession,
1668
1774
  socketEmit,