Skip to content

Commit

Permalink
Fix yup schemas (#3509)
Browse files Browse the repository at this point in the history
* Fix yup schemas
* Add internationalization
  • Loading branch information
DingDongSoLong4 authored Mar 7, 2023
1 parent 6b59b96 commit 9ede271
Show file tree
Hide file tree
Showing 39 changed files with 631 additions and 650 deletions.
14 changes: 13 additions & 1 deletion ui/v2.5/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
useSystemStatus,
} from "src/core/StashService";
import flattenMessages from "./utils/flattenMessages";
import * as yup from "yup";
import Mousetrap from "mousetrap";
import MousetrapPause from "mousetrap-pause";
import { ErrorBoundary } from "./components/ErrorBoundary";
Expand Down Expand Up @@ -126,7 +127,18 @@ export const App: React.FC = () => {
}
);

setMessages(flattenMessages(mergedMessages));
const newMessages = flattenMessages(mergedMessages) as Record<
string,
string
>;

yup.setLocale({
mixed: {
required: newMessages["validation.required"],
},
});

setMessages(newMessages);
};

setLocale();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { faSyncAlt } from "@fortawesome/free-solid-svg-icons";
import { galleryTitle } from "src/core/galleries";
import { useRatingKeybinds } from "src/hooks/keybinds";
import { ConfigurationContext } from "src/hooks/Config";
import isEqual from "lodash-es/isEqual";

interface IProps {
gallery: Partial<GQL.GalleryDataFragment>;
Expand Down Expand Up @@ -79,44 +80,50 @@ export const GalleryEditPanel: React.FC<IProps> = ({
isNew || (gallery?.files?.length === 0 && !gallery?.folder);

const schema = yup.object({
title: titleRequired
? yup.string().required()
: yup.string().optional().nullable(),
details: yup.string().optional().nullable(),
url: yup.string().optional().nullable(),
date: yup.string().optional().nullable(),
rating100: yup.number().optional().nullable(),
studio_id: yup.string().optional().nullable(),
performer_ids: yup.array(yup.string().required()).optional().nullable(),
tag_ids: yup.array(yup.string().required()).optional().nullable(),
scene_ids: yup.array(yup.string().required()).optional().nullable(),
title: titleRequired ? yup.string().required() : yup.string().ensure(),
url: yup.string().ensure(),
date: yup
.string()
.ensure()
.test({
name: "date",
test: (value) => {
if (!value) return true;
if (!value.match(/^\d{4}-\d{2}-\d{2}$/)) return false;
if (Number.isNaN(Date.parse(value))) return false;
return true;
},
message: intl.formatMessage({ id: "validation.date_invalid_form" }),
}),
rating100: yup.number().nullable().defined(),
studio_id: yup.string().required().nullable(),
performer_ids: yup.array(yup.string().required()).defined(),
tag_ids: yup.array(yup.string().required()).defined(),
scene_ids: yup.array(yup.string().required()).defined(),
details: yup.string().ensure(),
});

const initialValues = {
title: gallery?.title ?? "",
details: gallery?.details ?? "",
url: gallery?.url ?? "",
date: gallery?.date ?? "",
rating100: gallery?.rating100 ?? null,
studio_id: gallery?.studio?.id,
studio_id: gallery?.studio?.id ?? null,
performer_ids: (gallery?.performers ?? []).map((p) => p.id),
tag_ids: (gallery?.tags ?? []).map((t) => t.id),
scene_ids: (gallery?.scenes ?? []).map((s) => s.id),
details: gallery?.details ?? "",
};

type InputValues = typeof initialValues;
type InputValues = yup.InferType<typeof schema>;

const formik = useFormik({
const formik = useFormik<InputValues>({
initialValues,
enableReinitialize: true,
validationSchema: schema,
onSubmit: (values) => onSave(getGalleryInput(values)),
onSubmit: (values) => onSave(values),
});

// always dirty if creating a new gallery with a title
if (isNew && gallery?.title) {
formik.dirty = true;
}

function setRating(v: number) {
formik.setFieldValue("rating100", v);
}
Expand Down Expand Up @@ -166,24 +173,13 @@ export const GalleryEditPanel: React.FC<IProps> = ({
setQueryableScrapers(newQueryableScrapers);
}, [Scrapers]);

function getGalleryInput(
input: InputValues
): GQL.GalleryCreateInput | GQL.GalleryUpdateInput {
return {
id: isNew ? undefined : gallery?.id ?? "",
...input,
};
}

async function onSave(
input: GQL.GalleryCreateInput | GQL.GalleryUpdateInput
) {
async function onSave(input: GQL.GalleryCreateInput) {
setIsLoading(true);
try {
if (isNew) {
const result = await createGallery({
variables: {
input: input as GQL.GalleryCreateInput,
input,
},
});
if (result.data?.galleryCreate) {
Expand All @@ -202,7 +198,10 @@ export const GalleryEditPanel: React.FC<IProps> = ({
} else {
const result = await updateGallery({
variables: {
input: input as GQL.GalleryUpdateInput,
input: {
id: gallery.id!,
...input,
},
},
});
if (result.data?.galleryUpdate) {
Expand All @@ -216,7 +215,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({
}
),
});
formik.resetForm({ values: formik.values });
formik.resetForm();
}
}
} catch (e) {
Expand Down Expand Up @@ -271,7 +270,10 @@ export const GalleryEditPanel: React.FC<IProps> = ({
return;
}

const currentGallery = getGalleryInput(formik.values);
const currentGallery = {
id: gallery.id!,
...formik.values,
};

return (
<GalleryScrapeDialog
Expand Down Expand Up @@ -384,7 +386,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({

function renderTextField(field: string, title: string, placeholder?: string) {
return (
<Form.Group controlId={title} as={Row}>
<Form.Group controlId={field} as={Row}>
{FormUtils.renderLabel({
title,
})}
Expand Down Expand Up @@ -419,7 +421,9 @@ export const GalleryEditPanel: React.FC<IProps> = ({
<Button
className="edit-button"
variant="primary"
disabled={!formik.dirty}
disabled={
(!isNew && !formik.dirty) || !isEqual(formik.errors, {})
}
onClick={() => formik.submitForm()}
>
<FormattedMessage id="actions.save" />
Expand Down Expand Up @@ -561,8 +565,8 @@ export const GalleryEditPanel: React.FC<IProps> = ({
<Form.Control
as="textarea"
className="gallery-description text-input"
onChange={(newValue: React.ChangeEvent<HTMLTextAreaElement>) =>
formik.setFieldValue("details", newValue.currentTarget.value)
onChange={(e) =>
formik.setFieldValue("details", e.currentTarget.value)
}
value={formik.values.details}
/>
Expand Down
58 changes: 34 additions & 24 deletions ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Prompt } from "react-router-dom";
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
import { useRatingKeybinds } from "src/hooks/keybinds";
import { ConfigurationContext } from "src/hooks/Config";
import isEqual from "lodash-es/isEqual";

interface IProps {
image: GQL.ImageDataFragment;
Expand All @@ -42,31 +43,44 @@ export const ImageEditPanel: React.FC<IProps> = ({
const [updateImage] = useImageUpdate();

const schema = yup.object({
title: yup.string().optional().nullable(),
rating100: yup.number().optional().nullable(),
url: yup.string().optional().nullable(),
date: yup.string().optional().nullable(),
studio_id: yup.string().optional().nullable(),
performer_ids: yup.array(yup.string().required()).optional().nullable(),
tag_ids: yup.array(yup.string().required()).optional().nullable(),
title: yup.string().ensure(),
url: yup.string().ensure(),
date: yup
.string()
.ensure()
.test({
name: "date",
test: (value) => {
if (!value) return true;
if (!value.match(/^\d{4}-\d{2}-\d{2}$/)) return false;
if (Number.isNaN(Date.parse(value))) return false;
return true;
},
message: intl.formatMessage({ id: "validation.date_invalid_form" }),
}),
rating100: yup.number().nullable().defined(),
studio_id: yup.string().required().nullable(),
performer_ids: yup.array(yup.string().required()).defined(),
tag_ids: yup.array(yup.string().required()).defined(),
});

const initialValues = {
title: image.title ?? "",
rating100: image.rating100 ?? null,
url: image?.url ?? "",
date: image?.date ?? "",
studio_id: image.studio?.id,
rating100: image.rating100 ?? null,
studio_id: image.studio?.id ?? null,
performer_ids: (image.performers ?? []).map((p) => p.id),
tag_ids: (image.tags ?? []).map((t) => t.id),
};

type InputValues = typeof initialValues;
type InputValues = yup.InferType<typeof schema>;

const formik = useFormik({
const formik = useFormik<InputValues>({
initialValues,
enableReinitialize: true,
validationSchema: schema,
onSubmit: (values) => onSave(getImageInput(values)),
onSubmit: (values) => onSave(values),
});

function setRating(v: number) {
Expand Down Expand Up @@ -95,19 +109,15 @@ export const ImageEditPanel: React.FC<IProps> = ({
}
});

function getImageInput(input: InputValues): GQL.ImageUpdateInput {
return {
id: image.id,
...input,
};
}

async function onSave(input: GQL.ImageUpdateInput) {
async function onSave(input: InputValues) {
setIsLoading(true);
try {
const result = await updateImage({
variables: {
input,
input: {
id: image.id,
...input,
},
},
});
if (result.data?.imageUpdate) {
Expand All @@ -117,7 +127,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
{ entity: intl.formatMessage({ id: "image" }).toLocaleLowerCase() }
),
});
formik.resetForm({ values: formik.values });
formik.resetForm();
}
} catch (e) {
Toast.error(e);
Expand All @@ -127,7 +137,7 @@ export const ImageEditPanel: React.FC<IProps> = ({

function renderTextField(field: string, title: string, placeholder?: string) {
return (
<Form.Group controlId={title} as={Row}>
<Form.Group controlId={field} as={Row}>
{FormUtils.renderLabel({
title,
})}
Expand Down Expand Up @@ -161,7 +171,7 @@ export const ImageEditPanel: React.FC<IProps> = ({
<Button
className="edit-button"
variant="primary"
disabled={!formik.dirty}
disabled={!formik.dirty || !isEqual(formik.errors, {})}
onClick={() => formik.submitForm()}
>
<FormattedMessage id="actions.save" />
Expand Down
32 changes: 8 additions & 24 deletions ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,8 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);

// Editing movie state
const [frontImage, setFrontImage] = useState<string | undefined | null>(
undefined
);
const [backImage, setBackImage] = useState<string | undefined | null>(
undefined
);
const [frontImage, setFrontImage] = useState<string | null>();
const [backImage, setBackImage] = useState<string | null>();
const [encodingImage, setEncodingImage] = useState<boolean>(false);

const [updateMovie, { loading: updating }] = useMovieUpdate();
Expand All @@ -59,26 +55,14 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
};
});

const onImageEncoding = (isEncoding = false) => setEncodingImage(isEncoding);

function getMovieInput(
input: Partial<GQL.MovieCreateInput | GQL.MovieUpdateInput>
) {
const ret: Partial<GQL.MovieCreateInput | GQL.MovieUpdateInput> = {
...input,
id: movie.id,
};

return ret;
}

async function onSave(
input: Partial<GQL.MovieCreateInput | GQL.MovieUpdateInput>
) {
async function onSave(input: GQL.MovieCreateInput) {
try {
const result = await updateMovie({
variables: {
input: getMovieInput(input) as GQL.MovieUpdateInput,
input: {
id: movie.id,
...input,
},
},
});
if (result.data?.movieUpdate) {
Expand Down Expand Up @@ -214,7 +198,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
onDelete={onDelete}
setFrontImage={setFrontImage}
setBackImage={setBackImage}
onImageEncoding={onImageEncoding}
setEncodingImage={setEncodingImage}
/>
)}
</div>
Expand Down
Loading

0 comments on commit 9ede271

Please sign in to comment.