remove convertToExcalidrawElements call from restore

This commit is contained in:
Aakansha Doshi 2023-07-14 17:13:24 +05:30
parent 2ff0528a4f
commit d3361f910b
8 changed files with 128 additions and 151 deletions

View file

@ -13,7 +13,6 @@ import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem";
import { isValidExcalidrawData, isValidLibrary } from "./json"; import { isValidExcalidrawData, isValidLibrary } from "./json";
import { restore, restoreLibraryItems } from "./restore"; import { restore, restoreLibraryItems } from "./restore";
import { ImportedLibraryData } from "./types"; import { ImportedLibraryData } from "./types";
import { convertToExcalidrawElements } from "../data/transform";
const parseFileContents = async (blob: Blob | File) => { const parseFileContents = async (blob: Blob | File) => {
let contents: string; let contents: string;
@ -135,9 +134,7 @@ export const loadSceneOrLibraryFromBlob = async (
try { try {
const data = JSON.parse(contents); const data = JSON.parse(contents);
if (isValidExcalidrawData(data)) { if (isValidExcalidrawData(data)) {
const excaldrawElements = convertToExcalidrawElements( const excaldrawElements = data.elements || [];
data.elements || [],
);
return { return {
type: MIME_TYPES.excalidraw, type: MIME_TYPES.excalidraw,
data: restore( data: restore(

View file

@ -41,10 +41,7 @@ import {
getDefaultLineHeight, getDefaultLineHeight,
measureBaseline, measureBaseline,
} from "../element/textElement"; } from "../element/textElement";
import {
ExcalidrawProgrammaticAPI,
convertToExcalidrawElements,
} from "../data/transform";
import { normalizeLink } from "./url"; import { normalizeLink } from "./url";
type RestoredAppState = Omit< type RestoredAppState = Omit<
@ -394,44 +391,37 @@ const repairFrameMembership = (
}; };
export const restoreElements = ( export const restoreElements = (
elements: ExcalidrawProgrammaticAPI["elements"], elements: ImportedDataState["elements"],
/** NOTE doesn't serve for reconciliation */ /** NOTE doesn't serve for reconciliation */
localElements: readonly ExcalidrawElement[] | null | undefined, localElements: readonly ExcalidrawElement[] | null | undefined,
opts?: { refreshDimensions?: boolean; repairBindings?: boolean } | undefined, opts?: { refreshDimensions?: boolean; repairBindings?: boolean } | undefined,
): ExcalidrawElement[] => { ): ExcalidrawElement[] => {
// used to detect duplicate top-level element ids // used to detect duplicate top-level element ids
const existingIds = new Set<string>(); const existingIds = new Set<string>();
const excalidrawElements = convertToExcalidrawElements(elements);
const localElementsMap = localElements ? arrayToMap(localElements) : null; const localElementsMap = localElements ? arrayToMap(localElements) : null;
const restoredElements = (excalidrawElements || []).reduce( const restoredElements = (elements || []).reduce((elements, element) => {
(elements, element) => { // filtering out selection, which is legacy, no longer kept in elements,
// filtering out selection, which is legacy, no longer kept in elements, // and causing issues if retained
// and causing issues if retained if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
if (element.type !== "selection" && !isInvisiblySmallElement(element)) { let migratedElement: ExcalidrawElement | null = restoreElement(
let migratedElement: ExcalidrawElement | null = restoreElement( element,
element, opts?.refreshDimensions,
opts?.refreshDimensions, );
); if (migratedElement) {
if (migratedElement) { const localElement = localElementsMap?.get(element.id);
const localElement = localElementsMap?.get(element.id); if (localElement && localElement.version > migratedElement.version) {
if (localElement && localElement.version > migratedElement.version) { migratedElement = bumpVersion(migratedElement, localElement.version);
migratedElement = bumpVersion(
migratedElement,
localElement.version,
);
}
if (existingIds.has(migratedElement.id)) {
migratedElement = { ...migratedElement, id: randomId() };
}
existingIds.add(migratedElement.id);
elements.push(migratedElement);
} }
if (existingIds.has(migratedElement.id)) {
migratedElement = { ...migratedElement, id: randomId() };
}
existingIds.add(migratedElement.id);
elements.push(migratedElement);
} }
return elements; }
}, return elements;
[] as ExcalidrawElement[], }, [] as ExcalidrawElement[]);
);
if (!opts?.repairBindings) { if (!opts?.repairBindings) {
return restoredElements; return restoredElements;

View file

@ -24,16 +24,109 @@ import {
import { import {
ExcalidrawBindableElement, ExcalidrawBindableElement,
ExcalidrawElement, ExcalidrawElement,
ExcalidrawFrameElement,
ExcalidrawFreeDrawElement, ExcalidrawFreeDrawElement,
ExcalidrawGenericElement, ExcalidrawGenericElement,
ExcalidrawImageElement, ExcalidrawImageElement,
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawSelectionElement, ExcalidrawSelectionElement,
ExcalidrawTextElement, ExcalidrawTextElement,
FontFamilyValues,
TextAlign,
VerticalAlign,
} from "../element/types"; } from "../element/types";
import { MarkOptional } from "../utility-types"; import { MarkOptional } from "../utility-types";
import { getFontString } from "../utils"; import { getFontString } from "../utils";
import { ValidContainer, ValidLinearElement } from "./types";
export type ValidLinearElement = {
type: "arrow" | "line";
x: number;
y: number;
label?: {
text: string;
fontSize?: number;
fontFamily?: FontFamilyValues;
textAlign?: TextAlign;
verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
end?:
| (
| (
| {
type: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text" | "frame"
>;
id?: ExcalidrawGenericElement["id"];
}
| {
id: ExcalidrawGenericElement["id"];
type?: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text" | "frame"
>;
}
)
| ((
| {
type: "text";
text: string;
}
| {
type?: "text";
id: ExcalidrawTextElement["id"];
text: string;
}
) &
Partial<ExcalidrawTextElement>)
) &
MarkOptional<ElementConstructorOpts, "x" | "y">;
start?:
| (
| (
| {
type: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text" | "frame"
>;
id?: ExcalidrawGenericElement["id"];
}
| {
id: ExcalidrawGenericElement["id"];
type?: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text" | "frame"
>;
}
)
| ((
| {
type: "text";
text: string;
}
| {
type?: "text";
id: ExcalidrawTextElement["id"];
text: string;
}
) &
Partial<ExcalidrawTextElement>)
) &
MarkOptional<ElementConstructorOpts, "x" | "y">;
} & Partial<ExcalidrawLinearElement>;
export type ValidContainer =
| {
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
id?: ExcalidrawGenericElement["id"];
label?: {
text: string;
fontSize?: number;
fontFamily?: FontFamilyValues;
textAlign?: TextAlign;
verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
} & ElementConstructorOpts;
export interface ExcalidrawProgrammaticAPI { export interface ExcalidrawProgrammaticAPI {
elements?: elements?:
@ -168,7 +261,7 @@ const bindLinearElementToElement = (
.get() .get()
.find((ele) => ele?.id === start.id) as Exclude< .find((ele) => ele?.id === start.id) as Exclude<
ExcalidrawBindableElement, ExcalidrawBindableElement,
ExcalidrawImageElement ExcalidrawImageElement | ExcalidrawFrameElement
>; >;
if (!existingElement) { if (!existingElement) {
console.error(`No element for start binding with id ${start.id} found`); console.error(`No element for start binding with id ${start.id} found`);
@ -235,7 +328,7 @@ const bindLinearElementToElement = (
.get() .get()
.find((ele) => ele?.id === end.id) as Exclude< .find((ele) => ele?.id === end.id) as Exclude<
ExcalidrawBindableElement, ExcalidrawBindableElement,
ExcalidrawImageElement ExcalidrawImageElement | ExcalidrawFrameElement
>; >;
if (!existingElement) { if (!existingElement) {
console.error(`No element for end binding with id ${end.id} found`); console.error(`No element for end binding with id ${end.id} found`);

View file

@ -1,13 +1,4 @@
import { import { ExcalidrawElement } from "../element/types";
ExcalidrawBindableElement,
ExcalidrawElement,
ExcalidrawGenericElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
FontFamilyValues,
TextAlign,
VerticalAlign,
} from "../element/types";
import { import {
AppState, AppState,
BinaryFiles, BinaryFiles,
@ -16,8 +7,6 @@ import {
} from "../types"; } from "../types";
import type { cleanAppStateForExport } from "../appState"; import type { cleanAppStateForExport } from "../appState";
import { VERSIONS } from "../constants"; import { VERSIONS } from "../constants";
import { MarkOptional } from "../utility-types";
import { ElementConstructorOpts } from "../element/newElement";
export interface ExportedDataState { export interface ExportedDataState {
type: string; type: string;
@ -40,96 +29,6 @@ export type LegacyAppState = {
isSidebarDocked: [boolean, "defaultSidebarDockedPreference"]; isSidebarDocked: [boolean, "defaultSidebarDockedPreference"];
}; };
export type ValidLinearElement = {
type: "arrow" | "line";
x: number;
y: number;
label?: {
text: string;
fontSize?: number;
fontFamily?: FontFamilyValues;
textAlign?: TextAlign;
verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
end?:
| (
| (
| {
type: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text"
>;
id?: ExcalidrawGenericElement["id"];
}
| {
id: ExcalidrawGenericElement["id"];
type?: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text"
>;
}
)
| ((
| {
type: "text";
text: string;
}
| {
type?: "text";
id: ExcalidrawTextElement["id"];
text: string;
}
) &
Partial<ExcalidrawTextElement>)
) &
MarkOptional<ElementConstructorOpts, "x" | "y">;
start?:
| (
| (
| {
type: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text"
>;
id?: ExcalidrawGenericElement["id"];
}
| {
id: ExcalidrawGenericElement["id"];
type?: Exclude<
ExcalidrawBindableElement["type"],
"image" | "selection" | "text"
>;
}
)
| ((
| {
type: "text";
text: string;
}
| {
type?: "text";
id: ExcalidrawTextElement["id"];
text: string;
}
) &
Partial<ExcalidrawTextElement>)
) &
MarkOptional<ElementConstructorOpts, "x" | "y">;
} & Partial<ExcalidrawLinearElement>;
export type ValidContainer =
| {
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
id?: ExcalidrawGenericElement["id"];
label?: {
text: string;
fontSize?: number;
fontFamily?: FontFamilyValues;
textAlign?: TextAlign;
verticalAlign?: VerticalAlign;
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
} & ElementConstructorOpts;
export interface ImportedDataState { export interface ImportedDataState {
type?: string; type?: string;
version?: number; version?: number;

View file

@ -7,7 +7,6 @@ import {
import { serializeAsJSON } from "../../data/json"; import { serializeAsJSON } from "../../data/json";
import { restore } from "../../data/restore"; import { restore } from "../../data/restore";
import { ImportedDataState } from "../../data/types"; import { ImportedDataState } from "../../data/types";
import { convertToExcalidrawElements } from "../../data/transform";
import { isInvisiblySmallElement } from "../../element/sizeHelpers"; import { isInvisiblySmallElement } from "../../element/sizeHelpers";
import { isInitializedImageElement } from "../../element/typeChecks"; import { isInitializedImageElement } from "../../element/typeChecks";
import { ExcalidrawElement, FileId } from "../../element/types"; import { ExcalidrawElement, FileId } from "../../element/types";

View file

@ -91,7 +91,6 @@ import { appJotaiStore } from "./app-jotai";
import "./index.scss"; import "./index.scss";
import { ResolutionType } from "../utility-types"; import { ResolutionType } from "../utility-types";
import { convertToExcalidrawElements } from "../data/transform";
import { ShareableLinkDialog } from "../components/ShareableLinkDialog"; import { ShareableLinkDialog } from "../components/ShareableLinkDialog";
import { openConfirmModal } from "../components/OverwriteConfirm/OverwriteConfirmState"; import { openConfirmModal } from "../components/OverwriteConfirm/OverwriteConfirmState";
import { OverwriteConfirmDialog } from "../components/OverwriteConfirm/OverwriteConfirm"; import { OverwriteConfirmDialog } from "../components/OverwriteConfirm/OverwriteConfirm";
@ -229,7 +228,7 @@ const initializeScene = async (opts: {
isLoading: false, isLoading: false,
}, },
elements: reconcileElements( elements: reconcileElements(
convertToExcalidrawElements(scene?.elements || []), scene?.elements || [],
excalidrawAPI.getSceneElementsIncludingDeleted(), excalidrawAPI.getSceneElementsIncludingDeleted(),
excalidrawAPI.getAppState(), excalidrawAPI.getAppState(),
), ),
@ -310,7 +309,7 @@ const ExcalidrawWrapper = () => {
if (data.scene.elements) { if (data.scene.elements) {
collabAPI collabAPI
.fetchImageFilesFromFirebase({ .fetchImageFilesFromFirebase({
elements: convertToExcalidrawElements(data.scene.elements), elements: data.scene.elements,
forceFetchFiles: true, forceFetchFiles: true,
}) })
.then(({ loadedFiles, erroredFiles }) => { .then(({ loadedFiles, erroredFiles }) => {
@ -323,7 +322,7 @@ const ExcalidrawWrapper = () => {
}); });
} }
} else { } else {
const sceneElements = convertToExcalidrawElements(data.scene.elements); const sceneElements = data.scene.elements;
const fileIds = const fileIds =
sceneElements?.reduce((acc, element) => { sceneElements?.reduce((acc, element) => {
if (isInitializedImageElement(element)) { if (isInitializedImageElement(element)) {

View file

@ -189,7 +189,7 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
const updateScene = () => { const updateScene = () => {
const sceneData = { const sceneData = {
elements: restoreElements( elements: restoreElements(
[ convertToExcalidrawElements([
{ {
type: "rectangle", type: "rectangle",
id: "rect-1", id: "rect-1",
@ -222,7 +222,7 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
y: 100, y: 100,
text: "HELLO WORLD!", text: "HELLO WORLD!",
}, },
], ]),
null, null,
), ),
appState: { appState: {

View file

@ -32,7 +32,7 @@ describe("embedding scene data", () => {
const importedData: ImportedDataState = JSON.parse(parsedString); const importedData: ImportedDataState = JSON.parse(parsedString);
expect(sourceElements.map((x) => x.id)).toEqual( expect(sourceElements.map((x) => x.id)).toEqual(
convertToExcalidrawElements(importedData.elements)?.map((el) => el.id), importedData.elements?.map((el) => el.id),
); );
}); });
}); });
@ -61,7 +61,7 @@ describe("embedding scene data", () => {
const importedData: ImportedDataState = JSON.parse(parsedString); const importedData: ImportedDataState = JSON.parse(parsedString);
expect(sourceElements.map((x) => x.id)).toEqual( expect(sourceElements.map((x) => x.id)).toEqual(
convertToExcalidrawElements(importedData.elements)?.map((el) => el.id), importedData.elements?.map((el) => el.id),
); );
}); });
}); });