@liveblocks/react 1.11.3 → 1.12.0-lexical2

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.11.3";
8
+ var PKG_VERSION = "1.12.0-lexical2";
9
9
  var PKG_FORMAT = "esm";
10
10
 
11
11
  // src/ClientSideSuspense.tsx
@@ -18,68 +18,6 @@ function ClientSideSuspense(props) {
18
18
  return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: props.fallback }, mounted ? props.children() : props.fallback);
19
19
  }
20
20
 
21
- // src/liveblocks.tsx
22
- import { kInternal as kInternal3, makePoller as makePoller2 } from "@liveblocks/core";
23
- import { nanoid as nanoid3 } from "nanoid";
24
- import React3, {
25
- createContext as createContext2,
26
- useCallback as useCallback3,
27
- useContext as useContext3,
28
- useEffect as useEffect5
29
- } from "react";
30
- import { useSyncExternalStore as useSyncExternalStore3 } from "use-sync-external-store/shim/index.js";
31
- import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 } from "use-sync-external-store/shim/with-selector.js";
32
-
33
- // src/comments/lib/selected-inbox-notifications.ts
34
- import { applyOptimisticUpdates } from "@liveblocks/core";
35
- function selectedInboxNotifications(state) {
36
- const result = applyOptimisticUpdates(state);
37
- return Object.values(result.inboxNotifications).sort(
38
- // Sort so that the most recent notifications are first
39
- (a, b) => b.notifiedAt.getTime() - a.notifiedAt.getTime()
40
- );
41
- }
42
-
43
- // src/lib/retry-error.ts
44
- var MAX_ERROR_RETRY_COUNT = 5;
45
- var ERROR_RETRY_INTERVAL = 5e3;
46
- function retryError(action, retryCount) {
47
- if (retryCount >= MAX_ERROR_RETRY_COUNT)
48
- return;
49
- const timeout = Math.pow(2, retryCount) * ERROR_RETRY_INTERVAL;
50
- setTimeout(() => {
51
- void action();
52
- }, timeout);
53
- }
54
-
55
- // src/shared.ts
56
- import { kInternal as kInternal2 } from "@liveblocks/core";
57
- import { useCallback as useCallback2, useContext as useContext2, useEffect as useEffect4 } from "react";
58
- import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
59
-
60
- // src/room.tsx
61
- import { shallow } from "@liveblocks/client";
62
- import {
63
- addReaction,
64
- CommentsApiError,
65
- console as console2,
66
- deleteComment,
67
- deprecateIf,
68
- errorIf,
69
- isLiveNode,
70
- kInternal,
71
- makeEventSource,
72
- makePoller,
73
- NotificationsApiError,
74
- removeReaction,
75
- ServerMsgCode,
76
- stringify,
77
- upsertComment
78
- } from "@liveblocks/core";
79
- import { nanoid as nanoid2 } from "nanoid";
80
- import * as React2 from "react";
81
- import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
82
-
83
21
  // src/comments/errors.ts
84
22
  var CreateThreadError = class extends Error {
85
23
  constructor(cause, context) {
@@ -154,53 +92,28 @@ var UpdateNotificationSettingsError = class extends Error {
154
92
  }
155
93
  };
156
94
 
157
- // src/comments/lib/createIds.ts
158
- import { nanoid } from "nanoid";
159
- var THREAD_ID_PREFIX = "th";
160
- var COMMENT_ID_PREFIX = "cm";
161
- function createOptimisticId(prefix) {
162
- return `${prefix}_${nanoid()}`;
163
- }
164
- function createThreadId() {
165
- return createOptimisticId(THREAD_ID_PREFIX);
166
- }
167
- function createCommentId() {
168
- return createOptimisticId(COMMENT_ID_PREFIX);
169
- }
170
-
171
- // src/comments/lib/select-notification-settings.ts
172
- import {
173
- applyOptimisticUpdates as applyOptimisticUpdates2,
174
- nn
175
- } from "@liveblocks/core";
176
- function selectNotificationSettings(roomId, state) {
177
- const { notificationSettings } = applyOptimisticUpdates2(state);
178
- return nn(notificationSettings[roomId]);
179
- }
95
+ // src/liveblocks.tsx
96
+ import { kInternal as kInternal3, makePoller as makePoller2, raise as raise2 } from "@liveblocks/core";
97
+ import { nanoid as nanoid2 } from "nanoid";
98
+ import React4, {
99
+ createContext as createContext2,
100
+ useCallback as useCallback3,
101
+ useContext as useContext2,
102
+ useEffect as useEffect6
103
+ } from "react";
104
+ import { useSyncExternalStore as useSyncExternalStore3 } from "use-sync-external-store/shim/index.js";
105
+ import { useSyncExternalStoreWithSelector as useSyncExternalStoreWithSelector2 } from "use-sync-external-store/shim/with-selector.js";
180
106
 
181
- // src/comments/lib/selected-threads.ts
182
- import {
183
- applyOptimisticUpdates as applyOptimisticUpdates3
184
- } from "@liveblocks/core";
185
- function selectedThreads(roomId, state, options) {
186
- const result = applyOptimisticUpdates3(state);
187
- const threads = Object.values(result.threads).filter((thread) => {
188
- if (thread.roomId !== roomId)
189
- return false;
190
- if (thread.deletedAt !== void 0) {
191
- return false;
192
- }
193
- const query = options.query;
194
- if (!query)
195
- return true;
196
- for (const key in query.metadata) {
197
- if (thread.metadata[key] !== query.metadata[key]) {
198
- return false;
199
- }
200
- }
201
- return true;
202
- });
203
- return threads.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
107
+ // src/lib/retry-error.ts
108
+ var MAX_ERROR_RETRY_COUNT = 5;
109
+ var ERROR_RETRY_INTERVAL = 5e3;
110
+ function retryError(action, retryCount) {
111
+ if (retryCount >= MAX_ERROR_RETRY_COUNT)
112
+ return;
113
+ const timeout = Math.pow(2, retryCount) * ERROR_RETRY_INTERVAL;
114
+ setTimeout(() => {
115
+ void action();
116
+ }, timeout);
204
117
  }
205
118
 
206
119
  // src/lib/use-initial.ts
@@ -209,6 +122,34 @@ function useInitial(value) {
209
122
  return useState2(value)[0];
210
123
  }
211
124
 
125
+ // src/shared.ts
126
+ import { kInternal as kInternal2, raise } from "@liveblocks/core";
127
+ import { useCallback as useCallback2, useEffect as useEffect5 } from "react";
128
+ import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
129
+
130
+ // src/room.tsx
131
+ import { shallow } from "@liveblocks/client";
132
+ import {
133
+ addReaction,
134
+ CommentsApiError,
135
+ console as console2,
136
+ deleteComment,
137
+ deprecateIf,
138
+ errorIf,
139
+ isLiveNode,
140
+ kInternal,
141
+ makeEventSource,
142
+ makePoller,
143
+ NotificationsApiError,
144
+ removeReaction,
145
+ ServerMsgCode,
146
+ stringify,
147
+ upsertComment
148
+ } from "@liveblocks/core";
149
+ import { nanoid } from "nanoid";
150
+ import * as React3 from "react";
151
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
152
+
212
153
  // src/lib/use-latest.ts
213
154
  import { useEffect as useEffect2, useRef } from "react";
214
155
  function useLatest(value) {
@@ -232,7 +173,46 @@ function useRerender() {
232
173
  return update;
233
174
  }
234
175
 
176
+ // src/use-scroll-to-comment-on-load-effect.ts
177
+ import * as React2 from "react";
178
+ function handleScrollToCommentOnLoad(shouldScrollOnLoad, state) {
179
+ if (shouldScrollOnLoad === false)
180
+ return;
181
+ if (state.isLoading)
182
+ return;
183
+ const isWindowDefined = typeof window !== "undefined";
184
+ if (!isWindowDefined)
185
+ return;
186
+ const hash = window.location.hash;
187
+ const commentId = hash.slice(1);
188
+ if (!commentId.startsWith("cm_"))
189
+ return;
190
+ const comment = document.getElementById(commentId);
191
+ if (comment === null)
192
+ return;
193
+ const comments = state.threads.flatMap((thread) => thread.comments);
194
+ const isCommentInThreads = comments.some(
195
+ (comment2) => comment2.id === commentId
196
+ );
197
+ if (!isCommentInThreads)
198
+ return;
199
+ comment.scrollIntoView();
200
+ }
201
+ function useScrollToCommentOnLoadEffect(shouldScrollOnLoad, state) {
202
+ React2.useEffect(
203
+ () => {
204
+ handleScrollToCommentOnLoad(shouldScrollOnLoad, state);
205
+ },
206
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this effect once
207
+ [state.isLoading]
208
+ );
209
+ }
210
+
235
211
  // src/room.tsx
212
+ var ThreadCreateCallbackContext = React3.createContext(null);
213
+ var ThreadDeleteCallbackContext = React3.createContext(null);
214
+ var ComposerFocusCallbackContext = React3.createContext(null);
215
+ var IsThreadActiveCallbackContext = React3.createContext(null);
236
216
  var noop = () => {
237
217
  };
238
218
  var identity = (x) => x;
@@ -254,236 +234,662 @@ function useSyncExternalStore(s, gs, gss) {
254
234
  var STABLE_EMPTY_LIST = Object.freeze([]);
255
235
  var POLLING_INTERVAL = 5 * 60 * 1e3;
256
236
  var MENTION_SUGGESTIONS_DEBOUNCE = 500;
237
+ function makeNotificationSettingsQueryKey(roomId) {
238
+ return `${roomId}:NOTIFICATION_SETTINGS`;
239
+ }
257
240
  function alwaysEmptyList() {
258
241
  return STABLE_EMPTY_LIST;
259
242
  }
260
243
  function alwaysNull() {
261
244
  return null;
262
245
  }
246
+ function selectorFor_useOthersConnectionIds(others) {
247
+ return others.map((user) => user.connectionId);
248
+ }
263
249
  function makeMutationContext(room) {
264
- const errmsg = "This mutation cannot be used until connected to the Liveblocks room";
250
+ const cannotUseUntil = "This mutation cannot be used until";
251
+ const needsPresence = `${cannotUseUntil} connected to the Liveblocks room`;
252
+ const needsStorage = `${cannotUseUntil} storage has been loaded`;
265
253
  return {
266
254
  get storage() {
267
255
  const mutableRoot = room.getStorageSnapshot();
268
256
  if (mutableRoot === null) {
269
- throw new Error(errmsg);
257
+ throw new Error(needsStorage);
270
258
  }
271
259
  return mutableRoot;
272
260
  },
273
261
  get self() {
274
262
  const self = room.getSelf();
275
263
  if (self === null) {
276
- throw new Error(errmsg);
264
+ throw new Error(needsPresence);
277
265
  }
278
266
  return self;
279
267
  },
280
268
  get others() {
281
269
  const others = room.getOthers();
282
270
  if (room.getSelf() === null) {
283
- throw new Error(errmsg);
271
+ throw new Error(needsPresence);
284
272
  }
285
273
  return others;
286
274
  },
287
275
  setMyPresence: room.updatePresence
288
276
  };
289
277
  }
290
- var ContextBundle = React2.createContext(null);
291
- function useRoomContextBundle() {
292
- const bundle = React2.useContext(ContextBundle);
293
- if (bundle === null) {
294
- throw new Error("RoomProvider is missing from the React tree.");
278
+ function getCurrentUserId(room) {
279
+ const self = room.getSelf();
280
+ if (self === null || self.id === void 0) {
281
+ return "anonymous";
282
+ } else {
283
+ return self.id;
295
284
  }
296
- return bundle;
297
285
  }
298
- function createRoomContext(client, options) {
299
- if (options?.resolveUsers) {
300
- throw new Error(
301
- "The 'resolveUsers' option has moved to 'createClient' from '@liveblocks/client'. Please refer to our Upgrade Guide to learn more, see https://liveblocks.io/docs/platform/upgrading/1.10."
302
- );
286
+ function handleApiError(err) {
287
+ const message = `Request failed with status ${err.status}: ${err.message}`;
288
+ if (err.details?.error === "FORBIDDEN") {
289
+ const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
290
+ console2.error(detailedMessage);
303
291
  }
304
- if (options?.resolveMentionSuggestions) {
305
- throw new Error(
306
- "The 'resolveMentionSuggestions' option has moved to 'createClient' from '@liveblocks/client'. Please refer to our Upgrade Guide to learn more, see https://liveblocks.io/docs/platform/upgrading/1.10."
307
- );
292
+ return new Error(message);
293
+ }
294
+ var _extras = /* @__PURE__ */ new WeakMap();
295
+ var _bundles = /* @__PURE__ */ new WeakMap();
296
+ function getOrCreateRoomContextBundle(client) {
297
+ let bundle = _bundles.get(client);
298
+ if (!bundle) {
299
+ bundle = makeRoomContextBundle(client);
300
+ _bundles.set(client, bundle);
308
301
  }
309
- const RoomContext = React2.createContext(null);
310
- const commentsErrorEventSource = makeEventSource();
311
- const shared = createSharedContext(client);
312
- function RoomProviderOuter(props) {
313
- const [cache] = React2.useState(
314
- () => /* @__PURE__ */ new Map()
315
- );
316
- const stableEnterRoom = React2.useCallback(
317
- (roomId, options2) => {
318
- const cached = cache.get(roomId);
319
- if (cached)
320
- return cached;
321
- const rv = client.enterRoom(
322
- roomId,
323
- options2
324
- );
325
- const origLeave = rv.leave;
326
- rv.leave = () => {
327
- origLeave();
328
- cache.delete(roomId);
329
- };
330
- cache.set(roomId, rv);
331
- return rv;
332
- },
333
- [cache]
334
- );
335
- return /* @__PURE__ */ React2.createElement(RoomProviderInner, { ...props, stableEnterRoom });
302
+ return bundle;
303
+ }
304
+ function getExtrasForClient(client) {
305
+ let extras = _extras.get(client);
306
+ if (!extras) {
307
+ extras = makeExtrasForClient(client);
308
+ _extras.set(client, extras);
309
+ }
310
+ return extras;
311
+ }
312
+ function makeExtrasForClient(client) {
313
+ const store = client[kInternal].cacheStore;
314
+ const DEFAULT_DEDUPING_INTERVAL = 2e3;
315
+ const lastRequestedAtByRoom = /* @__PURE__ */ new Map();
316
+ const requestsByQuery = /* @__PURE__ */ new Map();
317
+ const requestStatusByRoom = /* @__PURE__ */ new Map();
318
+ const subscribersByQuery = /* @__PURE__ */ new Map();
319
+ const mentionSuggestionsCache = /* @__PURE__ */ new Map();
320
+ const poller = makePoller(refreshThreadsAndNotifications);
321
+ async function refreshThreadsAndNotifications() {
322
+ const requests = [];
323
+ client[kInternal].getRoomIds().map((roomId) => {
324
+ const room = client.getRoom(roomId);
325
+ if (room === null)
326
+ return;
327
+ requests.push(getThreadsUpdates(room.id));
328
+ });
329
+ await Promise.allSettled(requests);
336
330
  }
337
- function RoomProviderInner(props) {
338
- const { id: roomId, stableEnterRoom } = props;
339
- if (process.env.NODE_ENV !== "production") {
340
- if (!roomId) {
341
- throw new Error(
342
- "RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
331
+ function incrementQuerySubscribers(queryKey) {
332
+ const subscribers = subscribersByQuery.get(queryKey) ?? 0;
333
+ subscribersByQuery.set(queryKey, subscribers + 1);
334
+ poller.start(POLLING_INTERVAL);
335
+ return () => {
336
+ const subscribers2 = subscribersByQuery.get(queryKey);
337
+ if (subscribers2 === void 0 || subscribers2 <= 0) {
338
+ console2.warn(
339
+ `Internal unexpected behavior. Cannot decrease subscriber count for query "${queryKey}"`
343
340
  );
341
+ return;
344
342
  }
345
- if (typeof roomId !== "string") {
346
- throw new Error("RoomProvider id property should be a string.");
343
+ subscribersByQuery.set(queryKey, subscribers2 - 1);
344
+ let totalSubscribers = 0;
345
+ for (const subscribers3 of subscribersByQuery.values()) {
346
+ totalSubscribers += subscribers3;
347
347
  }
348
- const majorReactVersion = parseInt(React2.version) || 1;
349
- const oldReactVersion = majorReactVersion < 18;
350
- errorIf(
351
- oldReactVersion && props.unstable_batchedUpdates === void 0,
352
- missing_unstable_batchedUpdates(majorReactVersion, roomId)
353
- );
354
- deprecateIf(
355
- !oldReactVersion && props.unstable_batchedUpdates !== void 0,
356
- superfluous_unstable_batchedUpdates
348
+ if (totalSubscribers <= 0) {
349
+ poller.stop();
350
+ }
351
+ };
352
+ }
353
+ async function getThreadsUpdates(roomId) {
354
+ const room = client.getRoom(roomId);
355
+ if (room === null)
356
+ return;
357
+ const since = lastRequestedAtByRoom.get(room.id);
358
+ if (since === void 0)
359
+ return;
360
+ const isFetchingThreadsUpdates = requestStatusByRoom.get(room.id) ?? false;
361
+ if (isFetchingThreadsUpdates === true)
362
+ return;
363
+ try {
364
+ requestStatusByRoom.set(room.id, true);
365
+ const updates = await room[kInternal].comments.getThreads({ since });
366
+ setTimeout(() => {
367
+ requestStatusByRoom.set(room.id, false);
368
+ }, DEFAULT_DEDUPING_INTERVAL);
369
+ store.updateThreadsAndNotifications(
370
+ updates.threads,
371
+ updates.inboxNotifications,
372
+ updates.deletedThreads,
373
+ updates.deletedInboxNotifications
357
374
  );
375
+ lastRequestedAtByRoom.set(room.id, updates.meta.requestedAt);
376
+ } catch (err) {
377
+ requestStatusByRoom.set(room.id, false);
378
+ return;
358
379
  }
359
- const frozenProps = useInitial({
360
- initialPresence: props.initialPresence,
361
- initialStorage: props.initialStorage,
362
- unstable_batchedUpdates: props.unstable_batchedUpdates,
363
- autoConnect: props.autoConnect ?? props.shouldInitiallyConnect ?? typeof window !== "undefined"
380
+ }
381
+ async function getThreadsAndInboxNotifications(room, queryKey, options, { retryCount } = { retryCount: 0 }) {
382
+ const existingRequest = requestsByQuery.get(queryKey);
383
+ if (existingRequest !== void 0)
384
+ return existingRequest;
385
+ const commentsAPI = room[kInternal].comments;
386
+ const request = commentsAPI.getThreads(options);
387
+ requestsByQuery.set(queryKey, request);
388
+ store.setQueryState(queryKey, {
389
+ isLoading: true
364
390
  });
365
- const [{ room }, setRoomLeavePair] = React2.useState(
366
- () => stableEnterRoom(roomId, {
367
- ...frozenProps,
368
- autoConnect: false
369
- // Deliberately using false here on the first render, see below
370
- })
371
- );
372
- React2.useEffect(() => {
373
- async function handleCommentEvent(message) {
374
- const info = await room[kInternal].comments.getThread({
375
- threadId: message.threadId
376
- });
377
- if (!info) {
378
- store.deleteThread(message.threadId);
379
- return;
380
- }
381
- const { thread, inboxNotification } = info;
382
- const existingThread = store.get().threads[message.threadId];
383
- switch (message.type) {
384
- case ServerMsgCode.COMMENT_EDITED:
385
- case ServerMsgCode.THREAD_METADATA_UPDATED:
386
- case ServerMsgCode.COMMENT_REACTION_ADDED:
387
- case ServerMsgCode.COMMENT_REACTION_REMOVED:
388
- case ServerMsgCode.COMMENT_DELETED:
389
- if (!existingThread)
390
- break;
391
- store.updateThreadAndNotification(thread, inboxNotification);
392
- break;
393
- case ServerMsgCode.COMMENT_CREATED:
394
- store.updateThreadAndNotification(thread, inboxNotification);
395
- break;
396
- default:
397
- break;
398
- }
399
- }
400
- return room.events.comments.subscribe(
401
- (message) => void handleCommentEvent(message)
391
+ try {
392
+ const result = await request;
393
+ store.updateThreadsAndNotifications(
394
+ result.threads,
395
+ result.inboxNotifications,
396
+ result.deletedThreads,
397
+ result.deletedInboxNotifications,
398
+ queryKey
402
399
  );
403
- }, [room]);
404
- React2.useEffect(() => {
405
- void getThreadsUpdates(room.id);
406
- }, [room.id]);
407
- React2.useEffect(() => {
408
- function handleIsOnline() {
409
- void getThreadsUpdates(room.id);
410
- }
411
- window.addEventListener("online", handleIsOnline);
412
- return () => {
413
- window.removeEventListener("online", handleIsOnline);
414
- };
415
- }, [room.id]);
416
- React2.useEffect(() => {
417
- const pair = stableEnterRoom(roomId, frozenProps);
418
- setRoomLeavePair(pair);
419
- const { room: room2, leave } = pair;
420
- if (frozenProps.autoConnect) {
421
- room2.connect();
400
+ const lastRequestedAt = lastRequestedAtByRoom.get(room.id);
401
+ if (lastRequestedAt === void 0 || lastRequestedAt > result.meta.requestedAt) {
402
+ lastRequestedAtByRoom.set(room.id, result.meta.requestedAt);
422
403
  }
423
- return () => {
424
- leave();
425
- };
426
- }, [roomId, frozenProps, stableEnterRoom]);
427
- return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React2.createElement(
428
- ContextBundle.Provider,
429
- {
430
- value: bundle
431
- },
432
- props.children
433
- ));
434
- }
435
- function connectionIdSelector(others) {
436
- return others.map((user) => user.connectionId);
437
- }
438
- function useRoom() {
439
- const room = React2.useContext(RoomContext);
440
- if (room === null) {
441
- throw new Error("RoomProvider is missing from the React tree.");
404
+ poller.start(POLLING_INTERVAL);
405
+ } catch (err) {
406
+ requestsByQuery.delete(queryKey);
407
+ retryError(() => {
408
+ void getThreadsAndInboxNotifications(room, queryKey, options, {
409
+ retryCount: retryCount + 1
410
+ });
411
+ }, retryCount);
412
+ store.setQueryState(queryKey, {
413
+ isLoading: false,
414
+ error: err
415
+ });
442
416
  }
443
- return room;
444
- }
445
- function useStatus() {
446
- const room = useRoom();
447
- const subscribe = room.events.status.subscribe;
448
- const getSnapshot = room.getStatus;
449
- const getServerSnapshot = room.getStatus;
450
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
417
+ return;
451
418
  }
452
- function useMyPresence() {
453
- const room = useRoom();
454
- const subscribe = room.events.myPresence.subscribe;
455
- const getSnapshot = room.getPresence;
456
- const presence = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
457
- const setPresence = room.updatePresence;
458
- return [presence, setPresence];
419
+ async function getInboxNotificationSettings(room, queryKey, { retryCount } = { retryCount: 0 }) {
420
+ const existingRequest = requestsByQuery.get(queryKey);
421
+ if (existingRequest !== void 0)
422
+ return existingRequest;
423
+ try {
424
+ const request = room[kInternal].notifications.getRoomNotificationSettings();
425
+ requestsByQuery.set(queryKey, request);
426
+ store.setQueryState(queryKey, {
427
+ isLoading: true
428
+ });
429
+ const settings = await request;
430
+ store.updateRoomInboxNotificationSettings(room.id, settings, queryKey);
431
+ } catch (err) {
432
+ requestsByQuery.delete(queryKey);
433
+ retryError(() => {
434
+ void getInboxNotificationSettings(room, queryKey, {
435
+ retryCount: retryCount + 1
436
+ });
437
+ }, retryCount);
438
+ store.setQueryState(queryKey, {
439
+ isLoading: false,
440
+ error: err
441
+ });
442
+ }
443
+ return;
459
444
  }
460
- function useUpdateMyPresence() {
461
- return useRoom().updatePresence;
445
+ const commentsErrorEventSource = makeEventSource();
446
+ function onMutationFailure(innerError, optimisticUpdateId, createPublicError) {
447
+ store.set((state) => ({
448
+ ...state,
449
+ optimisticUpdates: state.optimisticUpdates.filter(
450
+ (update) => update.id !== optimisticUpdateId
451
+ )
452
+ }));
453
+ if (innerError instanceof CommentsApiError) {
454
+ const error = handleApiError(innerError);
455
+ commentsErrorEventSource.notify(createPublicError(error));
456
+ return;
457
+ }
458
+ if (innerError instanceof NotificationsApiError) {
459
+ handleApiError(innerError);
460
+ return;
461
+ }
462
+ throw innerError;
462
463
  }
463
- function useOthers(selector, isEqual) {
464
+ function useMentionSuggestions(search) {
464
465
  const room = useRoom();
465
- const subscribe = room.events.others.subscribe;
466
- const getSnapshot = room.getOthers;
467
- const getServerSnapshot = alwaysEmptyList;
468
- return useSyncExternalStoreWithSelector(
469
- subscribe,
470
- getSnapshot,
471
- getServerSnapshot,
472
- selector ?? identity,
473
- isEqual
474
- );
466
+ const [mentionSuggestions, setMentionSuggestions] = React3.useState();
467
+ const lastInvokedAt = React3.useRef();
468
+ React3.useEffect(() => {
469
+ const resolveMentionSuggestions = client[kInternal].resolveMentionSuggestions;
470
+ if (search === void 0 || !resolveMentionSuggestions) {
471
+ return;
472
+ }
473
+ const resolveMentionSuggestionsArgs = { text: search, roomId: room.id };
474
+ const mentionSuggestionsCacheKey = stringify(
475
+ resolveMentionSuggestionsArgs
476
+ );
477
+ let debounceTimeout;
478
+ let isCanceled = false;
479
+ const getMentionSuggestions = async () => {
480
+ try {
481
+ lastInvokedAt.current = performance.now();
482
+ const mentionSuggestions2 = await resolveMentionSuggestions(
483
+ resolveMentionSuggestionsArgs
484
+ );
485
+ if (!isCanceled) {
486
+ setMentionSuggestions(mentionSuggestions2);
487
+ mentionSuggestionsCache.set(
488
+ mentionSuggestionsCacheKey,
489
+ mentionSuggestions2
490
+ );
491
+ }
492
+ } catch (error) {
493
+ console2.error(error?.message);
494
+ }
495
+ };
496
+ if (mentionSuggestionsCache.has(mentionSuggestionsCacheKey)) {
497
+ setMentionSuggestions(
498
+ mentionSuggestionsCache.get(mentionSuggestionsCacheKey)
499
+ );
500
+ } else if (!lastInvokedAt.current || Math.abs(performance.now() - lastInvokedAt.current) > MENTION_SUGGESTIONS_DEBOUNCE) {
501
+ void getMentionSuggestions();
502
+ } else {
503
+ debounceTimeout = window.setTimeout(() => {
504
+ void getMentionSuggestions();
505
+ }, MENTION_SUGGESTIONS_DEBOUNCE);
506
+ }
507
+ return () => {
508
+ isCanceled = true;
509
+ window.clearTimeout(debounceTimeout);
510
+ };
511
+ }, [room.id, search]);
512
+ return mentionSuggestions;
513
+ }
514
+ return {
515
+ store,
516
+ incrementQuerySubscribers,
517
+ commentsErrorEventSource,
518
+ getThreadsUpdates,
519
+ getThreadsAndInboxNotifications,
520
+ getInboxNotificationSettings,
521
+ useMentionSuggestions,
522
+ onMutationFailure
523
+ };
524
+ }
525
+ var RoomContext = React3.createContext(null);
526
+ function makeRoomContextBundle(client) {
527
+ function RoomProvider_withImplicitLiveblocksProvider(props) {
528
+ return /* @__PURE__ */ React3.createElement(LiveblocksProvider, { client }, /* @__PURE__ */ React3.createElement(RoomProvider, { ...props }));
475
529
  }
476
- function useOthersConnectionIds() {
477
- return useOthers(connectionIdSelector, shallow);
530
+ const { useMentionSuggestions } = getExtrasForClient(client);
531
+ const shared = createSharedContext(client);
532
+ const bundle = {
533
+ RoomContext,
534
+ RoomProvider: RoomProvider_withImplicitLiveblocksProvider,
535
+ useRoom,
536
+ useStatus,
537
+ useBatch,
538
+ useBroadcastEvent,
539
+ useOthersListener,
540
+ useLostConnectionListener,
541
+ useErrorListener,
542
+ useEventListener,
543
+ useHistory,
544
+ useUndo,
545
+ useRedo,
546
+ useCanRedo,
547
+ useCanUndo,
548
+ // These are just aliases. The passed-in key will define their return values.
549
+ useList: useLegacyKey,
550
+ useMap: useLegacyKey,
551
+ useObject: useLegacyKey,
552
+ useStorageRoot,
553
+ useStorage: make_useStorage(),
554
+ useSelf,
555
+ useMyPresence,
556
+ useUpdateMyPresence,
557
+ useOthers,
558
+ useOthersMapped: make_useOthersMapped(),
559
+ useOthersConnectionIds,
560
+ useOther: make_useOther(),
561
+ useMutation: make_useMutation(),
562
+ useThreads,
563
+ useCreateThread,
564
+ useEditThreadMetadata,
565
+ useCreateComment,
566
+ useEditComment,
567
+ useDeleteComment,
568
+ useAddReaction,
569
+ useRemoveReaction,
570
+ useMarkThreadAsRead,
571
+ useThreadSubscription,
572
+ useRoomNotificationSettings,
573
+ useUpdateRoomNotificationSettings,
574
+ ...shared.classic,
575
+ suspense: {
576
+ RoomContext,
577
+ RoomProvider: RoomProvider_withImplicitLiveblocksProvider,
578
+ useRoom,
579
+ useStatus,
580
+ useBatch,
581
+ useBroadcastEvent,
582
+ useOthersListener,
583
+ useLostConnectionListener,
584
+ useErrorListener,
585
+ useEventListener,
586
+ useHistory,
587
+ useUndo,
588
+ useRedo,
589
+ useCanRedo,
590
+ useCanUndo,
591
+ // Legacy hooks
592
+ useList: useLegacyKeySuspense,
593
+ useMap: useLegacyKeySuspense,
594
+ useObject: useLegacyKeySuspense,
595
+ useStorageRoot,
596
+ useStorage: make_useStorageSuspense(),
597
+ useSelf: useSelfSuspense,
598
+ useMyPresence,
599
+ useUpdateMyPresence,
600
+ useOthers: make_useOthersSuspense(),
601
+ useOthersMapped: make_useOthersMappedSuspense(),
602
+ useOthersConnectionIds: useOthersConnectionIdsSuspense,
603
+ useOther: make_useOtherSuspense(),
604
+ useMutation: make_useMutation(),
605
+ useThreads: useThreadsSuspense,
606
+ useCreateThread,
607
+ useEditThreadMetadata,
608
+ useCreateComment,
609
+ useEditComment,
610
+ useDeleteComment,
611
+ useAddReaction,
612
+ useRemoveReaction,
613
+ useMarkThreadAsRead,
614
+ useThreadSubscription,
615
+ useRoomNotificationSettings: useRoomNotificationSettingsSuspense,
616
+ useUpdateRoomNotificationSettings,
617
+ ...shared.suspense
618
+ },
619
+ [kInternal]: {
620
+ useCurrentUserId,
621
+ useMentionSuggestions,
622
+ useCommentsErrorListener,
623
+ ThreadCreateCallbackProvider: ThreadCreateCallbackContext.Provider,
624
+ useThreadCreateCallback,
625
+ ThreadDeleteCallbackProvider: ThreadDeleteCallbackContext.Provider,
626
+ useThreadDeleteCallback,
627
+ ComposerFocusCallbackProvider: ComposerFocusCallbackContext.Provider,
628
+ useComposerFocusCallback,
629
+ IsThreadActiveCallbackProvider: IsThreadActiveCallbackContext.Provider,
630
+ useIsThreadActiveCallback
631
+ }
632
+ };
633
+ return Object.defineProperty(bundle, kInternal, {
634
+ enumerable: false
635
+ });
636
+ }
637
+ function RoomProvider(props) {
638
+ const client = useClient();
639
+ const [cache] = React3.useState(
640
+ () => /* @__PURE__ */ new Map()
641
+ );
642
+ const stableEnterRoom = React3.useCallback(
643
+ (roomId, options) => {
644
+ const cached = cache.get(roomId);
645
+ if (cached)
646
+ return cached;
647
+ const rv = client.enterRoom(roomId, options);
648
+ const origLeave = rv.leave;
649
+ rv.leave = () => {
650
+ origLeave();
651
+ cache.delete(roomId);
652
+ };
653
+ cache.set(roomId, rv);
654
+ return rv;
655
+ },
656
+ [client, cache]
657
+ );
658
+ return /* @__PURE__ */ React3.createElement(RoomProviderInner, { ...props, stableEnterRoom });
659
+ }
660
+ function RoomProviderInner(props) {
661
+ const client = useClient();
662
+ const { id: roomId, stableEnterRoom } = props;
663
+ if (process.env.NODE_ENV !== "production") {
664
+ if (!roomId) {
665
+ throw new Error(
666
+ "RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
667
+ );
668
+ }
669
+ if (typeof roomId !== "string") {
670
+ throw new Error("RoomProvider id property should be a string.");
671
+ }
672
+ const majorReactVersion = parseInt(React3.version) || 1;
673
+ const oldReactVersion = majorReactVersion < 18;
674
+ errorIf(
675
+ oldReactVersion && props.unstable_batchedUpdates === void 0,
676
+ missing_unstable_batchedUpdates(majorReactVersion, roomId)
677
+ );
678
+ deprecateIf(
679
+ !oldReactVersion && props.unstable_batchedUpdates !== void 0,
680
+ superfluous_unstable_batchedUpdates
681
+ );
682
+ }
683
+ const frozenProps = useInitial({
684
+ initialPresence: props.initialPresence,
685
+ initialStorage: props.initialStorage,
686
+ unstable_batchedUpdates: props.unstable_batchedUpdates,
687
+ autoConnect: props.autoConnect ?? props.shouldInitiallyConnect ?? typeof window !== "undefined"
688
+ });
689
+ const [{ room }, setRoomLeavePair] = React3.useState(
690
+ () => stableEnterRoom(roomId, {
691
+ ...frozenProps,
692
+ autoConnect: false
693
+ // Deliberately using false here on the first render, see below
694
+ })
695
+ );
696
+ React3.useEffect(() => {
697
+ const { store } = getExtrasForClient(client);
698
+ async function handleCommentEvent(message) {
699
+ const info = await room[kInternal].comments.getThread({
700
+ threadId: message.threadId
701
+ });
702
+ if (!info) {
703
+ store.deleteThread(message.threadId);
704
+ return;
705
+ }
706
+ const { thread, inboxNotification } = info;
707
+ const existingThread = store.get().threads[message.threadId];
708
+ switch (message.type) {
709
+ case ServerMsgCode.COMMENT_EDITED:
710
+ case ServerMsgCode.THREAD_METADATA_UPDATED:
711
+ case ServerMsgCode.COMMENT_REACTION_ADDED:
712
+ case ServerMsgCode.COMMENT_REACTION_REMOVED:
713
+ case ServerMsgCode.COMMENT_DELETED:
714
+ if (!existingThread)
715
+ break;
716
+ store.updateThreadAndNotification(thread, inboxNotification);
717
+ break;
718
+ case ServerMsgCode.COMMENT_CREATED:
719
+ store.updateThreadAndNotification(thread, inboxNotification);
720
+ break;
721
+ default:
722
+ break;
723
+ }
724
+ }
725
+ return room.events.comments.subscribe(
726
+ (message) => void handleCommentEvent(message)
727
+ );
728
+ }, [client, room]);
729
+ React3.useEffect(() => {
730
+ const { getThreadsUpdates } = getExtrasForClient(client);
731
+ void getThreadsUpdates(room.id);
732
+ }, [client, room.id]);
733
+ React3.useEffect(() => {
734
+ function handleIsOnline() {
735
+ const { getThreadsUpdates } = getExtrasForClient(client);
736
+ void getThreadsUpdates(room.id);
737
+ }
738
+ window.addEventListener("online", handleIsOnline);
739
+ return () => {
740
+ window.removeEventListener("online", handleIsOnline);
741
+ };
742
+ }, [client, room.id]);
743
+ React3.useEffect(() => {
744
+ const pair = stableEnterRoom(roomId, frozenProps);
745
+ setRoomLeavePair(pair);
746
+ const { room: room2, leave } = pair;
747
+ if (frozenProps.autoConnect) {
748
+ room2.connect();
749
+ }
750
+ return () => {
751
+ leave();
752
+ };
753
+ }, [roomId, frozenProps, stableEnterRoom]);
754
+ return /* @__PURE__ */ React3.createElement(RoomContext.Provider, { value: room }, props.children);
755
+ }
756
+ function useRoom() {
757
+ const room = React3.useContext(RoomContext);
758
+ if (room === null) {
759
+ throw new Error("RoomProvider is missing from the React tree.");
478
760
  }
479
- function useOthersMapped(itemSelector, itemIsEqual) {
480
- const wrappedSelector = React2.useCallback(
761
+ return room;
762
+ }
763
+ function useStatus() {
764
+ const room = useRoom();
765
+ const subscribe = room.events.status.subscribe;
766
+ const getSnapshot = room.getStatus;
767
+ const getServerSnapshot = room.getStatus;
768
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
769
+ }
770
+ function useBatch() {
771
+ return useRoom().batch;
772
+ }
773
+ function useBroadcastEvent() {
774
+ const room = useRoom();
775
+ return React3.useCallback(
776
+ (event, options = { shouldQueueEventIfNotReady: false }) => {
777
+ room.broadcastEvent(event, options);
778
+ },
779
+ [room]
780
+ );
781
+ }
782
+ function useOthersListener(callback) {
783
+ const room = useRoom();
784
+ const savedCallback = useLatest(callback);
785
+ React3.useEffect(
786
+ () => room.events.others.subscribe((event) => savedCallback.current(event)),
787
+ [room, savedCallback]
788
+ );
789
+ }
790
+ function useLostConnectionListener(callback) {
791
+ const room = useRoom();
792
+ const savedCallback = useLatest(callback);
793
+ React3.useEffect(
794
+ () => room.events.lostConnection.subscribe(
795
+ (event) => savedCallback.current(event)
796
+ ),
797
+ [room, savedCallback]
798
+ );
799
+ }
800
+ function useErrorListener(callback) {
801
+ const room = useRoom();
802
+ const savedCallback = useLatest(callback);
803
+ React3.useEffect(
804
+ () => room.events.error.subscribe((e) => savedCallback.current(e)),
805
+ [room, savedCallback]
806
+ );
807
+ }
808
+ function useEventListener(callback) {
809
+ const room = useRoom();
810
+ const savedCallback = useLatest(callback);
811
+ React3.useEffect(() => {
812
+ const listener = (eventData) => {
813
+ savedCallback.current(eventData);
814
+ };
815
+ return room.events.customEvent.subscribe(listener);
816
+ }, [room, savedCallback]);
817
+ }
818
+ function useHistory() {
819
+ return useRoom().history;
820
+ }
821
+ function useUndo() {
822
+ return useHistory().undo;
823
+ }
824
+ function useRedo() {
825
+ return useHistory().redo;
826
+ }
827
+ function useCanUndo() {
828
+ const room = useRoom();
829
+ const subscribe = room.events.history.subscribe;
830
+ const canUndo = room.history.canUndo;
831
+ return useSyncExternalStore(subscribe, canUndo, canUndo);
832
+ }
833
+ function useCanRedo() {
834
+ const room = useRoom();
835
+ const subscribe = room.events.history.subscribe;
836
+ const canRedo = room.history.canRedo;
837
+ return useSyncExternalStore(subscribe, canRedo, canRedo);
838
+ }
839
+ function useSelf(maybeSelector, isEqual) {
840
+ const room = useRoom();
841
+ const subscribe = room.events.self.subscribe;
842
+ const getSnapshot = room.getSelf;
843
+ const selector = maybeSelector ?? identity;
844
+ const wrappedSelector = React3.useCallback(
845
+ (me) => me !== null ? selector(me) : null,
846
+ [selector]
847
+ );
848
+ const getServerSnapshot = alwaysNull;
849
+ return useSyncExternalStoreWithSelector(
850
+ subscribe,
851
+ getSnapshot,
852
+ getServerSnapshot,
853
+ wrappedSelector,
854
+ isEqual
855
+ );
856
+ }
857
+ function useCurrentUserId() {
858
+ return useSelf((user) => typeof user.id === "string" ? user.id : null);
859
+ }
860
+ function useMyPresence() {
861
+ const room = useRoom();
862
+ const subscribe = room.events.myPresence.subscribe;
863
+ const getSnapshot = room.getPresence;
864
+ const presence = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
865
+ const setPresence = room.updatePresence;
866
+ return [presence, setPresence];
867
+ }
868
+ function useUpdateMyPresence() {
869
+ return useRoom().updatePresence;
870
+ }
871
+ function useOthers(selector, isEqual) {
872
+ const room = useRoom();
873
+ const subscribe = room.events.others.subscribe;
874
+ const getSnapshot = room.getOthers;
875
+ const getServerSnapshot = alwaysEmptyList;
876
+ return useSyncExternalStoreWithSelector(
877
+ subscribe,
878
+ getSnapshot,
879
+ getServerSnapshot,
880
+ selector ?? identity,
881
+ isEqual
882
+ );
883
+ }
884
+ function make_useOthersMapped() {
885
+ return function useOthersMapped(itemSelector, itemIsEqual) {
886
+ const wrappedSelector = React3.useCallback(
481
887
  (others) => others.map(
482
888
  (other) => [other.connectionId, itemSelector(other)]
483
889
  ),
484
890
  [itemSelector]
485
891
  );
486
- const wrappedIsEqual = React2.useCallback(
892
+ const wrappedIsEqual = React3.useCallback(
487
893
  (a, b) => {
488
894
  const eq = itemIsEqual ?? Object.is;
489
895
  return a.length === b.length && a.every((atuple, index) => {
@@ -494,10 +900,15 @@ function createRoomContext(client, options) {
494
900
  [itemIsEqual]
495
901
  );
496
902
  return useOthers(wrappedSelector, wrappedIsEqual);
497
- }
498
- const NOT_FOUND = Symbol();
499
- function useOther(connectionId, selector, isEqual) {
500
- const wrappedSelector = React2.useCallback(
903
+ };
904
+ }
905
+ function useOthersConnectionIds() {
906
+ return useOthers(selectorFor_useOthersConnectionIds, shallow);
907
+ }
908
+ var NOT_FOUND = Symbol();
909
+ function make_useOther() {
910
+ return function useOther(connectionId, selector, isEqual) {
911
+ const wrappedSelector = React3.useCallback(
501
912
  (others) => {
502
913
  const other2 = others.find(
503
914
  (other3) => other3.connectionId === connectionId
@@ -506,7 +917,7 @@ function createRoomContext(client, options) {
506
917
  },
507
918
  [connectionId, selector]
508
919
  );
509
- const wrappedIsEqual = React2.useCallback(
920
+ const wrappedIsEqual = React3.useCallback(
510
921
  (prev, curr) => {
511
922
  if (prev === NOT_FOUND || curr === NOT_FOUND) {
512
923
  return prev === curr;
@@ -523,153 +934,31 @@ function createRoomContext(client, options) {
523
934
  );
524
935
  }
525
936
  return other;
526
- }
527
- function useBroadcastEvent() {
528
- const room = useRoom();
529
- return React2.useCallback(
530
- (event, options2 = { shouldQueueEventIfNotReady: false }) => {
531
- room.broadcastEvent(event, options2);
532
- },
533
- [room]
534
- );
535
- }
536
- function useOthersListener(callback) {
937
+ };
938
+ }
939
+ function useMutableStorageRoot() {
940
+ const room = useRoom();
941
+ const subscribe = room.events.storageDidLoad.subscribeOnce;
942
+ const getSnapshot = room.getStorageSnapshot;
943
+ const getServerSnapshot = alwaysNull;
944
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
945
+ }
946
+ function useStorageRoot() {
947
+ return [useMutableStorageRoot()];
948
+ }
949
+ function make_useStorage() {
950
+ return function useStorage(selector, isEqual) {
537
951
  const room = useRoom();
538
- const savedCallback = useLatest(callback);
539
- React2.useEffect(
540
- () => room.events.others.subscribe((event) => savedCallback.current(event)),
541
- [room, savedCallback]
952
+ const rootOrNull = useMutableStorageRoot();
953
+ const wrappedSelector = React3.useCallback(
954
+ (rootOrNull2) => rootOrNull2 !== null ? selector(rootOrNull2) : null,
955
+ [selector]
542
956
  );
543
- }
544
- function useLostConnectionListener(callback) {
545
- const room = useRoom();
546
- const savedCallback = useLatest(callback);
547
- React2.useEffect(
548
- () => room.events.lostConnection.subscribe(
549
- (event) => savedCallback.current(event)
550
- ),
551
- [room, savedCallback]
957
+ const subscribe = React3.useCallback(
958
+ (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop,
959
+ [room, rootOrNull]
552
960
  );
553
- }
554
- function useErrorListener(callback) {
555
- const room = useRoom();
556
- const savedCallback = useLatest(callback);
557
- React2.useEffect(
558
- () => room.events.error.subscribe((e) => savedCallback.current(e)),
559
- [room, savedCallback]
560
- );
561
- }
562
- function useEventListener(callback) {
563
- const room = useRoom();
564
- const savedCallback = useLatest(callback);
565
- React2.useEffect(() => {
566
- const listener = (eventData) => {
567
- savedCallback.current(eventData);
568
- };
569
- return room.events.customEvent.subscribe(listener);
570
- }, [room, savedCallback]);
571
- }
572
- function useSelf(maybeSelector, isEqual) {
573
- const room = useRoom();
574
- const subscribe = room.events.self.subscribe;
575
- const getSnapshot = room.getSelf;
576
- const selector = maybeSelector ?? identity;
577
- const wrappedSelector = React2.useCallback(
578
- (me) => me !== null ? selector(me) : null,
579
- [selector]
580
- );
581
- const getServerSnapshot = alwaysNull;
582
- return useSyncExternalStoreWithSelector(
583
- subscribe,
584
- getSnapshot,
585
- getServerSnapshot,
586
- wrappedSelector,
587
- isEqual
588
- );
589
- }
590
- function useMutableStorageRoot() {
591
- const room = useRoom();
592
- const subscribe = room.events.storageDidLoad.subscribeOnce;
593
- const getSnapshot = room.getStorageSnapshot;
594
- const getServerSnapshot = alwaysNull;
595
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
596
- }
597
- function useStorageRoot() {
598
- return [useMutableStorageRoot()];
599
- }
600
- function useHistory() {
601
- return useRoom().history;
602
- }
603
- function useUndo() {
604
- return useHistory().undo;
605
- }
606
- function useRedo() {
607
- return useHistory().redo;
608
- }
609
- function useCanUndo() {
610
- const room = useRoom();
611
- const subscribe = room.events.history.subscribe;
612
- const canUndo = room.history.canUndo;
613
- return useSyncExternalStore(subscribe, canUndo, canUndo);
614
- }
615
- function useCanRedo() {
616
- const room = useRoom();
617
- const subscribe = room.events.history.subscribe;
618
- const canRedo = room.history.canRedo;
619
- return useSyncExternalStore(subscribe, canRedo, canRedo);
620
- }
621
- function useBatch() {
622
- return useRoom().batch;
623
- }
624
- function useLegacyKey(key) {
625
- const room = useRoom();
626
- const rootOrNull = useMutableStorageRoot();
627
- const rerender = useRerender();
628
- React2.useEffect(() => {
629
- if (rootOrNull === null) {
630
- return;
631
- }
632
- const root = rootOrNull;
633
- let unsubCurr;
634
- let curr = root.get(key);
635
- function subscribeToCurr() {
636
- unsubCurr = isLiveNode(curr) ? room.subscribe(curr, rerender) : void 0;
637
- }
638
- function onRootChange() {
639
- const newValue = root.get(key);
640
- if (newValue !== curr) {
641
- unsubCurr?.();
642
- curr = newValue;
643
- subscribeToCurr();
644
- rerender();
645
- }
646
- }
647
- subscribeToCurr();
648
- rerender();
649
- const unsubscribeRoot = room.subscribe(root, onRootChange);
650
- return () => {
651
- unsubscribeRoot();
652
- unsubCurr?.();
653
- };
654
- }, [rootOrNull, room, key, rerender]);
655
- if (rootOrNull === null) {
656
- return null;
657
- } else {
658
- return rootOrNull.get(key);
659
- }
660
- }
661
- function useStorage(selector, isEqual) {
662
- const room = useRoom();
663
- const rootOrNull = useMutableStorageRoot();
664
- const wrappedSelector = React2.useCallback(
665
- (rootOrNull2) => rootOrNull2 !== null ? selector(rootOrNull2) : null,
666
- [selector]
667
- );
668
- const subscribe = React2.useCallback(
669
- (onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop,
670
- [room, rootOrNull]
671
- );
672
- const getSnapshot = React2.useCallback(() => {
961
+ const getSnapshot = React3.useCallback(() => {
673
962
  if (rootOrNull === null) {
674
963
  return null;
675
964
  } else {
@@ -686,38 +975,49 @@ function createRoomContext(client, options) {
686
975
  wrappedSelector,
687
976
  isEqual
688
977
  );
689
- }
690
- function ensureNotServerSide() {
691
- if (typeof window === "undefined") {
692
- throw new Error(
693
- "You cannot use the Suspense version of this hook on the server side. Make sure to only call them on the client side.\nFor tips, see https://liveblocks.io/docs/api-reference/liveblocks-react#suspense-avoid-ssr"
694
- );
695
- }
696
- }
697
- function useSuspendUntilStorageLoaded() {
698
- const room = useRoom();
699
- if (room.getStorageSnapshot() !== null) {
978
+ };
979
+ }
980
+ function useLegacyKey(key) {
981
+ const room = useRoom();
982
+ const rootOrNull = useMutableStorageRoot();
983
+ const rerender = useRerender();
984
+ React3.useEffect(() => {
985
+ if (rootOrNull === null) {
700
986
  return;
701
987
  }
702
- ensureNotServerSide();
703
- throw new Promise((res) => {
704
- room.events.storageDidLoad.subscribeOnce(() => res());
705
- });
706
- }
707
- function useSuspendUntilPresenceLoaded() {
708
- const room = useRoom();
709
- if (room.getSelf() !== null) {
710
- return;
988
+ const root = rootOrNull;
989
+ let unsubCurr;
990
+ let curr = root.get(key);
991
+ function subscribeToCurr() {
992
+ unsubCurr = isLiveNode(curr) ? room.subscribe(curr, rerender) : void 0;
711
993
  }
712
- ensureNotServerSide();
713
- throw new Promise((res) => {
714
- room.events.self.subscribeOnce(() => res());
715
- room.events.status.subscribeOnce(() => res());
716
- });
994
+ function onRootChange() {
995
+ const newValue = root.get(key);
996
+ if (newValue !== curr) {
997
+ unsubCurr?.();
998
+ curr = newValue;
999
+ subscribeToCurr();
1000
+ rerender();
1001
+ }
1002
+ }
1003
+ subscribeToCurr();
1004
+ rerender();
1005
+ const unsubscribeRoot = room.subscribe(root, onRootChange);
1006
+ return () => {
1007
+ unsubscribeRoot();
1008
+ unsubCurr?.();
1009
+ };
1010
+ }, [rootOrNull, room, key, rerender]);
1011
+ if (rootOrNull === null) {
1012
+ return null;
1013
+ } else {
1014
+ return rootOrNull.get(key);
717
1015
  }
718
- function useMutation(callback, deps) {
1016
+ }
1017
+ function make_useMutation() {
1018
+ return function useMutation(callback, deps) {
719
1019
  const room = useRoom();
720
- return React2.useMemo(
1020
+ return React3.useMemo(
721
1021
  () => {
722
1022
  return (...args) => (
723
1023
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
@@ -735,1103 +1035,887 @@ function createRoomContext(client, options) {
735
1035
  // eslint-disable-next-line react-hooks/exhaustive-deps
736
1036
  [room, ...deps]
737
1037
  );
738
- }
739
- function useStorageSuspense(selector, isEqual) {
740
- useSuspendUntilStorageLoaded();
741
- return useStorage(
742
- selector,
743
- isEqual
744
- );
745
- }
746
- function useSelfSuspense(selector, isEqual) {
747
- useSuspendUntilPresenceLoaded();
748
- return useSelf(
749
- selector,
750
- isEqual
751
- );
752
- }
753
- function useOthersSuspense(selector, isEqual) {
754
- useSuspendUntilPresenceLoaded();
755
- return useOthers(
756
- selector,
757
- isEqual
758
- );
759
- }
760
- function useOthersConnectionIdsSuspense() {
761
- useSuspendUntilPresenceLoaded();
762
- return useOthersConnectionIds();
763
- }
764
- function useOthersMappedSuspense(itemSelector, itemIsEqual) {
765
- useSuspendUntilPresenceLoaded();
766
- return useOthersMapped(itemSelector, itemIsEqual);
767
- }
768
- function useOtherSuspense(connectionId, selector, isEqual) {
769
- useSuspendUntilPresenceLoaded();
770
- return useOther(connectionId, selector, isEqual);
771
- }
772
- function useLegacyKeySuspense(key) {
773
- useSuspendUntilStorageLoaded();
774
- return useLegacyKey(key);
775
- }
776
- const store = client[kInternal].cacheStore;
777
- function onMutationFailure(innerError, optimisticUpdateId, createPublicError) {
778
- store.set((state) => ({
779
- ...state,
780
- optimisticUpdates: state.optimisticUpdates.filter(
781
- (update) => update.id !== optimisticUpdateId
782
- )
783
- }));
784
- if (innerError instanceof CommentsApiError) {
785
- const error = handleApiError(innerError);
786
- commentsErrorEventSource.notify(createPublicError(error));
787
- return;
788
- }
789
- if (innerError instanceof NotificationsApiError) {
790
- handleApiError(innerError);
791
- return;
792
- }
793
- throw innerError;
794
- }
795
- const subscribersByQuery = /* @__PURE__ */ new Map();
796
- const requestsByQuery = /* @__PURE__ */ new Map();
797
- const poller = makePoller(refreshThreadsAndNotifications);
798
- async function refreshThreadsAndNotifications() {
799
- const requests = [];
800
- client[kInternal].getRoomIds().map((roomId) => {
801
- const room = client.getRoom(roomId);
802
- if (room === null)
803
- return;
804
- requests.push(getThreadsUpdates(room.id));
805
- });
806
- await Promise.allSettled(requests);
807
- }
808
- function incrementQuerySubscribers(queryKey) {
809
- const subscribers = subscribersByQuery.get(queryKey) ?? 0;
810
- subscribersByQuery.set(queryKey, subscribers + 1);
811
- poller.start(POLLING_INTERVAL);
812
- }
813
- function decrementQuerySubscribers(queryKey) {
814
- const subscribers = subscribersByQuery.get(queryKey);
815
- if (subscribers === void 0 || subscribers <= 0) {
816
- console2.warn(
817
- `Internal unexpected behavior. Cannot decrease subscriber count for query "${queryKey}"`
818
- );
819
- return;
820
- }
821
- subscribersByQuery.set(queryKey, subscribers - 1);
822
- let totalSubscribers = 0;
823
- for (const subscribers2 of subscribersByQuery.values()) {
824
- totalSubscribers += subscribers2;
825
- }
826
- if (totalSubscribers <= 0) {
827
- poller.stop();
828
- }
829
- }
830
- async function getThreadsAndInboxNotifications(room, queryKey, options2, { retryCount } = { retryCount: 0 }) {
831
- const existingRequest = requestsByQuery.get(queryKey);
832
- if (existingRequest !== void 0)
833
- return existingRequest;
834
- const request = room[kInternal].comments.getThreads(options2);
835
- requestsByQuery.set(queryKey, request);
836
- store.setQueryState(queryKey, {
837
- isLoading: true
838
- });
839
- try {
840
- const result = await request;
841
- store.updateThreadsAndNotifications(
842
- result.threads,
843
- result.inboxNotifications,
844
- result.deletedThreads,
845
- result.deletedInboxNotifications,
846
- queryKey
847
- );
848
- const lastRequestedAt = lastRequestedAtByRoom.get(room.id);
849
- if (lastRequestedAt === void 0 || lastRequestedAt > result.meta.requestedAt) {
850
- lastRequestedAtByRoom.set(room.id, result.meta.requestedAt);
1038
+ };
1039
+ }
1040
+ function useThreads(options = {
1041
+ query: { metadata: {} }
1042
+ }) {
1043
+ const { scrollOnLoad = true } = options;
1044
+ const client = useClient();
1045
+ const room = useRoom();
1046
+ const queryKey = React3.useMemo(
1047
+ () => generateQueryKey(room.id, options.query),
1048
+ [room, options]
1049
+ );
1050
+ const { store, getThreadsAndInboxNotifications, incrementQuerySubscribers } = getExtrasForClient(client);
1051
+ React3.useEffect(() => {
1052
+ void getThreadsAndInboxNotifications(room, queryKey, options);
1053
+ return incrementQuerySubscribers(queryKey);
1054
+ }, [room, queryKey]);
1055
+ const selector = React3.useCallback(
1056
+ (state2) => {
1057
+ const query = state2.queries[queryKey];
1058
+ if (query === void 0 || query.isLoading) {
1059
+ return {
1060
+ isLoading: true
1061
+ };
851
1062
  }
852
- poller.start(POLLING_INTERVAL);
853
- } catch (err) {
854
- requestsByQuery.delete(queryKey);
855
- retryError(() => {
856
- void getThreadsAndInboxNotifications(room, queryKey, options2, {
857
- retryCount: retryCount + 1
858
- });
859
- }, retryCount);
860
- store.setQueryState(queryKey, {
1063
+ return {
1064
+ threads: client[kInternal].comments.selectedThreads(
1065
+ room.id,
1066
+ state2,
1067
+ options
1068
+ ),
861
1069
  isLoading: false,
862
- error: err
1070
+ error: query.error
1071
+ };
1072
+ },
1073
+ [room, queryKey]
1074
+ // eslint-disable-line react-hooks/exhaustive-deps
1075
+ );
1076
+ const state = useSyncExternalStoreWithSelector(
1077
+ store.subscribe,
1078
+ store.get,
1079
+ store.get,
1080
+ selector
1081
+ );
1082
+ useScrollToCommentOnLoadEffect(scrollOnLoad, state);
1083
+ return state;
1084
+ }
1085
+ function useCommentsErrorListener(callback) {
1086
+ const client = useClient();
1087
+ const savedCallback = useLatest(callback);
1088
+ const { commentsErrorEventSource } = getExtrasForClient(client);
1089
+ React3.useEffect(() => {
1090
+ return commentsErrorEventSource.subscribe(savedCallback.current);
1091
+ }, [savedCallback, commentsErrorEventSource]);
1092
+ }
1093
+ function useThreadCreateCallback() {
1094
+ return React3.useContext(ThreadCreateCallbackContext);
1095
+ }
1096
+ function useThreadDeleteCallback() {
1097
+ return React3.useContext(ThreadDeleteCallbackContext);
1098
+ }
1099
+ function useComposerFocusCallback() {
1100
+ return React3.useContext(ComposerFocusCallbackContext);
1101
+ }
1102
+ function useIsThreadActiveCallback() {
1103
+ return React3.useContext(IsThreadActiveCallbackContext);
1104
+ }
1105
+ function useCreateThread() {
1106
+ const client = useClient();
1107
+ const room = useRoom();
1108
+ const onCreateThread = useThreadCreateCallback();
1109
+ return React3.useCallback(
1110
+ (options) => {
1111
+ const body = options.body;
1112
+ const metadata = "metadata" in options ? options.metadata : {};
1113
+ const threadId = client[kInternal].comments.createThreadId();
1114
+ const commentId = client[kInternal].comments.createCommentId();
1115
+ const createdAt = /* @__PURE__ */ new Date();
1116
+ const newComment = {
1117
+ id: commentId,
1118
+ threadId,
1119
+ roomId: room.id,
1120
+ createdAt,
1121
+ type: "comment",
1122
+ userId: getCurrentUserId(room),
1123
+ body,
1124
+ reactions: []
1125
+ };
1126
+ const newThread = {
1127
+ id: threadId,
1128
+ type: "thread",
1129
+ createdAt,
1130
+ updatedAt: createdAt,
1131
+ roomId: room.id,
1132
+ metadata,
1133
+ comments: [newComment]
1134
+ };
1135
+ const optimisticUpdateId = nanoid();
1136
+ const { store, onMutationFailure } = getExtrasForClient(client);
1137
+ store.pushOptimisticUpdate({
1138
+ type: "create-thread",
1139
+ thread: newThread,
1140
+ id: optimisticUpdateId,
1141
+ roomId: room.id
863
1142
  });
864
- return;
865
- }
866
- }
867
- const DEFAULT_DEDUPING_INTERVAL = 2e3;
868
- const lastRequestedAtByRoom = /* @__PURE__ */ new Map();
869
- const requestStatusByRoom = /* @__PURE__ */ new Map();
870
- async function getThreadsUpdates(roomId) {
871
- const room = client.getRoom(roomId);
872
- if (room === null)
873
- return;
874
- const since = lastRequestedAtByRoom.get(room.id);
875
- if (since === void 0)
876
- return;
877
- const isFetchingThreadsUpdates = requestStatusByRoom.get(room.id) ?? false;
878
- if (isFetchingThreadsUpdates === true)
879
- return;
880
- try {
881
- requestStatusByRoom.set(room.id, true);
882
- const updates = await room[kInternal].comments.getThreads({ since });
883
- setTimeout(() => {
884
- requestStatusByRoom.set(room.id, false);
885
- }, DEFAULT_DEDUPING_INTERVAL);
886
- store.updateThreadsAndNotifications(
887
- updates.threads,
888
- updates.inboxNotifications,
889
- updates.deletedThreads,
890
- updates.deletedInboxNotifications
1143
+ onCreateThread?.(newThread.id);
1144
+ const commentsAPI = room[kInternal].comments;
1145
+ commentsAPI.createThread({ threadId, commentId, body, metadata }).then(
1146
+ (thread) => {
1147
+ store.set((state) => ({
1148
+ ...state,
1149
+ threads: {
1150
+ ...state.threads,
1151
+ [threadId]: thread
1152
+ },
1153
+ optimisticUpdates: state.optimisticUpdates.filter(
1154
+ (update) => update.id !== optimisticUpdateId
1155
+ )
1156
+ }));
1157
+ },
1158
+ (err) => onMutationFailure(
1159
+ err,
1160
+ optimisticUpdateId,
1161
+ (err2) => new CreateThreadError(err2, {
1162
+ roomId: room.id,
1163
+ threadId,
1164
+ commentId,
1165
+ body,
1166
+ metadata
1167
+ })
1168
+ )
891
1169
  );
892
- lastRequestedAtByRoom.set(room.id, updates.meta.requestedAt);
893
- } catch (err) {
894
- requestStatusByRoom.set(room.id, false);
895
- return;
896
- }
897
- }
898
- function handleScrollToCommentOnLoad(isQueryLoading, shouldScrollOnLoad, state) {
899
- if (shouldScrollOnLoad === false)
900
- return;
901
- if (isQueryLoading === true)
902
- return;
903
- const isWindowDefined = typeof window !== "undefined";
904
- if (!isWindowDefined)
905
- return;
906
- const hash = window.location.hash;
907
- const commentId = hash.slice(1);
908
- if (!commentId.startsWith("cm_"))
909
- return;
910
- const comment = document.getElementById(commentId);
911
- if (comment === null)
912
- return;
913
- const comments = state.threads.flatMap((thread) => thread.comments);
914
- const isCommentInThreads = comments.some(
915
- (comment2) => comment2.id === commentId
916
- );
917
- if (!isCommentInThreads)
918
- return;
919
- comment.scrollIntoView();
920
- }
921
- function useThreads(options2 = {
922
- query: { metadata: {} }
923
- }) {
924
- const { scrollOnLoad = true } = options2;
925
- const room = useRoom();
926
- const queryKey = React2.useMemo(
927
- () => generateQueryKey(room.id, options2.query),
928
- [room, options2]
929
- );
930
- React2.useEffect(() => {
931
- void getThreadsAndInboxNotifications(room, queryKey, options2);
932
- incrementQuerySubscribers(queryKey);
933
- return () => decrementQuerySubscribers(queryKey);
934
- }, [room, queryKey]);
935
- const selector = React2.useCallback(
936
- (state2) => {
937
- const query = state2.queries[queryKey];
938
- if (query === void 0 || query.isLoading) {
939
- return {
940
- isLoading: true
941
- };
942
- }
943
- return {
944
- threads: selectedThreads(room.id, state2, options2),
945
- isLoading: false,
946
- error: query.error
947
- };
948
- },
949
- [room, queryKey]
950
- // eslint-disable-line react-hooks/exhaustive-deps
951
- );
952
- const state = useSyncExternalStoreWithSelector(
953
- store.subscribe,
954
- store.get,
955
- store.get,
956
- selector
957
- );
958
- React2.useEffect(
959
- () => {
960
- if (state.isLoading === true)
961
- return;
962
- handleScrollToCommentOnLoad(state.isLoading, scrollOnLoad, state);
963
- },
964
- // eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this effect once
965
- [state.isLoading]
966
- );
967
- return state;
968
- }
969
- function useThreadsSuspense(options2 = {
970
- query: { metadata: {} }
971
- }) {
972
- const { scrollOnLoad = true } = options2;
973
- const room = useRoom();
974
- const queryKey = React2.useMemo(
975
- () => generateQueryKey(room.id, options2.query),
976
- [room, options2]
977
- );
978
- const query = store.get().queries[queryKey];
979
- if (query === void 0 || query.isLoading) {
980
- throw getThreadsAndInboxNotifications(room, queryKey, options2);
981
- }
982
- if (query.error) {
983
- throw query.error;
984
- }
985
- const selector = React2.useCallback(
986
- (state2) => {
987
- return {
988
- threads: selectedThreads(room.id, state2, options2),
989
- isLoading: false
990
- };
991
- },
992
- [room, queryKey]
993
- // eslint-disable-line react-hooks/exhaustive-deps
994
- );
995
- React2.useEffect(() => {
996
- incrementQuerySubscribers(queryKey);
997
- return () => {
998
- decrementQuerySubscribers(queryKey);
1170
+ return newThread;
1171
+ },
1172
+ [client, room, onCreateThread]
1173
+ );
1174
+ }
1175
+ function useEditThreadMetadata() {
1176
+ const client = useClient();
1177
+ const room = useRoom();
1178
+ return React3.useCallback(
1179
+ (options) => {
1180
+ if (!("metadata" in options)) {
1181
+ return;
1182
+ }
1183
+ const threadId = options.threadId;
1184
+ const metadata = options.metadata;
1185
+ const updatedAt = /* @__PURE__ */ new Date();
1186
+ const optimisticUpdateId = nanoid();
1187
+ const { store, onMutationFailure } = getExtrasForClient(client);
1188
+ store.pushOptimisticUpdate({
1189
+ type: "edit-thread-metadata",
1190
+ metadata,
1191
+ id: optimisticUpdateId,
1192
+ threadId,
1193
+ updatedAt
1194
+ });
1195
+ const commentsAPI = room[kInternal].comments;
1196
+ commentsAPI.editThreadMetadata({ metadata, threadId }).then(
1197
+ (metadata2) => {
1198
+ store.set((state) => {
1199
+ const existingThread = state.threads[threadId];
1200
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1201
+ (update) => update.id !== optimisticUpdateId
1202
+ );
1203
+ if (existingThread === void 0) {
1204
+ return {
1205
+ ...state,
1206
+ optimisticUpdates: updatedOptimisticUpdates
1207
+ };
1208
+ }
1209
+ if (existingThread.deletedAt !== void 0) {
1210
+ return {
1211
+ ...state,
1212
+ optimisticUpdates: updatedOptimisticUpdates
1213
+ };
1214
+ }
1215
+ if (existingThread.updatedAt && existingThread.updatedAt > updatedAt) {
1216
+ return {
1217
+ ...state,
1218
+ optimisticUpdates: updatedOptimisticUpdates
1219
+ };
1220
+ }
1221
+ return {
1222
+ ...state,
1223
+ threads: {
1224
+ ...state.threads,
1225
+ [threadId]: {
1226
+ ...existingThread,
1227
+ metadata: metadata2
1228
+ }
1229
+ },
1230
+ optimisticUpdates: updatedOptimisticUpdates
1231
+ };
1232
+ });
1233
+ },
1234
+ (err) => onMutationFailure(
1235
+ err,
1236
+ optimisticUpdateId,
1237
+ (error) => new EditThreadMetadataError(error, {
1238
+ roomId: room.id,
1239
+ threadId,
1240
+ metadata
1241
+ })
1242
+ )
1243
+ );
1244
+ },
1245
+ [client, room]
1246
+ );
1247
+ }
1248
+ function useCreateComment() {
1249
+ const client = useClient();
1250
+ const room = useRoom();
1251
+ return React3.useCallback(
1252
+ ({ threadId, body }) => {
1253
+ const commentId = client[kInternal].comments.createCommentId();
1254
+ const createdAt = /* @__PURE__ */ new Date();
1255
+ const comment = {
1256
+ id: commentId,
1257
+ threadId,
1258
+ roomId: room.id,
1259
+ type: "comment",
1260
+ createdAt,
1261
+ userId: getCurrentUserId(room),
1262
+ body,
1263
+ reactions: []
999
1264
  };
1000
- }, [queryKey]);
1001
- const state = useSyncExternalStoreWithSelector(
1002
- store.subscribe,
1003
- store.get,
1004
- store.get,
1005
- selector
1006
- );
1007
- React2.useEffect(
1008
- () => {
1009
- handleScrollToCommentOnLoad(state.isLoading, scrollOnLoad, state);
1010
- },
1011
- // eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this effect once
1012
- [state.isLoading]
1013
- );
1014
- return state;
1015
- }
1016
- function useCreateThread() {
1017
- const room = useRoom();
1018
- return React2.useCallback(
1019
- (options2) => {
1020
- const body = options2.body;
1021
- const metadata = "metadata" in options2 ? options2.metadata : {};
1022
- const threadId = createThreadId();
1023
- const commentId = createCommentId();
1024
- const createdAt = /* @__PURE__ */ new Date();
1025
- const newComment = {
1026
- id: commentId,
1027
- threadId,
1028
- roomId: room.id,
1029
- createdAt,
1030
- type: "comment",
1031
- userId: getCurrentUserId(room),
1032
- body,
1033
- reactions: []
1034
- };
1035
- const newThread = {
1036
- id: threadId,
1037
- type: "thread",
1038
- createdAt,
1039
- updatedAt: createdAt,
1040
- roomId: room.id,
1041
- metadata,
1042
- comments: [newComment]
1043
- };
1044
- const optimisticUpdateId = nanoid2();
1045
- store.pushOptimisticUpdate({
1046
- type: "create-thread",
1047
- thread: newThread,
1048
- id: optimisticUpdateId
1049
- });
1050
- room[kInternal].comments.createThread({ threadId, commentId, body, metadata }).then(
1051
- (thread) => {
1052
- store.set((state) => ({
1265
+ const optimisticUpdateId = nanoid();
1266
+ const { store, onMutationFailure } = getExtrasForClient(client);
1267
+ store.pushOptimisticUpdate({
1268
+ type: "create-comment",
1269
+ comment,
1270
+ id: optimisticUpdateId
1271
+ });
1272
+ room[kInternal].comments.createComment({ threadId, commentId, body }).then(
1273
+ (newComment) => {
1274
+ store.set((state) => {
1275
+ const existingThread = state.threads[threadId];
1276
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1277
+ (update) => update.id !== optimisticUpdateId
1278
+ );
1279
+ if (existingThread === void 0) {
1280
+ return {
1281
+ ...state,
1282
+ optimisticUpdates: updatedOptimisticUpdates
1283
+ };
1284
+ }
1285
+ const inboxNotification = Object.values(
1286
+ state.inboxNotifications
1287
+ ).find(
1288
+ (notification) => notification.kind === "thread" && notification.threadId === threadId
1289
+ );
1290
+ const updatedInboxNotifications = inboxNotification !== void 0 ? {
1291
+ ...state.inboxNotifications,
1292
+ [inboxNotification.id]: {
1293
+ ...inboxNotification,
1294
+ notifiedAt: newComment.createdAt,
1295
+ readAt: newComment.createdAt
1296
+ }
1297
+ } : state.inboxNotifications;
1298
+ return {
1053
1299
  ...state,
1054
1300
  threads: {
1055
1301
  ...state.threads,
1056
- [threadId]: thread
1302
+ [threadId]: upsertComment(existingThread, newComment)
1303
+ // Upsert the new comment into the thread comments list (if applicable)
1057
1304
  },
1058
- optimisticUpdates: state.optimisticUpdates.filter(
1059
- (update) => update.id !== optimisticUpdateId
1060
- )
1061
- }));
1062
- },
1063
- (err) => onMutationFailure(
1064
- err,
1065
- optimisticUpdateId,
1066
- (err2) => new CreateThreadError(err2, {
1067
- roomId: room.id,
1068
- threadId,
1069
- commentId,
1070
- body,
1071
- metadata
1072
- })
1073
- )
1305
+ inboxNotifications: updatedInboxNotifications,
1306
+ optimisticUpdates: updatedOptimisticUpdates
1307
+ };
1308
+ });
1309
+ },
1310
+ (err) => onMutationFailure(
1311
+ err,
1312
+ optimisticUpdateId,
1313
+ (err2) => new CreateCommentError(err2, {
1314
+ roomId: room.id,
1315
+ threadId,
1316
+ commentId,
1317
+ body
1318
+ })
1319
+ )
1320
+ );
1321
+ return comment;
1322
+ },
1323
+ [client, room]
1324
+ );
1325
+ }
1326
+ function useEditComment() {
1327
+ const client = useClient();
1328
+ const room = useRoom();
1329
+ return React3.useCallback(
1330
+ ({ threadId, commentId, body }) => {
1331
+ const editedAt = /* @__PURE__ */ new Date();
1332
+ const optimisticUpdateId = nanoid();
1333
+ const { store, onMutationFailure } = getExtrasForClient(client);
1334
+ const thread = store.get().threads[threadId];
1335
+ if (thread === void 0) {
1336
+ console2.warn(
1337
+ `Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
1074
1338
  );
1075
- return newThread;
1076
- },
1077
- [room]
1078
- );
1079
- }
1080
- function useEditThreadMetadata() {
1081
- const room = useRoom();
1082
- return React2.useCallback(
1083
- (options2) => {
1084
- if (!("metadata" in options2)) {
1085
- return;
1086
- }
1087
- const threadId = options2.threadId;
1088
- const metadata = options2.metadata;
1089
- const updatedAt = /* @__PURE__ */ new Date();
1090
- const optimisticUpdateId = nanoid2();
1091
- store.pushOptimisticUpdate({
1092
- type: "edit-thread-metadata",
1093
- metadata,
1094
- id: optimisticUpdateId,
1095
- threadId,
1096
- updatedAt
1097
- });
1098
- room[kInternal].comments.editThreadMetadata({ metadata, threadId }).then(
1099
- (metadata2) => {
1100
- store.set((state) => {
1101
- const existingThread = state.threads[threadId];
1102
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1103
- (update) => update.id !== optimisticUpdateId
1104
- );
1105
- if (existingThread === void 0) {
1106
- return {
1107
- ...state,
1108
- optimisticUpdates: updatedOptimisticUpdates
1109
- };
1110
- }
1111
- if (existingThread.deletedAt !== void 0) {
1112
- return {
1113
- ...state,
1114
- optimisticUpdates: updatedOptimisticUpdates
1115
- };
1116
- }
1117
- if (existingThread.updatedAt && existingThread.updatedAt > updatedAt) {
1118
- return {
1119
- ...state,
1120
- optimisticUpdates: updatedOptimisticUpdates
1121
- };
1122
- }
1339
+ return;
1340
+ }
1341
+ const comment = thread.comments.find(
1342
+ (comment2) => comment2.id === commentId
1343
+ );
1344
+ if (comment === void 0 || comment.deletedAt !== void 0) {
1345
+ console2.warn(
1346
+ `Internal unexpected behavior. Cannot edit comment "${commentId}" in thread "${threadId}" because the comment does not exist in the cache.`
1347
+ );
1348
+ return;
1349
+ }
1350
+ store.pushOptimisticUpdate({
1351
+ type: "edit-comment",
1352
+ comment: {
1353
+ ...comment,
1354
+ editedAt,
1355
+ body
1356
+ },
1357
+ id: optimisticUpdateId
1358
+ });
1359
+ room[kInternal].comments.editComment({ threadId, commentId, body }).then(
1360
+ (editedComment) => {
1361
+ store.set((state) => {
1362
+ const existingThread = state.threads[threadId];
1363
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1364
+ (update) => update.id !== optimisticUpdateId
1365
+ );
1366
+ if (existingThread === void 0) {
1123
1367
  return {
1124
1368
  ...state,
1125
- threads: {
1126
- ...state.threads,
1127
- [threadId]: {
1128
- ...existingThread,
1129
- metadata: metadata2
1130
- }
1131
- },
1132
1369
  optimisticUpdates: updatedOptimisticUpdates
1133
1370
  };
1134
- });
1135
- },
1136
- (err) => onMutationFailure(
1137
- err,
1138
- optimisticUpdateId,
1139
- (error) => new EditThreadMetadataError(error, {
1140
- roomId: room.id,
1141
- threadId,
1142
- metadata
1143
- })
1144
- )
1145
- );
1146
- },
1147
- [room]
1148
- );
1149
- }
1150
- function useAddReaction() {
1151
- const room = useRoom();
1152
- return React2.useCallback(
1153
- ({ threadId, commentId, emoji }) => {
1154
- const createdAt = /* @__PURE__ */ new Date();
1155
- const userId = getCurrentUserId(room);
1156
- const optimisticUpdateId = nanoid2();
1157
- store.pushOptimisticUpdate({
1158
- type: "add-reaction",
1159
- threadId,
1160
- commentId,
1161
- reaction: {
1162
- emoji,
1163
- userId,
1164
- createdAt
1165
- },
1166
- id: optimisticUpdateId
1167
- });
1168
- room[kInternal].comments.addReaction({ threadId, commentId, emoji }).then(
1169
- (addedReaction) => {
1170
- store.set((state) => {
1171
- const existingThread = state.threads[threadId];
1172
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1173
- (update) => update.id !== optimisticUpdateId
1174
- );
1175
- if (existingThread === void 0) {
1176
- return {
1177
- ...state,
1178
- optimisticUpdates: updatedOptimisticUpdates
1179
- };
1180
- }
1371
+ }
1372
+ return {
1373
+ ...state,
1374
+ threads: {
1375
+ ...state.threads,
1376
+ [threadId]: upsertComment(existingThread, editedComment)
1377
+ // Upsert the edited comment into the thread comments list (if applicable)
1378
+ },
1379
+ optimisticUpdates: updatedOptimisticUpdates
1380
+ };
1381
+ });
1382
+ },
1383
+ (err) => onMutationFailure(
1384
+ err,
1385
+ optimisticUpdateId,
1386
+ (error) => new EditCommentError(error, {
1387
+ roomId: room.id,
1388
+ threadId,
1389
+ commentId,
1390
+ body
1391
+ })
1392
+ )
1393
+ );
1394
+ },
1395
+ [client, room]
1396
+ );
1397
+ }
1398
+ function useDeleteComment() {
1399
+ const client = useClient();
1400
+ const room = useRoom();
1401
+ const onDeleteThread = useThreadDeleteCallback();
1402
+ return React3.useCallback(
1403
+ ({ threadId, commentId }) => {
1404
+ const deletedAt = /* @__PURE__ */ new Date();
1405
+ const optimisticUpdateId = nanoid();
1406
+ const { store, onMutationFailure } = getExtrasForClient(client);
1407
+ store.pushOptimisticUpdate({
1408
+ type: "delete-comment",
1409
+ threadId,
1410
+ commentId,
1411
+ deletedAt,
1412
+ id: optimisticUpdateId,
1413
+ roomId: room.id
1414
+ });
1415
+ const thread = store.get().threads[threadId];
1416
+ if (thread !== void 0 && thread.deletedAt === void 0) {
1417
+ const newThread = deleteComment(thread, commentId, deletedAt);
1418
+ if (newThread.deletedAt !== void 0) {
1419
+ onDeleteThread?.(threadId);
1420
+ }
1421
+ }
1422
+ room[kInternal].comments.deleteComment({ threadId, commentId }).then(
1423
+ () => {
1424
+ store.set((state) => {
1425
+ const existingThread = state.threads[threadId];
1426
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1427
+ (update) => update.id !== optimisticUpdateId
1428
+ );
1429
+ if (existingThread === void 0) {
1181
1430
  return {
1182
1431
  ...state,
1183
- threads: {
1184
- ...state.threads,
1185
- [threadId]: addReaction(
1186
- existingThread,
1187
- commentId,
1188
- addedReaction
1189
- )
1190
- },
1191
1432
  optimisticUpdates: updatedOptimisticUpdates
1192
1433
  };
1193
- });
1194
- },
1195
- (err) => onMutationFailure(
1196
- err,
1197
- optimisticUpdateId,
1198
- (error) => new AddReactionError(error, {
1199
- roomId: room.id,
1200
- threadId,
1201
- commentId,
1202
- emoji
1203
- })
1204
- )
1205
- );
1206
- },
1207
- [room]
1208
- );
1209
- }
1210
- function useRemoveReaction() {
1211
- const room = useRoom();
1212
- return React2.useCallback(
1213
- ({ threadId, commentId, emoji }) => {
1214
- const userId = getCurrentUserId(room);
1215
- const removedAt = /* @__PURE__ */ new Date();
1216
- const optimisticUpdateId = nanoid2();
1217
- store.pushOptimisticUpdate({
1218
- type: "remove-reaction",
1219
- threadId,
1220
- commentId,
1434
+ }
1435
+ return {
1436
+ ...state,
1437
+ threads: {
1438
+ ...state.threads,
1439
+ [threadId]: deleteComment(existingThread, commentId, deletedAt)
1440
+ },
1441
+ optimisticUpdates: updatedOptimisticUpdates
1442
+ };
1443
+ });
1444
+ },
1445
+ (err) => onMutationFailure(
1446
+ err,
1447
+ optimisticUpdateId,
1448
+ (error) => new DeleteCommentError(error, {
1449
+ roomId: room.id,
1450
+ threadId,
1451
+ commentId
1452
+ })
1453
+ )
1454
+ );
1455
+ },
1456
+ [client, room, onDeleteThread]
1457
+ );
1458
+ }
1459
+ function useAddReaction() {
1460
+ const client = useClient();
1461
+ const room = useRoom();
1462
+ return React3.useCallback(
1463
+ ({ threadId, commentId, emoji }) => {
1464
+ const createdAt = /* @__PURE__ */ new Date();
1465
+ const userId = getCurrentUserId(room);
1466
+ const optimisticUpdateId = nanoid();
1467
+ const { store, onMutationFailure } = getExtrasForClient(client);
1468
+ store.pushOptimisticUpdate({
1469
+ type: "add-reaction",
1470
+ threadId,
1471
+ commentId,
1472
+ reaction: {
1221
1473
  emoji,
1222
1474
  userId,
1223
- removedAt,
1224
- id: optimisticUpdateId
1225
- });
1226
- room[kInternal].comments.removeReaction({ threadId, commentId, emoji }).then(
1227
- () => {
1228
- store.set((state) => {
1229
- const existingThread = state.threads[threadId];
1230
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1231
- (update) => update.id !== optimisticUpdateId
1232
- );
1233
- if (existingThread === void 0) {
1234
- return {
1235
- ...state,
1236
- optimisticUpdates: updatedOptimisticUpdates
1237
- };
1238
- }
1475
+ createdAt
1476
+ },
1477
+ id: optimisticUpdateId
1478
+ });
1479
+ room[kInternal].comments.addReaction({ threadId, commentId, emoji }).then(
1480
+ (addedReaction) => {
1481
+ store.set((state) => {
1482
+ const existingThread = state.threads[threadId];
1483
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1484
+ (update) => update.id !== optimisticUpdateId
1485
+ );
1486
+ if (existingThread === void 0) {
1239
1487
  return {
1240
1488
  ...state,
1241
- threads: {
1242
- ...state.threads,
1243
- [threadId]: removeReaction(
1244
- existingThread,
1245
- commentId,
1246
- emoji,
1247
- userId,
1248
- removedAt
1249
- )
1250
- },
1251
1489
  optimisticUpdates: updatedOptimisticUpdates
1252
1490
  };
1253
- });
1254
- },
1255
- (err) => onMutationFailure(
1256
- err,
1257
- optimisticUpdateId,
1258
- (error) => new RemoveReactionError(error, {
1259
- roomId: room.id,
1260
- threadId,
1261
- commentId,
1262
- emoji
1263
- })
1264
- )
1265
- );
1266
- },
1267
- [room]
1268
- );
1269
- }
1270
- function useCreateComment() {
1271
- const room = useRoom();
1272
- return React2.useCallback(
1273
- ({ threadId, body }) => {
1274
- const commentId = createCommentId();
1275
- const createdAt = /* @__PURE__ */ new Date();
1276
- const comment = {
1277
- id: commentId,
1278
- threadId,
1279
- roomId: room.id,
1280
- type: "comment",
1281
- createdAt,
1282
- userId: getCurrentUserId(room),
1283
- body,
1284
- reactions: []
1285
- };
1286
- const optimisticUpdateId = nanoid2();
1287
- store.pushOptimisticUpdate({
1288
- type: "create-comment",
1289
- comment,
1290
- id: optimisticUpdateId
1291
- });
1292
- room[kInternal].comments.createComment({ threadId, commentId, body }).then(
1293
- (newComment) => {
1294
- store.set((state) => {
1295
- const existingThread = state.threads[threadId];
1296
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1297
- (update) => update.id !== optimisticUpdateId
1298
- );
1299
- if (existingThread === void 0) {
1300
- return {
1301
- ...state,
1302
- optimisticUpdates: updatedOptimisticUpdates
1303
- };
1304
- }
1305
- const inboxNotification = Object.values(
1306
- state.inboxNotifications
1307
- ).find((notification) => notification.threadId === threadId);
1308
- const updatedInboxNotifications = inboxNotification !== void 0 ? {
1309
- ...state.inboxNotifications,
1310
- [inboxNotification.id]: {
1311
- ...inboxNotification,
1312
- notifiedAt: newComment.createdAt,
1313
- readAt: newComment.createdAt
1314
- }
1315
- } : state.inboxNotifications;
1491
+ }
1492
+ return {
1493
+ ...state,
1494
+ threads: {
1495
+ ...state.threads,
1496
+ [threadId]: addReaction(
1497
+ existingThread,
1498
+ commentId,
1499
+ addedReaction
1500
+ )
1501
+ },
1502
+ optimisticUpdates: updatedOptimisticUpdates
1503
+ };
1504
+ });
1505
+ },
1506
+ (err) => onMutationFailure(
1507
+ err,
1508
+ optimisticUpdateId,
1509
+ (error) => new AddReactionError(error, {
1510
+ roomId: room.id,
1511
+ threadId,
1512
+ commentId,
1513
+ emoji
1514
+ })
1515
+ )
1516
+ );
1517
+ },
1518
+ [client, room]
1519
+ );
1520
+ }
1521
+ function useRemoveReaction() {
1522
+ const client = useClient();
1523
+ const room = useRoom();
1524
+ return React3.useCallback(
1525
+ ({ threadId, commentId, emoji }) => {
1526
+ const userId = getCurrentUserId(room);
1527
+ const removedAt = /* @__PURE__ */ new Date();
1528
+ const optimisticUpdateId = nanoid();
1529
+ const { store, onMutationFailure } = getExtrasForClient(client);
1530
+ store.pushOptimisticUpdate({
1531
+ type: "remove-reaction",
1532
+ threadId,
1533
+ commentId,
1534
+ emoji,
1535
+ userId,
1536
+ removedAt,
1537
+ id: optimisticUpdateId
1538
+ });
1539
+ room[kInternal].comments.removeReaction({ threadId, commentId, emoji }).then(
1540
+ () => {
1541
+ store.set((state) => {
1542
+ const existingThread = state.threads[threadId];
1543
+ const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1544
+ (update) => update.id !== optimisticUpdateId
1545
+ );
1546
+ if (existingThread === void 0) {
1316
1547
  return {
1317
1548
  ...state,
1318
- threads: {
1319
- ...state.threads,
1320
- [threadId]: upsertComment(existingThread, newComment)
1321
- // Upsert the new comment into the thread comments list (if applicable)
1322
- },
1323
- inboxNotifications: updatedInboxNotifications,
1324
1549
  optimisticUpdates: updatedOptimisticUpdates
1325
1550
  };
1326
- });
1327
- },
1328
- (err) => onMutationFailure(
1551
+ }
1552
+ return {
1553
+ ...state,
1554
+ threads: {
1555
+ ...state.threads,
1556
+ [threadId]: removeReaction(
1557
+ existingThread,
1558
+ commentId,
1559
+ emoji,
1560
+ userId,
1561
+ removedAt
1562
+ )
1563
+ },
1564
+ optimisticUpdates: updatedOptimisticUpdates
1565
+ };
1566
+ });
1567
+ },
1568
+ (err) => onMutationFailure(
1569
+ err,
1570
+ optimisticUpdateId,
1571
+ (error) => new RemoveReactionError(error, {
1572
+ roomId: room.id,
1573
+ threadId,
1574
+ commentId,
1575
+ emoji
1576
+ })
1577
+ )
1578
+ );
1579
+ },
1580
+ [client, room]
1581
+ );
1582
+ }
1583
+ function useMarkThreadAsRead() {
1584
+ const client = useClient();
1585
+ const room = useRoom();
1586
+ return React3.useCallback(
1587
+ (threadId) => {
1588
+ const { store, onMutationFailure } = getExtrasForClient(client);
1589
+ const inboxNotification = Object.values(
1590
+ store.get().inboxNotifications
1591
+ ).find(
1592
+ (inboxNotification2) => inboxNotification2.kind === "thread" && inboxNotification2.threadId === threadId
1593
+ );
1594
+ if (!inboxNotification)
1595
+ return;
1596
+ const optimisticUpdateId = nanoid();
1597
+ const now = /* @__PURE__ */ new Date();
1598
+ store.pushOptimisticUpdate({
1599
+ type: "mark-inbox-notification-as-read",
1600
+ id: optimisticUpdateId,
1601
+ inboxNotificationId: inboxNotification.id,
1602
+ readAt: now
1603
+ });
1604
+ room[kInternal].notifications.markInboxNotificationAsRead(inboxNotification.id).then(
1605
+ () => {
1606
+ store.set((state) => ({
1607
+ ...state,
1608
+ inboxNotifications: {
1609
+ ...state.inboxNotifications,
1610
+ [inboxNotification.id]: {
1611
+ ...inboxNotification,
1612
+ readAt: now
1613
+ }
1614
+ },
1615
+ optimisticUpdates: state.optimisticUpdates.filter(
1616
+ (update) => update.id !== optimisticUpdateId
1617
+ )
1618
+ }));
1619
+ },
1620
+ (err) => {
1621
+ onMutationFailure(
1329
1622
  err,
1330
1623
  optimisticUpdateId,
1331
- (err2) => new CreateCommentError(err2, {
1332
- roomId: room.id,
1333
- threadId,
1334
- commentId,
1335
- body
1624
+ (error) => new MarkInboxNotificationAsReadError(error, {
1625
+ inboxNotificationId: inboxNotification.id
1336
1626
  })
1337
- )
1338
- );
1339
- return comment;
1340
- },
1341
- [room]
1342
- );
1343
- }
1344
- function useEditComment() {
1345
- const room = useRoom();
1346
- return React2.useCallback(
1347
- ({ threadId, commentId, body }) => {
1348
- const editedAt = /* @__PURE__ */ new Date();
1349
- const optimisticUpdateId = nanoid2();
1350
- const thread = store.get().threads[threadId];
1351
- if (thread === void 0) {
1352
- console2.warn(
1353
- `Internal unexpected behavior. Cannot edit comment in thread "${threadId}" because the thread does not exist in the cache.`
1354
1627
  );
1355
1628
  return;
1356
1629
  }
1357
- const comment = thread.comments.find(
1358
- (comment2) => comment2.id === commentId
1359
- );
1360
- if (comment === void 0 || comment.deletedAt !== void 0) {
1361
- console2.warn(
1362
- `Internal unexpected behavior. Cannot edit comment "${commentId}" in thread "${threadId}" because the comment does not exist in the cache.`
1363
- );
1364
- return;
1365
- }
1366
- store.pushOptimisticUpdate({
1367
- type: "edit-comment",
1368
- comment: {
1369
- ...comment,
1370
- editedAt,
1371
- body
1372
- },
1373
- id: optimisticUpdateId
1374
- });
1375
- room[kInternal].comments.editComment({ threadId, commentId, body }).then(
1376
- (editedComment) => {
1377
- store.set((state) => {
1378
- const existingThread = state.threads[threadId];
1379
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1380
- (update) => update.id !== optimisticUpdateId
1381
- );
1382
- if (existingThread === void 0) {
1383
- return {
1384
- ...state,
1385
- optimisticUpdates: updatedOptimisticUpdates
1386
- };
1387
- }
1388
- return {
1389
- ...state,
1390
- threads: {
1391
- ...state.threads,
1392
- [threadId]: upsertComment(existingThread, editedComment)
1393
- // Upsert the edited comment into the thread comments list (if applicable)
1394
- },
1395
- optimisticUpdates: updatedOptimisticUpdates
1396
- };
1397
- });
1398
- },
1399
- (err) => onMutationFailure(
1400
- err,
1401
- optimisticUpdateId,
1402
- (error) => new EditCommentError(error, {
1403
- roomId: room.id,
1404
- threadId,
1405
- commentId,
1406
- body
1407
- })
1408
- )
1409
- );
1410
- },
1411
- [room]
1412
- );
1413
- }
1414
- function useDeleteComment() {
1415
- const room = useRoom();
1416
- return React2.useCallback(
1417
- ({ threadId, commentId }) => {
1418
- const deletedAt = /* @__PURE__ */ new Date();
1419
- const optimisticUpdateId = nanoid2();
1420
- store.pushOptimisticUpdate({
1421
- type: "delete-comment",
1422
- threadId,
1423
- commentId,
1424
- deletedAt,
1425
- id: optimisticUpdateId
1426
- });
1427
- room[kInternal].comments.deleteComment({ threadId, commentId }).then(
1428
- () => {
1429
- store.set((state) => {
1430
- const existingThread = state.threads[threadId];
1431
- const updatedOptimisticUpdates = state.optimisticUpdates.filter(
1432
- (update) => update.id !== optimisticUpdateId
1433
- );
1434
- if (existingThread === void 0) {
1435
- return {
1436
- ...state,
1437
- optimisticUpdates: updatedOptimisticUpdates
1438
- };
1439
- }
1440
- return {
1441
- ...state,
1442
- threads: {
1443
- ...state.threads,
1444
- [threadId]: deleteComment(
1445
- existingThread,
1446
- commentId,
1447
- deletedAt
1448
- )
1449
- },
1450
- optimisticUpdates: updatedOptimisticUpdates
1451
- };
1452
- });
1453
- },
1454
- (err) => onMutationFailure(
1455
- err,
1456
- optimisticUpdateId,
1457
- (error) => new DeleteCommentError(error, {
1458
- roomId: room.id,
1459
- threadId,
1460
- commentId
1461
- })
1462
- )
1463
- );
1464
- },
1465
- [room]
1466
- );
1467
- }
1468
- const resolveMentionSuggestions = client[kInternal].resolveMentionSuggestions;
1469
- const mentionSuggestionsCache = /* @__PURE__ */ new Map();
1470
- function useMentionSuggestions(search) {
1471
- const room = useRoom();
1472
- const [mentionSuggestions, setMentionSuggestions] = React2.useState();
1473
- const lastInvokedAt = React2.useRef();
1474
- React2.useEffect(() => {
1475
- if (search === void 0 || !resolveMentionSuggestions) {
1476
- return;
1477
- }
1478
- const resolveMentionSuggestionsArgs = { text: search, roomId: room.id };
1479
- const mentionSuggestionsCacheKey = stringify(
1480
- resolveMentionSuggestionsArgs
1481
1630
  );
1482
- let debounceTimeout;
1483
- let isCanceled = false;
1484
- const getMentionSuggestions = async () => {
1485
- try {
1486
- lastInvokedAt.current = performance.now();
1487
- const mentionSuggestions2 = await resolveMentionSuggestions(
1488
- resolveMentionSuggestionsArgs
1489
- );
1490
- if (!isCanceled) {
1491
- setMentionSuggestions(mentionSuggestions2);
1492
- mentionSuggestionsCache.set(
1493
- mentionSuggestionsCacheKey,
1494
- mentionSuggestions2
1495
- );
1496
- }
1497
- } catch (error) {
1498
- console2.error(error?.message);
1499
- }
1500
- };
1501
- if (mentionSuggestionsCache.has(mentionSuggestionsCacheKey)) {
1502
- setMentionSuggestions(
1503
- mentionSuggestionsCache.get(mentionSuggestionsCacheKey)
1504
- );
1505
- } else if (!lastInvokedAt.current || Math.abs(performance.now() - lastInvokedAt.current) > MENTION_SUGGESTIONS_DEBOUNCE) {
1506
- void getMentionSuggestions();
1507
- } else {
1508
- debounceTimeout = window.setTimeout(() => {
1509
- void getMentionSuggestions();
1510
- }, MENTION_SUGGESTIONS_DEBOUNCE);
1511
- }
1512
- return () => {
1513
- isCanceled = true;
1514
- window.clearTimeout(debounceTimeout);
1515
- };
1516
- }, [room.id, search]);
1517
- return mentionSuggestions;
1518
- }
1519
- function useThreadSubscription(threadId) {
1520
- const selector = React2.useCallback(
1521
- (state) => {
1522
- const inboxNotification = selectedInboxNotifications(state).find(
1523
- (inboxNotification2) => inboxNotification2.threadId === threadId
1524
- );
1525
- const thread = state.threads[threadId];
1526
- if (inboxNotification === void 0 || thread === void 0) {
1527
- return {
1528
- status: "not-subscribed"
1529
- };
1530
- }
1631
+ },
1632
+ [client, room]
1633
+ );
1634
+ }
1635
+ function useThreadSubscription(threadId) {
1636
+ const client = useClient();
1637
+ const { store } = getExtrasForClient(client);
1638
+ const selector = React3.useCallback(
1639
+ (state) => {
1640
+ const inboxNotification = client[kInternal].comments.selectedInboxNotifications(state).find(
1641
+ (inboxNotification2) => inboxNotification2.kind === "thread" && inboxNotification2.threadId === threadId
1642
+ );
1643
+ const thread = state.threads[threadId];
1644
+ if (inboxNotification === void 0 || thread === void 0) {
1531
1645
  return {
1532
- status: "subscribed",
1533
- unreadSince: inboxNotification.readAt
1646
+ status: "not-subscribed"
1534
1647
  };
1535
- },
1536
- [threadId]
1537
- );
1538
- return useSyncExternalStoreWithSelector(
1539
- store.subscribe,
1540
- store.get,
1541
- store.get,
1542
- selector
1543
- );
1544
- }
1545
- function useMarkThreadAsRead() {
1546
- const room = useRoom();
1547
- return React2.useCallback(
1548
- (threadId) => {
1549
- const inboxNotification = Object.values(
1550
- store.get().inboxNotifications
1551
- ).find((inboxNotification2) => inboxNotification2.threadId === threadId);
1552
- if (!inboxNotification)
1553
- return;
1554
- const optimisticUpdateId = nanoid2();
1555
- const now = /* @__PURE__ */ new Date();
1556
- store.pushOptimisticUpdate({
1557
- type: "mark-inbox-notification-as-read",
1558
- id: optimisticUpdateId,
1559
- inboxNotificationId: inboxNotification.id,
1560
- readAt: now
1561
- });
1562
- room[kInternal].notifications.markInboxNotificationAsRead(inboxNotification.id).then(
1563
- () => {
1564
- store.set((state) => ({
1565
- ...state,
1566
- inboxNotifications: {
1567
- ...state.inboxNotifications,
1568
- [inboxNotification.id]: {
1569
- ...inboxNotification,
1570
- readAt: now
1571
- }
1572
- },
1573
- optimisticUpdates: state.optimisticUpdates.filter(
1574
- (update) => update.id !== optimisticUpdateId
1575
- )
1576
- }));
1577
- },
1578
- (err) => {
1579
- onMutationFailure(
1580
- err,
1581
- optimisticUpdateId,
1582
- (error) => new MarkInboxNotificationAsReadError(error, {
1583
- inboxNotificationId: inboxNotification.id
1584
- })
1585
- );
1586
- return;
1587
- }
1588
- );
1589
- },
1590
- [room]
1591
- );
1592
- }
1593
- function makeNotificationSettingsQueryKey(roomId) {
1594
- return `${roomId}:NOTIFICATION_SETTINGS`;
1595
- }
1596
- async function getInboxNotificationSettings(room, queryKey, { retryCount } = { retryCount: 0 }) {
1597
- const existingRequest = requestsByQuery.get(queryKey);
1598
- if (existingRequest !== void 0)
1599
- return existingRequest;
1600
- try {
1601
- const request = room[kInternal].notifications.getRoomNotificationSettings();
1602
- requestsByQuery.set(queryKey, request);
1603
- store.setQueryState(queryKey, {
1604
- isLoading: true
1605
- });
1606
- const settings = await request;
1607
- store.updateRoomInboxNotificationSettings(room.id, settings, queryKey);
1608
- } catch (err) {
1609
- requestsByQuery.delete(queryKey);
1610
- retryError(() => {
1611
- void getInboxNotificationSettings(room, queryKey, {
1612
- retryCount: retryCount + 1
1613
- });
1614
- }, retryCount);
1615
- store.setQueryState(queryKey, {
1648
+ }
1649
+ return {
1650
+ status: "subscribed",
1651
+ unreadSince: inboxNotification.readAt
1652
+ };
1653
+ },
1654
+ [threadId]
1655
+ );
1656
+ return useSyncExternalStoreWithSelector(
1657
+ store.subscribe,
1658
+ store.get,
1659
+ store.get,
1660
+ selector
1661
+ );
1662
+ }
1663
+ function useRoomNotificationSettings() {
1664
+ const client = useClient();
1665
+ const room = useRoom();
1666
+ const { store } = getExtrasForClient(client);
1667
+ React3.useEffect(() => {
1668
+ const { getInboxNotificationSettings } = getExtrasForClient(client);
1669
+ const queryKey = makeNotificationSettingsQueryKey(room.id);
1670
+ void getInboxNotificationSettings(room, queryKey);
1671
+ }, [client, room]);
1672
+ const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
1673
+ const selector = React3.useCallback(
1674
+ (state) => {
1675
+ const query = state.queries[makeNotificationSettingsQueryKey(room.id)];
1676
+ if (query === void 0 || query.isLoading) {
1677
+ return { isLoading: true };
1678
+ }
1679
+ if (query.error !== void 0) {
1680
+ return { isLoading: false, error: query.error };
1681
+ }
1682
+ return {
1616
1683
  isLoading: false,
1617
- error: err
1684
+ settings: client[kInternal].comments.selectNotificationSettings(
1685
+ room.id,
1686
+ state
1687
+ )
1688
+ };
1689
+ },
1690
+ [room]
1691
+ );
1692
+ const settings = useSyncExternalStoreWithSelector(
1693
+ store.subscribe,
1694
+ store.get,
1695
+ store.get,
1696
+ selector
1697
+ );
1698
+ return React3.useMemo(() => {
1699
+ return [settings, updateRoomNotificationSettings];
1700
+ }, [settings, updateRoomNotificationSettings]);
1701
+ }
1702
+ function useUpdateRoomNotificationSettings() {
1703
+ const client = useClient();
1704
+ const room = useRoom();
1705
+ return React3.useCallback(
1706
+ (settings) => {
1707
+ const optimisticUpdateId = nanoid();
1708
+ const { store, onMutationFailure } = getExtrasForClient(client);
1709
+ store.pushOptimisticUpdate({
1710
+ id: optimisticUpdateId,
1711
+ type: "update-notification-settings",
1712
+ roomId: room.id,
1713
+ settings
1618
1714
  });
1619
- return;
1620
- }
1621
- }
1622
- function useRoomNotificationSettings() {
1623
- const room = useRoom();
1624
- React2.useEffect(() => {
1625
- const queryKey = makeNotificationSettingsQueryKey(room.id);
1626
- void getInboxNotificationSettings(room, queryKey);
1627
- }, [room]);
1628
- const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
1629
- const selector = React2.useCallback(
1630
- (state) => {
1631
- const query = state.queries[makeNotificationSettingsQueryKey(room.id)];
1632
- if (query === void 0 || query.isLoading) {
1633
- return { isLoading: true };
1634
- }
1635
- if (query.error !== void 0) {
1636
- return { isLoading: false, error: query.error };
1637
- }
1638
- return {
1639
- isLoading: false,
1640
- settings: selectNotificationSettings(room.id, state)
1641
- };
1642
- },
1643
- [room]
1644
- );
1645
- const settings = useSyncExternalStoreWithSelector(
1646
- store.subscribe,
1647
- store.get,
1648
- store.get,
1649
- selector
1715
+ room[kInternal].notifications.updateRoomNotificationSettings(settings).then(
1716
+ (settings2) => {
1717
+ store.set((state) => ({
1718
+ ...state,
1719
+ notificationSettings: {
1720
+ [room.id]: settings2
1721
+ },
1722
+ optimisticUpdates: state.optimisticUpdates.filter(
1723
+ (update) => update.id !== optimisticUpdateId
1724
+ )
1725
+ }));
1726
+ },
1727
+ (err) => onMutationFailure(
1728
+ err,
1729
+ optimisticUpdateId,
1730
+ (error) => new UpdateNotificationSettingsError(error, {
1731
+ roomId: room.id
1732
+ })
1733
+ )
1734
+ );
1735
+ },
1736
+ [client, room]
1737
+ );
1738
+ }
1739
+ function ensureNotServerSide() {
1740
+ if (typeof window === "undefined") {
1741
+ throw new Error(
1742
+ "You cannot use the Suspense version of this hook on the server side. Make sure to only call them on the client side.\nFor tips, see https://liveblocks.io/docs/api-reference/liveblocks-react#suspense-avoid-ssr"
1650
1743
  );
1651
- return React2.useMemo(() => {
1652
- return [settings, updateRoomNotificationSettings];
1653
- }, [settings, updateRoomNotificationSettings]);
1654
1744
  }
1655
- function useRoomNotificationSettingsSuspense() {
1656
- const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
1657
- const room = useRoom();
1658
- const queryKey = makeNotificationSettingsQueryKey(room.id);
1659
- const query = store.get().queries[queryKey];
1660
- if (query === void 0 || query.isLoading) {
1661
- throw getInboxNotificationSettings(room, queryKey);
1662
- }
1663
- if (query.error) {
1664
- throw query.error;
1665
- }
1666
- const selector = React2.useCallback(
1667
- (state) => {
1668
- return {
1669
- isLoading: false,
1670
- settings: selectNotificationSettings(room.id, state)
1671
- };
1672
- },
1673
- [room]
1674
- );
1675
- const settings = useSyncExternalStoreWithSelector(
1676
- store.subscribe,
1677
- store.get,
1678
- store.get,
1679
- selector
1745
+ }
1746
+ function useSuspendUntilPresenceLoaded() {
1747
+ const room = useRoom();
1748
+ if (room.getSelf() !== null) {
1749
+ return;
1750
+ }
1751
+ ensureNotServerSide();
1752
+ throw new Promise((res) => {
1753
+ room.events.self.subscribeOnce(() => res());
1754
+ room.events.status.subscribeOnce(() => res());
1755
+ });
1756
+ }
1757
+ function useSelfSuspense(selector, isEqual) {
1758
+ useSuspendUntilPresenceLoaded();
1759
+ return useSelf(
1760
+ selector,
1761
+ isEqual
1762
+ );
1763
+ }
1764
+ function make_useOthersSuspense() {
1765
+ return function useOthersSuspense(selector, isEqual) {
1766
+ useSuspendUntilPresenceLoaded();
1767
+ return useOthers(
1768
+ selector,
1769
+ isEqual
1680
1770
  );
1681
- return React2.useMemo(() => {
1682
- return [settings, updateRoomNotificationSettings];
1683
- }, [settings, updateRoomNotificationSettings]);
1771
+ };
1772
+ }
1773
+ function useOthersConnectionIdsSuspense() {
1774
+ useSuspendUntilPresenceLoaded();
1775
+ return useOthersConnectionIds();
1776
+ }
1777
+ function make_useOthersMappedSuspense() {
1778
+ const useOthersMapped = make_useOthersMapped();
1779
+ return function(itemSelector, itemIsEqual) {
1780
+ useSuspendUntilPresenceLoaded();
1781
+ return useOthersMapped(itemSelector, itemIsEqual);
1782
+ };
1783
+ }
1784
+ function make_useOtherSuspense() {
1785
+ const useOther = make_useOther();
1786
+ return function useOtherSuspense(connectionId, selector, isEqual) {
1787
+ useSuspendUntilPresenceLoaded();
1788
+ return useOther(connectionId, selector, isEqual);
1789
+ };
1790
+ }
1791
+ function useSuspendUntilStorageLoaded() {
1792
+ const room = useRoom();
1793
+ if (room.getStorageSnapshot() !== null) {
1794
+ return;
1684
1795
  }
1685
- function useUpdateRoomNotificationSettings() {
1686
- const room = useRoom();
1687
- return React2.useCallback(
1688
- (settings) => {
1689
- const optimisticUpdateId = nanoid2();
1690
- store.pushOptimisticUpdate({
1691
- id: optimisticUpdateId,
1692
- type: "update-notification-settings",
1693
- roomId: room.id,
1694
- settings
1695
- });
1696
- room[kInternal].notifications.updateRoomNotificationSettings(settings).then(
1697
- (settings2) => {
1698
- store.set((state) => ({
1699
- ...state,
1700
- notificationSettings: {
1701
- [room.id]: settings2
1702
- },
1703
- optimisticUpdates: state.optimisticUpdates.filter(
1704
- (update) => update.id !== optimisticUpdateId
1705
- )
1706
- }));
1707
- },
1708
- (err) => onMutationFailure(
1709
- err,
1710
- optimisticUpdateId,
1711
- (error) => new UpdateNotificationSettingsError(error, {
1712
- roomId: room.id
1713
- })
1714
- )
1715
- );
1716
- },
1717
- [room]
1796
+ ensureNotServerSide();
1797
+ throw new Promise((res) => {
1798
+ room.events.storageDidLoad.subscribeOnce(() => res());
1799
+ });
1800
+ }
1801
+ function make_useStorageSuspense() {
1802
+ const useStorage = make_useStorage();
1803
+ return function useStorageSuspense(selector, isEqual) {
1804
+ useSuspendUntilStorageLoaded();
1805
+ return useStorage(
1806
+ selector,
1807
+ isEqual
1718
1808
  );
1809
+ };
1810
+ }
1811
+ function useLegacyKeySuspense(key) {
1812
+ useSuspendUntilStorageLoaded();
1813
+ return useLegacyKey(key);
1814
+ }
1815
+ function useThreadsSuspense(options = {
1816
+ query: { metadata: {} }
1817
+ }) {
1818
+ const { scrollOnLoad = true } = options;
1819
+ const client = useClient();
1820
+ const room = useRoom();
1821
+ const queryKey = React3.useMemo(
1822
+ () => generateQueryKey(room.id, options.query),
1823
+ [room, options]
1824
+ );
1825
+ const { store, getThreadsAndInboxNotifications } = getExtrasForClient(client);
1826
+ const query = store.get().queries[queryKey];
1827
+ if (query === void 0 || query.isLoading) {
1828
+ throw getThreadsAndInboxNotifications(room, queryKey, options);
1719
1829
  }
1720
- function useCurrentUserId() {
1721
- return useSelf((user) => typeof user.id === "string" ? user.id : null);
1830
+ if (query.error) {
1831
+ throw query.error;
1722
1832
  }
1723
- const bundle = {
1724
- RoomContext,
1725
- RoomProvider: RoomProviderOuter,
1726
- useRoom,
1727
- useStatus,
1728
- useBatch,
1729
- useBroadcastEvent,
1730
- useOthersListener,
1731
- useLostConnectionListener,
1732
- useErrorListener,
1733
- useEventListener,
1734
- useHistory,
1735
- useUndo,
1736
- useRedo,
1737
- useCanRedo,
1738
- useCanUndo,
1739
- // These are just aliases. The passed-in key will define their return values.
1740
- useList: useLegacyKey,
1741
- useMap: useLegacyKey,
1742
- useObject: useLegacyKey,
1743
- useStorageRoot,
1744
- useStorage,
1745
- useSelf,
1746
- useMyPresence,
1747
- useUpdateMyPresence,
1748
- useOthers,
1749
- useOthersMapped,
1750
- useOthersConnectionIds,
1751
- useOther,
1752
- useMutation,
1753
- useThreads,
1754
- useCreateThread,
1755
- useEditThreadMetadata,
1756
- useCreateComment,
1757
- useEditComment,
1758
- useDeleteComment,
1759
- useAddReaction,
1760
- useRemoveReaction,
1761
- useMarkThreadAsRead,
1762
- useThreadSubscription,
1763
- useRoomNotificationSettings,
1764
- useUpdateRoomNotificationSettings,
1765
- ...shared,
1766
- suspense: {
1767
- RoomContext,
1768
- RoomProvider: RoomProviderOuter,
1769
- useRoom,
1770
- useStatus,
1771
- useBatch,
1772
- useBroadcastEvent,
1773
- useOthersListener,
1774
- useLostConnectionListener,
1775
- useErrorListener,
1776
- useEventListener,
1777
- useHistory,
1778
- useUndo,
1779
- useRedo,
1780
- useCanRedo,
1781
- useCanUndo,
1782
- // Legacy hooks
1783
- useList: useLegacyKeySuspense,
1784
- useMap: useLegacyKeySuspense,
1785
- useObject: useLegacyKeySuspense,
1786
- useStorageRoot,
1787
- useStorage: useStorageSuspense,
1788
- useSelf: useSelfSuspense,
1789
- useMyPresence,
1790
- useUpdateMyPresence,
1791
- useOthers: useOthersSuspense,
1792
- useOthersMapped: useOthersMappedSuspense,
1793
- useOthersConnectionIds: useOthersConnectionIdsSuspense,
1794
- useOther: useOtherSuspense,
1795
- useMutation,
1796
- useThreads: useThreadsSuspense,
1797
- useCreateThread,
1798
- useEditThreadMetadata,
1799
- useCreateComment,
1800
- useEditComment,
1801
- useDeleteComment,
1802
- useAddReaction,
1803
- useRemoveReaction,
1804
- useMarkThreadAsRead,
1805
- useThreadSubscription,
1806
- useRoomNotificationSettings: useRoomNotificationSettingsSuspense,
1807
- useUpdateRoomNotificationSettings,
1808
- ...shared.suspense
1833
+ const selector = React3.useCallback(
1834
+ (state2) => {
1835
+ return {
1836
+ threads: client[kInternal].comments.selectedThreads(
1837
+ room.id,
1838
+ state2,
1839
+ options
1840
+ ),
1841
+ isLoading: false
1842
+ };
1809
1843
  },
1810
- [kInternal]: {
1811
- useCurrentUserId,
1812
- hasResolveMentionSuggestions: resolveMentionSuggestions !== void 0,
1813
- useMentionSuggestions
1814
- }
1815
- };
1816
- return Object.defineProperty(bundle, kInternal, {
1817
- enumerable: false
1818
- });
1844
+ [room, queryKey]
1845
+ // eslint-disable-line react-hooks/exhaustive-deps
1846
+ );
1847
+ React3.useEffect(() => {
1848
+ const { incrementQuerySubscribers } = getExtrasForClient(client);
1849
+ return incrementQuerySubscribers(queryKey);
1850
+ }, [client, queryKey]);
1851
+ const state = useSyncExternalStoreWithSelector(
1852
+ store.subscribe,
1853
+ store.get,
1854
+ store.get,
1855
+ selector
1856
+ );
1857
+ useScrollToCommentOnLoadEffect(scrollOnLoad, state);
1858
+ return state;
1819
1859
  }
1820
- function getCurrentUserId(room) {
1821
- const self = room.getSelf();
1822
- if (self === null || self.id === void 0) {
1823
- return "anonymous";
1824
- } else {
1825
- return self.id;
1826
- }
1860
+ function useRoomNotificationSettingsSuspense() {
1861
+ const updateRoomNotificationSettings = useUpdateRoomNotificationSettings();
1862
+ const client = useClient();
1863
+ const room = useRoom();
1864
+ const queryKey = makeNotificationSettingsQueryKey(room.id);
1865
+ const { store, getInboxNotificationSettings } = getExtrasForClient(client);
1866
+ const query = store.get().queries[queryKey];
1867
+ if (query === void 0 || query.isLoading) {
1868
+ throw getInboxNotificationSettings(room, queryKey);
1869
+ }
1870
+ if (query.error) {
1871
+ throw query.error;
1872
+ }
1873
+ const selector = React3.useCallback(
1874
+ (state) => {
1875
+ return {
1876
+ isLoading: false,
1877
+ settings: client[kInternal].comments.selectNotificationSettings(
1878
+ room.id,
1879
+ state
1880
+ )
1881
+ };
1882
+ },
1883
+ [room]
1884
+ );
1885
+ const settings = useSyncExternalStoreWithSelector(
1886
+ store.subscribe,
1887
+ store.get,
1888
+ store.get,
1889
+ selector
1890
+ );
1891
+ return React3.useMemo(() => {
1892
+ return [settings, updateRoomNotificationSettings];
1893
+ }, [settings, updateRoomNotificationSettings]);
1894
+ }
1895
+ function useRoomOrNull() {
1896
+ return React3.useContext(RoomContext);
1897
+ }
1898
+ function useRoomContextBundleOrNull() {
1899
+ const client = useClientOrNull();
1900
+ const room = useRoomOrNull();
1901
+ return client && room ? getOrCreateRoomContextBundle(client) : null;
1902
+ }
1903
+ function useRoomContextBundle() {
1904
+ const client = useClient();
1905
+ return getOrCreateRoomContextBundle(client);
1827
1906
  }
1828
- function handleApiError(err) {
1829
- const message = `Request failed with status ${err.status}: ${err.message}`;
1830
- if (err.details?.error === "FORBIDDEN") {
1831
- const detailedMessage = [message, err.details.suggestion, err.details.docs].filter(Boolean).join("\n");
1832
- console2.error(detailedMessage);
1907
+ function createRoomContext(client, options) {
1908
+ if (options?.resolveUsers) {
1909
+ throw new Error(
1910
+ "The 'resolveUsers' option has moved to 'createClient' from '@liveblocks/client'. Please refer to our Upgrade Guide to learn more, see https://liveblocks.io/docs/platform/upgrading/1.10."
1911
+ );
1833
1912
  }
1834
- return new Error(message);
1913
+ if (options?.resolveMentionSuggestions) {
1914
+ throw new Error(
1915
+ "The 'resolveMentionSuggestions' option has moved to 'createClient' from '@liveblocks/client'. Please refer to our Upgrade Guide to learn more, see https://liveblocks.io/docs/platform/upgrading/1.10."
1916
+ );
1917
+ }
1918
+ return getOrCreateRoomContextBundle(client);
1835
1919
  }
1836
1920
  function generateQueryKey(roomId, options) {
1837
1921
  return `${roomId}-${stringify(options ?? {})}`;
@@ -1839,14 +1923,14 @@ function generateQueryKey(roomId, options) {
1839
1923
 
1840
1924
  // src/shared.ts
1841
1925
  function useSharedContextBundle() {
1842
- const roomContextBundle = useContext2(ContextBundle);
1843
- const liveblocksContextBundle = useContext2(ContextBundle2);
1926
+ const roomContextBundle = useRoomContextBundleOrNull();
1927
+ const liveblocksContextBundle = useLiveblocksContextBundleOrNull();
1844
1928
  if (roomContextBundle !== null) {
1845
1929
  return roomContextBundle;
1846
1930
  } else if (liveblocksContextBundle !== null) {
1847
1931
  return liveblocksContextBundle;
1848
1932
  } else {
1849
- throw new Error(
1933
+ raise(
1850
1934
  "LiveblocksProvider or RoomProvider are missing from the React tree."
1851
1935
  );
1852
1936
  }
@@ -1857,142 +1941,201 @@ var missingUserError = new Error(
1857
1941
  var missingRoomInfoError = new Error(
1858
1942
  "resolveRoomsInfo didn't return anything for this room ID."
1859
1943
  );
1860
- function createSharedContext(client) {
1944
+ function useUser_withClient(client, userId) {
1945
+ const usersStore = client[kInternal2].usersStore;
1946
+ const getUserState = useCallback2(
1947
+ () => usersStore.getState(userId),
1948
+ [usersStore, userId]
1949
+ );
1950
+ useEffect5(() => {
1951
+ void usersStore.get(userId);
1952
+ }, [usersStore, userId]);
1953
+ const state = useSyncExternalStore2(
1954
+ usersStore.subscribe,
1955
+ getUserState,
1956
+ getUserState
1957
+ );
1958
+ return state ? {
1959
+ isLoading: state.isLoading,
1960
+ user: state.data,
1961
+ // Return an error if `undefined` was returned by `resolveUsers` for this user ID
1962
+ error: !state.isLoading && !state.data && !state.error ? missingUserError : state.error
1963
+ } : { isLoading: true };
1964
+ }
1965
+ function useUserSuspense_withClient(client, userId) {
1861
1966
  const usersStore = client[kInternal2].usersStore;
1967
+ const getUserState = useCallback2(
1968
+ () => usersStore.getState(userId),
1969
+ [usersStore, userId]
1970
+ );
1971
+ const userState = getUserState();
1972
+ if (!userState || userState.isLoading) {
1973
+ throw usersStore.get(userId);
1974
+ }
1975
+ if (userState.error) {
1976
+ throw userState.error;
1977
+ }
1978
+ if (!userState.data) {
1979
+ throw missingUserError;
1980
+ }
1981
+ const state = useSyncExternalStore2(
1982
+ usersStore.subscribe,
1983
+ getUserState,
1984
+ getUserState
1985
+ );
1986
+ return {
1987
+ isLoading: false,
1988
+ user: state?.data,
1989
+ error: state?.error
1990
+ };
1991
+ }
1992
+ function useRoomInfo_withClient(client, roomId) {
1862
1993
  const roomsInfoStore = client[kInternal2].roomsInfoStore;
1863
- function useUser(userId) {
1864
- const getUserState = useCallback2(
1865
- () => usersStore.getState(userId),
1866
- [userId]
1867
- );
1868
- useEffect4(() => {
1869
- void usersStore.get(userId);
1870
- }, [userId]);
1871
- const state = useSyncExternalStore2(
1872
- usersStore.subscribe,
1873
- getUserState,
1874
- getUserState
1875
- );
1876
- return state ? {
1877
- isLoading: state.isLoading,
1878
- user: state.data,
1879
- // Return an error if `undefined` was returned by `resolveUsers` for this user ID
1880
- error: !state.isLoading && !state.data && !state.error ? missingUserError : state.error
1881
- } : { isLoading: true };
1994
+ const getRoomInfoState = useCallback2(
1995
+ () => roomsInfoStore.getState(roomId),
1996
+ [roomsInfoStore, roomId]
1997
+ );
1998
+ useEffect5(() => {
1999
+ void roomsInfoStore.get(roomId);
2000
+ }, [roomsInfoStore, roomId]);
2001
+ const state = useSyncExternalStore2(
2002
+ roomsInfoStore.subscribe,
2003
+ getRoomInfoState,
2004
+ getRoomInfoState
2005
+ );
2006
+ return state ? {
2007
+ isLoading: state.isLoading,
2008
+ info: state.data,
2009
+ // Return an error if `undefined` was returned by `resolveRoomsInfo` for this room ID
2010
+ error: !state.isLoading && !state.data && !state.error ? missingRoomInfoError : state.error
2011
+ } : { isLoading: true };
2012
+ }
2013
+ function useRoomInfoSuspense_withClient(client, roomId) {
2014
+ const roomsInfoStore = client[kInternal2].roomsInfoStore;
2015
+ const getRoomInfoState = useCallback2(
2016
+ () => roomsInfoStore.getState(roomId),
2017
+ [roomsInfoStore, roomId]
2018
+ );
2019
+ const roomInfoState = getRoomInfoState();
2020
+ if (!roomInfoState || roomInfoState.isLoading) {
2021
+ throw roomsInfoStore.get(roomId);
1882
2022
  }
1883
- function useUserSuspense(userId) {
1884
- const getUserState = useCallback2(
1885
- () => usersStore.getState(userId),
1886
- [userId]
1887
- );
1888
- const userState = getUserState();
1889
- if (!userState || userState.isLoading) {
1890
- throw usersStore.get(userId);
1891
- }
1892
- if (userState.error) {
1893
- throw userState.error;
1894
- }
1895
- if (!userState.data) {
1896
- throw missingUserError;
2023
+ if (roomInfoState.error) {
2024
+ throw roomInfoState.error;
2025
+ }
2026
+ if (!roomInfoState.data) {
2027
+ throw missingRoomInfoError;
2028
+ }
2029
+ const state = useSyncExternalStore2(
2030
+ roomsInfoStore.subscribe,
2031
+ getRoomInfoState,
2032
+ getRoomInfoState
2033
+ );
2034
+ return {
2035
+ isLoading: false,
2036
+ info: state?.data,
2037
+ error: state?.error
2038
+ };
2039
+ }
2040
+ function createSharedContext(client) {
2041
+ return {
2042
+ classic: {
2043
+ useUser: (userId) => useUser_withClient(client, userId),
2044
+ useRoomInfo: (roomId) => useRoomInfo_withClient(client, roomId)
2045
+ },
2046
+ suspense: {
2047
+ useUser: (userId) => useUserSuspense_withClient(client, userId),
2048
+ useRoomInfo: (roomId) => useRoomInfoSuspense_withClient(client, roomId)
1897
2049
  }
1898
- const state = useSyncExternalStore2(
1899
- usersStore.subscribe,
1900
- getUserState,
1901
- getUserState
1902
- );
2050
+ };
2051
+ }
2052
+
2053
+ // src/liveblocks.tsx
2054
+ var ClientContext = createContext2(null);
2055
+ var _extras2 = /* @__PURE__ */ new WeakMap();
2056
+ var _bundles2 = /* @__PURE__ */ new WeakMap();
2057
+ var POLLING_INTERVAL2 = 5 * 1e3;
2058
+ var INBOX_NOTIFICATIONS_QUERY = "INBOX_NOTIFICATIONS";
2059
+ function selectorFor_useInboxNotifications(client, state) {
2060
+ const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2061
+ if (query === void 0 || query.isLoading) {
1903
2062
  return {
1904
- isLoading: false,
1905
- user: state?.data,
1906
- error: state?.error
2063
+ isLoading: true
1907
2064
  };
1908
2065
  }
1909
- function useRoomInfo(roomId) {
1910
- const getRoomInfoState = useCallback2(
1911
- () => roomsInfoStore.getState(roomId),
1912
- [roomId]
1913
- );
1914
- useEffect4(() => {
1915
- void roomsInfoStore.get(roomId);
1916
- }, [roomId]);
1917
- const state = useSyncExternalStore2(
1918
- roomsInfoStore.subscribe,
1919
- getRoomInfoState,
1920
- getRoomInfoState
1921
- );
1922
- return state ? {
1923
- isLoading: state.isLoading,
1924
- info: state.data,
1925
- // Return an error if `undefined` was returned by `resolveRoomsInfo` for this room ID
1926
- error: !state.isLoading && !state.data && !state.error ? missingRoomInfoError : state.error
1927
- } : { isLoading: true };
2066
+ if (query.error !== void 0) {
2067
+ return {
2068
+ error: query.error,
2069
+ isLoading: false
2070
+ };
1928
2071
  }
1929
- function useRoomInfoSuspense(roomId) {
1930
- const getRoomInfoState = useCallback2(
1931
- () => roomsInfoStore.getState(roomId),
1932
- [roomId]
1933
- );
1934
- const roomInfoState = getRoomInfoState();
1935
- if (!roomInfoState || roomInfoState.isLoading) {
1936
- throw roomsInfoStore.get(roomId);
1937
- }
1938
- if (roomInfoState.error) {
1939
- throw roomInfoState.error;
1940
- }
1941
- if (!roomInfoState.data) {
1942
- throw missingRoomInfoError;
2072
+ return {
2073
+ inboxNotifications: client[kInternal3].comments.selectedInboxNotifications(state),
2074
+ isLoading: false
2075
+ };
2076
+ }
2077
+ function selectorFor_useInboxNotificationsSuspense(client, state) {
2078
+ return {
2079
+ inboxNotifications: client[kInternal3].comments.selectedInboxNotifications(state),
2080
+ isLoading: false
2081
+ };
2082
+ }
2083
+ function selectUnreadInboxNotificationsCount(client, state) {
2084
+ let count = 0;
2085
+ for (const notification of client[kInternal3].comments.selectedInboxNotifications(state)) {
2086
+ if (notification.readAt === null || notification.readAt < notification.notifiedAt) {
2087
+ count++;
1943
2088
  }
1944
- const state = useSyncExternalStore2(
1945
- roomsInfoStore.subscribe,
1946
- getRoomInfoState,
1947
- getRoomInfoState
1948
- );
2089
+ }
2090
+ return count;
2091
+ }
2092
+ function selectorFor_useUnreadInboxNotificationsCount(client, state) {
2093
+ const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2094
+ if (query === void 0 || query.isLoading) {
1949
2095
  return {
1950
- isLoading: false,
1951
- info: state?.data,
1952
- error: state?.error
2096
+ isLoading: true
1953
2097
  };
1954
2098
  }
1955
- const bundle = {
1956
- useUser,
1957
- useRoomInfo,
1958
- suspense: {
1959
- useUser: useUserSuspense,
1960
- useRoomInfo: useRoomInfoSuspense
1961
- }
2099
+ if (query.error !== void 0) {
2100
+ return {
2101
+ error: query.error,
2102
+ isLoading: false
2103
+ };
2104
+ }
2105
+ return {
2106
+ isLoading: false,
2107
+ count: selectUnreadInboxNotificationsCount(client, state)
1962
2108
  };
1963
- return bundle;
1964
2109
  }
1965
-
1966
- // src/liveblocks.tsx
1967
- var ContextBundle2 = createContext2(null);
1968
- function useLiveblocksContextBundle() {
1969
- const bundle = useContext3(ContextBundle2);
1970
- if (bundle === null) {
1971
- throw new Error("LiveblocksProvider is missing from the React tree.");
2110
+ function selectorFor_useUnreadInboxNotificationsCountSuspense(client, state) {
2111
+ return {
2112
+ isLoading: false,
2113
+ count: selectUnreadInboxNotificationsCount(client, state)
2114
+ };
2115
+ }
2116
+ function getOrCreateContextBundle(client) {
2117
+ let bundle = _bundles2.get(client);
2118
+ if (!bundle) {
2119
+ bundle = makeLiveblocksContextBundle(client);
2120
+ _bundles2.set(client, bundle);
1972
2121
  }
1973
2122
  return bundle;
1974
2123
  }
1975
- var POLLING_INTERVAL2 = 60 * 1e3;
1976
- var INBOX_NOTIFICATIONS_QUERY = "INBOX_NOTIFICATIONS";
1977
- function createLiveblocksContext(client) {
1978
- const shared = createSharedContext(client);
2124
+ function getExtrasForClient2(client) {
2125
+ let extras = _extras2.get(client);
2126
+ if (!extras) {
2127
+ extras = makeExtrasForClient2(client);
2128
+ _extras2.set(client, extras);
2129
+ }
2130
+ return extras;
2131
+ }
2132
+ function makeExtrasForClient2(client) {
1979
2133
  const store = client[kInternal3].cacheStore;
1980
2134
  const notifications = client[kInternal3].notifications;
1981
- function LiveblocksProvider(props) {
1982
- return /* @__PURE__ */ React3.createElement(
1983
- ContextBundle2.Provider,
1984
- {
1985
- value: bundle
1986
- },
1987
- props.children
1988
- );
1989
- }
1990
2135
  let fetchInboxNotificationsRequest = null;
1991
- let inboxNotificationsSubscribers = 0;
1992
2136
  let lastRequestedAt;
1993
- const poller = makePoller2(refreshThreadsAndNotifications);
1994
- function refreshThreadsAndNotifications() {
1995
- return notifications.getInboxNotifications({ since: lastRequestedAt }).then(
2137
+ const poller = makePoller2(
2138
+ () => notifications.getInboxNotifications({ since: lastRequestedAt }).then(
1996
2139
  (result) => {
1997
2140
  lastRequestedAt = result.meta.requestedAt;
1998
2141
  store.updateThreadsAndNotifications(
@@ -2005,24 +2148,8 @@ function createLiveblocksContext(client) {
2005
2148
  },
2006
2149
  () => {
2007
2150
  }
2008
- );
2009
- }
2010
- function incrementInboxNotificationsSubscribers() {
2011
- inboxNotificationsSubscribers++;
2012
- poller.start(POLLING_INTERVAL2);
2013
- }
2014
- function decrementInboxNotificationsSubscribers() {
2015
- if (inboxNotificationsSubscribers <= 0) {
2016
- console.warn(
2017
- `Internal unexpected behavior. Cannot decrease subscriber count for query "${INBOX_NOTIFICATIONS_QUERY}"`
2018
- );
2019
- return;
2020
- }
2021
- inboxNotificationsSubscribers--;
2022
- if (inboxNotificationsSubscribers <= 0) {
2023
- poller.stop();
2024
- }
2025
- }
2151
+ )
2152
+ );
2026
2153
  async function fetchInboxNotifications({ retryCount } = { retryCount: 0 }) {
2027
2154
  if (fetchInboxNotificationsRequest !== null) {
2028
2155
  return fetchInboxNotificationsRequest;
@@ -2058,132 +2185,133 @@ function createLiveblocksContext(client) {
2058
2185
  }
2059
2186
  return;
2060
2187
  }
2061
- function useInboxNotificationsSelectorCallback(state) {
2062
- const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2063
- if (query === void 0 || query.isLoading) {
2064
- return {
2065
- isLoading: true
2066
- };
2067
- }
2068
- if (query.error !== void 0) {
2069
- return {
2070
- error: query.error,
2071
- isLoading: false
2072
- };
2073
- }
2074
- return {
2075
- inboxNotifications: selectedInboxNotifications(state),
2076
- isLoading: false
2077
- };
2078
- }
2079
- function useInboxNotifications() {
2080
- useEffect5(() => {
2081
- void fetchInboxNotifications();
2082
- incrementInboxNotificationsSubscribers();
2083
- return () => decrementInboxNotificationsSubscribers();
2084
- }, []);
2085
- const result = useSyncExternalStoreWithSelector2(
2086
- store.subscribe,
2087
- store.get,
2088
- store.get,
2089
- useInboxNotificationsSelectorCallback
2090
- );
2091
- return result;
2092
- }
2093
- function useInboxNotificationsSuspenseSelector(state) {
2094
- return {
2095
- inboxNotifications: selectedInboxNotifications(state),
2096
- isLoading: false
2097
- };
2098
- }
2099
- function useInboxNotificationsSuspense() {
2100
- const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2101
- if (query === void 0 || query.isLoading) {
2102
- throw fetchInboxNotifications();
2103
- }
2104
- if (query.error !== void 0) {
2105
- throw query.error;
2106
- }
2107
- React3.useEffect(() => {
2108
- incrementInboxNotificationsSubscribers();
2109
- return () => {
2110
- decrementInboxNotificationsSubscribers();
2111
- };
2112
- }, []);
2113
- return useSyncExternalStoreWithSelector2(
2114
- store.subscribe,
2115
- store.get,
2116
- store.get,
2117
- useInboxNotificationsSuspenseSelector
2118
- );
2119
- }
2120
- function selectUnreadInboxNotificationsCount(state) {
2121
- let count = 0;
2122
- for (const notification of selectedInboxNotifications(state)) {
2123
- if (notification.readAt === null || notification.readAt < notification.notifiedAt) {
2124
- count++;
2188
+ let inboxNotificationsSubscribers = 0;
2189
+ function useSubscribeToInboxNotificationsEffect(options) {
2190
+ const autoFetch = useInitial(options?.autoFetch ?? true);
2191
+ useEffect6(() => {
2192
+ if (autoFetch) {
2193
+ void fetchInboxNotifications();
2125
2194
  }
2126
- }
2127
- return count;
2128
- }
2129
- function useUnreadInboxNotificationsCountSelector(state) {
2130
- const query = state.queries[INBOX_NOTIFICATIONS_QUERY];
2131
- if (query === void 0 || query.isLoading) {
2132
- return {
2133
- isLoading: true
2134
- };
2135
- }
2136
- if (query.error !== void 0) {
2137
- return {
2138
- error: query.error,
2139
- isLoading: false
2195
+ inboxNotificationsSubscribers++;
2196
+ poller.start(POLLING_INTERVAL2);
2197
+ return () => {
2198
+ if (inboxNotificationsSubscribers <= 0) {
2199
+ console.warn(
2200
+ `Internal unexpected behavior. Cannot decrease subscriber count for query "${INBOX_NOTIFICATIONS_QUERY}"`
2201
+ );
2202
+ return;
2203
+ }
2204
+ inboxNotificationsSubscribers--;
2205
+ if (inboxNotificationsSubscribers <= 0) {
2206
+ poller.stop();
2207
+ }
2140
2208
  };
2141
- }
2142
- return {
2143
- isLoading: false,
2144
- count: selectUnreadInboxNotificationsCount(state)
2145
- };
2146
- }
2147
- function useUnreadInboxNotificationsCount() {
2148
- useEffect5(() => {
2149
- void fetchInboxNotifications();
2150
- incrementInboxNotificationsSubscribers();
2151
- return () => decrementInboxNotificationsSubscribers();
2152
- }, []);
2153
- return useSyncExternalStoreWithSelector2(
2154
- store.subscribe,
2155
- store.get,
2156
- store.get,
2157
- useUnreadInboxNotificationsCountSelector
2158
- );
2209
+ }, [autoFetch]);
2159
2210
  }
2160
- function useUnreadInboxNotificationsCountSuspenseSelector(state) {
2161
- return {
2162
- isLoading: false,
2163
- count: selectUnreadInboxNotificationsCount(state)
2164
- };
2211
+ return {
2212
+ store,
2213
+ notifications,
2214
+ fetchInboxNotifications,
2215
+ useSubscribeToInboxNotificationsEffect
2216
+ };
2217
+ }
2218
+ function makeLiveblocksContextBundle(client) {
2219
+ const useInboxNotificationThread = (inboxNotificationId) => useInboxNotificationThread_withClient(client, inboxNotificationId);
2220
+ const useMarkInboxNotificationAsRead = () => useMarkInboxNotificationAsRead_withClient(client);
2221
+ const useMarkAllInboxNotificationsAsRead = () => useMarkAllInboxNotificationsAsRead_withClient(client);
2222
+ function LiveblocksProvider2(props) {
2223
+ return /* @__PURE__ */ React4.createElement(ClientContext.Provider, { value: client }, props.children);
2165
2224
  }
2166
- function useUnreadInboxNotificationsCountSuspense() {
2167
- const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2168
- if (query === void 0 || query.isLoading) {
2169
- throw fetchInboxNotifications();
2225
+ const shared = createSharedContext(client);
2226
+ const bundle = {
2227
+ LiveblocksProvider: LiveblocksProvider2,
2228
+ useInboxNotifications: () => useInboxNotifications_withClient(client),
2229
+ useUnreadInboxNotificationsCount: () => useUnreadInboxNotificationsCount_withClient(client),
2230
+ useMarkInboxNotificationAsRead,
2231
+ useMarkAllInboxNotificationsAsRead,
2232
+ useInboxNotificationThread,
2233
+ ...shared.classic,
2234
+ suspense: {
2235
+ LiveblocksProvider: LiveblocksProvider2,
2236
+ useInboxNotifications: () => useInboxNotificationsSuspense_withClient(client),
2237
+ useUnreadInboxNotificationsCount: () => useUnreadInboxNotificationsCountSuspense_withClient(client),
2238
+ useMarkInboxNotificationAsRead,
2239
+ useMarkAllInboxNotificationsAsRead,
2240
+ useInboxNotificationThread,
2241
+ ...shared.suspense
2242
+ },
2243
+ [kInternal3]: {
2244
+ useCurrentUserId: () => useCurrentUserId_withClient(client)
2170
2245
  }
2171
- React3.useEffect(() => {
2172
- incrementInboxNotificationsSubscribers();
2173
- return () => {
2174
- decrementInboxNotificationsSubscribers();
2175
- };
2176
- }, []);
2177
- return useSyncExternalStoreWithSelector2(
2178
- store.subscribe,
2179
- store.get,
2180
- store.get,
2181
- useUnreadInboxNotificationsCountSuspenseSelector
2182
- );
2183
- }
2184
- function useMarkInboxNotificationAsRead() {
2185
- return useCallback3((inboxNotificationId) => {
2186
- const optimisticUpdateId = nanoid3();
2246
+ };
2247
+ return Object.defineProperty(bundle, kInternal3, {
2248
+ enumerable: false
2249
+ });
2250
+ }
2251
+ function useInboxNotifications_withClient(client) {
2252
+ const { store, useSubscribeToInboxNotificationsEffect } = getExtrasForClient2(client);
2253
+ useSubscribeToInboxNotificationsEffect();
2254
+ return useSyncExternalStoreWithSelector2(
2255
+ store.subscribe,
2256
+ store.get,
2257
+ store.get,
2258
+ () => selectorFor_useInboxNotifications(client, store.get())
2259
+ );
2260
+ }
2261
+ function useInboxNotificationsSuspense_withClient(client) {
2262
+ const {
2263
+ store,
2264
+ fetchInboxNotifications,
2265
+ useSubscribeToInboxNotificationsEffect
2266
+ } = getExtrasForClient2(client);
2267
+ const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2268
+ if (query === void 0 || query.isLoading) {
2269
+ throw fetchInboxNotifications();
2270
+ }
2271
+ if (query.error !== void 0) {
2272
+ throw query.error;
2273
+ }
2274
+ useSubscribeToInboxNotificationsEffect({ autoFetch: false });
2275
+ return useSyncExternalStoreWithSelector2(
2276
+ store.subscribe,
2277
+ store.get,
2278
+ store.get,
2279
+ () => selectorFor_useInboxNotificationsSuspense(client, store.get())
2280
+ );
2281
+ }
2282
+ function useUnreadInboxNotificationsCount_withClient(client) {
2283
+ const { store, useSubscribeToInboxNotificationsEffect } = getExtrasForClient2(client);
2284
+ useSubscribeToInboxNotificationsEffect();
2285
+ return useSyncExternalStoreWithSelector2(
2286
+ store.subscribe,
2287
+ store.get,
2288
+ store.get,
2289
+ () => selectorFor_useUnreadInboxNotificationsCount(client, store.get())
2290
+ );
2291
+ }
2292
+ function useUnreadInboxNotificationsCountSuspense_withClient(client) {
2293
+ const {
2294
+ store,
2295
+ fetchInboxNotifications,
2296
+ useSubscribeToInboxNotificationsEffect
2297
+ } = getExtrasForClient2(client);
2298
+ const query = store.get().queries[INBOX_NOTIFICATIONS_QUERY];
2299
+ if (query === void 0 || query.isLoading) {
2300
+ throw fetchInboxNotifications();
2301
+ }
2302
+ useSubscribeToInboxNotificationsEffect({ autoFetch: false });
2303
+ return useSyncExternalStoreWithSelector2(
2304
+ store.subscribe,
2305
+ store.get,
2306
+ store.get,
2307
+ () => selectorFor_useUnreadInboxNotificationsCountSuspense(client, store.get())
2308
+ );
2309
+ }
2310
+ function useMarkInboxNotificationAsRead_withClient(client) {
2311
+ return useCallback3(
2312
+ (inboxNotificationId) => {
2313
+ const { store, notifications } = getExtrasForClient2(client);
2314
+ const optimisticUpdateId = nanoid2();
2187
2315
  const readAt = /* @__PURE__ */ new Date();
2188
2316
  store.pushOptimisticUpdate({
2189
2317
  type: "mark-inbox-notification-as-read",
@@ -2227,96 +2355,99 @@ function createLiveblocksContext(client) {
2227
2355
  }));
2228
2356
  }
2229
2357
  );
2230
- }, []);
2231
- }
2232
- function useMarkAllInboxNotificationsAsRead() {
2233
- return useCallback3(() => {
2234
- const optimisticUpdateId = nanoid3();
2235
- const readAt = /* @__PURE__ */ new Date();
2236
- store.pushOptimisticUpdate({
2237
- type: "mark-inbox-notifications-as-read",
2238
- id: optimisticUpdateId,
2239
- readAt
2240
- });
2241
- notifications.markAllInboxNotificationsAsRead().then(
2242
- () => {
2243
- store.set((state) => ({
2244
- ...state,
2245
- inboxNotifications: Object.fromEntries(
2246
- Array.from(Object.entries(state.inboxNotifications)).map(
2247
- ([id, inboxNotification]) => [
2248
- id,
2249
- { ...inboxNotification, readAt }
2250
- ]
2251
- )
2252
- ),
2253
- optimisticUpdates: state.optimisticUpdates.filter(
2254
- (update) => update.id !== optimisticUpdateId
2255
- )
2256
- }));
2257
- },
2258
- () => {
2259
- store.set((state) => ({
2260
- ...state,
2261
- optimisticUpdates: state.optimisticUpdates.filter(
2262
- (update) => update.id !== optimisticUpdateId
2358
+ },
2359
+ [client]
2360
+ );
2361
+ }
2362
+ function useMarkAllInboxNotificationsAsRead_withClient(client) {
2363
+ return useCallback3(() => {
2364
+ const { store, notifications } = getExtrasForClient2(client);
2365
+ const optimisticUpdateId = nanoid2();
2366
+ const readAt = /* @__PURE__ */ new Date();
2367
+ store.pushOptimisticUpdate({
2368
+ type: "mark-inbox-notifications-as-read",
2369
+ id: optimisticUpdateId,
2370
+ readAt
2371
+ });
2372
+ notifications.markAllInboxNotificationsAsRead().then(
2373
+ () => {
2374
+ store.set((state) => ({
2375
+ ...state,
2376
+ inboxNotifications: Object.fromEntries(
2377
+ Array.from(Object.entries(state.inboxNotifications)).map(
2378
+ ([id, inboxNotification]) => [
2379
+ id,
2380
+ { ...inboxNotification, readAt }
2381
+ ]
2263
2382
  )
2264
- }));
2265
- }
2266
- );
2267
- }, []);
2268
- }
2269
- function useThreadFromCache(threadId) {
2270
- const selector = useCallback3(
2271
- (state) => {
2272
- const thread = state.threads[threadId];
2273
- if (thread === void 0) {
2274
- throw new Error(
2275
- `Internal error: thread with id "${threadId}" not found in cache`
2276
- );
2277
- }
2278
- return thread;
2383
+ ),
2384
+ optimisticUpdates: state.optimisticUpdates.filter(
2385
+ (update) => update.id !== optimisticUpdateId
2386
+ )
2387
+ }));
2279
2388
  },
2280
- [threadId]
2281
- );
2282
- return useSyncExternalStoreWithSelector2(
2283
- store.subscribe,
2284
- store.get,
2285
- store.get,
2286
- selector
2287
- );
2288
- }
2289
- const currentUserIdStore = client[kInternal3].currentUserIdStore;
2290
- function useCurrentUserId() {
2291
- return useSyncExternalStore3(
2292
- currentUserIdStore.subscribe,
2293
- currentUserIdStore.get,
2294
- currentUserIdStore.get
2389
+ () => {
2390
+ store.set((state) => ({
2391
+ ...state,
2392
+ optimisticUpdates: state.optimisticUpdates.filter(
2393
+ (update) => update.id !== optimisticUpdateId
2394
+ )
2395
+ }));
2396
+ }
2295
2397
  );
2296
- }
2297
- const bundle = {
2298
- LiveblocksProvider,
2299
- useInboxNotifications,
2300
- useUnreadInboxNotificationsCount,
2301
- useMarkInboxNotificationAsRead,
2302
- useMarkAllInboxNotificationsAsRead,
2303
- ...shared,
2304
- suspense: {
2305
- LiveblocksProvider,
2306
- useInboxNotifications: useInboxNotificationsSuspense,
2307
- useUnreadInboxNotificationsCount: useUnreadInboxNotificationsCountSuspense,
2308
- useMarkInboxNotificationAsRead,
2309
- useMarkAllInboxNotificationsAsRead,
2310
- ...shared.suspense
2398
+ }, [client]);
2399
+ }
2400
+ function useInboxNotificationThread_withClient(client, inboxNotificationId) {
2401
+ const { store } = getExtrasForClient2(client);
2402
+ const selector = useCallback3(
2403
+ (state) => {
2404
+ const inboxNotification = state.inboxNotifications[inboxNotificationId] ?? raise2(`Inbox notification with ID "${inboxNotificationId}" not found`);
2405
+ if (inboxNotification.kind !== "thread") {
2406
+ raise2(
2407
+ `Inbox notification with ID "${inboxNotificationId}" is not of kind "thread"`
2408
+ );
2409
+ }
2410
+ const thread = state.threads[inboxNotification.threadId] ?? raise2(
2411
+ `Thread with ID "${inboxNotification.threadId}" not found, this inbox notification might not be of kind "thread"`
2412
+ );
2413
+ return thread;
2311
2414
  },
2312
- [kInternal3]: {
2313
- useThreadFromCache,
2314
- useCurrentUserId
2315
- }
2316
- };
2317
- return Object.defineProperty(bundle, kInternal3, {
2318
- enumerable: false
2319
- });
2415
+ [inboxNotificationId]
2416
+ );
2417
+ return useSyncExternalStoreWithSelector2(
2418
+ store.subscribe,
2419
+ store.get,
2420
+ store.get,
2421
+ selector
2422
+ );
2423
+ }
2424
+ function useCurrentUserId_withClient(client) {
2425
+ const currentUserIdStore = client[kInternal3].currentUserIdStore;
2426
+ return useSyncExternalStore3(
2427
+ currentUserIdStore.subscribe,
2428
+ currentUserIdStore.get,
2429
+ currentUserIdStore.get
2430
+ );
2431
+ }
2432
+ function useClientOrNull() {
2433
+ return useContext2(ClientContext);
2434
+ }
2435
+ function useClient() {
2436
+ return useClientOrNull() ?? raise2("LiveblocksProvider is missing from the React tree.");
2437
+ }
2438
+ function LiveblocksProvider(props) {
2439
+ return /* @__PURE__ */ React4.createElement(ClientContext.Provider, { value: props.client }, props.children);
2440
+ }
2441
+ function useLiveblocksContextBundleOrNull() {
2442
+ const client = useClientOrNull();
2443
+ return client !== null ? getOrCreateContextBundle(client) : null;
2444
+ }
2445
+ function useLiveblocksContextBundle() {
2446
+ const client = useClient();
2447
+ return getOrCreateContextBundle(client);
2448
+ }
2449
+ function createLiveblocksContext(client) {
2450
+ return getOrCreateContextBundle(client);
2320
2451
  }
2321
2452
 
2322
2453
  // src/index.ts
@@ -2324,10 +2455,14 @@ import { shallow as shallow2 } from "@liveblocks/client";
2324
2455
  detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
2325
2456
  export {
2326
2457
  ClientSideSuspense,
2458
+ CreateThreadError,
2459
+ DeleteCommentError,
2327
2460
  createLiveblocksContext,
2328
2461
  createRoomContext,
2329
2462
  shallow2 as shallow,
2463
+ useClient,
2330
2464
  useLiveblocksContextBundle,
2465
+ useLiveblocksContextBundleOrNull,
2331
2466
  useRoomContextBundle,
2332
2467
  useSharedContextBundle
2333
2468
  };