feat: pass localElements to restore and restoreElement API's and bump versions of duplicate elements on import (#3797)

This commit is contained in:
David Luzar 2021-07-04 22:23:35 +02:00 committed by GitHub
parent 038e9c13dd
commit 097362662d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 164 additions and 61 deletions

View file

@ -1,6 +1,7 @@
import { cleanAppStateForExport } from "../appState";
import { EXPORT_DATA_TYPES } from "../constants";
import { clearElementsForExport } from "../element";
import { ExcalidrawElement } from "../element/types";
import { CanvasError } from "../errors";
import { t } from "../i18n";
import { calculateScrollCenter } from "../scene";
@ -83,6 +84,7 @@ export const loadFromBlob = async (
blob: Blob,
/** @see restore.localAppState */
localAppState: AppState | null,
localElements: readonly ExcalidrawElement[] | null,
) => {
const contents = await parseFileContents(blob);
try {
@ -103,6 +105,7 @@ export const loadFromBlob = async (
},
},
localAppState,
localElements,
);
return result;

View file

@ -49,7 +49,10 @@ export const saveAsJSON = async (
return { fileHandle };
};
export const loadFromJSON = async (localAppState: AppState) => {
export const loadFromJSON = async (
localAppState: AppState,
localElements: readonly ExcalidrawElement[] | null,
) => {
const blob = await fileOpen({
description: "Excalidraw files",
// ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442
@ -64,7 +67,7 @@ export const loadFromJSON = async (localAppState: AppState) => {
],
*/
});
return loadFromBlob(blob, localAppState);
return loadFromBlob(blob, localAppState, localElements);
};
export const isValidExcalidrawData = (data?: {

View file

@ -18,7 +18,7 @@ class Library {
};
restoreLibraryItem = (libraryItem: LibraryItem): LibraryItem | null => {
const elements = getNonDeletedElements(restoreElements(libraryItem));
const elements = getNonDeletedElements(restoreElements(libraryItem, null));
return elements.length ? elements : null;
};

View file

@ -5,7 +5,11 @@ import {
} from "../element/types";
import { AppState, NormalizedZoomValue } from "../types";
import { ImportedDataState } from "./types";
import { getNormalizedDimensions, isInvisiblySmallElement } from "../element";
import {
getElementMap,
getNormalizedDimensions,
isInvisiblySmallElement,
} from "../element";
import { isLinearElementType } from "../element/typeChecks";
import { randomId } from "../random";
import {
@ -16,6 +20,7 @@ import {
} from "../constants";
import { getDefaultAppState } from "../appState";
import { LinearElementEditor } from "../element/linearElementEditor";
import { bumpVersion } from "../element/mutateElement";
type RestoredAppState = Omit<
AppState,
@ -181,13 +186,20 @@ const restoreElement = (
export const restoreElements = (
elements: ImportedDataState["elements"],
/** NOTE doesn't serve for reconciliation */
localElements: readonly ExcalidrawElement[] | null | undefined,
): ExcalidrawElement[] => {
const localElementsMap = localElements ? getElementMap(localElements) : null;
return (elements || []).reduce((elements, element) => {
// filtering out selection, which is legacy, no longer kept in elements,
// and causing issues if retained
if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
const migratedElement = restoreElement(element);
let migratedElement: ExcalidrawElement = restoreElement(element);
if (migratedElement) {
const localElement = localElementsMap?.[element.id];
if (localElement && localElement.version > migratedElement.version) {
migratedElement = bumpVersion(migratedElement, localElement.version);
}
elements.push(migratedElement);
}
}
@ -197,25 +209,25 @@ export const restoreElements = (
export const restoreAppState = (
appState: ImportedDataState["appState"],
localAppState: Partial<AppState> | null,
localAppState: Partial<AppState> | null | undefined,
): RestoredAppState => {
appState = appState || {};
const defaultAppState = getDefaultAppState();
const nextAppState = {} as typeof defaultAppState;
for (const [key, val] of Object.entries(defaultAppState) as [
for (const [key, defaultValue] of Object.entries(defaultAppState) as [
keyof typeof defaultAppState,
any,
][]) {
const restoredValue = appState[key];
const suppliedValue = appState[key];
const localValue = localAppState ? localAppState[key] : undefined;
(nextAppState as any)[key] =
restoredValue !== undefined
? restoredValue
suppliedValue !== undefined
? suppliedValue
: localValue !== undefined
? localValue
: val;
: defaultValue;
}
return {
@ -243,9 +255,10 @@ export const restore = (
* Supply `null` if you can't get access to it.
*/
localAppState: Partial<AppState> | null | undefined,
localElements: readonly ExcalidrawElement[] | null | undefined,
): RestoredDataState => {
return {
elements: restoreElements(data?.elements),
elements: restoreElements(data?.elements, localElements),
appState: restoreAppState(data?.appState, localAppState || null),
};
};