Refactor image conversion logic in convert-image-to-webp.ts to handle HEIC files and improve compatibility with Safari. Update ReceiptForm and +page components to streamline image upload process, removing camera functionality and enhancing user feedback during image processing. Adjust styles for better accessibility and clarity in image handling.
This commit is contained in:
@@ -1,42 +1,94 @@
|
||||
import { convertHeicBlobToJpeg } from "./convert-heic";
|
||||
|
||||
function isHeicFile(file: File): boolean {
|
||||
const name = file.name.toLowerCase();
|
||||
const type = (file.type ?? "").toLowerCase();
|
||||
return (
|
||||
name.endsWith(".heic") ||
|
||||
name.endsWith(".heif") ||
|
||||
type === "image/heic" ||
|
||||
type === "image/heif"
|
||||
);
|
||||
}
|
||||
|
||||
function makeOutputFile(blob: Blob, baseName: string, ext: string): File {
|
||||
return new File([blob], `${baseName}.${ext}`, {
|
||||
type: blob.type,
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an image File to WebP and returns a new File.
|
||||
* Used so receipt uploads are stored only as WebP.
|
||||
* Converts an image File to WebP (or JPEG on Safari where WebP isn't supported).
|
||||
* HEIC from iPhone is handled: if the image fails to load, we decode with heic2any first.
|
||||
*/
|
||||
export function convertImageToWebP(file: File, quality = 0.85): Promise<File> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
const baseName = file.name.replace(/\.[^.]+$/i, "") || "image";
|
||||
|
||||
const tryCanvas = (sourceBlob: Blob) => {
|
||||
const url = URL.createObjectURL(sourceBlob);
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
reject(new Error("Could not get canvas context"));
|
||||
return;
|
||||
}
|
||||
ctx.drawImage(img, 0, 0);
|
||||
// Safari doesn't support WebP in toBlob; try WebP first, fall back to JPEG
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (blob && blob.type === "image/webp") {
|
||||
resolve(makeOutputFile(blob, baseName, "webp"));
|
||||
return;
|
||||
}
|
||||
canvas.toBlob(
|
||||
(jpegBlob) => {
|
||||
if (!jpegBlob) {
|
||||
reject(new Error("Failed to encode image"));
|
||||
return;
|
||||
}
|
||||
resolve(makeOutputFile(jpegBlob, baseName, "jpg"));
|
||||
},
|
||||
"image/jpeg",
|
||||
quality
|
||||
);
|
||||
},
|
||||
"image/webp",
|
||||
quality
|
||||
);
|
||||
};
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
reject(new Error("Failed to load image"));
|
||||
};
|
||||
img.src = url;
|
||||
};
|
||||
|
||||
const url = URL.createObjectURL(file);
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
reject(new Error("Could not get canvas context"));
|
||||
return;
|
||||
}
|
||||
ctx.drawImage(img, 0, 0);
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (!blob) {
|
||||
reject(new Error("Failed to encode WebP"));
|
||||
return;
|
||||
}
|
||||
const baseName = file.name.replace(/\.[^.]+$/i, "") || "image";
|
||||
const webpFile = new File([blob], `${baseName}.webp`, {
|
||||
type: "image/webp",
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
resolve(webpFile);
|
||||
},
|
||||
"image/webp",
|
||||
quality
|
||||
);
|
||||
tryCanvas(file);
|
||||
};
|
||||
img.onerror = () => {
|
||||
img.onerror = async () => {
|
||||
URL.revokeObjectURL(url);
|
||||
reject(new Error("Failed to load image"));
|
||||
// HEIC often fails to load in Image() on iOS; decode with heic2any then run canvas
|
||||
if (isHeicFile(file)) {
|
||||
try {
|
||||
const jpegBlob = await convertHeicBlobToJpeg(file);
|
||||
tryCanvas(jpegBlob);
|
||||
} catch (e) {
|
||||
reject(e instanceof Error ? e : new Error("Failed to convert HEIC"));
|
||||
}
|
||||
} else {
|
||||
reject(new Error("Failed to load image"));
|
||||
}
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user