Show error message when canvas to export is too big (#1256) (#2210)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Giacomo Debidda 2020-10-28 20:52:53 +01:00 committed by GitHub
parent 5c26bd19d7
commit fc58e51ab3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 193 additions and 118 deletions

View file

@ -1022,12 +1022,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
copyToClipboard(this.scene.getElements(), this.state);
};
private copyToClipboardAsPng = () => {
private copyToClipboardAsPng = async () => {
const elements = this.scene.getElements();
const selectedElements = getSelectedElements(elements, this.state);
try {
exportCanvas(
await exportCanvas(
"clipboard",
selectedElements.length ? selectedElements : elements,
this.state,
@ -1040,13 +1040,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
};
private copyToClipboardAsSvg = () => {
private copyToClipboardAsSvg = async () => {
const selectedElements = getSelectedElements(
this.scene.getElements(),
this.state,
);
try {
exportCanvas(
await exportCanvas(
"clipboard-svg",
selectedElements.length ? selectedElements : this.scene.getElements(),
this.state,

View file

@ -15,10 +15,24 @@ import { probablySupportsClipboardBlob } from "../clipboard";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import useIsMobile from "../is-mobile";
import { Dialog } from "./Dialog";
import { canvasToBlob } from "../data/blob";
import { CanvasError } from "../errors";
const scales = [1, 2, 3];
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
export const ErrorCanvasPreview = () => {
return (
<div>
<h3>{t("canvasError.cannotShowPreview")}</h3>
<p>
<span>{t("canvasError.canvasTooBig")}</span>
</p>
<em>({t("canvasError.canvasTooBigTip")})</em>
</div>
);
};
export type ExportCB = (
elements: readonly NonDeletedExcalidrawElement[],
scale?: number,
@ -47,6 +61,7 @@ const ExportModal = ({
const someElementIsSelected = isSomeElementSelected(elements, appState);
const [scale, setScale] = useState(defaultScale);
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const [previewError, setPreviewError] = useState<Error | null>(null);
const previewRef = useRef<HTMLDivElement>(null);
const {
exportBackground,
@ -64,17 +79,42 @@ const ExportModal = ({
useEffect(() => {
const previewNode = previewRef.current;
const canvas = exportToCanvas(exportedElements, appState, {
exportBackground,
viewBackgroundColor,
exportPadding,
scale,
shouldAddWatermark,
});
previewNode?.appendChild(canvas);
return () => {
previewNode?.removeChild(canvas);
};
if (!previewNode) {
return;
}
try {
const canvas = exportToCanvas(exportedElements, appState, {
exportBackground,
viewBackgroundColor,
exportPadding,
scale,
shouldAddWatermark,
});
let isRemoved = false;
// if converting to blob fails, there's some problem that will
// likely prevent preview and export (e.g. canvas too big)
canvasToBlob(canvas)
.then(() => {
if (isRemoved) {
return;
}
setPreviewError(null);
previewNode.appendChild(canvas);
})
.catch((error) => {
console.error(error);
setPreviewError(new CanvasError());
});
return () => {
isRemoved = true;
canvas.remove();
};
} catch (error) {
console.error(error);
setPreviewError(new CanvasError());
}
}, [
appState,
exportedElements,
@ -87,7 +127,9 @@ const ExportModal = ({
return (
<div className="ExportDialog">
<div className="ExportDialog__preview" ref={previewRef}></div>
<div className="ExportDialog__preview" ref={previewRef}>
{previewError && <ErrorCanvasPreview />}
</div>
<Stack.Col gap={2} align="center">
<div className="ExportDialog__actions">
<Stack.Row gap={2}>

View file

@ -315,18 +315,18 @@ const LayerUI = ({
scale,
) => {
if (canvas) {
try {
await exportCanvas(type, exportedElements, appState, canvas, {
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
scale,
shouldAddWatermark: appState.shouldAddWatermark,
await exportCanvas(type, exportedElements, appState, canvas, {
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
scale,
shouldAddWatermark: appState.shouldAddWatermark,
})
.catch(muteFSAbortError)
.catch((error) => {
console.error(error);
setAppState({ errorMessage: error.message });
});
} catch (error) {
console.error(error);
setAppState({ errorMessage: error.message });
}
}
};
return (
@ -351,8 +351,11 @@ const LayerUI = ({
appState,
);
} catch (error) {
console.error(error);
setAppState({ errorMessage: error.message });
if (error.name !== "AbortError") {
const { width, height } = canvas;
console.error(error, { width, height });
setAppState({ errorMessage: error.message });
}
}
}
}}

View file

@ -73,7 +73,7 @@ export class TopErrorBoundary extends React.Component<
private errorSplash() {
return (
<div className="ErrorSplash">
<div className="ErrorSplash excalidraw">
<div className="ErrorSplash-messageContainer">
<div className="ErrorSplash-paragraph bigger align-center">
{t("errorSplash.headingMain_pre")}