@liveblocks/react 0.18.0-beta2 → 0.18.0-beta3
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/.built-by-link-script +1 -1
- package/index.d.ts +476 -79
- package/index.js +107 -91
- package/package.json +2 -2
package/.built-by-link-script
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ee7b9e79d3549611f64024b4247e9a862cfddd34
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import { ReactElement, ReactNode } from 'react';
|
|
3
|
-
import { JsonObject, LsonObject, LiveObject,
|
|
2
|
+
import { JsonObject, LsonObject, BaseUserMeta, LiveObject, User, Others, Json, Room, BroadcastOptions, History, Client } from '@liveblocks/client';
|
|
4
3
|
export { Json, JsonObject, shallow } from '@liveblocks/client';
|
|
5
4
|
import { ToImmutable, Resolve, RoomInitializers } from '@liveblocks/client/internal';
|
|
6
5
|
|
|
@@ -27,25 +26,32 @@ declare type Props = {
|
|
|
27
26
|
*/
|
|
28
27
|
declare function ClientSideSuspense(props: Props): ReactElement;
|
|
29
28
|
|
|
29
|
+
declare type RoomProviderProps<TPresence extends JsonObject, TStorage extends LsonObject> = Resolve<{
|
|
30
|
+
/**
|
|
31
|
+
* The id of the room you want to connect to
|
|
32
|
+
*/
|
|
33
|
+
id: string;
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
} & RoomInitializers<TPresence, TStorage>>;
|
|
30
36
|
/**
|
|
31
37
|
* For any function type, returns a similar function type, but without the
|
|
32
38
|
* first argument.
|
|
33
39
|
*/
|
|
34
40
|
declare type OmitFirstArg<F> = F extends (first: any, ...rest: infer A) => infer R ? (...args: A) => R : never;
|
|
35
|
-
declare type MutationContext<TPresence extends JsonObject, TStorage extends LsonObject> = {
|
|
41
|
+
declare type MutationContext<TPresence extends JsonObject, TStorage extends LsonObject, TUserMeta extends BaseUserMeta> = {
|
|
36
42
|
root: LiveObject<TStorage>;
|
|
43
|
+
self: User<TPresence, TUserMeta>;
|
|
44
|
+
others: Others<TPresence, TUserMeta>;
|
|
37
45
|
setMyPresence: (patch: Partial<TPresence>, options?: {
|
|
38
46
|
addToHistory: boolean;
|
|
39
47
|
}) => void;
|
|
40
48
|
};
|
|
41
|
-
declare type
|
|
49
|
+
declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends LsonObject, TUserMeta extends BaseUserMeta, TRoomEvent extends Json> = {
|
|
42
50
|
/**
|
|
43
|
-
*
|
|
51
|
+
* You normally don't need to directly interact with the RoomContext, but
|
|
52
|
+
* it can be necessary if you're building an advanced app where you need to
|
|
53
|
+
* set up a context bridge between two React renderers.
|
|
44
54
|
*/
|
|
45
|
-
id: string;
|
|
46
|
-
children: React.ReactNode;
|
|
47
|
-
} & RoomInitializers<TPresence, TStorage>>;
|
|
48
|
-
declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends LsonObject, TUserMeta extends BaseUserMeta, TRoomEvent extends Json> = {
|
|
49
55
|
RoomContext: React.Context<Room<TPresence, TStorage, TUserMeta, TRoomEvent> | null>;
|
|
50
56
|
/**
|
|
51
57
|
* Makes a Room available in the component hierarchy below.
|
|
@@ -53,6 +59,11 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
53
59
|
* That means that you can't have 2 RoomProvider with the same room id in your react tree.
|
|
54
60
|
*/
|
|
55
61
|
RoomProvider(props: RoomProviderProps<TPresence, TStorage>): JSX.Element;
|
|
62
|
+
/**
|
|
63
|
+
* Returns the Room of the nearest RoomProvider above in the React component
|
|
64
|
+
* tree.
|
|
65
|
+
*/
|
|
66
|
+
useRoom(): Room<TPresence, TStorage, TUserMeta, TRoomEvent>;
|
|
56
67
|
/**
|
|
57
68
|
* Returns a function that batches modifications made during the given function.
|
|
58
69
|
* All the modifications are sent to other clients in a single message.
|
|
@@ -147,6 +158,14 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
147
158
|
* const object = useObject("obj");
|
|
148
159
|
*/
|
|
149
160
|
useObject<TKey extends Extract<keyof TStorage, string>>(key: TKey): TStorage[TKey] | null;
|
|
161
|
+
/**
|
|
162
|
+
* Returns the mutable (!) Storage root. This hook exists for
|
|
163
|
+
* backward-compatible reasons.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* const [root] = useStorageRoot();
|
|
167
|
+
*/
|
|
168
|
+
useStorageRoot(): [root: LiveObject<TStorage> | null];
|
|
150
169
|
/**
|
|
151
170
|
* Returns your entire Liveblocks Storage as an immutable data structure.
|
|
152
171
|
*
|
|
@@ -174,6 +193,38 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
174
193
|
* those cases, you'll probably want to use a `shallow` comparison check.
|
|
175
194
|
*/
|
|
176
195
|
useStorage<T>(selector: (root: ToImmutable<TStorage>) => T, isEqual?: (prev: T, curr: T) => boolean): T | null;
|
|
196
|
+
/**
|
|
197
|
+
* Gets the current user once it is connected to the room.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* const me = useSelf();
|
|
201
|
+
* const { x, y } = me.presence.cursor;
|
|
202
|
+
*/
|
|
203
|
+
useSelf(): User<TPresence, TUserMeta> | null;
|
|
204
|
+
/**
|
|
205
|
+
* Extract arbitrary data based on the current user.
|
|
206
|
+
*
|
|
207
|
+
* The selector function will get re-evaluated any time your presence data
|
|
208
|
+
* changes.
|
|
209
|
+
*
|
|
210
|
+
* The component that uses this hook will automatically re-render if your
|
|
211
|
+
* selector function returns a different value from its previous run.
|
|
212
|
+
*
|
|
213
|
+
* By default `useSelf()` uses strict `===` to check for equality. Take extra
|
|
214
|
+
* care when returning a computed object or list, for example when you return
|
|
215
|
+
* the result of a .map() or .filter() call from the selector. In those
|
|
216
|
+
* cases, you'll probably want to use a `shallow` comparison check.
|
|
217
|
+
*
|
|
218
|
+
* Will return `null` while Liveblocks isn't connected to a room yet.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* const cursor = useSelf(me => me.presence.cursor);
|
|
222
|
+
* if (cursor !== null) {
|
|
223
|
+
* const { x, y } = cursor;
|
|
224
|
+
* }
|
|
225
|
+
*
|
|
226
|
+
*/
|
|
227
|
+
useSelf<T>(selector: (me: User<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T | null;
|
|
177
228
|
/**
|
|
178
229
|
* Returns the presence of the current user of the current room, and a function to update it.
|
|
179
230
|
* It is different from the setState function returned by the useState hook from React.
|
|
@@ -235,53 +286,52 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
235
286
|
*/
|
|
236
287
|
useOthers<T>(selector: (others: Others<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
237
288
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
289
|
+
* Returns an array of connection IDs. This matches the values you'll get by
|
|
290
|
+
* using the `useOthers()` hook.
|
|
291
|
+
*
|
|
292
|
+
* Roughly equivalent to:
|
|
293
|
+
* useOthers((others) => others.map(other => other.connectionId), shallow)
|
|
242
294
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
295
|
+
* This is useful in particular to implement efficiently rendering components
|
|
296
|
+
* for each user in the room, e.g. cursors.
|
|
245
297
|
*
|
|
246
298
|
* @example
|
|
247
|
-
* const ids =
|
|
248
|
-
* //
|
|
299
|
+
* const ids = useConnectionIds();
|
|
300
|
+
* // [2, 4, 7]
|
|
249
301
|
*/
|
|
250
|
-
|
|
302
|
+
useConnectionIds(): readonly number[];
|
|
251
303
|
/**
|
|
252
304
|
* Related to useOthers(), but optimized for selecting only "subsets" of
|
|
253
305
|
* others. This is useful for performance reasons in particular, because
|
|
254
306
|
* selecting only a subset of users also means limiting the number of
|
|
255
307
|
* re-renders that will be triggered.
|
|
256
308
|
*
|
|
257
|
-
* Note that there are two ways to use this hook, and depending on how you
|
|
258
|
-
* call it, the return value will be slightly different.
|
|
259
|
-
*
|
|
260
309
|
* @example
|
|
261
|
-
* const avatars =
|
|
310
|
+
* const avatars = useOthersWithData(user => user.info.avatar);
|
|
262
311
|
* // ^^^^^^^
|
|
263
312
|
* // { connectionId: number; data: string }[]
|
|
264
313
|
*
|
|
265
|
-
* The selector function you pass to
|
|
314
|
+
* The selector function you pass to useOthersWithData() is called an "item
|
|
266
315
|
* selector", and operates on a single user at a time. If you provide an
|
|
267
|
-
* (optional) comparison function, it will
|
|
316
|
+
* (optional) "item comparison" function, it will be used to compare each
|
|
317
|
+
* item pairwise.
|
|
268
318
|
*
|
|
269
319
|
* For example, to select multiple properties:
|
|
270
320
|
*
|
|
271
321
|
* @example
|
|
272
|
-
* const avatarsAndCursors =
|
|
322
|
+
* const avatarsAndCursors = useOthersWithData(
|
|
273
323
|
* user => [u.info.avatar, u.presence.cursor],
|
|
274
|
-
* shallow, //
|
|
324
|
+
* shallow, // 👈
|
|
275
325
|
* );
|
|
276
326
|
*/
|
|
277
|
-
|
|
327
|
+
useOthersWithData<T>(itemSelector: (other: User<TPresence, TUserMeta>) => T, itemIsEqual?: (prev: T, curr: T) => boolean): readonly {
|
|
278
328
|
readonly connectionId: number;
|
|
279
329
|
readonly data: T;
|
|
280
330
|
}[];
|
|
281
331
|
/**
|
|
282
|
-
* Given a connection ID (as obtained by using `
|
|
283
|
-
* this selector deep down in your component stack to only have the
|
|
284
|
-
* re-render if properties for this particular
|
|
332
|
+
* Given a connection ID (as obtained by using `useConnectionIds()`), you can
|
|
333
|
+
* call this selector deep down in your component stack to only have the
|
|
334
|
+
* component re-render if properties for this particular user change.
|
|
285
335
|
*
|
|
286
336
|
* @example
|
|
287
337
|
* // Returns full user and re-renders whenever anything on the user changes
|
|
@@ -289,9 +339,9 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
289
339
|
*/
|
|
290
340
|
useOther(connectionId: number): User<TPresence, TUserMeta>;
|
|
291
341
|
/**
|
|
292
|
-
* Given a connection ID (as obtained by using `
|
|
293
|
-
* this selector deep down in your component stack to only have the
|
|
294
|
-
* re-render if properties for this particular
|
|
342
|
+
* Given a connection ID (as obtained by using `useConnectionIds()`), you can
|
|
343
|
+
* call this selector deep down in your component stack to only have the
|
|
344
|
+
* component re-render if properties for this particular user change.
|
|
295
345
|
*
|
|
296
346
|
* @example
|
|
297
347
|
* // Returns only the selected values re-renders whenever that selection changes)
|
|
@@ -299,84 +349,431 @@ declare type RoomContextBundle<TPresence extends JsonObject, TStorage extends Ls
|
|
|
299
349
|
*/
|
|
300
350
|
useOther<T>(connectionId: number, selector: (other: User<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
301
351
|
/**
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*/
|
|
305
|
-
useRoom(): Room<TPresence, TStorage, TUserMeta, TRoomEvent>;
|
|
306
|
-
/**
|
|
307
|
-
* Gets the current user once it is connected to the room.
|
|
352
|
+
* useUpdateMyPresence is similar to useMyPresence but it only returns the function to update the current user presence.
|
|
353
|
+
* If you don't use the current user presence in your component, but you need to update it (e.g. live cursor), it's better to use useUpdateMyPresence to avoid unnecessary renders.
|
|
308
354
|
*
|
|
309
355
|
* @example
|
|
310
|
-
* const
|
|
311
|
-
*
|
|
356
|
+
* const updateMyPresence = useUpdateMyPresence();
|
|
357
|
+
* updateMyPresence({ x: 0 });
|
|
358
|
+
* updateMyPresence({ y: 0 });
|
|
359
|
+
*
|
|
360
|
+
* // At the next render, the presence of the current user will be equal to "{ x: 0, y: 0 }"
|
|
312
361
|
*/
|
|
313
|
-
|
|
362
|
+
useUpdateMyPresence(): (patch: Partial<TPresence>, options?: {
|
|
363
|
+
addToHistory: boolean;
|
|
364
|
+
}) => void;
|
|
314
365
|
/**
|
|
315
|
-
*
|
|
366
|
+
* Create a callback function that can be called to mutate Liveblocks state.
|
|
316
367
|
*
|
|
317
|
-
* The
|
|
318
|
-
*
|
|
368
|
+
* The first argument that gets passed into your callback will be a "mutation
|
|
369
|
+
* context", which exposes the following:
|
|
319
370
|
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
371
|
+
* - `root` - The mutable Storage root.
|
|
372
|
+
* You can normal mutation on Live structures with this, for
|
|
373
|
+
* example: root.get('layers').get('layer1').set('fill', 'red')
|
|
322
374
|
*
|
|
323
|
-
*
|
|
324
|
-
* care when returning a computed object or list, for example when you return
|
|
325
|
-
* the result of a .map() or .filter() call from the selector. In those
|
|
326
|
-
* cases, you'll probably want to use a `shallow` comparison check.
|
|
375
|
+
* - `setMyPresence` - Call this with a new (partial) Presence value.
|
|
327
376
|
*
|
|
328
|
-
*
|
|
377
|
+
* - `self` - A read-only version of the latest self, if you need it to
|
|
378
|
+
* compute the next state.
|
|
329
379
|
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
* if (cursor !== null) {
|
|
333
|
-
* const { x, y } = cursor;
|
|
334
|
-
* }
|
|
380
|
+
* - `others` - A read-only version of the latest others list, if you need
|
|
381
|
+
* it to compute the next state.
|
|
335
382
|
*
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Returns the mutable (!) Storage root. This hook exists for
|
|
340
|
-
* backward-compatible reasons.
|
|
383
|
+
* useMutation is like React's useCallback, except that the first argument
|
|
384
|
+
* that gets passed into your callback will be a "mutation context".
|
|
341
385
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*/
|
|
345
|
-
useStorageRoot(): [root: LiveObject<TStorage> | null];
|
|
346
|
-
/**
|
|
347
|
-
* useUpdateMyPresence is similar to useMyPresence but it only returns the function to update the current user presence.
|
|
348
|
-
* If you don't use the current user presence in your component, but you need to update it (e.g. live cursor), it's better to use useUpdateMyPresence to avoid unnecessary renders.
|
|
386
|
+
* If you want get access to the immutable root somewhere in your mutation,
|
|
387
|
+
* you can use `root.ToImmutable()`.
|
|
349
388
|
*
|
|
350
389
|
* @example
|
|
351
|
-
* const
|
|
352
|
-
*
|
|
353
|
-
*
|
|
390
|
+
* const fillLayers = useMutation(
|
|
391
|
+
* ({ root }, color: Color) => {
|
|
392
|
+
* ...
|
|
393
|
+
* },
|
|
394
|
+
* [],
|
|
395
|
+
* );
|
|
354
396
|
*
|
|
355
|
-
*
|
|
397
|
+
* fillLayers('red');
|
|
398
|
+
*
|
|
399
|
+
* const deleteLayers = useMutation(
|
|
400
|
+
* ({ root }) => {
|
|
401
|
+
* ...
|
|
402
|
+
* },
|
|
403
|
+
* [],
|
|
404
|
+
* );
|
|
405
|
+
*
|
|
406
|
+
* deleteLayers();
|
|
356
407
|
*/
|
|
357
|
-
|
|
358
|
-
addToHistory: boolean;
|
|
359
|
-
}) => void;
|
|
360
|
-
useMutation<F extends (context: MutationContext<TPresence, TStorage>, ...args: any[]) => any>(callback: F, deps?: unknown[]): OmitFirstArg<F>;
|
|
408
|
+
useMutation<F extends (context: MutationContext<TPresence, TStorage, TUserMeta>, ...args: any[]) => any>(callback: F, deps: readonly unknown[]): OmitFirstArg<F>;
|
|
361
409
|
suspense: {
|
|
410
|
+
/**
|
|
411
|
+
* You normally don't need to directly interact with the RoomContext, but
|
|
412
|
+
* it can be necessary if you're building an advanced app where you need to
|
|
413
|
+
* set up a context bridge between two React renderers.
|
|
414
|
+
*/
|
|
415
|
+
RoomContext: React.Context<Room<TPresence, TStorage, TUserMeta, TRoomEvent> | null>;
|
|
416
|
+
/**
|
|
417
|
+
* Makes a Room available in the component hierarchy below.
|
|
418
|
+
* When this component is unmounted, the current user leave the room.
|
|
419
|
+
* That means that you can't have 2 RoomProvider with the same room id in your react tree.
|
|
420
|
+
*/
|
|
421
|
+
RoomProvider(props: RoomProviderProps<TPresence, TStorage>): JSX.Element;
|
|
422
|
+
/**
|
|
423
|
+
* Returns the Room of the nearest RoomProvider above in the React component
|
|
424
|
+
* tree.
|
|
425
|
+
*/
|
|
426
|
+
useRoom(): Room<TPresence, TStorage, TUserMeta, TRoomEvent>;
|
|
427
|
+
/**
|
|
428
|
+
* Returns a function that batches modifications made during the given function.
|
|
429
|
+
* All the modifications are sent to other clients in a single message.
|
|
430
|
+
* All the modifications are merged in a single history item (undo/redo).
|
|
431
|
+
* All the subscribers are called only after the batch is over.
|
|
432
|
+
*/
|
|
433
|
+
useBatch<T>(): (callback: () => T) => T;
|
|
434
|
+
/**
|
|
435
|
+
* Returns a callback that lets you broadcast custom events to other users in the room
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* const broadcast = useBroadcastEvent();
|
|
439
|
+
*
|
|
440
|
+
* broadcast({ type: "CUSTOM_EVENT", data: { x: 0, y: 0 } });
|
|
441
|
+
*/
|
|
442
|
+
useBroadcastEvent(): (event: TRoomEvent, options?: BroadcastOptions) => void;
|
|
443
|
+
/**
|
|
444
|
+
* useErrorListener is a react hook that lets you react to potential room connection errors.
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* useErrorListener(er => {
|
|
448
|
+
* console.error(er);
|
|
449
|
+
* })
|
|
450
|
+
*/
|
|
451
|
+
useErrorListener(callback: (err: Error) => void): void;
|
|
452
|
+
/**
|
|
453
|
+
* useEventListener is a react hook that lets you react to event broadcasted by other users in the room.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* useEventListener(({ connectionId, event }) => {
|
|
457
|
+
* if (event.type === "CUSTOM_EVENT") {
|
|
458
|
+
* // Do something
|
|
459
|
+
* }
|
|
460
|
+
* });
|
|
461
|
+
*/
|
|
462
|
+
useEventListener(callback: (eventData: {
|
|
463
|
+
connectionId: number;
|
|
464
|
+
event: TRoomEvent;
|
|
465
|
+
}) => void): void;
|
|
466
|
+
/**
|
|
467
|
+
* Returns the room.history
|
|
468
|
+
*/
|
|
469
|
+
useHistory(): History;
|
|
470
|
+
/**
|
|
471
|
+
* Returns a function that undoes the last operation executed by the current client.
|
|
472
|
+
* It does not impact operations made by other clients.
|
|
473
|
+
*/
|
|
474
|
+
useUndo(): () => void;
|
|
475
|
+
/**
|
|
476
|
+
* Returns a function that redoes the last operation executed by the current client.
|
|
477
|
+
* It does not impact operations made by other clients.
|
|
478
|
+
*/
|
|
479
|
+
useRedo(): () => void;
|
|
480
|
+
/**
|
|
481
|
+
* Returns whether there are any operations to undo.
|
|
482
|
+
*/
|
|
483
|
+
useCanUndo(): boolean;
|
|
484
|
+
/**
|
|
485
|
+
* Returns whether there are any operations to redo.
|
|
486
|
+
*/
|
|
487
|
+
useCanRedo(): boolean;
|
|
488
|
+
/**
|
|
489
|
+
* Returns the mutable (!) Storage root. This hook exists for
|
|
490
|
+
* backward-compatible reasons.
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* const [root] = useStorageRoot();
|
|
494
|
+
*/
|
|
495
|
+
useStorageRoot(): [root: LiveObject<TStorage> | null];
|
|
496
|
+
/**
|
|
497
|
+
* Returns your entire Liveblocks Storage as an immutable data structure.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* const root = useStorage();
|
|
501
|
+
*/
|
|
362
502
|
useStorage(): ToImmutable<TStorage>;
|
|
503
|
+
/**
|
|
504
|
+
* Extract arbitrary data from the Liveblocks Storage state, using an
|
|
505
|
+
* arbitrary selector function.
|
|
506
|
+
*
|
|
507
|
+
* The selector function will get re-evaluated any time something changes in
|
|
508
|
+
* Storage. The value returned by your selector function will also be the
|
|
509
|
+
* value returned by the hook.
|
|
510
|
+
*
|
|
511
|
+
* The `root` value that gets passed to your selector function is
|
|
512
|
+
* a immutable/readonly version of your Liveblocks storage root.
|
|
513
|
+
*
|
|
514
|
+
* The component that uses this hook will automatically re-render if the
|
|
515
|
+
* returned value changes.
|
|
516
|
+
*
|
|
517
|
+
* By default `useStorage()` uses strict `===` to check for equality. Take
|
|
518
|
+
* extra care when returning a computed object or list, for example when you
|
|
519
|
+
* return the result of a .map() or .filter() call from the selector. In
|
|
520
|
+
* those cases, you'll probably want to use a `shallow` comparison check.
|
|
521
|
+
*/
|
|
363
522
|
useStorage<T>(selector: (root: ToImmutable<TStorage>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
523
|
+
/**
|
|
524
|
+
* Gets the current user once it is connected to the room.
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* const me = useSelf();
|
|
528
|
+
* const { x, y } = me.presence.cursor;
|
|
529
|
+
*/
|
|
364
530
|
useSelf(): User<TPresence, TUserMeta>;
|
|
531
|
+
/**
|
|
532
|
+
* Extract arbitrary data based on the current user.
|
|
533
|
+
*
|
|
534
|
+
* The selector function will get re-evaluated any time your presence data
|
|
535
|
+
* changes.
|
|
536
|
+
*
|
|
537
|
+
* The component that uses this hook will automatically re-render if your
|
|
538
|
+
* selector function returns a different value from its previous run.
|
|
539
|
+
*
|
|
540
|
+
* By default `useSelf()` uses strict `===` to check for equality. Take extra
|
|
541
|
+
* care when returning a computed object or list, for example when you return
|
|
542
|
+
* the result of a .map() or .filter() call from the selector. In those
|
|
543
|
+
* cases, you'll probably want to use a `shallow` comparison check.
|
|
544
|
+
*
|
|
545
|
+
* Will return `null` while Liveblocks isn't connected to a room yet.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* const cursor = useSelf(me => me.presence.cursor);
|
|
549
|
+
* if (cursor !== null) {
|
|
550
|
+
* const { x, y } = cursor;
|
|
551
|
+
* }
|
|
552
|
+
*
|
|
553
|
+
*/
|
|
365
554
|
useSelf<T>(selector: (me: User<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
555
|
+
/**
|
|
556
|
+
* Returns the presence of the current user of the current room, and a function to update it.
|
|
557
|
+
* It is different from the setState function returned by the useState hook from React.
|
|
558
|
+
* You don't need to pass the full presence object to update it.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* const [myPresence, updateMyPresence] = useMyPresence();
|
|
562
|
+
* updateMyPresence({ x: 0 });
|
|
563
|
+
* updateMyPresence({ y: 0 });
|
|
564
|
+
*
|
|
565
|
+
* // At the next render, "myPresence" will be equal to "{ x: 0, y: 0 }"
|
|
566
|
+
*/
|
|
567
|
+
useMyPresence(): [
|
|
568
|
+
TPresence,
|
|
569
|
+
(patch: Partial<TPresence>, options?: {
|
|
570
|
+
addToHistory: boolean;
|
|
571
|
+
}) => void
|
|
572
|
+
];
|
|
573
|
+
/**
|
|
574
|
+
* Returns an object that lets you get information about all the users
|
|
575
|
+
* currently connected in the room.
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* const others = useOthers();
|
|
579
|
+
*
|
|
580
|
+
* // Example to map all cursors in JSX
|
|
581
|
+
* return (
|
|
582
|
+
* <>
|
|
583
|
+
* {others.map((user) => {
|
|
584
|
+
* if (user.presence.cursor == null) {
|
|
585
|
+
* return null;
|
|
586
|
+
* }
|
|
587
|
+
* return <Cursor key={user.connectionId} cursor={user.presence.cursor} />
|
|
588
|
+
* })}
|
|
589
|
+
* </>
|
|
590
|
+
* )
|
|
591
|
+
*/
|
|
366
592
|
useOthers(): Others<TPresence, TUserMeta>;
|
|
593
|
+
/**
|
|
594
|
+
* Extract arbitrary data based on all the users currently connected in the
|
|
595
|
+
* room (except yourself).
|
|
596
|
+
*
|
|
597
|
+
* The selector function will get re-evaluated any time a user enters or
|
|
598
|
+
* leaves the room, as well as whenever their presence data changes.
|
|
599
|
+
*
|
|
600
|
+
* The component that uses this hook will automatically re-render if your
|
|
601
|
+
* selector function returns a different value from its previous run.
|
|
602
|
+
*
|
|
603
|
+
* By default `useOthers()` uses strict `===` to check for equality. Take
|
|
604
|
+
* extra care when returning a computed object or list, for example when you
|
|
605
|
+
* return the result of a .map() or .filter() call from the selector. In
|
|
606
|
+
* those cases, you'll probably want to use a `shallow` comparison check.
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* const avatars = useOthers(users => users.map(u => u.info.avatar), shallow);
|
|
610
|
+
* const cursors = useOthers(users => users.map(u => u.presence.cursor), shallow);
|
|
611
|
+
* const someoneIsTyping = useOthers(users => users.some(u => u.presence.isTyping));
|
|
612
|
+
*
|
|
613
|
+
*/
|
|
367
614
|
useOthers<T>(selector: (others: Others<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
368
|
-
|
|
369
|
-
|
|
615
|
+
/**
|
|
616
|
+
* Returns an array of connection IDs. This matches the values you'll get by
|
|
617
|
+
* using the `useOthers()` hook.
|
|
618
|
+
*
|
|
619
|
+
* Roughly equivalent to:
|
|
620
|
+
* useOthers((others) => others.map(other => other.connectionId), shallow)
|
|
621
|
+
*
|
|
622
|
+
* This is useful in particular to implement efficiently rendering components
|
|
623
|
+
* for each user in the room, e.g. cursors.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* const ids = useConnectionIds();
|
|
627
|
+
* // [2, 4, 7]
|
|
628
|
+
*/
|
|
629
|
+
useConnectionIds(): readonly number[];
|
|
630
|
+
/**
|
|
631
|
+
* Related to useOthers(), but optimized for selecting only "subsets" of
|
|
632
|
+
* others. This is useful for performance reasons in particular, because
|
|
633
|
+
* selecting only a subset of users also means limiting the number of
|
|
634
|
+
* re-renders that will be triggered.
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* const avatars = useOthersWithData(user => user.info.avatar);
|
|
638
|
+
* // ^^^^^^^
|
|
639
|
+
* // { connectionId: number; data: string }[]
|
|
640
|
+
*
|
|
641
|
+
* The selector function you pass to useOthersWithData() is called an "item
|
|
642
|
+
* selector", and operates on a single user at a time. If you provide an
|
|
643
|
+
* (optional) "item comparison" function, it will be used to compare each
|
|
644
|
+
* item pairwise.
|
|
645
|
+
*
|
|
646
|
+
* For example, to select multiple properties:
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* const avatarsAndCursors = useOthersWithData(
|
|
650
|
+
* user => [u.info.avatar, u.presence.cursor],
|
|
651
|
+
* shallow, // 👈
|
|
652
|
+
* );
|
|
653
|
+
*/
|
|
654
|
+
useOthersWithData<T>(itemSelector: (other: User<TPresence, TUserMeta>) => T, itemIsEqual?: (prev: T, curr: T) => boolean): readonly {
|
|
370
655
|
readonly connectionId: number;
|
|
371
656
|
readonly data: T;
|
|
372
657
|
}[];
|
|
658
|
+
/**
|
|
659
|
+
* Given a connection ID (as obtained by using `useConnectionIds()`), you
|
|
660
|
+
* can call this selector deep down in your component stack to only have
|
|
661
|
+
* the component re-render if properties for this particular user change.
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* // Returns full user and re-renders whenever anything on the user changes
|
|
665
|
+
* const secondUser = useOther(2);
|
|
666
|
+
*/
|
|
373
667
|
useOther(connectionId: number): User<TPresence, TUserMeta>;
|
|
668
|
+
/**
|
|
669
|
+
* Given a connection ID (as obtained by using `useConnectionIds()`), you
|
|
670
|
+
* can call this selector deep down in your component stack to only have
|
|
671
|
+
* the component re-render if properties for this particular user change.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* // Returns only the selected values re-renders whenever that selection changes)
|
|
675
|
+
* const { x, y } = useOther(2, user => user.presence.cursor);
|
|
676
|
+
*/
|
|
374
677
|
useOther<T>(connectionId: number, selector: (other: User<TPresence, TUserMeta>) => T, isEqual?: (prev: T, curr: T) => boolean): T;
|
|
678
|
+
/**
|
|
679
|
+
* useUpdateMyPresence is similar to useMyPresence but it only returns the function to update the current user presence.
|
|
680
|
+
* If you don't use the current user presence in your component, but you need to update it (e.g. live cursor), it's better to use useUpdateMyPresence to avoid unnecessary renders.
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* const updateMyPresence = useUpdateMyPresence();
|
|
684
|
+
* updateMyPresence({ x: 0 });
|
|
685
|
+
* updateMyPresence({ y: 0 });
|
|
686
|
+
*
|
|
687
|
+
* // At the next render, the presence of the current user will be equal to "{ x: 0, y: 0 }"
|
|
688
|
+
*/
|
|
689
|
+
useUpdateMyPresence(): (patch: Partial<TPresence>, options?: {
|
|
690
|
+
addToHistory: boolean;
|
|
691
|
+
}) => void;
|
|
692
|
+
/**
|
|
693
|
+
* Create a callback function that can be called to mutate Liveblocks
|
|
694
|
+
* state.
|
|
695
|
+
*
|
|
696
|
+
* The first argument that gets passed into your callback will be
|
|
697
|
+
* a "mutation context", which exposes the following:
|
|
698
|
+
*
|
|
699
|
+
* - `root` - The mutable Storage root.
|
|
700
|
+
* You can normal mutation on Live structures with this, for
|
|
701
|
+
* example: root.get('layers').get('layer1').set('fill',
|
|
702
|
+
* 'red')
|
|
703
|
+
*
|
|
704
|
+
* - `setMyPresence` - Call this with a new (partial) Presence value.
|
|
705
|
+
*
|
|
706
|
+
* - `self` - A read-only version of the latest self, if you need it to
|
|
707
|
+
* compute the next state.
|
|
708
|
+
*
|
|
709
|
+
* - `others` - A read-only version of the latest others list, if you
|
|
710
|
+
* need it to compute the next state.
|
|
711
|
+
*
|
|
712
|
+
* useMutation is like React's useCallback, except that the first argument
|
|
713
|
+
* that gets passed into your callback will be a "mutation context".
|
|
714
|
+
*
|
|
715
|
+
* If you want get access to the immutable root somewhere in your mutation,
|
|
716
|
+
* you can use `root.ToImmutable()`.
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* const fillLayers = useMutation(
|
|
720
|
+
* ({ root }, color: Color) => {
|
|
721
|
+
* ...
|
|
722
|
+
* },
|
|
723
|
+
* [],
|
|
724
|
+
* );
|
|
725
|
+
*
|
|
726
|
+
* fillLayers('red');
|
|
727
|
+
*
|
|
728
|
+
* const deleteLayers = useMutation(
|
|
729
|
+
* ({ root }) => {
|
|
730
|
+
* ...
|
|
731
|
+
* },
|
|
732
|
+
* [],
|
|
733
|
+
* );
|
|
734
|
+
*
|
|
735
|
+
* deleteLayers();
|
|
736
|
+
*/
|
|
737
|
+
useMutation<F extends (context: MutationContext<TPresence, TStorage, TUserMeta>, ...args: any[]) => any>(callback: F, deps: readonly unknown[]): OmitFirstArg<F>;
|
|
738
|
+
/**
|
|
739
|
+
* Returns the LiveList associated with the provided key. The hook triggers
|
|
740
|
+
* a re-render if the LiveList is updated, however it does not triggers
|
|
741
|
+
* a re-render if a nested CRDT is updated.
|
|
742
|
+
*
|
|
743
|
+
* @param key The storage key associated with the LiveList
|
|
744
|
+
* @returns null while the storage is loading, otherwise, returns the LiveList associated to the storage
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* const animals = useList("animals"); // e.g. [] or ["🦁", "🐍", "🦍"]
|
|
748
|
+
*/
|
|
375
749
|
useList<TKey extends Extract<keyof TStorage, string>>(key: TKey): TStorage[TKey];
|
|
750
|
+
/**
|
|
751
|
+
* Returns the LiveMap associated with the provided key. If the LiveMap
|
|
752
|
+
* does not exist, a new empty LiveMap will be created. The hook triggers
|
|
753
|
+
* a re-render if the LiveMap is updated, however it does not triggers
|
|
754
|
+
* a re-render if a nested CRDT is updated.
|
|
755
|
+
*
|
|
756
|
+
* @param key The storage key associated with the LiveMap
|
|
757
|
+
* @returns null while the storage is loading, otherwise, returns the LiveMap associated to the storage
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* const shapesById = useMap("shapes");
|
|
761
|
+
*/
|
|
376
762
|
useMap<TKey extends Extract<keyof TStorage, string>>(key: TKey): TStorage[TKey];
|
|
763
|
+
/**
|
|
764
|
+
* Returns the LiveObject associated with the provided key.
|
|
765
|
+
* The hook triggers a re-render if the LiveObject is updated, however it does not triggers a re-render if a nested CRDT is updated.
|
|
766
|
+
*
|
|
767
|
+
* @param key The storage key associated with the LiveObject
|
|
768
|
+
* @returns null while the storage is loading, otherwise, returns the LveObject associated to the storage
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* const object = useObject("obj");
|
|
772
|
+
*/
|
|
377
773
|
useObject<TKey extends Extract<keyof TStorage, string>>(key: TKey): TStorage[TKey];
|
|
378
774
|
};
|
|
379
775
|
};
|
|
776
|
+
|
|
380
777
|
declare function createRoomContext<TPresence extends JsonObject, TStorage extends LsonObject = LsonObject, TUserMeta extends BaseUserMeta = BaseUserMeta, TRoomEvent extends Json = never>(client: Client): RoomContextBundle<TPresence, TStorage, TUserMeta, TRoomEvent>;
|
|
381
778
|
|
|
382
779
|
export { ClientSideSuspense, MutationContext, createRoomContext };
|
package/index.js
CHANGED
|
@@ -33,16 +33,46 @@ function useInitial(value) {
|
|
|
33
33
|
var noop = () => {
|
|
34
34
|
};
|
|
35
35
|
var identity = (x) => x;
|
|
36
|
+
function useSyncExternalStore(s, g, gg) {
|
|
37
|
+
return _withselector.useSyncExternalStoreWithSelector.call(void 0, s, g, gg, identity);
|
|
38
|
+
}
|
|
36
39
|
var EMPTY_OTHERS = _internal.asArrayWithLegacyMethods.call(void 0, []);
|
|
37
40
|
function getEmptyOthers() {
|
|
38
41
|
return EMPTY_OTHERS;
|
|
39
42
|
}
|
|
43
|
+
function makeMutationContext(room) {
|
|
44
|
+
const errmsg = "This mutation cannot be used until connected to the Liveblocks room";
|
|
45
|
+
return {
|
|
46
|
+
get root() {
|
|
47
|
+
const root = room.getStorageSnapshot();
|
|
48
|
+
if (root === null) {
|
|
49
|
+
throw new Error(errmsg);
|
|
50
|
+
}
|
|
51
|
+
return root;
|
|
52
|
+
},
|
|
53
|
+
get self() {
|
|
54
|
+
const self = room.getSelf();
|
|
55
|
+
if (self === null) {
|
|
56
|
+
throw new Error(errmsg);
|
|
57
|
+
}
|
|
58
|
+
return self;
|
|
59
|
+
},
|
|
60
|
+
get others() {
|
|
61
|
+
const others = room.getOthers();
|
|
62
|
+
if (!room.isSelfAware()) {
|
|
63
|
+
throw new Error(errmsg);
|
|
64
|
+
}
|
|
65
|
+
return others;
|
|
66
|
+
},
|
|
67
|
+
setMyPresence: room.updatePresence
|
|
68
|
+
};
|
|
69
|
+
}
|
|
40
70
|
function createRoomContext(client) {
|
|
41
71
|
const RoomContext = React2.createContext(null);
|
|
42
72
|
function RoomProvider(props) {
|
|
43
73
|
const { id: roomId, initialPresence, initialStorage } = props;
|
|
44
74
|
if (process.env.NODE_ENV !== "production") {
|
|
45
|
-
if (roomId
|
|
75
|
+
if (!roomId) {
|
|
46
76
|
throw new Error(
|
|
47
77
|
"RoomProvider id property is required. For more information: https://liveblocks.io/docs/errors/liveblocks-react/RoomProvider-id-property-is-required"
|
|
48
78
|
);
|
|
@@ -83,17 +113,17 @@ function createRoomContext(client) {
|
|
|
83
113
|
}
|
|
84
114
|
function useRoom() {
|
|
85
115
|
const room = React2.useContext(RoomContext);
|
|
86
|
-
if (room
|
|
116
|
+
if (room === null) {
|
|
87
117
|
throw new Error("RoomProvider is missing from the react tree");
|
|
88
118
|
}
|
|
89
119
|
return room;
|
|
90
120
|
}
|
|
91
121
|
function useMyPresence() {
|
|
92
122
|
const room = useRoom();
|
|
93
|
-
const
|
|
94
|
-
const
|
|
123
|
+
const subscribe = room.events.me.subscribe;
|
|
124
|
+
const getSnapshot = room.getPresence;
|
|
125
|
+
const presence = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
95
126
|
const setPresence = room.updatePresence;
|
|
96
|
-
React2.useEffect(() => room.events.me.subscribe(rerender), [room, rerender]);
|
|
97
127
|
return [presence, setPresence];
|
|
98
128
|
}
|
|
99
129
|
function useUpdateMyPresence() {
|
|
@@ -112,31 +142,28 @@ function createRoomContext(client) {
|
|
|
112
142
|
isEqual
|
|
113
143
|
);
|
|
114
144
|
}
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
(
|
|
130
|
-
const
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
138
|
-
return _useOthers(wrappedSelector, wrappedIsEqual);
|
|
139
|
-
}
|
|
145
|
+
function useConnectionIds() {
|
|
146
|
+
return useOthers(connectionIdSelector, _client.shallow);
|
|
147
|
+
}
|
|
148
|
+
function useOthersWithData(itemSelector, itemIsEqual) {
|
|
149
|
+
const wrappedSelector = React2.useCallback(
|
|
150
|
+
(others) => others.map((other) => ({
|
|
151
|
+
connectionId: other.connectionId,
|
|
152
|
+
data: itemSelector(other)
|
|
153
|
+
})),
|
|
154
|
+
[itemSelector]
|
|
155
|
+
);
|
|
156
|
+
const wrappedIsEqual = React2.useCallback(
|
|
157
|
+
(a, b) => {
|
|
158
|
+
const eq = itemIsEqual != null ? itemIsEqual : Object.is;
|
|
159
|
+
return a.length === b.length && a.every((atuple, index) => {
|
|
160
|
+
const btuple = b[index];
|
|
161
|
+
return atuple.connectionId === btuple.connectionId && eq(atuple.data, btuple.data);
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
[itemIsEqual]
|
|
165
|
+
);
|
|
166
|
+
return useOthers(wrappedSelector, wrappedIsEqual);
|
|
140
167
|
}
|
|
141
168
|
const sentinel = Symbol();
|
|
142
169
|
function useOther(connectionId, selector, isEqual) {
|
|
@@ -249,13 +276,7 @@ function createRoomContext(client) {
|
|
|
249
276
|
const subscribe = room.events.storageDidLoad.subscribeOnce;
|
|
250
277
|
const getSnapshot = room.getStorageSnapshot;
|
|
251
278
|
const getServerSnapshot = React2.useCallback(() => null, []);
|
|
252
|
-
|
|
253
|
-
return _withselector.useSyncExternalStoreWithSelector.call(void 0,
|
|
254
|
-
subscribe,
|
|
255
|
-
getSnapshot,
|
|
256
|
-
getServerSnapshot,
|
|
257
|
-
selector
|
|
258
|
-
);
|
|
279
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
259
280
|
}
|
|
260
281
|
function useStorageRoot() {
|
|
261
282
|
return [useMutableStorageRoot()];
|
|
@@ -271,21 +292,15 @@ function createRoomContext(client) {
|
|
|
271
292
|
}
|
|
272
293
|
function useCanUndo() {
|
|
273
294
|
const room = useRoom();
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
[room]
|
|
278
|
-
);
|
|
279
|
-
return canUndo;
|
|
295
|
+
const subscribe = room.events.history.subscribe;
|
|
296
|
+
const canUndo = room.history.canUndo;
|
|
297
|
+
return useSyncExternalStore(subscribe, canUndo, canUndo);
|
|
280
298
|
}
|
|
281
299
|
function useCanRedo() {
|
|
282
300
|
const room = useRoom();
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
[room]
|
|
287
|
-
);
|
|
288
|
-
return canRedo;
|
|
301
|
+
const subscribe = room.events.history.subscribe;
|
|
302
|
+
const canRedo = room.history.canRedo;
|
|
303
|
+
return useSyncExternalStore(subscribe, canRedo, canRedo);
|
|
289
304
|
}
|
|
290
305
|
function useBatch() {
|
|
291
306
|
return useRoom().batch;
|
|
@@ -295,7 +310,7 @@ function createRoomContext(client) {
|
|
|
295
310
|
const root = useMutableStorageRoot();
|
|
296
311
|
const rerender = useRerender();
|
|
297
312
|
React2.useEffect(() => {
|
|
298
|
-
if (root
|
|
313
|
+
if (root === null) {
|
|
299
314
|
return;
|
|
300
315
|
}
|
|
301
316
|
let liveValue = root.get(key);
|
|
@@ -325,7 +340,7 @@ function createRoomContext(client) {
|
|
|
325
340
|
unsubscribeCrdt();
|
|
326
341
|
};
|
|
327
342
|
}, [root, room, key, rerender]);
|
|
328
|
-
if (root
|
|
343
|
+
if (root === null) {
|
|
329
344
|
return null;
|
|
330
345
|
} else {
|
|
331
346
|
return root.get(key);
|
|
@@ -390,27 +405,13 @@ function createRoomContext(client) {
|
|
|
390
405
|
}
|
|
391
406
|
function useMutation(callback, deps) {
|
|
392
407
|
const room = useRoom();
|
|
393
|
-
const root = useMutableStorageRoot();
|
|
394
|
-
const setMyPresence = room.updatePresence;
|
|
395
408
|
return React2.useMemo(
|
|
396
409
|
() => {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
setMyPresence
|
|
401
|
-
};
|
|
402
|
-
return (...args) => room.batch(
|
|
403
|
-
() => callback(mutationCtx, ...args)
|
|
404
|
-
);
|
|
405
|
-
} else {
|
|
406
|
-
return () => {
|
|
407
|
-
throw new Error(
|
|
408
|
-
"Mutation cannot be called while Liveblocks Storage has not loaded yet"
|
|
409
|
-
);
|
|
410
|
-
};
|
|
411
|
-
}
|
|
410
|
+
return (...args) => room.batch(
|
|
411
|
+
() => callback(makeMutationContext(room), ...args)
|
|
412
|
+
);
|
|
412
413
|
},
|
|
413
|
-
|
|
414
|
+
[room, ...deps]
|
|
414
415
|
);
|
|
415
416
|
}
|
|
416
417
|
function useStorageSuspense(selector, isEqual) {
|
|
@@ -434,12 +435,9 @@ function createRoomContext(client) {
|
|
|
434
435
|
isEqual
|
|
435
436
|
);
|
|
436
437
|
}
|
|
437
|
-
function
|
|
438
|
+
function useOthersWithDataSuspense(itemSelector, itemIsEqual) {
|
|
438
439
|
useSuspendUntilPresenceLoaded();
|
|
439
|
-
return
|
|
440
|
-
itemSelector,
|
|
441
|
-
isEqual
|
|
442
|
-
);
|
|
440
|
+
return useOthersWithData(itemSelector, itemIsEqual);
|
|
443
441
|
}
|
|
444
442
|
function useOtherSuspense(connectionId, selector, isEqual) {
|
|
445
443
|
useSuspendUntilPresenceLoaded();
|
|
@@ -454,39 +452,57 @@ function createRoomContext(client) {
|
|
|
454
452
|
return useLegacyKey(key);
|
|
455
453
|
}
|
|
456
454
|
return {
|
|
455
|
+
RoomContext,
|
|
457
456
|
RoomProvider,
|
|
457
|
+
useRoom,
|
|
458
458
|
useBatch,
|
|
459
459
|
useBroadcastEvent,
|
|
460
|
-
useCanRedo,
|
|
461
|
-
useCanUndo,
|
|
462
460
|
useErrorListener,
|
|
463
461
|
useEventListener,
|
|
464
462
|
useHistory,
|
|
465
|
-
|
|
466
|
-
useOthers,
|
|
467
|
-
useOtherIds,
|
|
468
|
-
useOther,
|
|
463
|
+
useUndo,
|
|
469
464
|
useRedo,
|
|
470
|
-
|
|
471
|
-
|
|
465
|
+
useCanRedo,
|
|
466
|
+
useCanUndo,
|
|
467
|
+
useList: useLegacyKey,
|
|
468
|
+
useMap: useLegacyKey,
|
|
469
|
+
useObject: useLegacyKey,
|
|
472
470
|
useStorageRoot,
|
|
473
471
|
useStorage,
|
|
474
|
-
|
|
472
|
+
useSelf,
|
|
473
|
+
useMyPresence,
|
|
475
474
|
useUpdateMyPresence,
|
|
475
|
+
useOthers,
|
|
476
|
+
useOthersWithData,
|
|
477
|
+
useConnectionIds,
|
|
478
|
+
useOther,
|
|
476
479
|
useMutation,
|
|
477
|
-
useList: useLegacyKey,
|
|
478
|
-
useMap: useLegacyKey,
|
|
479
|
-
useObject: useLegacyKey,
|
|
480
|
-
RoomContext,
|
|
481
480
|
suspense: {
|
|
481
|
+
RoomContext,
|
|
482
|
+
RoomProvider,
|
|
483
|
+
useRoom,
|
|
484
|
+
useBatch,
|
|
485
|
+
useBroadcastEvent,
|
|
486
|
+
useErrorListener,
|
|
487
|
+
useEventListener,
|
|
488
|
+
useHistory,
|
|
489
|
+
useUndo,
|
|
490
|
+
useRedo,
|
|
491
|
+
useCanRedo,
|
|
492
|
+
useCanUndo,
|
|
493
|
+
useList: useLegacyKeySuspense,
|
|
494
|
+
useMap: useLegacyKeySuspense,
|
|
495
|
+
useObject: useLegacyKeySuspense,
|
|
496
|
+
useStorageRoot,
|
|
482
497
|
useStorage: useStorageSuspense,
|
|
483
498
|
useSelf: useSelfSuspense,
|
|
499
|
+
useMyPresence,
|
|
500
|
+
useUpdateMyPresence,
|
|
484
501
|
useOthers: useOthersSuspense,
|
|
485
|
-
|
|
502
|
+
useOthersWithData: useOthersWithDataSuspense,
|
|
503
|
+
useConnectionIds,
|
|
486
504
|
useOther: useOtherSuspense,
|
|
487
|
-
|
|
488
|
-
useMap: useLegacyKeySuspense,
|
|
489
|
-
useObject: useLegacyKeySuspense
|
|
505
|
+
useMutation
|
|
490
506
|
}
|
|
491
507
|
};
|
|
492
508
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liveblocks/react",
|
|
3
|
-
"version": "0.18.0-
|
|
3
|
+
"version": "0.18.0-beta3",
|
|
4
4
|
"description": "A set of React hooks and providers to use Liveblocks declaratively.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.mjs",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"use-sync-external-store": "^1.2.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@liveblocks/client": "0.18.0-
|
|
30
|
+
"@liveblocks/client": "0.18.0-beta3",
|
|
31
31
|
"react": "^16.14.0 || ^17 || ^18"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|