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