canvu-react 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/realtime.cjs +105 -9
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.js +105 -9
- package/dist/realtime.js.map +1 -1
- package/package.json +1 -1
package/dist/realtime.cjs
CHANGED
|
@@ -2398,6 +2398,30 @@ function prepareRealtimeItems(items) {
|
|
|
2398
2398
|
return null;
|
|
2399
2399
|
}
|
|
2400
2400
|
}
|
|
2401
|
+
function getRealtimeItemId(item) {
|
|
2402
|
+
const id = item.id;
|
|
2403
|
+
return typeof id === "string" ? id : null;
|
|
2404
|
+
}
|
|
2405
|
+
function serializeRealtimeItem(item) {
|
|
2406
|
+
try {
|
|
2407
|
+
return JSON.stringify(sanitizeRealtimeItem(item));
|
|
2408
|
+
} catch {
|
|
2409
|
+
return null;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
function resolvePendingDraftItemIds(board, items) {
|
|
2413
|
+
const pendingIds = /* @__PURE__ */ new Set();
|
|
2414
|
+
for (const item of items) {
|
|
2415
|
+
const id = getRealtimeItemId(item);
|
|
2416
|
+
if (!id) continue;
|
|
2417
|
+
const confirmedSerialized = board.lastServerConfirmedItemSerializations.get(id);
|
|
2418
|
+
const serialized = serializeRealtimeItem(item);
|
|
2419
|
+
if (!confirmedSerialized || !serialized || serialized !== confirmedSerialized) {
|
|
2420
|
+
pendingIds.add(id);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
return pendingIds.size > 0 ? Array.from(pendingIds) : void 0;
|
|
2424
|
+
}
|
|
2401
2425
|
function useRealtimeSession(options) {
|
|
2402
2426
|
const {
|
|
2403
2427
|
url,
|
|
@@ -2588,7 +2612,8 @@ function useRealtimeSession(options) {
|
|
|
2588
2612
|
}
|
|
2589
2613
|
const board = boardRef.current;
|
|
2590
2614
|
const draft = localDraftRef.current;
|
|
2591
|
-
const
|
|
2615
|
+
const canEncodeYDocState = board && draft.yDocState == null && sameSerializedItems(readVectorItems(board.yItems), draft.items);
|
|
2616
|
+
const draftToWrite = canEncodeYDocState ? { ...draft, yDocState: encodeYDocState(board) } : draft;
|
|
2592
2617
|
writeRealtimeOfflineDraft(draftToWrite);
|
|
2593
2618
|
}, [clearDraftPersistSchedule, roomId]);
|
|
2594
2619
|
const scheduleDraftPersistence = react.useCallback(() => {
|
|
@@ -2728,13 +2753,26 @@ function useRealtimeSession(options) {
|
|
|
2728
2753
|
}, DOCUMENT_FLUSH_DEBOUNCE_MS);
|
|
2729
2754
|
}, DOCUMENT_FLUSH_DEBOUNCE_MS);
|
|
2730
2755
|
}, [clearDocumentFlushSchedule, flushQueuedDocument]);
|
|
2731
|
-
const queueDocumentSend = react.useCallback(
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2756
|
+
const queueDocumentSend = react.useCallback(
|
|
2757
|
+
(items) => {
|
|
2758
|
+
pendingLocalItemsRef.current = items;
|
|
2759
|
+
queuedDirtyRef.current = true;
|
|
2760
|
+
setHasPendingDocumentSync(true);
|
|
2761
|
+
setHasLocalOfflineDraft(true);
|
|
2762
|
+
const board = boardRef.current;
|
|
2763
|
+
const draftItems = sanitizeRealtimeItems(items);
|
|
2764
|
+
setLocalDraftRef.current({
|
|
2765
|
+
roomId,
|
|
2766
|
+
baseRevision: currentRevisionRef.current,
|
|
2767
|
+
items: draftItems,
|
|
2768
|
+
updatedAt: nowMs(),
|
|
2769
|
+
pendingIds: board ? resolvePendingDraftItemIds(board, draftItems) : void 0
|
|
2770
|
+
});
|
|
2771
|
+
scheduleDraftPersistenceRef.current?.();
|
|
2772
|
+
scheduleDocumentFlushRef.current();
|
|
2773
|
+
},
|
|
2774
|
+
[roomId]
|
|
2775
|
+
);
|
|
2738
2776
|
const applyDraftSnapshot = react.useCallback(
|
|
2739
2777
|
(draft, options2) => {
|
|
2740
2778
|
const board = boardRef.current;
|
|
@@ -2898,6 +2936,25 @@ function useRealtimeSession(options) {
|
|
|
2898
2936
|
setLocalDraftRef.current = setLocalDraft;
|
|
2899
2937
|
const scheduleDraftPersistenceRef = react.useRef(scheduleDraftPersistence);
|
|
2900
2938
|
scheduleDraftPersistenceRef.current = scheduleDraftPersistence;
|
|
2939
|
+
const persistLocalDraftRef = react.useRef(persistLocalDraft);
|
|
2940
|
+
persistLocalDraftRef.current = persistLocalDraft;
|
|
2941
|
+
react.useEffect(() => {
|
|
2942
|
+
const persistDraft = () => {
|
|
2943
|
+
persistLocalDraftRef.current();
|
|
2944
|
+
};
|
|
2945
|
+
const handleVisibilityChange = () => {
|
|
2946
|
+
if (window.document.visibilityState === "hidden") persistDraft();
|
|
2947
|
+
};
|
|
2948
|
+
window.addEventListener("pagehide", persistDraft);
|
|
2949
|
+
window.document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
2950
|
+
return () => {
|
|
2951
|
+
window.removeEventListener("pagehide", persistDraft);
|
|
2952
|
+
window.document.removeEventListener(
|
|
2953
|
+
"visibilitychange",
|
|
2954
|
+
handleVisibilityChange
|
|
2955
|
+
);
|
|
2956
|
+
};
|
|
2957
|
+
}, []);
|
|
2901
2958
|
react.useEffect(() => {
|
|
2902
2959
|
if (boardRef.current) {
|
|
2903
2960
|
boardRef.current.doc.destroy();
|
|
@@ -3531,6 +3588,22 @@ function realtimeSessionPlugin(options) {
|
|
|
3531
3588
|
render: () => /* @__PURE__ */ jsxRuntime.jsx(RealtimeSessionPanel, { ...options })
|
|
3532
3589
|
};
|
|
3533
3590
|
}
|
|
3591
|
+
function getSceneItemId(item) {
|
|
3592
|
+
const id = item.id;
|
|
3593
|
+
return typeof id === "string" ? id : null;
|
|
3594
|
+
}
|
|
3595
|
+
function hasMissingLocalItems(localItems, incomingItems) {
|
|
3596
|
+
const incomingIds = /* @__PURE__ */ new Set();
|
|
3597
|
+
for (const item of incomingItems) {
|
|
3598
|
+
const id = getSceneItemId(item);
|
|
3599
|
+
if (id) incomingIds.add(id);
|
|
3600
|
+
}
|
|
3601
|
+
for (const item of localItems) {
|
|
3602
|
+
const id = getSceneItemId(item);
|
|
3603
|
+
if (id && !incomingIds.has(id)) return true;
|
|
3604
|
+
}
|
|
3605
|
+
return false;
|
|
3606
|
+
}
|
|
3534
3607
|
function useRealtimeCanvasDocument(options) {
|
|
3535
3608
|
const {
|
|
3536
3609
|
session,
|
|
@@ -3549,6 +3622,8 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3549
3622
|
const documentItems = session?.document?.items;
|
|
3550
3623
|
const documentUpdatedByClientId = session?.document?.updatedByClientId ?? null;
|
|
3551
3624
|
const connectionClientId = session?.connection.clientId ?? null;
|
|
3625
|
+
const hasLocalOfflineDraft = session?.hasLocalOfflineDraft ?? false;
|
|
3626
|
+
const hasPendingDocumentSync = session?.hasPendingDocumentSync ?? false;
|
|
3552
3627
|
const applyIncomingItems = react.useCallback(
|
|
3553
3628
|
async (nextItems) => {
|
|
3554
3629
|
const normalizedItems = normalizeItems ? normalizeItems(nextItems) : [...nextItems];
|
|
@@ -3569,6 +3644,11 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3569
3644
|
},
|
|
3570
3645
|
[enabled, normalizeItems, onItemsChange, session]
|
|
3571
3646
|
);
|
|
3647
|
+
react.useEffect(() => {
|
|
3648
|
+
if (items.length > 0) {
|
|
3649
|
+
hasEverPropagatedItemsRef.current = true;
|
|
3650
|
+
}
|
|
3651
|
+
}, [items.length]);
|
|
3572
3652
|
react.useEffect(() => {
|
|
3573
3653
|
if (!realtimeEnabled || !onItemsChange || !session?.document) return;
|
|
3574
3654
|
if (documentUpdatedByClientId === connectionClientId) return;
|
|
@@ -3582,7 +3662,18 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3582
3662
|
if (cancelled) return;
|
|
3583
3663
|
if (inFlightRevisionRef.current !== documentRevision) return;
|
|
3584
3664
|
lastAppliedRevisionRef.current = documentRevision;
|
|
3585
|
-
|
|
3665
|
+
const hasLocalItems = items.length > 0;
|
|
3666
|
+
const hasPendingLocalChanges = hasLocalOfflineDraft || hasPendingDocumentSync;
|
|
3667
|
+
if (resolvedItems.length === 0 && (hasEverPropagatedItemsRef.current || hasLocalItems || hasPendingLocalChanges)) {
|
|
3668
|
+
if (hasLocalItems) {
|
|
3669
|
+
const normalizedLocalItems = normalizeItems ? normalizeItems(items) : [...items];
|
|
3670
|
+
session.remoteAdapter.send?.(normalizedLocalItems);
|
|
3671
|
+
}
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
if (hasPendingLocalChanges && hasLocalItems && hasMissingLocalItems(items, resolvedItems)) {
|
|
3675
|
+
const normalizedLocalItems = normalizeItems ? normalizeItems(items) : [...items];
|
|
3676
|
+
session.remoteAdapter.send?.(normalizedLocalItems);
|
|
3586
3677
|
return;
|
|
3587
3678
|
}
|
|
3588
3679
|
if (resolvedItems.length > 0) {
|
|
@@ -3605,8 +3696,13 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3605
3696
|
documentItems,
|
|
3606
3697
|
documentRevision,
|
|
3607
3698
|
documentUpdatedByClientId,
|
|
3699
|
+
hasLocalOfflineDraft,
|
|
3700
|
+
hasPendingDocumentSync,
|
|
3701
|
+
items,
|
|
3702
|
+
normalizeItems,
|
|
3608
3703
|
onItemsChange,
|
|
3609
3704
|
realtimeEnabled,
|
|
3705
|
+
session?.remoteAdapter,
|
|
3610
3706
|
session?.document
|
|
3611
3707
|
]);
|
|
3612
3708
|
return react.useMemo(
|