Clearer errors when image download fails
Two fixes in here, for when image downloads fail! 1) Actually catch the error, and show UI feedback 2) Throw it as an actual exception, so the console message will have a stack trace Additionally, debugging this was a bit trickier than normal, because I didn't fully understand that the image `onerror` argument is an error _event_, not an Error object. So, Sentry captured the uncaught promise rejection, but it didn't have trace information, because it wasn't an Error. Whereas now, if I forget to catch `loadImage` calls in the future, we'll get a real trace! both in the console for debugging, and in Sentry if it makes it to prod :)
This commit is contained in:
parent
cfb5504341
commit
f6ce8611b2
2 changed files with 17 additions and 3 deletions
|
@ -407,6 +407,7 @@ function ControlButton({ icon, "aria-label": ariaLabel, ...props }) {
|
||||||
function useDownloadableImage(visibleLayers) {
|
function useDownloadableImage(visibleLayers) {
|
||||||
const [downloadImageUrl, setDownloadImageUrl] = React.useState(null);
|
const [downloadImageUrl, setDownloadImageUrl] = React.useState(null);
|
||||||
const [preparedForLayerIds, setPreparedForLayerIds] = React.useState([]);
|
const [preparedForLayerIds, setPreparedForLayerIds] = React.useState([]);
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
const prepareDownload = React.useCallback(async () => {
|
const prepareDownload = React.useCallback(async () => {
|
||||||
// Skip if the current image URL is already correct for these layers.
|
// Skip if the current image URL is already correct for these layers.
|
||||||
|
@ -426,7 +427,19 @@ function useDownloadableImage(visibleLayers) {
|
||||||
loadImage(getBestImageUrlForLayer(layer))
|
loadImage(getBestImageUrlForLayer(layer))
|
||||||
);
|
);
|
||||||
|
|
||||||
const images = await Promise.all(imagePromises);
|
let images;
|
||||||
|
try {
|
||||||
|
images = await Promise.all(imagePromises);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error building downloadable image", e);
|
||||||
|
toast({
|
||||||
|
status: "error",
|
||||||
|
title: "Oops, sorry, we couldn't download the image!",
|
||||||
|
description:
|
||||||
|
"Check your connection, then reload the page and try again.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const context = canvas.getContext("2d");
|
const context = canvas.getContext("2d");
|
||||||
|
@ -444,7 +457,7 @@ function useDownloadableImage(visibleLayers) {
|
||||||
);
|
);
|
||||||
setDownloadImageUrl(canvas.toDataURL("image/png"));
|
setDownloadImageUrl(canvas.toDataURL("image/png"));
|
||||||
setPreparedForLayerIds(layerIds);
|
setPreparedForLayerIds(layerIds);
|
||||||
}, [preparedForLayerIds, visibleLayers]);
|
}, [preparedForLayerIds, visibleLayers, toast]);
|
||||||
|
|
||||||
return [downloadImageUrl, prepareDownload];
|
return [downloadImageUrl, prepareDownload];
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,8 @@ export function loadImage({ src, crossOrigin = null }) {
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
image.onload = () => resolve(image);
|
image.onload = () => resolve(image);
|
||||||
image.onerror = (e) => reject(e);
|
image.onerror = () =>
|
||||||
|
reject(new Error(`Failed to load image: ${JSON.stringify(src)}`));
|
||||||
if (crossOrigin) {
|
if (crossOrigin) {
|
||||||
image.crossOrigin = crossOrigin;
|
image.crossOrigin = crossOrigin;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue