@poolse/react 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/dist/index.cjs ADDED
@@ -0,0 +1,695 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var sdk = require('@poolse/sdk');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/provider.tsx
9
+ var PoolseContext = react.createContext(null);
10
+ function PoolseProvider({ config, children }) {
11
+ const configRef = react.useRef(config);
12
+ configRef.current = config;
13
+ const initialConnectionRef = react.useRef(null);
14
+ const chat = react.useMemo(() => {
15
+ const initial = configRef.current;
16
+ initialConnectionRef.current = {
17
+ apiUrl: initial.apiUrl,
18
+ wsUrl: initial.wsUrl,
19
+ socketPath: initial.socketPath
20
+ };
21
+ return new sdk.Poolse({
22
+ apiUrl: initial.apiUrl,
23
+ // Always read the latest callbacks from the ref — they're free
24
+ // to change between renders.
25
+ getToken: () => configRef.current.getToken(),
26
+ ...initial.fetch ? { fetch: ((...args) => configRef.current.fetch(...args)) } : {},
27
+ ...initial.maxRetries !== void 0 ? { maxRetries: initial.maxRetries } : {},
28
+ ...initial.baseBackoffMs !== void 0 ? { baseBackoffMs: initial.baseBackoffMs } : {},
29
+ ...initial.maxBackoffMs !== void 0 ? { maxBackoffMs: initial.maxBackoffMs } : {},
30
+ ...initial.generateIdempotencyKey ? { generateIdempotencyKey: () => configRef.current.generateIdempotencyKey() } : {},
31
+ ...initial.wsUrl !== void 0 ? { wsUrl: initial.wsUrl } : {},
32
+ ...initial.socketPath !== void 0 ? { socketPath: initial.socketPath } : {},
33
+ ...initial.onSocketError ? { onSocketError: (err) => configRef.current.onSocketError(err) } : {}
34
+ });
35
+ }, []);
36
+ if (process.env.NODE_ENV !== "production") {
37
+ react.useEffect(() => {
38
+ const initial = initialConnectionRef.current;
39
+ if (!initial) return;
40
+ if (initial.apiUrl !== config.apiUrl || initial.wsUrl !== config.wsUrl || initial.socketPath !== config.socketPath) {
41
+ console.warn(
42
+ "[poolse] connection-shaped config (apiUrl / wsUrl / socketPath) changed after <PoolseProvider> mounted. These are captured at construction time \u2014 remount the provider with a new `key` to switch environments."
43
+ );
44
+ }
45
+ }, [config.apiUrl, config.wsUrl, config.socketPath]);
46
+ }
47
+ react.useEffect(() => {
48
+ return () => {
49
+ chat.destroy();
50
+ };
51
+ }, [chat]);
52
+ const value = react.useMemo(() => ({ chat }), [chat]);
53
+ return /* @__PURE__ */ jsxRuntime.jsx(PoolseContext.Provider, { value, children });
54
+ }
55
+ function usePoolse() {
56
+ const value = react.useContext(PoolseContext);
57
+ if (!value) {
58
+ throw new Error(
59
+ "usePoolse(): no <PoolseProvider> in the tree. Wrap your component with `<PoolseProvider config={{apiUrl, getToken}}>` first."
60
+ );
61
+ }
62
+ return value.chat;
63
+ }
64
+ function useMe() {
65
+ const chat = usePoolse();
66
+ const [state, setState] = react.useState({
67
+ me: null,
68
+ loading: true,
69
+ error: null
70
+ });
71
+ react.useEffect(() => {
72
+ let cancelled = false;
73
+ const controller = new AbortController();
74
+ chat.me.show(controller.signal).then((me) => {
75
+ if (!cancelled) setState({ me, loading: false, error: null });
76
+ }).catch((err) => {
77
+ if (!cancelled) {
78
+ setState({
79
+ me: null,
80
+ loading: false,
81
+ error: err instanceof Error ? err : new Error(String(err))
82
+ });
83
+ }
84
+ });
85
+ return () => {
86
+ cancelled = true;
87
+ controller.abort();
88
+ };
89
+ }, [chat]);
90
+ return state;
91
+ }
92
+ function useConversation(id) {
93
+ const chat = usePoolse();
94
+ const [conversation, setConversation] = react.useState(null);
95
+ const [loading, setLoading] = react.useState(id !== null);
96
+ const [error, setError] = react.useState(null);
97
+ const fetcher = react.useCallback(
98
+ async (signal) => {
99
+ if (id === null) return;
100
+ setLoading(true);
101
+ setError(null);
102
+ try {
103
+ const conv = await chat.conversations.one(id).show(signal);
104
+ setConversation(conv);
105
+ } catch (err) {
106
+ if (err?.name === "AbortError") return;
107
+ setError(err instanceof Error ? err : new Error(String(err)));
108
+ } finally {
109
+ setLoading(false);
110
+ }
111
+ },
112
+ [chat, id]
113
+ );
114
+ react.useEffect(() => {
115
+ if (id === null) {
116
+ setConversation(null);
117
+ setLoading(false);
118
+ setError(null);
119
+ return;
120
+ }
121
+ const controller = new AbortController();
122
+ void fetcher(controller.signal);
123
+ return () => controller.abort();
124
+ }, [fetcher, id]);
125
+ const refetch = react.useCallback(() => fetcher(), [fetcher]);
126
+ return { conversation, loading, error, refetch };
127
+ }
128
+ function useConversations() {
129
+ const chat = usePoolse();
130
+ const { me } = useMe();
131
+ const [conversations, setConversations] = react.useState([]);
132
+ const [loading, setLoading] = react.useState(true);
133
+ const [error, setError] = react.useState(null);
134
+ const refetch = react.useCallback(async () => {
135
+ setLoading(true);
136
+ try {
137
+ const list = await chat.conversations.list();
138
+ setConversations(list.data);
139
+ setError(null);
140
+ } catch (err) {
141
+ setError(err instanceof Error ? err : new Error(String(err)));
142
+ } finally {
143
+ setLoading(false);
144
+ }
145
+ }, [chat]);
146
+ const create = react.useCallback(
147
+ async (attrs) => {
148
+ const conv = await chat.conversations.create(attrs);
149
+ setConversations((prev) => prepend(prev, conv));
150
+ return conv;
151
+ },
152
+ [chat]
153
+ );
154
+ react.useEffect(() => {
155
+ void refetch();
156
+ }, [refetch]);
157
+ react.useEffect(() => {
158
+ if (!me) return;
159
+ const userChan = chat.realtime.user(me.id);
160
+ const off = userChan.onConversationCreated((conv) => {
161
+ setConversations((prev) => prepend(prev, conv));
162
+ });
163
+ return off;
164
+ }, [chat, me]);
165
+ return { conversations, loading, error, refetch, create };
166
+ }
167
+ function prepend(prev, conv) {
168
+ const idx = prev.findIndex((c) => c.id === conv.id);
169
+ if (idx === -1) return [conv, ...prev];
170
+ const next = prev.slice();
171
+ next[idx] = conv;
172
+ return next;
173
+ }
174
+ function useMembers(conversationId) {
175
+ const chat = usePoolse();
176
+ const [members, setMembers] = react.useState([]);
177
+ const [loading, setLoading] = react.useState(true);
178
+ const [error, setError] = react.useState(null);
179
+ const membersRef = react.useRef([]);
180
+ membersRef.current = members;
181
+ const fetcher = react.useCallback(
182
+ async (signal) => {
183
+ setLoading(true);
184
+ setError(null);
185
+ try {
186
+ const { data } = await chat.conversations.one(conversationId).listMembers(signal);
187
+ setMembers(data);
188
+ } catch (err) {
189
+ if (err?.name === "AbortError") return;
190
+ setError(err instanceof Error ? err : new Error(String(err)));
191
+ } finally {
192
+ setLoading(false);
193
+ }
194
+ },
195
+ [chat, conversationId]
196
+ );
197
+ react.useEffect(() => {
198
+ const controller = new AbortController();
199
+ void fetcher(controller.signal);
200
+ return () => controller.abort();
201
+ }, [fetcher]);
202
+ const refetch = react.useCallback(() => fetcher(), [fetcher]);
203
+ const addMembers = react.useCallback(
204
+ async (externalIds, opts = {}) => {
205
+ const addOpts = opts.role !== void 0 ? { role: opts.role } : {};
206
+ const { data } = await chat.conversations.one(conversationId).addMembers(externalIds, addOpts);
207
+ setMembers((prev) => {
208
+ const byId = new Map(prev.map((m) => [m.id, m]));
209
+ for (const m of data) byId.set(m.id, m);
210
+ return Array.from(byId.values());
211
+ });
212
+ return data;
213
+ },
214
+ [chat, conversationId]
215
+ );
216
+ const removeMember = react.useCallback(
217
+ async (userId) => {
218
+ const snapshot = membersRef.current;
219
+ setMembers((prev) => prev.filter((m) => m.user_id !== userId));
220
+ try {
221
+ await chat.conversations.one(conversationId).removeMember(userId);
222
+ } catch (err) {
223
+ setMembers(snapshot);
224
+ throw err;
225
+ }
226
+ },
227
+ [chat, conversationId]
228
+ );
229
+ return { members, loading, error, refetch, addMembers, removeMember };
230
+ }
231
+ var PAGE_SIZE = 50;
232
+ function useMessages(conversationId) {
233
+ const chat = usePoolse();
234
+ const [messages, setMessages] = react.useState([]);
235
+ const [loading, setLoading] = react.useState(true);
236
+ const [error, setError] = react.useState(null);
237
+ const [hasMore, setHasMore] = react.useState(true);
238
+ const oldestSequenceRef = react.useRef(null);
239
+ react.useEffect(() => {
240
+ let cancelled = false;
241
+ setLoading(true);
242
+ setMessages([]);
243
+ setError(null);
244
+ setHasMore(true);
245
+ oldestSequenceRef.current = null;
246
+ const handle = chat.conversations.one(conversationId).messages;
247
+ handle.list({ limit: PAGE_SIZE }).then((page) => {
248
+ if (cancelled) return;
249
+ const ordered = [...page.data].reverse();
250
+ setMessages(ordered);
251
+ setHasMore(page.data.length === PAGE_SIZE);
252
+ oldestSequenceRef.current = ordered[0]?.sequence ?? null;
253
+ setLoading(false);
254
+ }).catch((err) => {
255
+ if (cancelled) return;
256
+ setError(err instanceof Error ? err : new Error(String(err)));
257
+ setLoading(false);
258
+ });
259
+ const conv = chat.realtime.conversation(conversationId);
260
+ const offNew = conv.onMessage((msg) => {
261
+ setMessages((prev) => upsertById(prev, msg));
262
+ });
263
+ const offUpdated = conv.onMessageUpdated((msg) => {
264
+ setMessages((prev) => prev.map((m) => m.id === msg.id ? msg : m));
265
+ });
266
+ const offDeleted = conv.onMessageDeleted((evt) => {
267
+ setMessages(
268
+ (prev) => prev.map((m) => m.id === evt.id ? { ...m, deleted_at: evt.deleted_at, body: null } : m)
269
+ );
270
+ });
271
+ return () => {
272
+ cancelled = true;
273
+ offNew();
274
+ offUpdated();
275
+ offDeleted();
276
+ };
277
+ }, [chat, conversationId]);
278
+ const loadMore = react.useCallback(async () => {
279
+ if (!hasMore || loading) return;
280
+ const before = oldestSequenceRef.current;
281
+ if (!before) return;
282
+ const opts = { limit: PAGE_SIZE };
283
+ if (before !== null) opts.before = before;
284
+ const page = await chat.conversations.one(conversationId).messages.list(opts);
285
+ if (page.data.length === 0) {
286
+ setHasMore(false);
287
+ return;
288
+ }
289
+ const older = [...page.data].reverse();
290
+ setMessages((prev) => [...older, ...prev]);
291
+ setHasMore(page.data.length === PAGE_SIZE);
292
+ oldestSequenceRef.current = older[0]?.sequence ?? oldestSequenceRef.current;
293
+ }, [chat, conversationId, hasMore, loading]);
294
+ const send = react.useCallback(
295
+ async (attrs) => {
296
+ const id = attrs.id ?? crypto.randomUUID();
297
+ const tempMsg = {
298
+ id,
299
+ tenant_id: "",
300
+ conversation_id: conversationId,
301
+ sender_id: null,
302
+ type: attrs.type ?? "text",
303
+ body: attrs.body,
304
+ reply_to_id: attrs.reply_to_id ?? null,
305
+ thread_root_id: null,
306
+ mentions: attrs.mentions ?? [],
307
+ reactions: {},
308
+ edited_at: null,
309
+ deleted_at: null,
310
+ // MAX_SAFE_INTEGER sorts the temp row to the very end (newest
311
+ // position) until the server returns the real `sequence`.
312
+ sequence: Number.MAX_SAFE_INTEGER,
313
+ inserted_at: (/* @__PURE__ */ new Date()).toISOString(),
314
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
315
+ };
316
+ setMessages((prev) => [...prev, tempMsg]);
317
+ try {
318
+ const sent = await chat.conversations.one(conversationId).messages.send({ ...attrs, id });
319
+ setMessages((prev) => upsertById(prev, sent));
320
+ return sent;
321
+ } catch (err) {
322
+ setMessages((prev) => prev.filter((m) => m.id !== id));
323
+ throw err;
324
+ }
325
+ },
326
+ [chat, conversationId]
327
+ );
328
+ return { messages, loading, error, hasMore, loadMore, send };
329
+ }
330
+ function upsertById(prev, msg) {
331
+ const idx = prev.findIndex((m) => m.id === msg.id);
332
+ if (idx === -1) return [...prev, msg];
333
+ const next = prev.slice();
334
+ next[idx] = msg;
335
+ return next;
336
+ }
337
+ function useReactions(messageId, opts) {
338
+ const chat = usePoolse();
339
+ const [reactions, setReactions] = react.useState(opts.initialReactions ?? {});
340
+ const currentUserIdRef = react.useRef(opts.currentUserId ?? null);
341
+ currentUserIdRef.current = opts.currentUserId ?? null;
342
+ const reactionsRef = react.useRef(reactions);
343
+ reactionsRef.current = reactions;
344
+ react.useEffect(() => {
345
+ const conv = chat.realtime.conversation(opts.conversationId);
346
+ const offAdded = conv.onReactionAdded((evt) => {
347
+ if (evt.message_id !== messageId) return;
348
+ setReactions((prev) => addUser(prev, evt.emoji, evt.user_id));
349
+ });
350
+ const offRemoved = conv.onReactionRemoved((evt) => {
351
+ if (evt.message_id !== messageId) return;
352
+ setReactions((prev) => removeUser(prev, evt.emoji, evt.user_id));
353
+ });
354
+ return () => {
355
+ offAdded();
356
+ offRemoved();
357
+ };
358
+ }, [chat, opts.conversationId, messageId]);
359
+ const addReaction = react.useCallback(
360
+ async (emoji) => {
361
+ const uid = currentUserIdRef.current;
362
+ if (uid) setReactions((prev) => addUser(prev, emoji, uid));
363
+ try {
364
+ await chat.messages.one(messageId).addReaction(emoji);
365
+ } catch (err) {
366
+ if (uid) setReactions((prev) => removeUser(prev, emoji, uid));
367
+ throw err;
368
+ }
369
+ },
370
+ [chat, messageId]
371
+ );
372
+ const removeReaction = react.useCallback(
373
+ async (emoji) => {
374
+ const uid = currentUserIdRef.current;
375
+ const snapshot = reactionsRef.current;
376
+ if (uid) setReactions((prev) => removeUser(prev, emoji, uid));
377
+ try {
378
+ await chat.messages.one(messageId).removeReaction(emoji);
379
+ } catch (err) {
380
+ if (uid) setReactions(snapshot);
381
+ throw err;
382
+ }
383
+ },
384
+ [chat, messageId]
385
+ );
386
+ return { reactions, addReaction, removeReaction };
387
+ }
388
+ function addUser(prev, emoji, userId) {
389
+ const existing = prev[emoji] ?? [];
390
+ if (existing.includes(userId)) return prev;
391
+ return { ...prev, [emoji]: [...existing, userId] };
392
+ }
393
+ function removeUser(prev, emoji, userId) {
394
+ const existing = prev[emoji];
395
+ if (!existing || !existing.includes(userId)) return prev;
396
+ const next = existing.filter((id) => id !== userId);
397
+ if (next.length === 0) {
398
+ const { [emoji]: _, ...rest } = prev;
399
+ return rest;
400
+ }
401
+ return { ...prev, [emoji]: next };
402
+ }
403
+ var PAGE_SIZE2 = 50;
404
+ function useThread(conversationId, rootMessageId) {
405
+ const chat = usePoolse();
406
+ const [replies, setReplies] = react.useState([]);
407
+ const [loading, setLoading] = react.useState(true);
408
+ const [error, setError] = react.useState(null);
409
+ const [hasMore, setHasMore] = react.useState(true);
410
+ const newestSequenceRef = react.useRef(null);
411
+ react.useEffect(() => {
412
+ let cancelled = false;
413
+ setLoading(true);
414
+ setReplies([]);
415
+ setError(null);
416
+ setHasMore(true);
417
+ newestSequenceRef.current = null;
418
+ chat.messages.one(rootMessageId).replies({ limit: PAGE_SIZE2 }).then((page) => {
419
+ if (cancelled) return;
420
+ setReplies(page.data);
421
+ setHasMore(page.data.length === PAGE_SIZE2);
422
+ newestSequenceRef.current = page.data[page.data.length - 1]?.sequence ?? null;
423
+ setLoading(false);
424
+ }).catch((err) => {
425
+ if (cancelled) return;
426
+ setError(err instanceof Error ? err : new Error(String(err)));
427
+ setLoading(false);
428
+ });
429
+ const conv = chat.realtime.conversation(conversationId);
430
+ const offNew = conv.onMessage((msg) => {
431
+ if (msg.thread_root_id !== rootMessageId) return;
432
+ setReplies((prev) => upsertById2(prev, msg));
433
+ });
434
+ const offUpdated = conv.onMessageUpdated((msg) => {
435
+ if (msg.thread_root_id !== rootMessageId) return;
436
+ setReplies((prev) => prev.map((m) => m.id === msg.id ? msg : m));
437
+ });
438
+ const offDeleted = conv.onMessageDeleted((evt) => {
439
+ setReplies(
440
+ (prev) => prev.map((m) => m.id === evt.id ? { ...m, deleted_at: evt.deleted_at, body: null } : m)
441
+ );
442
+ });
443
+ return () => {
444
+ cancelled = true;
445
+ offNew();
446
+ offUpdated();
447
+ offDeleted();
448
+ };
449
+ }, [chat, conversationId, rootMessageId]);
450
+ const loadMore = react.useCallback(async () => {
451
+ if (!hasMore || loading) return;
452
+ const after = newestSequenceRef.current;
453
+ if (after === null) return;
454
+ const page = await chat.messages.one(rootMessageId).replies({ limit: PAGE_SIZE2, after });
455
+ if (page.data.length === 0) {
456
+ setHasMore(false);
457
+ return;
458
+ }
459
+ setReplies((prev) => [...prev, ...page.data]);
460
+ setHasMore(page.data.length === PAGE_SIZE2);
461
+ newestSequenceRef.current = page.data[page.data.length - 1]?.sequence ?? after;
462
+ }, [chat, rootMessageId, hasMore, loading]);
463
+ const sendReply = react.useCallback(
464
+ async (attrs) => {
465
+ const id = attrs.id ?? crypto.randomUUID();
466
+ const tempMsg = {
467
+ id,
468
+ tenant_id: "",
469
+ conversation_id: conversationId,
470
+ sender_id: null,
471
+ type: attrs.type ?? "text",
472
+ body: attrs.body,
473
+ reply_to_id: rootMessageId,
474
+ thread_root_id: rootMessageId,
475
+ mentions: attrs.mentions ?? [],
476
+ reactions: {},
477
+ edited_at: null,
478
+ deleted_at: null,
479
+ sequence: Number.MAX_SAFE_INTEGER,
480
+ inserted_at: (/* @__PURE__ */ new Date()).toISOString(),
481
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
482
+ };
483
+ setReplies((prev) => [...prev, tempMsg]);
484
+ try {
485
+ const sent = await chat.conversations.one(conversationId).messages.send({
486
+ ...attrs,
487
+ id,
488
+ reply_to_id: rootMessageId
489
+ });
490
+ setReplies((prev) => upsertById2(prev, sent));
491
+ return sent;
492
+ } catch (err) {
493
+ setReplies((prev) => prev.filter((m) => m.id !== id));
494
+ throw err;
495
+ }
496
+ },
497
+ [chat, conversationId, rootMessageId]
498
+ );
499
+ return { replies, loading, error, hasMore, loadMore, sendReply };
500
+ }
501
+ function upsertById2(prev, msg) {
502
+ const idx = prev.findIndex((m) => m.id === msg.id);
503
+ if (idx === -1) return [...prev, msg];
504
+ const next = prev.slice();
505
+ next[idx] = msg;
506
+ return next;
507
+ }
508
+ function useTyping(conversationId) {
509
+ const chat = usePoolse();
510
+ const { me } = useMe();
511
+ const [typing, setTyping] = react.useState(/* @__PURE__ */ new Set());
512
+ const lastPingRef = react.useRef(0);
513
+ const meIdRef = react.useRef(me?.id ?? null);
514
+ meIdRef.current = me?.id ?? null;
515
+ react.useEffect(() => {
516
+ setTyping(/* @__PURE__ */ new Set());
517
+ const conv = chat.realtime.conversation(conversationId);
518
+ const offStart = conv.onTypingStart((evt) => {
519
+ if (meIdRef.current && evt.user_id === meIdRef.current) return;
520
+ setTyping((prev) => {
521
+ if (prev.has(evt.user_id)) return prev;
522
+ const next = new Set(prev);
523
+ next.add(evt.user_id);
524
+ return next;
525
+ });
526
+ });
527
+ const offStop = conv.onTypingStop((evt) => {
528
+ if (meIdRef.current && evt.user_id === meIdRef.current) return;
529
+ setTyping((prev) => {
530
+ if (!prev.has(evt.user_id)) return prev;
531
+ const next = new Set(prev);
532
+ next.delete(evt.user_id);
533
+ return next;
534
+ });
535
+ });
536
+ return () => {
537
+ offStart();
538
+ offStop();
539
+ };
540
+ }, [chat, conversationId]);
541
+ const signalTyping = react.useCallback(() => {
542
+ const now = Date.now();
543
+ if (now - lastPingRef.current < 500) return;
544
+ lastPingRef.current = now;
545
+ chat.realtime.conversation(conversationId).sendTyping();
546
+ }, [chat, conversationId]);
547
+ return { typing, signalTyping };
548
+ }
549
+ function usePresence(conversationId) {
550
+ const chat = usePoolse();
551
+ const [online, setOnline] = react.useState(/* @__PURE__ */ new Set());
552
+ react.useEffect(() => {
553
+ setOnline(/* @__PURE__ */ new Set());
554
+ const conv = chat.realtime.conversation(conversationId);
555
+ const applyState = (state) => {
556
+ setOnline(new Set(Object.keys(state)));
557
+ };
558
+ const applyDiff = (diff) => {
559
+ setOnline((prev) => {
560
+ const next = new Set(prev);
561
+ if (diff.joins) Object.keys(diff.joins).forEach((id) => next.add(id));
562
+ if (diff.leaves) Object.keys(diff.leaves).forEach((id) => next.delete(id));
563
+ return next;
564
+ });
565
+ };
566
+ const offState = conv.onPresenceState(applyState);
567
+ const offDiff = conv.onPresenceDiff(applyDiff);
568
+ return () => {
569
+ offState();
570
+ offDiff();
571
+ };
572
+ }, [chat, conversationId]);
573
+ return { online };
574
+ }
575
+ function useRealtimeStatus() {
576
+ const chat = usePoolse();
577
+ const [status, setStatus] = react.useState(chat.realtime.getStatus());
578
+ react.useEffect(() => {
579
+ setStatus(chat.realtime.getStatus());
580
+ return chat.realtime.onStatus(setStatus);
581
+ }, [chat]);
582
+ return status;
583
+ }
584
+ function useAttachmentUpload() {
585
+ const chat = usePoolse();
586
+ const [attachment, setAttachment] = react.useState(null);
587
+ const [uploading, setUploading] = react.useState(false);
588
+ const [error, setError] = react.useState(null);
589
+ const inFlightRef = react.useRef(null);
590
+ const upload = react.useCallback(
591
+ async (input) => {
592
+ inFlightRef.current?.abort();
593
+ const controller = new AbortController();
594
+ inFlightRef.current = controller;
595
+ setUploading(true);
596
+ setError(null);
597
+ try {
598
+ const att = await chat.attachments.upload(input, { signal: controller.signal });
599
+ if (inFlightRef.current === controller) {
600
+ setAttachment(att);
601
+ }
602
+ return att;
603
+ } catch (err) {
604
+ if (err?.name === "AbortError") throw err;
605
+ const e = err instanceof Error ? err : new Error(String(err));
606
+ setError(e);
607
+ throw e;
608
+ } finally {
609
+ if (inFlightRef.current === controller) {
610
+ setUploading(false);
611
+ inFlightRef.current = null;
612
+ }
613
+ }
614
+ },
615
+ [chat]
616
+ );
617
+ const reset = react.useCallback(() => {
618
+ inFlightRef.current?.abort();
619
+ inFlightRef.current = null;
620
+ setAttachment(null);
621
+ setError(null);
622
+ setUploading(false);
623
+ }, []);
624
+ return { attachment, uploading, error, upload, reset };
625
+ }
626
+ function useAttachmentUrl(attachmentId) {
627
+ const chat = usePoolse();
628
+ const [url, setUrl] = react.useState(null);
629
+ const [loading, setLoading] = react.useState(attachmentId !== null);
630
+ const [error, setError] = react.useState(null);
631
+ react.useEffect(() => {
632
+ if (attachmentId === null) {
633
+ setUrl(null);
634
+ setLoading(false);
635
+ setError(null);
636
+ return;
637
+ }
638
+ let cancelled = false;
639
+ const controller = new AbortController();
640
+ setLoading(true);
641
+ setError(null);
642
+ chat.attachments.one(attachmentId).downloadUrl({ signal: controller.signal }).then((res) => {
643
+ if (cancelled) return;
644
+ setUrl(res.url);
645
+ setLoading(false);
646
+ }).catch((err) => {
647
+ if (cancelled) return;
648
+ if (err?.name === "AbortError") return;
649
+ setError(err instanceof Error ? err : new Error(String(err)));
650
+ setLoading(false);
651
+ });
652
+ return () => {
653
+ cancelled = true;
654
+ controller.abort();
655
+ };
656
+ }, [chat, attachmentId]);
657
+ return { url, loading, error };
658
+ }
659
+
660
+ Object.defineProperty(exports, "ApiError", {
661
+ enumerable: true,
662
+ get: function () { return sdk.ApiError; }
663
+ });
664
+ Object.defineProperty(exports, "AuthError", {
665
+ enumerable: true,
666
+ get: function () { return sdk.AuthError; }
667
+ });
668
+ Object.defineProperty(exports, "NetworkError", {
669
+ enumerable: true,
670
+ get: function () { return sdk.NetworkError; }
671
+ });
672
+ Object.defineProperty(exports, "PoolseError", {
673
+ enumerable: true,
674
+ get: function () { return sdk.PoolseError; }
675
+ });
676
+ Object.defineProperty(exports, "RateLimitedError", {
677
+ enumerable: true,
678
+ get: function () { return sdk.RateLimitedError; }
679
+ });
680
+ exports.PoolseProvider = PoolseProvider;
681
+ exports.useAttachmentUpload = useAttachmentUpload;
682
+ exports.useAttachmentUrl = useAttachmentUrl;
683
+ exports.useConversation = useConversation;
684
+ exports.useConversations = useConversations;
685
+ exports.useMe = useMe;
686
+ exports.useMembers = useMembers;
687
+ exports.useMessages = useMessages;
688
+ exports.usePoolse = usePoolse;
689
+ exports.usePresence = usePresence;
690
+ exports.useReactions = useReactions;
691
+ exports.useRealtimeStatus = useRealtimeStatus;
692
+ exports.useThread = useThread;
693
+ exports.useTyping = useTyping;
694
+ //# sourceMappingURL=index.cjs.map
695
+ //# sourceMappingURL=index.cjs.map