Files
reminder-bot/reminder-dashboard/src/components/Reminder/ImagePicker.tsx
T

141 lines
4.8 KiB
TypeScript

import { Modal } from "../Modal";
import { useRef, useState } from "preact/hooks";
import { useQuery } from "react-query";
import axios from "axios";
import { fetchUserInfo, uploadImage } from "../../api";
export const ImagePicker = ({ alt, url, setImage, ...props }) => {
const [modalOpen, setModalOpen] = useState(false);
return (
<>
<a
onClick={() => {
setModalOpen(true);
}}
role={"button"}
>
<img {...props} src={url || "/static/img/bg.webp"} alt={alt}></img>
</a>
{modalOpen && (
<ImagePickerModal
setModalOpen={setModalOpen}
setImage={setImage}
url={url}
></ImagePickerModal>
)}
</>
);
};
const ImagePickerModal = ({ setModalOpen, setImage, url }) => {
const [value, setValue] = useState(url || "");
const [uploading, setUploading] = useState(false);
const [error, setError] = useState("");
const [blocked, setBlocked] = useState(false);
const fileInput = useRef<HTMLInputElement>(null);
const { data: userInfo } = useQuery(fetchUserInfo());
const upload = async (file: File) => {
setError("");
if (file.size > 8 * 1024 * 1024) {
setError("Images must be no larger than 8 MiB.");
return;
}
setUploading(true);
try {
setValue(await uploadImage(file));
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 451) {
setBlocked(true);
} else {
setError(
axios.isAxiosError(error) && typeof error.response?.data?.error === "string"
? error.response.data.error
: "Could not upload image. Please try again.",
);
}
} finally {
setUploading(false);
}
};
if (blocked) {
return (
<Modal
setModalOpen={() => setBlocked(false)}
title="Image upload blocked"
onSubmit={() => setBlocked(false)}
onSubmitText="OK"
>
<p>
This image was flagged as probably NSFW (not safe for work) and cannot be
uploaded. Please choose a different image that does not contain sexual or
explicit content.
</p>
</Modal>
);
}
return (
<Modal
setModalOpen={setModalOpen}
title={"Enter Image URL"}
onSubmit={() => {
setImage(value);
setModalOpen(false);
}}
onSubmitText={"Save"}
onSubmitDisabled={uploading}
>
<p>
Please note: if you attach an image directly from Discord, it will not be visible in
the dashboard, but will be visible on reminders. Other image-sharing sites such as
Imgur don't have this issue.
</p>
<input
class="input"
id="urlInput"
placeholder="Image URL..."
value={value}
disabled={uploading}
onInput={(ev) => {
setValue(ev.currentTarget.value);
}}
></input>
{userInfo?.image_uploads && (
<div class="mt-4">
<input
ref={fileInput}
type="file"
accept="image/png,image/jpeg,image/gif,image/webp"
hidden
disabled={uploading}
aria-label="Choose an image to upload"
onChange={(event) => {
const file = event.currentTarget.files?.[0];
event.currentTarget.value = "";
if (file) void upload(file);
}}
/>
<button
type="button"
class={`button is-info ${uploading ? "is-loading" : ""}`}
disabled={uploading}
onClick={() => fileInput.current?.click()}
>
Upload image
</button>
<p class="help">PNG, JPEG, GIF or WebP, up to 8 MiB. Saved as a static PNG.</p>
{uploading && <p role="status">Checking and uploading image…</p>}
</div>
)}
{error && (
<p class="help is-danger" role="alert">
{error}
</p>
)}
</Modal>
);
};