mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-06-21 03:54:14 -05:00
Show a note under non-autoValidate image fields that uploads are moderated before going public. Move autoValidate to a per-field schema property (org logo only) driving both the action logic and the note. Preview pending images on the team edit page while keeping them hidden on the public page.
106 lines
2.6 KiB
TypeScript
106 lines
2.6 KiB
TypeScript
import clsx from "clsx";
|
|
import Compressor from "compressorjs";
|
|
import * as React from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { SendouButton } from "~/components/elements/Button";
|
|
import { logger } from "~/utils/logger";
|
|
import {
|
|
type ImageFieldValue,
|
|
resolveImageFieldDimensions,
|
|
} from "../image-field";
|
|
import type { FormFieldProps } from "../types";
|
|
import { FormFieldWrapper } from "./FormFieldWrapper";
|
|
import styles from "./ImageFormField.module.css";
|
|
|
|
type ImageFormFieldProps = Omit<FormFieldProps<"image">, "onBlur"> & {
|
|
value: ImageFieldValue;
|
|
onChange: (value: ImageFieldValue) => void;
|
|
};
|
|
|
|
export function ImageFormField({
|
|
name,
|
|
label,
|
|
dimensions,
|
|
autoValidate,
|
|
error,
|
|
value,
|
|
onChange,
|
|
}: ImageFormFieldProps) {
|
|
const id = React.useId();
|
|
const { t } = useTranslation(["common"]);
|
|
const resolvedDimensions = resolveImageFieldDimensions(dimensions);
|
|
|
|
const previewUrl =
|
|
value?.type === "EXISTING"
|
|
? value.url
|
|
: value?.type === "NEW"
|
|
? value.dataUrl
|
|
: null;
|
|
|
|
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
const uploadedFile = event.target.files?.[0];
|
|
if (!uploadedFile) return;
|
|
|
|
new Compressor(uploadedFile, {
|
|
width: resolvedDimensions.width,
|
|
height: resolvedDimensions.height,
|
|
maxWidth: resolvedDimensions.width,
|
|
maxHeight: resolvedDimensions.height,
|
|
resize: "cover",
|
|
mimeType: "image/webp",
|
|
success(result) {
|
|
const reader = new FileReader();
|
|
reader.onload = () =>
|
|
onChange({ type: "NEW", dataUrl: reader.result as string });
|
|
reader.onerror = () => logger.error("Failed to read compressed image");
|
|
reader.readAsDataURL(result);
|
|
},
|
|
error(err) {
|
|
logger.error(err.message);
|
|
},
|
|
});
|
|
};
|
|
|
|
const isBanner =
|
|
dimensions === "thick-banner" ||
|
|
(typeof dimensions === "object" && dimensions.width > dimensions.height);
|
|
|
|
return (
|
|
<FormFieldWrapper
|
|
id={id}
|
|
name={name}
|
|
label={label}
|
|
error={error}
|
|
bottomText={
|
|
autoValidate ? undefined : "forms:bottomTexts.imageModeration"
|
|
}
|
|
>
|
|
<div className="stack sm items-start">
|
|
{previewUrl ? (
|
|
<img
|
|
src={previewUrl}
|
|
alt=""
|
|
className={clsx(styles.preview, { [styles.banner]: isBanner })}
|
|
/>
|
|
) : null}
|
|
{value ? (
|
|
<SendouButton
|
|
variant="minimal-destructive"
|
|
size="small"
|
|
onPress={() => onChange(null)}
|
|
>
|
|
{t("common:actions.remove")}
|
|
</SendouButton>
|
|
) : (
|
|
<input
|
|
id={id}
|
|
type="file"
|
|
accept="image/png, image/jpeg, image/webp"
|
|
onChange={handleFileChange}
|
|
/>
|
|
)}
|
|
</div>
|
|
</FormFieldWrapper>
|
|
);
|
|
}
|