99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
|
import { Button } from "@mui/material";
|
||
|
import { filesize } from "filesize";
|
||
|
import React from "react";
|
||
|
import { ServerApi } from "../../api/ServerApi";
|
||
|
import { ImageCropperDialog } from "../../dialogs/ImageCropperDialog";
|
||
|
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||
|
import { Area } from "react-easy-crop";
|
||
|
import getCroppedImg from "../../utils/crop_image";
|
||
|
|
||
|
export function UploadPhotoButton(p: {
|
||
|
label: string;
|
||
|
onPhotoSelected: (b: Blob) => Promise<void>;
|
||
|
}): React.ReactElement {
|
||
|
const [processing, setProcessing] = React.useState(false);
|
||
|
const [imageBlob, setImageBlob] = React.useState<Blob>();
|
||
|
const [imageURL, setImageURL] = React.useState<string>();
|
||
|
const alert = useAlert();
|
||
|
|
||
|
const uploadPhoto = async () => {
|
||
|
try {
|
||
|
// Create file element
|
||
|
const fileEl = document.createElement("input");
|
||
|
fileEl.type = "file";
|
||
|
fileEl.accept =
|
||
|
ServerApi.Config.constraints.photo_allowed_types.join(",");
|
||
|
fileEl.click();
|
||
|
|
||
|
// Wait for a file to be chosen
|
||
|
await new Promise((res, _rej) =>
|
||
|
fileEl.addEventListener("change", () => res(null))
|
||
|
);
|
||
|
|
||
|
if ((fileEl.files?.length ?? 0) === 0) return null;
|
||
|
const file = fileEl.files![0];
|
||
|
|
||
|
// Check file size
|
||
|
if (file.size > ServerApi.Config.constraints.photo_max_size) {
|
||
|
await alert(
|
||
|
`Le fichier sélectionné est trop lourd ! (taille maximale acceptée : ${filesize(
|
||
|
ServerApi.Config.constraints.photo_max_size
|
||
|
)})`
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const tempURL = URL.createObjectURL(fileEl.files![0]);
|
||
|
|
||
|
setImageBlob(file);
|
||
|
setImageURL(tempURL);
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
alert("Failed to upload custom account image!");
|
||
|
return null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const cancelCrop = () => {
|
||
|
setImageURL(undefined);
|
||
|
};
|
||
|
|
||
|
const submitCrop = async (a: Area | undefined) => {
|
||
|
setProcessing(true);
|
||
|
try {
|
||
|
let blob = imageBlob!;
|
||
|
|
||
|
if (a) {
|
||
|
blob = await getCroppedImg(imageURL!, a!);
|
||
|
}
|
||
|
|
||
|
await p.onPhotoSelected(blob);
|
||
|
|
||
|
setImageBlob(undefined);
|
||
|
setImageURL(undefined);
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
alert("Echec du traitement de la photo !");
|
||
|
}
|
||
|
|
||
|
setProcessing(false);
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<>
|
||
|
{/* Upload button */}
|
||
|
<Button onClick={uploadPhoto}>{p.label}</Button>
|
||
|
|
||
|
{/* Crop image dialog */}
|
||
|
{imageURL && (
|
||
|
<ImageCropperDialog
|
||
|
processing={processing}
|
||
|
src={imageURL!}
|
||
|
onCancel={cancelCrop}
|
||
|
onSubmit={submitCrop}
|
||
|
/>
|
||
|
)}
|
||
|
</>
|
||
|
);
|
||
|
}
|