@liveblocks/react 1.9.2 → 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.9.2";
8
+ var PKG_VERSION = "1.9.3";
9
9
  var PKG_FORMAT = "esm";
10
10
 
11
11
  // src/ClientSideSuspense.tsx
@@ -29,20 +29,19 @@ import {
29
29
  stringify as stringify2
30
30
  } from "@liveblocks/core";
31
31
  import * as React3 from "react";
32
- import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
32
+ import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 } from "use-sync-external-store/shim/with-selector.js";
33
33
 
34
34
  // src/comments/CommentsRoom.tsx
35
35
  import { CommentsApiError, makeEventSource, stringify } from "@liveblocks/core";
36
36
  import { nanoid } from "nanoid";
37
37
  import React2, {
38
38
  createContext,
39
- useCallback as useCallback3,
39
+ useCallback as useCallback2,
40
40
  useContext,
41
41
  useEffect as useEffect3,
42
- useMemo,
43
- useRef as useRef3
42
+ useMemo
44
43
  } from "react";
45
- import { useSyncExternalStore as useSyncExternalStore3 } from "use-sync-external-store/shim/index.js";
44
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
46
45
 
47
46
  // src/comments/errors.ts
48
47
  var CreateThreadError = class extends Error {
@@ -103,66 +102,19 @@ var RemoveReactionError = class extends Error {
103
102
  };
104
103
 
105
104
  // src/comments/lib/revalidation.ts
106
- import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
107
-
108
- // src/comments/lib/use-is-document-visible.ts
109
- import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
110
- function useIsDocumentVisible() {
111
- const isVisible = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
112
- return isVisible;
113
- }
114
- function subscribe(onStoreChange) {
115
- document.addEventListener("visibilitychange", onStoreChange);
116
- return () => {
117
- document.removeEventListener("visibilitychange", onStoreChange);
118
- };
119
- }
120
- function getSnapshot() {
121
- const isDocumentDefined = typeof document !== "undefined";
122
- return isDocumentDefined ? document.visibilityState === "visible" : true;
123
- }
124
-
125
- // src/comments/lib/use-is-online.ts
126
- import { useCallback, useRef } from "react";
127
- import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
128
- function useIsOnline() {
129
- const isOnlineRef = useRef(true);
130
- const subscribe2 = useCallback((onStoreChange) => {
131
- function handleIsOnline() {
132
- isOnlineRef.current = true;
133
- onStoreChange();
134
- }
135
- function handleIsOffline() {
136
- isOnlineRef.current = false;
137
- onStoreChange();
138
- }
139
- window.addEventListener("online", handleIsOnline);
140
- window.addEventListener("offline", handleIsOffline);
141
- return () => {
142
- window.removeEventListener("online", handleIsOnline);
143
- window.removeEventListener("offline", handleIsOffline);
144
- };
145
- }, []);
146
- const getSnapshot2 = useCallback(() => {
147
- return isOnlineRef.current;
148
- }, []);
149
- const isOnline = useSyncExternalStore2(subscribe2, getSnapshot2, getSnapshot2);
150
- return isOnline;
151
- }
152
-
153
- // src/comments/lib/revalidation.ts
105
+ import { useCallback, useEffect as useEffect2, useRef } from "react";
154
106
  var DEFAULT_ERROR_RETRY_INTERVAL = 5e3;
155
107
  var DEFAULT_MAX_ERROR_RETRY_COUNT = 5;
156
108
  var DEFAULT_DEDUPING_INTERVAL = 2e3;
157
109
  var timestamp = 0;
158
110
  function useRevalidateCache(manager, fetcher, options = {}) {
159
- const isOnlineRef = useRef2(true);
111
+ const isOnlineRef = useRef(true);
160
112
  const {
161
113
  dedupingInterval = DEFAULT_DEDUPING_INTERVAL,
162
114
  errorRetryInterval = DEFAULT_ERROR_RETRY_INTERVAL,
163
115
  errorRetryCount = DEFAULT_MAX_ERROR_RETRY_COUNT
164
116
  } = options;
165
- const _revalidateCache = useCallback2(
117
+ const _revalidateCache = useCallback(
166
118
  async ({
167
119
  shouldDedupe,
168
120
  retryCount = 0
@@ -237,7 +189,7 @@ function useRevalidateCache(manager, fetcher, options = {}) {
237
189
  window.removeEventListener("offline", handleIsOffline);
238
190
  };
239
191
  }, []);
240
- const revalidateCache = useCallback2(
192
+ const revalidateCache = useCallback(
241
193
  ({ shouldDedupe }) => {
242
194
  return _revalidateCache({ shouldDedupe, retryCount: 0 });
243
195
  },
@@ -246,7 +198,7 @@ function useRevalidateCache(manager, fetcher, options = {}) {
246
198
  return revalidateCache;
247
199
  }
248
200
  function useMutate(manager, revalidateCache) {
249
- const mutate = useCallback2(
201
+ const mutate = useCallback(
250
202
  async (data, options) => {
251
203
  const beforeMutationTimestamp = ++timestamp;
252
204
  manager.setMutation({
@@ -283,122 +235,15 @@ function useMutate(manager, revalidateCache) {
283
235
  );
284
236
  return mutate;
285
237
  }
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.getError()) {
301
- void revalidateCache({ shouldDedupe: true }).then(
302
- scheduleRevalidation
303
- );
304
- return;
305
- }
306
- scheduleRevalidation();
307
- }, refreshInterval);
308
- }
309
- scheduleRevalidation();
310
- return () => {
311
- window.clearTimeout(revalidationTimerId);
312
- };
313
- }, [revalidateCache, refreshInterval, isOnline, isDocumentVisible, manager]);
314
- useEffect2(() => {
315
- function handleIsOnline() {
316
- if (revalidateOnReconnect && isDocumentVisible) {
317
- void revalidateCache({ shouldDedupe: true });
318
- }
319
- }
320
- window.addEventListener("online", handleIsOnline);
321
- return () => {
322
- window.removeEventListener("online", handleIsOnline);
323
- };
324
- }, [revalidateCache, revalidateOnReconnect, isDocumentVisible]);
325
- useEffect2(() => {
326
- function handleVisibilityChange() {
327
- const isVisible = document.visibilityState === "visible";
328
- if (revalidateOnFocus && isVisible && isOnline) {
329
- void revalidateCache({ shouldDedupe: true });
330
- }
331
- }
332
- document.addEventListener("visibilitychange", handleVisibilityChange);
333
- return () => {
334
- document.removeEventListener("visibilitychange", handleVisibilityChange);
335
- };
336
- }, [revalidateCache, revalidateOnFocus, isOnline]);
337
- }
338
238
 
339
239
  // src/comments/CommentsRoom.tsx
340
- var POLLING_INTERVAL_REALTIME = 3e4;
341
- var POLLING_INTERVAL = 5e3;
342
240
  var THREAD_ID_PREFIX = "th";
343
241
  var COMMENT_ID_PREFIX = "cm";
344
242
  function createCommentsRoom(errorEventSource) {
345
- const manager = createThreadsCacheManager();
346
- const filterOptions = /* @__PURE__ */ new Map();
347
- const cacheStates = /* @__PURE__ */ new Map();
348
- const revalidationManagers = /* @__PURE__ */ new Map();
349
- function createThreadsRevalidationManager(key) {
350
- let request;
351
- let error;
352
- return {
353
- getCache() {
354
- return void 0;
355
- },
356
- setCache(value) {
357
- const cache = new Map(
358
- (manager.getCache() ?? []).map((thread) => [thread.id, thread])
359
- );
360
- for (const thread of value) {
361
- cache.set(thread.id, thread);
362
- }
363
- setCache(key, {
364
- isLoading: false,
365
- data: value
366
- });
367
- manager.setCache(Array.from(cache.values()));
368
- },
369
- // Request
370
- getRequest() {
371
- return request;
372
- },
373
- setRequest(value) {
374
- request = value;
375
- },
376
- // Error
377
- getError() {
378
- return error;
379
- },
380
- setError(err) {
381
- error = err;
382
- manager.setError(err);
383
- },
384
- // Mutation
385
- getMutation() {
386
- return void 0;
387
- },
388
- setMutation() {
389
- }
390
- };
391
- }
392
- const eventSource = makeEventSource();
393
- const subscribe2 = eventSource.subscribe;
394
- const getCache = (key) => cacheStates.get(key);
395
- const setCache = (key, value) => {
396
- cacheStates.set(key, value);
397
- };
243
+ const store = createClientCacheStore();
398
244
  const FetcherContext = createContext(null);
399
- const CommentsEventSubscriptionContext = createContext(() => {
400
- });
401
- function getThreads() {
245
+ const RoomManagerContext = createContext(null);
246
+ function getThreads(manager) {
402
247
  const threads = manager.getCache();
403
248
  if (!threads) {
404
249
  throw new Error(
@@ -411,84 +256,54 @@ function createCommentsRoom(errorEventSource) {
411
256
  room,
412
257
  children
413
258
  }) {
414
- const commentsEventSubscribersCountRef = useRef3(0);
415
- const commentsEventDisposerRef = useRef3();
416
- const fetcher = useCallback3(async () => {
259
+ const manager = useMemo(() => {
260
+ return createRoomRevalidationManager(room.id, {
261
+ getCache: store.getThreads,
262
+ setCache: store.setThreads
263
+ });
264
+ }, [room.id]);
265
+ const fetcher = React2.useCallback(async () => {
266
+ const options = manager.getRevalidationManagers().filter(([key]) => manager.getReferenceCount(key) > 0).map(([_, manager2]) => manager2.getOptions());
417
267
  const responses = await Promise.all(
418
- Array.from(filterOptions.values()).map((info) => {
419
- return room.getThreads(info.options);
268
+ options.map(async (option) => {
269
+ return await room.getThreads(option);
420
270
  })
421
271
  );
422
272
  const threads = Array.from(
423
273
  new Map(responses.flat().map((thread) => [thread.id, thread])).values()
424
274
  );
425
275
  return threads;
426
- }, [room]);
276
+ }, [room, manager]);
427
277
  const revalidateCache = useRevalidateCache(manager, fetcher);
428
- const subscribeToCommentEvents = useCallback3(() => {
429
- const commentsEventSubscribersCount = commentsEventSubscribersCountRef.current;
430
- if (commentsEventSubscribersCount === 0) {
431
- const unsubscribe = room.events.comments.subscribe(() => {
432
- void revalidateCache({ shouldDedupe: true });
433
- });
434
- commentsEventDisposerRef.current = unsubscribe;
435
- }
436
- commentsEventSubscribersCountRef.current = commentsEventSubscribersCount + 1;
437
- return () => {
438
- commentsEventSubscribersCountRef.current = commentsEventSubscribersCountRef.current - 1;
439
- if (commentsEventSubscribersCountRef.current > 0)
440
- return;
441
- commentsEventDisposerRef.current?.();
442
- commentsEventDisposerRef.current = void 0;
443
- };
444
- }, [revalidateCache, room]);
445
278
  useEffect3(() => {
446
- const unsubscribe = manager.subscribe("cache", (threads) => {
447
- for (const [key, info] of filterOptions.entries()) {
448
- const filtered = threads.filter((thread) => {
449
- const query = info.options.query;
450
- if (!query)
451
- return true;
452
- for (const key2 in query.metadata) {
453
- if (thread.metadata[key2] !== query.metadata[key2]) {
454
- return false;
455
- }
456
- }
457
- return true;
458
- });
459
- setCache(key, {
460
- isLoading: false,
461
- data: filtered
462
- });
463
- }
464
- for (const [key] of cacheStates.entries()) {
465
- if (filterOptions.has(key))
466
- continue;
467
- cacheStates.delete(key);
468
- }
469
- eventSource.notify(threads);
279
+ const unsubscribe = room.events.comments.subscribe(() => {
280
+ void revalidateCache({ shouldDedupe: false });
470
281
  });
471
282
  return () => {
472
283
  unsubscribe();
473
284
  };
474
- }, []);
475
- useEffect3(() => {
476
- const unsubscribe = manager.subscribe("error", (error) => {
477
- for (const state of cacheStates.values()) {
478
- state.error = error;
479
- }
480
- });
481
- return () => {
482
- unsubscribe();
483
- };
484
- }, []);
485
- return /* @__PURE__ */ React2.createElement(FetcherContext.Provider, { value: fetcher }, /* @__PURE__ */ React2.createElement(
486
- CommentsEventSubscriptionContext.Provider,
487
- {
488
- value: subscribeToCommentEvents
489
- },
490
- children
491
- ));
285
+ }, [room, revalidateCache]);
286
+ return /* @__PURE__ */ React2.createElement(FetcherContext.Provider, { value: fetcher }, /* @__PURE__ */ React2.createElement(RoomManagerContext.Provider, { value: manager }, children));
287
+ }
288
+ function useRoomManager() {
289
+ const manager = useContext(RoomManagerContext);
290
+ if (manager === null) {
291
+ throw new Error("CommentsRoomProvider is missing from the React tree.");
292
+ }
293
+ return manager;
294
+ }
295
+ function getUseThreadsRevalidationManager(options, roomManager) {
296
+ const key = stringify(options);
297
+ const revalidationManager = roomManager.getRevalidationManager(key);
298
+ if (!revalidationManager) {
299
+ const useThreadsRevalidationManager = createUseThreadsRevalidationManager(
300
+ options,
301
+ roomManager
302
+ );
303
+ roomManager.setRevalidationmanager(key, useThreadsRevalidationManager);
304
+ return useThreadsRevalidationManager;
305
+ }
306
+ return revalidationManager;
492
307
  }
493
308
  function useThreadsFetcher() {
494
309
  const fetcher = useContext(FetcherContext);
@@ -497,156 +312,147 @@ function createCommentsRoom(errorEventSource) {
497
312
  }
498
313
  return fetcher;
499
314
  }
500
- function _useThreads(room, key) {
501
- const fetcher = useThreadsFetcher();
502
- const revalidateCache = useRevalidateCache(manager, fetcher);
503
- const status = useSyncExternalStore3(
504
- room.events.status.subscribe,
505
- room.getStatus,
506
- room.getStatus
507
- );
508
- const isOnline = useIsOnline();
509
- const isDocumentVisible = useIsDocumentVisible();
510
- const subscribeToCommentEvents = useContext(
511
- CommentsEventSubscriptionContext
512
- );
513
- const interval = getPollingInterval(
514
- isOnline,
515
- isDocumentVisible,
516
- status === "connected"
517
- );
518
- useAutomaticRevalidation(manager, revalidateCache, {
519
- revalidateOnFocus: true,
520
- revalidateOnReconnect: true,
521
- refreshInterval: interval
522
- });
523
- useEffect3(subscribeToCommentEvents, [subscribeToCommentEvents]);
524
- const cache = useSyncExternalStore3(
525
- subscribe2,
526
- () => getCache(key),
527
- () => getCache(key)
528
- );
529
- if (!cache || cache.isLoading) {
530
- return { isLoading: true };
531
- }
532
- return {
533
- isLoading: cache.isLoading,
534
- threads: cache.data || [],
535
- error: cache.error
536
- };
537
- }
538
315
  function useThreads(room, options = { query: { metadata: {} } }) {
539
316
  const key = useMemo(() => stringify(options), [options]);
540
- let revalidationManager = revalidationManagers.get(key);
541
- if (!revalidationManager) {
542
- revalidationManager = createThreadsRevalidationManager(key);
543
- revalidationManagers.set(key, revalidationManager);
544
- }
545
- const fetcher = useCallback3(
317
+ const manager = useRoomManager();
318
+ const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
319
+ options,
320
+ manager
321
+ );
322
+ const fetcher = React2.useCallback(
546
323
  () => {
547
324
  return room.getThreads(options);
548
325
  },
549
- // eslint-disable-next-line react-hooks/exhaustive-deps
550
- [key]
326
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
327
+ [key, room]
551
328
  );
552
- const revalidateCache = useRevalidateCache(revalidationManager, fetcher);
553
- useEffect3(
554
- () => {
555
- const info = filterOptions.get(key);
556
- if (info) {
557
- info.count += 1;
558
- } else {
559
- filterOptions.set(key, {
560
- options,
561
- count: 1
562
- });
563
- cacheStates.set(key, { isLoading: true });
564
- }
565
- return () => {
566
- const info2 = filterOptions.get(key);
567
- if (!info2)
568
- return;
569
- info2.count -= 1;
570
- if (info2.count > 0)
571
- return;
572
- filterOptions.delete(key);
573
- };
574
- },
575
- // eslint-disable-next-line react-hooks/exhaustive-deps
576
- [key]
329
+ const revalidateCache = useRevalidateCache(
330
+ useThreadsRevalidationManager,
331
+ fetcher
577
332
  );
578
333
  useEffect3(() => {
579
334
  void revalidateCache({ shouldDedupe: true });
580
335
  }, [revalidateCache]);
581
- return _useThreads(room, key);
336
+ useEffect3(() => {
337
+ manager.incrementReferenceCount(key);
338
+ return () => {
339
+ manager.decrementReferenceCount(key);
340
+ };
341
+ });
342
+ return useSyncExternalStoreWithSelector(
343
+ store.subscribe,
344
+ () => store.getThreads(),
345
+ () => store.getThreads(),
346
+ (state) => {
347
+ const isLoading = useThreadsRevalidationManager.getIsLoading();
348
+ if (isLoading) {
349
+ return {
350
+ isLoading: true
351
+ };
352
+ }
353
+ const options2 = useThreadsRevalidationManager.getOptions();
354
+ const error = useThreadsRevalidationManager.getError();
355
+ const filtered = state.filter((thread) => {
356
+ if (thread.roomId !== room.id)
357
+ return false;
358
+ const query = options2.query ?? {};
359
+ for (const key2 in query.metadata) {
360
+ if (thread.metadata[key2] !== query.metadata[key2]) {
361
+ return false;
362
+ }
363
+ }
364
+ return true;
365
+ });
366
+ return {
367
+ isLoading: false,
368
+ threads: filtered,
369
+ error
370
+ };
371
+ }
372
+ );
582
373
  }
583
- function useThreadsSuspense(room, options = {}) {
374
+ function useThreadsSuspense(room, options = { query: { metadata: {} } }) {
584
375
  const key = useMemo(() => stringify(options), [options]);
585
- let revalidationManager = revalidationManagers.get(key);
586
- if (!revalidationManager) {
587
- revalidationManager = createThreadsRevalidationManager(key);
588
- revalidationManagers.set(key, revalidationManager);
589
- }
590
- const fetcher = useCallback3(
376
+ const manager = useRoomManager();
377
+ const useThreadsRevalidationManager = getUseThreadsRevalidationManager(
378
+ options,
379
+ manager
380
+ );
381
+ const fetcher = React2.useCallback(
591
382
  () => {
592
383
  return room.getThreads(options);
593
384
  },
594
- // eslint-disable-next-line react-hooks/exhaustive-deps
595
- [key]
385
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- The missing dependency is `options` but `key` and `normalized` are analogous, so we only include `key` as dependency. This helps minimize the number of re-renders as `options` can change on each render
386
+ [key, room]
596
387
  );
597
- const info = filterOptions.get(key);
598
- if (!info) {
599
- filterOptions.set(key, {
600
- options,
601
- count: 0
602
- });
603
- cacheStates.set(key, { isLoading: true });
604
- }
605
- const revalidateCache = useRevalidateCache(revalidationManager, fetcher);
606
- useEffect3(
607
- () => {
608
- const info2 = filterOptions.get(key);
609
- if (info2) {
610
- info2.count += 1;
611
- } else {
612
- filterOptions.set(key, {
613
- options,
614
- count: 1
615
- });
388
+ const revalidateCache = useRevalidateCache(
389
+ useThreadsRevalidationManager,
390
+ fetcher
391
+ );
392
+ useEffect3(() => {
393
+ void revalidateCache({ shouldDedupe: true });
394
+ }, [revalidateCache]);
395
+ useEffect3(() => {
396
+ manager.incrementReferenceCount(key);
397
+ return () => {
398
+ manager.decrementReferenceCount(key);
399
+ };
400
+ });
401
+ const cache = useSyncExternalStoreWithSelector(
402
+ store.subscribe,
403
+ () => store.getThreads(),
404
+ () => store.getThreads(),
405
+ (state) => {
406
+ const isLoading = useThreadsRevalidationManager.getIsLoading();
407
+ if (isLoading) {
408
+ return {
409
+ isLoading: true
410
+ };
616
411
  }
617
- return () => {
618
- const info3 = filterOptions.get(key);
619
- if (!info3)
620
- return;
621
- info3.count -= 1;
622
- if (info3.count > 0)
623
- return;
624
- filterOptions.delete(key);
412
+ const options2 = useThreadsRevalidationManager.getOptions();
413
+ const error = useThreadsRevalidationManager.getError();
414
+ const filtered = state.filter((thread) => {
415
+ if (thread.roomId !== room.id)
416
+ return false;
417
+ const query = options2.query ?? {};
418
+ for (const key2 in query.metadata) {
419
+ if (thread.metadata[key2] !== query.metadata[key2]) {
420
+ return false;
421
+ }
422
+ }
423
+ return true;
424
+ });
425
+ return {
426
+ isLoading: false,
427
+ threads: filtered,
428
+ error
625
429
  };
626
- },
627
- // eslint-disable-next-line react-hooks/exhaustive-deps
628
- [key]
430
+ }
629
431
  );
630
- const cache = _useThreads(room, key);
631
432
  if (cache.error) {
632
433
  throw cache.error;
633
434
  }
634
435
  if (cache.isLoading || !cache.threads) {
635
- throw revalidateCache({ shouldDedupe: true });
436
+ throw revalidateCache({
437
+ shouldDedupe: true
438
+ });
636
439
  }
637
440
  return {
441
+ isLoading: false,
638
442
  threads: cache.threads,
639
- isLoading: false
443
+ error: cache.error
640
444
  };
641
445
  }
642
446
  function useEditThreadMetadata(room) {
643
- const revalidate = useRevalidateCache(manager, room.getThreads);
447
+ const manager = useRoomManager();
448
+ const fetcher = useThreadsFetcher();
449
+ const revalidate = useRevalidateCache(manager, fetcher);
644
450
  const mutate = useMutate(manager, revalidate);
645
- const editThreadMetadata = useCallback3(
451
+ const editThreadMetadata = useCallback2(
646
452
  (options) => {
647
453
  const threadId = options.threadId;
648
454
  const metadata = "metadata" in options ? options.metadata : {};
649
- const threads = getThreads();
455
+ const threads = getThreads(manager);
650
456
  const optimisticData = threads.map(
651
457
  (thread) => thread.id === threadId ? {
652
458
  ...thread,
@@ -672,19 +478,20 @@ function createCommentsRoom(errorEventSource) {
672
478
  );
673
479
  });
674
480
  },
675
- [room, mutate]
481
+ [room, mutate, manager]
676
482
  );
677
483
  return editThreadMetadata;
678
484
  }
679
485
  function useCreateThread(room) {
486
+ const manager = useRoomManager();
680
487
  const fetcher = useThreadsFetcher();
681
488
  const revalidate = useRevalidateCache(manager, fetcher);
682
489
  const mutate = useMutate(manager, revalidate);
683
- const createThread = useCallback3(
490
+ const createThread = useCallback2(
684
491
  (options) => {
685
492
  const body = options.body;
686
493
  const metadata = "metadata" in options ? options.metadata : {};
687
- const threads = getThreads();
494
+ const threads = getThreads(manager);
688
495
  const threadId = createOptimisticId(THREAD_ID_PREFIX);
689
496
  const commentId = createOptimisticId(COMMENT_ID_PREFIX);
690
497
  const now = /* @__PURE__ */ new Date();
@@ -725,17 +532,18 @@ function createCommentsRoom(errorEventSource) {
725
532
  });
726
533
  return newThread;
727
534
  },
728
- [room, mutate]
535
+ [room, mutate, manager]
729
536
  );
730
537
  return createThread;
731
538
  }
732
539
  function useCreateComment(room) {
540
+ const manager = useRoomManager();
733
541
  const fetcher = useThreadsFetcher();
734
542
  const revalidate = useRevalidateCache(manager, fetcher);
735
543
  const mutate = useMutate(manager, revalidate);
736
- const createComment = useCallback3(
544
+ const createComment = useCallback2(
737
545
  ({ threadId, body }) => {
738
- const threads = getThreads();
546
+ const threads = getThreads(manager);
739
547
  const commentId = createOptimisticId(COMMENT_ID_PREFIX);
740
548
  const now = /* @__PURE__ */ new Date();
741
549
  const comment = {
@@ -772,16 +580,18 @@ function createCommentsRoom(errorEventSource) {
772
580
  });
773
581
  return comment;
774
582
  },
775
- [room, mutate]
583
+ [room, mutate, manager]
776
584
  );
777
585
  return createComment;
778
586
  }
779
587
  function useEditComment(room) {
780
- const revalidate = useRevalidateCache(manager, room.getThreads);
588
+ const manager = useRoomManager();
589
+ const fetcher = useThreadsFetcher();
590
+ const revalidate = useRevalidateCache(manager, fetcher);
781
591
  const mutate = useMutate(manager, revalidate);
782
- const editComment = useCallback3(
592
+ const editComment = useCallback2(
783
593
  ({ threadId, commentId, body }) => {
784
- const threads = getThreads();
594
+ const threads = getThreads(manager);
785
595
  const now = /* @__PURE__ */ new Date();
786
596
  const optimisticData = threads.map(
787
597
  (thread) => thread.id === threadId ? {
@@ -812,16 +622,18 @@ function createCommentsRoom(errorEventSource) {
812
622
  );
813
623
  });
814
624
  },
815
- [room, mutate]
625
+ [room, mutate, manager]
816
626
  );
817
627
  return editComment;
818
628
  }
819
629
  function useDeleteComment(room) {
820
- const revalidate = useRevalidateCache(manager, room.getThreads);
630
+ const manager = useRoomManager();
631
+ const fetcher = useThreadsFetcher();
632
+ const revalidate = useRevalidateCache(manager, fetcher);
821
633
  const mutate = useMutate(manager, revalidate);
822
- const deleteComment = useCallback3(
634
+ const deleteComment = useCallback2(
823
635
  ({ threadId, commentId }) => {
824
- const threads = getThreads();
636
+ const threads = getThreads(manager);
825
637
  const now = /* @__PURE__ */ new Date();
826
638
  const newThreads = [];
827
639
  for (const thread of threads) {
@@ -861,16 +673,18 @@ function createCommentsRoom(errorEventSource) {
861
673
  );
862
674
  });
863
675
  },
864
- [room, mutate]
676
+ [room, mutate, manager]
865
677
  );
866
678
  return deleteComment;
867
679
  }
868
680
  function useAddReaction(room) {
869
- const revalidate = useRevalidateCache(manager, room.getThreads);
681
+ const manager = useRoomManager();
682
+ const fetcher = useThreadsFetcher();
683
+ const revalidate = useRevalidateCache(manager, fetcher);
870
684
  const mutate = useMutate(manager, revalidate);
871
- const createComment = useCallback3(
685
+ const createComment = useCallback2(
872
686
  ({ threadId, commentId, emoji }) => {
873
- const threads = getThreads();
687
+ const threads = getThreads(manager);
874
688
  const now = /* @__PURE__ */ new Date();
875
689
  const userId = getCurrentUserId(room);
876
690
  const optimisticData = threads.map(
@@ -924,16 +738,18 @@ function createCommentsRoom(errorEventSource) {
924
738
  );
925
739
  });
926
740
  },
927
- [room, mutate]
741
+ [room, mutate, manager]
928
742
  );
929
743
  return createComment;
930
744
  }
931
745
  function useRemoveReaction(room) {
932
- const revalidate = useRevalidateCache(manager, room.getThreads);
746
+ const manager = useRoomManager();
747
+ const fetcher = useThreadsFetcher();
748
+ const revalidate = useRevalidateCache(manager, fetcher);
933
749
  const mutate = useMutate(manager, revalidate);
934
- const createComment = useCallback3(
750
+ const createComment = useCallback2(
935
751
  ({ threadId, commentId, emoji }) => {
936
- const threads = getThreads();
752
+ const threads = getThreads(manager);
937
753
  const userId = getCurrentUserId(room);
938
754
  const optimisticData = threads.map(
939
755
  (thread) => thread.id === threadId ? {
@@ -985,7 +801,7 @@ function createCommentsRoom(errorEventSource) {
985
801
  );
986
802
  });
987
803
  },
988
- [room, mutate]
804
+ [room, mutate, manager]
989
805
  );
990
806
  return createComment;
991
807
  }
@@ -1021,31 +837,35 @@ function handleCommentsApiError(err) {
1021
837
  }
1022
838
  return new Error(message);
1023
839
  }
1024
- function getPollingInterval(isBrowserOnline, isDocumentVisible, isRoomConnected) {
1025
- if (!isBrowserOnline || !isDocumentVisible)
1026
- return;
1027
- if (isRoomConnected)
1028
- return POLLING_INTERVAL_REALTIME;
1029
- return POLLING_INTERVAL;
1030
- }
1031
- function createThreadsCacheManager() {
1032
- let cache;
840
+ function createRoomRevalidationManager(roomId, {
841
+ getCache,
842
+ setCache
843
+ }) {
1033
844
  let request;
1034
845
  let error;
1035
846
  let mutation;
1036
- const cacheEventSource = makeEventSource();
1037
- const errorEventSource = makeEventSource();
847
+ const revalidationManagerByOptions = /* @__PURE__ */ new Map();
848
+ const referenceCountByOptions = /* @__PURE__ */ new Map();
1038
849
  return {
1039
850
  // Cache
1040
851
  getCache() {
1041
- return cache;
852
+ const threads = getCache();
853
+ const filtered = threads.filter((thread) => thread.roomId === roomId);
854
+ return filtered;
1042
855
  },
1043
856
  setCache(value) {
857
+ for (const key of revalidationManagerByOptions.keys()) {
858
+ if (referenceCountByOptions.get(key) === 0) {
859
+ revalidationManagerByOptions.delete(key);
860
+ referenceCountByOptions.delete(key);
861
+ }
862
+ }
1044
863
  const sorted = value.sort(
1045
864
  (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
1046
865
  );
1047
- cache = sorted;
1048
- cacheEventSource.notify(cache);
866
+ const threads = getCache();
867
+ const newThreads = threads.filter((thread) => thread.roomId !== roomId).concat(sorted);
868
+ setCache(newThreads);
1049
869
  },
1050
870
  // Request
1051
871
  getRequest() {
@@ -1060,7 +880,6 @@ function createThreadsCacheManager() {
1060
880
  },
1061
881
  setError(err) {
1062
882
  error = err;
1063
- errorEventSource.notify(err);
1064
883
  },
1065
884
  // Mutation
1066
885
  getMutation() {
@@ -1069,25 +888,103 @@ function createThreadsCacheManager() {
1069
888
  setMutation(info) {
1070
889
  mutation = info;
1071
890
  },
1072
- // Subscription
1073
- subscribe(type, callback) {
1074
- switch (type) {
1075
- case "cache":
1076
- return cacheEventSource.subscribe(
1077
- callback
1078
- );
1079
- case "error":
1080
- return errorEventSource.subscribe(callback);
891
+ getRevalidationManagers() {
892
+ return Array.from(revalidationManagerByOptions.entries());
893
+ },
894
+ getRevalidationManager(key) {
895
+ return revalidationManagerByOptions.get(key);
896
+ },
897
+ setRevalidationmanager(key, manager) {
898
+ revalidationManagerByOptions.set(key, manager);
899
+ },
900
+ incrementReferenceCount(key) {
901
+ const count = referenceCountByOptions.get(key) ?? 0;
902
+ referenceCountByOptions.set(key, count + 1);
903
+ },
904
+ decrementReferenceCount(key) {
905
+ const count = referenceCountByOptions.get(key) ?? 0;
906
+ referenceCountByOptions.set(key, count - 1);
907
+ },
908
+ getReferenceCount(key) {
909
+ return referenceCountByOptions.get(key) ?? 0;
910
+ }
911
+ };
912
+ }
913
+ function createClientCacheStore() {
914
+ let threads = [];
915
+ const threadsEventSource = makeEventSource();
916
+ return {
917
+ getThreads() {
918
+ return threads;
919
+ },
920
+ setThreads(value) {
921
+ threads = value;
922
+ threadsEventSource.notify(threads);
923
+ },
924
+ subscribe(callback) {
925
+ return threadsEventSource.subscribe(callback);
926
+ }
927
+ };
928
+ }
929
+ function createUseThreadsRevalidationManager(options, manager) {
930
+ let isLoading = true;
931
+ let request;
932
+ let error;
933
+ const errorEventSource = makeEventSource();
934
+ return {
935
+ // Cache
936
+ getCache() {
937
+ return void 0;
938
+ },
939
+ setCache(value) {
940
+ const cache = new Map(
941
+ (manager.getCache() ?? []).map((thread) => [thread.id, thread])
942
+ );
943
+ for (const thread of value) {
944
+ cache.set(thread.id, thread);
1081
945
  }
946
+ manager.setCache(Array.from(cache.values()));
947
+ isLoading = false;
948
+ },
949
+ // Request
950
+ getRequest() {
951
+ return request;
952
+ },
953
+ setRequest(value) {
954
+ request = value;
955
+ },
956
+ // Error
957
+ getError() {
958
+ return error;
959
+ },
960
+ setError(err) {
961
+ error = err;
962
+ errorEventSource.notify(err);
963
+ },
964
+ // Mutation
965
+ getMutation() {
966
+ return void 0;
967
+ },
968
+ setMutation(_) {
969
+ return;
970
+ },
971
+ getOptions() {
972
+ return options;
973
+ },
974
+ getIsLoading() {
975
+ return isLoading;
976
+ },
977
+ setIsLoading(value) {
978
+ isLoading = value;
1082
979
  }
1083
980
  };
1084
981
  }
1085
982
 
1086
983
  // src/comments/lib/use-debounce.ts
1087
- import { useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
984
+ import { useEffect as useEffect4, useRef as useRef2, useState as useState2 } from "react";
1088
985
  var DEFAULT_DELAY = 500;
1089
986
  function useDebounce(value, delay = DEFAULT_DELAY) {
1090
- const timeout = useRef4();
987
+ const timeout = useRef2();
1091
988
  const [debouncedValue, setDebouncedValue] = useState2(value);
1092
989
  useEffect4(() => {
1093
990
  if (delay === false) {
@@ -1108,8 +1005,8 @@ function useDebounce(value, delay = DEFAULT_DELAY) {
1108
1005
  }
1109
1006
 
1110
1007
  // src/lib/use-async-cache.ts
1111
- import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo2, useRef as useRef5 } from "react";
1112
- import { useSyncExternalStore as useSyncExternalStore4 } from "use-sync-external-store/shim/index.js";
1008
+ import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo2, useRef as useRef3 } from "react";
1009
+ import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
1113
1010
 
1114
1011
  // src/lib/use-initial.ts
1115
1012
  import { useState as useState3 } from "react";
@@ -1135,17 +1032,17 @@ function useAsyncCache(cache, key, options) {
1135
1032
  void cacheItem2.get();
1136
1033
  return cacheItem2;
1137
1034
  }, [cache, key]);
1138
- const subscribe2 = useCallback4(
1035
+ const subscribe = useCallback3(
1139
1036
  (callback) => cacheItem?.subscribe(callback) ?? noop,
1140
1037
  [cacheItem]
1141
1038
  );
1142
- const getState = useCallback4(
1039
+ const getState = useCallback3(
1143
1040
  () => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
1144
1041
  [cacheItem]
1145
1042
  );
1146
- const revalidate = useCallback4(() => cacheItem?.revalidate(), [cacheItem]);
1147
- const state = useSyncExternalStore4(subscribe2, getState, getState);
1148
- const previousData = useRef5();
1043
+ const revalidate = useCallback3(() => cacheItem?.revalidate(), [cacheItem]);
1044
+ const state = useSyncExternalStore(subscribe, getState, getState);
1045
+ const previousData = useRef3();
1149
1046
  let data = state.data;
1150
1047
  useEffect5(() => {
1151
1048
  previousData.current = { key, data: state.data };
@@ -1186,9 +1083,9 @@ function useAsyncCache(cache, key, options) {
1186
1083
  }
1187
1084
 
1188
1085
  // src/lib/use-latest.ts
1189
- import { useEffect as useEffect6, useRef as useRef6 } from "react";
1086
+ import { useEffect as useEffect6, useRef as useRef4 } from "react";
1190
1087
  function useLatest(value) {
1191
- const ref = useRef6(value);
1088
+ const ref = useRef4(value);
1192
1089
  useEffect6(() => {
1193
1090
  ref.current = value;
1194
1091
  }, [value]);
@@ -1224,8 +1121,8 @@ var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\
1224
1121
 
1225
1122
  Why? Please see https://liveblocks.io/docs/platform/troubleshooting#stale-props-zombie-child for more information`;
1226
1123
  var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
1227
- function useSyncExternalStore5(s, gs, gss) {
1228
- return useSyncExternalStoreWithSelector(s, gs, gss, identity);
1124
+ function useSyncExternalStore2(s, gs, gss) {
1125
+ return useSyncExternalStoreWithSelector2(s, gs, gss, identity);
1229
1126
  }
1230
1127
  var STABLE_EMPTY_LIST = Object.freeze([]);
1231
1128
  function alwaysEmptyList() {
@@ -1373,16 +1270,16 @@ function createRoomContext(client, options) {
1373
1270
  }
1374
1271
  function useStatus() {
1375
1272
  const room = useRoom();
1376
- const subscribe2 = room.events.status.subscribe;
1377
- const getSnapshot2 = room.getStatus;
1273
+ const subscribe = room.events.status.subscribe;
1274
+ const getSnapshot = room.getStatus;
1378
1275
  const getServerSnapshot = room.getStatus;
1379
- return useSyncExternalStore5(subscribe2, getSnapshot2, getServerSnapshot);
1276
+ return useSyncExternalStore2(subscribe, getSnapshot, getServerSnapshot);
1380
1277
  }
1381
1278
  function useMyPresence() {
1382
1279
  const room = useRoom();
1383
- const subscribe2 = room.events.myPresence.subscribe;
1384
- const getSnapshot2 = room.getPresence;
1385
- const presence = useSyncExternalStore5(subscribe2, getSnapshot2, getSnapshot2);
1280
+ const subscribe = room.events.myPresence.subscribe;
1281
+ const getSnapshot = room.getPresence;
1282
+ const presence = useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
1386
1283
  const setPresence = room.updatePresence;
1387
1284
  return [presence, setPresence];
1388
1285
  }
@@ -1391,12 +1288,12 @@ function createRoomContext(client, options) {
1391
1288
  }
1392
1289
  function useOthers(selector, isEqual) {
1393
1290
  const room = useRoom();
1394
- const subscribe2 = room.events.others.subscribe;
1395
- const getSnapshot2 = room.getOthers;
1291
+ const subscribe = room.events.others.subscribe;
1292
+ const getSnapshot = room.getOthers;
1396
1293
  const getServerSnapshot = alwaysEmptyList;
1397
- return useSyncExternalStoreWithSelector(
1398
- subscribe2,
1399
- getSnapshot2,
1294
+ return useSyncExternalStoreWithSelector2(
1295
+ subscribe,
1296
+ getSnapshot,
1400
1297
  getServerSnapshot,
1401
1298
  selector ?? identity,
1402
1299
  isEqual
@@ -1500,17 +1397,17 @@ function createRoomContext(client, options) {
1500
1397
  }
1501
1398
  function useSelf(maybeSelector, isEqual) {
1502
1399
  const room = useRoom();
1503
- const subscribe2 = room.events.self.subscribe;
1504
- const getSnapshot2 = room.getSelf;
1400
+ const subscribe = room.events.self.subscribe;
1401
+ const getSnapshot = room.getSelf;
1505
1402
  const selector = maybeSelector ?? identity;
1506
1403
  const wrappedSelector = React3.useCallback(
1507
1404
  (me) => me !== null ? selector(me) : null,
1508
1405
  [selector]
1509
1406
  );
1510
1407
  const getServerSnapshot = alwaysNull;
1511
- return useSyncExternalStoreWithSelector(
1512
- subscribe2,
1513
- getSnapshot2,
1408
+ return useSyncExternalStoreWithSelector2(
1409
+ subscribe,
1410
+ getSnapshot,
1514
1411
  getServerSnapshot,
1515
1412
  wrappedSelector,
1516
1413
  isEqual
@@ -1518,10 +1415,10 @@ function createRoomContext(client, options) {
1518
1415
  }
1519
1416
  function useMutableStorageRoot() {
1520
1417
  const room = useRoom();
1521
- const subscribe2 = room.events.storageDidLoad.subscribeOnce;
1522
- const getSnapshot2 = room.getStorageSnapshot;
1418
+ const subscribe = room.events.storageDidLoad.subscribeOnce;
1419
+ const getSnapshot = room.getStorageSnapshot;
1523
1420
  const getServerSnapshot = alwaysNull;
1524
- return useSyncExternalStore5(subscribe2, getSnapshot2, getServerSnapshot);
1421
+ return useSyncExternalStore2(subscribe, getSnapshot, getServerSnapshot);
1525
1422
  }
1526
1423
  function useStorageRoot() {
1527
1424
  return [useMutableStorageRoot()];
@@ -1537,15 +1434,15 @@ function createRoomContext(client, options) {
1537
1434
  }
1538
1435
  function useCanUndo() {
1539
1436
  const room = useRoom();
1540
- const subscribe2 = room.events.history.subscribe;
1437
+ const subscribe = room.events.history.subscribe;
1541
1438
  const canUndo = room.history.canUndo;
1542
- return useSyncExternalStore5(subscribe2, canUndo, canUndo);
1439
+ return useSyncExternalStore2(subscribe, canUndo, canUndo);
1543
1440
  }
1544
1441
  function useCanRedo() {
1545
1442
  const room = useRoom();
1546
- const subscribe2 = room.events.history.subscribe;
1443
+ const subscribe = room.events.history.subscribe;
1547
1444
  const canRedo = room.history.canRedo;
1548
- return useSyncExternalStore5(subscribe2, canRedo, canRedo);
1445
+ return useSyncExternalStore2(subscribe, canRedo, canRedo);
1549
1446
  }
1550
1447
  function useBatch() {
1551
1448
  return useRoom().batch;
@@ -1594,11 +1491,11 @@ function createRoomContext(client, options) {
1594
1491
  (rootOrNull2) => rootOrNull2 !== null ? selector(rootOrNull2) : null,
1595
1492
  [selector]
1596
1493
  );
1597
- const subscribe2 = React3.useCallback(
1494
+ const subscribe = React3.useCallback(
1598
1495
  (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop2,
1599
1496
  [room, rootOrNull]
1600
1497
  );
1601
- const getSnapshot2 = React3.useCallback(() => {
1498
+ const getSnapshot = React3.useCallback(() => {
1602
1499
  if (rootOrNull === null) {
1603
1500
  return null;
1604
1501
  } else {
@@ -1608,9 +1505,9 @@ function createRoomContext(client, options) {
1608
1505
  }
1609
1506
  }, [rootOrNull]);
1610
1507
  const getServerSnapshot = alwaysNull;
1611
- return useSyncExternalStoreWithSelector(
1612
- subscribe2,
1613
- getSnapshot2,
1508
+ return useSyncExternalStoreWithSelector2(
1509
+ subscribe,
1510
+ getSnapshot,
1614
1511
  getServerSnapshot,
1615
1512
  wrappedSelector,
1616
1513
  isEqual