@liveblocks/react 1.19.0-test1 → 2.0.0-alpha2

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