diff --git a/src/actions/actionExport.tsx b/src/actions/actionExport.tsx index 41fd9dc6eb..f01df05f26 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 021e58e25c..1c39dd4485 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 a1f16436de..7a5c5e38c4 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 0000000000..10ace9454a --- /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 e6d47d5b88..fce230a612 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 62afd40b5c..53204de1dd 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 2d7adce422..25ffb32ab6 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 2909bf1df4..ab6a7cdbee 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 3d4ee82ff2..ec321f826c 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;