mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
fix: library init/import race conditions (#5101)
This commit is contained in:
parent
6a0f800716
commit
d53ac2a61e
11 changed files with 248 additions and 133 deletions
|
@ -257,6 +257,7 @@ import {
|
|||
isPointHittingLinkIcon,
|
||||
isLocalLink,
|
||||
} from "../element/Hyperlink";
|
||||
import { AbortError } from "../errors";
|
||||
|
||||
const defaultDeviceTypeContext: DeviceType = {
|
||||
isMobile: false,
|
||||
|
@ -703,21 +704,35 @@ class App extends React.Component<AppProps, AppState> {
|
|||
window.history.replaceState({}, APP_NAME, `?${query.toString()}`);
|
||||
}
|
||||
|
||||
const defaultStatus = "published";
|
||||
|
||||
this.setState({ isLibraryOpen: true });
|
||||
|
||||
try {
|
||||
const request = await fetch(decodeURIComponent(url));
|
||||
const blob = await request.blob();
|
||||
const defaultStatus = "published";
|
||||
const libraryItems = await loadLibraryFromBlob(blob, defaultStatus);
|
||||
if (
|
||||
token === this.id ||
|
||||
window.confirm(
|
||||
t("alerts.confirmAddLibrary", {
|
||||
numShapes: libraryItems.length,
|
||||
}),
|
||||
)
|
||||
) {
|
||||
await this.library.importLibrary(libraryItems, defaultStatus);
|
||||
}
|
||||
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") });
|
||||
|
@ -1674,6 +1689,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||
collaborators?: SceneData["collaborators"];
|
||||
commitToHistory?: SceneData["commitToHistory"];
|
||||
libraryItems?:
|
||||
| ((
|
||||
currentLibraryItems: LibraryItems,
|
||||
) =>
|
||||
| Required<SceneData>["libraryItems"]
|
||||
| Promise<Required<SceneData>["libraryItems"]>)
|
||||
| Required<SceneData>["libraryItems"]
|
||||
| Promise<Required<SceneData>["libraryItems"]>;
|
||||
}) => {
|
||||
|
@ -1694,20 +1714,20 @@ class App extends React.Component<AppProps, AppState> {
|
|||
}
|
||||
|
||||
if (sceneData.libraryItems) {
|
||||
this.library.saveLibrary(
|
||||
new Promise<LibraryItems>(async (resolve, reject) => {
|
||||
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 sceneData.libraryItems,
|
||||
"unpublished",
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
reject(new Error(t("errors.importLibraryError")));
|
||||
resolve(restoreLibraryItems(await nextItems, "unpublished"));
|
||||
} catch (error: any) {
|
||||
reject(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -5280,11 +5300,14 @@ class App extends React.Component<AppProps, AppState> {
|
|||
file?.type === MIME_TYPES.excalidrawlib ||
|
||||
file?.name?.endsWith(".excalidrawlib")
|
||||
) {
|
||||
this.library
|
||||
.importLibrary(file)
|
||||
.catch((error) =>
|
||||
this.setState({ isLoading: false, errorMessage: error.message }),
|
||||
);
|
||||
this.setState({ isLibraryOpen: true });
|
||||
this.library.importLibrary(file).catch((error) => {
|
||||
console.error(error);
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: t("errors.importLibraryError"),
|
||||
});
|
||||
});
|
||||
// default: assume an Excalidraw file regardless of extension/MimeType
|
||||
} else if (file) {
|
||||
this.setState({ isLoading: true });
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
width: 100%;
|
||||
margin: 2px 0;
|
||||
|
||||
.Spinner {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
// 2px from the left to account for focus border of left-most button
|
||||
margin: 0 2px;
|
||||
|
|
|
@ -139,7 +139,7 @@ export const LibraryMenu = ({
|
|||
const nextItems = libraryItems.filter(
|
||||
(item) => !selectedItems.includes(item.id),
|
||||
);
|
||||
library.saveLibrary(nextItems).catch(() => {
|
||||
library.setLibrary(nextItems).catch(() => {
|
||||
setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
|
||||
});
|
||||
setSelectedItems([]);
|
||||
|
@ -170,7 +170,7 @@ export const LibraryMenu = ({
|
|||
...libraryItems,
|
||||
];
|
||||
onAddToLibrary();
|
||||
library.saveLibrary(nextItems).catch(() => {
|
||||
library.setLibrary(nextItems).catch(() => {
|
||||
setAppState({ errorMessage: t("alerts.errorAddingToLibrary") });
|
||||
});
|
||||
},
|
||||
|
@ -220,7 +220,7 @@ export const LibraryMenu = ({
|
|||
libItem.status = "published";
|
||||
}
|
||||
});
|
||||
library.saveLibrary(nextLibItems);
|
||||
library.setLibrary(nextLibItems);
|
||||
},
|
||||
[setShowPublishLibraryDialog, setPublishLibSuccess, selectedItems, library],
|
||||
);
|
||||
|
@ -229,7 +229,10 @@ export const LibraryMenu = ({
|
|||
LibraryItem["id"] | null
|
||||
>(null);
|
||||
|
||||
if (libraryItemsData.status === "loading") {
|
||||
if (
|
||||
libraryItemsData.status === "loading" &&
|
||||
!libraryItemsData.isInitialized
|
||||
) {
|
||||
return (
|
||||
<LibraryMenuWrapper ref={ref}>
|
||||
<div className="layer-ui__library-message">
|
||||
|
@ -255,7 +258,7 @@ export const LibraryMenu = ({
|
|||
}
|
||||
onError={(error) => window.alert(error)}
|
||||
updateItemsInStorage={() =>
|
||||
library.saveLibrary(libraryItemsData.libraryItems)
|
||||
library.setLibrary(libraryItemsData.libraryItems)
|
||||
}
|
||||
onRemove={(id: string) =>
|
||||
setSelectedItems(selectedItems.filter((_id) => _id !== id))
|
||||
|
@ -264,6 +267,7 @@ export const LibraryMenu = ({
|
|||
)}
|
||||
{publishLibSuccess && renderPublishSuccess()}
|
||||
<LibraryMenuItems
|
||||
isLoading={libraryItemsData.status === "loading"}
|
||||
libraryItems={libraryItemsData.libraryItems}
|
||||
onRemoveFromLibrary={() =>
|
||||
removeFromLibrary(libraryItemsData.libraryItems)
|
||||
|
|
|
@ -22,8 +22,10 @@ import { Tooltip } from "./Tooltip";
|
|||
|
||||
import "./LibraryMenuItems.scss";
|
||||
import { VERSIONS } from "../constants";
|
||||
import Spinner from "./Spinner";
|
||||
|
||||
const LibraryMenuItems = ({
|
||||
isLoading,
|
||||
libraryItems,
|
||||
onRemoveFromLibrary,
|
||||
onAddToLibrary,
|
||||
|
@ -40,6 +42,7 @@ const LibraryMenuItems = ({
|
|||
onPublish,
|
||||
resetLibrary,
|
||||
}: {
|
||||
isLoading: boolean;
|
||||
libraryItems: LibraryItems;
|
||||
pendingElements: LibraryItem["elements"];
|
||||
onRemoveFromLibrary: () => void;
|
||||
|
@ -108,7 +111,8 @@ const LibraryMenuItems = ({
|
|||
importLibraryFromJSON(library)
|
||||
.catch(muteFSAbortError)
|
||||
.catch((error) => {
|
||||
setAppState({ errorMessage: error.message });
|
||||
console.error(error);
|
||||
setAppState({ errorMessage: t("errors.importLibraryError") });
|
||||
});
|
||||
}}
|
||||
className="library-actions--load"
|
||||
|
@ -125,7 +129,7 @@ const LibraryMenuItems = ({
|
|||
onClick={async () => {
|
||||
const libraryItems = itemsSelected
|
||||
? items
|
||||
: await library.loadLibrary();
|
||||
: await library.getLatestLibrary();
|
||||
saveLibraryAsJSON(libraryItems)
|
||||
.catch(muteFSAbortError)
|
||||
.catch((error) => {
|
||||
|
@ -284,16 +288,20 @@ const LibraryMenuItems = ({
|
|||
{showRemoveLibAlert && renderRemoveLibAlert()}
|
||||
<div className="layer-ui__library-header" key="library-header">
|
||||
{renderLibraryActions()}
|
||||
<a
|
||||
href={`${process.env.REACT_APP_LIBRARY_URL}?target=${
|
||||
window.name || "_blank"
|
||||
}&referrer=${referrer}&useHash=true&token=${id}&theme=${theme}&version=${
|
||||
VERSIONS.excalidrawLibrary
|
||||
}`}
|
||||
target="_excalidraw_libraries"
|
||||
>
|
||||
{t("labels.libraries")}
|
||||
</a>
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<a
|
||||
href={`${process.env.REACT_APP_LIBRARY_URL}?target=${
|
||||
window.name || "_blank"
|
||||
}&referrer=${referrer}&useHash=true&token=${id}&theme=${theme}&version=${
|
||||
VERSIONS.excalidrawLibrary
|
||||
}`}
|
||||
target="_excalidraw_libraries"
|
||||
>
|
||||
{t("labels.libraries")}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<Stack.Col
|
||||
className="library-menu-items-container__items"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue