2023-08-10 10:10:09 +00:00
|
|
|
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";
|
2023-08-10 11:53:23 +00:00
|
|
|
import UploadIcon from "@mui/icons-material/Upload";
|
2023-08-11 08:30:04 +00:00
|
|
|
import LinkIcon from "@mui/icons-material/Link";
|
|
|
|
import { isDebug } from "../../utils/debug_utils";
|
2023-08-10 10:10:09 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-08-11 08:30:04 +00:00
|
|
|
const uploadPhotoFromURL = async () => {
|
|
|
|
const URL = prompt("Image URL ?");
|
|
|
|
if (URL === null || URL.length === 0) return;
|
|
|
|
setImageURL(URL);
|
|
|
|
};
|
|
|
|
|
2023-08-10 10:10:09 +00:00
|
|
|
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 */}
|
2023-08-10 11:53:23 +00:00
|
|
|
<Button
|
|
|
|
onClick={uploadPhoto}
|
|
|
|
variant="outlined"
|
|
|
|
startIcon={<UploadIcon />}
|
|
|
|
>
|
|
|
|
{p.label}
|
|
|
|
</Button>
|
2023-08-11 08:30:04 +00:00
|
|
|
{/* Upload button (from URL) */}{" "}
|
|
|
|
{isDebug() && (
|
|
|
|
<Button
|
|
|
|
onClick={uploadPhotoFromURL}
|
|
|
|
variant="outlined"
|
|
|
|
startIcon={<LinkIcon />}
|
|
|
|
>
|
|
|
|
{p.label} from URL
|
|
|
|
</Button>
|
|
|
|
)}{" "}
|
2023-08-10 10:10:09 +00:00
|
|
|
{/* Crop image dialog */}
|
|
|
|
{imageURL && (
|
|
|
|
<ImageCropperDialog
|
|
|
|
processing={processing}
|
|
|
|
src={imageURL!}
|
|
|
|
onCancel={cancelCrop}
|
|
|
|
onSubmit={submitCrop}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|