diff --git a/src/actions/actionExport.tsx b/src/actions/actionExport.tsx index 41fd9dc6e..f01df05f2 100644 --- a/src/actions/actionExport.tsx +++ b/src/actions/actionExport.tsx @@ -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 }); + }); }} /> ), diff --git a/src/appState.ts b/src/appState.ts index 021e58e25..1c39dd448 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -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; diff --git a/src/components/App.tsx b/src/components/App.tsx index a1f16436d..7a5c5e38c 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -247,9 +247,13 @@ export class App extends React.Component { }), ) .catch((error) => { - console.error(error); - this.setState({ isLoading: false }); + this.setState({ isLoading: false, errorMessage: error }); }); + } else { + this.setState({ + isLoading: false, + errorMessage: t("alerts.couldNotLoadInvalidFile"), + }); } }} > diff --git a/src/components/ErrorDialog.tsx b/src/components/ErrorDialog.tsx new file mode 100644 index 000000000..10ace9454 --- /dev/null +++ b/src/components/ErrorDialog.tsx @@ -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 && ( + +
{message}
+
+ )} + + ); +} diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index e6d47d5b8..fce230a61 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -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 && } + {appState.errorMessage && ( + setAppState({ errorMessage: null })} + /> + )}
diff --git a/src/data/blob.ts b/src/data/blob.ts index 62afd40b5..53204de1d 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -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((resolve) => { resolve(restore(elements, appState, { scrollToContent: true })); diff --git a/src/locales/en.json b/src/locales/en.json index 2d7adce42..25ffb32ab 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -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" } } diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index 2909bf1df..ab6a7cdbe 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -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, diff --git a/src/types.ts b/src/types.ts index 3d4ee82ff..ec321f826 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,6 +11,7 @@ export type Point = Readonly; export type AppState = { isLoading: boolean; + errorMessage: string | null; draggingElement: ExcalidrawElement | null; resizingElement: ExcalidrawElement | null; multiElement: ExcalidrawLinearElement | null;