feat: factor out url library init & switch to updateLibrary API (#5115)

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
David Luzar 2022-05-11 15:08:54 +02:00 committed by GitHub
parent 2537b225ac
commit cad6097d60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 394 additions and 235 deletions

View file

@ -70,14 +70,12 @@ import {
TEXT_TO_CENTER_SNAP_THRESHOLD,
THEME,
TOUCH_CTX_MENU_TIMEOUT,
URL_HASH_KEYS,
URL_QUERY_KEYS,
VERTICAL_ALIGN,
ZOOM_STEP,
} from "../constants";
import { loadFromBlob } from "../data";
import Library from "../data/library";
import { restore, restoreElements, restoreLibraryItems } from "../data/restore";
import { restore, restoreElements } from "../data/restore";
import {
dragNewElement,
dragSelectedElements,
@ -234,7 +232,6 @@ import {
isSupportedImageFile,
loadSceneOrLibraryFromBlob,
normalizeFile,
loadLibraryFromBlob,
resizeImageFile,
SVGStringToFile,
} from "../data/blob";
@ -261,7 +258,6 @@ import {
isPointHittingLinkIcon,
isLocalLink,
} from "../element/Hyperlink";
import { AbortError } from "../errors";
const defaultDeviceTypeContext: DeviceType = {
isMobile: false,
@ -361,6 +357,8 @@ class App extends React.Component<AppProps, AppState> {
this.id = nanoid();
this.library = new Library(this);
if (excalidrawRef) {
const readyPromise =
("current" in excalidrawRef && excalidrawRef.current?.readyPromise) ||
@ -370,6 +368,7 @@ class App extends React.Component<AppProps, AppState> {
ready: true,
readyPromise,
updateScene: this.updateScene,
updateLibrary: this.library.updateLibrary,
addFiles: this.addFiles,
resetScene: this.resetScene,
getSceneElementsIncludingDeleted: this.getSceneElementsIncludingDeleted,
@ -381,7 +380,6 @@ class App extends React.Component<AppProps, AppState> {
getAppState: () => this.state,
getFiles: () => this.files,
refresh: this.refresh,
importLibrary: this.importLibraryFromUrl,
setToastMessage: this.setToastMessage,
id: this.id,
setActiveTool: this.setActiveTool,
@ -400,7 +398,6 @@ class App extends React.Component<AppProps, AppState> {
};
this.scene = new Scene();
this.library = new Library(this);
this.history = new History();
this.actionManager = new ActionManager(
this.syncActionResult,
@ -698,54 +695,6 @@ class App extends React.Component<AppProps, AppState> {
this.onSceneUpdated();
};
private importLibraryFromUrl = async (url: string, token?: string | null) => {
if (window.location.hash.includes(URL_HASH_KEYS.addLibrary)) {
const hash = new URLSearchParams(window.location.hash.slice(1));
hash.delete(URL_HASH_KEYS.addLibrary);
window.history.replaceState({}, APP_NAME, `#${hash.toString()}`);
} else if (window.location.search.includes(URL_QUERY_KEYS.addLibrary)) {
const query = new URLSearchParams(window.location.search);
query.delete(URL_QUERY_KEYS.addLibrary);
window.history.replaceState({}, APP_NAME, `?${query.toString()}`);
}
const defaultStatus = "published";
this.setState({ isLibraryOpen: true });
try {
await this.library.importLibrary(
new Promise<LibraryItems>(async (resolve, reject) => {
try {
const request = await fetch(decodeURIComponent(url));
const blob = await request.blob();
const libraryItems = await loadLibraryFromBlob(blob, defaultStatus);
if (
token === this.id ||
window.confirm(
t("alerts.confirmAddLibrary", {
numShapes: libraryItems.length,
}),
)
) {
resolve(libraryItems);
} else {
reject(new AbortError());
}
} catch (error: any) {
reject(error);
}
}),
);
} catch (error: any) {
console.error(error);
this.setState({ errorMessage: t("errors.importLibraryError") });
} finally {
this.focusContainer();
}
};
private resetHistory = () => {
this.history.clear();
};
@ -790,7 +739,14 @@ class App extends React.Component<AppProps, AppState> {
try {
initialData = (await this.props.initialData) || null;
if (initialData?.libraryItems) {
this.library.importLibrary(initialData.libraryItems, "unpublished");
this.library
.updateLibrary({
libraryItems: initialData.libraryItems,
merge: true,
})
.catch((error) => {
console.error(error);
});
}
} catch (error: any) {
console.error(error);
@ -802,10 +758,10 @@ class App extends React.Component<AppProps, AppState> {
},
};
}
const scene = restore(initialData, null, null);
scene.appState = {
...scene.appState,
isLibraryOpen: this.state.isLibraryOpen,
activeTool:
scene.appState.activeTool.type === "image"
? { ...scene.appState.activeTool, type: "selection" }
@ -834,20 +790,6 @@ class App extends React.Component<AppProps, AppState> {
...scene,
commitToHistory: true,
});
const libraryUrl =
// current
new URLSearchParams(window.location.hash.slice(1)).get(
URL_HASH_KEYS.addLibrary,
) ||
// legacy, kept for compat reasons
new URLSearchParams(window.location.search).get(
URL_QUERY_KEYS.addLibrary,
);
if (libraryUrl) {
await this.importLibraryFromUrl(libraryUrl);
}
};
public async componentDidMount() {
@ -1691,14 +1633,6 @@ class App extends React.Component<AppProps, AppState> {
appState?: Pick<AppState, K> | null;
collaborators?: SceneData["collaborators"];
commitToHistory?: SceneData["commitToHistory"];
libraryItems?:
| ((
currentLibraryItems: LibraryItems,
) =>
| Required<SceneData>["libraryItems"]
| Promise<Required<SceneData>["libraryItems"]>)
| Required<SceneData>["libraryItems"]
| Promise<Required<SceneData>["libraryItems"]>;
}) => {
if (sceneData.commitToHistory) {
this.history.resumeRecording();
@ -1715,23 +1649,6 @@ class App extends React.Component<AppProps, AppState> {
if (sceneData.collaborators) {
this.setState({ collaborators: sceneData.collaborators });
}
if (sceneData.libraryItems) {
this.library.setLibrary((currentLibraryItems) => {
const nextItems =
typeof sceneData.libraryItems === "function"
? sceneData.libraryItems(currentLibraryItems)
: sceneData.libraryItems;
return new Promise<LibraryItems>(async (resolve, reject) => {
try {
resolve(restoreLibraryItems(await nextItems, "unpublished"));
} catch (error: any) {
reject(error);
}
});
});
}
},
);
@ -5335,19 +5252,15 @@ class App extends React.Component<AppProps, AppState> {
commitToHistory: true,
});
} else if (ret.type === MIME_TYPES.excalidrawlib) {
this.library
.importLibrary(file)
.then(() => {
this.setState({
isLoading: false,
});
await this.library
.updateLibrary({
libraryItems: file,
merge: true,
openLibraryMenu: true,
})
.catch((error) => {
console.error(error);
this.setState({
isLoading: false,
errorMessage: t("errors.importLibraryError"),
});
this.setState({ errorMessage: t("errors.importLibraryError") });
});
}
} catch (error: any) {

View file

@ -1,6 +1,6 @@
import { chunk } from "lodash";
import React, { useCallback, useState } from "react";
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
import { saveLibraryAsJSON } from "../data/json";
import Library from "../data/library";
import { ExcalidrawElement, NonDeleted } from "../element/types";
import { t } from "../i18n";
@ -23,6 +23,7 @@ import { Tooltip } from "./Tooltip";
import "./LibraryMenuItems.scss";
import { VERSIONS } from "../constants";
import Spinner from "./Spinner";
import { fileOpen } from "../data/filesystem";
const LibraryMenuItems = ({
isLoading,
@ -107,13 +108,23 @@ const LibraryMenuItems = ({
title={t("buttons.load")}
aria-label={t("buttons.load")}
icon={load}
onClick={() => {
importLibraryFromJSON(library)
.catch(muteFSAbortError)
.catch((error) => {
console.error(error);
setAppState({ errorMessage: t("errors.importLibraryError") });
onClick={async () => {
try {
await fileOpen({
description: "Excalidraw library files",
// ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442
// gets resolved. Else, iOS users cannot open `.excalidraw` files.
/*
extensions: [".json", ".excalidrawlib"],
*/
});
} catch (error: any) {
if (error?.name === "AbortError") {
console.warn(error);
return;
}
setAppState({ errorMessage: t("errors.importLibraryError") });
}
}}
className="library-actions--load"
/>