@liveblocks/react 2.15.1 → 2.15.2

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.
@@ -85,6 +85,18 @@ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnaps
85
85
  return value;
86
86
  }
87
87
 
88
+ // src/use-signal.ts
89
+ var identity = (value) => value;
90
+ function useSignal(signal, selector, isEqual) {
91
+ return useSyncExternalStoreWithSelector(
92
+ signal.subscribe,
93
+ signal.get,
94
+ signal.get,
95
+ selector ?? identity,
96
+ isEqual
97
+ );
98
+ }
99
+
88
100
  // src/liveblocks.tsx
89
101
  import {
90
102
  assert,
@@ -121,19 +133,30 @@ var config = {
121
133
  NOTIFICATION_SETTINGS_MAX_STALE_TIME: 5 * SECONDS
122
134
  };
123
135
 
124
- // src/lib/shallow2.ts
125
- import { isPlainObject, shallow } from "@liveblocks/core";
126
- function shallow2(a, b) {
127
- if (!isPlainObject(a) || !isPlainObject(b)) {
128
- return shallow(a, b);
136
+ // src/lib/AsyncResult.ts
137
+ var ASYNC_LOADING = Object.freeze({ isLoading: true });
138
+ var ASYNC_ERR = (error) => Object.freeze({ isLoading: false, error });
139
+ function ASYNC_OK(fieldOrData, data) {
140
+ if (arguments.length === 1) {
141
+ return Object.freeze({ isLoading: false, data: fieldOrData });
142
+ } else {
143
+ return Object.freeze({ isLoading: false, [fieldOrData]: data });
129
144
  }
130
- const keysA = Object.keys(a);
131
- if (keysA.length !== Object.keys(b).length) {
132
- return false;
145
+ }
146
+
147
+ // src/lib/itertools.ts
148
+ function find(it, predicate) {
149
+ for (const item of it) {
150
+ if (predicate(item)) return item;
133
151
  }
134
- return keysA.every(
135
- (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
136
- );
152
+ return void 0;
153
+ }
154
+ function count(it, predicate) {
155
+ let total = 0;
156
+ for (const item of it) {
157
+ if (predicate(item)) total++;
158
+ }
159
+ return total;
137
160
  }
138
161
 
139
162
  // src/lib/use-initial.ts
@@ -199,16 +222,17 @@ import {
199
222
  batch as batch2,
200
223
  compactObject,
201
224
  console as console2,
225
+ DefaultMap,
202
226
  DerivedSignal,
203
227
  HttpError,
204
228
  kInternal,
205
- makeEventSource,
206
229
  MutableSignal as MutableSignal2,
207
230
  nanoid,
208
231
  nn,
209
232
  shallow as shallow3,
210
233
  Signal,
211
- stringify
234
+ stringify,
235
+ unstringify
212
236
  } from "@liveblocks/core";
213
237
 
214
238
  // src/lib/autobind.ts
@@ -228,12 +252,19 @@ function autobind(self) {
228
252
  } while ((obj = Reflect.getPrototypeOf(obj)) && obj !== Object.prototype);
229
253
  }
230
254
 
231
- // src/lib/itertools.ts
232
- function find(it, predicate) {
233
- for (const item of it) {
234
- if (predicate(item)) return item;
255
+ // src/lib/shallow2.ts
256
+ import { isPlainObject, shallow } from "@liveblocks/core";
257
+ function shallow2(a, b) {
258
+ if (!isPlainObject(a) || !isPlainObject(b)) {
259
+ return shallow(a, b);
235
260
  }
236
- return void 0;
261
+ const keysA = Object.keys(a);
262
+ if (keysA.length !== Object.keys(b).length) {
263
+ return false;
264
+ }
265
+ return keysA.every(
266
+ (key) => Object.prototype.hasOwnProperty.call(b, key) && shallow(a[key], b[key])
267
+ );
237
268
  }
238
269
 
239
270
  // src/ThreadDB.ts
@@ -355,12 +386,12 @@ var ThreadDB = class _ThreadDB {
355
386
  this.upsert(thread);
356
387
  }
357
388
  }
358
- applyDelta(updates) {
389
+ applyDelta(newThreads, deletedThreads) {
359
390
  batch(() => {
360
- for (const thread of updates.newThreads) {
391
+ for (const thread of newThreads) {
361
392
  this.upsertIfNewer(thread);
362
393
  }
363
- for (const { id, deletedAt } of updates.deletedThreads) {
394
+ for (const { id, deletedAt } of deletedThreads) {
364
395
  const existing = this.getEvenIfDeleted(id);
365
396
  if (!existing) continue;
366
397
  this.delete(id, deletedAt);
@@ -401,16 +432,10 @@ var ThreadDB = class _ThreadDB {
401
432
 
402
433
  // src/umbrella-store.ts
403
434
  function makeRoomThreadsQueryKey(roomId, query) {
404
- return `${roomId}-${stringify(query ?? {})}`;
435
+ return stringify([roomId, query ?? {}]);
405
436
  }
406
437
  function makeUserThreadsQueryKey(query) {
407
- return `USER_THREADS:${stringify(query ?? {})}`;
408
- }
409
- function makeNotificationSettingsQueryKey(roomId) {
410
- return `${roomId}:NOTIFICATION_SETTINGS`;
411
- }
412
- function makeVersionsQueryKey(roomId) {
413
- return `${roomId}-VERSIONS`;
438
+ return stringify(query ?? {});
414
439
  }
415
440
  function usify(promise) {
416
441
  if ("status" in promise) {
@@ -431,53 +456,50 @@ function usify(promise) {
431
456
  return usable;
432
457
  }
433
458
  var noop2 = Promise.resolve();
434
- var ASYNC_LOADING = Object.freeze({ isLoading: true });
435
459
  var PaginatedResource = class {
436
- observable;
437
- #eventSource;
460
+ #signal;
461
+ signal;
438
462
  #fetchPage;
439
- #paginationState;
440
- // Should be null while in loading or error state!
441
463
  #pendingFetchMore;
442
464
  constructor(fetchPage) {
443
- this.#paginationState = null;
465
+ this.#signal = new Signal(ASYNC_LOADING);
444
466
  this.#fetchPage = fetchPage;
445
- this.#eventSource = makeEventSource();
446
467
  this.#pendingFetchMore = null;
447
- this.observable = this.#eventSource.observable;
468
+ this.signal = this.#signal.asReadonly();
448
469
  autobind(this);
449
470
  }
450
- #patchPaginationState(patch) {
451
- const state = this.#paginationState;
452
- if (state === null) return;
453
- this.#paginationState = { ...state, ...patch };
454
- this.#eventSource.notify();
471
+ get() {
472
+ return this.#signal.get();
473
+ }
474
+ #patch(patch) {
475
+ const state = this.#signal.get();
476
+ if (state.data === void 0) return;
477
+ this.#signal.set(ASYNC_OK({ ...state.data, ...patch }));
455
478
  }
456
479
  async #fetchMore() {
457
- const state = this.#paginationState;
458
- if (!state?.cursor) {
480
+ const state = this.#signal.get();
481
+ if (!state.data?.cursor || state.data.isFetchingMore) {
459
482
  return;
460
483
  }
461
- this.#patchPaginationState({ isFetchingMore: true });
484
+ this.#patch({ isFetchingMore: true });
462
485
  try {
463
- const nextCursor = await this.#fetchPage(state.cursor);
464
- this.#patchPaginationState({
486
+ const nextCursor = await this.#fetchPage(state.data.cursor);
487
+ this.#patch({
465
488
  cursor: nextCursor,
489
+ hasFetchedAll: nextCursor === null,
466
490
  fetchMoreError: void 0,
467
491
  isFetchingMore: false
468
492
  });
469
493
  } catch (err) {
470
- this.#patchPaginationState({
494
+ this.#patch({
471
495
  isFetchingMore: false,
472
496
  fetchMoreError: err
473
497
  });
474
498
  }
475
499
  }
476
500
  fetchMore() {
477
- const state = this.#paginationState;
478
- if (state?.cursor === null) {
479
- return noop2;
480
- }
501
+ const state = this.#signal.get();
502
+ if (!state.data?.cursor) return noop2;
481
503
  if (!this.#pendingFetchMore) {
482
504
  this.#pendingFetchMore = this.#fetchMore().finally(() => {
483
505
  this.#pendingFetchMore = null;
@@ -485,31 +507,12 @@ var PaginatedResource = class {
485
507
  }
486
508
  return this.#pendingFetchMore;
487
509
  }
488
- get() {
489
- const usable = this.#cachedPromise;
490
- if (usable === null || usable.status === "pending") {
491
- return ASYNC_LOADING;
492
- }
493
- if (usable.status === "rejected") {
494
- return { isLoading: false, error: usable.reason };
495
- }
496
- const state = this.#paginationState;
497
- return {
498
- isLoading: false,
499
- data: {
500
- fetchMore: this.fetchMore,
501
- isFetchingMore: state.isFetchingMore,
502
- fetchMoreError: state.fetchMoreError,
503
- hasFetchedAll: state.cursor === null
504
- }
505
- };
506
- }
507
510
  #cachedPromise = null;
508
511
  waitUntilLoaded() {
509
512
  if (this.#cachedPromise) {
510
513
  return this.#cachedPromise;
511
514
  }
512
- const initialFetcher = autoRetry(
515
+ const initialPageFetch$ = autoRetry(
513
516
  () => this.#fetchPage(
514
517
  /* cursor */
515
518
  void 0
@@ -517,67 +520,64 @@ var PaginatedResource = class {
517
520
  5,
518
521
  [5e3, 5e3, 1e4, 15e3]
519
522
  );
520
- const promise = usify(
521
- initialFetcher.then((cursor) => {
522
- this.#paginationState = {
523
- cursor,
524
- isFetchingMore: false,
525
- fetchMoreError: void 0
526
- };
527
- })
528
- );
523
+ const promise = usify(initialPageFetch$);
529
524
  promise.then(
530
- () => this.#eventSource.notify(),
531
- () => {
532
- this.#eventSource.notify();
525
+ (cursor) => {
526
+ this.#signal.set(
527
+ ASYNC_OK({
528
+ cursor,
529
+ hasFetchedAll: cursor === null,
530
+ isFetchingMore: false,
531
+ fetchMoreError: void 0,
532
+ fetchMore: this.fetchMore
533
+ })
534
+ );
535
+ },
536
+ (err) => {
537
+ this.#signal.set(ASYNC_ERR(err));
533
538
  setTimeout(() => {
534
539
  this.#cachedPromise = null;
535
- this.#eventSource.notify();
540
+ this.#signal.set(ASYNC_LOADING);
536
541
  }, 5e3);
537
542
  }
538
543
  );
539
544
  this.#cachedPromise = promise;
540
- return promise;
545
+ return this.#cachedPromise;
541
546
  }
542
547
  };
543
548
  var SinglePageResource = class {
544
- observable;
545
- #eventSource;
549
+ #signal;
550
+ signal;
546
551
  #fetchPage;
547
552
  constructor(fetchPage) {
553
+ this.#signal = new Signal(ASYNC_LOADING);
554
+ this.signal = this.#signal.asReadonly();
548
555
  this.#fetchPage = fetchPage;
549
- this.#eventSource = makeEventSource();
550
- this.observable = this.#eventSource.observable;
551
556
  autobind(this);
552
557
  }
553
558
  get() {
554
- const usable = this.#cachedPromise;
555
- if (usable === null || usable.status === "pending") {
556
- return ASYNC_LOADING;
557
- } else if (usable.status === "rejected") {
558
- return { isLoading: false, error: usable.reason };
559
- } else {
560
- return { isLoading: false, data: void 0 };
561
- }
559
+ return this.#signal.get();
562
560
  }
563
561
  #cachedPromise = null;
564
562
  waitUntilLoaded() {
565
563
  if (this.#cachedPromise) {
566
564
  return this.#cachedPromise;
567
565
  }
568
- const initialFetcher = autoRetry(
566
+ const initialFetcher$ = autoRetry(
569
567
  () => this.#fetchPage(),
570
568
  5,
571
569
  [5e3, 5e3, 1e4, 15e3]
572
570
  );
573
- const promise = usify(initialFetcher);
571
+ const promise = usify(initialFetcher$);
574
572
  promise.then(
575
- () => this.#eventSource.notify(),
576
573
  () => {
577
- this.#eventSource.notify();
574
+ this.#signal.set(ASYNC_OK(void 0));
575
+ },
576
+ (err) => {
577
+ this.#signal.set(ASYNC_ERR(err));
578
578
  setTimeout(() => {
579
579
  this.#cachedPromise = null;
580
- this.#eventSource.notify();
580
+ this.#signal.set(ASYNC_LOADING);
581
581
  }, 5e3);
582
582
  }
583
583
  );
@@ -610,10 +610,10 @@ function createStore_forNotifications() {
610
610
  function clear() {
611
611
  signal.mutate((lut) => lut.clear());
612
612
  }
613
- function applyDelta(newInboxNotifications, deletedNotifications) {
613
+ function applyDelta(newNotifications, deletedNotifications) {
614
614
  signal.mutate((lut) => {
615
615
  let mutated = false;
616
- for (const n of newInboxNotifications) {
616
+ for (const n of newNotifications) {
617
617
  const existing = lut.get(n.id);
618
618
  if (existing) {
619
619
  const result = compareInboxNotifications(existing, n);
@@ -644,6 +644,11 @@ function createStore_forNotifications() {
644
644
  return true;
645
645
  });
646
646
  }
647
+ function upsert(notification) {
648
+ signal.mutate((lut) => {
649
+ lut.set(notification.id, notification);
650
+ });
651
+ }
647
652
  return {
648
653
  signal: signal.asReadonly(),
649
654
  // Mutations
@@ -653,66 +658,70 @@ function createStore_forNotifications() {
653
658
  applyDelta,
654
659
  clear,
655
660
  updateAssociatedNotification,
656
- // XXX_vincent Remove this eventually
657
- force_set: (mutationCallback) => signal.mutate(mutationCallback),
658
- invalidate: () => signal.mutate()
661
+ upsert
659
662
  };
660
663
  }
661
- function createStore_forRoomNotificationSettings() {
662
- const signal = new MutableSignal2(/* @__PURE__ */ new Map());
664
+ function createStore_forRoomNotificationSettings(updates) {
665
+ const baseSignal = new MutableSignal2(/* @__PURE__ */ new Map());
663
666
  function update(roomId, settings) {
664
- signal.mutate((lut) => {
667
+ baseSignal.mutate((lut) => {
665
668
  lut.set(roomId, settings);
666
669
  });
667
670
  }
668
671
  return {
669
- signal: signal.asReadonly(),
672
+ signal: DerivedSignal.from(
673
+ baseSignal,
674
+ updates,
675
+ (base, updates2) => applyOptimisticUpdates_forSettings(base, updates2)
676
+ ),
670
677
  // Mutations
671
- update,
672
- // XXX_vincent Remove this eventually
673
- invalidate: () => signal.mutate()
678
+ update
674
679
  };
675
680
  }
676
681
  function createStore_forHistoryVersions() {
677
- const signal = new MutableSignal2(/* @__PURE__ */ new Map());
682
+ const baseSignal = new MutableSignal2(
683
+ new DefaultMap(() => /* @__PURE__ */ new Map())
684
+ );
678
685
  function update(roomId, versions) {
679
- signal.mutate((lut) => {
680
- const versionsById = lut.get(roomId) ?? (lut.set(roomId, /* @__PURE__ */ new Map()), lut.get(roomId));
686
+ baseSignal.mutate((lut) => {
687
+ const versionsById = lut.getOrCreate(roomId);
681
688
  for (const version of versions) {
682
689
  versionsById.set(version.id, version);
683
690
  }
684
691
  });
685
692
  }
686
693
  return {
687
- signal: signal.asReadonly(),
694
+ signal: DerivedSignal.from(
695
+ baseSignal,
696
+ (hv) => Object.fromEntries(
697
+ [...hv].map(([roomId, versions]) => [
698
+ roomId,
699
+ Object.fromEntries(versions)
700
+ ])
701
+ )
702
+ ),
688
703
  // Mutations
689
- update,
690
- // XXX_vincent Remove these eventually
691
- force_set: (callback) => signal.mutate(callback),
692
- invalidate: () => signal.mutate()
704
+ update
693
705
  };
694
706
  }
695
707
  function createStore_forPermissionHints() {
696
- const signal = new Signal({});
708
+ const signal = new MutableSignal2(
709
+ new DefaultMap(() => /* @__PURE__ */ new Set())
710
+ );
697
711
  function update(newHints) {
698
- signal.set((prev) => {
699
- const permissionsByRoom = { ...prev };
712
+ signal.mutate((lut) => {
700
713
  for (const [roomId, newPermissions] of Object.entries(newHints)) {
701
- const existing = permissionsByRoom[roomId] ?? /* @__PURE__ */ new Set();
714
+ const existing = lut.getOrCreate(roomId);
702
715
  for (const permission of newPermissions) {
703
716
  existing.add(permission);
704
717
  }
705
- permissionsByRoom[roomId] = existing;
706
718
  }
707
- return permissionsByRoom;
708
719
  });
709
720
  }
710
721
  return {
711
722
  signal: signal.asReadonly(),
712
723
  // Mutations
713
- update,
714
- // XXX_vincent Remove this eventually
715
- invalidate: () => signal.set((store) => ({ ...store }))
724
+ update
716
725
  };
717
726
  }
718
727
  function createStore_forOptimistic(client) {
@@ -736,9 +745,7 @@ function createStore_forOptimistic(client) {
736
745
  signal: signal.asReadonly(),
737
746
  // Mutations
738
747
  add,
739
- remove,
740
- // XXX_vincent Remove this eventually
741
- invalidate: () => signal.set((store) => [...store])
748
+ remove
742
749
  };
743
750
  }
744
751
  var UmbrellaStore = class {
@@ -788,48 +795,37 @@ var UmbrellaStore = class {
788
795
  // threads and notifications separately, but the threadifications signal will
789
796
  // be updated whenever either of them change.
790
797
  //
791
- // XXX_vincent APIs like getRoomThreadsLoadingState should really also be modeled as output signals.
792
- //
793
798
  outputs;
794
799
  // Notifications
795
800
  #notificationsLastRequestedAt = null;
796
801
  // Keeps track of when we successfully requested an inbox notifications update for the last time. Will be `null` as long as the first successful fetch hasn't happened yet.
797
- #notifications;
802
+ #notificationsPaginationState;
798
803
  // Room Threads
799
804
  #roomThreadsLastRequestedAtByRoom = /* @__PURE__ */ new Map();
800
- #roomThreads = /* @__PURE__ */ new Map();
801
805
  // User Threads
802
806
  #userThreadsLastRequestedAt = null;
803
- #userThreads = /* @__PURE__ */ new Map();
804
807
  // Room versions
805
- #roomVersions = /* @__PURE__ */ new Map();
806
808
  #roomVersionsLastRequestedAtByRoom = /* @__PURE__ */ new Map();
807
- // Room notification settings
808
- #roomNotificationSettings = /* @__PURE__ */ new Map();
809
809
  constructor(client) {
810
810
  this.#client = client[kInternal].as();
811
811
  this.optimisticUpdates = createStore_forOptimistic(this.#client);
812
812
  this.permissionHints = createStore_forPermissionHints();
813
- const inboxFetcher = async (cursor) => {
814
- const result = await this.#client.getInboxNotifications({ cursor });
815
- this.updateThreadifications(result.threads, result.inboxNotifications);
816
- if (this.#notificationsLastRequestedAt === null) {
817
- this.#notificationsLastRequestedAt = result.requestedAt;
813
+ this.#notificationsPaginationState = new PaginatedResource(
814
+ async (cursor) => {
815
+ const result = await this.#client.getInboxNotifications({ cursor });
816
+ this.updateThreadifications(result.threads, result.inboxNotifications);
817
+ if (this.#notificationsLastRequestedAt === null) {
818
+ this.#notificationsLastRequestedAt = result.requestedAt;
819
+ }
820
+ const nextCursor = result.nextCursor;
821
+ return nextCursor;
818
822
  }
819
- const nextCursor = result.nextCursor;
820
- return nextCursor;
821
- };
822
- this.#notifications = new PaginatedResource(inboxFetcher);
823
- this.#notifications.observable.subscribe(
824
- () => (
825
- // Note that the store itself does not change, but it's only vehicle at
826
- // the moment to trigger a re-render, so we'll do a no-op update here.
827
- this.invalidateEntireStore()
828
- )
829
823
  );
830
824
  this.threads = new ThreadDB();
831
825
  this.notifications = createStore_forNotifications();
832
- this.roomNotificationSettings = createStore_forRoomNotificationSettings();
826
+ this.roomNotificationSettings = createStore_forRoomNotificationSettings(
827
+ this.optimisticUpdates.signal
828
+ );
833
829
  this.historyVersions = createStore_forHistoryVersions();
834
830
  const threadifications = DerivedSignal.from(
835
831
  this.threads.signal,
@@ -837,13 +833,7 @@ var UmbrellaStore = class {
837
833
  this.optimisticUpdates.signal,
838
834
  (ts, ns, updates) => applyOptimisticUpdates_forThreadifications(ts, ns, updates)
839
835
  );
840
- const threads = DerivedSignal.from(
841
- threadifications,
842
- (s) => ({
843
- threadsDB: s.threadsDB
844
- }),
845
- shallow3
846
- );
836
+ const threads = DerivedSignal.from(threadifications, (s) => s.threadsDB);
847
837
  const notifications = DerivedSignal.from(
848
838
  threadifications,
849
839
  (s) => ({
@@ -852,177 +842,181 @@ var UmbrellaStore = class {
852
842
  }),
853
843
  shallow3
854
844
  );
855
- const settingsByRoomId = DerivedSignal.from(
856
- this.roomNotificationSettings.signal,
857
- this.optimisticUpdates.signal,
858
- (settings, updates) => applyOptimisticUpdates_forSettings(settings, updates)
845
+ const loadingUserThreads = new DefaultMap(
846
+ (queryKey) => {
847
+ const query = unstringify(queryKey);
848
+ const resource = new PaginatedResource(async (cursor) => {
849
+ const result = await this.#client[kInternal].httpClient.getUserThreads_experimental({
850
+ cursor,
851
+ query
852
+ });
853
+ this.updateThreadifications(
854
+ result.threads,
855
+ result.inboxNotifications
856
+ );
857
+ this.permissionHints.update(result.permissionHints);
858
+ if (this.#userThreadsLastRequestedAt === null) {
859
+ this.#userThreadsLastRequestedAt = result.requestedAt;
860
+ }
861
+ return result.nextCursor;
862
+ });
863
+ const signal = DerivedSignal.from(() => {
864
+ const result = resource.get();
865
+ if (result.isLoading || result.error) {
866
+ return result;
867
+ }
868
+ const threads2 = this.outputs.threads.get().findMany(
869
+ void 0,
870
+ // Do _not_ filter by roomId
871
+ query ?? {},
872
+ "desc"
873
+ );
874
+ const page = result.data;
875
+ return {
876
+ isLoading: false,
877
+ threads: threads2,
878
+ hasFetchedAll: page.hasFetchedAll,
879
+ isFetchingMore: page.isFetchingMore,
880
+ fetchMoreError: page.fetchMoreError,
881
+ fetchMore: page.fetchMore
882
+ };
883
+ }, shallow2);
884
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
885
+ }
859
886
  );
860
- const versionsByRoomId = DerivedSignal.from(
861
- this.historyVersions.signal,
862
- (hv) => Object.fromEntries(
863
- [...hv].map(([roomId, versions]) => [
864
- roomId,
865
- Object.fromEntries(versions)
866
- ])
867
- )
887
+ const loadingRoomThreads = new DefaultMap(
888
+ (queryKey) => {
889
+ const [roomId, query] = unstringify(queryKey);
890
+ const resource = new PaginatedResource(async (cursor) => {
891
+ const result = await this.#client[kInternal].httpClient.getThreads({
892
+ roomId,
893
+ cursor,
894
+ query
895
+ });
896
+ this.updateThreadifications(
897
+ result.threads,
898
+ result.inboxNotifications
899
+ );
900
+ this.permissionHints.update(result.permissionHints);
901
+ const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
902
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
903
+ this.#roomThreadsLastRequestedAtByRoom.set(
904
+ roomId,
905
+ result.requestedAt
906
+ );
907
+ }
908
+ return result.nextCursor;
909
+ });
910
+ const signal = DerivedSignal.from(() => {
911
+ const result = resource.get();
912
+ if (result.isLoading || result.error) {
913
+ return result;
914
+ }
915
+ const threads2 = this.outputs.threads.get().findMany(roomId, query ?? {}, "asc");
916
+ const page = result.data;
917
+ return {
918
+ isLoading: false,
919
+ threads: threads2,
920
+ hasFetchedAll: page.hasFetchedAll,
921
+ isFetchingMore: page.isFetchingMore,
922
+ fetchMoreError: page.fetchMoreError,
923
+ fetchMore: page.fetchMore
924
+ };
925
+ }, shallow2);
926
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
927
+ }
928
+ );
929
+ const loadingNotifications = {
930
+ signal: DerivedSignal.from(() => {
931
+ const result = this.#notificationsPaginationState.get();
932
+ if (result.isLoading || result.error) {
933
+ return result;
934
+ }
935
+ const page = result.data;
936
+ return {
937
+ isLoading: false,
938
+ inboxNotifications: this.outputs.notifications.get().sortedNotifications,
939
+ hasFetchedAll: page.hasFetchedAll,
940
+ isFetchingMore: page.isFetchingMore,
941
+ fetchMoreError: page.fetchMoreError,
942
+ fetchMore: page.fetchMore
943
+ };
944
+ }),
945
+ waitUntilLoaded: this.#notificationsPaginationState.waitUntilLoaded
946
+ };
947
+ const settingsByRoomId = new DefaultMap((roomId) => {
948
+ const resource = new SinglePageResource(async () => {
949
+ const room = this.#client.getRoom(roomId);
950
+ if (room === null) {
951
+ throw new HttpError(
952
+ `Room '${roomId}' is not available on client`,
953
+ 479
954
+ );
955
+ }
956
+ const result = await room.getNotificationSettings();
957
+ this.roomNotificationSettings.update(roomId, result);
958
+ });
959
+ const signal = DerivedSignal.from(() => {
960
+ const result = resource.get();
961
+ if (result.isLoading || result.error) {
962
+ return result;
963
+ } else {
964
+ return ASYNC_OK(
965
+ "settings",
966
+ nn(this.roomNotificationSettings.signal.get()[roomId])
967
+ );
968
+ }
969
+ }, shallow3);
970
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
971
+ });
972
+ const versionsByRoomId = new DefaultMap(
973
+ (roomId) => {
974
+ const resource = new SinglePageResource(async () => {
975
+ const room = this.#client.getRoom(roomId);
976
+ if (room === null) {
977
+ throw new HttpError(
978
+ `Room '${roomId}' is not available on client`,
979
+ 479
980
+ );
981
+ }
982
+ const result = await room[kInternal].listTextVersions();
983
+ this.historyVersions.update(roomId, result.versions);
984
+ const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
985
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
986
+ this.#roomVersionsLastRequestedAtByRoom.set(
987
+ roomId,
988
+ result.requestedAt
989
+ );
990
+ }
991
+ });
992
+ const signal = DerivedSignal.from(() => {
993
+ const result = resource.get();
994
+ if (result.isLoading || result.error) {
995
+ return result;
996
+ } else {
997
+ return {
998
+ isLoading: false,
999
+ versions: Object.values(
1000
+ this.historyVersions.signal.get()[roomId] ?? {}
1001
+ )
1002
+ };
1003
+ }
1004
+ }, shallow3);
1005
+ return { signal, waitUntilLoaded: resource.waitUntilLoaded };
1006
+ }
868
1007
  );
869
1008
  this.outputs = {
870
1009
  threadifications,
871
1010
  threads,
1011
+ loadingRoomThreads,
1012
+ loadingUserThreads,
872
1013
  notifications,
1014
+ loadingNotifications,
873
1015
  settingsByRoomId,
874
1016
  versionsByRoomId
875
1017
  };
876
1018
  autobind(this);
877
1019
  }
878
- get1_both() {
879
- return this.outputs.threadifications.get();
880
- }
881
- subscribe1_both(callback) {
882
- return this.outputs.threadifications.subscribe(callback);
883
- }
884
- get1_threads() {
885
- return this.outputs.threads.get();
886
- }
887
- subscribe1_threads(callback) {
888
- return this.outputs.threads.subscribe(callback);
889
- }
890
- get1_notifications() {
891
- return this.outputs.notifications.get();
892
- }
893
- subscribe1_notifications(callback) {
894
- return this.outputs.notifications.subscribe(callback);
895
- }
896
- get2() {
897
- return this.outputs.settingsByRoomId.get();
898
- }
899
- subscribe2(callback) {
900
- return this.outputs.settingsByRoomId.subscribe(callback);
901
- }
902
- get3() {
903
- return this.outputs.versionsByRoomId.get();
904
- }
905
- subscribe3(callback) {
906
- return this.outputs.versionsByRoomId.subscribe(callback);
907
- }
908
- /**
909
- * Returns the async result of the given query and room id. If the query is success,
910
- * then it will return the threads that match that provided query and room id.
911
- *
912
- */
913
- getRoomThreadsLoadingState(roomId, query) {
914
- const queryKey = makeRoomThreadsQueryKey(roomId, query);
915
- const paginatedResource = this.#roomThreads.get(queryKey);
916
- if (paginatedResource === void 0) {
917
- return ASYNC_LOADING;
918
- }
919
- const asyncResult = paginatedResource.get();
920
- if (asyncResult.isLoading || asyncResult.error) {
921
- return asyncResult;
922
- }
923
- const threads = this.get1_threads().threadsDB.findMany(
924
- roomId,
925
- query ?? {},
926
- "asc"
927
- );
928
- const page = asyncResult.data;
929
- return {
930
- isLoading: false,
931
- threads,
932
- hasFetchedAll: page.hasFetchedAll,
933
- isFetchingMore: page.isFetchingMore,
934
- fetchMoreError: page.fetchMoreError,
935
- fetchMore: page.fetchMore
936
- };
937
- }
938
- getUserThreadsLoadingState(query) {
939
- const queryKey = makeUserThreadsQueryKey(query);
940
- const paginatedResource = this.#userThreads.get(queryKey);
941
- if (paginatedResource === void 0) {
942
- return ASYNC_LOADING;
943
- }
944
- const asyncResult = paginatedResource.get();
945
- if (asyncResult.isLoading || asyncResult.error) {
946
- return asyncResult;
947
- }
948
- const threads = this.get1_threads().threadsDB.findMany(
949
- void 0,
950
- // Do _not_ filter by roomId
951
- query ?? {},
952
- "desc"
953
- );
954
- const page = asyncResult.data;
955
- return {
956
- isLoading: false,
957
- threads,
958
- hasFetchedAll: page.hasFetchedAll,
959
- isFetchingMore: page.isFetchingMore,
960
- fetchMoreError: page.fetchMoreError,
961
- fetchMore: page.fetchMore
962
- };
963
- }
964
- // NOTE: This will read the async result, but WILL NOT start loading at the moment!
965
- getInboxNotificationsLoadingState() {
966
- const asyncResult = this.#notifications.get();
967
- if (asyncResult.isLoading || asyncResult.error) {
968
- return asyncResult;
969
- }
970
- const page = asyncResult.data;
971
- return {
972
- isLoading: false,
973
- inboxNotifications: this.get1_notifications().sortedNotifications,
974
- hasFetchedAll: page.hasFetchedAll,
975
- isFetchingMore: page.isFetchingMore,
976
- fetchMoreError: page.fetchMoreError,
977
- fetchMore: page.fetchMore
978
- };
979
- }
980
- // NOTE: This will read the async result, but WILL NOT start loading at the moment!
981
- // XXX_vincent This should really be a derived Signal!
982
- getNotificationSettingsLoadingState(roomId) {
983
- const queryKey = makeNotificationSettingsQueryKey(roomId);
984
- const resource = this.#roomNotificationSettings.get(queryKey);
985
- if (resource === void 0) {
986
- return ASYNC_LOADING;
987
- }
988
- const asyncResult = resource.get();
989
- if (asyncResult.isLoading || asyncResult.error) {
990
- return asyncResult;
991
- }
992
- return {
993
- isLoading: false,
994
- settings: nn(this.get2()[roomId])
995
- };
996
- }
997
- getRoomVersionsLoadingState(roomId) {
998
- const queryKey = makeVersionsQueryKey(roomId);
999
- const resource = this.#roomVersions.get(queryKey);
1000
- if (resource === void 0) {
1001
- return ASYNC_LOADING;
1002
- }
1003
- const asyncResult = resource.get();
1004
- if (asyncResult.isLoading || asyncResult.error) {
1005
- return asyncResult;
1006
- }
1007
- return {
1008
- isLoading: false,
1009
- versions: Object.values(this.get3()[roomId] ?? {})
1010
- };
1011
- }
1012
- /** @internal - Only call this method from unit tests. */
1013
- force_set_versions(callback) {
1014
- batch2(() => {
1015
- this.historyVersions.force_set(callback);
1016
- this.invalidateEntireStore();
1017
- });
1018
- }
1019
- /** @internal - Only call this method from unit tests. */
1020
- force_set_notifications(callback) {
1021
- batch2(() => {
1022
- this.notifications.force_set(callback);
1023
- this.invalidateEntireStore();
1024
- });
1025
- }
1026
1020
  /**
1027
1021
  * Updates an existing inbox notification with a new value, replacing the
1028
1022
  * corresponding optimistic update.
@@ -1164,7 +1158,7 @@ var UmbrellaStore = class {
1164
1158
  }
1165
1159
  updateThreadifications(threads, notifications, deletedThreads = [], deletedNotifications = []) {
1166
1160
  batch2(() => {
1167
- this.threads.applyDelta({ newThreads: threads, deletedThreads });
1161
+ this.threads.applyDelta(threads, deletedThreads);
1168
1162
  this.notifications.applyDelta(notifications, deletedNotifications);
1169
1163
  });
1170
1164
  }
@@ -1197,39 +1191,6 @@ var UmbrellaStore = class {
1197
1191
  result.inboxNotifications.deleted
1198
1192
  );
1199
1193
  }
1200
- waitUntilNotificationsLoaded() {
1201
- return this.#notifications.waitUntilLoaded();
1202
- }
1203
- waitUntilRoomThreadsLoaded(roomId, query) {
1204
- const threadsFetcher = async (cursor) => {
1205
- const result = await this.#client[kInternal].httpClient.getThreads({
1206
- roomId,
1207
- cursor,
1208
- query
1209
- });
1210
- this.updateThreadifications(result.threads, result.inboxNotifications);
1211
- this.permissionHints.update(result.permissionHints);
1212
- const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
1213
- if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
1214
- this.#roomThreadsLastRequestedAtByRoom.set(roomId, result.requestedAt);
1215
- }
1216
- return result.nextCursor;
1217
- };
1218
- const queryKey = makeRoomThreadsQueryKey(roomId, query);
1219
- let paginatedResource = this.#roomThreads.get(queryKey);
1220
- if (paginatedResource === void 0) {
1221
- paginatedResource = new PaginatedResource(threadsFetcher);
1222
- }
1223
- paginatedResource.observable.subscribe(
1224
- () => (
1225
- // Note that the store itself does not change, but it's only vehicle at
1226
- // the moment to trigger a re-render, so we'll do a no-op update here.
1227
- this.invalidateEntireStore()
1228
- )
1229
- );
1230
- this.#roomThreads.set(queryKey, paginatedResource);
1231
- return paginatedResource.waitUntilLoaded();
1232
- }
1233
1194
  async fetchRoomThreadsDeltaUpdate(roomId, signal) {
1234
1195
  const lastRequestedAt = this.#roomThreadsLastRequestedAtByRoom.get(roomId);
1235
1196
  if (lastRequestedAt === void 0) {
@@ -1251,45 +1212,6 @@ var UmbrellaStore = class {
1251
1212
  this.#roomThreadsLastRequestedAtByRoom.set(roomId, updates.requestedAt);
1252
1213
  }
1253
1214
  }
1254
- waitUntilUserThreadsLoaded(query) {
1255
- const queryKey = makeUserThreadsQueryKey(query);
1256
- const threadsFetcher = async (cursor) => {
1257
- const result = await this.#client[kInternal].httpClient.getUserThreads_experimental({
1258
- cursor,
1259
- query
1260
- });
1261
- this.updateThreadifications(result.threads, result.inboxNotifications);
1262
- this.permissionHints.update(result.permissionHints);
1263
- if (this.#userThreadsLastRequestedAt === null) {
1264
- this.#userThreadsLastRequestedAt = result.requestedAt;
1265
- }
1266
- return result.nextCursor;
1267
- };
1268
- let paginatedResource = this.#userThreads.get(queryKey);
1269
- if (paginatedResource === void 0) {
1270
- paginatedResource = new PaginatedResource(threadsFetcher);
1271
- }
1272
- paginatedResource.observable.subscribe(
1273
- () => (
1274
- // Note that the store itself does not change, but it's only vehicle at
1275
- // the moment to trigger a re-render, so we'll do a no-op update here.
1276
- this.invalidateEntireStore()
1277
- )
1278
- );
1279
- this.#userThreads.set(queryKey, paginatedResource);
1280
- return paginatedResource.waitUntilLoaded();
1281
- }
1282
- // XXX_vincent We should really be going over all call sites, and replace this call
1283
- // with a more specific invalidation!
1284
- invalidateEntireStore() {
1285
- batch2(() => {
1286
- this.historyVersions.invalidate();
1287
- this.notifications.invalidate();
1288
- this.optimisticUpdates.invalidate();
1289
- this.permissionHints.invalidate();
1290
- this.roomNotificationSettings.invalidate();
1291
- });
1292
- }
1293
1215
  async fetchUserThreadsDeltaUpdate(signal) {
1294
1216
  const lastRequestedAt = this.#userThreadsLastRequestedAt;
1295
1217
  if (lastRequestedAt === null) {
@@ -1310,40 +1232,6 @@ var UmbrellaStore = class {
1310
1232
  );
1311
1233
  this.permissionHints.update(result.permissionHints);
1312
1234
  }
1313
- waitUntilRoomVersionsLoaded(roomId) {
1314
- const queryKey = makeVersionsQueryKey(roomId);
1315
- let resource = this.#roomVersions.get(queryKey);
1316
- if (resource === void 0) {
1317
- const versionsFetcher = async () => {
1318
- const room = this.#client.getRoom(roomId);
1319
- if (room === null) {
1320
- throw new HttpError(
1321
- `Room '${roomId}' is not available on client`,
1322
- 479
1323
- );
1324
- }
1325
- const result = await room[kInternal].listTextVersions();
1326
- this.historyVersions.update(roomId, result.versions);
1327
- const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
1328
- if (lastRequestedAt === void 0 || lastRequestedAt > result.requestedAt) {
1329
- this.#roomVersionsLastRequestedAtByRoom.set(
1330
- roomId,
1331
- result.requestedAt
1332
- );
1333
- }
1334
- };
1335
- resource = new SinglePageResource(versionsFetcher);
1336
- }
1337
- resource.observable.subscribe(
1338
- () => (
1339
- // Note that the store itself does not change, but it's only vehicle at
1340
- // the moment to trigger a re-render, so we'll do a no-op update here.
1341
- this.invalidateEntireStore()
1342
- )
1343
- );
1344
- this.#roomVersions.set(queryKey, resource);
1345
- return resource.waitUntilLoaded();
1346
- }
1347
1235
  async fetchRoomVersionsDeltaUpdate(roomId, signal) {
1348
1236
  const lastRequestedAt = this.#roomVersionsLastRequestedAtByRoom.get(roomId);
1349
1237
  if (lastRequestedAt === void 0) {
@@ -1362,33 +1250,6 @@ var UmbrellaStore = class {
1362
1250
  this.#roomVersionsLastRequestedAtByRoom.set(roomId, updates.requestedAt);
1363
1251
  }
1364
1252
  }
1365
- waitUntilRoomNotificationSettingsLoaded(roomId) {
1366
- const queryKey = makeNotificationSettingsQueryKey(roomId);
1367
- let resource = this.#roomNotificationSettings.get(queryKey);
1368
- if (resource === void 0) {
1369
- const notificationSettingsFetcher = async () => {
1370
- const room = this.#client.getRoom(roomId);
1371
- if (room === null) {
1372
- throw new HttpError(
1373
- `Room '${roomId}' is not available on client`,
1374
- 479
1375
- );
1376
- }
1377
- const result = await room.getNotificationSettings();
1378
- this.roomNotificationSettings.update(roomId, result);
1379
- };
1380
- resource = new SinglePageResource(notificationSettingsFetcher);
1381
- }
1382
- resource.observable.subscribe(
1383
- () => (
1384
- // Note that the store itself does not change, but it's only vehicle at
1385
- // the moment to trigger a re-render, so we'll do a no-op update here.
1386
- this.invalidateEntireStore()
1387
- )
1388
- );
1389
- this.#roomNotificationSettings.set(queryKey, resource);
1390
- return resource.waitUntilLoaded();
1391
- }
1392
1253
  async refreshRoomNotificationSettings(roomId, signal) {
1393
1254
  const room = nn(
1394
1255
  this.#client.getRoom(roomId),
@@ -1760,29 +1621,23 @@ function missingRoomInfoError(roomId) {
1760
1621
  `resolveRoomsInfo didn't return anything for room '${roomId}'`
1761
1622
  );
1762
1623
  }
1763
- function identity(x) {
1624
+ function identity2(x) {
1764
1625
  return x;
1765
1626
  }
1766
1627
  var _umbrellaStores = /* @__PURE__ */ new WeakMap();
1767
1628
  var _extras = /* @__PURE__ */ new WeakMap();
1768
1629
  var _bundles = /* @__PURE__ */ new WeakMap();
1769
- function selectUnreadInboxNotificationsCount(inboxNotifications) {
1770
- let count = 0;
1771
- for (const notification of inboxNotifications) {
1772
- if (notification.readAt === null || notification.readAt < notification.notifiedAt) {
1773
- count++;
1774
- }
1775
- }
1776
- return count;
1777
- }
1778
1630
  function selectorFor_useUnreadInboxNotificationsCount(result) {
1779
1631
  if (!result.inboxNotifications) {
1780
1632
  return result;
1781
1633
  }
1782
- return {
1783
- isLoading: false,
1784
- count: selectUnreadInboxNotificationsCount(result.inboxNotifications)
1785
- };
1634
+ return ASYNC_OK(
1635
+ "count",
1636
+ count(
1637
+ result.inboxNotifications,
1638
+ (n) => n.readAt === null || n.readAt < n.notifiedAt
1639
+ )
1640
+ );
1786
1641
  }
1787
1642
  function selectorFor_useUser(state, userId) {
1788
1643
  if (state === void 0 || state?.isLoading) {
@@ -1889,7 +1744,7 @@ function makeLiveblocksContextBundle(client) {
1889
1744
  const shared = createSharedContext(client);
1890
1745
  const bundle = {
1891
1746
  LiveblocksProvider: LiveblocksProvider2,
1892
- useInboxNotifications: () => useInboxNotifications_withClient(client, identity, shallow4),
1747
+ useInboxNotifications: () => useInboxNotifications_withClient(client, identity2, shallow4),
1893
1748
  useUnreadInboxNotificationsCount: () => useUnreadInboxNotificationsCount_withClient(client),
1894
1749
  useMarkInboxNotificationAsRead: useMarkInboxNotificationAsRead2,
1895
1750
  useMarkAllInboxNotificationsAsRead: useMarkAllInboxNotificationsAsRead2,
@@ -1915,9 +1770,17 @@ function makeLiveblocksContextBundle(client) {
1915
1770
  }
1916
1771
  function useInboxNotifications_withClient(client, selector, isEqual) {
1917
1772
  const { store, notificationsPoller: poller } = getLiveblocksExtrasForClient(client);
1918
- useEffect3(() => {
1919
- void store.waitUntilNotificationsLoaded();
1920
- });
1773
+ useEffect3(
1774
+ () => void store.outputs.loadingNotifications.waitUntilLoaded()
1775
+ // NOTE: Deliberately *not* using a dependency array here!
1776
+ //
1777
+ // It is important to call waitUntil on *every* render.
1778
+ // This is harmless though, on most renders, except:
1779
+ // 1. The very first render, in which case we'll want to trigger the initial page fetch.
1780
+ // 2. All other subsequent renders now "just" return the same promise (a quick operation).
1781
+ // 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
1782
+ // *next* render after that, a *new* fetch/promise will get created.
1783
+ );
1921
1784
  useEffect3(() => {
1922
1785
  poller.inc();
1923
1786
  poller.pollNowIfStale();
@@ -1925,18 +1788,16 @@ function useInboxNotifications_withClient(client, selector, isEqual) {
1925
1788
  poller.dec();
1926
1789
  };
1927
1790
  }, [poller]);
1928
- return useSyncExternalStoreWithSelector(
1929
- store.subscribe1_notifications,
1930
- store.getInboxNotificationsLoadingState,
1931
- store.getInboxNotificationsLoadingState,
1791
+ return useSignal(
1792
+ store.outputs.loadingNotifications.signal,
1932
1793
  selector,
1933
1794
  isEqual
1934
1795
  );
1935
1796
  }
1936
1797
  function useInboxNotificationsSuspense_withClient(client) {
1937
1798
  const store = getLiveblocksExtrasForClient(client).store;
1938
- use(store.waitUntilNotificationsLoaded());
1939
- const result = useInboxNotifications_withClient(client, identity, shallow4);
1799
+ use(store.outputs.loadingNotifications.waitUntilLoaded());
1800
+ const result = useInboxNotifications_withClient(client, identity2, shallow4);
1940
1801
  assert(!result.error, "Did not expect error");
1941
1802
  assert(!result.isLoading, "Did not expect loading");
1942
1803
  return result;
@@ -1950,7 +1811,7 @@ function useUnreadInboxNotificationsCount_withClient(client) {
1950
1811
  }
1951
1812
  function useUnreadInboxNotificationsCountSuspense_withClient(client) {
1952
1813
  const store = getLiveblocksExtrasForClient(client).store;
1953
- use(store.waitUntilNotificationsLoaded());
1814
+ use(store.outputs.loadingNotifications.waitUntilLoaded());
1954
1815
  const result = useUnreadInboxNotificationsCount_withClient(client);
1955
1816
  assert(!result.isLoading, "Did not expect loading");
1956
1817
  assert(!result.error, "Did not expect error");
@@ -2042,34 +1903,31 @@ function useDeleteAllInboxNotifications_withClient(client) {
2042
1903
  }
2043
1904
  function useInboxNotificationThread_withClient(client, inboxNotificationId) {
2044
1905
  const { store } = getLiveblocksExtrasForClient(client);
2045
- const getter = store.get1_both;
2046
- const selector = useCallback2(
2047
- (state) => {
2048
- const inboxNotification = state.notificationsById[inboxNotificationId] ?? raise(`Inbox notification with ID "${inboxNotificationId}" not found`);
2049
- if (inboxNotification.kind !== "thread") {
2050
- raise(
2051
- `Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
1906
+ return useSignal(
1907
+ store.outputs.threadifications,
1908
+ useCallback2(
1909
+ (state) => {
1910
+ const inboxNotification = state.notificationsById[inboxNotificationId] ?? raise(
1911
+ `Inbox notification with ID "${inboxNotificationId}" not found`
2052
1912
  );
2053
- }
2054
- const thread = state.threadsDB.get(inboxNotification.threadId) ?? raise(
2055
- `Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
2056
- );
2057
- return thread;
2058
- },
2059
- [inboxNotificationId]
2060
- );
2061
- return useSyncExternalStoreWithSelector(
2062
- store.subscribe1_both,
2063
- // Re-evaluate if we need to update any time the notification changes over time
2064
- getter,
2065
- getter,
2066
- selector
1913
+ if (inboxNotification.kind !== "thread") {
1914
+ raise(
1915
+ `Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
1916
+ );
1917
+ }
1918
+ const thread = state.threadsDB.get(inboxNotification.threadId) ?? raise(
1919
+ `Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
1920
+ );
1921
+ return thread;
1922
+ },
1923
+ [inboxNotificationId]
1924
+ )
2067
1925
  );
2068
1926
  }
2069
1927
  function useUser_withClient(client, userId) {
2070
1928
  const usersStore = client[kInternal2].usersStore;
2071
1929
  const getUserState = useCallback2(
2072
- () => usersStore.getState(userId),
1930
+ () => usersStore.getItemState(userId),
2073
1931
  [usersStore, userId]
2074
1932
  );
2075
1933
  const selector = useCallback2(
@@ -2083,20 +1941,29 @@ function useUser_withClient(client, userId) {
2083
1941
  selector,
2084
1942
  shallow4
2085
1943
  );
2086
- useEffect3(() => {
2087
- void usersStore.get(userId);
2088
- }, [usersStore, userId, result]);
1944
+ useEffect3(
1945
+ () => void usersStore.enqueue(userId)
1946
+ // NOTE: Deliberately *not* using a dependency array here!
1947
+ //
1948
+ // It is important to call usersStore.enqueue on *every* render.
1949
+ // This is harmless though, on most renders, except:
1950
+ // 1. The very first render, in which case we'll want to trigger evaluation
1951
+ // of the userId.
1952
+ // 2. All other subsequent renders now are a no-op (from the implementation
1953
+ // of .enqueue)
1954
+ // 3. If ever the userId gets invalidated, the user would be fetched again.
1955
+ );
2089
1956
  return result;
2090
1957
  }
2091
1958
  function useUserSuspense_withClient(client, userId) {
2092
1959
  const usersStore = client[kInternal2].usersStore;
2093
1960
  const getUserState = useCallback2(
2094
- () => usersStore.getState(userId),
1961
+ () => usersStore.getItemState(userId),
2095
1962
  [usersStore, userId]
2096
1963
  );
2097
1964
  const userState = getUserState();
2098
1965
  if (!userState || userState.isLoading) {
2099
- throw usersStore.get(userId);
1966
+ throw usersStore.enqueue(userId);
2100
1967
  }
2101
1968
  if (userState.error) {
2102
1969
  throw userState.error;
@@ -2121,7 +1988,7 @@ function useUserSuspense_withClient(client, userId) {
2121
1988
  function useRoomInfo_withClient(client, roomId) {
2122
1989
  const roomsInfoStore = client[kInternal2].roomsInfoStore;
2123
1990
  const getRoomInfoState = useCallback2(
2124
- () => roomsInfoStore.getState(roomId),
1991
+ () => roomsInfoStore.getItemState(roomId),
2125
1992
  [roomsInfoStore, roomId]
2126
1993
  );
2127
1994
  const selector = useCallback2(
@@ -2135,20 +2002,29 @@ function useRoomInfo_withClient(client, roomId) {
2135
2002
  selector,
2136
2003
  shallow4
2137
2004
  );
2138
- useEffect3(() => {
2139
- void roomsInfoStore.get(roomId);
2140
- }, [roomsInfoStore, roomId, result]);
2005
+ useEffect3(
2006
+ () => void roomsInfoStore.enqueue(roomId)
2007
+ // NOTE: Deliberately *not* using a dependency array here!
2008
+ //
2009
+ // It is important to call roomsInfoStore.enqueue on *every* render.
2010
+ // This is harmless though, on most renders, except:
2011
+ // 1. The very first render, in which case we'll want to trigger evaluation
2012
+ // of the roomId.
2013
+ // 2. All other subsequent renders now are a no-op (from the implementation
2014
+ // of .enqueue)
2015
+ // 3. If ever the roomId gets invalidated, the room info would be fetched again.
2016
+ );
2141
2017
  return result;
2142
2018
  }
2143
2019
  function useRoomInfoSuspense_withClient(client, roomId) {
2144
2020
  const roomsInfoStore = client[kInternal2].roomsInfoStore;
2145
2021
  const getRoomInfoState = useCallback2(
2146
- () => roomsInfoStore.getState(roomId),
2022
+ () => roomsInfoStore.getItemState(roomId),
2147
2023
  [roomsInfoStore, roomId]
2148
2024
  );
2149
2025
  const roomInfoState = getRoomInfoState();
2150
2026
  if (!roomInfoState || roomInfoState.isLoading) {
2151
- throw roomsInfoStore.get(roomId);
2027
+ throw roomsInfoStore.enqueue(roomId);
2152
2028
  }
2153
2029
  if (roomInfoState.error) {
2154
2030
  throw roomInfoState.error;
@@ -2250,10 +2126,9 @@ function useUserThreads_experimental(options = {
2250
2126
  }) {
2251
2127
  const client = useClient();
2252
2128
  const { store, userThreadsPoller: poller } = getLiveblocksExtrasForClient(client);
2129
+ const queryKey = makeUserThreadsQueryKey(options.query);
2253
2130
  useEffect3(
2254
- () => {
2255
- void store.waitUntilUserThreadsLoaded(options.query);
2256
- }
2131
+ () => void store.outputs.loadingUserThreads.getOrCreate(queryKey).waitUntilLoaded()
2257
2132
  // NOTE: Deliberately *not* using a dependency array here!
2258
2133
  //
2259
2134
  // It is important to call waitUntil on *every* render.
@@ -2270,17 +2145,8 @@ function useUserThreads_experimental(options = {
2270
2145
  poller.dec();
2271
2146
  };
2272
2147
  }, [poller]);
2273
- const getter = useCallback2(
2274
- () => store.getUserThreadsLoadingState(options.query),
2275
- [store, options.query]
2276
- );
2277
- return useSyncExternalStoreWithSelector(
2278
- store.subscribe1_threads,
2279
- getter,
2280
- getter,
2281
- identity,
2282
- shallow2
2283
- // NOTE: Using 2-level-deep shallow check here, because the result of selectThreads() is not stable!
2148
+ return useSignal(
2149
+ store.outputs.loadingUserThreads.getOrCreate(queryKey).signal
2284
2150
  );
2285
2151
  }
2286
2152
  function useUserThreadsSuspense_experimental(options = {
@@ -2290,14 +2156,15 @@ function useUserThreadsSuspense_experimental(options = {
2290
2156
  }) {
2291
2157
  const client = useClient();
2292
2158
  const { store } = getLiveblocksExtrasForClient(client);
2293
- use(store.waitUntilUserThreadsLoaded(options.query));
2159
+ const queryKey = makeUserThreadsQueryKey(options.query);
2160
+ use(store.outputs.loadingUserThreads.getOrCreate(queryKey).waitUntilLoaded());
2294
2161
  const result = useUserThreads_experimental(options);
2295
2162
  assert(!result.error, "Did not expect error");
2296
2163
  assert(!result.isLoading, "Did not expect loading");
2297
2164
  return result;
2298
2165
  }
2299
2166
  function useInboxNotifications() {
2300
- return useInboxNotifications_withClient(useClient(), identity, shallow4);
2167
+ return useInboxNotifications_withClient(useClient(), identity2, shallow4);
2301
2168
  }
2302
2169
  function useInboxNotificationsSuspense() {
2303
2170
  return useInboxNotificationsSuspense_withClient(useClient());
@@ -2484,18 +2351,6 @@ var UpdateNotificationSettingsError = class extends Error {
2484
2351
  }
2485
2352
  };
2486
2353
 
2487
- // src/use-signal.ts
2488
- var identity2 = (value) => value;
2489
- function useSignal(signal, selector, isEqual) {
2490
- return useSyncExternalStoreWithSelector(
2491
- signal.subscribe,
2492
- signal.get,
2493
- signal.get,
2494
- selector ?? identity2,
2495
- isEqual
2496
- );
2497
- }
2498
-
2499
2354
  // src/room.tsx
2500
2355
  import { shallow as shallow5 } from "@liveblocks/client";
2501
2356
  import {
@@ -2503,10 +2358,11 @@ import {
2503
2358
  console as console3,
2504
2359
  createCommentId,
2505
2360
  createThreadId,
2361
+ DefaultMap as DefaultMap2,
2506
2362
  errorIf,
2507
2363
  HttpError as HttpError2,
2508
2364
  kInternal as kInternal3,
2509
- makeEventSource as makeEventSource2,
2365
+ makeEventSource,
2510
2366
  makePoller as makePoller2,
2511
2367
  ServerMsgCode
2512
2368
  } from "@liveblocks/core";
@@ -2628,7 +2484,7 @@ function getRoomExtrasForClient(client) {
2628
2484
  }
2629
2485
  function makeRoomExtrasForClient(client) {
2630
2486
  const store = getUmbrellaStoreForClient(client);
2631
- const commentsErrorEventSource = makeEventSource2();
2487
+ const commentsErrorEventSource = makeEventSource();
2632
2488
  function onMutationFailure(innerError, optimisticId, createPublicError) {
2633
2489
  store.optimisticUpdates.remove(optimisticId);
2634
2490
  if (innerError instanceof HttpError2) {
@@ -2642,73 +2498,59 @@ function makeRoomExtrasForClient(client) {
2642
2498
  }
2643
2499
  throw innerError;
2644
2500
  }
2645
- const threadsPollersByRoomId = /* @__PURE__ */ new Map();
2646
- const versionsPollersByRoomId = /* @__PURE__ */ new Map();
2647
- const roomNotificationSettingsPollersByRoomId = /* @__PURE__ */ new Map();
2648
- function getOrCreateThreadsPollerForRoomId(roomId) {
2649
- let poller = threadsPollersByRoomId.get(roomId);
2650
- if (!poller) {
2651
- poller = makePoller2(
2652
- async (signal) => {
2653
- try {
2654
- return await store.fetchRoomThreadsDeltaUpdate(roomId, signal);
2655
- } catch (err) {
2656
- console3.warn(`Polling new threads for '${roomId}' failed: ${String(err)}`);
2657
- throw err;
2658
- }
2659
- },
2660
- config.ROOM_THREADS_POLL_INTERVAL,
2661
- { maxStaleTimeMs: config.ROOM_THREADS_MAX_STALE_TIME }
2662
- );
2663
- threadsPollersByRoomId.set(roomId, poller);
2664
- }
2665
- return poller;
2666
- }
2667
- function getOrCreateVersionsPollerForRoomId(roomId) {
2668
- let poller = versionsPollersByRoomId.get(roomId);
2669
- if (!poller) {
2670
- poller = makePoller2(
2671
- async (signal) => {
2672
- try {
2673
- return await store.fetchRoomVersionsDeltaUpdate(roomId, signal);
2674
- } catch (err) {
2675
- console3.warn(`Polling new history versions for '${roomId}' failed: ${String(err)}`);
2676
- throw err;
2677
- }
2678
- },
2679
- config.HISTORY_VERSIONS_POLL_INTERVAL,
2680
- { maxStaleTimeMs: config.HISTORY_VERSIONS_MAX_STALE_TIME }
2681
- );
2682
- versionsPollersByRoomId.set(roomId, poller);
2683
- }
2684
- return poller;
2685
- }
2686
- function getOrCreateNotificationsSettingsPollerForRoomId(roomId) {
2687
- let poller = roomNotificationSettingsPollersByRoomId.get(roomId);
2688
- if (!poller) {
2689
- poller = makePoller2(
2690
- async (signal) => {
2691
- try {
2692
- return await store.refreshRoomNotificationSettings(roomId, signal);
2693
- } catch (err) {
2694
- console3.warn(`Polling notification settings for '${roomId}' failed: ${String(err)}`);
2695
- throw err;
2696
- }
2697
- },
2698
- config.NOTIFICATION_SETTINGS_POLL_INTERVAL,
2699
- { maxStaleTimeMs: config.NOTIFICATION_SETTINGS_MAX_STALE_TIME }
2700
- );
2701
- roomNotificationSettingsPollersByRoomId.set(roomId, poller);
2702
- }
2703
- return poller;
2704
- }
2501
+ const threadsPollersByRoomId = new DefaultMap2(
2502
+ (roomId) => makePoller2(
2503
+ async (signal) => {
2504
+ try {
2505
+ return await store.fetchRoomThreadsDeltaUpdate(roomId, signal);
2506
+ } catch (err) {
2507
+ console3.warn(`Polling new threads for '${roomId}' failed: ${String(err)}`);
2508
+ throw err;
2509
+ }
2510
+ },
2511
+ config.ROOM_THREADS_POLL_INTERVAL,
2512
+ { maxStaleTimeMs: config.ROOM_THREADS_MAX_STALE_TIME }
2513
+ )
2514
+ );
2515
+ const versionsPollersByRoomId = new DefaultMap2(
2516
+ (roomId) => makePoller2(
2517
+ async (signal) => {
2518
+ try {
2519
+ return await store.fetchRoomVersionsDeltaUpdate(roomId, signal);
2520
+ } catch (err) {
2521
+ console3.warn(`Polling new history versions for '${roomId}' failed: ${String(err)}`);
2522
+ throw err;
2523
+ }
2524
+ },
2525
+ config.HISTORY_VERSIONS_POLL_INTERVAL,
2526
+ { maxStaleTimeMs: config.HISTORY_VERSIONS_MAX_STALE_TIME }
2527
+ )
2528
+ );
2529
+ const roomNotificationSettingsPollersByRoomId = new DefaultMap2(
2530
+ (roomId) => makePoller2(
2531
+ async (signal) => {
2532
+ try {
2533
+ return await store.refreshRoomNotificationSettings(roomId, signal);
2534
+ } catch (err) {
2535
+ console3.warn(`Polling notification settings for '${roomId}' failed: ${String(err)}`);
2536
+ throw err;
2537
+ }
2538
+ },
2539
+ config.NOTIFICATION_SETTINGS_POLL_INTERVAL,
2540
+ { maxStaleTimeMs: config.NOTIFICATION_SETTINGS_MAX_STALE_TIME }
2541
+ )
2542
+ );
2705
2543
  return {
2706
2544
  store,
2707
2545
  commentsErrorEventSource: commentsErrorEventSource.observable,
2708
2546
  onMutationFailure,
2709
- getOrCreateThreadsPollerForRoomId,
2710
- getOrCreateVersionsPollerForRoomId,
2711
- getOrCreateNotificationsSettingsPollerForRoomId
2547
+ getOrCreateThreadsPollerForRoomId: threadsPollersByRoomId.getOrCreate.bind(
2548
+ threadsPollersByRoomId
2549
+ ),
2550
+ getOrCreateVersionsPollerForRoomId: versionsPollersByRoomId.getOrCreate.bind(versionsPollersByRoomId),
2551
+ getOrCreateNotificationsSettingsPollerForRoomId: roomNotificationSettingsPollersByRoomId.getOrCreate.bind(
2552
+ roomNotificationSettingsPollersByRoomId
2553
+ )
2712
2554
  };
2713
2555
  }
2714
2556
  function makeRoomContextBundle(client) {
@@ -2887,7 +2729,7 @@ function RoomProviderInner(props) {
2887
2729
  return;
2888
2730
  }
2889
2731
  const { thread, inboxNotification: maybeNotification } = info;
2890
- const existingThread = store.get1_threads().threadsDB.getEvenIfDeleted(message.threadId);
2732
+ const existingThread = store.outputs.threads.get().getEvenIfDeleted(message.threadId);
2891
2733
  switch (message.type) {
2892
2734
  case ServerMsgCode.COMMENT_EDITED:
2893
2735
  case ServerMsgCode.THREAD_METADATA_UPDATED:
@@ -3258,18 +3100,15 @@ function useMutation(callback, deps) {
3258
3100
  [room, ...deps]
3259
3101
  );
3260
3102
  }
3261
- function useThreads(options = {
3262
- query: { metadata: {} }
3263
- }) {
3103
+ function useThreads(options = {}) {
3264
3104
  const { scrollOnLoad = true } = options;
3265
3105
  const client = useClient();
3266
3106
  const room = useRoom();
3267
3107
  const { store, getOrCreateThreadsPollerForRoomId } = getRoomExtrasForClient(client);
3108
+ const queryKey = makeRoomThreadsQueryKey(room.id, options.query);
3268
3109
  const poller = getOrCreateThreadsPollerForRoomId(room.id);
3269
3110
  useEffect5(
3270
- () => {
3271
- void store.waitUntilRoomThreadsLoaded(room.id, options.query);
3272
- }
3111
+ () => void store.outputs.loadingRoomThreads.getOrCreate(queryKey).waitUntilLoaded()
3273
3112
  // NOTE: Deliberately *not* using a dependency array here!
3274
3113
  //
3275
3114
  // It is important to call waitUntil on *every* render.
@@ -3284,20 +3123,11 @@ function useThreads(options = {
3284
3123
  poller.pollNowIfStale();
3285
3124
  return () => poller.dec();
3286
3125
  }, [poller]);
3287
- const getter = useCallback3(
3288
- () => store.getRoomThreadsLoadingState(room.id, options.query),
3289
- [store, room.id, options.query]
3290
- );
3291
- const state = useSyncExternalStoreWithSelector(
3292
- store.subscribe1_threads,
3293
- getter,
3294
- getter,
3295
- identity3,
3296
- shallow2
3297
- // NOTE: Using 2-level-deep shallow check here, because the result of selectThreads() is not stable!
3126
+ const result = useSignal(
3127
+ store.outputs.loadingRoomThreads.getOrCreate(queryKey).signal
3298
3128
  );
3299
- useScrollToCommentOnLoadEffect(scrollOnLoad, state);
3300
- return state;
3129
+ useScrollToCommentOnLoadEffect(scrollOnLoad, result);
3130
+ return result;
3301
3131
  }
3302
3132
  function useCommentsErrorListener(callback) {
3303
3133
  const client = useClient();
@@ -3385,7 +3215,7 @@ function useDeleteRoomThread(roomId) {
3385
3215
  (threadId) => {
3386
3216
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3387
3217
  const userId = getCurrentUserId(client);
3388
- const existing = store.get1_threads().threadsDB.get(threadId);
3218
+ const existing = store.outputs.threads.get().get(threadId);
3389
3219
  if (existing?.comments?.[0]?.userId !== userId) {
3390
3220
  throw new Error("Only the thread creator can delete the thread");
3391
3221
  }
@@ -3503,7 +3333,7 @@ function useEditRoomComment(roomId) {
3503
3333
  ({ threadId, commentId, body, attachments }) => {
3504
3334
  const editedAt = /* @__PURE__ */ new Date();
3505
3335
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3506
- const existing = store.get1_threads().threadsDB.getEvenIfDeleted(threadId);
3336
+ const existing = store.outputs.threads.get().getEvenIfDeleted(threadId);
3507
3337
  if (existing === void 0) {
3508
3338
  console3.warn(
3509
3339
  `Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
@@ -3680,7 +3510,7 @@ function useMarkRoomThreadAsRead(roomId) {
3680
3510
  (threadId) => {
3681
3511
  const { store, onMutationFailure } = getRoomExtrasForClient(client);
3682
3512
  const inboxNotification = Object.values(
3683
- store.get1_notifications().notificationsById
3513
+ store.outputs.notifications.get().notificationsById
3684
3514
  ).find(
3685
3515
  (inboxNotification2) => inboxNotification2.kind === "thread" && inboxNotification2.threadId === threadId
3686
3516
  );
@@ -3818,9 +3648,7 @@ function useRoomNotificationSettings() {
3818
3648
  const { store, getOrCreateNotificationsSettingsPollerForRoomId } = getRoomExtrasForClient(client);
3819
3649
  const poller = getOrCreateNotificationsSettingsPollerForRoomId(room.id);
3820
3650
  useEffect5(
3821
- () => {
3822
- void store.waitUntilRoomNotificationSettingsLoaded(room.id);
3823
- }
3651
+ () => void store.outputs.settingsByRoomId.getOrCreate(room.id).waitUntilLoaded()
3824
3652
  // NOTE: Deliberately *not* using a dependency array here!
3825
3653
  //
3826
3654
  // It is important to call waitUntil on *every* render.
@@ -3837,16 +3665,8 @@ function useRoomNotificationSettings() {
3837
3665
  poller.dec();
3838
3666
  };
3839
3667
  }, [poller]);
3840
- const getter = useCallback3(
3841
- () => store.getNotificationSettingsLoadingState(room.id),
3842
- [store, room.id]
3843
- );
3844
- const settings = useSyncExternalStoreWithSelector(
3845
- store.subscribe2,
3846
- getter,
3847
- getter,
3848
- identity3,
3849
- shallow2
3668
+ const settings = useSignal(
3669
+ store.outputs.settingsByRoomId.getOrCreate(room.id).signal
3850
3670
  );
3851
3671
  return useMemo3(() => {
3852
3672
  return [settings, updateRoomNotificationSettings];
@@ -3856,7 +3676,7 @@ function useRoomNotificationSettingsSuspense() {
3856
3676
  const client = useClient();
3857
3677
  const store = getRoomExtrasForClient(client).store;
3858
3678
  const room = useRoom();
3859
- use(store.waitUntilRoomNotificationSettingsLoaded(room.id));
3679
+ use(store.outputs.settingsByRoomId.getOrCreate(room.id).waitUntilLoaded());
3860
3680
  const [settings, updateRoomNotificationSettings] = useRoomNotificationSettings();
3861
3681
  assert2(!settings.error, "Did not expect error");
3862
3682
  assert2(!settings.isLoading, "Did not expect loading");
@@ -3903,14 +3723,8 @@ function useHistoryVersions() {
3903
3723
  poller.pollNowIfStale();
3904
3724
  return () => poller.dec();
3905
3725
  }, [poller]);
3906
- const getter = useCallback3(
3907
- () => store.getRoomVersionsLoadingState(room.id),
3908
- [store, room.id]
3909
- );
3910
3726
  useEffect5(
3911
- () => {
3912
- void store.waitUntilRoomVersionsLoaded(room.id);
3913
- }
3727
+ () => void store.outputs.versionsByRoomId.getOrCreate(room.id).waitUntilLoaded()
3914
3728
  // NOTE: Deliberately *not* using a dependency array here!
3915
3729
  //
3916
3730
  // It is important to call waitUntil on *every* render.
@@ -3920,20 +3734,13 @@ function useHistoryVersions() {
3920
3734
  // 3. If ever the promise would fail, then after 5 seconds it would reset, and on the very
3921
3735
  // *next* render after that, a *new* fetch/promise will get created.
3922
3736
  );
3923
- const state = useSyncExternalStoreWithSelector(
3924
- store.subscribe3,
3925
- getter,
3926
- getter,
3927
- identity3,
3928
- shallow2
3929
- );
3930
- return state;
3737
+ return useSignal(store.outputs.versionsByRoomId.getOrCreate(room.id).signal);
3931
3738
  }
3932
3739
  function useHistoryVersionsSuspense() {
3933
3740
  const client = useClient();
3934
3741
  const room = useRoom();
3935
3742
  const store = getRoomExtrasForClient(client).store;
3936
- use(store.waitUntilRoomVersionsLoaded(room.id));
3743
+ use(store.outputs.versionsByRoomId.getOrCreate(room.id).waitUntilLoaded());
3937
3744
  const result = useHistoryVersions();
3938
3745
  assert2(!result.error, "Did not expect error");
3939
3746
  assert2(!result.isLoading, "Did not expect loading");
@@ -4020,13 +3827,12 @@ function useStorageStatusSuspense(options) {
4020
3827
  useSuspendUntilStorageReady();
4021
3828
  return useStorageStatus(options);
4022
3829
  }
4023
- function useThreadsSuspense(options = {
4024
- query: { metadata: {} }
4025
- }) {
3830
+ function useThreadsSuspense(options = {}) {
4026
3831
  const client = useClient();
4027
3832
  const room = useRoom();
4028
3833
  const { store } = getRoomExtrasForClient(client);
4029
- use(store.waitUntilRoomThreadsLoaded(room.id, options.query));
3834
+ const queryKey = makeRoomThreadsQueryKey(room.id, options.query);
3835
+ use(store.outputs.loadingRoomThreads.getOrCreate(queryKey).waitUntilLoaded());
4030
3836
  const result = useThreads(options);
4031
3837
  assert2(!result.error, "Did not expect error");
4032
3838
  assert2(!result.isLoading, "Did not expect loading");
@@ -4053,11 +3859,11 @@ function useRoomAttachmentUrl(attachmentId, roomId) {
4053
3859
  const client = useClient();
4054
3860
  const store = client[kInternal3].httpClient.getOrCreateAttachmentUrlsStore(roomId);
4055
3861
  const getAttachmentUrlState = useCallback3(
4056
- () => store.getState(attachmentId),
3862
+ () => store.getItemState(attachmentId),
4057
3863
  [store, attachmentId]
4058
3864
  );
4059
3865
  useEffect5(() => {
4060
- void store.get(attachmentId);
3866
+ void store.enqueue(attachmentId);
4061
3867
  }, [store, attachmentId]);
4062
3868
  return useSyncExternalStoreWithSelector(
4063
3869
  store.subscribe,
@@ -4071,12 +3877,12 @@ function useAttachmentUrlSuspense(attachmentId) {
4071
3877
  const room = useRoom();
4072
3878
  const { attachmentUrlsStore } = room[kInternal3];
4073
3879
  const getAttachmentUrlState = useCallback3(
4074
- () => attachmentUrlsStore.getState(attachmentId),
3880
+ () => attachmentUrlsStore.getItemState(attachmentId),
4075
3881
  [attachmentUrlsStore, attachmentId]
4076
3882
  );
4077
3883
  const attachmentUrlState = getAttachmentUrlState();
4078
3884
  if (!attachmentUrlState || attachmentUrlState.isLoading) {
4079
- throw attachmentUrlsStore.get(attachmentId);
3885
+ throw attachmentUrlsStore.enqueue(attachmentId);
4080
3886
  }
4081
3887
  if (attachmentUrlState.error) {
4082
3888
  throw attachmentUrlState.error;
@@ -4095,12 +3901,13 @@ function useAttachmentUrlSuspense(attachmentId) {
4095
3901
  error: void 0
4096
3902
  };
4097
3903
  }
3904
+ var NO_PERMISSIONS = /* @__PURE__ */ new Set();
4098
3905
  function useRoomPermissions(roomId) {
4099
3906
  const client = useClient();
4100
3907
  const store = getRoomExtrasForClient(client).store;
4101
3908
  return useSignal(
4102
3909
  store.permissionHints.signal,
4103
- (hints) => hints[roomId] ?? /* @__PURE__ */ new Set()
3910
+ (hints) => hints.get(roomId) ?? NO_PERMISSIONS
4104
3911
  );
4105
3912
  }
4106
3913
  function createRoomContext(client) {
@@ -4149,6 +3956,7 @@ export {
4149
3956
  RoomContext,
4150
3957
  useRoomOrNull,
4151
3958
  useSyncExternalStoreWithSelector,
3959
+ useSignal,
4152
3960
  ClientContext,
4153
3961
  getUmbrellaStoreForClient,
4154
3962
  useClientOrNull,
@@ -4172,7 +3980,6 @@ export {
4172
3980
  _useUserThreadsSuspense_experimental,
4173
3981
  useSyncStatus,
4174
3982
  CreateThreadError,
4175
- useSignal,
4176
3983
  useStatus,
4177
3984
  useReportTextEditor,
4178
3985
  useYjsProvider,
@@ -4250,4 +4057,4 @@ export {
4250
4057
  _useStorageRoot,
4251
4058
  _useUpdateMyPresence
4252
4059
  };
4253
- //# sourceMappingURL=chunk-GMKB6I6V.mjs.map
4060
+ //# sourceMappingURL=chunk-EEYUKRIA.mjs.map