Offline support with increments peristed and restored to / from indexedb

This commit is contained in:
Marcel Mraz 2024-12-12 14:41:20 +01:00
parent 15d2942aaa
commit 040a57f56a
No known key found for this signature in database
GPG key ID: 4EBD6E62DC830CD2
19 changed files with 1827 additions and 1104 deletions

View file

@ -75,7 +75,6 @@ import {
exportToExcalidrawPlus,
} from "./components/ExportToExcalidrawPlus";
import { updateStaleImageStatuses } from "./data/FileManager";
import { newElementWith } from "../packages/excalidraw/element/mutateElement";
import { isInitializedImageElement } from "../packages/excalidraw/element/typeChecks";
import { loadFilesFromFirebase } from "./data/firebase";
import {
@ -372,8 +371,8 @@ const ExcalidrawWrapper = () => {
const [syncAPI] = useAtom(syncAPIAtom);
const [nextVersion, setNextVersion] = useState(-1);
const currentVersion = useRef(-1);
const [acknowledgedChanges, setAcknowledgedChanges] = useState<
ElementsChange[]
const [acknowledgedIncrements, setAcknowledgedIncrements] = useState<
StoreIncrement[]
>([]);
const [isCollaborating] = useAtomWithInitialValue(isCollaboratingAtom, () => {
return isCollaborationLink(window.location.href);
@ -382,7 +381,7 @@ const ExcalidrawWrapper = () => {
useEffect(() => {
const interval = setInterval(() => {
setAcknowledgedChanges([...(syncAPI?.acknowledgedChanges ?? [])]);
setAcknowledgedIncrements([...(syncAPI?.acknowledgedIncrements ?? [])]);
}, 250);
syncAPI?.reconnect();
@ -648,35 +647,36 @@ const ExcalidrawWrapper = () => {
// this check is redundant, but since this is a hot path, it's best
// not to evaludate the nested expression every time
if (!LocalData.isSavePaused()) {
LocalData.save(elements, appState, files, () => {
if (excalidrawAPI) {
let didChange = false;
// CFDO: temporary
// if (!LocalData.isSavePaused()) {
// LocalData.save(elements, appState, files, () => {
// if (excalidrawAPI) {
// let didChange = false;
const elements = excalidrawAPI
.getSceneElementsIncludingDeleted()
.map((element) => {
if (
LocalData.fileStorage.shouldUpdateImageElementStatus(element)
) {
const newElement = newElementWith(element, { status: "saved" });
if (newElement !== element) {
didChange = true;
}
return newElement;
}
return element;
});
// const elements = excalidrawAPI
// .getSceneElementsIncludingDeleted()
// .map((element) => {
// if (
// LocalData.fileStorage.shouldUpdateImageElementStatus(element)
// ) {
// const newElement = newElementWith(element, { status: "saved" });
// if (newElement !== element) {
// didChange = true;
// }
// return newElement;
// }
// return element;
// });
if (didChange) {
excalidrawAPI.updateScene({
elements,
storeAction: StoreAction.UPDATE,
});
}
}
});
}
// if (didChange) {
// excalidrawAPI.updateScene({
// elements,
// storeAction: StoreAction.UPDATE,
// });
// }
// }
// });
// }
// Render the debug scene if the debug canvas is available
if (debugCanvasRef.current && excalidrawAPI) {
@ -694,10 +694,13 @@ const ExcalidrawWrapper = () => {
// - wysiwyg, dragging elements / points, mouse movements, etc.
const { elementsChange } = increment;
// some appState like selections should also be transfered (we could even persist it)
// CFDO: some appState like selections should also be transfered (we could even persist it)
if (!elementsChange.isEmpty()) {
console.log(elementsChange)
syncAPI?.push("durable", [elementsChange]);
try {
syncAPI?.push("durable", increment);
} catch (e) {
console.error(e);
}
}
};
@ -828,22 +831,23 @@ const ExcalidrawWrapper = () => {
excalidrawAPI?.getSceneElements().map((x) => [x.id, x]),
);
let changes: ElementsChange[] = [];
let increments: StoreIncrement[] = [];
const goingLeft =
currentVersion.current === -1 || value - currentVersion.current <= 0;
if (goingLeft) {
changes = acknowledgedChanges
increments = acknowledgedIncrements
.slice(value)
.reverse()
.map((x) => x.inverse());
} else {
changes = acknowledgedChanges.slice(currentVersion.current, value) ?? [];
increments =
acknowledgedIncrements.slice(currentVersion.current, value) ?? [];
}
for (const change of changes) {
[elements] = change.applyTo(
for (const increment of increments) {
[elements] = increment.elementsChange.applyTo(
elements as SceneElementsMap,
excalidrawAPI?.store.snapshot.elements!,
);
@ -852,7 +856,7 @@ const ExcalidrawWrapper = () => {
excalidrawAPI?.updateScene({
appState: {
...excalidrawAPI?.getAppState(),
viewModeEnabled: value !== acknowledgedChanges.length,
viewModeEnabled: value !== acknowledgedIncrements.length,
},
elements: Array.from(elements.values()),
storeAction: StoreAction.UPDATE,
@ -878,8 +882,8 @@ const ExcalidrawWrapper = () => {
}}
step={1}
min={0}
max={acknowledgedChanges.length}
value={nextVersion === -1 ? acknowledgedChanges.length : nextVersion}
max={acknowledgedIncrements.length}
value={nextVersion === -1 ? acknowledgedIncrements.length : nextVersion}
onChange={(value) => {
setNextVersion(value as number);
debouncedTimeTravel(value as number);
@ -967,7 +971,6 @@ const ExcalidrawWrapper = () => {
/>
<OverwriteConfirmDialog>
<OverwriteConfirmDialog.Actions.ExportToImage />
<OverwriteConfirmDialog.Actions.SaveToDisk />
{excalidrawAPI && (
<OverwriteConfirmDialog.Action
title={t("overwriteConfirm.action.excalidrawPlus.title")}