@liveblocks/react 1.2.2-comments3 → 1.2.2-comments5

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.mjs CHANGED
@@ -1,9 +1,11 @@
1
+ "use client";
2
+
1
3
  // src/index.ts
2
4
  import { detectDupes } from "@liveblocks/core";
3
5
 
4
6
  // src/version.ts
5
7
  var PKG_NAME = "@liveblocks/react";
6
- var PKG_VERSION = "1.2.2-comments3";
8
+ var PKG_VERSION = "1.2.2-comments5";
7
9
  var PKG_FORMAT = "esm";
8
10
 
9
11
  // src/ClientSideSuspense.tsx
@@ -20,14 +22,458 @@ function ClientSideSuspense(props) {
20
22
  import { shallow } from "@liveblocks/client";
21
23
  import {
22
24
  asArrayWithLegacyMethods,
25
+ createAsyncCache,
23
26
  deprecateIf,
24
- errorIf
27
+ errorIf,
28
+ makeEventSource as makeEventSource2
25
29
  } from "@liveblocks/core";
26
30
  import * as React2 from "react";
27
31
  import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
28
32
 
33
+ // src/comments/CommentsRoom.ts
34
+ import { makePoller } from "@liveblocks/core";
35
+ import { nanoid } from "nanoid";
36
+ import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
37
+
38
+ // src/comments/errors.ts
39
+ var CreateThreadError = class extends Error {
40
+ constructor(cause, context) {
41
+ super("Create thread failed.");
42
+ this.cause = cause;
43
+ this.context = context;
44
+ this.name = "CreateThreadError";
45
+ }
46
+ };
47
+ var EditThreadMetadataError = class extends Error {
48
+ constructor(cause, context) {
49
+ super("Edit thread metadata failed.");
50
+ this.cause = cause;
51
+ this.context = context;
52
+ this.name = "EditThreadMetadataError";
53
+ }
54
+ };
55
+ var CreateCommentError = class extends Error {
56
+ constructor(cause, context) {
57
+ super("Create comment failed.");
58
+ this.cause = cause;
59
+ this.context = context;
60
+ this.name = "CreateCommentError";
61
+ }
62
+ };
63
+ var EditCommentError = class extends Error {
64
+ constructor(cause, context) {
65
+ super("Edit comment failed.");
66
+ this.cause = cause;
67
+ this.context = context;
68
+ this.name = "EditCommentError";
69
+ }
70
+ };
71
+ var DeleteCommentError = class extends Error {
72
+ constructor(cause, context) {
73
+ super("Delete comment failed.");
74
+ this.cause = cause;
75
+ this.context = context;
76
+ this.name = "DeleteCommentError";
77
+ }
78
+ };
79
+
80
+ // src/comments/lib/store.ts
81
+ import { makeEventSource } from "@liveblocks/core";
82
+ function createStore(initialState) {
83
+ let state = initialState;
84
+ const eventSource = makeEventSource();
85
+ return {
86
+ get() {
87
+ return state;
88
+ },
89
+ set(newState) {
90
+ state = newState;
91
+ eventSource.notify(state);
92
+ },
93
+ subscribe(callback) {
94
+ return eventSource.subscribe(callback);
95
+ },
96
+ subscribeOnce(callback) {
97
+ return eventSource.subscribeOnce(callback);
98
+ },
99
+ subscribersCount() {
100
+ return eventSource.count();
101
+ },
102
+ destroy() {
103
+ return eventSource.clear();
104
+ }
105
+ };
106
+ }
107
+
108
+ // src/comments/CommentsRoom.ts
109
+ var POLLING_INTERVAL_REALTIME = 3e4;
110
+ var POLLING_INTERVAL = 5e3;
111
+ var THREAD_ID_PREFIX = "th";
112
+ var COMMENT_ID_PREFIX = "cm";
113
+ function createOptimisticId(prefix) {
114
+ return `${prefix}_${nanoid()}`;
115
+ }
116
+ function createCommentsRoom(room, errorEventSource) {
117
+ const store = createStore({
118
+ isLoading: true
119
+ });
120
+ let numberOfMutations = 0;
121
+ function endMutation() {
122
+ numberOfMutations--;
123
+ if (numberOfMutations === 0) {
124
+ revalidateThreads();
125
+ }
126
+ }
127
+ function startMutation() {
128
+ pollingHub.threads.stop();
129
+ numberOfMutations++;
130
+ }
131
+ const pollingHub = {
132
+ // TODO: If there's an error, it will currently infinitely retry at the current polling rate → add retry logic
133
+ threads: makePoller(revalidateThreads)
134
+ };
135
+ let unsubscribeRealtimeEvents;
136
+ let unsubscribeRealtimeConnection;
137
+ let realtimeClientConnected = false;
138
+ function getPollingInterval() {
139
+ return realtimeClientConnected ? POLLING_INTERVAL_REALTIME : POLLING_INTERVAL;
140
+ }
141
+ function ensureThreadsAreLoadedForMutations() {
142
+ const state = store.get();
143
+ if (state.isLoading || state.error) {
144
+ throw new Error(
145
+ "Cannot update threads or comments before they are loaded"
146
+ );
147
+ }
148
+ return state.threads;
149
+ }
150
+ async function revalidateThreads() {
151
+ pollingHub.threads.pause();
152
+ if (numberOfMutations === 0) {
153
+ setThreads(await room.getThreads());
154
+ }
155
+ pollingHub.threads.resume();
156
+ }
157
+ function subscribe() {
158
+ if (!unsubscribeRealtimeEvents) {
159
+ unsubscribeRealtimeEvents = room.events.comments.subscribe(() => {
160
+ pollingHub.threads.restart(getPollingInterval());
161
+ revalidateThreads();
162
+ });
163
+ }
164
+ if (!unsubscribeRealtimeConnection) {
165
+ unsubscribeRealtimeConnection = room.events.status.subscribe((status) => {
166
+ const nextRealtimeClientConnected = status === "connected";
167
+ if (nextRealtimeClientConnected !== realtimeClientConnected) {
168
+ realtimeClientConnected = nextRealtimeClientConnected;
169
+ pollingHub.threads.restart(getPollingInterval());
170
+ }
171
+ });
172
+ }
173
+ pollingHub.threads.start(getPollingInterval());
174
+ revalidateThreads();
175
+ return () => {
176
+ pollingHub.threads.stop();
177
+ unsubscribeRealtimeEvents?.();
178
+ unsubscribeRealtimeEvents = void 0;
179
+ unsubscribeRealtimeConnection?.();
180
+ unsubscribeRealtimeConnection = void 0;
181
+ };
182
+ }
183
+ function setThreads(newThreads) {
184
+ store.set({
185
+ threads: newThreads,
186
+ isLoading: false
187
+ });
188
+ }
189
+ function getCurrentUserId() {
190
+ const self = room.getSelf();
191
+ if (self === null || self.id === void 0) {
192
+ return "anonymous";
193
+ } else {
194
+ return self.id;
195
+ }
196
+ }
197
+ function createThread(options) {
198
+ const body = options.body;
199
+ const metadata = "metadata" in options ? options.metadata : {};
200
+ const threads = ensureThreadsAreLoadedForMutations();
201
+ const threadId = createOptimisticId(THREAD_ID_PREFIX);
202
+ const commentId = createOptimisticId(COMMENT_ID_PREFIX);
203
+ const now = (/* @__PURE__ */ new Date()).toISOString();
204
+ const newThread = {
205
+ id: threadId,
206
+ type: "thread",
207
+ createdAt: now,
208
+ roomId: room.id,
209
+ metadata,
210
+ comments: [
211
+ {
212
+ id: commentId,
213
+ createdAt: now,
214
+ type: "comment",
215
+ userId: getCurrentUserId(),
216
+ body
217
+ }
218
+ ]
219
+ };
220
+ setThreads([...threads, newThread]);
221
+ startMutation();
222
+ room.createThread({ threadId, commentId, body, metadata }).catch(
223
+ (er) => errorEventSource.notify(
224
+ new CreateThreadError(er, {
225
+ roomId: room.id,
226
+ threadId,
227
+ commentId,
228
+ body,
229
+ metadata
230
+ })
231
+ )
232
+ ).finally(endMutation);
233
+ return newThread;
234
+ }
235
+ function editThreadMetadata(options) {
236
+ const threadId = options.threadId;
237
+ const metadata = "metadata" in options ? options.metadata : {};
238
+ const threads = ensureThreadsAreLoadedForMutations();
239
+ setThreads(
240
+ threads.map(
241
+ (thread) => thread.id === threadId ? {
242
+ ...thread,
243
+ metadata: {
244
+ ...thread.metadata,
245
+ ...metadata
246
+ }
247
+ } : thread
248
+ )
249
+ );
250
+ startMutation();
251
+ room.editThreadMetadata({ metadata, threadId }).catch(
252
+ (er) => errorEventSource.notify(
253
+ new EditThreadMetadataError(er, {
254
+ roomId: room.id,
255
+ threadId,
256
+ metadata
257
+ })
258
+ )
259
+ ).finally(endMutation);
260
+ }
261
+ function createComment({
262
+ threadId,
263
+ body
264
+ }) {
265
+ const threads = ensureThreadsAreLoadedForMutations();
266
+ const commentId = createOptimisticId(COMMENT_ID_PREFIX);
267
+ const now = (/* @__PURE__ */ new Date()).toISOString();
268
+ const comment = {
269
+ id: commentId,
270
+ threadId,
271
+ roomId: room.id,
272
+ type: "comment",
273
+ createdAt: now,
274
+ userId: getCurrentUserId(),
275
+ body
276
+ };
277
+ setThreads(
278
+ threads.map(
279
+ (thread) => thread.id === threadId ? {
280
+ ...thread,
281
+ comments: [...thread.comments, comment]
282
+ } : thread
283
+ )
284
+ );
285
+ startMutation();
286
+ room.createComment({ threadId, commentId, body }).catch(
287
+ (er) => errorEventSource.notify(
288
+ new CreateCommentError(er, {
289
+ roomId: room.id,
290
+ threadId,
291
+ commentId,
292
+ body
293
+ })
294
+ )
295
+ ).finally(endMutation);
296
+ return comment;
297
+ }
298
+ function editComment({ threadId, commentId, body }) {
299
+ const threads = ensureThreadsAreLoadedForMutations();
300
+ const now = (/* @__PURE__ */ new Date()).toISOString();
301
+ setThreads(
302
+ threads.map(
303
+ (thread) => thread.id === threadId ? {
304
+ ...thread,
305
+ comments: thread.comments.map(
306
+ (comment) => comment.id === commentId ? {
307
+ ...comment,
308
+ editedAt: now,
309
+ body
310
+ } : comment
311
+ )
312
+ } : thread
313
+ )
314
+ );
315
+ startMutation();
316
+ room.editComment({ threadId, commentId, body }).catch(
317
+ (er) => errorEventSource.notify(
318
+ new EditCommentError(er, {
319
+ roomId: room.id,
320
+ threadId,
321
+ commentId,
322
+ body
323
+ })
324
+ )
325
+ ).finally(endMutation);
326
+ }
327
+ function deleteComment({ threadId, commentId }) {
328
+ const threads = ensureThreadsAreLoadedForMutations();
329
+ const now = (/* @__PURE__ */ new Date()).toISOString();
330
+ const newThreads = [];
331
+ for (const thread of threads) {
332
+ if (thread.id === threadId) {
333
+ const newThread = {
334
+ ...thread,
335
+ comments: thread.comments.map(
336
+ (comment) => comment.id === commentId ? {
337
+ ...comment,
338
+ deletedAt: now,
339
+ body: void 0
340
+ } : comment
341
+ )
342
+ };
343
+ if (newThread.comments.some((comment) => comment.deletedAt === void 0)) {
344
+ newThreads.push(newThread);
345
+ }
346
+ } else {
347
+ newThreads.push(thread);
348
+ }
349
+ }
350
+ setThreads(newThreads);
351
+ startMutation();
352
+ room.deleteComment({ threadId, commentId }).catch(
353
+ (er) => errorEventSource.notify(
354
+ new DeleteCommentError(er, {
355
+ roomId: room.id,
356
+ threadId,
357
+ commentId
358
+ })
359
+ )
360
+ ).finally(endMutation);
361
+ }
362
+ function useThreads() {
363
+ return useSyncExternalStore(
364
+ store.subscribe,
365
+ store.get,
366
+ store.get
367
+ );
368
+ }
369
+ function useThreadsSuspense() {
370
+ const result = useThreads();
371
+ if (result.isLoading) {
372
+ throw new Promise(store.subscribeOnce);
373
+ }
374
+ if (result.error) {
375
+ throw result.error;
376
+ }
377
+ return result.threads;
378
+ }
379
+ return {
380
+ useThreads,
381
+ useThreadsSuspense,
382
+ createThread,
383
+ editThreadMetadata,
384
+ createComment,
385
+ editComment,
386
+ deleteComment,
387
+ subscribe
388
+ };
389
+ }
390
+
391
+ // src/comments/lib/use-async-cache.ts
392
+ import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
393
+ import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
394
+
395
+ // src/comments/lib/use-initial.ts
396
+ import { useRef } from "react";
397
+ function useInitial(value) {
398
+ return useRef(value instanceof Function ? value() : value).current;
399
+ }
400
+
401
+ // src/comments/lib/use-async-cache.ts
402
+ var INITIAL_ASYNC_STATE = {
403
+ isLoading: false,
404
+ data: void 0,
405
+ error: void 0
406
+ };
407
+ var noop = () => {
408
+ };
409
+ function useAsyncCache(cache, key, options) {
410
+ const frozenOptions = useInitial(options);
411
+ const cacheItem = useMemo(() => {
412
+ if (key === null || !cache) {
413
+ return null;
414
+ }
415
+ const cacheItem2 = cache.create(key, frozenOptions?.overrideFunction);
416
+ void cacheItem2.get();
417
+ return cacheItem2;
418
+ }, [cache, frozenOptions, key]);
419
+ const subscribe = useCallback(
420
+ (callback) => cacheItem?.subscribe(callback) ?? noop,
421
+ [cacheItem]
422
+ );
423
+ const getState = useCallback(
424
+ () => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
425
+ [cacheItem]
426
+ );
427
+ const revalidate = useCallback(() => cacheItem?.revalidate(), [cacheItem]);
428
+ const state = useSyncExternalStore2(subscribe, getState, getState);
429
+ const previousData = useRef2();
430
+ let data = state.data;
431
+ useEffect2(() => {
432
+ previousData.current = { key, data: state.data };
433
+ }, [key, state]);
434
+ if (frozenOptions?.suspense && state.isLoading && cacheItem) {
435
+ throw new Promise((resolve) => {
436
+ cacheItem.subscribeOnce(() => resolve());
437
+ });
438
+ }
439
+ if (state.isLoading && frozenOptions?.keepPreviousDataWhileLoading && typeof state.data === "undefined" && previousData.current?.key !== key && typeof previousData.current?.data !== "undefined") {
440
+ data = previousData.current.data;
441
+ }
442
+ return {
443
+ isLoading: state.isLoading,
444
+ data,
445
+ error: state.error,
446
+ getState,
447
+ revalidate
448
+ };
449
+ }
450
+
451
+ // src/comments/lib/use-debounce.ts
452
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
453
+ var DEFAULT_DELAY = 500;
454
+ function useDebounce(value, delay = DEFAULT_DELAY) {
455
+ const timeout = useRef3();
456
+ const [debouncedValue, setDebouncedValue] = useState2(value);
457
+ useEffect3(() => {
458
+ if (delay === false) {
459
+ return;
460
+ }
461
+ if (timeout.current === void 0) {
462
+ setDebouncedValue(value);
463
+ }
464
+ timeout.current = window.setTimeout(() => {
465
+ setDebouncedValue(value);
466
+ timeout.current = void 0;
467
+ }, delay);
468
+ return () => {
469
+ window.clearTimeout(timeout.current);
470
+ };
471
+ }, [value, delay]);
472
+ return debouncedValue;
473
+ }
474
+
29
475
  // src/hooks.ts
30
- import { useReducer, useRef } from "react";
476
+ import { useReducer, useRef as useRef4 } from "react";
31
477
  function useRerender() {
32
478
  const [, update] = useReducer(
33
479
  // This implementation works by incrementing a hidden counter value that is
@@ -38,12 +484,12 @@ function useRerender() {
38
484
  );
39
485
  return update;
40
486
  }
41
- function useInitial(value) {
42
- return useRef(value).current;
487
+ function useInitial2(value) {
488
+ return useRef4(value).current;
43
489
  }
44
490
 
45
491
  // src/factory.tsx
46
- var noop = () => {
492
+ var noop2 = () => {
47
493
  };
48
494
  var identity = (x) => x;
49
495
  var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\u2019re using React ${reactVersion}. Please pass unstable_batchedUpdates at the RoomProvider level until you\u2019re ready to upgrade to React 18:
@@ -58,7 +504,7 @@ var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\
58
504
 
59
505
  Why? Please see https://liveblocks.io/docs/guides/troubleshooting#stale-props-zombie-child for more information`;
60
506
  var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
61
- function useSyncExternalStore(s, gs, gss) {
507
+ function useSyncExternalStore3(s, gs, gss) {
62
508
  return useSyncExternalStoreWithSelector(s, gs, gss, identity);
63
509
  }
64
510
  var EMPTY_OTHERS = (
@@ -95,7 +541,20 @@ function makeMutationContext(room) {
95
541
  setMyPresence: room.updatePresence
96
542
  };
97
543
  }
98
- function createRoomContext(client) {
544
+ var hasWarnedIfNoResolveUser = false;
545
+ function warnIfNoResolveUser(usersCache) {
546
+ if (!hasWarnedIfNoResolveUser && !usersCache && process.env.NODE_ENV !== "production") {
547
+ console.warn(
548
+ "Set the resolveUser option in createRoomContext to specify user info."
549
+ );
550
+ hasWarnedIfNoResolveUser = true;
551
+ }
552
+ }
553
+ var ContextBundle = React2.createContext(null);
554
+ function useRoomContextBundle() {
555
+ return React2.useContext(ContextBundle);
556
+ }
557
+ function createRoomContext(client, options) {
99
558
  const RoomContext = React2.createContext(null);
100
559
  function RoomProvider(props) {
101
560
  const {
@@ -125,7 +584,7 @@ function createRoomContext(client) {
125
584
  superfluous_unstable_batchedUpdates
126
585
  );
127
586
  }
128
- const frozen = useInitial({
587
+ const frozen = useInitial2({
129
588
  initialPresence,
130
589
  initialStorage,
131
590
  unstable_batchedUpdates,
@@ -140,19 +599,24 @@ function createRoomContext(client) {
140
599
  })
141
600
  );
142
601
  React2.useEffect(() => {
143
- setRoom(
144
- client.enter(roomId, {
602
+ const room2 = client.enter(
603
+ roomId,
604
+ {
145
605
  initialPresence: frozen.initialPresence,
146
606
  initialStorage: frozen.initialStorage,
147
607
  shouldInitiallyConnect: frozen.shouldInitiallyConnect,
148
608
  unstable_batchedUpdates: frozen.unstable_batchedUpdates
149
- })
609
+ }
150
610
  );
611
+ setRoom(room2);
612
+ const unsubscribe = getCommentsRoom(room2).subscribe();
151
613
  return () => {
614
+ unsubscribe();
615
+ commentsRooms.delete(room2.id);
152
616
  client.leave(roomId);
153
617
  };
154
618
  }, [roomId, frozen]);
155
- return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, props.children);
619
+ return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React2.createElement(ContextBundle.Provider, { value: bundle }, props.children));
156
620
  }
157
621
  function connectionIdSelector(others) {
158
622
  return others.map((user) => user.connectionId);
@@ -160,7 +624,7 @@ function createRoomContext(client) {
160
624
  function useRoom() {
161
625
  const room = React2.useContext(RoomContext);
162
626
  if (room === null) {
163
- throw new Error("RoomProvider is missing from the react tree");
627
+ throw new Error("RoomProvider is missing from the React tree.");
164
628
  }
165
629
  return room;
166
630
  }
@@ -168,13 +632,13 @@ function createRoomContext(client) {
168
632
  const room = useRoom();
169
633
  const subscribe = room.events.status.subscribe;
170
634
  const getSnapshot = room.getStatus;
171
- return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
635
+ return useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
172
636
  }
173
637
  function useMyPresence() {
174
638
  const room = useRoom();
175
639
  const subscribe = room.events.myPresence.subscribe;
176
640
  const getSnapshot = room.getPresence;
177
- const presence = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
641
+ const presence = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
178
642
  const setPresence = room.updatePresence;
179
643
  return [presence, setPresence];
180
644
  }
@@ -248,8 +712,8 @@ function createRoomContext(client) {
248
712
  function useBroadcastEvent() {
249
713
  const room = useRoom();
250
714
  return React2.useCallback(
251
- (event, options = { shouldQueueEventIfNotReady: false }) => {
252
- room.broadcastEvent(event, options);
715
+ (event, options2 = { shouldQueueEventIfNotReady: false }) => {
716
+ room.broadcastEvent(event, options2);
253
717
  },
254
718
  [room]
255
719
  );
@@ -314,7 +778,7 @@ function createRoomContext(client) {
314
778
  const subscribe = room.events.storageDidLoad.subscribeOnce;
315
779
  const getSnapshot = room.getStorageSnapshot;
316
780
  const getServerSnapshot = React2.useCallback(() => null, []);
317
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
781
+ return useSyncExternalStore3(subscribe, getSnapshot, getServerSnapshot);
318
782
  }
319
783
  function useStorageRoot() {
320
784
  return [useMutableStorageRoot()];
@@ -332,13 +796,13 @@ function createRoomContext(client) {
332
796
  const room = useRoom();
333
797
  const subscribe = room.events.history.subscribe;
334
798
  const canUndo = room.history.canUndo;
335
- return useSyncExternalStore(subscribe, canUndo, canUndo);
799
+ return useSyncExternalStore3(subscribe, canUndo, canUndo);
336
800
  }
337
801
  function useCanRedo() {
338
802
  const room = useRoom();
339
803
  const subscribe = room.events.history.subscribe;
340
804
  const canRedo = room.history.canRedo;
341
- return useSyncExternalStore(subscribe, canRedo, canRedo);
805
+ return useSyncExternalStore3(subscribe, canRedo, canRedo);
342
806
  }
343
807
  function useBatch() {
344
808
  return useRoom().batch;
@@ -395,7 +859,7 @@ function createRoomContext(client) {
395
859
  [selector]
396
860
  );
397
861
  const subscribe = React2.useCallback(
398
- (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop,
862
+ (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop2,
399
863
  [room, rootOrNull]
400
864
  );
401
865
  const getSnapshot = React2.useCallback(() => {
@@ -501,7 +965,119 @@ function createRoomContext(client) {
501
965
  useSuspendUntilStorageLoaded();
502
966
  return useLegacyKey(key);
503
967
  }
504
- return {
968
+ const errorEventSource = makeEventSource2();
969
+ const commentsRooms = /* @__PURE__ */ new Map();
970
+ function getCommentsRoom(room) {
971
+ let commentsRoom = commentsRooms.get(room.id);
972
+ if (commentsRoom === void 0) {
973
+ commentsRoom = createCommentsRoom(room, errorEventSource);
974
+ commentsRooms.set(room.id, commentsRoom);
975
+ }
976
+ return commentsRoom;
977
+ }
978
+ function useThreads() {
979
+ const room = useRoom();
980
+ return getCommentsRoom(room).useThreads();
981
+ }
982
+ function useThreadsSuspense() {
983
+ const room = useRoom();
984
+ return getCommentsRoom(room).useThreadsSuspense();
985
+ }
986
+ function useCreateThread() {
987
+ const room = useRoom();
988
+ return React2.useCallback(
989
+ (options2) => getCommentsRoom(room).createThread(options2),
990
+ [room]
991
+ );
992
+ }
993
+ function useEditThreadMetadata() {
994
+ const room = useRoom();
995
+ return React2.useCallback(
996
+ (options2) => getCommentsRoom(room).editThreadMetadata(options2),
997
+ [room]
998
+ );
999
+ }
1000
+ function useCreateComment() {
1001
+ const room = useRoom();
1002
+ return React2.useCallback(
1003
+ (options2) => getCommentsRoom(room).createComment(options2),
1004
+ [room]
1005
+ );
1006
+ }
1007
+ function useEditComment() {
1008
+ const room = useRoom();
1009
+ return React2.useCallback(
1010
+ (options2) => getCommentsRoom(room).editComment(options2),
1011
+ [room]
1012
+ );
1013
+ }
1014
+ function useDeleteComment() {
1015
+ const room = useRoom();
1016
+ return React2.useCallback(
1017
+ (options2) => getCommentsRoom(room).deleteComment(options2),
1018
+ [room]
1019
+ );
1020
+ }
1021
+ const { resolveUser, resolveMentionSuggestions } = options ?? {};
1022
+ const usersCache = resolveUser ? createAsyncCache((stringifiedOptions) => {
1023
+ return resolveUser(
1024
+ JSON.parse(stringifiedOptions)
1025
+ );
1026
+ }) : void 0;
1027
+ function useUser(userId) {
1028
+ const resolverKey = React2.useMemo(
1029
+ () => JSON.stringify({ userId }),
1030
+ [userId]
1031
+ );
1032
+ const state = useAsyncCache(usersCache, resolverKey);
1033
+ React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
1034
+ if (state.isLoading) {
1035
+ return {
1036
+ isLoading: true
1037
+ };
1038
+ } else {
1039
+ return {
1040
+ user: state.data,
1041
+ error: state.error,
1042
+ isLoading: false
1043
+ };
1044
+ }
1045
+ }
1046
+ function useUserSuspense(userId) {
1047
+ const resolverKey = React2.useMemo(
1048
+ () => JSON.stringify({ userId }),
1049
+ [userId]
1050
+ );
1051
+ const state = useAsyncCache(usersCache, resolverKey, {
1052
+ suspense: true
1053
+ });
1054
+ React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
1055
+ return {
1056
+ user: state.data,
1057
+ error: state.error,
1058
+ isLoading: false
1059
+ };
1060
+ }
1061
+ const mentionSuggestionsCache = createAsyncCache(
1062
+ resolveMentionSuggestions ? (stringifiedOptions) => {
1063
+ return resolveMentionSuggestions(
1064
+ JSON.parse(stringifiedOptions)
1065
+ );
1066
+ } : () => Promise.resolve([])
1067
+ );
1068
+ function useMentionSuggestions(search) {
1069
+ const room = useRoom();
1070
+ const debouncedSearch = useDebounce(search, 500);
1071
+ const resolverKey = React2.useMemo(
1072
+ () => debouncedSearch !== void 0 ? JSON.stringify({ text: debouncedSearch, roomId: room.id }) : null,
1073
+ [debouncedSearch, room.id]
1074
+ );
1075
+ const { data } = useAsyncCache(mentionSuggestionsCache, resolverKey, {
1076
+ keepPreviousDataWhileLoading: true
1077
+ });
1078
+ return data;
1079
+ }
1080
+ const bundle = {
505
1081
  RoomContext,
506
1082
  RoomProvider,
507
1083
  useRoom,
@@ -530,6 +1106,14 @@ function createRoomContext(client) {
530
1106
  useOthersConnectionIds,
531
1107
  useOther,
532
1108
  useMutation,
1109
+ useThreads,
1110
+ useUser,
1111
+ useCreateThread,
1112
+ useEditThreadMetadata,
1113
+ useCreateComment,
1114
+ useEditComment,
1115
+ useDeleteComment,
1116
+ useMentionSuggestions,
533
1117
  suspense: {
534
1118
  RoomContext,
535
1119
  RoomProvider,
@@ -558,9 +1142,17 @@ function createRoomContext(client) {
558
1142
  useOthersMapped: useOthersMappedSuspense,
559
1143
  useOthersConnectionIds: useOthersConnectionIdsSuspense,
560
1144
  useOther: useOtherSuspense,
561
- useMutation
1145
+ useMutation,
1146
+ useThreads: useThreadsSuspense,
1147
+ useUser: useUserSuspense,
1148
+ useCreateThread,
1149
+ useEditThreadMetadata,
1150
+ useCreateComment,
1151
+ useEditComment,
1152
+ useDeleteComment
562
1153
  }
563
1154
  };
1155
+ return bundle;
564
1156
  }
565
1157
 
566
1158
  // src/index.ts
@@ -569,6 +1161,7 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
569
1161
  export {
570
1162
  ClientSideSuspense,
571
1163
  createRoomContext,
572
- shallow2 as shallow
1164
+ shallow2 as shallow,
1165
+ useRoomContextBundle
573
1166
  };
574
1167
  //# sourceMappingURL=index.mjs.map