mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
Warn on invalid JSON file (#1159)
* add error dialog * show error modal on file dnd * add locales * Update src/locales/en.json Co-Authored-By: Lipis <lipiridis@gmail.com> * Update src/data/blob.ts * Update src/data/blob.ts * fix titles, update snapshots * make modal smaller * fix dnd wrong file type * reset errorMessage Co-authored-by: Faustino Kialungila <faustino.kialungila@gmail.com> Co-authored-by: Lipis <lipiridis@gmail.com>
This commit is contained in:
parent
2be34effd4
commit
0c9459e9e5
9 changed files with 108 additions and 7 deletions
|
@ -64,11 +64,14 @@ export const actionLoadScene = register({
|
|||
perform: (
|
||||
elements,
|
||||
appState,
|
||||
{ elements: loadedElements, appState: loadedAppState },
|
||||
{ elements: loadedElements, appState: loadedAppState, error },
|
||||
) => {
|
||||
return {
|
||||
elements: loadedElements,
|
||||
appState: loadedAppState,
|
||||
appState: {
|
||||
...loadedAppState,
|
||||
errorMessage: error,
|
||||
},
|
||||
commitToHistory: false,
|
||||
};
|
||||
},
|
||||
|
@ -84,7 +87,9 @@ export const actionLoadScene = register({
|
|||
.then(({ elements, appState }) => {
|
||||
updateData({ elements: elements, appState: appState });
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
.catch((error) => {
|
||||
updateData({ error: error });
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -6,6 +6,7 @@ export const DEFAULT_FONT = "20px Virgil";
|
|||
export function getDefaultAppState(): AppState {
|
||||
return {
|
||||
isLoading: false,
|
||||
errorMessage: null,
|
||||
draggingElement: null,
|
||||
resizingElement: null,
|
||||
multiElement: null,
|
||||
|
@ -52,6 +53,7 @@ export function clearAppStateForLocalStorage(appState: AppState) {
|
|||
collaborators,
|
||||
isCollaborating,
|
||||
isLoading,
|
||||
errorMessage,
|
||||
...exportedState
|
||||
} = appState;
|
||||
return exportedState;
|
||||
|
|
|
@ -247,8 +247,12 @@ export class App extends React.Component<any, AppState> {
|
|||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
this.setState({ isLoading: false });
|
||||
this.setState({ isLoading: false, errorMessage: error });
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMessage: t("alerts.couldNotLoadInvalidFile"),
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
36
src/components/ErrorDialog.tsx
Normal file
36
src/components/ErrorDialog.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React, { useState } from "react";
|
||||
import { t } from "../i18n";
|
||||
|
||||
import { Dialog } from "./Dialog";
|
||||
|
||||
export function ErrorDialog({
|
||||
message,
|
||||
onClose,
|
||||
}: {
|
||||
message: string;
|
||||
onClose?: () => void;
|
||||
}) {
|
||||
const [modalIsShown, setModalIsShown] = useState(!!message);
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
setModalIsShown(false);
|
||||
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{modalIsShown && (
|
||||
<Dialog
|
||||
maxWidth={500}
|
||||
onCloseRequest={handleClose}
|
||||
title={t("errorDialog.title")}
|
||||
>
|
||||
<div>{message}</div>
|
||||
</Dialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -22,6 +22,7 @@ import { MobileMenu } from "./MobileMenu";
|
|||
import { ZoomActions, SelectedShapeActions, ShapesSwitcher } from "./Actions";
|
||||
import { Section } from "./Section";
|
||||
import { RoomDialog } from "./RoomDialog";
|
||||
import { ErrorDialog } from "./ErrorDialog";
|
||||
import { LoadingMessage } from "./LoadingMessage";
|
||||
|
||||
interface LayerUIProps {
|
||||
|
@ -105,6 +106,12 @@ export const LayerUI = React.memo(
|
|||
) : (
|
||||
<>
|
||||
{appState.isLoading && <LoadingMessage />}
|
||||
{appState.errorMessage && (
|
||||
<ErrorDialog
|
||||
message={appState.errorMessage}
|
||||
onClose={() => setAppState({ errorMessage: null })}
|
||||
/>
|
||||
)}
|
||||
<FixedSideContainer side="top">
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
<div className="App-menu App-menu_top">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { getDefaultAppState } from "../appState";
|
||||
import { DataState } from "./types";
|
||||
import { restore } from "./restore";
|
||||
import { t } from "../i18n";
|
||||
|
||||
export async function loadFromBlob(blob: any) {
|
||||
const updateAppState = (contents: string) => {
|
||||
|
@ -10,7 +11,7 @@ export async function loadFromBlob(blob: any) {
|
|||
try {
|
||||
const data = JSON.parse(contents);
|
||||
if (data.type !== "excalidraw") {
|
||||
throw new Error("Cannot load invalid json");
|
||||
throw new Error(t("alerts.couldNotLoadInvalidFile"));
|
||||
}
|
||||
elements = data.elements || [];
|
||||
appState = { ...defaultAppState, ...data.appState };
|
||||
|
@ -39,7 +40,7 @@ export async function loadFromBlob(blob: any) {
|
|||
}
|
||||
const { elements, appState } = updateAppState(contents);
|
||||
if (!elements.length) {
|
||||
return Promise.reject("Cannot load invalid json");
|
||||
return Promise.reject(t("alerts.couldNotLoadInvalidFile"));
|
||||
}
|
||||
return new Promise<DataState>((resolve) => {
|
||||
resolve(restore(elements, appState, { scrollToContent: true }));
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
"alerts": {
|
||||
"clearReset": "This will clear the whole canvas. Are you sure?",
|
||||
"couldNotCreateShareableLink": "Couldn't create shareable link.",
|
||||
"couldNotLoadInvalidFile": "Couldn't load invalid file",
|
||||
"importBackendFailed": "Importing from backend failed.",
|
||||
"cannotExportEmptyCanvas": "Cannot export empty canvas.",
|
||||
"couldNotCopyToClipboard": "Couldn't copy to clipboard. Try using Chrome browser.",
|
||||
|
@ -123,5 +124,8 @@
|
|||
"desc_persistenceWarning": "Note that the scene data is shared across collaborators in a P2P fashion, and not persisted to our server. Thus, if all of you disconnect, you will loose the data unless you export it to a file or a shareable link.",
|
||||
"desc_shareLink": "Share this link with anyone you want to collaborate with:",
|
||||
"desc_exitSession": "Stopping the session will disconnect your from the room, but you'll be able to continue working with the scene, locally. Note that this won't affect other people, and they'll still be able to collaborate on their version."
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Error"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -202,6 +203,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -311,6 +313,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -560,6 +563,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -705,6 +709,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -886,6 +891,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -1072,6 +1078,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -1354,6 +1361,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -1949,6 +1957,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2058,6 +2067,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2167,6 +2177,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2276,6 +2287,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2407,6 +2419,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2538,6 +2551,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2669,6 +2683,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2778,6 +2793,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -2887,6 +2903,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -3018,6 +3035,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -3127,6 +3145,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -3178,6 +3197,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -3863,6 +3883,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -4224,6 +4245,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -4513,6 +4535,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -4730,6 +4753,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -4875,6 +4899,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -5524,6 +5549,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -6101,6 +6127,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -6606,6 +6633,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -7039,6 +7067,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -7436,6 +7465,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -7761,6 +7791,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -8014,6 +8045,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -8195,6 +8227,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -8880,6 +8913,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -9493,6 +9527,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -10034,6 +10069,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -10503,6 +10539,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -10746,6 +10783,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -10795,6 +10833,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -10846,6 +10885,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
@ -11127,6 +11167,7 @@ Object {
|
|||
"editingElement": null,
|
||||
"elementLocked": false,
|
||||
"elementType": "selection",
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"isCollaborating": false,
|
||||
"isLoading": false,
|
||||
|
|
|
@ -11,6 +11,7 @@ export type Point = Readonly<RoughPoint>;
|
|||
|
||||
export type AppState = {
|
||||
isLoading: boolean;
|
||||
errorMessage: string | null;
|
||||
draggingElement: ExcalidrawElement | null;
|
||||
resizingElement: ExcalidrawElement | null;
|
||||
multiElement: ExcalidrawLinearElement | null;
|
||||
|
|
Loading…
Add table
Reference in a new issue