@liveblocks/react 1.12.0 → 2.0.0-alpha1

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