@liveblocks/react 1.2.2-comments3 → 1.2.2-comments4
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 +996 -403
- package/dist/index.d.ts +996 -403
- package/dist/index.js +658 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +660 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
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.2.2-
|
|
8
|
+
var PKG_VERSION = "1.2.2-comments4";
|
|
7
9
|
var PKG_FORMAT = "esm";
|
|
8
10
|
|
|
9
11
|
// src/ClientSideSuspense.tsx
|
|
@@ -20,14 +22,489 @@ function ClientSideSuspense(props) {
|
|
|
20
22
|
import { shallow } from "@liveblocks/client";
|
|
21
23
|
import {
|
|
22
24
|
asArrayWithLegacyMethods,
|
|
25
|
+
createAsyncCache,
|
|
23
26
|
deprecateIf,
|
|
24
|
-
errorIf
|
|
27
|
+
errorIf,
|
|
28
|
+
makeEventSource as makeEventSource2
|
|
25
29
|
} from "@liveblocks/core";
|
|
26
30
|
import * as React2 from "react";
|
|
27
31
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
28
32
|
|
|
33
|
+
// src/comments/CommentsRoom.ts
|
|
34
|
+
import { makePoller } from "@liveblocks/core";
|
|
35
|
+
|
|
36
|
+
// ../../node_modules/nanoid/index.js
|
|
37
|
+
import crypto from "crypto";
|
|
38
|
+
|
|
39
|
+
// ../../node_modules/nanoid/url-alphabet/index.js
|
|
40
|
+
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
41
|
+
|
|
42
|
+
// ../../node_modules/nanoid/index.js
|
|
43
|
+
var POOL_SIZE_MULTIPLIER = 128;
|
|
44
|
+
var pool;
|
|
45
|
+
var poolOffset;
|
|
46
|
+
var fillPool = (bytes) => {
|
|
47
|
+
if (!pool || pool.length < bytes) {
|
|
48
|
+
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
|
|
49
|
+
crypto.randomFillSync(pool);
|
|
50
|
+
poolOffset = 0;
|
|
51
|
+
} else if (poolOffset + bytes > pool.length) {
|
|
52
|
+
crypto.randomFillSync(pool);
|
|
53
|
+
poolOffset = 0;
|
|
54
|
+
}
|
|
55
|
+
poolOffset += bytes;
|
|
56
|
+
};
|
|
57
|
+
var nanoid = (size = 21) => {
|
|
58
|
+
fillPool(size -= 0);
|
|
59
|
+
let id = "";
|
|
60
|
+
for (let i = poolOffset - size; i < poolOffset; i++) {
|
|
61
|
+
id += urlAlphabet[pool[i] & 63];
|
|
62
|
+
}
|
|
63
|
+
return id;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/comments/CommentsRoom.ts
|
|
67
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
68
|
+
|
|
69
|
+
// src/comments/errors.ts
|
|
70
|
+
var CreateThreadError = class extends Error {
|
|
71
|
+
constructor(cause, context) {
|
|
72
|
+
super("Create thread failed.");
|
|
73
|
+
this.cause = cause;
|
|
74
|
+
this.context = context;
|
|
75
|
+
this.name = "CreateThreadError";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var EditThreadMetadataError = class extends Error {
|
|
79
|
+
constructor(cause, context) {
|
|
80
|
+
super("Edit thread metadata failed.");
|
|
81
|
+
this.cause = cause;
|
|
82
|
+
this.context = context;
|
|
83
|
+
this.name = "EditThreadMetadataError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var CreateCommentError = class extends Error {
|
|
87
|
+
constructor(cause, context) {
|
|
88
|
+
super("Create comment failed.");
|
|
89
|
+
this.cause = cause;
|
|
90
|
+
this.context = context;
|
|
91
|
+
this.name = "CreateCommentError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var EditCommentError = class extends Error {
|
|
95
|
+
constructor(cause, context) {
|
|
96
|
+
super("Edit comment failed.");
|
|
97
|
+
this.cause = cause;
|
|
98
|
+
this.context = context;
|
|
99
|
+
this.name = "EditCommentError";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var DeleteCommentError = class extends Error {
|
|
103
|
+
constructor(cause, context) {
|
|
104
|
+
super("Delete comment failed.");
|
|
105
|
+
this.cause = cause;
|
|
106
|
+
this.context = context;
|
|
107
|
+
this.name = "DeleteCommentError";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/comments/lib/store.ts
|
|
112
|
+
import { makeEventSource } from "@liveblocks/core";
|
|
113
|
+
function createStore(initialState) {
|
|
114
|
+
let state = initialState;
|
|
115
|
+
const eventSource = makeEventSource();
|
|
116
|
+
return {
|
|
117
|
+
get() {
|
|
118
|
+
return state;
|
|
119
|
+
},
|
|
120
|
+
set(newState) {
|
|
121
|
+
state = newState;
|
|
122
|
+
eventSource.notify(state);
|
|
123
|
+
},
|
|
124
|
+
subscribe(callback) {
|
|
125
|
+
return eventSource.subscribe(callback);
|
|
126
|
+
},
|
|
127
|
+
subscribeOnce(callback) {
|
|
128
|
+
return eventSource.subscribeOnce(callback);
|
|
129
|
+
},
|
|
130
|
+
subscribersCount() {
|
|
131
|
+
return eventSource.count();
|
|
132
|
+
},
|
|
133
|
+
destroy() {
|
|
134
|
+
return eventSource.clear();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/comments/CommentsRoom.ts
|
|
140
|
+
var POLLING_INTERVAL_REALTIME = 3e4;
|
|
141
|
+
var POLLING_INTERVAL = 5e3;
|
|
142
|
+
var THREAD_ID_PREFIX = "th";
|
|
143
|
+
var COMMENT_ID_PREFIX = "cm";
|
|
144
|
+
function createOptimisticId(prefix) {
|
|
145
|
+
return `${prefix}_${nanoid()}`;
|
|
146
|
+
}
|
|
147
|
+
function createCommentsRoom(room, errorEventSource) {
|
|
148
|
+
const store = createStore({
|
|
149
|
+
isLoading: true
|
|
150
|
+
});
|
|
151
|
+
let numberOfMutations = 0;
|
|
152
|
+
function endMutation() {
|
|
153
|
+
numberOfMutations--;
|
|
154
|
+
if (numberOfMutations === 0) {
|
|
155
|
+
revalidateThreads();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function startMutation() {
|
|
159
|
+
pollingHub.threads.stop();
|
|
160
|
+
numberOfMutations++;
|
|
161
|
+
}
|
|
162
|
+
const pollingHub = {
|
|
163
|
+
// TODO: If there's an error, it will currently infinitely retry at the current polling rate → add retry logic
|
|
164
|
+
threads: makePoller(revalidateThreads)
|
|
165
|
+
};
|
|
166
|
+
let unsubscribeRealtimeEvents;
|
|
167
|
+
let unsubscribeRealtimeConnection;
|
|
168
|
+
let realtimeClientConnected = false;
|
|
169
|
+
function getPollingInterval() {
|
|
170
|
+
return realtimeClientConnected ? POLLING_INTERVAL_REALTIME : POLLING_INTERVAL;
|
|
171
|
+
}
|
|
172
|
+
function ensureThreadsAreLoadedForMutations() {
|
|
173
|
+
const state = store.get();
|
|
174
|
+
if (state.isLoading || state.error) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"Cannot update threads or comments before they are loaded"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return state.threads;
|
|
180
|
+
}
|
|
181
|
+
async function revalidateThreads() {
|
|
182
|
+
pollingHub.threads.pause();
|
|
183
|
+
if (numberOfMutations === 0) {
|
|
184
|
+
setThreads(await room.getThreads());
|
|
185
|
+
}
|
|
186
|
+
pollingHub.threads.resume();
|
|
187
|
+
}
|
|
188
|
+
function subscribe() {
|
|
189
|
+
if (!unsubscribeRealtimeEvents) {
|
|
190
|
+
unsubscribeRealtimeEvents = room.events.comments.subscribe(() => {
|
|
191
|
+
pollingHub.threads.restart(getPollingInterval());
|
|
192
|
+
revalidateThreads();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (!unsubscribeRealtimeConnection) {
|
|
196
|
+
unsubscribeRealtimeConnection = room.events.status.subscribe((status) => {
|
|
197
|
+
const nextRealtimeClientConnected = status === "connected";
|
|
198
|
+
if (nextRealtimeClientConnected !== realtimeClientConnected) {
|
|
199
|
+
realtimeClientConnected = nextRealtimeClientConnected;
|
|
200
|
+
pollingHub.threads.restart(getPollingInterval());
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
pollingHub.threads.start(getPollingInterval());
|
|
205
|
+
revalidateThreads();
|
|
206
|
+
return () => {
|
|
207
|
+
pollingHub.threads.stop();
|
|
208
|
+
unsubscribeRealtimeEvents?.();
|
|
209
|
+
unsubscribeRealtimeEvents = void 0;
|
|
210
|
+
unsubscribeRealtimeConnection?.();
|
|
211
|
+
unsubscribeRealtimeConnection = void 0;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function setThreads(newThreads) {
|
|
215
|
+
store.set({
|
|
216
|
+
threads: newThreads,
|
|
217
|
+
isLoading: false
|
|
218
|
+
});
|
|
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
|
+
function useThreads() {
|
|
394
|
+
return useSyncExternalStore(
|
|
395
|
+
store.subscribe,
|
|
396
|
+
store.get,
|
|
397
|
+
store.get
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
function useThreadsSuspense() {
|
|
401
|
+
const result = useThreads();
|
|
402
|
+
if (result.isLoading) {
|
|
403
|
+
throw new Promise(store.subscribeOnce);
|
|
404
|
+
}
|
|
405
|
+
if (result.error) {
|
|
406
|
+
throw result.error;
|
|
407
|
+
}
|
|
408
|
+
return result.threads;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
useThreads,
|
|
412
|
+
useThreadsSuspense,
|
|
413
|
+
createThread,
|
|
414
|
+
editThreadMetadata,
|
|
415
|
+
createComment,
|
|
416
|
+
editComment,
|
|
417
|
+
deleteComment,
|
|
418
|
+
subscribe
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/comments/lib/use-async-cache.ts
|
|
423
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
|
|
424
|
+
import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
|
|
425
|
+
|
|
426
|
+
// src/comments/lib/use-initial.ts
|
|
427
|
+
import { useRef } from "react";
|
|
428
|
+
function useInitial(value) {
|
|
429
|
+
return useRef(value instanceof Function ? value() : value).current;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/comments/lib/use-async-cache.ts
|
|
433
|
+
var INITIAL_ASYNC_STATE = {
|
|
434
|
+
isLoading: false,
|
|
435
|
+
data: void 0,
|
|
436
|
+
error: void 0
|
|
437
|
+
};
|
|
438
|
+
var noop = () => {
|
|
439
|
+
};
|
|
440
|
+
function useAsyncCache(cache, key, options) {
|
|
441
|
+
const frozenOptions = useInitial(options);
|
|
442
|
+
const cacheItem = useMemo(() => {
|
|
443
|
+
if (key === null || !cache) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
const cacheItem2 = cache.create(key, frozenOptions?.overrideFunction);
|
|
447
|
+
void cacheItem2.get();
|
|
448
|
+
return cacheItem2;
|
|
449
|
+
}, [cache, frozenOptions, key]);
|
|
450
|
+
const subscribe = useCallback(
|
|
451
|
+
(callback) => cacheItem?.subscribe(callback) ?? noop,
|
|
452
|
+
[cacheItem]
|
|
453
|
+
);
|
|
454
|
+
const getState = useCallback(
|
|
455
|
+
() => cacheItem?.getState() ?? INITIAL_ASYNC_STATE,
|
|
456
|
+
[cacheItem]
|
|
457
|
+
);
|
|
458
|
+
const revalidate = useCallback(() => cacheItem?.revalidate(), [cacheItem]);
|
|
459
|
+
const state = useSyncExternalStore2(subscribe, getState, getState);
|
|
460
|
+
const previousData = useRef2();
|
|
461
|
+
let data = state.data;
|
|
462
|
+
useEffect2(() => {
|
|
463
|
+
previousData.current = { key, data: state.data };
|
|
464
|
+
}, [key, state]);
|
|
465
|
+
if (frozenOptions?.suspense && state.isLoading && cacheItem) {
|
|
466
|
+
throw new Promise((resolve) => {
|
|
467
|
+
cacheItem.subscribeOnce(() => resolve());
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (state.isLoading && frozenOptions?.keepPreviousDataWhileLoading && typeof state.data === "undefined" && previousData.current?.key !== key && typeof previousData.current?.data !== "undefined") {
|
|
471
|
+
data = previousData.current.data;
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
isLoading: state.isLoading,
|
|
475
|
+
data,
|
|
476
|
+
error: state.error,
|
|
477
|
+
getState,
|
|
478
|
+
revalidate
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/comments/lib/use-debounce.ts
|
|
483
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
484
|
+
var DEFAULT_DELAY = 500;
|
|
485
|
+
function useDebounce(value, delay = DEFAULT_DELAY) {
|
|
486
|
+
const timeout = useRef3();
|
|
487
|
+
const [debouncedValue, setDebouncedValue] = useState2(value);
|
|
488
|
+
useEffect3(() => {
|
|
489
|
+
if (delay === false) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (timeout.current === void 0) {
|
|
493
|
+
setDebouncedValue(value);
|
|
494
|
+
}
|
|
495
|
+
timeout.current = window.setTimeout(() => {
|
|
496
|
+
setDebouncedValue(value);
|
|
497
|
+
timeout.current = void 0;
|
|
498
|
+
}, delay);
|
|
499
|
+
return () => {
|
|
500
|
+
window.clearTimeout(timeout.current);
|
|
501
|
+
};
|
|
502
|
+
}, [value, delay]);
|
|
503
|
+
return debouncedValue;
|
|
504
|
+
}
|
|
505
|
+
|
|
29
506
|
// src/hooks.ts
|
|
30
|
-
import { useReducer, useRef } from "react";
|
|
507
|
+
import { useReducer, useRef as useRef4 } from "react";
|
|
31
508
|
function useRerender() {
|
|
32
509
|
const [, update] = useReducer(
|
|
33
510
|
// This implementation works by incrementing a hidden counter value that is
|
|
@@ -38,12 +515,12 @@ function useRerender() {
|
|
|
38
515
|
);
|
|
39
516
|
return update;
|
|
40
517
|
}
|
|
41
|
-
function
|
|
42
|
-
return
|
|
518
|
+
function useInitial2(value) {
|
|
519
|
+
return useRef4(value).current;
|
|
43
520
|
}
|
|
44
521
|
|
|
45
522
|
// src/factory.tsx
|
|
46
|
-
var
|
|
523
|
+
var noop2 = () => {
|
|
47
524
|
};
|
|
48
525
|
var identity = (x) => x;
|
|
49
526
|
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,7 +535,7 @@ var missing_unstable_batchedUpdates = (reactVersion, roomId) => `We noticed you\
|
|
|
58
535
|
|
|
59
536
|
Why? Please see https://liveblocks.io/docs/guides/troubleshooting#stale-props-zombie-child for more information`;
|
|
60
537
|
var superfluous_unstable_batchedUpdates = "You don\u2019t need to pass unstable_batchedUpdates to RoomProvider anymore, since you\u2019re on React 18+ already.";
|
|
61
|
-
function
|
|
538
|
+
function useSyncExternalStore3(s, gs, gss) {
|
|
62
539
|
return useSyncExternalStoreWithSelector(s, gs, gss, identity);
|
|
63
540
|
}
|
|
64
541
|
var EMPTY_OTHERS = (
|
|
@@ -95,7 +572,27 @@ function makeMutationContext(room) {
|
|
|
95
572
|
setMyPresence: room.updatePresence
|
|
96
573
|
};
|
|
97
574
|
}
|
|
98
|
-
|
|
575
|
+
var hasWarnedIfNoResolveUser = false;
|
|
576
|
+
var hasWarnedIfNoResolveMentionSuggestions = false;
|
|
577
|
+
function warnIfNoResolveUser(usersCache) {
|
|
578
|
+
if (!hasWarnedIfNoResolveUser && !usersCache && process.env.NODE_ENV !== "production") {
|
|
579
|
+
console.warn("The resolveUser option wasn't set in createRoomContext.");
|
|
580
|
+
hasWarnedIfNoResolveUser = true;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function warnIfNoResolveMentionSuggestions(mentionSuggestionsCache) {
|
|
584
|
+
if (!hasWarnedIfNoResolveMentionSuggestions && !mentionSuggestionsCache && process.env.NODE_ENV !== "production") {
|
|
585
|
+
console.warn(
|
|
586
|
+
"The resolveMentionSuggestions option wasn't set in createRoomContext."
|
|
587
|
+
);
|
|
588
|
+
hasWarnedIfNoResolveMentionSuggestions = true;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
var ContextBundle = React2.createContext(null);
|
|
592
|
+
function useRoomContextBundle() {
|
|
593
|
+
return React2.useContext(ContextBundle);
|
|
594
|
+
}
|
|
595
|
+
function createRoomContext(client, options) {
|
|
99
596
|
const RoomContext = React2.createContext(null);
|
|
100
597
|
function RoomProvider(props) {
|
|
101
598
|
const {
|
|
@@ -125,7 +622,7 @@ function createRoomContext(client) {
|
|
|
125
622
|
superfluous_unstable_batchedUpdates
|
|
126
623
|
);
|
|
127
624
|
}
|
|
128
|
-
const frozen =
|
|
625
|
+
const frozen = useInitial2({
|
|
129
626
|
initialPresence,
|
|
130
627
|
initialStorage,
|
|
131
628
|
unstable_batchedUpdates,
|
|
@@ -140,19 +637,24 @@ function createRoomContext(client) {
|
|
|
140
637
|
})
|
|
141
638
|
);
|
|
142
639
|
React2.useEffect(() => {
|
|
143
|
-
|
|
144
|
-
|
|
640
|
+
const room2 = client.enter(
|
|
641
|
+
roomId,
|
|
642
|
+
{
|
|
145
643
|
initialPresence: frozen.initialPresence,
|
|
146
644
|
initialStorage: frozen.initialStorage,
|
|
147
645
|
shouldInitiallyConnect: frozen.shouldInitiallyConnect,
|
|
148
646
|
unstable_batchedUpdates: frozen.unstable_batchedUpdates
|
|
149
|
-
}
|
|
647
|
+
}
|
|
150
648
|
);
|
|
649
|
+
setRoom(room2);
|
|
650
|
+
const unsubscribe = getCommentsRoom(room2).subscribe();
|
|
151
651
|
return () => {
|
|
652
|
+
unsubscribe();
|
|
653
|
+
commentsRooms.delete(room2.id);
|
|
152
654
|
client.leave(roomId);
|
|
153
655
|
};
|
|
154
656
|
}, [roomId, frozen]);
|
|
155
|
-
return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, props.children);
|
|
657
|
+
return /* @__PURE__ */ React2.createElement(RoomContext.Provider, { value: room }, /* @__PURE__ */ React2.createElement(ContextBundle.Provider, { value: bundle }, props.children));
|
|
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,123 @@ function createRoomContext(client) {
|
|
|
501
1003
|
useSuspendUntilStorageLoaded();
|
|
502
1004
|
return useLegacyKey(key);
|
|
503
1005
|
}
|
|
504
|
-
|
|
1006
|
+
const errorEventSource = 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, errorEventSource);
|
|
1012
|
+
commentsRooms.set(room.id, commentsRoom);
|
|
1013
|
+
}
|
|
1014
|
+
return commentsRoom;
|
|
1015
|
+
}
|
|
1016
|
+
function useThreads() {
|
|
1017
|
+
const room = useRoom();
|
|
1018
|
+
return getCommentsRoom(room).useThreads();
|
|
1019
|
+
}
|
|
1020
|
+
function useThreadsSuspense() {
|
|
1021
|
+
const room = useRoom();
|
|
1022
|
+
return getCommentsRoom(room).useThreadsSuspense();
|
|
1023
|
+
}
|
|
1024
|
+
function useCreateThread() {
|
|
1025
|
+
const room = useRoom();
|
|
1026
|
+
return React2.useCallback(
|
|
1027
|
+
(options2) => getCommentsRoom(room).createThread(options2),
|
|
1028
|
+
[room]
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
function useEditThreadMetadata() {
|
|
1032
|
+
const room = useRoom();
|
|
1033
|
+
return React2.useCallback(
|
|
1034
|
+
(options2) => getCommentsRoom(room).editThreadMetadata(options2),
|
|
1035
|
+
[room]
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
function useCreateComment() {
|
|
1039
|
+
const room = useRoom();
|
|
1040
|
+
return React2.useCallback(
|
|
1041
|
+
(options2) => getCommentsRoom(room).createComment(options2),
|
|
1042
|
+
[room]
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
function useEditComment() {
|
|
1046
|
+
const room = useRoom();
|
|
1047
|
+
return React2.useCallback(
|
|
1048
|
+
(options2) => getCommentsRoom(room).editComment(options2),
|
|
1049
|
+
[room]
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
function useDeleteComment() {
|
|
1053
|
+
const room = useRoom();
|
|
1054
|
+
return React2.useCallback(
|
|
1055
|
+
(options2) => getCommentsRoom(room).deleteComment(options2),
|
|
1056
|
+
[room]
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
const { resolveUser, resolveMentionSuggestions } = options ?? {};
|
|
1060
|
+
const usersCache = resolveUser ? createAsyncCache((stringifiedOptions) => {
|
|
1061
|
+
return resolveUser(
|
|
1062
|
+
JSON.parse(stringifiedOptions)
|
|
1063
|
+
);
|
|
1064
|
+
}) : void 0;
|
|
1065
|
+
function useUser(userId) {
|
|
1066
|
+
const resolverKey = React2.useMemo(
|
|
1067
|
+
() => JSON.stringify({ userId }),
|
|
1068
|
+
[userId]
|
|
1069
|
+
);
|
|
1070
|
+
const state = useAsyncCache(usersCache, resolverKey);
|
|
1071
|
+
React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
|
|
1072
|
+
if (state.isLoading) {
|
|
1073
|
+
return {
|
|
1074
|
+
isLoading: true
|
|
1075
|
+
};
|
|
1076
|
+
} else {
|
|
1077
|
+
return {
|
|
1078
|
+
user: state.data,
|
|
1079
|
+
error: state.error,
|
|
1080
|
+
isLoading: false
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function useUserSuspense(userId) {
|
|
1085
|
+
const resolverKey = React2.useMemo(
|
|
1086
|
+
() => JSON.stringify({ userId }),
|
|
1087
|
+
[userId]
|
|
1088
|
+
);
|
|
1089
|
+
const state = useAsyncCache(usersCache, resolverKey, {
|
|
1090
|
+
suspense: true
|
|
1091
|
+
});
|
|
1092
|
+
React2.useEffect(() => warnIfNoResolveUser(usersCache), []);
|
|
1093
|
+
return {
|
|
1094
|
+
user: state.data,
|
|
1095
|
+
error: state.error,
|
|
1096
|
+
isLoading: false
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
const mentionSuggestionsCache = createAsyncCache(
|
|
1100
|
+
resolveMentionSuggestions ? (stringifiedOptions) => {
|
|
1101
|
+
return resolveMentionSuggestions(
|
|
1102
|
+
JSON.parse(stringifiedOptions)
|
|
1103
|
+
);
|
|
1104
|
+
} : () => Promise.resolve([])
|
|
1105
|
+
);
|
|
1106
|
+
function useMentionSuggestions(search) {
|
|
1107
|
+
const room = useRoom();
|
|
1108
|
+
const debouncedSearch = useDebounce(search, 500);
|
|
1109
|
+
const resolverKey = React2.useMemo(
|
|
1110
|
+
() => debouncedSearch !== void 0 ? JSON.stringify({ text: debouncedSearch, roomId: room.id }) : null,
|
|
1111
|
+
[debouncedSearch, room.id]
|
|
1112
|
+
);
|
|
1113
|
+
const { data } = useAsyncCache(mentionSuggestionsCache, resolverKey, {
|
|
1114
|
+
keepPreviousDataWhileLoading: true
|
|
1115
|
+
});
|
|
1116
|
+
React2.useEffect(
|
|
1117
|
+
() => warnIfNoResolveMentionSuggestions(mentionSuggestionsCache),
|
|
1118
|
+
[]
|
|
1119
|
+
);
|
|
1120
|
+
return data;
|
|
1121
|
+
}
|
|
1122
|
+
const bundle = {
|
|
505
1123
|
RoomContext,
|
|
506
1124
|
RoomProvider,
|
|
507
1125
|
useRoom,
|
|
@@ -530,6 +1148,14 @@ function createRoomContext(client) {
|
|
|
530
1148
|
useOthersConnectionIds,
|
|
531
1149
|
useOther,
|
|
532
1150
|
useMutation,
|
|
1151
|
+
useThreads,
|
|
1152
|
+
useUser,
|
|
1153
|
+
useCreateThread,
|
|
1154
|
+
useEditThreadMetadata,
|
|
1155
|
+
useCreateComment,
|
|
1156
|
+
useEditComment,
|
|
1157
|
+
useDeleteComment,
|
|
1158
|
+
useMentionSuggestions,
|
|
533
1159
|
suspense: {
|
|
534
1160
|
RoomContext,
|
|
535
1161
|
RoomProvider,
|
|
@@ -558,9 +1184,17 @@ function createRoomContext(client) {
|
|
|
558
1184
|
useOthersMapped: useOthersMappedSuspense,
|
|
559
1185
|
useOthersConnectionIds: useOthersConnectionIdsSuspense,
|
|
560
1186
|
useOther: useOtherSuspense,
|
|
561
|
-
useMutation
|
|
1187
|
+
useMutation,
|
|
1188
|
+
useThreads: useThreadsSuspense,
|
|
1189
|
+
useUser: useUserSuspense,
|
|
1190
|
+
useCreateThread,
|
|
1191
|
+
useEditThreadMetadata,
|
|
1192
|
+
useCreateComment,
|
|
1193
|
+
useEditComment,
|
|
1194
|
+
useDeleteComment
|
|
562
1195
|
}
|
|
563
1196
|
};
|
|
1197
|
+
return bundle;
|
|
564
1198
|
}
|
|
565
1199
|
|
|
566
1200
|
// src/index.ts
|
|
@@ -569,6 +1203,7 @@ detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
|
569
1203
|
export {
|
|
570
1204
|
ClientSideSuspense,
|
|
571
1205
|
createRoomContext,
|
|
572
|
-
shallow2 as shallow
|
|
1206
|
+
shallow2 as shallow,
|
|
1207
|
+
useRoomContextBundle
|
|
573
1208
|
};
|
|
574
1209
|
//# sourceMappingURL=index.mjs.map
|