@antzsoft/chat-core 1.0.0 → 1.0.3
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 +518 -31
- package/dist/chat.store-PY3YVYGN.js +7 -0
- package/dist/chat.store-PY3YVYGN.js.map +1 -0
- package/dist/chunk-TB52RCSF.js +54 -0
- package/dist/chunk-TB52RCSF.js.map +1 -0
- package/dist/index.cjs +252 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -13
- package/dist/index.d.ts +217 -13
- package/dist/index.js +186 -55
- package/dist/index.js.map +1 -1
- package/docs/integration-guide.html +2076 -0
- package/package.json +3 -2
|
@@ -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, {
|
|
@@ -47,6 +110,7 @@ __export(src_exports, {
|
|
|
47
110
|
normalizeConversation: () => normalizeConversation,
|
|
48
111
|
onSocketStatus: () => onSocketStatus,
|
|
49
112
|
reconnectSocket: () => reconnectSocket,
|
|
113
|
+
refreshSocketAuth: () => refreshSocketAuth,
|
|
50
114
|
resetAuthStore: () => resetAuthStore,
|
|
51
115
|
resolveConfig: () => resolveConfig,
|
|
52
116
|
setApiClientInstance: () => setApiClientInstance,
|
|
@@ -54,13 +118,23 @@ __export(src_exports, {
|
|
|
54
118
|
storageApi: () => storageApi,
|
|
55
119
|
tryGetSocket: () => tryGetSocket,
|
|
56
120
|
uploadBatch: () => uploadBatch,
|
|
57
|
-
useChatStore: () => useChatStore
|
|
121
|
+
useChatStore: () => useChatStore,
|
|
122
|
+
usersApi: () => usersApi
|
|
58
123
|
});
|
|
59
124
|
module.exports = __toCommonJS(src_exports);
|
|
60
125
|
|
|
61
126
|
// src/config/types.ts
|
|
62
127
|
function resolveConfig(config) {
|
|
63
128
|
const socketUrl = config.socketUrl ?? config.apiUrl.replace(/\/api\/v\d+\/?$/, "").replace(/\/$/, "");
|
|
129
|
+
let socketOrigin = socketUrl;
|
|
130
|
+
let socketPath = "/socket.io";
|
|
131
|
+
try {
|
|
132
|
+
const parsed = new URL(socketUrl);
|
|
133
|
+
socketOrigin = parsed.origin;
|
|
134
|
+
const pathname = parsed.pathname.replace(/\/$/, "");
|
|
135
|
+
if (pathname && pathname !== "/") socketPath = `${pathname}/socket.io`;
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
64
138
|
const raw = config.upload?.maxFileSizeMB;
|
|
65
139
|
let limits;
|
|
66
140
|
if (typeof raw === "number") {
|
|
@@ -78,10 +152,13 @@ function resolveConfig(config) {
|
|
|
78
152
|
return {
|
|
79
153
|
apiUrl: config.apiUrl.replace(/\/$/, ""),
|
|
80
154
|
socketUrl,
|
|
155
|
+
socketOrigin,
|
|
156
|
+
socketPath,
|
|
81
157
|
authToken: config.authToken,
|
|
82
158
|
authProvider: config.authProvider,
|
|
83
159
|
userId: config.userId,
|
|
84
160
|
tenantId: config.tenantId,
|
|
161
|
+
avatar: config.avatar,
|
|
85
162
|
encryptionMode: config.encryptionMode ?? "none",
|
|
86
163
|
upload: {
|
|
87
164
|
maxFileSizeMB: limits,
|
|
@@ -91,7 +168,10 @@ function resolveConfig(config) {
|
|
|
91
168
|
onProgress: config.upload?.onProgress
|
|
92
169
|
},
|
|
93
170
|
platformUploadFn: config.platformUploadFn,
|
|
94
|
-
persistStorage: config.persistStorage
|
|
171
|
+
persistStorage: config.persistStorage,
|
|
172
|
+
messagePageSize: config.messagePageSize ?? 40,
|
|
173
|
+
starredMessagePageSize: config.starredMessagePageSize ?? 30,
|
|
174
|
+
searchPageSize: config.searchPageSize ?? 50
|
|
95
175
|
};
|
|
96
176
|
}
|
|
97
177
|
|
|
@@ -99,9 +179,11 @@ function resolveConfig(config) {
|
|
|
99
179
|
var import_axios = __toESM(require("axios"), 1);
|
|
100
180
|
var _tokenStore = null;
|
|
101
181
|
var _config = null;
|
|
182
|
+
var _avatarSent = false;
|
|
102
183
|
function initApiClient(config, tokenStore) {
|
|
103
184
|
_config = config;
|
|
104
185
|
_tokenStore = tokenStore;
|
|
186
|
+
_avatarSent = false;
|
|
105
187
|
const client = import_axios.default.create({
|
|
106
188
|
baseURL: config.apiUrl,
|
|
107
189
|
headers: { "Content-Type": "application/json" }
|
|
@@ -111,6 +193,11 @@ function initApiClient(config, tokenStore) {
|
|
|
111
193
|
if (token) req.headers["Authorization"] = `Bearer ${token}`;
|
|
112
194
|
if (_config?.userId) req.headers["x-user-id"] = _config.userId;
|
|
113
195
|
if (_config?.tenantId) req.headers["X-Tenant-ID"] = _config.tenantId;
|
|
196
|
+
if (token && !_avatarSent && _config?.avatar) {
|
|
197
|
+
if (_config.avatar.base64) req.headers["x-avatar-base64"] = _config.avatar.base64;
|
|
198
|
+
else if (_config.avatar.url) req.headers["x-avatar-url"] = _config.avatar.url;
|
|
199
|
+
_avatarSent = true;
|
|
200
|
+
}
|
|
114
201
|
return req;
|
|
115
202
|
});
|
|
116
203
|
let isRefreshing = false;
|
|
@@ -195,15 +282,37 @@ var authApi = {
|
|
|
195
282
|
async getMe() {
|
|
196
283
|
const { data } = await getApiClient().get("/users/me");
|
|
197
284
|
return data;
|
|
285
|
+
},
|
|
286
|
+
// Upload a new avatar file (builtin mode — multipart)
|
|
287
|
+
async uploadAvatar(file, mimeType) {
|
|
288
|
+
const form = new FormData();
|
|
289
|
+
form.append("avatar", file instanceof File ? file : new File([file], "avatar.jpg", { type: mimeType ?? "image/jpeg" }));
|
|
290
|
+
const { data } = await getApiClient().put("/users/me/avatar", form, {
|
|
291
|
+
headers: { "Content-Type": "multipart/form-data" }
|
|
292
|
+
});
|
|
293
|
+
return data;
|
|
294
|
+
},
|
|
295
|
+
// Sync avatar from URL or base64 (non-builtin modes — or post-init update)
|
|
296
|
+
async syncAvatar(source) {
|
|
297
|
+
const headers = {};
|
|
298
|
+
if (source.base64) headers["x-avatar-base64"] = source.base64;
|
|
299
|
+
else if (source.url) headers["x-avatar-url"] = source.url;
|
|
300
|
+
const { data } = await getApiClient().post("/users/me/avatar/sync", {}, { headers });
|
|
301
|
+
return data;
|
|
198
302
|
}
|
|
199
303
|
};
|
|
200
304
|
|
|
201
305
|
// src/api/messages.ts
|
|
202
306
|
var messagesApi = {
|
|
203
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
|
+
}
|
|
204
313
|
const { data } = await getApiClient().get(
|
|
205
314
|
`/conversations/${conversationId}/messages`,
|
|
206
|
-
{ params }
|
|
315
|
+
{ params: serverParams }
|
|
207
316
|
);
|
|
208
317
|
return data;
|
|
209
318
|
},
|
|
@@ -225,6 +334,9 @@ var messagesApi = {
|
|
|
225
334
|
async delete(messageId) {
|
|
226
335
|
await getApiClient().delete(`/messages/${messageId}`);
|
|
227
336
|
},
|
|
337
|
+
async deleteForMe(messageId) {
|
|
338
|
+
await getApiClient().delete(`/messages/${messageId}/for-me`);
|
|
339
|
+
},
|
|
228
340
|
async addReaction(messageId, emoji) {
|
|
229
341
|
const { data } = await getApiClient().post(`/messages/${messageId}/reactions`, { emoji });
|
|
230
342
|
return data;
|
|
@@ -249,6 +361,12 @@ var messagesApi = {
|
|
|
249
361
|
const { data } = await getApiClient().get("/messages/search", { params });
|
|
250
362
|
return data;
|
|
251
363
|
},
|
|
364
|
+
async getLastRead(conversationId) {
|
|
365
|
+
const { data } = await getApiClient().get(
|
|
366
|
+
`/conversations/${conversationId}/read-receipt`
|
|
367
|
+
);
|
|
368
|
+
return data;
|
|
369
|
+
},
|
|
252
370
|
async markAsRead(conversationId, messageId) {
|
|
253
371
|
await getApiClient().post(`/conversations/${conversationId}/read`, messageId ? { messageId } : {});
|
|
254
372
|
},
|
|
@@ -273,6 +391,7 @@ function normalizeParticipant(p) {
|
|
|
273
391
|
userId: p.userId,
|
|
274
392
|
role: p.role,
|
|
275
393
|
joinedAt: p.joinedAt,
|
|
394
|
+
isActive: p.isActive,
|
|
276
395
|
user: hasUserDetails ? {
|
|
277
396
|
id: p.userId,
|
|
278
397
|
tenantId: "",
|
|
@@ -299,7 +418,7 @@ function normalizeLastMessage(lastMsg) {
|
|
|
299
418
|
text: lastMsg.contentPreview
|
|
300
419
|
},
|
|
301
420
|
reactions: [],
|
|
302
|
-
status: "sent",
|
|
421
|
+
status: lastMsg.status ?? "sent",
|
|
303
422
|
isEdited: false,
|
|
304
423
|
sentAt: lastMsg.sentAt ?? "",
|
|
305
424
|
createdAt: lastMsg.sentAt ?? ""
|
|
@@ -376,9 +495,25 @@ var conversationsApi = {
|
|
|
376
495
|
const { data } = await getApiClient().get(`/conversations/${conversationId}/participants`);
|
|
377
496
|
return data;
|
|
378
497
|
},
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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;
|
|
382
517
|
}
|
|
383
518
|
};
|
|
384
519
|
|
|
@@ -483,11 +618,52 @@ var devicesApi = {
|
|
|
483
618
|
}
|
|
484
619
|
};
|
|
485
620
|
|
|
621
|
+
// src/api/users.ts
|
|
622
|
+
var usersApi = {
|
|
623
|
+
async list(params = {}) {
|
|
624
|
+
const { data } = await getApiClient().get("/users", { params });
|
|
625
|
+
return data;
|
|
626
|
+
},
|
|
627
|
+
async getById(userId) {
|
|
628
|
+
const { data } = await getApiClient().get(`/users/${userId}`);
|
|
629
|
+
return data;
|
|
630
|
+
},
|
|
631
|
+
async getLastSeen(userId) {
|
|
632
|
+
const { data } = await getApiClient().get(`/users/${userId}`);
|
|
633
|
+
return { lastSeenAt: data.lastSeenAt ?? null };
|
|
634
|
+
},
|
|
635
|
+
/**
|
|
636
|
+
* Update notification preferences for the current user.
|
|
637
|
+
* Partial update — only send fields you want to change.
|
|
638
|
+
* A prefs record is automatically created with defaults when a device
|
|
639
|
+
* token is first registered, so this never fails with "not found".
|
|
640
|
+
*/
|
|
641
|
+
async updatePreferences(prefs) {
|
|
642
|
+
const { data } = await getApiClient().put("/users/me/preferences", prefs);
|
|
643
|
+
return data;
|
|
644
|
+
},
|
|
645
|
+
/**
|
|
646
|
+
* Fetch current notification preferences for the current user.
|
|
647
|
+
* Returns null if no prefs record exists yet (all defaults apply).
|
|
648
|
+
*/
|
|
649
|
+
async getPreferences() {
|
|
650
|
+
try {
|
|
651
|
+
const { data } = await getApiClient().get("/users/me/preferences");
|
|
652
|
+
return data;
|
|
653
|
+
} catch {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
|
|
486
659
|
// src/socket/socket.ts
|
|
487
660
|
var import_socket = require("socket.io-client");
|
|
488
661
|
var _socket = null;
|
|
489
662
|
var _status = "disconnected";
|
|
490
663
|
var _statusListeners = /* @__PURE__ */ new Set();
|
|
664
|
+
var _getToken = null;
|
|
665
|
+
var _userId;
|
|
666
|
+
var _tenantId;
|
|
491
667
|
function setStatus(s) {
|
|
492
668
|
_status = s;
|
|
493
669
|
_statusListeners.forEach((l) => l(s));
|
|
@@ -507,10 +683,23 @@ function onSocketStatus(listener) {
|
|
|
507
683
|
return () => _statusListeners.delete(listener);
|
|
508
684
|
}
|
|
509
685
|
async function connectSocket(config, getToken) {
|
|
510
|
-
if (_socket
|
|
686
|
+
if (_socket && !_socket.disconnected) return _socket;
|
|
687
|
+
_getToken = getToken;
|
|
688
|
+
_userId = config.userId;
|
|
689
|
+
_tenantId = config.tenantId;
|
|
511
690
|
const token = getToken();
|
|
512
|
-
_socket = (0, import_socket.io)(`${config.
|
|
513
|
-
auth: {
|
|
691
|
+
_socket = (0, import_socket.io)(`${config.socketOrigin}/chat`, {
|
|
692
|
+
auth: {
|
|
693
|
+
token: token ? `Bearer ${token}` : "",
|
|
694
|
+
...config.userId && { userId: config.userId },
|
|
695
|
+
...config.tenantId && { tenantId: config.tenantId },
|
|
696
|
+
...config.avatar?.url && { avatarUrl: config.avatar.url },
|
|
697
|
+
...config.avatar?.base64 && { avatarBase64: config.avatar.base64 }
|
|
698
|
+
},
|
|
699
|
+
// path must match SOCKET_IO_PATH on the server (default '/socket.io').
|
|
700
|
+
// Set socketPath in SDK config when the server is behind a reverse proxy
|
|
701
|
+
// that adds a path prefix (e.g. '/chat-api/socket.io' for UAT).
|
|
702
|
+
path: config.socketPath,
|
|
514
703
|
transports: ["websocket", "polling"],
|
|
515
704
|
reconnection: true,
|
|
516
705
|
reconnectionAttempts: 10,
|
|
@@ -521,9 +710,30 @@ async function connectSocket(config, getToken) {
|
|
|
521
710
|
setStatus("connecting");
|
|
522
711
|
_socket.on("connect", () => setStatus("connected"));
|
|
523
712
|
_socket.on("disconnect", () => setStatus("disconnected"));
|
|
524
|
-
_socket.on("connect_error", () =>
|
|
713
|
+
_socket.on("connect_error", (err) => {
|
|
714
|
+
console.error("[AntzChat] Socket connect_error:", err?.message, err?.data);
|
|
715
|
+
setStatus("error");
|
|
716
|
+
});
|
|
525
717
|
_socket.on("reconnecting", () => setStatus("reconnecting"));
|
|
526
718
|
_socket.on("reconnect", () => setStatus("connected"));
|
|
719
|
+
_socket.on("read_receipt", (event) => {
|
|
720
|
+
Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
|
|
721
|
+
useChatStore2.getState().setLastRead(event.conversationId, event.messageId, event.readAt);
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
_socket.on("user_online", (event) => {
|
|
725
|
+
Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
|
|
726
|
+
const store = useChatStore2.getState();
|
|
727
|
+
store.setUserOnline(event.userId);
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
_socket.on("user_offline", (event) => {
|
|
731
|
+
Promise.resolve().then(() => (init_chat_store(), chat_store_exports)).then(({ useChatStore: useChatStore2 }) => {
|
|
732
|
+
const store = useChatStore2.getState();
|
|
733
|
+
store.setUserOffline(event.userId);
|
|
734
|
+
if (event.lastSeenAt) store.setLastSeen(event.userId, event.lastSeenAt);
|
|
735
|
+
});
|
|
736
|
+
});
|
|
527
737
|
return _socket;
|
|
528
738
|
}
|
|
529
739
|
function disconnectSocket() {
|
|
@@ -532,13 +742,31 @@ function disconnectSocket() {
|
|
|
532
742
|
_socket = null;
|
|
533
743
|
setStatus("disconnected");
|
|
534
744
|
}
|
|
745
|
+
_getToken = null;
|
|
746
|
+
_userId = void 0;
|
|
747
|
+
_tenantId = void 0;
|
|
535
748
|
}
|
|
536
|
-
function reconnectSocket(token) {
|
|
749
|
+
function reconnectSocket(token, userId, tenantId) {
|
|
537
750
|
if (_socket) {
|
|
538
|
-
_socket.auth = {
|
|
751
|
+
_socket.auth = {
|
|
752
|
+
token: `Bearer ${token}`,
|
|
753
|
+
...userId && { userId },
|
|
754
|
+
...tenantId && { tenantId }
|
|
755
|
+
};
|
|
539
756
|
_socket.connect();
|
|
540
757
|
}
|
|
541
758
|
}
|
|
759
|
+
function refreshSocketAuth() {
|
|
760
|
+
if (!_socket || !_getToken) return false;
|
|
761
|
+
const fresh = _getToken();
|
|
762
|
+
if (!fresh) return false;
|
|
763
|
+
_socket.auth = {
|
|
764
|
+
token: `Bearer ${fresh}`,
|
|
765
|
+
..._userId && { userId: _userId },
|
|
766
|
+
..._tenantId && { tenantId: _tenantId }
|
|
767
|
+
};
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
542
770
|
|
|
543
771
|
// src/socket/emitters.ts
|
|
544
772
|
var ACK_TIMEOUT = 5e3;
|
|
@@ -574,6 +802,9 @@ var socketEmit = {
|
|
|
574
802
|
deleteMessage(messageId) {
|
|
575
803
|
return withAck("delete_message", { messageId });
|
|
576
804
|
},
|
|
805
|
+
deleteMessageForMe(messageId) {
|
|
806
|
+
return withAck("delete_message_for_me", { messageId });
|
|
807
|
+
},
|
|
577
808
|
addReaction(messageId, emoji) {
|
|
578
809
|
return withAck("add_reaction", { messageId, emoji });
|
|
579
810
|
},
|
|
@@ -617,12 +848,12 @@ var socketEmit = {
|
|
|
617
848
|
};
|
|
618
849
|
|
|
619
850
|
// src/stores/auth.store.ts
|
|
620
|
-
var
|
|
851
|
+
var import_zustand2 = require("zustand");
|
|
621
852
|
var import_middleware = require("zustand/middleware");
|
|
622
853
|
function createAuthStore(storage) {
|
|
623
854
|
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.");
|
|
624
855
|
const ref = { store: null };
|
|
625
|
-
const store = (0,
|
|
856
|
+
const store = (0, import_zustand2.create)()(
|
|
626
857
|
(0, import_middleware.persist)(
|
|
627
858
|
(set) => ({
|
|
628
859
|
user: null,
|
|
@@ -703,47 +934,8 @@ function resetAuthStore() {
|
|
|
703
934
|
_authStore = null;
|
|
704
935
|
}
|
|
705
936
|
|
|
706
|
-
// src/
|
|
707
|
-
|
|
708
|
-
var useChatStore = (0, import_zustand2.create)((set) => ({
|
|
709
|
-
activeConversationId: null,
|
|
710
|
-
pendingTarget: null,
|
|
711
|
-
typingUsers: {},
|
|
712
|
-
onlineUsers: [],
|
|
713
|
-
replyingTo: null,
|
|
714
|
-
editingMessage: null,
|
|
715
|
-
isSidebarOpen: true,
|
|
716
|
-
isGroupInfoOpen: false,
|
|
717
|
-
isStarredPanelOpen: false,
|
|
718
|
-
setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
|
|
719
|
-
setPendingTarget: (target) => set({ pendingTarget: target }),
|
|
720
|
-
addTypingUser: (conversationId, user) => set((state) => {
|
|
721
|
-
const existing = state.typingUsers[conversationId] ?? [];
|
|
722
|
-
const deduped = existing.filter((u) => u.userId !== user.userId);
|
|
723
|
-
return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };
|
|
724
|
-
}),
|
|
725
|
-
removeTypingUser: (conversationId, userId) => set((state) => ({
|
|
726
|
-
typingUsers: {
|
|
727
|
-
...state.typingUsers,
|
|
728
|
-
[conversationId]: (state.typingUsers[conversationId] ?? []).filter(
|
|
729
|
-
(u) => u.userId !== userId
|
|
730
|
-
)
|
|
731
|
-
}
|
|
732
|
-
})),
|
|
733
|
-
setUserOnline: (userId) => set((state) => ({
|
|
734
|
-
onlineUsers: state.onlineUsers.includes(userId) ? state.onlineUsers : [...state.onlineUsers, userId]
|
|
735
|
-
})),
|
|
736
|
-
setUserOffline: (userId) => set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),
|
|
737
|
-
setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),
|
|
738
|
-
setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
|
|
739
|
-
setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
|
|
740
|
-
toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
|
|
741
|
-
setSidebarOpen: (open) => set({ isSidebarOpen: open }),
|
|
742
|
-
toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
|
|
743
|
-
setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
|
|
744
|
-
toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
|
|
745
|
-
setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
|
|
746
|
-
}));
|
|
937
|
+
// src/index.ts
|
|
938
|
+
init_chat_store();
|
|
747
939
|
|
|
748
940
|
// src/client-facade.ts
|
|
749
941
|
var AntzChatClient = class {
|
|
@@ -752,6 +944,7 @@ var AntzChatClient = class {
|
|
|
752
944
|
this.messages = messagesApi;
|
|
753
945
|
this.conversations = conversationsApi;
|
|
754
946
|
this.storage = storageApi;
|
|
947
|
+
this.users = usersApi;
|
|
755
948
|
this.socket = {
|
|
756
949
|
emit: socketEmit,
|
|
757
950
|
on: (event, handler) => getSocket().on(event, handler),
|
|
@@ -799,6 +992,7 @@ var AntzChatClient = class {
|
|
|
799
992
|
normalizeConversation,
|
|
800
993
|
onSocketStatus,
|
|
801
994
|
reconnectSocket,
|
|
995
|
+
refreshSocketAuth,
|
|
802
996
|
resetAuthStore,
|
|
803
997
|
resolveConfig,
|
|
804
998
|
setApiClientInstance,
|
|
@@ -806,6 +1000,7 @@ var AntzChatClient = class {
|
|
|
806
1000
|
storageApi,
|
|
807
1001
|
tryGetSocket,
|
|
808
1002
|
uploadBatch,
|
|
809
|
-
useChatStore
|
|
1003
|
+
useChatStore,
|
|
1004
|
+
usersApi
|
|
810
1005
|
});
|
|
811
1006
|
//# sourceMappingURL=index.cjs.map
|