@korajs/react 1.0.0-beta.12 → 1.0.0-beta.14
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/README.md +48 -40
- package/dist/index.cjs +169 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +159 -5
- package/dist/index.d.ts +159 -5
- package/dist/index.js +167 -63
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,70 +1,78 @@
|
|
|
1
1
|
# @korajs/react
|
|
2
2
|
|
|
3
|
-
React
|
|
3
|
+
React bindings for Kora.js: a provider, reactive queries, mutations, sync status, rich text and
|
|
4
|
+
presence. Hooks use `useSyncExternalStore`, so they are safe in concurrent rendering, StrictMode and
|
|
5
|
+
server rendering.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
pnpm add korajs @korajs/react
|
|
10
|
+
pnpm add korajs@beta @korajs/react@beta
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
<!-- docs-check-prelude
|
|
16
|
+
import { createApp, defineSchema, t } from 'korajs'
|
|
17
|
+
const schema = defineSchema({
|
|
18
|
+
version: 1,
|
|
19
|
+
collections: { todos: { fields: { title: t.string(), completed: t.boolean().default(false), createdAt: t.timestamp().auto() } } },
|
|
20
|
+
})
|
|
21
|
+
const app = createApp({ schema })
|
|
22
|
+
-->
|
|
14
23
|
|
|
15
24
|
```tsx
|
|
16
|
-
import { KoraProvider } from '@korajs/react'
|
|
25
|
+
import { createKoraHooks, KoraProvider } from '@korajs/react'
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
// Hooks typed by your schema
|
|
28
|
+
const { useCollection, useMutation, useQuery, useSyncStatus } = createKoraHooks<typeof app>()
|
|
29
|
+
|
|
30
|
+
export function Root() {
|
|
19
31
|
return (
|
|
20
|
-
<KoraProvider app={app}>
|
|
32
|
+
<KoraProvider app={app} fallback={<p>Loading...</p>}>
|
|
21
33
|
<TodoList />
|
|
22
34
|
</KoraProvider>
|
|
23
35
|
)
|
|
24
36
|
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Reactive Queries
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
import { useQuery } from '@korajs/react'
|
|
31
37
|
|
|
32
38
|
function TodoList() {
|
|
33
|
-
const todos =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Mutations
|
|
41
|
-
|
|
42
|
-
```tsx
|
|
43
|
-
import { useMutation } from '@korajs/react'
|
|
44
|
-
|
|
45
|
-
function AddTodo() {
|
|
46
|
-
const addTodo = useMutation(app.todos.insert)
|
|
39
|
+
const todos = useCollection('todos')
|
|
40
|
+
const open = useQuery(todos.where({ completed: false }).orderBy('createdAt'))
|
|
41
|
+
const addTodo = useMutation(todos.insert)
|
|
42
|
+
const status = useSyncStatus()
|
|
47
43
|
|
|
48
|
-
return
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<button onClick={() => addTodo.mutate({ title: 'New todo' })}>Add</button>
|
|
47
|
+
{addTodo.error && <p role="alert">{addTodo.error.message}</p>}
|
|
48
|
+
<ul>
|
|
49
|
+
{open.map((todo) => (
|
|
50
|
+
<li key={todo.id}>{todo.title}</li>
|
|
51
|
+
))}
|
|
52
|
+
</ul>
|
|
53
|
+
<small>
|
|
54
|
+
{status.status}, {status.pendingOperations} pending
|
|
55
|
+
</small>
|
|
56
|
+
</>
|
|
57
|
+
)
|
|
49
58
|
}
|
|
50
59
|
```
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
- `KoraProvider` renders `fallback` until the local database is open.
|
|
62
|
+
- `useQuery` renders `[]` first and the rows right after mount, then re-renders only when the
|
|
63
|
+
result changes. `useQueryState` returns `{ data, error, ready }`.
|
|
64
|
+
- `useMutation` returns `{ mutate, mutateAsync, isLoading, error, reset }`; writes land locally at
|
|
65
|
+
once and upload in the background.
|
|
66
|
+
- `useSyncStatus()` returns the full status (`status`, `pendingOperations`, `lastSyncedAt`, ...)
|
|
67
|
+
and re-renders only when it changes.
|
|
68
|
+
- `useRichText`, `usePresence`, `useCollaborators` and `AuthBoundKoraProvider` cover collaborative
|
|
69
|
+
editing and per-user databases.
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
const status = useSyncStatus()
|
|
59
|
-
// 'connected' | 'syncing' | 'synced' | 'offline' | 'error'
|
|
60
|
-
return <span>{status}</span>
|
|
61
|
-
}
|
|
62
|
-
```
|
|
71
|
+
## Documentation
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
[React Hooks guide](https://korajs.dev/guide/react-hooks) and the
|
|
74
|
+
[React API reference](https://korajs.dev/api/react).
|
|
65
75
|
|
|
66
76
|
## License
|
|
67
77
|
|
|
68
78
|
MIT
|
|
69
|
-
|
|
70
|
-
See the [full documentation](https://github.com/ehoneahobed/kora) for guides, API reference, and examples.
|
package/dist/index.cjs
CHANGED
|
@@ -23,12 +23,14 @@ __export(index_exports, {
|
|
|
23
23
|
AuthBoundKoraProvider: () => AuthBoundKoraProvider,
|
|
24
24
|
KoraProvider: () => KoraProvider,
|
|
25
25
|
classifyKoraInitializationError: () => classifyKoraInitializationError,
|
|
26
|
+
createKoraHooks: () => createKoraHooks,
|
|
26
27
|
useApp: () => useApp,
|
|
27
28
|
useCollaborators: () => useCollaborators,
|
|
28
29
|
useCollection: () => useCollection,
|
|
29
30
|
useMutation: () => useMutation,
|
|
30
31
|
usePresence: () => usePresence,
|
|
31
32
|
useQuery: () => useQuery,
|
|
33
|
+
useQueryState: () => useQueryState,
|
|
32
34
|
useRichText: () => useRichText,
|
|
33
35
|
useSyncStatus: () => useSyncStatus
|
|
34
36
|
});
|
|
@@ -151,6 +153,7 @@ function AuthBoundKoraProvider({
|
|
|
151
153
|
authClient,
|
|
152
154
|
createApp,
|
|
153
155
|
signedOut = null,
|
|
156
|
+
locked,
|
|
154
157
|
fallback = null,
|
|
155
158
|
error: renderError,
|
|
156
159
|
children
|
|
@@ -244,6 +247,7 @@ function AuthBoundKoraProvider({
|
|
|
244
247
|
);
|
|
245
248
|
}
|
|
246
249
|
if (state.state === "signed-out" || state.state === "anonymous") return signedOut;
|
|
250
|
+
if (state.state === "authenticated" && state.locked) return locked ?? signedOut;
|
|
247
251
|
if (!app) return fallback;
|
|
248
252
|
return (0, import_react2.createElement)(KoraProvider, { app, fallback }, children);
|
|
249
253
|
}
|
|
@@ -267,10 +271,17 @@ var noopSubscribe = (_onStoreChange) => {
|
|
|
267
271
|
return () => {
|
|
268
272
|
};
|
|
269
273
|
};
|
|
270
|
-
|
|
274
|
+
var getServerSnapshot = () => EMPTY_ARRAY;
|
|
275
|
+
var getNoError = () => null;
|
|
276
|
+
function readQueryError(queryStore) {
|
|
277
|
+
if (!queryStore) return null;
|
|
278
|
+
const channel = queryStore;
|
|
279
|
+
return typeof channel.getError === "function" ? channel.getError() : null;
|
|
280
|
+
}
|
|
281
|
+
function useQuerySubscription(query, options) {
|
|
271
282
|
const { queryStoreCache } = useKoraContext();
|
|
272
283
|
const enabled = options?.enabled !== false;
|
|
273
|
-
const descriptorKey =
|
|
284
|
+
const descriptorKey = (0, import_store2.queryKey)(query.getDescriptor());
|
|
274
285
|
const queryRef = (0, import_react3.useRef)(query);
|
|
275
286
|
queryRef.current = query;
|
|
276
287
|
const lastSnapshotRef = (0, import_react3.useRef)(EMPTY_ARRAY);
|
|
@@ -288,23 +299,42 @@ function useQuery(query, options) {
|
|
|
288
299
|
queryStoreCache.release(currentQuery);
|
|
289
300
|
};
|
|
290
301
|
}, [descriptorKey, enabled, queryStoreCache]);
|
|
302
|
+
const subscribe = enabled && queryStore ? queryStore.subscribe : noopSubscribe;
|
|
291
303
|
const getSnapshot = (0, import_react3.useCallback)(() => {
|
|
292
304
|
if (!enabled) {
|
|
293
305
|
lastSnapshotRef.current = EMPTY_ARRAY;
|
|
294
306
|
return lastSnapshotRef.current;
|
|
295
307
|
}
|
|
296
|
-
if (!queryStore) {
|
|
297
|
-
return lastSnapshotRef.current;
|
|
298
|
-
}
|
|
299
|
-
if (!queryStore.hasSnapshot()) {
|
|
308
|
+
if (!queryStore || !queryStore.hasSnapshot()) {
|
|
300
309
|
return lastSnapshotRef.current;
|
|
301
310
|
}
|
|
302
311
|
const snapshot = queryStore.getSnapshot();
|
|
303
312
|
lastSnapshotRef.current = snapshot;
|
|
304
313
|
return snapshot;
|
|
305
314
|
}, [enabled, queryStore]);
|
|
306
|
-
|
|
315
|
+
const getError = (0, import_react3.useCallback)(
|
|
316
|
+
() => enabled ? readQueryError(queryStore) : null,
|
|
317
|
+
[enabled, queryStore]
|
|
318
|
+
);
|
|
319
|
+
const data = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot);
|
|
320
|
+
const error = (0, import_react3.useSyncExternalStore)(subscribe, getError, getNoError);
|
|
321
|
+
const ready = enabled && queryStore !== null && queryStore.hasSnapshot();
|
|
322
|
+
return { data, error, ready };
|
|
307
323
|
}
|
|
324
|
+
function useQuery(query, options) {
|
|
325
|
+
const { data, error } = useQuerySubscription(query, options);
|
|
326
|
+
if (error && options?.throwOnError !== false) {
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
return data;
|
|
330
|
+
}
|
|
331
|
+
function useQueryState(query, options) {
|
|
332
|
+
const { data, error, ready } = useQuerySubscription(query, options);
|
|
333
|
+
return (0, import_react3.useMemo)(() => ({ data, error, ready }), [data, error, ready]);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/hooks/create-kora-hooks.ts
|
|
337
|
+
var import_react7 = require("react");
|
|
308
338
|
|
|
309
339
|
// src/hooks/use-mutation.ts
|
|
310
340
|
var import_bindings = require("@korajs/core/bindings");
|
|
@@ -312,98 +342,161 @@ var import_react5 = require("react");
|
|
|
312
342
|
|
|
313
343
|
// src/hooks/use-controller.ts
|
|
314
344
|
var import_react4 = require("react");
|
|
345
|
+
var ORPHAN_REAP_MS = 1e3;
|
|
315
346
|
function useController(create, destroy, deps) {
|
|
316
|
-
const
|
|
347
|
+
const entryRef = (0, import_react4.useRef)(null);
|
|
317
348
|
const createRef = (0, import_react4.useRef)(create);
|
|
318
349
|
createRef.current = create;
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
350
|
+
const destroyRef = (0, import_react4.useRef)(destroy);
|
|
351
|
+
destroyRef.current = destroy;
|
|
352
|
+
const [version, bumpVersion] = (0, import_react4.useReducer)((count) => count + 1, 0);
|
|
353
|
+
const handle = (0, import_react4.useRef)({
|
|
354
|
+
get: () => {
|
|
355
|
+
const current = entryRef.current;
|
|
356
|
+
if (current !== null && !current.reaped) {
|
|
357
|
+
return current.controller;
|
|
358
|
+
}
|
|
359
|
+
const entry = {
|
|
360
|
+
controller: createRef.current(),
|
|
361
|
+
claimed: false,
|
|
362
|
+
reaped: false
|
|
363
|
+
};
|
|
364
|
+
entryRef.current = entry;
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
if (!entry.claimed && !entry.reaped) {
|
|
367
|
+
entry.reaped = true;
|
|
368
|
+
destroyRef.current(entry.controller);
|
|
369
|
+
}
|
|
370
|
+
}, ORPHAN_REAP_MS);
|
|
371
|
+
return entry.controller;
|
|
372
|
+
},
|
|
373
|
+
peek: () => {
|
|
374
|
+
const current = entryRef.current;
|
|
375
|
+
return current?.claimed && !current.reaped ? current.controller : null;
|
|
323
376
|
}
|
|
324
|
-
return controllerRef.current;
|
|
325
377
|
}).current;
|
|
326
378
|
(0, import_react4.useEffect)(() => {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
379
|
+
const current = entryRef.current;
|
|
380
|
+
if (current !== null && !current.reaped && !current.claimed) {
|
|
381
|
+
current.claimed = true;
|
|
382
|
+
} else if (current === null || current.reaped) {
|
|
383
|
+
entryRef.current = { controller: createRef.current(), claimed: true, reaped: false };
|
|
384
|
+
bumpVersion();
|
|
330
385
|
}
|
|
331
386
|
return () => {
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
if (
|
|
335
|
-
|
|
387
|
+
const entry = entryRef.current;
|
|
388
|
+
entryRef.current = null;
|
|
389
|
+
if (entry !== null && !entry.reaped) {
|
|
390
|
+
entry.reaped = true;
|
|
391
|
+
destroyRef.current(entry.controller);
|
|
336
392
|
}
|
|
337
393
|
};
|
|
338
394
|
}, deps);
|
|
339
|
-
return
|
|
395
|
+
return { get: handle.get, peek: handle.peek, version };
|
|
340
396
|
}
|
|
341
397
|
|
|
342
398
|
// src/hooks/use-mutation.ts
|
|
399
|
+
var IDLE = Object.freeze({ isLoading: false, error: null });
|
|
400
|
+
var getServerSnapshot2 = () => IDLE;
|
|
343
401
|
function useMutation(mutationFn, options) {
|
|
344
402
|
const fnRef = (0, import_react5.useRef)(mutationFn);
|
|
345
403
|
fnRef.current = mutationFn;
|
|
346
404
|
const optionsRef = (0, import_react5.useRef)(options);
|
|
347
405
|
optionsRef.current = options;
|
|
348
|
-
const
|
|
406
|
+
const controller = useController(
|
|
349
407
|
() => (0, import_bindings.createMutationController)({
|
|
350
408
|
mutationFn: (...args) => fnRef.current(...args),
|
|
351
409
|
resolveOptions: () => optionsRef.current
|
|
352
410
|
}),
|
|
353
|
-
(
|
|
411
|
+
(instance) => instance.destroy(),
|
|
354
412
|
[]
|
|
355
413
|
);
|
|
356
|
-
const
|
|
414
|
+
const getController = controller.get;
|
|
415
|
+
const subscribe = (0, import_react5.useCallback)(
|
|
357
416
|
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
358
|
-
|
|
359
|
-
|
|
417
|
+
[getController, controller.version]
|
|
418
|
+
);
|
|
419
|
+
const getSnapshot = (0, import_react5.useCallback)(() => getController().getSnapshot(), [getController]);
|
|
420
|
+
const state = (0, import_react5.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot2);
|
|
421
|
+
const mutate = (0, import_react5.useCallback)((...args) => getController().mutate(...args), [getController]);
|
|
422
|
+
const mutateAsync = (0, import_react5.useCallback)(
|
|
423
|
+
(...args) => getController().mutateAsync(...args),
|
|
424
|
+
[getController]
|
|
425
|
+
);
|
|
426
|
+
const reset = (0, import_react5.useCallback)(() => getController().reset(), [getController]);
|
|
427
|
+
return (0, import_react5.useMemo)(
|
|
428
|
+
() => ({ mutate, mutateAsync, isLoading: state.isLoading, error: state.error, reset }),
|
|
429
|
+
[mutate, mutateAsync, reset, state.isLoading, state.error]
|
|
360
430
|
);
|
|
361
|
-
return {
|
|
362
|
-
mutate: (...args) => getController().mutate(...args),
|
|
363
|
-
mutateAsync: (...args) => getController().mutateAsync(...args),
|
|
364
|
-
isLoading: state.isLoading,
|
|
365
|
-
error: state.error,
|
|
366
|
-
reset: () => getController().reset()
|
|
367
|
-
};
|
|
368
431
|
}
|
|
369
432
|
|
|
370
433
|
// src/hooks/use-sync-status.ts
|
|
371
434
|
var import_sync = require("@korajs/sync");
|
|
372
435
|
var import_react6 = require("react");
|
|
436
|
+
var getServerSnapshot3 = () => import_sync.OFFLINE_SYNC_STATUS;
|
|
373
437
|
function useSyncStatus() {
|
|
374
438
|
const { syncEngine, subscribeSyncStatus, events } = useKoraContext();
|
|
375
|
-
const
|
|
439
|
+
const controller = useController(
|
|
376
440
|
() => (0, import_sync.createSyncStatusController)({
|
|
377
441
|
syncEngine,
|
|
378
442
|
subscribeSyncStatus,
|
|
379
443
|
events: subscribeSyncStatus ? null : events
|
|
380
444
|
}),
|
|
381
|
-
(
|
|
445
|
+
(instance) => instance.destroy(),
|
|
382
446
|
[syncEngine, subscribeSyncStatus, events]
|
|
383
447
|
);
|
|
384
|
-
|
|
448
|
+
const getController = controller.get;
|
|
449
|
+
const subscribe = (0, import_react6.useCallback)(
|
|
385
450
|
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
386
|
-
|
|
387
|
-
|
|
451
|
+
[getController, controller.version]
|
|
452
|
+
);
|
|
453
|
+
const peek = controller.peek;
|
|
454
|
+
const getSnapshot = (0, import_react6.useCallback)(
|
|
455
|
+
() => peek()?.getSnapshot() ?? import_sync.OFFLINE_SYNC_STATUS,
|
|
456
|
+
[peek]
|
|
388
457
|
);
|
|
458
|
+
return (0, import_react6.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot3);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/hooks/create-kora-hooks.ts
|
|
462
|
+
function createKoraHooks() {
|
|
463
|
+
function useTypedApp() {
|
|
464
|
+
return useApp();
|
|
465
|
+
}
|
|
466
|
+
function useTypedCollection(name) {
|
|
467
|
+
const { app, store } = useKoraContext();
|
|
468
|
+
return (0, import_react7.useMemo)(() => {
|
|
469
|
+
const collections = app?.collections;
|
|
470
|
+
const accessor = collections && name in collections ? collections[name] : store.collection(name);
|
|
471
|
+
return accessor;
|
|
472
|
+
}, [app, store, name]);
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
useApp: useTypedApp,
|
|
476
|
+
useCollection: useTypedCollection,
|
|
477
|
+
useQuery,
|
|
478
|
+
useQueryState,
|
|
479
|
+
useMutation,
|
|
480
|
+
useSyncStatus
|
|
481
|
+
};
|
|
389
482
|
}
|
|
390
483
|
|
|
391
484
|
// src/hooks/use-collection.ts
|
|
392
|
-
var
|
|
485
|
+
var import_react8 = require("react");
|
|
393
486
|
function useCollection(name) {
|
|
394
487
|
const { store } = useKoraContext();
|
|
395
|
-
return (0,
|
|
488
|
+
return (0, import_react8.useMemo)(() => {
|
|
396
489
|
return store.collection(name);
|
|
397
490
|
}, [store, name]);
|
|
398
491
|
}
|
|
399
492
|
|
|
400
493
|
// src/hooks/use-rich-text.ts
|
|
401
494
|
var import_store3 = require("@korajs/store");
|
|
402
|
-
var
|
|
495
|
+
var import_react9 = require("react");
|
|
403
496
|
function useRichText(collectionName, recordId, fieldName, options) {
|
|
404
497
|
const { store, syncEngine } = useKoraContext();
|
|
405
|
-
const collection = (0,
|
|
406
|
-
const
|
|
498
|
+
const collection = (0, import_react9.useMemo)(() => store.collection(collectionName), [store, collectionName]);
|
|
499
|
+
const controller = useController(
|
|
407
500
|
() => (0, import_store3.createRichTextController)({
|
|
408
501
|
collection,
|
|
409
502
|
collectionName,
|
|
@@ -414,29 +507,35 @@ function useRichText(collectionName, recordId, fieldName, options) {
|
|
|
414
507
|
useDocChannel: options?.useDocChannel,
|
|
415
508
|
user: options?.user
|
|
416
509
|
}),
|
|
417
|
-
(
|
|
510
|
+
(instance) => instance.destroy(),
|
|
418
511
|
[collection, collectionName, fieldName, options?.useDocChannel, recordId, store, syncEngine]
|
|
419
512
|
);
|
|
420
|
-
|
|
513
|
+
const getController = controller.get;
|
|
514
|
+
(0, import_react9.useEffect)(() => {
|
|
421
515
|
getController().setUser(options?.user);
|
|
422
|
-
}, [getController, options?.user]);
|
|
423
|
-
const
|
|
516
|
+
}, [getController, options?.user, controller.version]);
|
|
517
|
+
const subscribe = (0, import_react9.useCallback)(
|
|
424
518
|
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
425
|
-
|
|
426
|
-
() => getController().getSnapshot()
|
|
519
|
+
[getController, controller.version]
|
|
427
520
|
);
|
|
428
|
-
const
|
|
521
|
+
const getSnapshot = (0, import_react9.useCallback)(() => getController().getSnapshot(), [getController]);
|
|
522
|
+
const snapshot = (0, import_react9.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
523
|
+
const undo = (0, import_react9.useCallback)(() => {
|
|
429
524
|
getController().undo();
|
|
430
525
|
}, [getController]);
|
|
431
|
-
const redo = (0,
|
|
526
|
+
const redo = (0, import_react9.useCallback)(() => {
|
|
432
527
|
getController().redo();
|
|
433
528
|
}, [getController]);
|
|
434
|
-
const setCursor = (0,
|
|
529
|
+
const setCursor = (0, import_react9.useCallback)(
|
|
435
530
|
(anchor, head) => getController().setCursor(anchor, head),
|
|
436
531
|
[getController]
|
|
437
532
|
);
|
|
438
|
-
const clearCursor = (0,
|
|
439
|
-
|
|
533
|
+
const clearCursor = (0, import_react9.useCallback)(() => getController().clearCursor(), [getController]);
|
|
534
|
+
const live = getController();
|
|
535
|
+
return (0, import_react9.useMemo)(
|
|
536
|
+
() => buildResult(live, snapshot, undo, redo, setCursor, clearCursor),
|
|
537
|
+
[live, snapshot, undo, redo, setCursor, clearCursor, controller.version]
|
|
538
|
+
);
|
|
440
539
|
}
|
|
441
540
|
function buildResult(controller, snapshot, undo, redo, setCursor, clearCursor) {
|
|
442
541
|
return {
|
|
@@ -450,18 +549,21 @@ function buildResult(controller, snapshot, undo, redo, setCursor, clearCursor) {
|
|
|
450
549
|
error: snapshot.error,
|
|
451
550
|
cursors: [...snapshot.cursors],
|
|
452
551
|
setCursor,
|
|
453
|
-
clearCursor
|
|
552
|
+
clearCursor,
|
|
553
|
+
hasUnsavedChanges: snapshot.hasUnsavedChanges,
|
|
554
|
+
retrySave: () => controller.retrySave(),
|
|
555
|
+
getUnsavedState: () => controller.getUnsavedState()
|
|
454
556
|
};
|
|
455
557
|
}
|
|
456
558
|
|
|
457
559
|
// src/hooks/use-presence.ts
|
|
458
|
-
var
|
|
560
|
+
var import_react10 = require("react");
|
|
459
561
|
function usePresence(user) {
|
|
460
562
|
const { syncEngine } = useKoraContext();
|
|
461
563
|
const name = user?.name ?? null;
|
|
462
564
|
const color = user?.color ?? null;
|
|
463
565
|
const avatar = user?.avatar ?? null;
|
|
464
|
-
(0,
|
|
566
|
+
(0, import_react10.useEffect)(() => {
|
|
465
567
|
if (!syncEngine || !name || !color) return;
|
|
466
568
|
const awareness = syncEngine.getAwarenessManager();
|
|
467
569
|
awareness.setLocalState({
|
|
@@ -475,12 +577,13 @@ function usePresence(user) {
|
|
|
475
577
|
|
|
476
578
|
// src/hooks/use-collaborators.ts
|
|
477
579
|
var import_sync2 = require("@korajs/sync");
|
|
478
|
-
var
|
|
580
|
+
var import_react11 = require("react");
|
|
479
581
|
var EMPTY_ARRAY2 = [];
|
|
582
|
+
var getServerSnapshot4 = () => EMPTY_ARRAY2;
|
|
480
583
|
function useCollaborators() {
|
|
481
584
|
const { syncEngine } = useKoraContext();
|
|
482
|
-
const snapshotRef = (0,
|
|
483
|
-
const subscribe = (0,
|
|
585
|
+
const snapshotRef = (0, import_react11.useRef)(EMPTY_ARRAY2);
|
|
586
|
+
const subscribe = (0, import_react11.useCallback)(
|
|
484
587
|
(onStoreChange) => {
|
|
485
588
|
if (!syncEngine) {
|
|
486
589
|
snapshotRef.current = EMPTY_ARRAY2;
|
|
@@ -488,6 +591,7 @@ function useCollaborators() {
|
|
|
488
591
|
};
|
|
489
592
|
}
|
|
490
593
|
const awareness = syncEngine.getAwarenessManager();
|
|
594
|
+
snapshotRef.current = EMPTY_ARRAY2;
|
|
491
595
|
return (0, import_sync2.subscribeRemoteAwarenessStates)(awareness, (states) => {
|
|
492
596
|
snapshotRef.current = states;
|
|
493
597
|
onStoreChange();
|
|
@@ -495,25 +599,27 @@ function useCollaborators() {
|
|
|
495
599
|
},
|
|
496
600
|
[syncEngine]
|
|
497
601
|
);
|
|
498
|
-
const getSnapshot = (0,
|
|
499
|
-
(0,
|
|
602
|
+
const getSnapshot = (0, import_react11.useCallback)(() => snapshotRef.current, []);
|
|
603
|
+
(0, import_react11.useEffect)(() => {
|
|
500
604
|
if (!syncEngine) {
|
|
501
605
|
snapshotRef.current = EMPTY_ARRAY2;
|
|
502
606
|
}
|
|
503
607
|
}, [syncEngine]);
|
|
504
|
-
return (0,
|
|
608
|
+
return (0, import_react11.useSyncExternalStore)(subscribe, getSnapshot, getServerSnapshot4);
|
|
505
609
|
}
|
|
506
610
|
// Annotate the CommonJS export names for ESM import in node:
|
|
507
611
|
0 && (module.exports = {
|
|
508
612
|
AuthBoundKoraProvider,
|
|
509
613
|
KoraProvider,
|
|
510
614
|
classifyKoraInitializationError,
|
|
615
|
+
createKoraHooks,
|
|
511
616
|
useApp,
|
|
512
617
|
useCollaborators,
|
|
513
618
|
useCollection,
|
|
514
619
|
useMutation,
|
|
515
620
|
usePresence,
|
|
516
621
|
useQuery,
|
|
622
|
+
useQueryState,
|
|
517
623
|
useRichText,
|
|
518
624
|
useSyncStatus
|
|
519
625
|
});
|