canvu-react 0.3.39 → 0.4.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.
- package/dist/{asset-hydration-DowNdaOJ.d.cts → asset-hydration-B7yMDQE-.d.cts} +2 -2
- package/dist/{asset-hydration-DdFLdlqX.d.ts → asset-hydration-CbwQVAwh.d.ts} +2 -2
- package/dist/{camera-Di5R_Rwl.d.cts → camera-CVVG7z56.d.cts} +1 -1
- package/dist/{camera-AoTwBSoE.d.ts → camera-CoRYN_IV.d.ts} +1 -1
- package/dist/chatbot.d.cts +4 -4
- package/dist/chatbot.d.ts +4 -4
- package/dist/index.cjs +59 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +59 -15
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +57 -14
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +2 -2
- package/dist/native.d.ts +2 -2
- package/dist/native.js +57 -14
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +699 -255
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +10 -10
- package/dist/react.d.ts +10 -10
- package/dist/react.js +699 -255
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs +498 -129
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +6 -6
- package/dist/realtime.d.ts +6 -6
- package/dist/realtime.js +479 -129
- package/dist/realtime.js.map +1 -1
- package/dist/{shape-builders-Dedcl6tw.d.cts → shape-builders-BAWu-PxX.d.cts} +7 -3
- package/dist/{shape-builders-C7bxJBGR.d.ts → shape-builders-ClKv9tz9.d.ts} +7 -3
- package/dist/tldraw.cjs +56 -14
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.d.cts +1 -1
- package/dist/tldraw.d.ts +1 -1
- package/dist/tldraw.js +56 -14
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-DUW61Tjy.d.cts → types-BC9Xgfu6.d.cts} +11 -6
- package/dist/{types-Bnq2HtHQ.d.cts → types-BCCvY6ie.d.cts} +2 -0
- package/dist/{types-Bnq2HtHQ.d.ts → types-BCCvY6ie.d.ts} +2 -0
- package/dist/{types-B2Na677H.d.cts → types-BUPc2Zgw.d.cts} +1 -1
- package/dist/{types-zmUah-vP.d.ts → types-CYtq9Pr9.d.ts} +1 -1
- package/dist/{types-BBb8KoyW.d.ts → types-DlSVGX0w.d.ts} +11 -6
- package/package.json +3 -2
package/dist/realtime.cjs
CHANGED
|
@@ -4,10 +4,30 @@ var lucideReact = require('lucide-react');
|
|
|
4
4
|
var getStroke = require('perfect-freehand');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var react = require('react');
|
|
7
|
+
var Y = require('yjs');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
|
|
11
|
+
function _interopNamespace(e) {
|
|
12
|
+
if (e && e.__esModule) return e;
|
|
13
|
+
var n = Object.create(null);
|
|
14
|
+
if (e) {
|
|
15
|
+
Object.keys(e).forEach(function (k) {
|
|
16
|
+
if (k !== 'default') {
|
|
17
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return e[k]; }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
n.default = e;
|
|
26
|
+
return Object.freeze(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
var getStroke__default = /*#__PURE__*/_interopDefault(getStroke);
|
|
30
|
+
var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
|
|
11
31
|
|
|
12
32
|
// src/react/presence/map-placement-preview.ts
|
|
13
33
|
function remoteMarkupStrokeFromPlacementPreview(preview) {
|
|
@@ -155,6 +175,30 @@ function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware =
|
|
|
155
175
|
simulatePressure: true
|
|
156
176
|
};
|
|
157
177
|
}
|
|
178
|
+
function dashArrayForDrawStroke(strokeWidth) {
|
|
179
|
+
const dash = Math.max(strokeWidth * 1.8, 4);
|
|
180
|
+
const gap = Math.max(strokeWidth * 1.4, 3);
|
|
181
|
+
return `${dash} ${gap}`;
|
|
182
|
+
}
|
|
183
|
+
function buildSmoothedCenterlinePath(points) {
|
|
184
|
+
if (points.length < 2) return null;
|
|
185
|
+
const first = points[0];
|
|
186
|
+
if (!first) return null;
|
|
187
|
+
let d = `M ${first.x} ${first.y}`;
|
|
188
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
189
|
+
const a = points[i];
|
|
190
|
+
const b = points[i + 1];
|
|
191
|
+
if (!a || !b) continue;
|
|
192
|
+
const midX = (a.x + b.x) / 2;
|
|
193
|
+
const midY = (a.y + b.y) / 2;
|
|
194
|
+
d += ` Q ${a.x} ${a.y} ${midX} ${midY}`;
|
|
195
|
+
}
|
|
196
|
+
const last = points[points.length - 1];
|
|
197
|
+
if (last) {
|
|
198
|
+
d += ` L ${last.x} ${last.y}`;
|
|
199
|
+
}
|
|
200
|
+
return d;
|
|
201
|
+
}
|
|
158
202
|
function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeComplete = true) {
|
|
159
203
|
if (pathPointsLocal.length === 0) return null;
|
|
160
204
|
if (pathPointsLocal.length === 1) {
|
|
@@ -169,6 +213,18 @@ function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeCompl
|
|
|
169
213
|
fillOpacity: style.strokeOpacity
|
|
170
214
|
};
|
|
171
215
|
}
|
|
216
|
+
if (style.strokeDash === "dashed" && (toolKind === "draw" || toolKind === "pencil")) {
|
|
217
|
+
const d2 = buildSmoothedCenterlinePath(pathPointsLocal);
|
|
218
|
+
if (!d2) return null;
|
|
219
|
+
return {
|
|
220
|
+
kind: "strokePath",
|
|
221
|
+
d: d2,
|
|
222
|
+
stroke: style.stroke,
|
|
223
|
+
strokeWidth: style.strokeWidth,
|
|
224
|
+
strokeOpacity: style.strokeOpacity,
|
|
225
|
+
strokeDasharray: dashArrayForDrawStroke(style.strokeWidth)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
172
228
|
const hasPressure = pathPointsLocal.some(
|
|
173
229
|
(p) => p.pressure != null && Number.isFinite(p.pressure)
|
|
174
230
|
);
|
|
@@ -1956,8 +2012,241 @@ function useRealtimePeerFollow(options) {
|
|
|
1956
2012
|
lastAppliedCameraKeyRef.current = nextCameraKey;
|
|
1957
2013
|
}, [followedPeerId, onFollowEnd, sessionPeers, viewportRef]);
|
|
1958
2014
|
}
|
|
2015
|
+
var ITEMS_KEY = "items";
|
|
2016
|
+
var SERVER_ADD_RACE_WINDOW_MS = 2e3;
|
|
2017
|
+
function createYjsBoardDoc() {
|
|
2018
|
+
const doc = new Y__namespace.Doc();
|
|
2019
|
+
const yItems = doc.getArray(ITEMS_KEY);
|
|
2020
|
+
return {
|
|
2021
|
+
doc,
|
|
2022
|
+
yItems,
|
|
2023
|
+
lastServerConfirmedIds: /* @__PURE__ */ new Set(),
|
|
2024
|
+
serverItemSeenAt: /* @__PURE__ */ new Map()
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
function getItemId(item) {
|
|
2028
|
+
if (item instanceof Y__namespace.Map) {
|
|
2029
|
+
const id2 = item.get("id");
|
|
2030
|
+
return typeof id2 === "string" ? id2 : null;
|
|
2031
|
+
}
|
|
2032
|
+
const id = item.id;
|
|
2033
|
+
return typeof id === "string" ? id : null;
|
|
2034
|
+
}
|
|
2035
|
+
function vectorItemToYMap(item) {
|
|
2036
|
+
const yMap = new Y__namespace.Map();
|
|
2037
|
+
for (const [key, value] of Object.entries(item)) {
|
|
2038
|
+
if (value === void 0) continue;
|
|
2039
|
+
yMap.set(key, value);
|
|
2040
|
+
}
|
|
2041
|
+
return yMap;
|
|
2042
|
+
}
|
|
2043
|
+
function yMapToVectorItem(yMap) {
|
|
2044
|
+
const obj = {};
|
|
2045
|
+
for (const [key, value] of yMap.entries()) {
|
|
2046
|
+
obj[key] = value;
|
|
2047
|
+
}
|
|
2048
|
+
return obj;
|
|
2049
|
+
}
|
|
2050
|
+
function readVectorItems(yItems) {
|
|
2051
|
+
const result = [];
|
|
2052
|
+
for (let i = 0; i < yItems.length; i++) {
|
|
2053
|
+
const yMap = yItems.get(i);
|
|
2054
|
+
if (yMap) result.push(yMapToVectorItem(yMap));
|
|
2055
|
+
}
|
|
2056
|
+
return result;
|
|
2057
|
+
}
|
|
2058
|
+
function indexYItemsById(yItems) {
|
|
2059
|
+
const result = /* @__PURE__ */ new Map();
|
|
2060
|
+
for (let i = 0; i < yItems.length; i++) {
|
|
2061
|
+
const yMap = yItems.get(i);
|
|
2062
|
+
if (!yMap) continue;
|
|
2063
|
+
const id = getItemId(yMap);
|
|
2064
|
+
if (!id) continue;
|
|
2065
|
+
result.set(id, { yMap, index: i });
|
|
2066
|
+
}
|
|
2067
|
+
return result;
|
|
2068
|
+
}
|
|
2069
|
+
function valuesEqual(left, right) {
|
|
2070
|
+
if (left === right) return true;
|
|
2071
|
+
if (typeof left !== typeof right) return false;
|
|
2072
|
+
if (left === null || right === null) return false;
|
|
2073
|
+
if (typeof left !== "object") return false;
|
|
2074
|
+
try {
|
|
2075
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
2076
|
+
} catch {
|
|
2077
|
+
return false;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
function updateYMapInPlace(yMap, next) {
|
|
2081
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
2082
|
+
for (const [key, value] of Object.entries(next)) {
|
|
2083
|
+
if (value === void 0) continue;
|
|
2084
|
+
nextKeys.add(key);
|
|
2085
|
+
const current = yMap.get(key);
|
|
2086
|
+
if (!valuesEqual(current, value)) {
|
|
2087
|
+
yMap.set(key, value);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
for (const key of Array.from(yMap.keys())) {
|
|
2091
|
+
if (!nextKeys.has(key)) yMap.delete(key);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
function applyLocalItemsToYDoc(board, options) {
|
|
2095
|
+
const { items, origin } = options;
|
|
2096
|
+
const addedIds = [];
|
|
2097
|
+
const removedIds = [];
|
|
2098
|
+
const now = Date.now();
|
|
2099
|
+
board.doc.transact(() => {
|
|
2100
|
+
const currentIndex = indexYItemsById(board.yItems);
|
|
2101
|
+
const nextIds = /* @__PURE__ */ new Set();
|
|
2102
|
+
for (const item of items) {
|
|
2103
|
+
const id = getItemId(item);
|
|
2104
|
+
if (!id) continue;
|
|
2105
|
+
nextIds.add(id);
|
|
2106
|
+
}
|
|
2107
|
+
const toDelete = [];
|
|
2108
|
+
for (const [id, entry] of currentIndex) {
|
|
2109
|
+
if (nextIds.has(id)) continue;
|
|
2110
|
+
const serverSeenAt = board.serverItemSeenAt.get(id);
|
|
2111
|
+
if (serverSeenAt != null && now - serverSeenAt < SERVER_ADD_RACE_WINDOW_MS) {
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
toDelete.push({ id, index: entry.index });
|
|
2115
|
+
}
|
|
2116
|
+
toDelete.sort((a, b) => b.index - a.index);
|
|
2117
|
+
for (const { id, index } of toDelete) {
|
|
2118
|
+
board.yItems.delete(index, 1);
|
|
2119
|
+
board.serverItemSeenAt.delete(id);
|
|
2120
|
+
removedIds.push(id);
|
|
2121
|
+
}
|
|
2122
|
+
const refreshedIndex = indexYItemsById(board.yItems);
|
|
2123
|
+
for (let nextOrder = 0; nextOrder < items.length; nextOrder++) {
|
|
2124
|
+
const item = items[nextOrder];
|
|
2125
|
+
if (!item) continue;
|
|
2126
|
+
const id = getItemId(item);
|
|
2127
|
+
if (!id) continue;
|
|
2128
|
+
const existing = refreshedIndex.get(id);
|
|
2129
|
+
if (existing) {
|
|
2130
|
+
updateYMapInPlace(existing.yMap, item);
|
|
2131
|
+
continue;
|
|
2132
|
+
}
|
|
2133
|
+
const yMap = vectorItemToYMap(item);
|
|
2134
|
+
board.yItems.push([yMap]);
|
|
2135
|
+
addedIds.push(id);
|
|
2136
|
+
}
|
|
2137
|
+
}, origin);
|
|
2138
|
+
return { addedIds, removedIds };
|
|
2139
|
+
}
|
|
2140
|
+
function applyServerSnapshotToYDoc(board, options) {
|
|
2141
|
+
const { items: snapshotItems, origin } = options;
|
|
2142
|
+
const now = Date.now();
|
|
2143
|
+
board.doc.transact(() => {
|
|
2144
|
+
const snapshotIds = /* @__PURE__ */ new Set();
|
|
2145
|
+
const snapshotById = /* @__PURE__ */ new Map();
|
|
2146
|
+
for (const item of snapshotItems) {
|
|
2147
|
+
const id = getItemId(item);
|
|
2148
|
+
if (!id) continue;
|
|
2149
|
+
snapshotIds.add(id);
|
|
2150
|
+
snapshotById.set(id, item);
|
|
2151
|
+
}
|
|
2152
|
+
const currentIndex = indexYItemsById(board.yItems);
|
|
2153
|
+
const toDeleteIds = [];
|
|
2154
|
+
for (const [id] of currentIndex) {
|
|
2155
|
+
if (snapshotIds.has(id)) continue;
|
|
2156
|
+
if (board.lastServerConfirmedIds.has(id)) {
|
|
2157
|
+
toDeleteIds.push(id);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
if (toDeleteIds.length > 0) {
|
|
2161
|
+
const refreshed = indexYItemsById(board.yItems);
|
|
2162
|
+
const sortedIndices = toDeleteIds.map((id) => ({ id, index: refreshed.get(id)?.index })).filter(
|
|
2163
|
+
(entry) => entry.index != null
|
|
2164
|
+
).sort((a, b) => b.index - a.index);
|
|
2165
|
+
for (const { id, index } of sortedIndices) {
|
|
2166
|
+
board.yItems.delete(index, 1);
|
|
2167
|
+
board.serverItemSeenAt.delete(id);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
for (const item of snapshotItems) {
|
|
2171
|
+
const id = getItemId(item);
|
|
2172
|
+
if (!id) continue;
|
|
2173
|
+
const existing = indexYItemsById(board.yItems).get(id);
|
|
2174
|
+
if (existing) {
|
|
2175
|
+
updateYMapInPlace(existing.yMap, item);
|
|
2176
|
+
board.serverItemSeenAt.set(id, now);
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
const yMap = vectorItemToYMap(item);
|
|
2180
|
+
board.yItems.push([yMap]);
|
|
2181
|
+
board.serverItemSeenAt.set(id, now);
|
|
2182
|
+
}
|
|
2183
|
+
}, origin);
|
|
2184
|
+
board.lastServerConfirmedIds = /* @__PURE__ */ new Set();
|
|
2185
|
+
for (const item of snapshotItems) {
|
|
2186
|
+
const id = getItemId(item);
|
|
2187
|
+
if (id) board.lastServerConfirmedIds.add(id);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
function replaceYDocWithSnapshot(board, options) {
|
|
2191
|
+
const { items: snapshotItems, origin } = options;
|
|
2192
|
+
const now = Date.now();
|
|
2193
|
+
board.doc.transact(() => {
|
|
2194
|
+
if (board.yItems.length > 0) {
|
|
2195
|
+
board.yItems.delete(0, board.yItems.length);
|
|
2196
|
+
}
|
|
2197
|
+
for (const item of snapshotItems) {
|
|
2198
|
+
board.yItems.push([vectorItemToYMap(item)]);
|
|
2199
|
+
}
|
|
2200
|
+
}, origin);
|
|
2201
|
+
board.lastServerConfirmedIds = /* @__PURE__ */ new Set();
|
|
2202
|
+
board.serverItemSeenAt = /* @__PURE__ */ new Map();
|
|
2203
|
+
for (const item of snapshotItems) {
|
|
2204
|
+
const id = getItemId(item);
|
|
2205
|
+
if (id) {
|
|
2206
|
+
board.lastServerConfirmedIds.add(id);
|
|
2207
|
+
board.serverItemSeenAt.set(id, now);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
function getLocallyPendingItemIds(board) {
|
|
2212
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2213
|
+
for (let i = 0; i < board.yItems.length; i++) {
|
|
2214
|
+
const yMap = board.yItems.get(i);
|
|
2215
|
+
if (!yMap) continue;
|
|
2216
|
+
const id = getItemId(yMap);
|
|
2217
|
+
if (!id) continue;
|
|
2218
|
+
if (!board.lastServerConfirmedIds.has(id)) {
|
|
2219
|
+
pending.add(id);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
return pending;
|
|
2223
|
+
}
|
|
2224
|
+
function encodeYDocState(board) {
|
|
2225
|
+
const update = Y__namespace.encodeStateAsUpdate(board.doc);
|
|
2226
|
+
let binary = "";
|
|
2227
|
+
for (let i = 0; i < update.length; i++) {
|
|
2228
|
+
binary += String.fromCharCode(update[i]);
|
|
2229
|
+
}
|
|
2230
|
+
return btoa(binary);
|
|
2231
|
+
}
|
|
2232
|
+
function decodeYDocState(board, encoded) {
|
|
2233
|
+
try {
|
|
2234
|
+
const binary = atob(encoded);
|
|
2235
|
+
const update = new Uint8Array(binary.length);
|
|
2236
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2237
|
+
update[i] = binary.charCodeAt(i);
|
|
2238
|
+
}
|
|
2239
|
+
Y__namespace.applyUpdate(board.doc, update);
|
|
2240
|
+
return true;
|
|
2241
|
+
} catch {
|
|
2242
|
+
return false;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
1959
2245
|
|
|
1960
2246
|
// src/react/plugins/realtime/use-realtime-session.ts
|
|
2247
|
+
var ORIGIN_LOCAL = /* @__PURE__ */ Symbol("canvu/realtime/local");
|
|
2248
|
+
var ORIGIN_REMOTE = /* @__PURE__ */ Symbol("canvu/realtime/remote");
|
|
2249
|
+
var ORIGIN_BOOTSTRAP = /* @__PURE__ */ Symbol("canvu/realtime/bootstrap");
|
|
1961
2250
|
var DRAFT_STORAGE_PREFIX = "canvu-realtime-draft:";
|
|
1962
2251
|
var DOCUMENT_FLUSH_DEBOUNCE_MS = 120;
|
|
1963
2252
|
var DRAFT_PERSIST_DEBOUNCE_MS = 420;
|
|
@@ -2044,7 +2333,16 @@ function draftStorageKey(roomId) {
|
|
|
2044
2333
|
function isRealtimeOfflineDraft(value) {
|
|
2045
2334
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2046
2335
|
const record = value;
|
|
2047
|
-
|
|
2336
|
+
if (typeof record.roomId !== "string" || typeof record.baseRevision !== "number" || typeof record.updatedAt !== "number" || !Array.isArray(record.items)) {
|
|
2337
|
+
return false;
|
|
2338
|
+
}
|
|
2339
|
+
if (record.yDocState != null && typeof record.yDocState !== "string") {
|
|
2340
|
+
return false;
|
|
2341
|
+
}
|
|
2342
|
+
if (record.pendingIds != null && !Array.isArray(record.pendingIds)) {
|
|
2343
|
+
return false;
|
|
2344
|
+
}
|
|
2345
|
+
return true;
|
|
2048
2346
|
}
|
|
2049
2347
|
function readRealtimeOfflineDraft(roomId) {
|
|
2050
2348
|
if (typeof window === "undefined" || !roomId) return null;
|
|
@@ -2082,14 +2380,6 @@ function removeRealtimeOfflineDraft(roomId) {
|
|
|
2082
2380
|
} catch {
|
|
2083
2381
|
}
|
|
2084
2382
|
}
|
|
2085
|
-
function buildDraftSnapshot(draft, clientId) {
|
|
2086
|
-
return {
|
|
2087
|
-
revision: draft.baseRevision,
|
|
2088
|
-
items: draft.items,
|
|
2089
|
-
updatedAt: draft.updatedAt,
|
|
2090
|
-
updatedByClientId: clientId
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
2383
|
function getViewportCameraSnapshot(viewport) {
|
|
2094
2384
|
if (!viewport) return null;
|
|
2095
2385
|
const camera = viewport.getCamera();
|
|
@@ -2140,8 +2430,13 @@ function useRealtimeSession(options) {
|
|
|
2140
2430
|
const retryCountRef = react.useRef(0);
|
|
2141
2431
|
const currentRevisionRef = react.useRef(0);
|
|
2142
2432
|
const outboundInFlightRef = react.useRef(null);
|
|
2143
|
-
const
|
|
2433
|
+
const queuedDirtyRef = react.useRef(false);
|
|
2434
|
+
const pendingLocalItemsRef = react.useRef(null);
|
|
2144
2435
|
const subscriberRefs = react.useRef(/* @__PURE__ */ new Set());
|
|
2436
|
+
const boardRef = react.useRef(null);
|
|
2437
|
+
if (boardRef.current == null) {
|
|
2438
|
+
boardRef.current = createYjsBoardDoc();
|
|
2439
|
+
}
|
|
2145
2440
|
const lastCursorRef = react.useRef(null);
|
|
2146
2441
|
const lastMarkupStrokeRef = react.useRef(null);
|
|
2147
2442
|
const lastCameraRef = react.useRef(
|
|
@@ -2236,11 +2531,30 @@ function useRealtimeSession(options) {
|
|
|
2236
2531
|
if (currentSnapshot && currentSnapshot.revision === snapshot.revision && currentSnapshot.updatedByClientId === snapshot.updatedByClientId && sameSerializedItems(currentSnapshot.items, snapshot.items)) {
|
|
2237
2532
|
return;
|
|
2238
2533
|
}
|
|
2534
|
+
const board = boardRef.current;
|
|
2535
|
+
if (board) {
|
|
2536
|
+
if (options2?.replace) {
|
|
2537
|
+
replaceYDocWithSnapshot(board, {
|
|
2538
|
+
items: snapshot.items,
|
|
2539
|
+
origin: ORIGIN_REMOTE
|
|
2540
|
+
});
|
|
2541
|
+
} else {
|
|
2542
|
+
applyServerSnapshotToYDoc(board, {
|
|
2543
|
+
items: snapshot.items,
|
|
2544
|
+
origin: ORIGIN_REMOTE
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
const mergedItems = board ? readVectorItems(board.yItems) : snapshot.items;
|
|
2549
|
+
const mergedSnapshot = {
|
|
2550
|
+
...snapshot,
|
|
2551
|
+
items: mergedItems
|
|
2552
|
+
};
|
|
2239
2553
|
currentRevisionRef.current = snapshot.revision;
|
|
2240
|
-
latestDocumentRef.current =
|
|
2241
|
-
setDocument(
|
|
2554
|
+
latestDocumentRef.current = mergedSnapshot;
|
|
2555
|
+
setDocument(mergedSnapshot);
|
|
2242
2556
|
if (!options2?.suppressSubscriberNotify) {
|
|
2243
|
-
notifySubscribers(
|
|
2557
|
+
notifySubscribers(mergedItems);
|
|
2244
2558
|
}
|
|
2245
2559
|
},
|
|
2246
2560
|
[notifySubscribers]
|
|
@@ -2350,13 +2664,23 @@ function useRealtimeSession(options) {
|
|
|
2350
2664
|
);
|
|
2351
2665
|
const flushQueuedDocument = react.useCallback(() => {
|
|
2352
2666
|
clearDocumentFlushSchedule();
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
if (!
|
|
2667
|
+
const board = boardRef.current;
|
|
2668
|
+
if (!board) return;
|
|
2669
|
+
if (!queuedDirtyRef.current && pendingLocalItemsRef.current == null) return;
|
|
2670
|
+
if (outboundInFlightRef.current) return;
|
|
2671
|
+
const pendingLocal = pendingLocalItemsRef.current;
|
|
2672
|
+
if (pendingLocal) {
|
|
2673
|
+
pendingLocalItemsRef.current = null;
|
|
2674
|
+
applyLocalItemsToYDoc(board, {
|
|
2675
|
+
items: pendingLocal,
|
|
2676
|
+
origin: ORIGIN_LOCAL
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2356
2679
|
const baseRevision = currentRevisionRef.current;
|
|
2357
|
-
const
|
|
2680
|
+
const mergedItems = readVectorItems(board.yItems);
|
|
2681
|
+
const preparedItems = prepareRealtimeItems(mergedItems);
|
|
2358
2682
|
if (!preparedItems) return;
|
|
2359
|
-
|
|
2683
|
+
queuedDirtyRef.current = false;
|
|
2360
2684
|
outboundInFlightRef.current = {
|
|
2361
2685
|
baseRevision,
|
|
2362
2686
|
items: preparedItems.items,
|
|
@@ -2369,8 +2693,20 @@ function useRealtimeSession(options) {
|
|
|
2369
2693
|
baseRevision,
|
|
2370
2694
|
items: preparedItems.items
|
|
2371
2695
|
});
|
|
2696
|
+
const pendingIds = getLocallyPendingItemIds(board);
|
|
2697
|
+
if (pendingIds.size > 0) {
|
|
2698
|
+
setLocalDraftRef.current({
|
|
2699
|
+
roomId,
|
|
2700
|
+
baseRevision,
|
|
2701
|
+
items: mergedItems,
|
|
2702
|
+
updatedAt: nowMs(),
|
|
2703
|
+
yDocState: encodeYDocState(board),
|
|
2704
|
+
pendingIds: Array.from(pendingIds)
|
|
2705
|
+
});
|
|
2706
|
+
scheduleDraftPersistenceRef.current?.();
|
|
2707
|
+
}
|
|
2372
2708
|
if (!didSend) {
|
|
2373
|
-
|
|
2709
|
+
queuedDirtyRef.current = true;
|
|
2374
2710
|
outboundInFlightRef.current = null;
|
|
2375
2711
|
setHasPendingDocumentSync(true);
|
|
2376
2712
|
}
|
|
@@ -2385,66 +2721,103 @@ function useRealtimeSession(options) {
|
|
|
2385
2721
|
}, DOCUMENT_FLUSH_DEBOUNCE_MS);
|
|
2386
2722
|
}, DOCUMENT_FLUSH_DEBOUNCE_MS);
|
|
2387
2723
|
}, [clearDocumentFlushSchedule, flushQueuedDocument]);
|
|
2388
|
-
const queueDocumentSend = react.useCallback(
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
});
|
|
2396
|
-
scheduleDraftPersistence();
|
|
2397
|
-
queuedItemsRef.current = items;
|
|
2398
|
-
setHasPendingDocumentSync(true);
|
|
2399
|
-
if (conflictRef.current) return;
|
|
2400
|
-
scheduleDocumentFlushRef.current();
|
|
2401
|
-
},
|
|
2402
|
-
[roomId, scheduleDraftPersistence, setLocalDraft]
|
|
2403
|
-
);
|
|
2724
|
+
const queueDocumentSend = react.useCallback((items) => {
|
|
2725
|
+
pendingLocalItemsRef.current = items;
|
|
2726
|
+
queuedDirtyRef.current = true;
|
|
2727
|
+
setHasPendingDocumentSync(true);
|
|
2728
|
+
setHasLocalOfflineDraft(true);
|
|
2729
|
+
scheduleDocumentFlushRef.current();
|
|
2730
|
+
}, []);
|
|
2404
2731
|
const applyDraftSnapshot = react.useCallback(
|
|
2405
2732
|
(draft, options2) => {
|
|
2406
|
-
|
|
2733
|
+
const board = boardRef.current;
|
|
2734
|
+
if (board) {
|
|
2735
|
+
let restoredFromBinary = false;
|
|
2736
|
+
if (draft.yDocState) {
|
|
2737
|
+
if (board.yItems.length > 0) {
|
|
2738
|
+
board.doc.transact(() => {
|
|
2739
|
+
board.yItems.delete(0, board.yItems.length);
|
|
2740
|
+
}, ORIGIN_BOOTSTRAP);
|
|
2741
|
+
}
|
|
2742
|
+
restoredFromBinary = decodeYDocState(board, draft.yDocState);
|
|
2743
|
+
if (restoredFromBinary) {
|
|
2744
|
+
const allIds = /* @__PURE__ */ new Set();
|
|
2745
|
+
for (let i = 0; i < board.yItems.length; i++) {
|
|
2746
|
+
const yMap = board.yItems.get(i);
|
|
2747
|
+
if (!yMap) continue;
|
|
2748
|
+
const id = yMap.get("id");
|
|
2749
|
+
if (typeof id === "string") allIds.add(id);
|
|
2750
|
+
}
|
|
2751
|
+
const pendingIds = new Set(draft.pendingIds ?? []);
|
|
2752
|
+
board.lastServerConfirmedIds = /* @__PURE__ */ new Set();
|
|
2753
|
+
for (const id of allIds) {
|
|
2754
|
+
if (!pendingIds.has(id)) {
|
|
2755
|
+
board.lastServerConfirmedIds.add(id);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
if (!restoredFromBinary) {
|
|
2761
|
+
replaceYDocWithSnapshot(board, {
|
|
2762
|
+
items: draft.items,
|
|
2763
|
+
origin: ORIGIN_BOOTSTRAP
|
|
2764
|
+
});
|
|
2765
|
+
board.lastServerConfirmedIds = /* @__PURE__ */ new Set();
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
const items = board ? readVectorItems(board.yItems) : draft.items;
|
|
2769
|
+
const snapshot = {
|
|
2770
|
+
revision: draft.baseRevision,
|
|
2771
|
+
items,
|
|
2772
|
+
updatedAt: draft.updatedAt,
|
|
2773
|
+
updatedByClientId: clientIdRef.current
|
|
2774
|
+
};
|
|
2775
|
+
currentRevisionRef.current = snapshot.revision;
|
|
2776
|
+
latestDocumentRef.current = snapshot;
|
|
2777
|
+
setDocument(snapshot);
|
|
2778
|
+
if (!options2?.suppressSubscriberNotify) {
|
|
2779
|
+
notifySubscribers(items);
|
|
2780
|
+
}
|
|
2407
2781
|
},
|
|
2408
|
-
[
|
|
2782
|
+
[notifySubscribers]
|
|
2409
2783
|
);
|
|
2410
2784
|
const resolveAuthoritativeDocument = react.useCallback(
|
|
2411
2785
|
(serverDocument, options2) => {
|
|
2412
|
-
const
|
|
2413
|
-
|
|
2414
|
-
|
|
2786
|
+
const board = boardRef.current;
|
|
2787
|
+
const hadLocalContent = localDraftRef.current != null || board != null && board.yItems.length > 0;
|
|
2788
|
+
applyDocument(serverDocument, {
|
|
2789
|
+
...options2,
|
|
2790
|
+
replace: !hadLocalContent
|
|
2791
|
+
});
|
|
2792
|
+
if (!board) {
|
|
2415
2793
|
setHasPendingDocumentSync(false);
|
|
2416
|
-
|
|
2794
|
+
clearLocalDraftRef.current();
|
|
2417
2795
|
return false;
|
|
2418
2796
|
}
|
|
2419
|
-
|
|
2797
|
+
const pendingIds = getLocallyPendingItemIds(board);
|
|
2798
|
+
if (pendingIds.size === 0) {
|
|
2420
2799
|
clearLocalDraftRef.current();
|
|
2421
2800
|
setHasPendingDocumentSync(false);
|
|
2422
|
-
setConflictState(null);
|
|
2423
|
-
applyDocument(serverDocument, options2);
|
|
2424
|
-
return true;
|
|
2425
|
-
}
|
|
2426
|
-
if (serverDocument.revision === localDraft.baseRevision) {
|
|
2427
|
-
setConflictState(null);
|
|
2428
|
-
applyDraftSnapshot(localDraft, {
|
|
2429
|
-
suppressSubscriberNotify: options2?.suppressSubscriberNotify ?? sameSerializedItems(latestDocumentRef.current?.items, localDraft.items)
|
|
2430
|
-
});
|
|
2431
2801
|
outboundInFlightRef.current = null;
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
scheduleDocumentFlush();
|
|
2435
|
-
return true;
|
|
2802
|
+
queuedDirtyRef.current = false;
|
|
2803
|
+
return false;
|
|
2436
2804
|
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2805
|
+
const mergedItems = readVectorItems(board.yItems);
|
|
2806
|
+
setLocalDraft({
|
|
2807
|
+
roomId,
|
|
2808
|
+
baseRevision: serverDocument.revision,
|
|
2809
|
+
items: mergedItems,
|
|
2810
|
+
updatedAt: nowMs(),
|
|
2811
|
+
yDocState: encodeYDocState(board),
|
|
2812
|
+
pendingIds: Array.from(pendingIds)
|
|
2444
2813
|
});
|
|
2814
|
+
outboundInFlightRef.current = null;
|
|
2815
|
+
queuedDirtyRef.current = true;
|
|
2816
|
+
setHasPendingDocumentSync(true);
|
|
2817
|
+
scheduleDocumentFlush();
|
|
2445
2818
|
return true;
|
|
2446
2819
|
},
|
|
2447
|
-
[applyDocument,
|
|
2820
|
+
[applyDocument, roomId, scheduleDocumentFlush, setLocalDraft]
|
|
2448
2821
|
);
|
|
2449
2822
|
const sendPresenceUpdate = react.useCallback(() => {
|
|
2450
2823
|
sendRaw({
|
|
@@ -2483,54 +2856,8 @@ function useRealtimeSession(options) {
|
|
|
2483
2856
|
reconnect,
|
|
2484
2857
|
updateConnection
|
|
2485
2858
|
]);
|
|
2486
|
-
const resolveConflict = react.useCallback(
|
|
2487
|
-
|
|
2488
|
-
const activeConflict = conflictRef.current;
|
|
2489
|
-
if (!activeConflict) return;
|
|
2490
|
-
if (action === "use-server") {
|
|
2491
|
-
clearLocalDraft();
|
|
2492
|
-
queuedItemsRef.current = null;
|
|
2493
|
-
outboundInFlightRef.current = null;
|
|
2494
|
-
setConflictState(null);
|
|
2495
|
-
applyDocument({
|
|
2496
|
-
revision: activeConflict.serverRevision,
|
|
2497
|
-
items: activeConflict.serverItems,
|
|
2498
|
-
updatedAt: nowMs()
|
|
2499
|
-
});
|
|
2500
|
-
return;
|
|
2501
|
-
}
|
|
2502
|
-
const nextDraft = {
|
|
2503
|
-
roomId,
|
|
2504
|
-
baseRevision: activeConflict.serverRevision,
|
|
2505
|
-
items: activeConflict.localItems,
|
|
2506
|
-
updatedAt: nowMs()
|
|
2507
|
-
};
|
|
2508
|
-
setLocalDraft(nextDraft);
|
|
2509
|
-
scheduleDraftPersistence();
|
|
2510
|
-
setConflictState(null);
|
|
2511
|
-
applyDraftSnapshot(nextDraft, {
|
|
2512
|
-
suppressSubscriberNotify: sameSerializedItems(
|
|
2513
|
-
latestDocumentRef.current?.items,
|
|
2514
|
-
nextDraft.items
|
|
2515
|
-
)
|
|
2516
|
-
});
|
|
2517
|
-
currentRevisionRef.current = activeConflict.serverRevision;
|
|
2518
|
-
queuedItemsRef.current = nextDraft.items;
|
|
2519
|
-
outboundInFlightRef.current = null;
|
|
2520
|
-
setHasPendingDocumentSync(true);
|
|
2521
|
-
scheduleDocumentFlush();
|
|
2522
|
-
},
|
|
2523
|
-
[
|
|
2524
|
-
applyDocument,
|
|
2525
|
-
applyDraftSnapshot,
|
|
2526
|
-
clearLocalDraft,
|
|
2527
|
-
roomId,
|
|
2528
|
-
scheduleDocumentFlush,
|
|
2529
|
-
scheduleDraftPersistence,
|
|
2530
|
-
setConflictState,
|
|
2531
|
-
setLocalDraft
|
|
2532
|
-
]
|
|
2533
|
-
);
|
|
2859
|
+
const resolveConflict = react.useCallback((_action) => {
|
|
2860
|
+
}, []);
|
|
2534
2861
|
const setConflictStateRef = react.useRef(setConflictState);
|
|
2535
2862
|
setConflictStateRef.current = setConflictState;
|
|
2536
2863
|
const updateConnectionRef = react.useRef(updateConnection);
|
|
@@ -2551,7 +2878,15 @@ function useRealtimeSession(options) {
|
|
|
2551
2878
|
scheduleReconnectRef.current = scheduleReconnect;
|
|
2552
2879
|
const sendRawRef = react.useRef(sendRaw);
|
|
2553
2880
|
sendRawRef.current = sendRaw;
|
|
2881
|
+
const setLocalDraftRef = react.useRef(setLocalDraft);
|
|
2882
|
+
setLocalDraftRef.current = setLocalDraft;
|
|
2883
|
+
const scheduleDraftPersistenceRef = react.useRef(scheduleDraftPersistence);
|
|
2884
|
+
scheduleDraftPersistenceRef.current = scheduleDraftPersistence;
|
|
2554
2885
|
react.useEffect(() => {
|
|
2886
|
+
if (boardRef.current) {
|
|
2887
|
+
boardRef.current.doc.destroy();
|
|
2888
|
+
}
|
|
2889
|
+
boardRef.current = createYjsBoardDoc();
|
|
2555
2890
|
if (!roomId) {
|
|
2556
2891
|
clearDocumentFlushSchedule();
|
|
2557
2892
|
clearDraftPersistSchedule();
|
|
@@ -2559,7 +2894,8 @@ function useRealtimeSession(options) {
|
|
|
2559
2894
|
setHasLocalOfflineDraft(false);
|
|
2560
2895
|
setHasPendingDocumentSync(false);
|
|
2561
2896
|
setConflictState(null);
|
|
2562
|
-
|
|
2897
|
+
queuedDirtyRef.current = false;
|
|
2898
|
+
pendingLocalItemsRef.current = null;
|
|
2563
2899
|
outboundInFlightRef.current = null;
|
|
2564
2900
|
latestDocumentRef.current = null;
|
|
2565
2901
|
setDocument(null);
|
|
@@ -2570,7 +2906,8 @@ function useRealtimeSession(options) {
|
|
|
2570
2906
|
setLocalDraft(localDraft);
|
|
2571
2907
|
setHasPendingDocumentSync(localDraft != null);
|
|
2572
2908
|
setConflictState(null);
|
|
2573
|
-
|
|
2909
|
+
queuedDirtyRef.current = localDraft != null;
|
|
2910
|
+
pendingLocalItemsRef.current = null;
|
|
2574
2911
|
outboundInFlightRef.current = null;
|
|
2575
2912
|
if (localDraft) {
|
|
2576
2913
|
applyDraftSnapshot(localDraft, {
|
|
@@ -2601,7 +2938,7 @@ function useRealtimeSession(options) {
|
|
|
2601
2938
|
clearDocumentFlushSchedule();
|
|
2602
2939
|
wsRef.current?.close();
|
|
2603
2940
|
wsRef.current = null;
|
|
2604
|
-
|
|
2941
|
+
queuedDirtyRef.current = localDraftRef.current != null;
|
|
2605
2942
|
outboundInFlightRef.current = null;
|
|
2606
2943
|
setHasPendingDocumentSync(localDraftRef.current != null);
|
|
2607
2944
|
collapsePeersToSelfRef.current("offline");
|
|
@@ -2716,7 +3053,7 @@ function useRealtimeSession(options) {
|
|
|
2716
3053
|
}
|
|
2717
3054
|
);
|
|
2718
3055
|
if (!handledByDraft) {
|
|
2719
|
-
|
|
3056
|
+
queuedDirtyRef.current = false;
|
|
2720
3057
|
outboundInFlightRef.current = null;
|
|
2721
3058
|
setHasPendingDocumentSync(false);
|
|
2722
3059
|
}
|
|
@@ -2767,8 +3104,6 @@ function useRealtimeSession(options) {
|
|
|
2767
3104
|
const isSelfAck = parsed.document.updatedByClientId === selfClientId;
|
|
2768
3105
|
if (!isSelfAck) {
|
|
2769
3106
|
outboundInFlightRef.current = null;
|
|
2770
|
-
queuedItemsRef.current = localDraftRef.current?.items ?? null;
|
|
2771
|
-
setHasPendingDocumentSync(queuedItemsRef.current != null);
|
|
2772
3107
|
resolveAuthoritativeDocumentRef.current(
|
|
2773
3108
|
sanitizeRealtimeSnapshot(parsed.document)
|
|
2774
3109
|
);
|
|
@@ -2779,24 +3114,33 @@ function useRealtimeSession(options) {
|
|
|
2779
3114
|
applyDocumentRef.current(sanitizeRealtimeSnapshot(parsed.document), {
|
|
2780
3115
|
suppressSubscriberNotify: shouldSuppress
|
|
2781
3116
|
});
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
3117
|
+
const board = boardRef.current;
|
|
3118
|
+
const stillPending = board ? getLocallyPendingItemIds(board).size > 0 : false;
|
|
3119
|
+
if (stillPending) {
|
|
3120
|
+
const mergedItems = board ? readVectorItems(board.yItems) : [];
|
|
3121
|
+
setLocalDraftRef.current({
|
|
3122
|
+
roomId,
|
|
3123
|
+
baseRevision: currentRevisionRef.current,
|
|
3124
|
+
items: mergedItems,
|
|
3125
|
+
updatedAt: nowMs(),
|
|
3126
|
+
yDocState: board ? encodeYDocState(board) : void 0,
|
|
3127
|
+
pendingIds: board ? Array.from(getLocallyPendingItemIds(board)) : void 0
|
|
3128
|
+
});
|
|
3129
|
+
queuedDirtyRef.current = true;
|
|
3130
|
+
setHasPendingDocumentSync(true);
|
|
3131
|
+
scheduleDocumentFlushRef.current();
|
|
3132
|
+
} else {
|
|
3133
|
+
queuedDirtyRef.current = false;
|
|
2790
3134
|
clearLocalDraftRef.current();
|
|
3135
|
+
setHasPendingDocumentSync(false);
|
|
2791
3136
|
}
|
|
2792
|
-
setHasPendingDocumentSync(queuedItemsRef.current != null);
|
|
2793
3137
|
setConflictStateRef.current(null);
|
|
2794
3138
|
return;
|
|
2795
3139
|
}
|
|
2796
3140
|
if (parsed.type === "document:resync-required") {
|
|
2797
3141
|
outboundInFlightRef.current = null;
|
|
2798
|
-
|
|
2799
|
-
setHasPendingDocumentSync(
|
|
3142
|
+
queuedDirtyRef.current = localDraftRef.current != null;
|
|
3143
|
+
setHasPendingDocumentSync(queuedDirtyRef.current);
|
|
2800
3144
|
updateConnectionRef.current((prev) => ({
|
|
2801
3145
|
...prev,
|
|
2802
3146
|
lastError: parsed.reason
|
|
@@ -2871,14 +3215,39 @@ function useRealtimeSession(options) {
|
|
|
2871
3215
|
() => () => {
|
|
2872
3216
|
clearDocumentFlushSchedule();
|
|
2873
3217
|
clearDraftPersistSchedule();
|
|
3218
|
+
if (boardRef.current) {
|
|
3219
|
+
boardRef.current.doc.destroy();
|
|
3220
|
+
boardRef.current = null;
|
|
3221
|
+
}
|
|
2874
3222
|
},
|
|
2875
3223
|
[clearDocumentFlushSchedule, clearDraftPersistSchedule]
|
|
2876
3224
|
);
|
|
2877
3225
|
const flushDocumentSync = react.useCallback(async () => {
|
|
3226
|
+
const board = boardRef.current;
|
|
3227
|
+
const pendingLocal = pendingLocalItemsRef.current;
|
|
3228
|
+
if (board && pendingLocal) {
|
|
3229
|
+
pendingLocalItemsRef.current = null;
|
|
3230
|
+
applyLocalItemsToYDoc(board, {
|
|
3231
|
+
items: pendingLocal,
|
|
3232
|
+
origin: ORIGIN_LOCAL
|
|
3233
|
+
});
|
|
3234
|
+
const mergedItems = readVectorItems(board.yItems);
|
|
3235
|
+
const pendingIds = getLocallyPendingItemIds(board);
|
|
3236
|
+
if (pendingIds.size > 0) {
|
|
3237
|
+
setLocalDraftRef.current({
|
|
3238
|
+
roomId,
|
|
3239
|
+
baseRevision: currentRevisionRef.current,
|
|
3240
|
+
items: mergedItems,
|
|
3241
|
+
updatedAt: nowMs(),
|
|
3242
|
+
yDocState: encodeYDocState(board),
|
|
3243
|
+
pendingIds: Array.from(pendingIds)
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
2878
3247
|
persistLocalDraft();
|
|
2879
3248
|
if (!connection.connected) return;
|
|
2880
3249
|
flushQueuedDocument();
|
|
2881
|
-
}, [connection.connected, flushQueuedDocument, persistLocalDraft]);
|
|
3250
|
+
}, [connection.connected, flushQueuedDocument, persistLocalDraft, roomId]);
|
|
2882
3251
|
const remoteAdapter = react.useMemo(
|
|
2883
3252
|
() => ({
|
|
2884
3253
|
subscribe(onItems) {
|