@pluv/react 0.14.1 → 0.16.0

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.
@@ -1,8 +1,8 @@
1
1
  import type { PluvClient } from "@pluv/client";
2
+ import type { AbstractCrdtType } from "@pluv/crdt";
2
3
  import type { IOLike, JsonObject } from "@pluv/types";
3
- import { createContext, FC, ReactNode } from "react";
4
- import { memo, useContext } from "react";
5
- import type { AbstractType } from "yjs";
4
+ import type { FC, ReactNode } from "react";
5
+ import { createContext, memo, useContext } from "react";
6
6
  import type {
7
7
  CreateRoomBundle,
8
8
  CreateRoomBundleOptions,
@@ -17,16 +17,16 @@ export interface CreateBundle<TIO extends IOLike> {
17
17
  // factories
18
18
  createRoomBundle: <
19
19
  TPresence extends JsonObject = {},
20
- TStorage extends Record<string, AbstractType<any>> = {},
20
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
21
21
  >(
22
- options?: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
22
+ options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
23
23
  ) => CreateRoomBundle<TIO, TPresence, TStorage>;
24
24
 
25
25
  // components
26
26
  PluvProvider: FC<PluvProviderProps>;
27
27
 
28
28
  // hooks
29
- usePluvClient: () => PluvClient<TIO>;
29
+ useClient: () => PluvClient<TIO>;
30
30
  }
31
31
 
32
32
  export const createBundle = <TIO extends IOLike>(
@@ -53,19 +53,19 @@ export const createBundle = <TIO extends IOLike>(
53
53
 
54
54
  PluvProvider.displayName = "PluvProvider";
55
55
 
56
- const usePluvClient = (): PluvClient<TIO> => useContext(PluvContext);
56
+ const useClient = (): PluvClient<TIO> => useContext(PluvContext);
57
57
 
58
58
  const createRoomBundle = <
59
59
  TPresence extends JsonObject = {},
60
- TStorage extends Record<string, AbstractType<any>> = {},
60
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
61
61
  >(
62
- options: CreateRoomBundleOptions<TIO, TPresence, TStorage> = {},
62
+ options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
63
63
  ): CreateRoomBundle<TIO, TPresence, TStorage> =>
64
64
  _createRoomBundle(client, options);
65
65
 
66
66
  return {
67
67
  createRoomBundle,
68
68
  PluvProvider,
69
- usePluvClient,
69
+ useClient,
70
70
  };
71
71
  };
@@ -1,16 +1,21 @@
1
1
  import type {
2
2
  AbstractRoom,
3
- InferYjsSharedTypeJson,
4
3
  MockedRoomEvents,
5
4
  PluvClient,
6
5
  PluvRoom,
7
6
  PluvRoomAddon,
8
7
  PluvRoomDebug,
9
- TrackOriginOptions,
10
8
  UserInfo,
11
9
  WebSocketConnection,
12
10
  } from "@pluv/client";
13
11
  import { MockedRoom } from "@pluv/client";
12
+ import type {
13
+ AbstractCrdtDoc,
14
+ AbstractCrdtDocFactory,
15
+ AbstractCrdtType,
16
+ InferCrdtStorageJson,
17
+ } from "@pluv/crdt";
18
+ import { noop } from "@pluv/crdt";
14
19
  import type {
15
20
  IOEventMessage,
16
21
  IOLike,
@@ -30,7 +35,6 @@ import {
30
35
  useEffect,
31
36
  useState,
32
37
  } from "react";
33
- import type { AbstractType, Doc } from "yjs";
34
38
  import {
35
39
  identity,
36
40
  shallowArrayEqual,
@@ -41,16 +45,16 @@ import {
41
45
  export type CreateRoomBundleOptions<
42
46
  TIO extends IOLike,
43
47
  TPresence extends JsonObject = {},
44
- TStorage extends Record<string, AbstractType<any>> = {},
48
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
45
49
  > = {
46
50
  addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
47
- initialStorage?: () => TStorage;
51
+ initialStorage?: AbstractCrdtDocFactory<TStorage>;
48
52
  presence?: InputZodLike<TPresence>;
49
- } & TrackOriginOptions;
53
+ };
50
54
 
51
55
  type BaseRoomProviderProps<
52
56
  TPresence extends JsonObject,
53
- TStorage extends Record<string, AbstractType<any>>,
57
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
54
58
  > = {
55
59
  children?: ReactNode;
56
60
  initialStorage?: keyof TStorage extends never ? never : () => TStorage;
@@ -62,7 +66,7 @@ type BaseRoomProviderProps<
62
66
  export type MockedRoomProviderProps<
63
67
  TIO extends IOLike,
64
68
  TPresence extends JsonObject,
65
- TStorage extends Record<string, AbstractType<any>>,
69
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
66
70
  > = BaseRoomProviderProps<TPresence, TStorage> & {
67
71
  events?: MockedRoomEvents<TIO>;
68
72
  };
@@ -70,7 +74,7 @@ export type MockedRoomProviderProps<
70
74
  export type PluvRoomProviderProps<
71
75
  TIO extends IOLike,
72
76
  TPresence extends JsonObject,
73
- TStorage extends Record<string, AbstractType<any>>,
77
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
74
78
  > = BaseRoomProviderProps<TPresence, TStorage> & {
75
79
  debug?: boolean | PluvRoomDebug<TIO>;
76
80
  onAuthorizationFail?: (error: Error) => void;
@@ -87,73 +91,83 @@ export type UpdateMyPresenceAction<TPresence extends JsonObject> =
87
91
  export interface CreateRoomBundle<
88
92
  TIO extends IOLike,
89
93
  TPresence extends JsonObject,
90
- TStorage extends Record<string, AbstractType<any>>,
94
+ TStorage extends Record<string, AbstractCrdtType<any, any>>,
91
95
  > {
92
96
  // components
93
97
  MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
94
98
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
95
99
 
96
100
  // hooks
97
- usePluvBroadcast: () => <TEvent extends keyof InferIOInput<TIO>>(
101
+ useBroadcast: () => <TEvent extends keyof InferIOInput<TIO>>(
98
102
  event: TEvent,
99
103
  data: Id<InferIOInput<TIO>[TEvent]>,
100
104
  ) => void;
101
- usePluvCanRedo: () => boolean;
102
- usePluvCanUndo: () => boolean;
103
- usePluvConnection: <T extends unknown = WebSocketConnection>(
105
+ useCanRedo: () => boolean;
106
+ useCanUndo: () => boolean;
107
+ useConnection: <T extends unknown = WebSocketConnection>(
104
108
  selector: (connection: WebSocketConnection) => T,
105
109
  options?: SubscriptionHookOptions<Id<T>>,
106
110
  ) => Id<T>;
107
- usePluvDoc: () => Doc;
108
- usePluvEvent: <TType extends keyof InferIOOutput<TIO>>(
111
+ useDoc: () => AbstractCrdtDoc<TStorage>;
112
+ useEvent: <TType extends keyof InferIOOutput<TIO>>(
109
113
  type: TType,
110
114
  callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
111
115
  ) => void;
112
- usePluvMyPresence: <T extends unknown = TPresence>(
116
+ useMyPresence: <T extends unknown = TPresence>(
113
117
  selector?: (myPresence: TPresence) => T,
114
118
  options?: SubscriptionHookOptions<Id<T> | null>,
115
119
  ) => [
116
120
  myPresence: Id<T>,
117
121
  updateMyPresence: Dispatch<UpdateMyPresenceAction<TPresence>>,
118
122
  ];
119
- usePluvMyself: <T extends unknown = UserInfo<TIO, TPresence>>(
123
+ useMyself: <T extends unknown = UserInfo<TIO, TPresence>>(
120
124
  selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T,
121
125
  options?: SubscriptionHookOptions<Id<T> | null>,
122
126
  ) => Id<T> | null;
123
- usePluvOther: <T extends unknown = UserInfo<TIO, TPresence>>(
127
+ useOther: <T extends unknown = UserInfo<TIO, TPresence>>(
124
128
  connectionId: string,
125
129
  selector?: (other: UserInfo<TIO, TPresence>) => T,
126
130
  options?: SubscriptionHookOptions<Id<T> | null>,
127
131
  ) => Id<T> | null;
128
- usePluvOthers: <T extends unknown = UserInfo<TIO, TPresence>>(
132
+ useOthers: <T extends unknown = UserInfo<TIO, TPresence>>(
129
133
  selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T[],
130
134
  options?: SubscriptionHookOptions<readonly Id<T>[]>,
131
135
  ) => readonly Id<T>[];
132
- usePluvRedo: () => () => void;
133
- usePluvRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
134
- usePluvStorage: <
136
+ useRedo: () => () => void;
137
+ useRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
138
+ useStorage: <
135
139
  TKey extends keyof TStorage,
136
- TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>,
140
+ TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>,
137
141
  >(
138
142
  key: TKey,
139
- selector?: (data: InferYjsSharedTypeJson<TStorage[TKey]>) => TData,
140
- options?: SubscriptionHookOptions<TData>,
141
- ) => [data: TData, sharedType: TStorage[TKey]];
142
- usePluvTransact: () => (
143
+ selector?: (data: InferCrdtStorageJson<TStorage[TKey]>) => TData,
144
+ options?: SubscriptionHookOptions<TData | null>,
145
+ ) => [data: TData | null, sharedType: TStorage[TKey] | null];
146
+ useTransact: () => (
143
147
  fn: (storage: TStorage) => void,
144
148
  origin?: string,
145
149
  ) => void;
146
- usePluvUndo: () => () => void;
150
+ useUndo: () => () => void;
147
151
  }
148
152
 
153
+ export type InferRoomStorage<
154
+ TRoomBundle extends CreateRoomBundle<any, any, any>,
155
+ > =
156
+ TRoomBundle extends CreateRoomBundle<any, any, infer IStorage>
157
+ ? IStorage
158
+ : never;
159
+
149
160
  export const createRoomBundle = <
150
161
  TIO extends IOLike,
151
162
  TPresence extends JsonObject = {},
152
- TStorage extends Record<string, AbstractType<any>> = {},
163
+ TStorage extends Record<string, AbstractCrdtType<any, any>> = {},
153
164
  >(
154
165
  client: PluvClient<TIO>,
155
- options: CreateRoomBundleOptions<TIO, TPresence, TStorage> = {},
166
+ options: CreateRoomBundleOptions<TIO, TPresence, TStorage>,
156
167
  ): CreateRoomBundle<TIO, TPresence, TStorage> => {
168
+ const _initialStorage = (options.initialStorage ??
169
+ noop.doc()) as AbstractCrdtDocFactory<TStorage>;
170
+
157
171
  /**
158
172
  * !HACK
159
173
  * @description We'll let the context error out if the room is not provided,
@@ -182,15 +196,13 @@ export const createRoomBundle = <
182
196
 
183
197
  const [room] = useState<MockedRoom<TIO, TPresence, TStorage>>(() => {
184
198
  return new MockedRoom<TIO, TPresence, TStorage>(_room, {
185
- captureTimeout: options.captureTimeout,
186
199
  events,
187
200
  initialPresence,
188
201
  initialStorage:
189
202
  typeof initialStorage === "function"
190
- ? initialStorage
191
- : options.initialStorage,
203
+ ? _initialStorage.getFactory(initialStorage)
204
+ : _initialStorage,
192
205
  presence: options.presence,
193
- trackedOrigins: options.trackedOrigins,
194
206
  });
195
207
  });
196
208
 
@@ -223,16 +235,14 @@ export const createRoomBundle = <
223
235
  const [room] = useState<PluvRoom<TIO, TPresence, TStorage>>(() => {
224
236
  return client.createRoom<TPresence, TStorage>(_room, {
225
237
  addons: options.addons,
226
- captureTimeout: options.captureTimeout,
227
238
  debug,
228
239
  initialPresence,
229
240
  initialStorage:
230
241
  typeof initialStorage === "function"
231
- ? initialStorage
232
- : options.initialStorage,
242
+ ? _initialStorage.getFactory(initialStorage)
243
+ : _initialStorage,
233
244
  presence: options.presence,
234
245
  onAuthorizationFail,
235
- trackedOrigins: options.trackedOrigins,
236
246
  });
237
247
  });
238
248
 
@@ -263,13 +273,13 @@ export const createRoomBundle = <
263
273
 
264
274
  PluvRoomProvider.displayName = "PluvRoomProvider";
265
275
 
266
- const usePluvRoom = () => useContext(PluvRoomContext);
276
+ const useRoom = () => useContext(PluvRoomContext);
267
277
 
268
- const usePluvBroadcast = (): (<TEvent extends keyof InferIOInput<TIO>>(
278
+ const useBroadcast = (): (<TEvent extends keyof InferIOInput<TIO>>(
269
279
  event: TEvent,
270
280
  data: Id<InferIOInput<TIO>[TEvent]>,
271
281
  ) => void) => {
272
- const room = usePluvRoom();
282
+ const room = useRoom();
273
283
 
274
284
  return useCallback(
275
285
  <TEvent extends keyof InferIOInput<TIO>>(
@@ -282,8 +292,8 @@ export const createRoomBundle = <
282
292
  );
283
293
  };
284
294
 
285
- const usePluvCanRedo = (): boolean => {
286
- const room = usePluvRoom();
295
+ const useCanRedo = (): boolean => {
296
+ const room = useRoom();
287
297
 
288
298
  const subscribe = useCallback(
289
299
  (onStoreChange: () => void) => room.storageRoot(onStoreChange),
@@ -302,8 +312,8 @@ export const createRoomBundle = <
302
312
  return canRedo;
303
313
  };
304
314
 
305
- const usePluvCanUndo = (): boolean => {
306
- const room = usePluvRoom();
315
+ const useCanUndo = (): boolean => {
316
+ const room = useRoom();
307
317
 
308
318
  const subscribe = useCallback(
309
319
  (onStoreChange: () => void) => room.storageRoot(onStoreChange),
@@ -322,11 +332,11 @@ export const createRoomBundle = <
322
332
  return canRedo;
323
333
  };
324
334
 
325
- const usePluvConnection = <T extends unknown = WebSocketConnection>(
335
+ const useConnection = <T extends unknown = WebSocketConnection>(
326
336
  selector = identity as (connection: WebSocketConnection) => T,
327
337
  options?: SubscriptionHookOptions<Id<T>>,
328
338
  ): Id<T> => {
329
- const room = usePluvRoom();
339
+ const room = useRoom();
330
340
 
331
341
  const subscribe = useCallback(
332
342
  (onStoreChange: () => void) => {
@@ -351,17 +361,17 @@ export const createRoomBundle = <
351
361
  );
352
362
  };
353
363
 
354
- const usePluvDoc = () => {
355
- const room = usePluvRoom();
364
+ const useDoc = () => {
365
+ const room = useRoom();
356
366
 
357
367
  return room.getDoc();
358
368
  };
359
369
 
360
- const usePluvEvent = <TType extends keyof InferIOOutput<TIO>>(
370
+ const useEvent = <TType extends keyof InferIOOutput<TIO>>(
361
371
  type: TType,
362
372
  callback: (data: Id<IOEventMessage<TIO, TType>>) => void,
363
373
  ): void => {
364
- const room = usePluvRoom();
374
+ const room = useRoom();
365
375
 
366
376
  useEffect(() => {
367
377
  const unsubscribe = room.event(type, callback);
@@ -372,11 +382,11 @@ export const createRoomBundle = <
372
382
  }, [callback, room, type]);
373
383
  };
374
384
 
375
- const usePluvMyPresence = <T extends unknown = TPresence>(
385
+ const useMyPresence = <T extends unknown = TPresence>(
376
386
  selector = identity as (myPresence: TPresence) => T,
377
387
  options?: SubscriptionHookOptions<Id<T> | null>,
378
388
  ): [Id<T>, Dispatch<UpdateMyPresenceAction<TPresence>>] => {
379
- const room = usePluvRoom();
389
+ const room = useRoom();
380
390
 
381
391
  const subscribe = useCallback(
382
392
  (onStoreChange: () => void) => {
@@ -416,11 +426,11 @@ export const createRoomBundle = <
416
426
  return [myPresence, updateMyPresence];
417
427
  };
418
428
 
419
- const usePluvMyself = <T extends unknown = UserInfo<TIO, TPresence>>(
429
+ const useMyself = <T extends unknown = UserInfo<TIO, TPresence>>(
420
430
  selector = identity as (myself: Id<UserInfo<TIO, TPresence>>) => T,
421
431
  options?: SubscriptionHookOptions<Id<T> | null>,
422
432
  ): Id<T> | null => {
423
- const room = usePluvRoom();
433
+ const room = useRoom();
424
434
 
425
435
  const subscribe = useCallback(
426
436
  (onStoreChange: () => void) => {
@@ -447,12 +457,12 @@ export const createRoomBundle = <
447
457
  );
448
458
  };
449
459
 
450
- const usePluvOther = <T extends unknown = UserInfo<TIO, TPresence>>(
460
+ const useOther = <T extends unknown = UserInfo<TIO, TPresence>>(
451
461
  connectionId: string,
452
462
  selector = identity as (other: UserInfo<TIO, TPresence>) => T,
453
463
  options?: SubscriptionHookOptions<Id<T> | null>,
454
464
  ): Id<T> | null => {
455
- const room = usePluvRoom();
465
+ const room = useRoom();
456
466
 
457
467
  const subscribe = useCallback(
458
468
  (onStoreChange: () => void) => {
@@ -482,13 +492,13 @@ export const createRoomBundle = <
482
492
  );
483
493
  };
484
494
 
485
- const usePluvOthers = <T extends unknown = UserInfo<TIO, TPresence>>(
495
+ const useOthers = <T extends unknown = UserInfo<TIO, TPresence>>(
486
496
  selector = identity as (
487
497
  other: readonly Id<UserInfo<TIO, TPresence>>[],
488
498
  ) => T[],
489
499
  options?: SubscriptionHookOptions<readonly Id<T>[]>,
490
500
  ): readonly Id<T>[] => {
491
- const room = usePluvRoom();
501
+ const room = useRoom();
492
502
 
493
503
  const subscribe = useCallback(
494
504
  (onStoreChange: () => void) => {
@@ -510,38 +520,43 @@ export const createRoomBundle = <
510
520
  );
511
521
  };
512
522
 
513
- const usePluvRedo = () => {
514
- const room = usePluvRoom();
523
+ const useRedo = () => {
524
+ const room = useRoom();
515
525
 
516
526
  return room.redo;
517
527
  };
518
528
 
519
- const usePluvStorage = <
529
+ const useStorage = <
520
530
  TKey extends keyof TStorage,
521
- TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>,
531
+ TData extends unknown = InferCrdtStorageJson<TStorage[TKey]>,
522
532
  >(
523
533
  key: TKey,
524
534
  selector = identity as (
525
- data: InferYjsSharedTypeJson<TStorage[TKey]>,
535
+ data: InferCrdtStorageJson<TStorage[TKey]>,
526
536
  ) => TData,
527
- options?: SubscriptionHookOptions<TData>,
528
- ): [data: TData, sharedType: TStorage[TKey]] => {
529
- const room = usePluvRoom();
537
+ options?: SubscriptionHookOptions<TData | null>,
538
+ ): [data: TData | null, sharedType: TStorage[TKey] | null] => {
539
+ const room = useRoom();
540
+ const rerender = useRerender();
541
+
542
+ room.subscribe("storage-loaded", () => {
543
+ rerender();
544
+ });
530
545
 
531
546
  const subscribe = useCallback(
532
547
  (onStoreChange: () => void) => room.storage(key, onStoreChange),
533
548
  [key, room],
534
549
  );
535
550
 
536
- const getSnapshot = useCallback((): InferYjsSharedTypeJson<
551
+ const getSnapshot = useCallback((): InferCrdtStorageJson<
537
552
  TStorage[TKey]
538
- > => {
539
- return room.getStorage(key).toJSON();
553
+ > | null => {
554
+ return room.getStorage(key)?.toJson();
540
555
  }, [key, room]);
541
556
 
542
557
  const _selector = useCallback(
543
- (snapshot: InferYjsSharedTypeJson<TStorage[TKey]>) => {
544
- return selector(snapshot);
558
+ (snapshot: InferCrdtStorageJson<TStorage[TKey]> | null) => {
559
+ return snapshot === null ? null : selector(snapshot);
545
560
  },
546
561
  [selector],
547
562
  );
@@ -554,19 +569,19 @@ export const createRoomBundle = <
554
569
  options?.isEqual ?? fastDeepEqual,
555
570
  );
556
571
 
557
- const sharedType = room.getStorage(key);
572
+ const sharedType = room.getStorage(key) ?? null;
558
573
 
559
574
  return [data, sharedType];
560
575
  };
561
576
 
562
- const usePluvTransact = () => {
563
- const room = usePluvRoom();
577
+ const useTransact = () => {
578
+ const room = useRoom();
564
579
 
565
580
  return room.transact;
566
581
  };
567
582
 
568
- const usePluvUndo = () => {
569
- const room = usePluvRoom();
583
+ const useUndo = () => {
584
+ const room = useRoom();
570
585
 
571
586
  return room.undo;
572
587
  };
@@ -577,20 +592,20 @@ export const createRoomBundle = <
577
592
  PluvRoomProvider,
578
593
 
579
594
  // hooks
580
- usePluvBroadcast,
581
- usePluvCanRedo,
582
- usePluvCanUndo,
583
- usePluvConnection,
584
- usePluvDoc,
585
- usePluvEvent,
586
- usePluvMyPresence,
587
- usePluvMyself,
588
- usePluvOther,
589
- usePluvOthers,
590
- usePluvRedo,
591
- usePluvRoom,
592
- usePluvStorage,
593
- usePluvTransact,
594
- usePluvUndo,
595
+ useBroadcast,
596
+ useCanRedo,
597
+ useCanUndo,
598
+ useConnection,
599
+ useDoc,
600
+ useEvent,
601
+ useMyPresence,
602
+ useMyself,
603
+ useOther,
604
+ useOthers,
605
+ useRedo,
606
+ useRoom,
607
+ useStorage,
608
+ useTransact,
609
+ useUndo,
595
610
  };
596
611
  };
package/src/index.ts CHANGED
@@ -1,16 +1,17 @@
1
- export type { AbstractType } from "@pluv/client";
2
- export { ConnectionState, createClient, y } from "@pluv/client";
1
+ export { ConnectionState, createClient } from "@pluv/client";
2
+ export type { AbstractCrdtType, InferCrdtStorageJson } from "@pluv/crdt";
3
3
  export type {
4
4
  BaseUser,
5
5
  EventMessage,
6
6
  EventRecord,
7
7
  IOEventMessage,
8
8
  } from "@pluv/types";
9
- export type { CreateBundle, PluvProviderProps } from "./createBundle";
10
9
  export { createBundle } from "./createBundle";
10
+ export type { CreateBundle, PluvProviderProps } from "./createBundle";
11
11
  export type {
12
12
  CreateRoomBundle,
13
13
  CreateRoomBundleOptions,
14
+ InferRoomStorage,
14
15
  PluvRoomProviderProps,
15
16
  SubscriptionHookOptions,
16
17
  } from "./createRoomBundle";