@liveblocks/react 1.2.4 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1018 -401
- package/dist/index.d.ts +1018 -401
- package/dist/index.js +686 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +688 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -7
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
// src/index.ts
|
|
2
4
|
import { detectDupes } from "@liveblocks/core";
|
|
3
5
|
|
|
4
6
|
// src/version.ts
|
|
5
7
|
var PKG_NAME = "@liveblocks/react";
|
|
6
|
-
var PKG_VERSION = "1.
|
|
8
|
+
var PKG_VERSION = "1.3.1";
|
|
7
9
|
var PKG_FORMAT = "esm";
|
|
8
10
|
|
|
9
11
|
// src/ClientSideSuspense.tsx
|
|
@@ -19,15 +21,484 @@ function ClientSideSuspense(props) {
|
|
|
19
21
|
// src/factory.tsx
|
|
20
22
|
import { shallow } from "@liveblocks/client";
|
|
21
23
|
import {
|
|
22
|
-
|
|
24
|
+
createAsyncCache,
|
|
23
25
|
deprecateIf,
|
|
24
|
-
errorIf
|
|
26
|
+
errorIf,
|
|
27
|
+
makeEventSource as makeEventSource2
|
|
25
28
|
} from "@liveblocks/core";
|
|
26
29
|
import * as React2 from "react";
|
|
27
30
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
28
31
|
|
|
29
|
-
// src/
|
|
30
|
-
import {
|
|
32
|
+
// src/comments/CommentsRoom.ts
|
|
33
|
+
import { makePoller } from "@liveblocks/core";
|
|
34
|
+
import { nanoid } from "nanoid";
|
|
35
|
+
import { useEffect as useEffect2 } from "react";
|
|
36
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
37
|
+
|
|
38
|
+
// src/comments/errors.ts
|
|
39
|
+
var CreateThreadError = class extends Error {
|
|
40
|
+
constructor(cause, context) {
|
|
41
|
+
super("Create thread failed.");
|
|
42
|
+
this.cause = cause;
|
|
43
|
+
this.context = context;
|
|
44
|
+
this.name = "CreateThreadError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var EditThreadMetadataError = class extends Error {
|
|
48
|
+
constructor(cause, context) {
|
|
49
|
+
super("Edit thread metadata failed.");
|
|
50
|
+
this.cause = cause;
|
|
51
|
+
this.context = context;
|
|
52
|
+
this.name = "EditThreadMetadataError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var CreateCommentError = class extends Error {
|
|
56
|
+
constructor(cause, context) {
|
|
57
|
+
super("Create comment failed.");
|
|
58
|
+
this.cause = cause;
|
|
59
|
+
this.context = context;
|
|
60
|
+
this.name = "CreateCommentError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var EditCommentError = class extends Error {
|
|
64
|
+
constructor(cause, context) {
|
|
65
|
+
super("Edit comment failed.");
|
|
66
|
+
this.cause = cause;
|
|
67
|
+
this.context = context;
|
|
68
|
+
this.name = "EditCommentError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var DeleteCommentError = class extends Error {
|
|
72
|
+
constructor(cause, context) {
|
|
73
|
+
super("Delete comment failed.");
|
|
74
|
+
this.cause = cause;
|
|
75
|
+
this.context = context;
|
|
76
|
+
this.name = "DeleteCommentError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/comments/lib/store.ts
|
|
81
|
+
import { makeEventSource } from "@liveblocks/core";
|
|
82
|
+
function createStore(initialState) {
|
|
83
|
+
let state = initialState;
|
|
84
|
+
const eventSource = makeEventSource();
|
|
85
|
+
return {
|
|
86
|
+
get() {
|
|
87
|
+
return state;
|
|
88
|
+
},
|
|
89
|
+
set(newState) {
|
|
90
|
+
state = newState;
|
|
91
|
+
eventSource.notify(state);
|
|
92
|
+
},
|
|
93
|
+
subscribe(callback) {
|
|
94
|
+
return eventSource.subscribe(callback);
|
|
95
|
+
},
|
|
96
|
+
subscribeOnce(callback) {
|
|
97
|
+
return eventSource.subscribeOnce(callback);
|
|
98
|
+
},
|
|
99
|
+
subscribersCount() {
|
|
100
|
+
return eventSource.count();
|
|
101
|
+
},
|
|
102
|
+
destroy() {
|
|
103
|
+
return eventSource.clear();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/comments/CommentsRoom.ts
|
|
109
|
+
var POLLING_INTERVAL_REALTIME = 3e4;
|
|
110
|
+
var POLLING_INTERVAL = 5e3;
|
|
111
|
+
var THREAD_ID_PREFIX = "th";
|
|
112
|
+
var COMMENT_ID_PREFIX = "cm";
|
|
113
|
+
function createOptimisticId(prefix) {
|
|
114
|
+
return `${prefix}_${nanoid()}`;
|
|
115
|
+
}
|
|
116
|
+
function createCommentsRoom(room, errorEventSource) {
|
|
117
|
+
const store = createStore({
|
|
118
|
+
isLoading: true
|
|
119
|
+
});
|
|
120
|
+
let fetchThreadsPromise = null;
|
|
121
|
+
let numberOfMutations = 0;
|
|
122
|
+
function endMutation() {
|
|
123
|
+
numberOfMutations--;
|
|
124
|
+
if (numberOfMutations === 0) {
|
|
125
|
+
void revalidateThreads();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function startMutation() {
|
|
129
|
+
pollingHub.threads.stop();
|
|
130
|
+
numberOfMutations++;
|
|
131
|
+
}
|
|
132
|
+
const pollingHub = {
|
|
133
|
+
// TODO: If there's an error, it will currently infinitely retry at the current polling rate → add retry logic
|
|
134
|
+
threads: makePoller(revalidateThreads)
|
|
135
|
+
};
|
|
136
|
+
let unsubscribeRealtimeEvents;
|
|
137
|
+
let unsubscribeRealtimeConnection;
|
|
138
|
+
let realtimeClientConnected = false;
|
|
139
|
+
function getPollingInterval() {
|
|
140
|
+
return realtimeClientConnected ? POLLING_INTERVAL_REALTIME : POLLING_INTERVAL;
|
|
141
|
+
}
|
|
142
|
+
function ensureThreadsAreLoadedForMutations() {
|
|
143
|
+
const state = store.get();
|
|
144
|
+
if (state.isLoading || state.error) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
"Cannot update threads or comments before they are loaded"
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return state.threads;
|
|
150
|
+
}
|
|
151
|
+
async function revalidateThreads() {
|
|
152
|
+
pollingHub.threads.pause();
|
|
153
|
+
if (numberOfMutations === 0) {
|
|
154
|
+
if (fetchThreadsPromise === null) {
|
|
155
|
+
fetchThreadsPromise = room.getThreads();
|
|
156
|
+
}
|
|
157
|
+
setThreads(await fetchThreadsPromise);
|
|
158
|
+
fetchThreadsPromise = null;
|
|
159
|
+
}
|
|
160
|
+
pollingHub.threads.resume();
|
|
161
|
+
}
|
|
162
|
+
function subscribe() {
|
|
163
|
+
if (!unsubscribeRealtimeEvents) {
|
|
164
|
+
unsubscribeRealtimeEvents = room.events.comments.subscribe(() => {
|
|
165
|
+
pollingHub.threads.restart(getPollingInterval());
|
|
166
|
+
void revalidateThreads();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (!unsubscribeRealtimeConnection) {
|
|
170
|
+
unsubscribeRealtimeConnection = room.events.status.subscribe((status) => {
|
|
171
|
+
const nextRealtimeClientConnected = status === "connected";
|
|
172
|
+
if (nextRealtimeClientConnected !== realtimeClientConnected) {
|
|
173
|
+
realtimeClientConnected = nextRealtimeClientConnected;
|
|
174
|
+
pollingHub.threads.restart(getPollingInterval());
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
pollingHub.threads.start(getPollingInterval());
|
|
179
|
+
return () => {
|
|
180
|
+
if (store.subscribersCount() > 1) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
pollingHub.threads.stop();
|
|
184
|
+
unsubscribeRealtimeEvents?.();
|
|
185
|
+
unsubscribeRealtimeEvents = void 0;
|
|
186
|
+
unsubscribeRealtimeConnection?.();
|
|
187
|
+
unsubscribeRealtimeConnection = void 0;
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function setThreads(newThreads) {
|
|
191
|
+
store.set({
|
|
192
|
+
threads: newThreads,
|
|
193
|
+
isLoading: false
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function useThreadsInternal() {
|
|
197
|
+
useEffect2(subscribe, []);
|
|
198
|
+
return useSyncExternalStore(
|
|
199
|
+
store.subscribe,
|
|
200
|
+
store.get,
|
|
201
|
+
store.get
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
function useThreads() {
|
|
205
|
+
useEffect2(() => {
|
|
206
|
+
void revalidateThreads();
|
|
207
|
+
}, []);
|
|
208
|
+
return useThreadsInternal();
|
|
209
|
+
}
|
|
210
|
+
function useThreadsSuspense() {
|
|
211
|
+
const result = useThreadsInternal();
|
|
212
|
+
if (result.isLoading) {
|
|
213
|
+
throw revalidateThreads();
|
|
214
|
+
}
|
|
215
|
+
if (result.error) {
|
|
216
|
+
throw result.error;
|
|
217
|
+
}
|
|
218
|
+
return result.threads;
|
|
219
|
+
}
|
|
220
|
+
function getCurrentUserId() {
|
|
221
|
+
const self = room.getSelf();
|
|
222
|
+
if (self === null || self.id === void 0) {
|
|
223
|
+
return "anonymous";
|
|
224
|
+
} else {
|
|
225
|
+
return self.id;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function createThread(options) {
|
|
229
|
+
const body = options.body;
|
|
230
|
+
const metadata = "metadata" in options ? options.metadata : {};
|
|
231
|
+
const threads = ensureThreadsAreLoadedForMutations();
|
|
232
|
+
const threadId = createOptimisticId(THREAD_ID_PREFIX);
|
|
233
|
+
const commentId = createOptimisticId(COMMENT_ID_PREFIX);
|
|
234
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
235
|
+
const newThread = {
|
|
236
|
+
id: threadId,
|
|
237
|
+
type: "thread",
|
|
238
|
+
createdAt: now,
|
|
239
|
+
roomId: room.id,
|
|
240
|
+
metadata,
|
|
241
|
+
comments: [
|
|
242
|
+
{
|
|
243
|
+
id: commentId,
|
|
244
|
+
createdAt: now,
|
|
245
|
+
type: "comment",
|
|
246
|
+
userId: getCurrentUserId(),
|
|
247
|
+
body
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
};
|
|
251
|
+
setThreads([...threads, newThread]);
|
|
252
|
+
startMutation();
|
|
253
|
+
room.createThread({ threadId, commentId, body, metadata }).catch(
|
|
254
|
+
(er) => errorEventSource.notify(
|
|
255
|
+
new CreateThreadError(er, {
|
|
256
|
+
roomId: room.id,
|
|
257
|
+
threadId,
|
|
258
|
+
commentId,
|
|
259
|
+
body,
|
|
260
|
+
metadata
|
|
261
|
+
})
|
|
262
|
+
)
|
|
263
|
+
).finally(endMutation);
|
|
264
|
+
return newThread;
|
|
265
|
+
}
|
|
266
|
+
function editThreadMetadata(options) {
|
|
267
|
+
const threadId = options.threadId;
|
|
268
|
+
const metadata = "metadata" in options ? options.metadata : {};
|
|
269
|
+
const threads = ensureThreadsAreLoadedForMutations();
|
|
270
|
+
setThreads(
|
|
271
|
+
threads.map(
|
|
272
|
+
(thread) => thread.id === threadId ? {
|
|
273
|
+
...thread,
|
|
274
|
+
metadata: {
|
|
275
|
+
...thread.metadata,
|
|
276
|
+
...metadata
|
|
277
|
+
}
|
|
278
|
+
} : thread
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
startMutation();
|
|
282
|
+
room.editThreadMetadata({ metadata, threadId }).catch(
|
|
283
|
+
(er) => errorEventSource.notify(
|
|
284
|
+
new EditThreadMetadataError(er, {
|
|
285
|
+
roomId: room.id,
|
|
286
|
+
threadId,
|
|
287
|
+
metadata
|
|
288
|
+
})
|
|
289
|
+
)
|
|
290
|
+
).finally(endMutation);
|
|
291
|
+
}
|
|
292
|
+
function createComment({
|
|
293
|
+
threadId,
|
|
294
|
+
body
|
|
295
|
+
}) {
|
|
296
|
+
const threads = ensureThreadsAreLoadedForMutations();
|
|
297
|
+
const commentId = createOptimisticId(COMMENT_ID_PREFIX);
|
|
298
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
|
+
const comment = {
|
|
300
|
+
id: commentId,
|
|
301
|
+
threadId,
|
|
302
|
+
roomId: room.id,
|
|
303
|
+
type: "comment",
|
|
304
|
+
createdAt: now,
|
|
305
|
+
userId: getCurrentUserId(),
|
|
306
|
+
body
|
|
307
|
+
};
|
|
308
|
+
setThreads(
|
|
309
|
+
threads.map(
|
|
310
|
+
(thread) => thread.id === threadId ? {
|
|
311
|
+
...thread,
|
|
312
|
+
comments: [...thread.comments, comment]
|
|
313
|
+
} : thread
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
startMutation();
|
|
317
|
+
room.createComment({ threadId, commentId, body }).catch(
|
|
318
|
+
(er) => errorEventSource.notify(
|
|
319
|
+
new CreateCommentError(er, {
|
|
320
|
+
roomId: room.id,
|
|
321
|
+
threadId,
|
|
322
|
+
commentId,
|
|
323
|
+
body
|
|
324
|
+
})
|
|
325
|
+
)
|
|
326
|
+
).finally(endMutation);
|
|
327
|
+
return comment;
|
|
328
|
+
}
|
|
329
|
+
function editComment({ threadId, commentId, body }) {
|
|
330
|
+
const threads = ensureThreadsAreLoadedForMutations();
|
|
331
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
332
|
+
setThreads(
|
|
333
|
+
threads.map(
|
|
334
|
+
(thread) => thread.id === threadId ? {
|
|
335
|
+
...thread,
|
|
336
|
+
comments: thread.comments.map(
|
|
337
|
+
(comment) => comment.id === commentId ? {
|
|
338
|
+
...comment,
|
|
339
|
+
editedAt: now,
|
|
340
|
+
body
|
|
341
|
+
} : comment
|
|
342
|
+
)
|
|
343
|
+
} : thread
|
|
344
|
+
)
|
|
345
|
+
);
|
|
346
|
+
startMutation();
|
|
347
|
+
room.editComment({ threadId, commentId, body }).catch(
|
|
348
|
+
(er) => errorEventSource.notify(
|
|
349
|
+
new EditCommentError(er, {
|
|
350
|
+
roomId: room.id,
|
|
351
|
+
threadId,
|
|
352
|
+
commentId,
|
|
353
|
+
body
|
|
354
|
+
})
|
|
355
|
+
)
|
|
356
|
+
).finally(endMutation);
|
|
357
|
+
}
|
|
358
|
+
function deleteComment({ threadId, commentId }) {
|
|
359
|
+
const threads = ensureThreadsAreLoadedForMutations();
|
|
360
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
361
|
+
const newThreads = [];
|
|
362
|
+
for (const thread of threads) {
|
|
363
|
+
if (thread.id === threadId) {
|
|
364
|
+
const newThread = {
|
|
365
|
+
...thread,
|
|
366
|
+
comments: thread.comments.map(
|
|
367
|
+
(comment) => comment.id === commentId ? {
|
|
368
|
+
...comment,
|
|
369
|
+
deletedAt: now,
|
|
370
|
+
body: void 0
|
|
371
|
+
} : comment
|
|
372
|
+
)
|
|
373
|
+
};
|
|
374
|
+
if (newThread.comments.some((comment) => comment.deletedAt === void 0)) {
|
|
375
|
+
newThreads.push(newThread);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
newThreads.push(thread);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
setThreads(newThreads);
|
|
382
|
+
startMutation();
|
|
383
|
+
room.deleteComment({ threadId, commentId }).catch(
|
|
384
|
+
(er) => errorEventSource.notify(
|
|
385
|
+
new DeleteCommentError(er, {
|
|
386
|
+
roomId: room.id,
|
|
387
|
+
threadId,
|
|
388
|
+
commentId
|
|
389
|
+
})
|
|
390
|
+
)
|
|
391
|
+
).finally(endMutation);
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
useThreads,
|
|
395
|
+
useThreadsSuspense,
|
|
396
|
+
createThread,
|
|
397
|
+
editThreadMetadata,
|
|
398
|
+
createComment,
|
|
399
|
+
editComment,
|
|
400
|
+
deleteComment
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/comments/lib/use-debounce.ts
|
|
405
|
+
import { useEffect as useEffect3, useRef, useState as useState2 } from "react";
|
|
406
|
+
var DEFAULT_DELAY = 500;
|
|
407
|
+
function useDebounce(value, delay = DEFAULT_DELAY) {
|
|
408
|
+
const timeout = useRef();
|
|
409
|
+
const [debouncedValue, setDebouncedValue] = useState2(value);
|
|
410
|
+
useEffect3(() => {
|
|
411
|
+
if (delay === false) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (timeout.current === void 0) {
|
|
415
|
+
setDebouncedValue(value);
|
|
416
|
+
}
|
|
417
|
+
timeout.current = window.setTimeout(() => {
|
|
418
|
+
setDebouncedValue(value);
|
|
419
|
+
timeout.current = void 0;
|
|
420
|
+
}, delay);
|
|
421
|
+
return () => {
|
|
422
|
+
window.clearTimeout(timeout.current);
|
|
423
|
+
};
|
|
424
|
+
}, [value, delay]);
|
|
425
|
+
return debouncedValue;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/lib/stable-stringify.ts
|
|
429
|
+
function stableStringify(object, ...args) {
|
|
430
|
+
const sortedObject = Object.keys(object).sort().reduce(
|
|
431
|
+
(sortedObject2, key) => {
|
|
432
|
+
sortedObject2[key] = object[key];
|
|
433
|
+
return sortedObject2;
|
|
434
|
+
},
|
|
435
|
+
{}
|
|
436
|
+
);
|
|
437
|
+
return JSON.stringify(sortedObject, ...args);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/lib/use-async-cache.ts
|
|
441
|
+
import { useCallback, useEffect as useEffect4, useMemo, useRef as useRef3 } from "react";
|
|
442
|
+
import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
|
|
443
|
+
|
|
444
|
+
// src/lib/use-initial.ts
|
|
445
|
+
import { useRef as useRef2 } from "react";
|
|
446
|
+
function useInitial(value) {
|
|
447
|
+
return useRef2(value).current;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/lib/use-async-cache.ts
|
|
451
|
+
var INITIAL_ASYNC_STATE = {
|
|
452
|
+
isLoading: false,
|
|
453
|
+
data: void 0,
|
|
454
|
+
error: void 0
|
|
455
|
+
};
|
|
456
|
+
var noop = () => {
|
|
457
|
+
};
|
|
458
|
+
function useAsyncCache(cache, key, options) {
|
|
459
|
+
const frozenOptions = useInitial(options);
|
|
460
|
+
const cacheItem = useMemo(() => {
|
|
461
|
+
if (key === null || !cache) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const cacheItem2 = cache.create(key, frozenOptions?.overrideFunction);
|
|
465
|
+
void cacheItem2.get();
|
|
466
|
+
return cacheItem2;
|
|
467
|
+
}, [cache, frozenOptions, key]);
|
|
468
|
+
const subscribe = useCallback(
|
|
469
|
+
(callback) => cacheItem?.subscribe(callback) ?? noop,
|
|
470
|
+
[cacheItem]
|
|
471
|
+
);
|
|
472
|
+
const getState = useCallback(
|
|
473
|
+
() => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
|
|
474
|
+
[cacheItem]
|
|
475
|
+
);
|
|
476
|
+
const revalidate = useCallback(() => cacheItem?.revalidate(), [cacheItem]);
|
|
477
|
+
const state = useSyncExternalStore2(subscribe, getState, getState);
|
|
478
|
+
const previousData = useRef3();
|
|
479
|
+
let data = state.data;
|
|
480
|
+
useEffect4(() => {
|
|
481
|
+
previousData.current = { key, data: state.data };
|
|
482
|
+
}, [key, state]);
|
|
483
|
+
if (frozenOptions?.suspense && state.isLoading && cacheItem) {
|
|
484
|
+
throw new Promise((resolve) => {
|
|
485
|
+
cacheItem.subscribeOnce(() => resolve());
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (state.isLoading && frozenOptions?.keepPreviousDataWhileLoading && typeof state.data === "undefined" && previousData.current?.key !== key && typeof previousData.current?.data !== "undefined") {
|
|
489
|
+
data = previousData.current.data;
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
isLoading: state.isLoading,
|
|
493
|
+
data,
|
|
494
|
+
error: state.error,
|
|
495
|
+
getState,
|
|
496
|
+
revalidate
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/lib/use-rerender.ts
|
|
501
|
+
import { useReducer } from "react";
|
|
31
502
|
function useRerender() {
|
|
32
503
|
const [, update] = useReducer(
|
|
33
504
|
// This implementation works by incrementing a hidden counter value that is
|
|
@@ -38,12 +509,9 @@ function useRerender() {
|
|
|
38
509
|
);
|
|
39
510
|
return update;
|
|
40
511
|
}
|
|
41
|
-
function useInitial(value) {
|
|
42
|
-
return useRef(value).current;
|
|
43
|
-
}
|
|
44
512
|
|
|
45
513
|
// src/factory.tsx
|
|
46
|
-
var
|
|
514
|
+
var noop2 = () => {
|
|
47
515
|
};
|
|
48
516
|
var identity = (x) => x;
|
|
49
517
|
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:
|
|
@@ -58,15 +526,11 @@ var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\
|
|
|
58
526
|
|
|
59
527
|
Why? Please see https://liveblocks.io/docs/guides/troubleshooting#stale-props-zombie-child for more information`;
|
|
60
528
|
var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
|
|
61
|
-
function
|
|
529
|
+
function useSyncExternalStore3(s, gs, gss) {
|
|
62
530
|
return useSyncExternalStoreWithSelector(s, gs, gss, identity);
|
|
63
531
|
}
|
|
64
|
-
var EMPTY_OTHERS = (
|
|
65
|
-
// NOTE: asArrayWithLegacyMethods() wrapping should no longer be necessary in 0.19
|
|
66
|
-
asArrayWithLegacyMethods([])
|
|
67
|
-
);
|
|
68
532
|
function getEmptyOthers() {
|
|
69
|
-
return
|
|
533
|
+
return [];
|
|
70
534
|
}
|
|
71
535
|
function makeMutationContext(room) {
|
|
72
536
|
const errmsg = "This mutation cannot be used until connected to the Liveblocks room";
|
|
@@ -95,7 +559,33 @@ function makeMutationContext(room) {
|
|
|
95
559
|
setMyPresence: room.updatePresence
|
|
96
560
|
};
|
|
97
561
|
}
|
|
98
|
-
|
|
562
|
+
var hasWarnedIfNoResolveUser = false;
|
|
563
|
+
function warnIfNoResolveUser(usersCache) {
|
|
564
|
+
if (!hasWarnedIfNoResolveUser && !usersCache && process.env.NODE_ENV !== "production") {
|
|
565
|
+
console.warn(
|
|
566
|
+
"Set the resolveUser option in createRoomContext to specify user info."
|
|
567
|
+
);
|
|
568
|
+
hasWarnedIfNoResolveUser = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
var hasWarnedAboutCommentsBeta = false;
|
|
572
|
+
function warnIfBetaCommentsHook() {
|
|
573
|
+
if (!hasWarnedAboutCommentsBeta && process.env.NODE_ENV !== "production") {
|
|
574
|
+
console.warn(
|
|
575
|
+
"Comments is currently in private beta. Learn more at https://liveblocks.io/docs/products/comments."
|
|
576
|
+
);
|
|
577
|
+
hasWarnedAboutCommentsBeta = true;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
var ContextBundle = React2.createContext(null);
|
|
581
|
+
function useRoomContextBundle() {
|
|
582
|
+
const bundle = React2.useContext(ContextBundle);
|
|
583
|
+
if (bundle === null) {
|
|
584
|
+
throw new Error("RoomProvider is missing from the React tree.");
|
|
585
|
+
}
|
|
586
|
+
return bundle;
|
|
587
|
+
}
|
|
588
|
+
function createRoomContext(client, options) {
|
|
99
589
|
const RoomContext = React2.createContext(null);
|
|
100
590
|
function RoomProvider(props) {
|
|
101
591
|
const {
|
|
@@ -140,19 +630,31 @@ function createRoomContext(client) {
|
|
|
140
630
|
})
|
|
141
631
|
);
|
|
142
632
|
React2.useEffect(() => {
|
|
143
|
-
|
|
144
|
-
|
|
633
|
+
const room2 = client.enter(
|
|
634
|
+
roomId,
|
|
635
|
+
{
|
|
145
636
|
initialPresence: frozen.initialPresence,
|
|
146
637
|
initialStorage: frozen.initialStorage,
|
|
147
638
|
shouldInitiallyConnect: frozen.shouldInitiallyConnect,
|
|
148
639
|
unstable_batchedUpdates: frozen.unstable_batchedUpdates
|
|
149
|
-
}
|
|
640
|
+
}
|
|
150
641
|
);
|
|
642
|
+
setRoom(room2);
|
|
151
643
|
return () => {
|
|
644
|
+
const commentsRoom = commentsRooms.get(roomId);
|
|
645
|
+
if (commentsRoom) {
|
|
646
|
+
commentsRooms.delete(roomId);
|
|
647
|
+
}
|
|
152
648
|
client.leave(roomId);
|
|
153
649
|
};
|
|
154
650
|
}, [roomId, frozen]);
|
|
155
|
-
return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room },
|
|
651
|
+
return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React2.createElement(
|
|
652
|
+
ContextBundle.Provider,
|
|
653
|
+
{
|
|
654
|
+
value: internalBundle
|
|
655
|
+
},
|
|
656
|
+
props.children
|
|
657
|
+
));
|
|
156
658
|
}
|
|
157
659
|
function connectionIdSelector(others) {
|
|
158
660
|
return others.map((user) => user.connectionId);
|
|
@@ -160,7 +662,7 @@ function createRoomContext(client) {
|
|
|
160
662
|
function useRoom() {
|
|
161
663
|
const room = React2.useContext(RoomContext);
|
|
162
664
|
if (room === null) {
|
|
163
|
-
throw new Error("RoomProvider is missing from the
|
|
665
|
+
throw new Error("RoomProvider is missing from the React tree.");
|
|
164
666
|
}
|
|
165
667
|
return room;
|
|
166
668
|
}
|
|
@@ -168,13 +670,13 @@ function createRoomContext(client) {
|
|
|
168
670
|
const room = useRoom();
|
|
169
671
|
const subscribe = room.events.status.subscribe;
|
|
170
672
|
const getSnapshot = room.getStatus;
|
|
171
|
-
return
|
|
673
|
+
return useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
172
674
|
}
|
|
173
675
|
function useMyPresence() {
|
|
174
676
|
const room = useRoom();
|
|
175
677
|
const subscribe = room.events.myPresence.subscribe;
|
|
176
678
|
const getSnapshot = room.getPresence;
|
|
177
|
-
const presence =
|
|
679
|
+
const presence = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
|
|
178
680
|
const setPresence = room.updatePresence;
|
|
179
681
|
return [presence, setPresence];
|
|
180
682
|
}
|
|
@@ -248,8 +750,8 @@ function createRoomContext(client) {
|
|
|
248
750
|
function useBroadcastEvent() {
|
|
249
751
|
const room = useRoom();
|
|
250
752
|
return React2.useCallback(
|
|
251
|
-
(event,
|
|
252
|
-
room.broadcastEvent(event,
|
|
753
|
+
(event, options2 = { shouldQueueEventIfNotReady: false }) => {
|
|
754
|
+
room.broadcastEvent(event, options2);
|
|
253
755
|
},
|
|
254
756
|
[room]
|
|
255
757
|
);
|
|
@@ -314,7 +816,7 @@ function createRoomContext(client) {
|
|
|
314
816
|
const subscribe = room.events.storageDidLoad.subscribeOnce;
|
|
315
817
|
const getSnapshot = room.getStorageSnapshot;
|
|
316
818
|
const getServerSnapshot = React2.useCallback(() => null, []);
|
|
317
|
-
return
|
|
819
|
+
return useSyncExternalStore3(subscribe, getSnapshot, getServerSnapshot);
|
|
318
820
|
}
|
|
319
821
|
function useStorageRoot() {
|
|
320
822
|
return [useMutableStorageRoot()];
|
|
@@ -332,13 +834,13 @@ function createRoomContext(client) {
|
|
|
332
834
|
const room = useRoom();
|
|
333
835
|
const subscribe = room.events.history.subscribe;
|
|
334
836
|
const canUndo = room.history.canUndo;
|
|
335
|
-
return
|
|
837
|
+
return useSyncExternalStore3(subscribe, canUndo, canUndo);
|
|
336
838
|
}
|
|
337
839
|
function useCanRedo() {
|
|
338
840
|
const room = useRoom();
|
|
339
841
|
const subscribe = room.events.history.subscribe;
|
|
340
842
|
const canRedo = room.history.canRedo;
|
|
341
|
-
return
|
|
843
|
+
return useSyncExternalStore3(subscribe, canRedo, canRedo);
|
|
342
844
|
}
|
|
343
845
|
function useBatch() {
|
|
344
846
|
return useRoom().batch;
|
|
@@ -395,7 +897,7 @@ function createRoomContext(client) {
|
|
|
395
897
|
[selector]
|
|
396
898
|
);
|
|
397
899
|
const subscribe = React2.useCallback(
|
|
398
|
-
(onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) :
|
|
900
|
+
(onStoreChange) => rootOrNull !== null ? room.subscribe(rootOrNull, onStoreChange, { isDeep: true }) : noop2,
|
|
399
901
|
[room, rootOrNull]
|
|
400
902
|
);
|
|
401
903
|
const getSnapshot = React2.useCallback(() => {
|
|
@@ -501,7 +1003,140 @@ function createRoomContext(client) {
|
|
|
501
1003
|
useSuspendUntilStorageLoaded();
|
|
502
1004
|
return useLegacyKey(key);
|
|
503
1005
|
}
|
|
504
|
-
|
|
1006
|
+
const commentsErrorEventSource = makeEventSource2();
|
|
1007
|
+
const commentsRooms = /* @__PURE__ */ new Map();
|
|
1008
|
+
function getCommentsRoom(room) {
|
|
1009
|
+
let commentsRoom = commentsRooms.get(room.id);
|
|
1010
|
+
if (commentsRoom === void 0) {
|
|
1011
|
+
commentsRoom = createCommentsRoom(room, commentsErrorEventSource);
|
|
1012
|
+
commentsRooms.set(room.id, commentsRoom);
|
|
1013
|
+
}
|
|
1014
|
+
return commentsRoom;
|
|
1015
|
+
}
|
|
1016
|
+
function useThreads() {
|
|
1017
|
+
const room = useRoom();
|
|
1018
|
+
React2.useEffect(() => {
|
|
1019
|
+
warnIfBetaCommentsHook();
|
|
1020
|
+
}, []);
|
|
1021
|
+
return getCommentsRoom(room).useThreads();
|
|
1022
|
+
}
|
|
1023
|
+
function useThreadsSuspense() {
|
|
1024
|
+
const room = useRoom();
|
|
1025
|
+
React2.useEffect(() => {
|
|
1026
|
+
warnIfBetaCommentsHook();
|
|
1027
|
+
}, []);
|
|
1028
|
+
return getCommentsRoom(room).useThreadsSuspense();
|
|
1029
|
+
}
|
|
1030
|
+
function useCreateThread() {
|
|
1031
|
+
const room = useRoom();
|
|
1032
|
+
React2.useEffect(() => {
|
|
1033
|
+
warnIfBetaCommentsHook();
|
|
1034
|
+
}, []);
|
|
1035
|
+
return React2.useCallback(
|
|
1036
|
+
(options2) => getCommentsRoom(room).createThread(options2),
|
|
1037
|
+
[room]
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
function useEditThreadMetadata() {
|
|
1041
|
+
const room = useRoom();
|
|
1042
|
+
React2.useEffect(() => {
|
|
1043
|
+
warnIfBetaCommentsHook();
|
|
1044
|
+
}, []);
|
|
1045
|
+
return React2.useCallback(
|
|
1046
|
+
(options2) => getCommentsRoom(room).editThreadMetadata(options2),
|
|
1047
|
+
[room]
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
function useCreateComment() {
|
|
1051
|
+
const room = useRoom();
|
|
1052
|
+
React2.useEffect(() => {
|
|
1053
|
+
warnIfBetaCommentsHook();
|
|
1054
|
+
}, []);
|
|
1055
|
+
return React2.useCallback(
|
|
1056
|
+
(options2) => getCommentsRoom(room).createComment(options2),
|
|
1057
|
+
[room]
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
function useEditComment() {
|
|
1061
|
+
const room = useRoom();
|
|
1062
|
+
React2.useEffect(() => {
|
|
1063
|
+
warnIfBetaCommentsHook();
|
|
1064
|
+
}, []);
|
|
1065
|
+
return React2.useCallback(
|
|
1066
|
+
(options2) => getCommentsRoom(room).editComment(options2),
|
|
1067
|
+
[room]
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
function useDeleteComment() {
|
|
1071
|
+
const room = useRoom();
|
|
1072
|
+
React2.useEffect(() => {
|
|
1073
|
+
warnIfBetaCommentsHook();
|
|
1074
|
+
}, []);
|
|
1075
|
+
return React2.useCallback(
|
|
1076
|
+
(options2) => getCommentsRoom(room).deleteComment(options2),
|
|
1077
|
+
[room]
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
const { resolveUser, resolveMentionSuggestions } = options ?? {};
|
|
1081
|
+
const usersCache = resolveUser ? createAsyncCache((stringifiedOptions) => {
|
|
1082
|
+
return resolveUser(
|
|
1083
|
+
JSON.parse(stringifiedOptions)
|
|
1084
|
+
);
|
|
1085
|
+
}) : void 0;
|
|
1086
|
+
function useUser(userId) {
|
|
1087
|
+
const resolverKey = React2.useMemo(
|
|
1088
|
+
() => stableStringify({ userId }),
|
|
1089
|
+
[userId]
|
|
1090
|
+
);
|
|
1091
|
+
const state = useAsyncCache(usersCache, resolverKey);
|
|
1092
|
+
React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
|
|
1093
|
+
if (state.isLoading) {
|
|
1094
|
+
return {
|
|
1095
|
+
isLoading: true
|
|
1096
|
+
};
|
|
1097
|
+
} else {
|
|
1098
|
+
return {
|
|
1099
|
+
user: state.data,
|
|
1100
|
+
error: state.error,
|
|
1101
|
+
isLoading: false
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function useUserSuspense(userId) {
|
|
1106
|
+
const resolverKey = React2.useMemo(
|
|
1107
|
+
() => stableStringify({ userId }),
|
|
1108
|
+
[userId]
|
|
1109
|
+
);
|
|
1110
|
+
const state = useAsyncCache(usersCache, resolverKey, {
|
|
1111
|
+
suspense: true
|
|
1112
|
+
});
|
|
1113
|
+
React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
|
|
1114
|
+
return {
|
|
1115
|
+
user: state.data,
|
|
1116
|
+
error: state.error,
|
|
1117
|
+
isLoading: false
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
const mentionSuggestionsCache = createAsyncCache(
|
|
1121
|
+
resolveMentionSuggestions ? (stringifiedOptions) => {
|
|
1122
|
+
return resolveMentionSuggestions(
|
|
1123
|
+
JSON.parse(stringifiedOptions)
|
|
1124
|
+
);
|
|
1125
|
+
} : () => Promise.resolve([])
|
|
1126
|
+
);
|
|
1127
|
+
function useMentionSuggestions(search) {
|
|
1128
|
+
const room = useRoom();
|
|
1129
|
+
const debouncedSearch = useDebounce(search, 500);
|
|
1130
|
+
const resolverKey = React2.useMemo(
|
|
1131
|
+
() => debouncedSearch !== void 0 ? stableStringify({ text: debouncedSearch, roomId: room.id }) : null,
|
|
1132
|
+
[debouncedSearch, room.id]
|
|
1133
|
+
);
|
|
1134
|
+
const { data } = useAsyncCache(mentionSuggestionsCache, resolverKey, {
|
|
1135
|
+
keepPreviousDataWhileLoading: true
|
|
1136
|
+
});
|
|
1137
|
+
return data;
|
|
1138
|
+
}
|
|
1139
|
+
const bundle = {
|
|
505
1140
|
RoomContext,
|
|
506
1141
|
RoomProvider,
|
|
507
1142
|
useRoom,
|
|
@@ -530,6 +1165,13 @@ function createRoomContext(client) {
|
|
|
530
1165
|
useOthersConnectionIds,
|
|
531
1166
|
useOther,
|
|
532
1167
|
useMutation,
|
|
1168
|
+
useThreads,
|
|
1169
|
+
useUser,
|
|
1170
|
+
useCreateThread,
|
|
1171
|
+
useEditThreadMetadata,
|
|
1172
|
+
useCreateComment,
|
|
1173
|
+
useEditComment,
|
|
1174
|
+
useDeleteComment,
|
|
533
1175
|
suspense: {
|
|
534
1176
|
RoomContext,
|
|
535
1177
|
RoomProvider,
|
|
@@ -558,9 +1200,22 @@ function createRoomContext(client) {
|
|
|
558
1200
|
useOthersMapped: useOthersMappedSuspense,
|
|
559
1201
|
useOthersConnectionIds: useOthersConnectionIdsSuspense,
|
|
560
1202
|
useOther: useOtherSuspense,
|
|
561
|
-
useMutation
|
|
1203
|
+
useMutation,
|
|
1204
|
+
useThreads: useThreadsSuspense,
|
|
1205
|
+
useUser: useUserSuspense,
|
|
1206
|
+
useCreateThread,
|
|
1207
|
+
useEditThreadMetadata,
|
|
1208
|
+
useCreateComment,
|
|
1209
|
+
useEditComment,
|
|
1210
|
+
useDeleteComment
|
|
562
1211
|
}
|
|
563
1212
|
};
|
|
1213
|
+
const internalBundle = {
|
|
1214
|
+
...bundle,
|
|
1215
|
+
hasResolveMentionSuggestions: resolveMentionSuggestions !== void 0,
|
|
1216
|
+
useMentionSuggestions
|
|
1217
|
+
};
|
|
1218
|
+
return bundle;
|
|
564
1219
|
}
|
|
565
1220
|
|
|
566
1221
|
// src/index.ts
|
|
@@ -569,6 +1224,7 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
569
1224
|
export {
|
|
570
1225
|
ClientSideSuspense,
|
|
571
1226
|
createRoomContext,
|
|
572
|
-
shallow2 as shallow
|
|
1227
|
+
shallow2 as shallow,
|
|
1228
|
+
useRoomContextBundle
|
|
573
1229
|
};
|
|
574
1230
|
//# sourceMappingURL=index.mjs.map
|