@liveblocks/react 1.8.1 → 1.8.3-oss1

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
@@ -5,7 +5,7 @@ import { detectDupes } from "@liveblocks/core";
5
5
 
6
6
  // src/version.ts
7
7
  var PKG_NAME = "@liveblocks/react";
8
- var PKG_VERSION = "1.8.1";
8
+ var PKG_VERSION = "1.8.3-oss1";
9
9
  var PKG_FORMAT = "esm";
10
10
 
11
11
  // src/ClientSideSuspense.tsx
@@ -32,10 +32,10 @@ import * as React2 from "react";
32
32
  import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
33
33
 
34
34
  // src/comments/CommentsRoom.ts
35
- import { CommentsApiError, console as console2, makeEventSource } from "@liveblocks/core";
35
+ import { CommentsApiError, console as console2 } from "@liveblocks/core";
36
36
  import { nanoid } from "nanoid";
37
- import { useEffect as useEffect2 } from "react";
38
- import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
37
+ import { useCallback as useCallback3, useEffect as useEffect3 } from "react";
38
+ import { useSyncExternalStore as useSyncExternalStore3 } from "use-sync-external-store/shim/index.js";
39
39
 
40
40
  // src/comments/errors.ts
41
41
  var CreateThreadError = class extends Error {
@@ -95,18 +95,245 @@ var RemoveReactionError = class extends Error {
95
95
  }
96
96
  };
97
97
 
98
- // src/comments/CommentsRoom.ts
99
- var POLLING_INTERVAL_REALTIME = 3e4;
100
- var POLLING_INTERVAL = 5e3;
101
- var MAX_ERROR_RETRY_COUNT = 5;
102
- var ERROR_RETRY_INTERVAL = 5e3;
103
- var THREAD_ID_PREFIX = "th";
104
- var COMMENT_ID_PREFIX = "cm";
105
- var DEDUPING_INTERVAL = 1e3;
106
- function createOptimisticId(prefix) {
107
- return `${prefix}_${nanoid()}`;
98
+ // src/comments/lib/revalidation.ts
99
+ import { makeEventSource } from "@liveblocks/core";
100
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
101
+
102
+ // src/comments/lib/use-is-document-visible.ts
103
+ import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
104
+ function useIsDocumentVisible() {
105
+ const isVisible = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
106
+ return isVisible;
107
+ }
108
+ function subscribe(onStoreChange) {
109
+ document.addEventListener("visibilitychange", onStoreChange);
110
+ return () => {
111
+ document.removeEventListener("visibilitychange", onStoreChange);
112
+ };
113
+ }
114
+ function getSnapshot() {
115
+ const isDocumentDefined = typeof document !== "undefined";
116
+ return isDocumentDefined ? document.visibilityState === "visible" : true;
117
+ }
118
+
119
+ // src/comments/lib/use-is-online.ts
120
+ import { useCallback, useRef } from "react";
121
+ import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
122
+ function useIsOnline() {
123
+ const isOnlineRef = useRef(true);
124
+ const subscribe2 = useCallback((onStoreChange) => {
125
+ function handleIsOnline() {
126
+ isOnlineRef.current = true;
127
+ onStoreChange();
128
+ }
129
+ function handleIsOffline() {
130
+ isOnlineRef.current = false;
131
+ onStoreChange();
132
+ }
133
+ window.addEventListener("online", handleIsOnline);
134
+ window.addEventListener("offline", handleIsOffline);
135
+ return () => {
136
+ window.removeEventListener("online", handleIsOnline);
137
+ window.removeEventListener("offline", handleIsOffline);
138
+ };
139
+ }, []);
140
+ const getSnapshot2 = useCallback(() => {
141
+ return isOnlineRef.current;
142
+ }, []);
143
+ const isOnline = useSyncExternalStore2(subscribe2, getSnapshot2, getSnapshot2);
144
+ return isOnline;
145
+ }
146
+
147
+ // src/comments/lib/revalidation.ts
148
+ var DEFAULT_ERROR_RETRY_INTERVAL = 5e3;
149
+ var DEFAULT_MAX_ERROR_RETRY_COUNT = 5;
150
+ var DEFAULT_DEDUPING_INTERVAL = 2e3;
151
+ var timestamp = 0;
152
+ function useRevalidateCache(manager, fetcher, options = {}) {
153
+ const isOnlineRef = useRef2(true);
154
+ const {
155
+ dedupingInterval = DEFAULT_DEDUPING_INTERVAL,
156
+ errorRetryInterval = DEFAULT_ERROR_RETRY_INTERVAL,
157
+ errorRetryCount = DEFAULT_MAX_ERROR_RETRY_COUNT
158
+ } = options;
159
+ const _revalidateCache = useCallback2(
160
+ async (shouldDedupe, retryCount = 0) => {
161
+ let startAt;
162
+ const shouldStartRequest = !manager.request || !shouldDedupe;
163
+ function deleteActiveRequest() {
164
+ const activeRequest = manager.request;
165
+ if (!activeRequest)
166
+ return;
167
+ if (activeRequest.timestamp !== startAt)
168
+ return;
169
+ manager.request = void 0;
170
+ }
171
+ function handleError() {
172
+ const timeout = ~~((Math.random() + 0.5) * (1 << (retryCount < 8 ? retryCount : 8))) * errorRetryInterval;
173
+ if (retryCount > errorRetryCount)
174
+ return;
175
+ setTimeout(() => {
176
+ void _revalidateCache(false, retryCount + 1);
177
+ }, timeout);
178
+ }
179
+ try {
180
+ if (shouldStartRequest) {
181
+ const currentCache = manager.cache;
182
+ if (!currentCache)
183
+ manager.cache = { isLoading: true };
184
+ manager.request = {
185
+ fetcher: fetcher(),
186
+ timestamp: ++timestamp
187
+ };
188
+ }
189
+ const activeRequest = manager.request;
190
+ if (!activeRequest)
191
+ return;
192
+ startAt = activeRequest.timestamp;
193
+ const newData = await activeRequest.fetcher;
194
+ if (shouldStartRequest) {
195
+ setTimeout(deleteActiveRequest, dedupingInterval);
196
+ }
197
+ if (!manager.request || manager.request.timestamp !== startAt)
198
+ return;
199
+ const activeMutation = manager.mutation;
200
+ if (activeMutation && (activeMutation.startTime > startAt || activeMutation.endTime > startAt || activeMutation.endTime === 0)) {
201
+ return;
202
+ }
203
+ manager.cache = {
204
+ isLoading: false,
205
+ data: newData
206
+ };
207
+ } catch (err) {
208
+ deleteActiveRequest();
209
+ const isVisible = document.visibilityState === "visible";
210
+ const isOnline = isOnlineRef.current;
211
+ if (shouldStartRequest && isVisible && isOnline)
212
+ handleError();
213
+ manager.cache = {
214
+ data: manager.cache?.data,
215
+ isLoading: false,
216
+ error: err
217
+ };
218
+ }
219
+ return;
220
+ },
221
+ [manager, fetcher, dedupingInterval, errorRetryInterval, errorRetryCount]
222
+ );
223
+ useEffect2(() => {
224
+ function handleIsOnline() {
225
+ isOnlineRef.current = true;
226
+ }
227
+ function handleIsOffline() {
228
+ isOnlineRef.current = false;
229
+ }
230
+ window.addEventListener("online", handleIsOnline);
231
+ window.addEventListener("offline", handleIsOffline);
232
+ return () => {
233
+ window.removeEventListener("online", handleIsOnline);
234
+ window.removeEventListener("offline", handleIsOffline);
235
+ };
236
+ }, []);
237
+ const revalidateCache = useCallback2(
238
+ (shoulDedupe) => {
239
+ return _revalidateCache(shoulDedupe, 0);
240
+ },
241
+ [_revalidateCache]
242
+ );
243
+ return revalidateCache;
244
+ }
245
+ function useMutate(manager, revalidateCache) {
246
+ const mutate = useCallback2(
247
+ async (data, options) => {
248
+ const beforeMutationTimestamp = ++timestamp;
249
+ manager.mutation = {
250
+ startTime: beforeMutationTimestamp,
251
+ endTime: 0
252
+ };
253
+ const currentCache = manager.cache;
254
+ manager.cache = {
255
+ isLoading: false,
256
+ data: options.optimisticData
257
+ };
258
+ let error;
259
+ try {
260
+ await data;
261
+ } catch (err) {
262
+ error = err;
263
+ }
264
+ const activeMutation = manager.mutation;
265
+ if (activeMutation && beforeMutationTimestamp !== activeMutation.startTime) {
266
+ if (error)
267
+ throw error;
268
+ return;
269
+ }
270
+ if (error) {
271
+ manager.cache = currentCache;
272
+ }
273
+ manager.mutation = {
274
+ startTime: beforeMutationTimestamp,
275
+ endTime: ++timestamp
276
+ };
277
+ manager.request = void 0;
278
+ void revalidateCache(false);
279
+ if (error)
280
+ throw error;
281
+ },
282
+ [manager, revalidateCache]
283
+ );
284
+ return mutate;
285
+ }
286
+ function useAutomaticRevalidation(manager, revalidateCache, options = {}) {
287
+ const isOnline = useIsOnline();
288
+ const isDocumentVisible = useIsDocumentVisible();
289
+ const {
290
+ revalidateOnFocus = true,
291
+ revalidateOnReconnect = true,
292
+ refreshInterval = 0
293
+ } = options;
294
+ useEffect2(() => {
295
+ let revalidationTimerId;
296
+ function scheduleRevalidation() {
297
+ if (refreshInterval === 0)
298
+ return;
299
+ revalidationTimerId = window.setTimeout(() => {
300
+ if (isOnline && isDocumentVisible && !manager.cache?.error) {
301
+ void revalidateCache(true).then(scheduleRevalidation);
302
+ return;
303
+ }
304
+ scheduleRevalidation();
305
+ }, refreshInterval);
306
+ }
307
+ scheduleRevalidation();
308
+ return () => {
309
+ window.clearTimeout(revalidationTimerId);
310
+ };
311
+ }, [revalidateCache, refreshInterval, isOnline, isDocumentVisible, manager]);
312
+ useEffect2(() => {
313
+ function handleIsOnline() {
314
+ if (revalidateOnReconnect && isDocumentVisible) {
315
+ void revalidateCache(true);
316
+ }
317
+ }
318
+ window.addEventListener("online", handleIsOnline);
319
+ return () => {
320
+ window.removeEventListener("online", handleIsOnline);
321
+ };
322
+ }, [revalidateCache, revalidateOnReconnect, isDocumentVisible]);
323
+ useEffect2(() => {
324
+ function handleVisibilityChange() {
325
+ const isVisible = document.visibilityState === "visible";
326
+ if (revalidateOnFocus && isVisible && isOnline) {
327
+ void revalidateCache(true);
328
+ }
329
+ }
330
+ document.addEventListener("visibilitychange", handleVisibilityChange);
331
+ return () => {
332
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
333
+ };
334
+ }, [revalidateCache, revalidateOnFocus, isOnline]);
108
335
  }
109
- function createThreadsManager() {
336
+ function createCacheManager() {
110
337
  let cache;
111
338
  let request;
112
339
  let mutation;
@@ -136,516 +363,482 @@ function createThreadsManager() {
136
363
  }
137
364
  };
138
365
  }
139
- function handleCommentsApiError(err) {
140
- const message = `Request failed with status ${err.status}: ${err.message}`;
141
- if (err.details?.error === "FORBIDDEN") {
142
- const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
143
- console2.error(detailedMessage);
144
- }
145
- return new Error(message);
366
+
367
+ // src/comments/CommentsRoom.ts
368
+ var POLLING_INTERVAL_REALTIME = 3e4;
369
+ var POLLING_INTERVAL = 5e3;
370
+ var THREAD_ID_PREFIX = "th";
371
+ var COMMENT_ID_PREFIX = "cm";
372
+ function createOptimisticId(prefix) {
373
+ return `${prefix}_${nanoid()}`;
374
+ }
375
+ function getPollingInterval(isBrowserOnline, isDocumentVisible, isRoomConnected) {
376
+ if (!isBrowserOnline || !isDocumentVisible)
377
+ return;
378
+ if (isRoomConnected)
379
+ return POLLING_INTERVAL_REALTIME;
380
+ return POLLING_INTERVAL;
146
381
  }
147
382
  function createCommentsRoom(room, errorEventSource) {
148
- const manager = createThreadsManager();
149
- let timestamp = 0;
150
- let commentsEventRefCount = 0;
151
- let commentsEventDisposer;
152
- async function mutate(data, options) {
153
- const beforeMutationTimestamp = ++timestamp;
154
- manager.mutation = {
155
- startTime: beforeMutationTimestamp,
156
- endTime: 0
157
- };
158
- const currentCache = manager.cache;
159
- manager.cache = {
160
- isLoading: false,
161
- threads: options.optimisticData
162
- };
163
- try {
164
- await data;
165
- const activeMutation = manager.mutation;
166
- if (activeMutation && beforeMutationTimestamp !== activeMutation.startTime) {
167
- return;
168
- }
169
- } catch (err) {
170
- manager.cache = currentCache;
171
- throw err;
172
- }
173
- manager.mutation = {
174
- startTime: beforeMutationTimestamp,
175
- endTime: ++timestamp
176
- };
177
- manager.request = void 0;
178
- void revalidateCache(false);
179
- }
180
- async function revalidateCache(shouldDedupe, retryCount = 0) {
181
- let startAt;
182
- const shouldStartRequest = !manager.request || !shouldDedupe;
183
- function deleteActiveRequest() {
184
- const activeRequest = manager.request;
185
- if (!activeRequest)
186
- return;
187
- if (activeRequest.timestamp !== startAt)
188
- return;
189
- manager.request = void 0;
190
- }
191
- function handleError() {
192
- const timeout = ~~((Math.random() + 0.5) * (1 << (retryCount < 8 ? retryCount : 8))) * ERROR_RETRY_INTERVAL;
193
- if (retryCount > MAX_ERROR_RETRY_COUNT)
194
- return;
195
- setTimeout(() => {
196
- void revalidateCache(true, retryCount + 1);
197
- }, timeout);
198
- }
199
- try {
200
- if (shouldStartRequest) {
201
- const currentCache = manager.cache;
202
- if (!currentCache)
203
- manager.cache = { isLoading: true };
204
- manager.request = {
205
- fetcher: room.getThreads(),
206
- timestamp: ++timestamp
207
- };
208
- }
209
- const activeRequest = manager.request;
210
- if (!activeRequest)
211
- return;
212
- const newData = await activeRequest.fetcher;
213
- startAt = activeRequest.timestamp;
214
- if (shouldStartRequest) {
215
- setTimeout(deleteActiveRequest, DEDUPING_INTERVAL);
216
- }
217
- if (!manager.request || manager.request.timestamp !== startAt)
218
- return;
219
- const activeMutation = manager.mutation;
220
- if (activeMutation && (activeMutation.startTime > startAt || activeMutation.endTime > startAt || activeMutation.endTime === 0)) {
221
- return;
222
- }
223
- manager.cache = {
224
- isLoading: false,
225
- threads: newData
226
- };
227
- } catch (err) {
228
- if (shouldStartRequest)
229
- handleError();
230
- deleteActiveRequest();
231
- manager.cache = {
232
- isLoading: false,
233
- error: err
234
- };
235
- }
236
- }
237
- function editThreadMetadata(options) {
238
- const threadId = options.threadId;
239
- const metadata = "metadata" in options ? options.metadata : {};
240
- const threads = getThreads();
241
- const optimisticData = threads.map(
242
- (thread) => thread.id === threadId ? {
243
- ...thread,
244
- metadata: {
245
- ...thread.metadata,
246
- ...metadata
247
- }
248
- } : thread
249
- );
250
- mutate(room.editThreadMetadata({ metadata, threadId }), {
251
- optimisticData
252
- }).catch((err) => {
253
- if (!(err instanceof CommentsApiError)) {
254
- throw err;
255
- }
256
- const error = handleCommentsApiError(err);
257
- errorEventSource.notify(
258
- new EditThreadMetadataError(error, {
259
- roomId: room.id,
260
- threadId,
261
- metadata
262
- })
263
- );
264
- });
265
- }
266
- function createThread(options) {
267
- const body = options.body;
268
- const metadata = "metadata" in options ? options.metadata : {};
269
- const threads = getThreads();
270
- const threadId = createOptimisticId(THREAD_ID_PREFIX);
271
- const commentId = createOptimisticId(COMMENT_ID_PREFIX);
272
- const now = (/* @__PURE__ */ new Date()).toISOString();
273
- const newComment = {
274
- id: commentId,
275
- threadId,
276
- roomId: room.id,
277
- createdAt: now,
278
- type: "comment",
279
- userId: getCurrentUserId(),
280
- body,
281
- reactions: []
282
- };
283
- const newThread = {
284
- id: threadId,
285
- type: "thread",
286
- createdAt: now,
287
- roomId: room.id,
288
- metadata,
289
- comments: [newComment]
290
- };
291
- mutate(room.createThread({ threadId, commentId, body, metadata }), {
292
- optimisticData: [...threads, newThread]
293
- }).catch((err) => {
294
- if (!(err instanceof CommentsApiError)) {
295
- throw err;
296
- }
297
- const error = handleCommentsApiError(err);
298
- errorEventSource.notify(
299
- new CreateThreadError(error, {
300
- roomId: room.id,
301
- threadId,
302
- commentId,
303
- body,
304
- metadata
305
- })
306
- );
307
- });
308
- return newThread;
309
- }
310
- function createComment({
311
- threadId,
312
- body
313
- }) {
314
- const threads = getThreads();
315
- const commentId = createOptimisticId(COMMENT_ID_PREFIX);
316
- const now = (/* @__PURE__ */ new Date()).toISOString();
317
- const comment = {
318
- id: commentId,
319
- threadId,
320
- roomId: room.id,
321
- type: "comment",
322
- createdAt: now,
323
- userId: getCurrentUserId(),
324
- body,
325
- reactions: []
326
- };
327
- const optimisticData = threads.map(
328
- (thread) => thread.id === threadId ? {
329
- ...thread,
330
- comments: [...thread.comments, comment]
331
- } : thread
332
- );
333
- mutate(room.createComment({ threadId, commentId, body }), {
334
- optimisticData
335
- }).catch((err) => {
336
- if (!(err instanceof CommentsApiError)) {
337
- throw err;
338
- }
339
- const error = handleCommentsApiError(err);
340
- errorEventSource.notify(
341
- new CreateCommentError(error, {
342
- roomId: room.id,
343
- threadId,
344
- commentId,
345
- body
346
- })
347
- );
348
- });
349
- return comment;
350
- }
351
- function editComment({ threadId, commentId, body }) {
352
- const threads = getThreads();
353
- const now = (/* @__PURE__ */ new Date()).toISOString();
354
- const optimisticData = threads.map(
355
- (thread) => thread.id === threadId ? {
356
- ...thread,
357
- comments: thread.comments.map(
358
- (comment) => comment.id === commentId ? {
359
- ...comment,
360
- editedAt: now,
361
- body
362
- } : comment
363
- )
364
- } : thread
365
- );
366
- mutate(room.editComment({ threadId, commentId, body }), {
367
- optimisticData
368
- }).catch((err) => {
369
- if (!(err instanceof CommentsApiError)) {
370
- throw err;
371
- }
372
- const error = handleCommentsApiError(err);
373
- errorEventSource.notify(
374
- new EditCommentError(error, {
375
- roomId: room.id,
376
- threadId,
377
- commentId,
378
- body
379
- })
380
- );
381
- });
382
- }
383
- function deleteComment({ threadId, commentId }) {
384
- const threads = getThreads();
385
- const now = (/* @__PURE__ */ new Date()).toISOString();
386
- const newThreads = [];
387
- for (const thread of threads) {
388
- if (thread.id === threadId) {
389
- const newThread = {
390
- ...thread,
391
- comments: thread.comments.map(
392
- (comment) => comment.id === commentId ? {
393
- ...comment,
394
- deletedAt: now,
395
- body: void 0
396
- } : comment
397
- )
398
- };
399
- if (newThread.comments.some((comment) => comment.deletedAt === void 0)) {
400
- newThreads.push(newThread);
401
- }
402
- } else {
403
- newThreads.push(thread);
404
- }
405
- }
406
- mutate(room.deleteComment({ threadId, commentId }), {
407
- optimisticData: newThreads
408
- }).catch((err) => {
409
- if (!(err instanceof CommentsApiError)) {
410
- throw err;
411
- }
412
- const error = handleCommentsApiError(err);
413
- errorEventSource.notify(
414
- new DeleteCommentError(error, {
415
- roomId: room.id,
416
- threadId,
417
- commentId
418
- })
419
- );
420
- });
421
- }
422
- function getCurrentUserId() {
423
- const self = room.getSelf();
424
- if (self === null || self.id === void 0) {
425
- return "anonymous";
426
- } else {
427
- return self.id;
428
- }
429
- }
430
- function getThreads() {
431
- const threads = manager.cache;
432
- if (!threads || threads.isLoading || threads.error) {
433
- throw new Error(
434
- "Cannot update threads or comments before they are loaded."
435
- );
436
- }
437
- return threads.threads;
438
- }
439
- function _subscribe() {
440
- if (commentsEventRefCount === 0) {
441
- commentsEventDisposer = room.events.comments.subscribe(() => {
442
- void revalidateCache(true);
443
- });
444
- }
445
- commentsEventRefCount = commentsEventRefCount + 1;
446
- return () => {
447
- commentsEventRefCount = commentsEventRefCount - 1;
448
- if (commentsEventRefCount > 0)
449
- return;
450
- commentsEventDisposer?.();
451
- commentsEventDisposer = void 0;
452
- };
453
- }
454
- function usePolling() {
455
- const status = useSyncExternalStore(
383
+ const manager = createCacheManager();
384
+ function _useThreads(revalidateCache) {
385
+ const status = useSyncExternalStore3(
456
386
  room.events.status.subscribe,
457
387
  room.getStatus,
458
388
  room.getStatus
459
389
  );
460
- useEffect2(
461
- () => {
462
- const interval = status === "connected" ? POLLING_INTERVAL_REALTIME : POLLING_INTERVAL;
463
- let revalidationTimerId;
464
- function scheduleRevalidation() {
465
- revalidationTimerId = window.setTimeout(
466
- executeRevalidation,
467
- interval
468
- );
469
- }
470
- function executeRevalidation() {
471
- void revalidateCache(true).then(scheduleRevalidation);
472
- }
473
- scheduleRevalidation();
474
- return () => {
475
- window.clearTimeout(revalidationTimerId);
476
- };
477
- },
478
- // eslint-disable-next-line react-hooks/exhaustive-deps -- ESLint recommends against adding `revalidateCache` as a dependency, but not doing so causes the code inside `useEffect` to reference an outdated version of `revalidateCache`
479
- [status, revalidateCache]
390
+ const isOnline = useIsOnline();
391
+ const isDocumentVisible = useIsDocumentVisible();
392
+ const interval = getPollingInterval(
393
+ isOnline,
394
+ isDocumentVisible,
395
+ status === "connected"
480
396
  );
481
- }
482
- function useThreadsInternal() {
483
- useEffect2(_subscribe, [_subscribe]);
484
- usePolling();
485
- const cache = useSyncExternalStore(
397
+ useAutomaticRevalidation(manager, revalidateCache, {
398
+ revalidateOnFocus: true,
399
+ revalidateOnReconnect: true,
400
+ refreshInterval: interval
401
+ });
402
+ useEffect3(() => {
403
+ const unsubscribe = room.events.comments.subscribe(() => {
404
+ void revalidateCache(true);
405
+ });
406
+ return () => {
407
+ unsubscribe();
408
+ };
409
+ }, [revalidateCache]);
410
+ const cache = useSyncExternalStore3(
486
411
  manager.subscribe,
487
412
  () => manager.cache,
488
413
  () => manager.cache
489
414
  );
490
- return cache ?? { isLoading: true };
415
+ if (!cache || cache.isLoading) {
416
+ return { isLoading: true };
417
+ }
418
+ return {
419
+ isLoading: cache.isLoading,
420
+ threads: cache.data ?? [],
421
+ error: cache.error
422
+ };
491
423
  }
492
424
  function useThreads() {
493
- useEffect2(
494
- () => {
495
- void revalidateCache(true);
496
- },
497
- // eslint-disable-next-line react-hooks/exhaustive-deps -- ESLint recommends against adding `revalidateCache` as a dependency, but not doing so causes the code inside `useEffect` to reference an outdated version of `revalidateCache`
498
- [revalidateCache]
499
- );
500
- return useThreadsInternal();
425
+ const revalidate = useRevalidateCache(manager, room.getThreads);
426
+ useEffect3(() => {
427
+ void revalidate(true);
428
+ }, [revalidate]);
429
+ return _useThreads(revalidate);
501
430
  }
502
431
  function useThreadsSuspense() {
503
- const cache = useThreadsInternal();
504
- if (cache.isLoading) {
505
- throw revalidateCache(true);
506
- }
432
+ const revalidate = useRevalidateCache(manager, room.getThreads);
433
+ const cache = _useThreads(revalidate);
507
434
  if (cache.error) {
508
435
  throw cache.error;
509
436
  }
437
+ if (cache.isLoading || !cache.threads) {
438
+ throw revalidate(true);
439
+ }
510
440
  return {
511
441
  threads: cache.threads,
512
442
  isLoading: false
513
443
  };
514
444
  }
515
- function addReaction({
516
- threadId,
517
- commentId,
518
- emoji
519
- }) {
520
- const threads = getThreads();
521
- const now = (/* @__PURE__ */ new Date()).toISOString();
522
- const userId = getCurrentUserId();
523
- const optimisticData = threads.map(
524
- (thread) => thread.id === threadId ? {
525
- ...thread,
526
- comments: thread.comments.map((comment) => {
527
- if (comment.id !== commentId) {
528
- return comment;
529
- }
530
- let reactions;
531
- if (comment.reactions.some((reaction) => reaction.emoji === emoji)) {
532
- reactions = comment.reactions.map(
533
- (reaction) => reaction.emoji === emoji ? {
534
- ...reaction,
535
- users: [...reaction.users, { id: userId }]
536
- } : reaction
537
- );
538
- } else {
539
- reactions = [
540
- ...comment.reactions,
541
- {
542
- emoji,
543
- createdAt: now,
544
- users: [{ id: userId }]
545
- }
546
- ];
445
+ function useEditThreadMetadata() {
446
+ const revalidate = useRevalidateCache(manager, room.getThreads);
447
+ const mutate = useMutate(manager, revalidate);
448
+ const editThreadMetadata = useCallback3(
449
+ (options) => {
450
+ const threadId = options.threadId;
451
+ const metadata = "metadata" in options ? options.metadata : {};
452
+ const threads = getThreads();
453
+ const optimisticData = threads.map(
454
+ (thread) => thread.id === threadId ? {
455
+ ...thread,
456
+ metadata: {
457
+ ...thread.metadata,
458
+ ...metadata
459
+ }
460
+ } : thread
461
+ );
462
+ mutate(room.editThreadMetadata({ metadata, threadId }), {
463
+ optimisticData
464
+ }).catch((err) => {
465
+ if (!(err instanceof CommentsApiError)) {
466
+ throw err;
547
467
  }
548
- return {
549
- ...comment,
550
- reactions
551
- };
552
- })
553
- } : thread
468
+ const error = handleCommentsApiError(err);
469
+ errorEventSource.notify(
470
+ new EditThreadMetadataError(error, {
471
+ roomId: room.id,
472
+ threadId,
473
+ metadata
474
+ })
475
+ );
476
+ });
477
+ },
478
+ [mutate]
554
479
  );
555
- mutate(room.addReaction({ threadId, commentId, emoji }), {
556
- optimisticData
557
- }).catch((err) => {
558
- if (!(err instanceof CommentsApiError)) {
559
- throw err;
560
- }
561
- const error = handleCommentsApiError(err);
562
- errorEventSource.notify(
563
- new AddReactionError(error, {
480
+ return editThreadMetadata;
481
+ }
482
+ function useCreateThread() {
483
+ const revalidate = useRevalidateCache(manager, room.getThreads);
484
+ const mutate = useMutate(manager, revalidate);
485
+ const createThread = useCallback3(
486
+ (options) => {
487
+ const body = options.body;
488
+ const metadata = "metadata" in options ? options.metadata : {};
489
+ const threads = getThreads();
490
+ const threadId = createOptimisticId(THREAD_ID_PREFIX);
491
+ const commentId = createOptimisticId(COMMENT_ID_PREFIX);
492
+ const now = /* @__PURE__ */ new Date();
493
+ const newComment = {
494
+ id: commentId,
495
+ threadId,
564
496
  roomId: room.id,
497
+ createdAt: now,
498
+ type: "comment",
499
+ userId: getCurrentUserId(),
500
+ body,
501
+ reactions: []
502
+ };
503
+ const newThread = {
504
+ id: threadId,
505
+ type: "thread",
506
+ createdAt: now,
507
+ roomId: room.id,
508
+ metadata,
509
+ comments: [newComment]
510
+ };
511
+ mutate(room.createThread({ threadId, commentId, body, metadata }), {
512
+ optimisticData: [...threads, newThread]
513
+ }).catch((err) => {
514
+ if (!(err instanceof CommentsApiError)) {
515
+ throw err;
516
+ }
517
+ const error = handleCommentsApiError(err);
518
+ errorEventSource.notify(
519
+ new CreateThreadError(error, {
520
+ roomId: room.id,
521
+ threadId,
522
+ commentId,
523
+ body,
524
+ metadata
525
+ })
526
+ );
527
+ });
528
+ return newThread;
529
+ },
530
+ [mutate]
531
+ );
532
+ return createThread;
533
+ }
534
+ function useCreateComment() {
535
+ const revalidate = useRevalidateCache(manager, room.getThreads);
536
+ const mutate = useMutate(manager, revalidate);
537
+ const createComment = useCallback3(
538
+ ({ threadId, body }) => {
539
+ const threads = getThreads();
540
+ const commentId = createOptimisticId(COMMENT_ID_PREFIX);
541
+ const now = /* @__PURE__ */ new Date();
542
+ const comment = {
543
+ id: commentId,
565
544
  threadId,
566
- commentId,
567
- emoji
568
- })
569
- );
570
- });
545
+ roomId: room.id,
546
+ type: "comment",
547
+ createdAt: now,
548
+ userId: getCurrentUserId(),
549
+ body,
550
+ reactions: []
551
+ };
552
+ const optimisticData = threads.map(
553
+ (thread) => thread.id === threadId ? {
554
+ ...thread,
555
+ comments: [...thread.comments, comment]
556
+ } : thread
557
+ );
558
+ mutate(room.createComment({ threadId, commentId, body }), {
559
+ optimisticData
560
+ }).catch((err) => {
561
+ if (!(err instanceof CommentsApiError)) {
562
+ throw err;
563
+ }
564
+ const error = handleCommentsApiError(err);
565
+ errorEventSource.notify(
566
+ new CreateCommentError(error, {
567
+ roomId: room.id,
568
+ threadId,
569
+ commentId,
570
+ body
571
+ })
572
+ );
573
+ });
574
+ return comment;
575
+ },
576
+ [mutate]
577
+ );
578
+ return createComment;
571
579
  }
572
- function removeReaction({
573
- threadId,
574
- commentId,
575
- emoji
576
- }) {
577
- const threads = getThreads();
578
- const userId = getCurrentUserId();
579
- const optimisticData = threads.map(
580
- (thread) => thread.id === threadId ? {
581
- ...thread,
582
- comments: thread.comments.map((comment) => {
583
- if (comment.id !== commentId) {
584
- return comment;
580
+ function useEditComment() {
581
+ const revalidate = useRevalidateCache(manager, room.getThreads);
582
+ const mutate = useMutate(manager, revalidate);
583
+ const editComment = useCallback3(
584
+ ({ threadId, commentId, body }) => {
585
+ const threads = getThreads();
586
+ const now = /* @__PURE__ */ new Date();
587
+ const optimisticData = threads.map(
588
+ (thread) => thread.id === threadId ? {
589
+ ...thread,
590
+ comments: thread.comments.map(
591
+ (comment) => comment.id === commentId ? {
592
+ ...comment,
593
+ editedAt: now,
594
+ body
595
+ } : comment
596
+ )
597
+ } : thread
598
+ );
599
+ mutate(room.editComment({ threadId, commentId, body }), {
600
+ optimisticData
601
+ }).catch((err) => {
602
+ if (!(err instanceof CommentsApiError)) {
603
+ throw err;
585
604
  }
586
- const reactionIndex = comment.reactions.findIndex(
587
- (reaction) => reaction.emoji === emoji
605
+ const error = handleCommentsApiError(err);
606
+ errorEventSource.notify(
607
+ new EditCommentError(error, {
608
+ roomId: room.id,
609
+ threadId,
610
+ commentId,
611
+ body
612
+ })
588
613
  );
589
- let reactions = comment.reactions;
590
- if (reactionIndex >= 0 && comment.reactions[reactionIndex].users.some(
591
- (user) => user.id === userId
592
- )) {
593
- if (comment.reactions[reactionIndex].users.length <= 1) {
594
- reactions = [...comment.reactions];
595
- reactions.splice(reactionIndex, 1);
596
- } else {
597
- reactions[reactionIndex] = {
598
- ...reactions[reactionIndex],
599
- users: reactions[reactionIndex].users.filter(
600
- (user) => user.id !== userId
601
- )
602
- };
614
+ });
615
+ },
616
+ [mutate]
617
+ );
618
+ return editComment;
619
+ }
620
+ function useDeleteComment() {
621
+ const revalidate = useRevalidateCache(manager, room.getThreads);
622
+ const mutate = useMutate(manager, revalidate);
623
+ const deleteComment = useCallback3(
624
+ ({ threadId, commentId }) => {
625
+ const threads = getThreads();
626
+ const now = /* @__PURE__ */ new Date();
627
+ const newThreads = [];
628
+ for (const thread of threads) {
629
+ if (thread.id === threadId) {
630
+ const newThread = {
631
+ ...thread,
632
+ comments: thread.comments.map(
633
+ (comment) => comment.id === commentId ? {
634
+ ...comment,
635
+ deletedAt: now,
636
+ body: void 0
637
+ } : comment
638
+ )
639
+ };
640
+ if (newThread.comments.some(
641
+ (comment) => comment.deletedAt === void 0
642
+ )) {
643
+ newThreads.push(newThread);
603
644
  }
645
+ } else {
646
+ newThreads.push(thread);
604
647
  }
605
- return {
606
- ...comment,
607
- reactions
608
- };
609
- })
610
- } : thread
648
+ }
649
+ mutate(room.deleteComment({ threadId, commentId }), {
650
+ optimisticData: newThreads
651
+ }).catch((err) => {
652
+ if (!(err instanceof CommentsApiError)) {
653
+ throw err;
654
+ }
655
+ const error = handleCommentsApiError(err);
656
+ errorEventSource.notify(
657
+ new DeleteCommentError(error, {
658
+ roomId: room.id,
659
+ threadId,
660
+ commentId
661
+ })
662
+ );
663
+ });
664
+ },
665
+ [mutate]
611
666
  );
612
- mutate(room.removeReaction({ threadId, commentId, emoji }), {
613
- optimisticData
614
- }).catch((err) => {
615
- if (!(err instanceof CommentsApiError)) {
616
- throw err;
617
- }
618
- const error = handleCommentsApiError(err);
619
- errorEventSource.notify(
620
- new RemoveReactionError(error, {
621
- roomId: room.id,
622
- threadId,
623
- commentId,
624
- emoji
625
- })
667
+ return deleteComment;
668
+ }
669
+ function useAddReaction() {
670
+ const revalidate = useRevalidateCache(manager, room.getThreads);
671
+ const mutate = useMutate(manager, revalidate);
672
+ const createComment = useCallback3(
673
+ ({ threadId, commentId, emoji }) => {
674
+ const threads = getThreads();
675
+ const now = /* @__PURE__ */ new Date();
676
+ const userId = getCurrentUserId();
677
+ const optimisticData = threads.map(
678
+ (thread) => thread.id === threadId ? {
679
+ ...thread,
680
+ comments: thread.comments.map((comment) => {
681
+ if (comment.id !== commentId) {
682
+ return comment;
683
+ }
684
+ let reactions;
685
+ if (comment.reactions.some(
686
+ (reaction) => reaction.emoji === emoji
687
+ )) {
688
+ reactions = comment.reactions.map(
689
+ (reaction) => reaction.emoji === emoji ? {
690
+ ...reaction,
691
+ users: [...reaction.users, { id: userId }]
692
+ } : reaction
693
+ );
694
+ } else {
695
+ reactions = [
696
+ ...comment.reactions,
697
+ {
698
+ emoji,
699
+ createdAt: now,
700
+ users: [{ id: userId }]
701
+ }
702
+ ];
703
+ }
704
+ return {
705
+ ...comment,
706
+ reactions
707
+ };
708
+ })
709
+ } : thread
710
+ );
711
+ mutate(room.addReaction({ threadId, commentId, emoji }), {
712
+ optimisticData
713
+ }).catch((err) => {
714
+ if (!(err instanceof CommentsApiError)) {
715
+ throw err;
716
+ }
717
+ const error = handleCommentsApiError(err);
718
+ errorEventSource.notify(
719
+ new AddReactionError(error, {
720
+ roomId: room.id,
721
+ threadId,
722
+ commentId,
723
+ emoji
724
+ })
725
+ );
726
+ });
727
+ },
728
+ [mutate]
729
+ );
730
+ return createComment;
731
+ }
732
+ function useRemoveReaction() {
733
+ const revalidate = useRevalidateCache(manager, room.getThreads);
734
+ const mutate = useMutate(manager, revalidate);
735
+ const createComment = useCallback3(
736
+ ({ threadId, commentId, emoji }) => {
737
+ const threads = getThreads();
738
+ const userId = getCurrentUserId();
739
+ const optimisticData = threads.map(
740
+ (thread) => thread.id === threadId ? {
741
+ ...thread,
742
+ comments: thread.comments.map((comment) => {
743
+ if (comment.id !== commentId) {
744
+ return comment;
745
+ }
746
+ const reactionIndex = comment.reactions.findIndex(
747
+ (reaction) => reaction.emoji === emoji
748
+ );
749
+ let reactions = comment.reactions;
750
+ if (reactionIndex >= 0 && comment.reactions[reactionIndex].users.some(
751
+ (user) => user.id === userId
752
+ )) {
753
+ if (comment.reactions[reactionIndex].users.length <= 1) {
754
+ reactions = [...comment.reactions];
755
+ reactions.splice(reactionIndex, 1);
756
+ } else {
757
+ reactions[reactionIndex] = {
758
+ ...reactions[reactionIndex],
759
+ users: reactions[reactionIndex].users.filter(
760
+ (user) => user.id !== userId
761
+ )
762
+ };
763
+ }
764
+ }
765
+ return {
766
+ ...comment,
767
+ reactions
768
+ };
769
+ })
770
+ } : thread
771
+ );
772
+ mutate(room.removeReaction({ threadId, commentId, emoji }), {
773
+ optimisticData
774
+ }).catch((err) => {
775
+ if (!(err instanceof CommentsApiError)) {
776
+ throw err;
777
+ }
778
+ const error = handleCommentsApiError(err);
779
+ errorEventSource.notify(
780
+ new RemoveReactionError(error, {
781
+ roomId: room.id,
782
+ threadId,
783
+ commentId,
784
+ emoji
785
+ })
786
+ );
787
+ });
788
+ },
789
+ [mutate]
790
+ );
791
+ return createComment;
792
+ }
793
+ function getThreads() {
794
+ const threads = manager.cache;
795
+ if (!threads || threads.isLoading || threads.error || threads.data === void 0) {
796
+ throw new Error(
797
+ "Cannot update threads or comments before they are loaded."
626
798
  );
627
- });
799
+ }
800
+ return threads.data;
801
+ }
802
+ function getCurrentUserId() {
803
+ const self = room.getSelf();
804
+ if (self === null || self.id === void 0) {
805
+ return "anonymous";
806
+ } else {
807
+ return self.id;
808
+ }
809
+ }
810
+ function handleCommentsApiError(err) {
811
+ const message = `Request failed with status ${err.status}: ${err.message}`;
812
+ if (err.details?.error === "FORBIDDEN") {
813
+ const detailedMessage = [
814
+ message,
815
+ err.details.suggestion,
816
+ err.details.docs
817
+ ].filter(Boolean).join("\n");
818
+ console2.error(detailedMessage);
819
+ }
820
+ return new Error(message);
628
821
  }
629
822
  return {
630
823
  useThreads,
631
824
  useThreadsSuspense,
632
- editThreadMetadata,
633
- addReaction,
634
- removeReaction,
635
- createThread,
636
- createComment,
637
- editComment,
638
- deleteComment
825
+ useCreateThread,
826
+ useEditThreadMetadata,
827
+ useCreateComment,
828
+ useEditComment,
829
+ useDeleteComment,
830
+ useAddReaction,
831
+ useRemoveReaction
639
832
  };
640
833
  }
641
834
 
642
835
  // src/comments/lib/use-debounce.ts
643
- import { useEffect as useEffect3, useRef, useState as useState2 } from "react";
836
+ import { useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
644
837
  var DEFAULT_DELAY = 500;
645
838
  function useDebounce(value, delay = DEFAULT_DELAY) {
646
- const timeout = useRef();
839
+ const timeout = useRef3();
647
840
  const [debouncedValue, setDebouncedValue] = useState2(value);
648
- useEffect3(() => {
841
+ useEffect4(() => {
649
842
  if (delay === false) {
650
843
  return;
651
844
  }
@@ -664,8 +857,8 @@ function useDebounce(value, delay = DEFAULT_DELAY) {
664
857
  }
665
858
 
666
859
  // src/lib/use-async-cache.ts
667
- import { useCallback, useEffect as useEffect4, useMemo, useRef as useRef2 } from "react";
668
- import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
860
+ import { useCallback as useCallback4, useEffect as useEffect5, useMemo, useRef as useRef4 } from "react";
861
+ import { useSyncExternalStore as useSyncExternalStore4 } from "use-sync-external-store/shim/index.js";
669
862
 
670
863
  // src/lib/use-initial.ts
671
864
  import { useState as useState3 } from "react";
@@ -691,19 +884,19 @@ function useAsyncCache(cache, key, options) {
691
884
  void cacheItem2.get();
692
885
  return cacheItem2;
693
886
  }, [cache, key]);
694
- const subscribe = useCallback(
887
+ const subscribe2 = useCallback4(
695
888
  (callback) => cacheItem?.subscribe(callback) ?? noop,
696
889
  [cacheItem]
697
890
  );
698
- const getState = useCallback(
891
+ const getState = useCallback4(
699
892
  () => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
700
893
  [cacheItem]
701
894
  );
702
- const revalidate = useCallback(() => cacheItem?.revalidate(), [cacheItem]);
703
- const state = useSyncExternalStore2(subscribe, getState, getState);
704
- const previousData = useRef2();
895
+ const revalidate = useCallback4(() => cacheItem?.revalidate(), [cacheItem]);
896
+ const state = useSyncExternalStore4(subscribe2, getState, getState);
897
+ const previousData = useRef4();
705
898
  let data = state.data;
706
- useEffect4(() => {
899
+ useEffect5(() => {
707
900
  previousData.current = { key, data: state.data };
708
901
  }, [key, state.data]);
709
902
  if (!cacheItem) {
@@ -742,10 +935,10 @@ function useAsyncCache(cache, key, options) {
742
935
  }
743
936
 
744
937
  // src/lib/use-latest.ts
745
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
938
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
746
939
  function useLatest(value) {
747
- const ref = useRef3(value);
748
- useEffect5(() => {
940
+ const ref = useRef5(value);
941
+ useEffect6(() => {
749
942
  ref.current = value;
750
943
  }, [value]);
751
944
  return ref;
@@ -780,7 +973,7 @@ var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\
780
973
 
781
974
  Why? Please see https://liveblocks.io/docs/platform/troubleshooting#stale-props-zombie-child for more information`;
782
975
  var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
783
- function useSyncExternalStore3(s, gs, gss) {
976
+ function useSyncExternalStore5(s, gs, gss) {
784
977
  return useSyncExternalStoreWithSelector(s, gs, gss, identity);
785
978
  }
786
979
  var STABLE_EMPTY_LIST = Object.freeze([]);
@@ -931,16 +1124,16 @@ function createRoomContext(client, options) {
931
1124
  }
932
1125
  function useStatus() {
933
1126
  const room = useRoom();
934
- const subscribe = room.events.status.subscribe;
935
- const getSnapshot = room.getStatus;
1127
+ const subscribe2 = room.events.status.subscribe;
1128
+ const getSnapshot2 = room.getStatus;
936
1129
  const getServerSnapshot = room.getStatus;
937
- return useSyncExternalStore3(subscribe, getSnapshot, getServerSnapshot);
1130
+ return useSyncExternalStore5(subscribe2, getSnapshot2, getServerSnapshot);
938
1131
  }
939
1132
  function useMyPresence() {
940
1133
  const room = useRoom();
941
- const subscribe = room.events.myPresence.subscribe;
942
- const getSnapshot = room.getPresence;
943
- const presence = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
1134
+ const subscribe2 = room.events.myPresence.subscribe;
1135
+ const getSnapshot2 = room.getPresence;
1136
+ const presence = useSyncExternalStore5(subscribe2, getSnapshot2, getSnapshot2);
944
1137
  const setPresence = room.updatePresence;
945
1138
  return [presence, setPresence];
946
1139
  }
@@ -949,12 +1142,12 @@ function createRoomContext(client, options) {
949
1142
  }
950
1143
  function useOthers(selector, isEqual) {
951
1144
  const room = useRoom();
952
- const subscribe = room.events.others.subscribe;
953
- const getSnapshot = room.getOthers;
1145
+ const subscribe2 = room.events.others.subscribe;
1146
+ const getSnapshot2 = room.getOthers;
954
1147
  const getServerSnapshot = alwaysEmptyList;
955
1148
  return useSyncExternalStoreWithSelector(
956
- subscribe,
957
- getSnapshot,
1149
+ subscribe2,
1150
+ getSnapshot2,
958
1151
  getServerSnapshot,
959
1152
  selector ?? identity,
960
1153
  isEqual
@@ -1058,8 +1251,8 @@ function createRoomContext(client, options) {
1058
1251
  }
1059
1252
  function useSelf(maybeSelector, isEqual) {
1060
1253
  const room = useRoom();
1061
- const subscribe = room.events.self.subscribe;
1062
- const getSnapshot = room.getSelf;
1254
+ const subscribe2 = room.events.self.subscribe;
1255
+ const getSnapshot2 = room.getSelf;
1063
1256
  const selector = maybeSelector ?? identity;
1064
1257
  const wrappedSelector = React2.useCallback(
1065
1258
  (me) => me !== null ? selector(me) : null,
@@ -1067,8 +1260,8 @@ function createRoomContext(client, options) {
1067
1260
  );
1068
1261
  const getServerSnapshot = alwaysNull;
1069
1262
  return useSyncExternalStoreWithSelector(
1070
- subscribe,
1071
- getSnapshot,
1263
+ subscribe2,
1264
+ getSnapshot2,
1072
1265
  getServerSnapshot,
1073
1266
  wrappedSelector,
1074
1267
  isEqual
@@ -1076,10 +1269,10 @@ function createRoomContext(client, options) {
1076
1269
  }
1077
1270
  function useMutableStorageRoot() {
1078
1271
  const room = useRoom();
1079
- const subscribe = room.events.storageDidLoad.subscribeOnce;
1080
- const getSnapshot = room.getStorageSnapshot;
1272
+ const subscribe2 = room.events.storageDidLoad.subscribeOnce;
1273
+ const getSnapshot2 = room.getStorageSnapshot;
1081
1274
  const getServerSnapshot = alwaysNull;
1082
- return useSyncExternalStore3(subscribe, getSnapshot, getServerSnapshot);
1275
+ return useSyncExternalStore5(subscribe2, getSnapshot2, getServerSnapshot);
1083
1276
  }
1084
1277
  function useStorageRoot() {
1085
1278
  return [useMutableStorageRoot()];
@@ -1095,15 +1288,15 @@ function createRoomContext(client, options) {
1095
1288
  }
1096
1289
  function useCanUndo() {
1097
1290
  const room = useRoom();
1098
- const subscribe = room.events.history.subscribe;
1291
+ const subscribe2 = room.events.history.subscribe;
1099
1292
  const canUndo = room.history.canUndo;
1100
- return useSyncExternalStore3(subscribe, canUndo, canUndo);
1293
+ return useSyncExternalStore5(subscribe2, canUndo, canUndo);
1101
1294
  }
1102
1295
  function useCanRedo() {
1103
1296
  const room = useRoom();
1104
- const subscribe = room.events.history.subscribe;
1297
+ const subscribe2 = room.events.history.subscribe;
1105
1298
  const canRedo = room.history.canRedo;
1106
- return useSyncExternalStore3(subscribe, canRedo, canRedo);
1299
+ return useSyncExternalStore5(subscribe2, canRedo, canRedo);
1107
1300
  }
1108
1301
  function useBatch() {
1109
1302
  return useRoom().batch;
@@ -1152,11 +1345,11 @@ function createRoomContext(client, options) {
1152
1345
  (rootOrNull2) => rootOrNull2 !== null ? selector(rootOrNull2) : null,
1153
1346
  [selector]
1154
1347
  );
1155
- const subscribe = React2.useCallback(
1348
+ const subscribe2 = React2.useCallback(
1156
1349
  (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop2,
1157
1350
  [room, rootOrNull]
1158
1351
  );
1159
- const getSnapshot = React2.useCallback(() => {
1352
+ const getSnapshot2 = React2.useCallback(() => {
1160
1353
  if (rootOrNull === null) {
1161
1354
  return null;
1162
1355
  } else {
@@ -1167,8 +1360,8 @@ function createRoomContext(client, options) {
1167
1360
  }, [rootOrNull]);
1168
1361
  const getServerSnapshot = alwaysNull;
1169
1362
  return useSyncExternalStoreWithSelector(
1170
- subscribe,
1171
- getSnapshot,
1363
+ subscribe2,
1364
+ getSnapshot2,
1172
1365
  getServerSnapshot,
1173
1366
  wrappedSelector,
1174
1367
  isEqual
@@ -1280,52 +1473,31 @@ function createRoomContext(client, options) {
1280
1473
  }
1281
1474
  function useCreateThread() {
1282
1475
  const room = useRoom();
1283
- return React2.useCallback(
1284
- (options2) => getCommentsRoom(room).createThread(options2),
1285
- [room]
1286
- );
1476
+ return getCommentsRoom(room).useCreateThread();
1287
1477
  }
1288
1478
  function useEditThreadMetadata() {
1289
1479
  const room = useRoom();
1290
- return React2.useCallback(
1291
- (options2) => getCommentsRoom(room).editThreadMetadata(options2),
1292
- [room]
1293
- );
1480
+ return getCommentsRoom(room).useEditThreadMetadata();
1294
1481
  }
1295
1482
  function useAddReaction() {
1296
1483
  const room = useRoom();
1297
- return React2.useCallback(
1298
- (options2) => getCommentsRoom(room).addReaction(options2),
1299
- [room]
1300
- );
1484
+ return getCommentsRoom(room).useAddReaction();
1301
1485
  }
1302
1486
  function useRemoveReaction() {
1303
1487
  const room = useRoom();
1304
- return React2.useCallback(
1305
- (options2) => getCommentsRoom(room).removeReaction(options2),
1306
- [room]
1307
- );
1488
+ return getCommentsRoom(room).useRemoveReaction();
1308
1489
  }
1309
1490
  function useCreateComment() {
1310
1491
  const room = useRoom();
1311
- return React2.useCallback(
1312
- (options2) => getCommentsRoom(room).createComment(options2),
1313
- [room]
1314
- );
1492
+ return getCommentsRoom(room).useCreateComment();
1315
1493
  }
1316
1494
  function useEditComment() {
1317
1495
  const room = useRoom();
1318
- return React2.useCallback(
1319
- (options2) => getCommentsRoom(room).editComment(options2),
1320
- [room]
1321
- );
1496
+ return getCommentsRoom(room).useEditComment();
1322
1497
  }
1323
1498
  function useDeleteComment() {
1324
1499
  const room = useRoom();
1325
- return React2.useCallback(
1326
- (options2) => getCommentsRoom(room).deleteComment(options2),
1327
- [room]
1328
- );
1500
+ return getCommentsRoom(room).useDeleteComment();
1329
1501
  }
1330
1502
  const { resolveUsers, resolveMentionSuggestions } = options ?? {};
1331
1503
  const usersCache = resolveUsers ? createAsyncCache(async (stringifiedOptions) => {