@snapie/chat-client 0.1.0
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 +313 -0
- package/dist/client-Bztyx_3e.d.mts +220 -0
- package/dist/client-Bztyx_3e.d.ts +220 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +472 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +467 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react.d.mts +56 -0
- package/dist/react.d.ts +56 -0
- package/dist/react.js +124 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +118 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +64 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
// src/service.ts
|
|
2
|
+
var TOKEN_KEY = "snapie-chat-token";
|
|
3
|
+
var TOKEN_USER_KEY = "snapie-chat-token-user";
|
|
4
|
+
var ChatService = class {
|
|
5
|
+
constructor(baseUrl, storage) {
|
|
6
|
+
this.token = null;
|
|
7
|
+
this.tokenUsername = null;
|
|
8
|
+
this.base = `${baseUrl.replace(/\/$/, "")}/api/chat`;
|
|
9
|
+
this.storage = storage;
|
|
10
|
+
this.token = storage.getItem(TOKEN_KEY);
|
|
11
|
+
this.tokenUsername = storage.getItem(TOKEN_USER_KEY);
|
|
12
|
+
}
|
|
13
|
+
isAuthenticated() {
|
|
14
|
+
return !!this.token;
|
|
15
|
+
}
|
|
16
|
+
getTokenUsername() {
|
|
17
|
+
return this.tokenUsername;
|
|
18
|
+
}
|
|
19
|
+
async authenticate(username, signMessage) {
|
|
20
|
+
const { challenge } = await this.post(
|
|
21
|
+
`${this.base}/auth/challenge`,
|
|
22
|
+
{ username },
|
|
23
|
+
false
|
|
24
|
+
);
|
|
25
|
+
const signature = await signMessage(challenge);
|
|
26
|
+
const { token } = await this.post(
|
|
27
|
+
`${this.base}/auth/verify`,
|
|
28
|
+
{ username, challenge, signature },
|
|
29
|
+
false
|
|
30
|
+
);
|
|
31
|
+
this.token = token;
|
|
32
|
+
this.tokenUsername = username;
|
|
33
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
34
|
+
this.storage.setItem(TOKEN_USER_KEY, username);
|
|
35
|
+
}
|
|
36
|
+
logout() {
|
|
37
|
+
this.token = null;
|
|
38
|
+
this.tokenUsername = null;
|
|
39
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
40
|
+
this.storage.removeItem(TOKEN_USER_KEY);
|
|
41
|
+
}
|
|
42
|
+
async getChannels() {
|
|
43
|
+
const { channels } = await this.get(`${this.base}/channels`, false);
|
|
44
|
+
return channels;
|
|
45
|
+
}
|
|
46
|
+
async getConversations() {
|
|
47
|
+
const { conversations } = await this.get(`${this.base}/conversations`, true);
|
|
48
|
+
return conversations;
|
|
49
|
+
}
|
|
50
|
+
async getChannelMessages(channelId, opts = {}) {
|
|
51
|
+
const qs = this.buildQS(opts);
|
|
52
|
+
const { messages } = await this.get(
|
|
53
|
+
`${this.base}/channels/${channelId}/messages${qs}`,
|
|
54
|
+
true
|
|
55
|
+
);
|
|
56
|
+
return messages;
|
|
57
|
+
}
|
|
58
|
+
async sendChannelMessage(channelId, content, replyTo) {
|
|
59
|
+
const { message } = await this.post(
|
|
60
|
+
`${this.base}/channels/${channelId}/messages`,
|
|
61
|
+
{ content, replyTo: replyTo ?? null },
|
|
62
|
+
true
|
|
63
|
+
);
|
|
64
|
+
return message;
|
|
65
|
+
}
|
|
66
|
+
async editChannelMessage(channelId, messageId, content) {
|
|
67
|
+
const { message } = await this.request(
|
|
68
|
+
`${this.base}/channels/${channelId}/messages`,
|
|
69
|
+
{ method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messageId, content }) },
|
|
70
|
+
true
|
|
71
|
+
);
|
|
72
|
+
return message;
|
|
73
|
+
}
|
|
74
|
+
async joinChannel(channelId) {
|
|
75
|
+
await this.post(`${this.base}/channels/${channelId}/join`, {}, true);
|
|
76
|
+
}
|
|
77
|
+
async leaveChannel(channelId) {
|
|
78
|
+
await this.post(`${this.base}/channels/${channelId}/leave`, {}, true);
|
|
79
|
+
}
|
|
80
|
+
async getDmMessages(conversationId, opts = {}) {
|
|
81
|
+
const qs = this.buildQS(opts);
|
|
82
|
+
const { messages, status } = await this.get(
|
|
83
|
+
`${this.base}/dm/${conversationId}/messages${qs}`,
|
|
84
|
+
true
|
|
85
|
+
);
|
|
86
|
+
return { messages, status };
|
|
87
|
+
}
|
|
88
|
+
async sendDmMessage(conversationId, content, replyTo) {
|
|
89
|
+
return this.post(
|
|
90
|
+
`${this.base}/dm/${conversationId}/messages`,
|
|
91
|
+
{ content, replyTo: replyTo ?? null },
|
|
92
|
+
true
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
async editDmMessage(conversationId, messageId, content) {
|
|
96
|
+
const { message } = await this.request(
|
|
97
|
+
`${this.base}/dm/${conversationId}/messages`,
|
|
98
|
+
{ method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messageId, content }) },
|
|
99
|
+
true
|
|
100
|
+
);
|
|
101
|
+
return message;
|
|
102
|
+
}
|
|
103
|
+
async openDm(targetUser) {
|
|
104
|
+
const { conversation } = await this.post(
|
|
105
|
+
`${this.base}/dm`,
|
|
106
|
+
{ targetUser },
|
|
107
|
+
true
|
|
108
|
+
);
|
|
109
|
+
return conversation;
|
|
110
|
+
}
|
|
111
|
+
async createGroup(payload) {
|
|
112
|
+
const { group } = await this.post(`${this.base}/groups`, payload, true);
|
|
113
|
+
return group;
|
|
114
|
+
}
|
|
115
|
+
async getGroups() {
|
|
116
|
+
const { groups } = await this.get(`${this.base}/groups`, true);
|
|
117
|
+
return groups;
|
|
118
|
+
}
|
|
119
|
+
async addGroupMember(groupId, member) {
|
|
120
|
+
const { group } = await this.post(
|
|
121
|
+
`${this.base}/groups/${groupId}/members`,
|
|
122
|
+
{ member },
|
|
123
|
+
true
|
|
124
|
+
);
|
|
125
|
+
return group;
|
|
126
|
+
}
|
|
127
|
+
async removeGroupMember(groupId, member) {
|
|
128
|
+
const { group } = await this.request(
|
|
129
|
+
`${this.base}/groups/${groupId}/members`,
|
|
130
|
+
{ method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ member }) },
|
|
131
|
+
true
|
|
132
|
+
);
|
|
133
|
+
return group;
|
|
134
|
+
}
|
|
135
|
+
async getUnreadCount() {
|
|
136
|
+
if (!this.token) return 0;
|
|
137
|
+
try {
|
|
138
|
+
const { unread } = await this.get(`${this.base}/unread`, true);
|
|
139
|
+
return unread;
|
|
140
|
+
} catch {
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async setTyping(conversationId, isTyping) {
|
|
145
|
+
await this.post(`${this.base}/typing`, { conversationId, isTyping }, true);
|
|
146
|
+
}
|
|
147
|
+
async getTyping(conversationId) {
|
|
148
|
+
const qs = new URLSearchParams({ conversationId }).toString();
|
|
149
|
+
return this.get(`${this.base}/typing?${qs}`, true);
|
|
150
|
+
}
|
|
151
|
+
async getPreferences() {
|
|
152
|
+
return this.get(`${this.base}/preferences`, true);
|
|
153
|
+
}
|
|
154
|
+
async muteUser(username) {
|
|
155
|
+
await this.post(`${this.base}/preferences`, { action: "mute", target: username }, true);
|
|
156
|
+
}
|
|
157
|
+
async unmuteUser(username) {
|
|
158
|
+
await this.post(`${this.base}/preferences`, { action: "unmute", target: username }, true);
|
|
159
|
+
}
|
|
160
|
+
async blockUser(username) {
|
|
161
|
+
await this.post(`${this.base}/preferences`, { action: "block", target: username }, true);
|
|
162
|
+
}
|
|
163
|
+
async unblockUser(username) {
|
|
164
|
+
await this.post(`${this.base}/preferences`, { action: "unblock", target: username }, true);
|
|
165
|
+
}
|
|
166
|
+
async registerDevice(fcmToken) {
|
|
167
|
+
await this.post(`${this.base}/register-device`, { fcmToken }, true);
|
|
168
|
+
}
|
|
169
|
+
async markDmMemoFallbackSent(conversationId) {
|
|
170
|
+
await this.post(`${this.base}/dm/${conversationId}/memo-fallback`, { success: true }, true);
|
|
171
|
+
}
|
|
172
|
+
buildQS(opts) {
|
|
173
|
+
const p = new URLSearchParams();
|
|
174
|
+
if (opts.before) p.set("before", opts.before);
|
|
175
|
+
if (opts.after) p.set("after", opts.after);
|
|
176
|
+
if (opts.limit) p.set("limit", String(opts.limit));
|
|
177
|
+
const s = p.toString();
|
|
178
|
+
return s ? `?${s}` : "";
|
|
179
|
+
}
|
|
180
|
+
async get(url, auth) {
|
|
181
|
+
return this.request(url, { method: "GET" }, auth);
|
|
182
|
+
}
|
|
183
|
+
async post(url, body, auth) {
|
|
184
|
+
return this.request(
|
|
185
|
+
url,
|
|
186
|
+
{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) },
|
|
187
|
+
auth
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
async request(url, opts, auth) {
|
|
191
|
+
const headers = { ...opts.headers };
|
|
192
|
+
if (auth && this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
193
|
+
const res = await fetch(url, { ...opts, headers });
|
|
194
|
+
if (res.status === 401 && auth) {
|
|
195
|
+
this.token = null;
|
|
196
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
197
|
+
throw new Error("CHAT_UNAUTHORIZED");
|
|
198
|
+
}
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
const err = await res.json().catch(() => ({}));
|
|
201
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
202
|
+
}
|
|
203
|
+
return res.json();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// src/polling.ts
|
|
208
|
+
var PollingManager = class {
|
|
209
|
+
constructor(intervalMs) {
|
|
210
|
+
this.timer = null;
|
|
211
|
+
this.handlers = /* @__PURE__ */ new Set();
|
|
212
|
+
this.interval = intervalMs;
|
|
213
|
+
}
|
|
214
|
+
subscribe(handler) {
|
|
215
|
+
this.handlers.add(handler);
|
|
216
|
+
if (!this.timer) {
|
|
217
|
+
this.timer = setInterval(() => this.tick(), this.interval);
|
|
218
|
+
}
|
|
219
|
+
return () => {
|
|
220
|
+
this.handlers.delete(handler);
|
|
221
|
+
if (this.handlers.size === 0 && this.timer) {
|
|
222
|
+
clearInterval(this.timer);
|
|
223
|
+
this.timer = null;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
tick() {
|
|
228
|
+
for (const h of this.handlers) {
|
|
229
|
+
try {
|
|
230
|
+
h();
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
destroy() {
|
|
236
|
+
if (this.timer) {
|
|
237
|
+
clearInterval(this.timer);
|
|
238
|
+
this.timer = null;
|
|
239
|
+
}
|
|
240
|
+
this.handlers.clear();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/storage.ts
|
|
245
|
+
function createDefaultStorage() {
|
|
246
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
247
|
+
return window.localStorage;
|
|
248
|
+
}
|
|
249
|
+
const mem = {};
|
|
250
|
+
return {
|
|
251
|
+
getItem: (k) => mem[k] ?? null,
|
|
252
|
+
setItem: (k, v) => {
|
|
253
|
+
mem[k] = v;
|
|
254
|
+
},
|
|
255
|
+
removeItem: (k) => {
|
|
256
|
+
delete mem[k];
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/client.ts
|
|
262
|
+
var ChatClient = class {
|
|
263
|
+
constructor(options) {
|
|
264
|
+
/** Cache of messages per conversationId — avoids re-rendering unchanged data */
|
|
265
|
+
this.messageCache = /* @__PURE__ */ new Map();
|
|
266
|
+
const storage = options.storage ?? createDefaultStorage();
|
|
267
|
+
this.service = new ChatService(options.baseUrl, storage);
|
|
268
|
+
this.poller = new PollingManager(options.pollInterval ?? 15e3);
|
|
269
|
+
}
|
|
270
|
+
// ── Auth ─────────────────────────────────────────────────────────────────
|
|
271
|
+
isAuthenticated() {
|
|
272
|
+
return this.service.isAuthenticated();
|
|
273
|
+
}
|
|
274
|
+
getUsername() {
|
|
275
|
+
return this.service.getTokenUsername();
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Authenticate with a Hive account.
|
|
279
|
+
* `signMessage` should call Hive Keychain or equivalent with the posting key.
|
|
280
|
+
*/
|
|
281
|
+
async authenticate(username, signMessage) {
|
|
282
|
+
return this.service.authenticate(username, signMessage);
|
|
283
|
+
}
|
|
284
|
+
logout() {
|
|
285
|
+
this.service.logout();
|
|
286
|
+
this.messageCache.clear();
|
|
287
|
+
}
|
|
288
|
+
// ── Conversations ────────────────────────────────────────────────────────
|
|
289
|
+
getConversations() {
|
|
290
|
+
return this.service.getConversations();
|
|
291
|
+
}
|
|
292
|
+
openDm(targetUser) {
|
|
293
|
+
return this.service.openDm(targetUser);
|
|
294
|
+
}
|
|
295
|
+
// ── Channels ─────────────────────────────────────────────────────────────
|
|
296
|
+
getChannels() {
|
|
297
|
+
return this.service.getChannels();
|
|
298
|
+
}
|
|
299
|
+
getGroups() {
|
|
300
|
+
return this.service.getGroups();
|
|
301
|
+
}
|
|
302
|
+
joinChannel(channelId) {
|
|
303
|
+
return this.service.joinChannel(channelId);
|
|
304
|
+
}
|
|
305
|
+
leaveChannel(channelId) {
|
|
306
|
+
return this.service.leaveChannel(channelId);
|
|
307
|
+
}
|
|
308
|
+
createGroup(payload) {
|
|
309
|
+
return this.service.createGroup(payload);
|
|
310
|
+
}
|
|
311
|
+
addGroupMember(groupId, member) {
|
|
312
|
+
return this.service.addGroupMember(groupId, member);
|
|
313
|
+
}
|
|
314
|
+
removeGroupMember(groupId, member) {
|
|
315
|
+
return this.service.removeGroupMember(groupId, member);
|
|
316
|
+
}
|
|
317
|
+
// ── Messages ─────────────────────────────────────────────────────────────
|
|
318
|
+
async getMessages(conversationId, type, opts = {}) {
|
|
319
|
+
if (type === "dm") {
|
|
320
|
+
return this.service.getDmMessages(conversationId, opts);
|
|
321
|
+
}
|
|
322
|
+
const messages = await this.service.getChannelMessages(conversationId, opts);
|
|
323
|
+
return { messages };
|
|
324
|
+
}
|
|
325
|
+
async sendMessage(conversationId, type, content, replyTo) {
|
|
326
|
+
if (type === "dm") {
|
|
327
|
+
return this.service.sendDmMessage(conversationId, content, replyTo);
|
|
328
|
+
}
|
|
329
|
+
const message = await this.service.sendChannelMessage(conversationId, content, replyTo);
|
|
330
|
+
return { message };
|
|
331
|
+
}
|
|
332
|
+
async editMessage(conversationId, type, messageId, content) {
|
|
333
|
+
if (type === "dm") {
|
|
334
|
+
return this.service.editDmMessage(conversationId, messageId, content);
|
|
335
|
+
}
|
|
336
|
+
return this.service.editChannelMessage(conversationId, messageId, content);
|
|
337
|
+
}
|
|
338
|
+
// ── Real-time subscriptions ───────────────────────────────────────────────
|
|
339
|
+
/**
|
|
340
|
+
* Subscribe to live updates for a conversation's message list.
|
|
341
|
+
* Calls `callback` immediately with the current messages, then again on every poll tick.
|
|
342
|
+
* Returns an unsubscribe function.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* const unsub = client.subscribeToMessages(conv._id, conv.type, msgs => setMessages(msgs));
|
|
346
|
+
* // later:
|
|
347
|
+
* unsub();
|
|
348
|
+
*/
|
|
349
|
+
subscribeToMessages(conversationId, type, callback) {
|
|
350
|
+
let latestMessageId;
|
|
351
|
+
const fetch2 = async () => {
|
|
352
|
+
try {
|
|
353
|
+
const opts = latestMessageId ? { after: latestMessageId } : { limit: 40 };
|
|
354
|
+
const { messages: incoming } = await this.getMessages(conversationId, type, opts);
|
|
355
|
+
if (incoming.length === 0) return;
|
|
356
|
+
const existing = this.messageCache.get(conversationId) ?? [];
|
|
357
|
+
if (latestMessageId) {
|
|
358
|
+
const existingIds = new Set(existing.map((m) => m._id));
|
|
359
|
+
const fresh = incoming.filter((m) => !existingIds.has(m._id));
|
|
360
|
+
if (fresh.length === 0) return;
|
|
361
|
+
const merged = [...existing, ...fresh];
|
|
362
|
+
this.messageCache.set(conversationId, merged);
|
|
363
|
+
callback(merged);
|
|
364
|
+
} else {
|
|
365
|
+
this.messageCache.set(conversationId, incoming);
|
|
366
|
+
callback(incoming);
|
|
367
|
+
}
|
|
368
|
+
latestMessageId = incoming[incoming.length - 1]._id;
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
fetch2();
|
|
373
|
+
const stopPolling = this.poller.subscribe(fetch2);
|
|
374
|
+
return () => {
|
|
375
|
+
stopPolling();
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Subscribe to the full conversations list, refreshed on every poll tick.
|
|
380
|
+
* Returns an unsubscribe function.
|
|
381
|
+
*/
|
|
382
|
+
subscribeToConversations(callback) {
|
|
383
|
+
const fetch2 = async () => {
|
|
384
|
+
try {
|
|
385
|
+
const conversations = await this.service.getConversations();
|
|
386
|
+
callback(conversations);
|
|
387
|
+
} catch {
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
fetch2();
|
|
391
|
+
return this.poller.subscribe(fetch2);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Subscribe to the unread message count, refreshed on every poll tick.
|
|
395
|
+
* Returns an unsubscribe function.
|
|
396
|
+
*/
|
|
397
|
+
subscribeToUnreadCount(callback) {
|
|
398
|
+
const fetch2 = async () => {
|
|
399
|
+
try {
|
|
400
|
+
const count = await this.service.getUnreadCount();
|
|
401
|
+
callback(count);
|
|
402
|
+
} catch {
|
|
403
|
+
callback(0);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
fetch2();
|
|
407
|
+
return this.poller.subscribe(fetch2);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Notify the client of an incoming FCM foreground message.
|
|
411
|
+
* Call this from your FCM `onMessage` handler to trigger an immediate refresh
|
|
412
|
+
* rather than waiting for the next poll tick.
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* onMessage(messaging, () => client.onForegroundPush());
|
|
416
|
+
*/
|
|
417
|
+
onForegroundPush() {
|
|
418
|
+
for (const handler of this.poller.handlers) {
|
|
419
|
+
try {
|
|
420
|
+
handler();
|
|
421
|
+
} catch {
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// ── Presence & typing ────────────────────────────────────────────────────
|
|
426
|
+
setTyping(conversationId, isTyping) {
|
|
427
|
+
return this.service.setTyping(conversationId, isTyping);
|
|
428
|
+
}
|
|
429
|
+
getTyping(conversationId) {
|
|
430
|
+
return this.service.getTyping(conversationId);
|
|
431
|
+
}
|
|
432
|
+
getUnreadCount() {
|
|
433
|
+
return this.service.getUnreadCount();
|
|
434
|
+
}
|
|
435
|
+
// ── Preferences ──────────────────────────────────────────────────────────
|
|
436
|
+
getPreferences() {
|
|
437
|
+
return this.service.getPreferences();
|
|
438
|
+
}
|
|
439
|
+
muteUser(username) {
|
|
440
|
+
return this.service.muteUser(username);
|
|
441
|
+
}
|
|
442
|
+
unmuteUser(username) {
|
|
443
|
+
return this.service.unmuteUser(username);
|
|
444
|
+
}
|
|
445
|
+
blockUser(username) {
|
|
446
|
+
return this.service.blockUser(username);
|
|
447
|
+
}
|
|
448
|
+
unblockUser(username) {
|
|
449
|
+
return this.service.unblockUser(username);
|
|
450
|
+
}
|
|
451
|
+
// ── Push notifications ────────────────────────────────────────────────────
|
|
452
|
+
registerDevice(fcmToken) {
|
|
453
|
+
return this.service.registerDevice(fcmToken);
|
|
454
|
+
}
|
|
455
|
+
markDmMemoFallbackSent(conversationId) {
|
|
456
|
+
return this.service.markDmMemoFallbackSent(conversationId);
|
|
457
|
+
}
|
|
458
|
+
// ── Cleanup ───────────────────────────────────────────────────────────────
|
|
459
|
+
destroy() {
|
|
460
|
+
this.poller.destroy();
|
|
461
|
+
this.messageCache.clear();
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
export { ChatClient, ChatService, PollingManager, createDefaultStorage };
|
|
466
|
+
//# sourceMappingURL=index.mjs.map
|
|
467
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/service.ts","../src/polling.ts","../src/storage.ts","../src/client.ts"],"names":["fetch"],"mappings":"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","file":"index.mjs","sourcesContent":["import type {\n Channel,\n Conversation,\n Message,\n MessagesResult,\n DmDeliveryInfo,\n TypingStatusInfo,\n ChatPreferences,\n StorageAdapter,\n} from './types';\n\nconst TOKEN_KEY = 'snapie-chat-token';\nconst TOKEN_USER_KEY = 'snapie-chat-token-user';\n\nexport class ChatService {\n private token: string | null = null;\n private tokenUsername: string | null = null;\n private base: string;\n private storage: StorageAdapter;\n\n constructor(baseUrl: string, storage: StorageAdapter) {\n this.base = `${baseUrl.replace(/\\/$/, '')}/api/chat`;\n this.storage = storage;\n this.token = storage.getItem(TOKEN_KEY);\n this.tokenUsername = storage.getItem(TOKEN_USER_KEY);\n }\n\n isAuthenticated(): boolean {\n return !!this.token;\n }\n\n getTokenUsername(): string | null {\n return this.tokenUsername;\n }\n\n async authenticate(\n username: string,\n signMessage: (msg: string) => Promise<string>\n ): Promise<void> {\n const { challenge } = await this.post<{ challenge: string }>(\n `${this.base}/auth/challenge`,\n { username },\n false\n );\n const signature = await signMessage(challenge);\n const { token } = await this.post<{ token: string }>(\n `${this.base}/auth/verify`,\n { username, challenge, signature },\n false\n );\n this.token = token;\n this.tokenUsername = username;\n this.storage.setItem(TOKEN_KEY, token);\n this.storage.setItem(TOKEN_USER_KEY, username);\n }\n\n logout(): void {\n this.token = null;\n this.tokenUsername = null;\n this.storage.removeItem(TOKEN_KEY);\n this.storage.removeItem(TOKEN_USER_KEY);\n }\n\n async getChannels(): Promise<Channel[]> {\n const { channels } = await this.get<{ channels: Channel[] }>(`${this.base}/channels`, false);\n return channels;\n }\n\n async getConversations(): Promise<Conversation[]> {\n const { conversations } = await this.get<{ conversations: Conversation[] }>(`${this.base}/conversations`, true);\n return conversations;\n }\n\n async getChannelMessages(\n channelId: string,\n opts: { before?: string; after?: string; limit?: number } = {}\n ): Promise<Message[]> {\n const qs = this.buildQS(opts);\n const { messages } = await this.get<{ messages: Message[] }>(\n `${this.base}/channels/${channelId}/messages${qs}`,\n true\n );\n return messages;\n }\n\n async sendChannelMessage(channelId: string, content: string, replyTo?: string): Promise<Message> {\n const { message } = await this.post<{ message: Message }>(\n `${this.base}/channels/${channelId}/messages`,\n { content, replyTo: replyTo ?? null },\n true\n );\n return message;\n }\n\n async editChannelMessage(channelId: string, messageId: string, content: string): Promise<Message> {\n const { message } = await this.request<{ message: Message }>(\n `${this.base}/channels/${channelId}/messages`,\n { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messageId, content }) },\n true\n );\n return message;\n }\n\n async joinChannel(channelId: string): Promise<void> {\n await this.post(`${this.base}/channels/${channelId}/join`, {}, true);\n }\n\n async leaveChannel(channelId: string): Promise<void> {\n await this.post(`${this.base}/channels/${channelId}/leave`, {}, true);\n }\n\n async getDmMessages(\n conversationId: string,\n opts: { before?: string; after?: string; limit?: number } = {}\n ): Promise<MessagesResult> {\n const qs = this.buildQS(opts);\n const { messages, status } = await this.get<MessagesResult>(\n `${this.base}/dm/${conversationId}/messages${qs}`,\n true\n );\n return { messages, status };\n }\n\n async sendDmMessage(\n conversationId: string,\n content: string,\n replyTo?: string\n ): Promise<{ message: Message; delivery?: DmDeliveryInfo }> {\n return this.post<{ message: Message; delivery?: DmDeliveryInfo }>(\n `${this.base}/dm/${conversationId}/messages`,\n { content, replyTo: replyTo ?? null },\n true\n );\n }\n\n async editDmMessage(conversationId: string, messageId: string, content: string): Promise<Message> {\n const { message } = await this.request<{ message: Message }>(\n `${this.base}/dm/${conversationId}/messages`,\n { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messageId, content }) },\n true\n );\n return message;\n }\n\n async openDm(targetUser: string): Promise<Conversation> {\n const { conversation } = await this.post<{ conversation: Conversation }>(\n `${this.base}/dm`,\n { targetUser },\n true\n );\n return conversation;\n }\n\n async createGroup(payload: {\n name: string;\n description?: string;\n isPublic?: boolean;\n members?: string[];\n }): Promise<Channel> {\n const { group } = await this.post<{ group: Channel }>(`${this.base}/groups`, payload, true);\n return group;\n }\n\n async getGroups(): Promise<Channel[]> {\n const { groups } = await this.get<{ groups: Channel[] }>(`${this.base}/groups`, true);\n return groups;\n }\n\n async addGroupMember(groupId: string, member: string): Promise<Channel> {\n const { group } = await this.post<{ group: Channel }>(\n `${this.base}/groups/${groupId}/members`,\n { member },\n true\n );\n return group;\n }\n\n async removeGroupMember(groupId: string, member: string): Promise<Channel> {\n const { group } = await this.request<{ group: Channel }>(\n `${this.base}/groups/${groupId}/members`,\n { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ member }) },\n true\n );\n return group;\n }\n\n async getUnreadCount(): Promise<number> {\n if (!this.token) return 0;\n try {\n const { unread } = await this.get<{ unread: number }>(`${this.base}/unread`, true);\n return unread;\n } catch {\n return 0;\n }\n }\n\n async setTyping(conversationId: string, isTyping: boolean): Promise<void> {\n await this.post(`${this.base}/typing`, { conversationId, isTyping }, true);\n }\n\n async getTyping(conversationId: string): Promise<TypingStatusInfo> {\n const qs = new URLSearchParams({ conversationId }).toString();\n return this.get<TypingStatusInfo>(`${this.base}/typing?${qs}`, true);\n }\n\n async getPreferences(): Promise<ChatPreferences> {\n return this.get<ChatPreferences>(`${this.base}/preferences`, true);\n }\n\n async muteUser(username: string): Promise<void> {\n await this.post(`${this.base}/preferences`, { action: 'mute', target: username }, true);\n }\n\n async unmuteUser(username: string): Promise<void> {\n await this.post(`${this.base}/preferences`, { action: 'unmute', target: username }, true);\n }\n\n async blockUser(username: string): Promise<void> {\n await this.post(`${this.base}/preferences`, { action: 'block', target: username }, true);\n }\n\n async unblockUser(username: string): Promise<void> {\n await this.post(`${this.base}/preferences`, { action: 'unblock', target: username }, true);\n }\n\n async registerDevice(fcmToken: string): Promise<void> {\n await this.post(`${this.base}/register-device`, { fcmToken }, true);\n }\n\n async markDmMemoFallbackSent(conversationId: string): Promise<void> {\n await this.post(`${this.base}/dm/${conversationId}/memo-fallback`, { success: true }, true);\n }\n\n private buildQS(opts: { before?: string; after?: string; limit?: number }): string {\n const p = new URLSearchParams();\n if (opts.before) p.set('before', opts.before);\n if (opts.after) p.set('after', opts.after);\n if (opts.limit) p.set('limit', String(opts.limit));\n const s = p.toString();\n return s ? `?${s}` : '';\n }\n\n private async get<T>(url: string, auth: boolean): Promise<T> {\n return this.request<T>(url, { method: 'GET' }, auth);\n }\n\n private async post<T>(url: string, body: unknown, auth: boolean): Promise<T> {\n return this.request<T>(\n url,\n { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) },\n auth\n );\n }\n\n async request<T>(url: string, opts: RequestInit, auth: boolean): Promise<T> {\n const headers: Record<string, string> = { ...(opts.headers as Record<string, string>) };\n if (auth && this.token) headers['Authorization'] = `Bearer ${this.token}`;\n\n const res = await fetch(url, { ...opts, headers });\n\n if (res.status === 401 && auth) {\n this.token = null;\n this.storage.removeItem(TOKEN_KEY);\n throw new Error('CHAT_UNAUTHORIZED');\n }\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<T>;\n }\n}\n","type Handler = () => void | Promise<void>;\n\n/**\n * Manages a single setInterval that fans out to multiple subscribers.\n * Starting the first subscription starts the timer; removing the last stops it.\n */\nexport class PollingManager {\n private timer: ReturnType<typeof setInterval> | null = null;\n private handlers: Set<Handler> = new Set();\n private interval: number;\n\n constructor(intervalMs: number) {\n this.interval = intervalMs;\n }\n\n subscribe(handler: Handler): () => void {\n this.handlers.add(handler);\n if (!this.timer) {\n this.timer = setInterval(() => this.tick(), this.interval);\n }\n return () => {\n this.handlers.delete(handler);\n if (this.handlers.size === 0 && this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n };\n }\n\n private tick() {\n for (const h of this.handlers) {\n try { h(); } catch { /* individual handler errors don't break others */ }\n }\n }\n\n destroy() {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.handlers.clear();\n }\n}\n","import type { StorageAdapter } from './types';\n\n/** Default adapter — uses localStorage when available, falls back to in-memory. */\nexport function createDefaultStorage(): StorageAdapter {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n const mem: Record<string, string> = {};\n return {\n getItem: (k) => mem[k] ?? null,\n setItem: (k, v) => { mem[k] = v; },\n removeItem: (k) => { delete mem[k]; },\n };\n}\n","import { ChatService } from './service';\nimport { PollingManager } from './polling';\nimport { createDefaultStorage } from './storage';\nimport type {\n ChatClientOptions,\n Channel,\n Conversation,\n Message,\n MessagesResult,\n ChatPreferences,\n TypingStatusInfo,\n DmDeliveryInfo,\n} from './types';\n\ntype ConversationsCallback = (conversations: Conversation[]) => void;\ntype MessagesCallback = (messages: Message[]) => void;\ntype UnreadCallback = (count: number) => void;\n\n/**\n * High-level Snapie chat client.\n *\n * Usage:\n * const client = new ChatClient({ baseUrl: 'https://snapie.io' });\n * await client.authenticate(username, msg => keychain.sign(msg));\n * const conversations = await client.getConversations();\n * const unsub = client.subscribeToMessages(convId, 'dm', msgs => setMessages(msgs));\n */\nexport class ChatClient {\n readonly service: ChatService;\n private poller: PollingManager;\n\n /** Cache of messages per conversationId — avoids re-rendering unchanged data */\n private messageCache: Map<string, Message[]> = new Map();\n\n constructor(options: ChatClientOptions) {\n const storage = options.storage ?? createDefaultStorage();\n this.service = new ChatService(options.baseUrl, storage);\n this.poller = new PollingManager(options.pollInterval ?? 15_000);\n }\n\n // ── Auth ─────────────────────────────────────────────────────────────────\n\n isAuthenticated(): boolean {\n return this.service.isAuthenticated();\n }\n\n getUsername(): string | null {\n return this.service.getTokenUsername();\n }\n\n /**\n * Authenticate with a Hive account.\n * `signMessage` should call Hive Keychain or equivalent with the posting key.\n */\n async authenticate(\n username: string,\n signMessage: (challenge: string) => Promise<string>\n ): Promise<void> {\n return this.service.authenticate(username, signMessage);\n }\n\n logout(): void {\n this.service.logout();\n this.messageCache.clear();\n }\n\n // ── Conversations ────────────────────────────────────────────────────────\n\n getConversations(): Promise<Conversation[]> {\n return this.service.getConversations();\n }\n\n openDm(targetUser: string): Promise<Conversation> {\n return this.service.openDm(targetUser);\n }\n\n // ── Channels ─────────────────────────────────────────────────────────────\n\n getChannels(): Promise<Channel[]> {\n return this.service.getChannels();\n }\n\n getGroups(): Promise<Channel[]> {\n return this.service.getGroups();\n }\n\n joinChannel(channelId: string): Promise<void> {\n return this.service.joinChannel(channelId);\n }\n\n leaveChannel(channelId: string): Promise<void> {\n return this.service.leaveChannel(channelId);\n }\n\n createGroup(payload: {\n name: string;\n description?: string;\n isPublic?: boolean;\n members?: string[];\n }): Promise<Channel> {\n return this.service.createGroup(payload);\n }\n\n addGroupMember(groupId: string, member: string): Promise<Channel> {\n return this.service.addGroupMember(groupId, member);\n }\n\n removeGroupMember(groupId: string, member: string): Promise<Channel> {\n return this.service.removeGroupMember(groupId, member);\n }\n\n // ── Messages ─────────────────────────────────────────────────────────────\n\n async getMessages(\n conversationId: string,\n type: 'channel' | 'dm' | 'group',\n opts: { before?: string; after?: string; limit?: number } = {}\n ): Promise<MessagesResult> {\n if (type === 'dm') {\n return this.service.getDmMessages(conversationId, opts);\n }\n const messages = await this.service.getChannelMessages(conversationId, opts);\n return { messages };\n }\n\n async sendMessage(\n conversationId: string,\n type: 'channel' | 'dm' | 'group',\n content: string,\n replyTo?: string\n ): Promise<{ message: Message; delivery?: DmDeliveryInfo }> {\n if (type === 'dm') {\n return this.service.sendDmMessage(conversationId, content, replyTo);\n }\n const message = await this.service.sendChannelMessage(conversationId, content, replyTo);\n return { message };\n }\n\n async editMessage(\n conversationId: string,\n type: 'channel' | 'dm' | 'group',\n messageId: string,\n content: string\n ): Promise<Message> {\n if (type === 'dm') {\n return this.service.editDmMessage(conversationId, messageId, content);\n }\n return this.service.editChannelMessage(conversationId, messageId, content);\n }\n\n // ── Real-time subscriptions ───────────────────────────────────────────────\n\n /**\n * Subscribe to live updates for a conversation's message list.\n * Calls `callback` immediately with the current messages, then again on every poll tick.\n * Returns an unsubscribe function.\n *\n * @example\n * const unsub = client.subscribeToMessages(conv._id, conv.type, msgs => setMessages(msgs));\n * // later:\n * unsub();\n */\n subscribeToMessages(\n conversationId: string,\n type: 'channel' | 'dm' | 'group',\n callback: MessagesCallback\n ): () => void {\n let latestMessageId: string | undefined;\n\n const fetch = async () => {\n try {\n const opts = latestMessageId ? { after: latestMessageId } : { limit: 40 };\n const { messages: incoming } = await this.getMessages(conversationId, type, opts);\n if (incoming.length === 0) return;\n\n const existing = this.messageCache.get(conversationId) ?? [];\n\n if (latestMessageId) {\n // Append only genuinely new messages\n const existingIds = new Set(existing.map(m => m._id));\n const fresh = incoming.filter(m => !existingIds.has(m._id));\n if (fresh.length === 0) return;\n const merged = [...existing, ...fresh];\n this.messageCache.set(conversationId, merged);\n callback(merged);\n } else {\n // Initial load\n this.messageCache.set(conversationId, incoming);\n callback(incoming);\n }\n\n latestMessageId = incoming[incoming.length - 1]._id;\n } catch {\n // Silently retry on next tick\n }\n };\n\n // Fire immediately, then on each poll tick\n fetch();\n const stopPolling = this.poller.subscribe(fetch);\n\n return () => {\n stopPolling();\n };\n }\n\n /**\n * Subscribe to the full conversations list, refreshed on every poll tick.\n * Returns an unsubscribe function.\n */\n subscribeToConversations(callback: ConversationsCallback): () => void {\n const fetch = async () => {\n try {\n const conversations = await this.service.getConversations();\n callback(conversations);\n } catch {\n // Silently retry on next tick\n }\n };\n\n fetch();\n return this.poller.subscribe(fetch);\n }\n\n /**\n * Subscribe to the unread message count, refreshed on every poll tick.\n * Returns an unsubscribe function.\n */\n subscribeToUnreadCount(callback: UnreadCallback): () => void {\n const fetch = async () => {\n try {\n const count = await this.service.getUnreadCount();\n callback(count);\n } catch {\n callback(0);\n }\n };\n\n fetch();\n return this.poller.subscribe(fetch);\n }\n\n /**\n * Notify the client of an incoming FCM foreground message.\n * Call this from your FCM `onMessage` handler to trigger an immediate refresh\n * rather than waiting for the next poll tick.\n *\n * @example\n * onMessage(messaging, () => client.onForegroundPush());\n */\n onForegroundPush(): void {\n for (const handler of (this.poller as unknown as { handlers: Set<() => void> }).handlers) {\n try { handler(); } catch { /* */ }\n }\n }\n\n // ── Presence & typing ────────────────────────────────────────────────────\n\n setTyping(conversationId: string, isTyping: boolean): Promise<void> {\n return this.service.setTyping(conversationId, isTyping);\n }\n\n getTyping(conversationId: string): Promise<TypingStatusInfo> {\n return this.service.getTyping(conversationId);\n }\n\n getUnreadCount(): Promise<number> {\n return this.service.getUnreadCount();\n }\n\n // ── Preferences ──────────────────────────────────────────────────────────\n\n getPreferences(): Promise<ChatPreferences> {\n return this.service.getPreferences();\n }\n\n muteUser(username: string): Promise<void> {\n return this.service.muteUser(username);\n }\n\n unmuteUser(username: string): Promise<void> {\n return this.service.unmuteUser(username);\n }\n\n blockUser(username: string): Promise<void> {\n return this.service.blockUser(username);\n }\n\n unblockUser(username: string): Promise<void> {\n return this.service.unblockUser(username);\n }\n\n // ── Push notifications ────────────────────────────────────────────────────\n\n registerDevice(fcmToken: string): Promise<void> {\n return this.service.registerDevice(fcmToken);\n }\n\n markDmMemoFallbackSent(conversationId: string): Promise<void> {\n return this.service.markDmMemoFallbackSent(conversationId);\n }\n\n // ── Cleanup ───────────────────────────────────────────────────────────────\n\n destroy(): void {\n this.poller.destroy();\n this.messageCache.clear();\n }\n}\n"]}
|
package/dist/react.d.mts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { C as ChatClient, d as Conversation, M as Message } from './client-Bztyx_3e.mjs';
|
|
4
|
+
|
|
5
|
+
interface ChatProviderProps {
|
|
6
|
+
client: ChatClient;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
/** Wrap your app (or the chat section) with this to access hooks without prop-drilling. */
|
|
10
|
+
declare function ChatProvider({ client, children }: ChatProviderProps): react_jsx_runtime.JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to the live conversations list.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const { conversations, loading } = useConversations();
|
|
16
|
+
*/
|
|
17
|
+
declare function useConversations(clientOverride?: ChatClient): {
|
|
18
|
+
conversations: Conversation[];
|
|
19
|
+
loading: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to live messages for a conversation.
|
|
23
|
+
* Handles initial load + polling automatically.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const { messages, loading, sendMessage } = useChatMessages(conv._id, conv.type);
|
|
27
|
+
*/
|
|
28
|
+
declare function useChatMessages(conversationId: string | null, type: 'channel' | 'dm' | 'group', clientOverride?: ChatClient): {
|
|
29
|
+
messages: Message[];
|
|
30
|
+
loading: boolean;
|
|
31
|
+
error: string | null;
|
|
32
|
+
sendMessage: (content: string, replyTo?: string) => Promise<void>;
|
|
33
|
+
editMessage: (messageId: string, content: string) => Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Subscribe to the unread message count badge.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const { unreadCount } = useUnreadCount();
|
|
40
|
+
*/
|
|
41
|
+
declare function useUnreadCount(clientOverride?: ChatClient): {
|
|
42
|
+
unreadCount: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Typing indicator for a conversation.
|
|
46
|
+
* Returns who is typing and exposes `setTyping` to broadcast your own state.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const { typingUsers, setTyping } = useTyping(conv._id);
|
|
50
|
+
*/
|
|
51
|
+
declare function useTyping(conversationId: string | null, clientOverride?: ChatClient): {
|
|
52
|
+
typingUsers: string[];
|
|
53
|
+
setTyping: (isTyping: boolean) => void;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { ChatClient, ChatProvider, useChatMessages, useConversations, useTyping, useUnreadCount };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { C as ChatClient, d as Conversation, M as Message } from './client-Bztyx_3e.js';
|
|
4
|
+
|
|
5
|
+
interface ChatProviderProps {
|
|
6
|
+
client: ChatClient;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
/** Wrap your app (or the chat section) with this to access hooks without prop-drilling. */
|
|
10
|
+
declare function ChatProvider({ client, children }: ChatProviderProps): react_jsx_runtime.JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to the live conversations list.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const { conversations, loading } = useConversations();
|
|
16
|
+
*/
|
|
17
|
+
declare function useConversations(clientOverride?: ChatClient): {
|
|
18
|
+
conversations: Conversation[];
|
|
19
|
+
loading: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to live messages for a conversation.
|
|
23
|
+
* Handles initial load + polling automatically.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const { messages, loading, sendMessage } = useChatMessages(conv._id, conv.type);
|
|
27
|
+
*/
|
|
28
|
+
declare function useChatMessages(conversationId: string | null, type: 'channel' | 'dm' | 'group', clientOverride?: ChatClient): {
|
|
29
|
+
messages: Message[];
|
|
30
|
+
loading: boolean;
|
|
31
|
+
error: string | null;
|
|
32
|
+
sendMessage: (content: string, replyTo?: string) => Promise<void>;
|
|
33
|
+
editMessage: (messageId: string, content: string) => Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Subscribe to the unread message count badge.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const { unreadCount } = useUnreadCount();
|
|
40
|
+
*/
|
|
41
|
+
declare function useUnreadCount(clientOverride?: ChatClient): {
|
|
42
|
+
unreadCount: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Typing indicator for a conversation.
|
|
46
|
+
* Returns who is typing and exposes `setTyping` to broadcast your own state.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const { typingUsers, setTyping } = useTyping(conv._id);
|
|
50
|
+
*/
|
|
51
|
+
declare function useTyping(conversationId: string | null, clientOverride?: ChatClient): {
|
|
52
|
+
typingUsers: string[];
|
|
53
|
+
setTyping: (isTyping: boolean) => void;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { ChatClient, ChatProvider, useChatMessages, useConversations, useTyping, useUnreadCount };
|