From 59e8bf498d36fda136c9110fe0ae383a223cbcda Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Wed, 3 May 2023 13:41:41 +0530 Subject: [PATCH] fix --- src/components/App.tsx | 5 +- src/data/blob.ts | 7 ++- src/data/restore.ts | 58 +++++++++++------------ src/data/types.ts | 27 ++++++++++- src/element/newElement.ts | 49 +++++++++++-------- src/element/textElement.ts | 11 +++-- src/excalidraw-app/data/index.ts | 3 +- src/excalidraw-app/index.tsx | 9 ++-- src/tests/packages/utils.unmocked.test.ts | 5 +- 9 files changed, 109 insertions(+), 65 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index d22a0507cb..14ce7992df 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -129,6 +129,7 @@ import { import { LinearElementEditor } from "../element/linearElementEditor"; import { mutateElement, newElementWith } from "../element/mutateElement"; import { + convertToExcalidrawElements, deepCopyElement, duplicateElements, newFreeDrawElement, @@ -2001,7 +2002,9 @@ class App extends React.Component { } if (sceneData.elements) { - this.scene.replaceAllElements(sceneData.elements); + this.scene.replaceAllElements( + convertToExcalidrawElements(sceneData.elements), + ); } if (sceneData.collaborators) { diff --git a/src/data/blob.ts b/src/data/blob.ts index c0aa66ee73..e500bbf8fe 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -13,6 +13,7 @@ import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem"; import { isValidExcalidrawData, isValidLibrary } from "./json"; import { restore, restoreLibraryItems } from "./restore"; import { ImportedLibraryData } from "./types"; +import { convertToExcalidrawElements } from "../element/newElement"; const parseFileContents = async (blob: Blob | File) => { let contents: string; @@ -138,14 +139,16 @@ export const loadSceneOrLibraryFromBlob = async ( type: MIME_TYPES.excalidraw, data: restore( { - elements: clearElementsForExport(data.elements || []), + elements: clearElementsForExport( + convertToExcalidrawElements(data.elements || []), + ), appState: { theme: localAppState?.theme, fileHandle: fileHandle || blob.handle || null, ...cleanAppStateForExport(data.appState || {}), ...(localAppState ? calculateScrollCenter( - data.elements || [], + convertToExcalidrawElements(data.elements || []), localAppState, null, ) diff --git a/src/data/restore.ts b/src/data/restore.ts index 7af6e4a85a..979ce90973 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -40,7 +40,7 @@ import { getDefaultLineHeight, measureBaseline, } from "../element/textElement"; -import { updateElementChildren } from "../element/newElement"; +import { convertToExcalidrawElements } from "../element/newElement"; type RestoredAppState = Omit< AppState, @@ -373,41 +373,37 @@ export const restoreElements = ( ): ExcalidrawElement[] => { // used to detect duplicate top-level element ids const existingIds = new Set(); - + const excalidrawElements = convertToExcalidrawElements(elements); const localElementsMap = localElements ? arrayToMap(localElements) : null; - const restoredElements = (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)) { - let migratedElement: ExcalidrawElement | null = restoreElement( - element, - opts?.refreshDimensions, - ); - if (migratedElement) { - const localElement = localElementsMap?.get(element.id); - if (localElement && localElement.version > migratedElement.version) { - migratedElement = bumpVersion(migratedElement, localElement.version); - } - if (existingIds.has(migratedElement.id)) { - migratedElement = { ...migratedElement, id: randomId() }; - } - existingIds.add(migratedElement.id); - //@ts-ignore - if (element.children?.length) { - //@ts-ignore - const newElements = updateElementChildren(element); - if (newElements) { - elements.push(...newElements); - } else { - elements.push(migratedElement); + const restoredElements = (excalidrawElements || []).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)) { + let migratedElement: ExcalidrawElement | null = restoreElement( + element, + opts?.refreshDimensions, + ); + if (migratedElement) { + const localElement = localElementsMap?.get(element.id); + if (localElement && localElement.version > migratedElement.version) { + migratedElement = bumpVersion( + migratedElement, + localElement.version, + ); } - } else { + if (existingIds.has(migratedElement.id)) { + migratedElement = { ...migratedElement, id: randomId() }; + } + existingIds.add(migratedElement.id); + elements.push(migratedElement); } } - } - return elements; - }, [] as ExcalidrawElement[]); + return elements; + }, + [] as ExcalidrawElement[], + ); if (!opts?.repairBindings) { return restoredElements; diff --git a/src/data/types.ts b/src/data/types.ts index b8c9592185..06a60bfb38 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -1,4 +1,4 @@ -import { ExcalidrawElement } from "../element/types"; +import { ExcalidrawElement, ExcalidrawGenericElement } from "../element/types"; import { AppState, BinaryFiles, @@ -7,6 +7,8 @@ import { } from "../types"; import type { cleanAppStateForExport } from "../appState"; import { VERSIONS } from "../constants"; +import { MarkOptional } from "../utility-types"; +import { ElementConstructorOpts } from "../element/newElement"; export interface ExportedDataState { type: string; @@ -35,7 +37,28 @@ export interface ImportedDataState { type?: string; version?: number; source?: string; - elements?: readonly ExcalidrawElement[] | null; + elements?: + | readonly ( + | (ExcalidrawElement & { + children?: [ + { text: string } & MarkOptional< + ElementConstructorOpts, + "x" | "y" + >, + ]; + }) + | { + type: Exclude; + children?: [ + { text: string } & MarkOptional< + ElementConstructorOpts, + "x" | "y" + >, + ] & + MarkOptional; + } + )[] + | null; appState?: Readonly< Partial< AppState & { diff --git a/src/element/newElement.ts b/src/element/newElement.ts index e16e090b1d..7e0d401929 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -12,7 +12,6 @@ import { ExcalidrawFreeDrawElement, FontFamilyValues, ExcalidrawTextContainer, - NonDeletedExcalidrawElement, } from "../element/types"; import { arrayToMap, @@ -47,9 +46,9 @@ import { DEFAULT_VERTICAL_ALIGN, VERTICAL_ALIGN, } from "../constants"; -import { isArrowElement, isTextElement } from "./typeChecks"; +import { isArrowElement } from "./typeChecks"; import { MarkOptional, Merge, Mutable } from "../utility-types"; -import { Children } from "react"; +import { ImportedDataState } from "../data/types"; export type ElementConstructorOpts = MarkOptional< Omit, @@ -649,21 +648,33 @@ export const duplicateElements = ( return clonedElements; }; -export const updateElementChildren = (element: { - type: ExcalidrawGenericElement["type"]; - children?: [ - { text: string } & MarkOptional, - ] & - MarkOptional; -}) => { - const textElement = element.children?.find( - ( - child: { text: string } & MarkOptional, - ) => child.text !== null, - ); - if (isValidTextContainer(element) && textElement) { - const elements = bindTextToContainer(element, textElement); - return elements; +export const convertToExcalidrawElements = ( + elements: ImportedDataState["elements"], +) => { + const res: ExcalidrawElement[] = []; + if (!elements) { + return []; } - return null; + elements.forEach((element) => { + if (!element) { + return; + } + const textElement = element.children?.find((child) => child.text !== null); + if ( + isValidTextContainer(element) && + textElement && + (element.type === "rectangle" || + element.type === "ellipse" || + element.type === "diamond") + ) { + const elements = bindTextToContainer(element, textElement); + res.push(...elements); + } else { + delete element.children; + //@ts-ignore + res.push(element); + } + }); + + return res; }; diff --git a/src/element/textElement.ts b/src/element/textElement.ts index d7f97d42c8..7afe0b12b0 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -1,7 +1,10 @@ import { getFontString, arrayToMap, isTestEnv } from "../utils"; import { + ExcalidrawDiamondElement, ExcalidrawElement, + ExcalidrawEllipseElement, ExcalidrawGenericElement, + ExcalidrawRectangleElement, ExcalidrawTextContainer, ExcalidrawTextElement, ExcalidrawTextElementWithContainer, @@ -982,9 +985,10 @@ export const getDefaultLineHeight = (fontFamily: FontFamilyValues) => { }; export const bindTextToContainer = ( - containerProps: { - type: ExcalidrawGenericElement["type"]; - } & MarkOptional, + containerProps: + | { + type: Exclude; + } & MarkOptional, textProps: { text: string } & MarkOptional, ) => { const container = newElement({ @@ -1000,6 +1004,7 @@ export const bindTextToContainer = ( textAlign: TEXT_ALIGN.CENTER, verticalAlign: VERTICAL_ALIGN.MIDDLE, }); + mutateElement(container, { boundElements: (container.boundElements || []).concat({ type: "text", diff --git a/src/excalidraw-app/data/index.ts b/src/excalidraw-app/data/index.ts index 2e50abf1f9..eb6c15533f 100644 --- a/src/excalidraw-app/data/index.ts +++ b/src/excalidraw-app/data/index.ts @@ -7,6 +7,7 @@ import { import { serializeAsJSON } from "../../data/json"; import { restore } from "../../data/restore"; import { ImportedDataState } from "../../data/types"; +import { convertToExcalidrawElements } from "../../element/newElement"; import { isInvisiblySmallElement } from "../../element/sizeHelpers"; import { isInitializedImageElement } from "../../element/typeChecks"; import { ExcalidrawElement, FileId } from "../../element/types"; @@ -262,7 +263,7 @@ export const loadScene = async ( data = restore( await importFromBackend(id, privateKey), localDataState?.appState, - localDataState?.elements, + convertToExcalidrawElements(localDataState?.elements), { repairBindings: true, refreshDimensions: false }, ); } else { diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 0003b96b66..38beb66123 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -86,6 +86,7 @@ import { appJotaiStore } from "./app-jotai"; import "./index.scss"; import { ResolutionType } from "../utility-types"; +import { convertToExcalidrawElements } from "../element/newElement"; polyfill(); @@ -206,7 +207,7 @@ const initializeScene = async (opts: { isLoading: false, }, elements: reconcileElements( - scene?.elements || [], + convertToExcalidrawElements(scene?.elements || []), excalidrawAPI.getSceneElementsIncludingDeleted(), excalidrawAPI.getAppState(), ), @@ -286,7 +287,7 @@ const ExcalidrawWrapper = () => { if (data.scene.elements) { collabAPI .fetchImageFilesFromFirebase({ - elements: data.scene.elements, + elements: convertToExcalidrawElements(data.scene.elements), forceFetchFiles: true, }) .then(({ loadedFiles, erroredFiles }) => { @@ -299,8 +300,9 @@ const ExcalidrawWrapper = () => { }); } } else { + const sceneElements = convertToExcalidrawElements(data.scene.elements); const fileIds = - data.scene.elements?.reduce((acc, element) => { + sceneElements?.reduce((acc, element) => { if (isInitializedImageElement(element)) { return acc.concat(element.fileId); } @@ -351,7 +353,6 @@ const ExcalidrawWrapper = () => { elements: [ { type: "rectangle", - //@ts-ignore children: [{ text: "HELLO DAMMMMY" }], }, ], diff --git a/src/tests/packages/utils.unmocked.test.ts b/src/tests/packages/utils.unmocked.test.ts index 28db08c49f..a51f7744d6 100644 --- a/src/tests/packages/utils.unmocked.test.ts +++ b/src/tests/packages/utils.unmocked.test.ts @@ -1,5 +1,6 @@ import { decodePngMetadata, decodeSvgMetadata } from "../../data/image"; import { ImportedDataState } from "../../data/types"; +import { convertToExcalidrawElements } from "../../element/newElement"; import * as utils from "../../packages/utils"; import { API } from "../helpers/api"; @@ -31,7 +32,7 @@ describe("embedding scene data", () => { const importedData: ImportedDataState = JSON.parse(parsedString); expect(sourceElements.map((x) => x.id)).toEqual( - importedData.elements?.map((el) => el.id), + convertToExcalidrawElements(importedData.elements)?.map((el) => el.id), ); }); }); @@ -60,7 +61,7 @@ describe("embedding scene data", () => { const importedData: ImportedDataState = JSON.parse(parsedString); expect(sourceElements.map((x) => x.id)).toEqual( - importedData.elements?.map((el) => el.id), + convertToExcalidrawElements(importedData.elements)?.map((el) => el.id), ); }); });