From acd7df93296385dd28c6c3c3d9d829316f2445f0 Mon Sep 17 00:00:00 2001 From: gitgiggety Date: Tue, 24 Aug 2021 09:17:08 +0200 Subject: [PATCH 1/2] Rebuild image edit using formik * Prompt on page leave when changes are not saved * Only enables save when changes are made * Wrap in
(not sure if this does anything) --- .../Images/ImageDetails/ImageEditPanel.tsx | 266 +++++++++++------- 1 file changed, 163 insertions(+), 103 deletions(-) diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx index efeae41bc57..ffce99e372d 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx @@ -3,6 +3,7 @@ import { Button, Form, Col, Row } from "react-bootstrap"; import { FormattedMessage, useIntl } from "react-intl"; import Mousetrap from "mousetrap"; import * as GQL from "src/core/generated-graphql"; +import * as yup from "yup"; import { useImageUpdate } from "src/core/StashService"; import { PerformerSelect, @@ -12,6 +13,8 @@ import { } from "src/components/Shared"; import { useToast } from "src/hooks"; import { FormUtils } from "src/utils"; +import { useFormik } from "formik"; +import { Prompt } from "react-router"; import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars"; interface IProps { @@ -27,25 +30,44 @@ export const ImageEditPanel: React.FC = ({ }) => { const intl = useIntl(); const Toast = useToast(); - const [title, setTitle] = useState(image?.title ?? ""); - const [rating, setRating] = useState(image.rating ?? NaN); - const [studioId, setStudioId] = useState( - image.studio?.id ?? undefined - ); - const [performerIds, setPerformerIds] = useState( - image.performers.map((p) => p.id) - ); - const [tagIds, setTagIds] = useState(image.tags.map((t) => t.id)); // Network state const [isLoading, setIsLoading] = useState(false); const [updateImage] = useImageUpdate(); + const schema = yup.object({ + title: yup.string().optional().nullable(), + rating: 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(), + }); + + const initialValues = { + title: image.title ?? "", + rating: image.rating ?? null, + studio_id: image.studio?.id, + performer_ids: (image.performers ?? []).map((p) => p.id), + tag_ids: (image.tags ?? []).map((t) => t.id), + }; + + type InputValues = typeof initialValues; + + const formik = useFormik({ + initialValues, + validationSchema: schema, + onSubmit: (values) => onSave(getImageInput(values)), + }); + + function setRating(v: number) { + formik.setFieldValue("rating", v); + } + useEffect(() => { if (isVisible) { Mousetrap.bind("s s", () => { - onSave(); + formik.handleSubmit(); }); Mousetrap.bind("d d", () => { onDelete(); @@ -84,23 +106,19 @@ export const ImageEditPanel: React.FC = ({ } }); - function getImageInput(): GQL.ImageUpdateInput { + function getImageInput(input: InputValues): GQL.ImageUpdateInput { return { id: image.id, - title, - rating: rating ?? null, - studio_id: studioId ?? null, - performer_ids: performerIds, - tag_ids: tagIds, + ...input, }; } - async function onSave() { + async function onSave(input: GQL.ImageUpdateInput) { setIsLoading(true); try { const result = await updateImage({ variables: { - input: getImageInput(), + input, }, }); if (result.data?.imageUpdate) { @@ -110,6 +128,7 @@ export const ImageEditPanel: React.FC = ({ { entity: intl.formatMessage({ id: "image" }).toLocaleLowerCase() } ), }); + formik.resetForm({ values: formik.values }); } } catch (e) { Toast.error(e); @@ -117,97 +136,138 @@ export const ImageEditPanel: React.FC = ({ setIsLoading(false); } + function renderTextField(field: string, title: string, placeholder?: string) { + return ( + + {FormUtils.renderLabel({ + title, + })} + + + + {formik.getFieldMeta(field).error} + + + + ); + } + if (isLoading) return ; return (
-
-
- - + + + +
+
+ + +
-
-
-
- {FormUtils.renderInputGroup({ - title: intl.formatMessage({ id: "title" }), - value: title, - onChange: setTitle, - isEditing: true, - })} - - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "rating" }), - })} - - setRating(value ?? NaN)} - /> - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "studio" }), - })} - - - setStudioId(items.length > 0 ? items[0]?.id : undefined) - } - ids={studioId ? [studioId] : []} - /> - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "performers" }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - - setPerformerIds(items.map((item) => item.id)) - } - ids={performerIds} - /> - - - - - {FormUtils.renderLabel({ - title: intl.formatMessage({ id: "tags" }), - labelProps: { - column: true, - sm: 3, - xl: 12, - }, - })} - - setTagIds(items.map((item) => item.id))} - ids={tagIds} - /> - - +
+
+ {renderTextField("title", intl.formatMessage({ id: "title" }))} + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "rating" }), + })} + + + formik.setFieldValue("rating", value ?? null) + } + /> + + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "studio" }), + })} + + + formik.setFieldValue( + "studio_id", + items.length > 0 ? items[0]?.id : null + ) + } + ids={formik.values.studio_id ? [formik.values.studio_id] : []} + /> + + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "performers" }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + + formik.setFieldValue( + "performer_ids", + items.map((item) => item.id) + ) + } + ids={formik.values.performer_ids} + /> + + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "tags" }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + + formik.setFieldValue( + "tag_ids", + items.map((item) => item.id) + ) + } + ids={formik.values.tag_ids} + /> + + +
-
+
); }; From c104850ed7538a6c7909f337789222b832e4483b Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Thu, 26 Aug 2021 10:57:41 +1000 Subject: [PATCH 2/2] Add changelog entry --- ui/v2.5/src/components/Changelog/versions/v090.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/v2.5/src/components/Changelog/versions/v090.md b/ui/v2.5/src/components/Changelog/versions/v090.md index b84aba52cd2..62219fc9171 100644 --- a/ui/v2.5/src/components/Changelog/versions/v090.md +++ b/ui/v2.5/src/components/Changelog/versions/v090.md @@ -13,7 +13,7 @@ ### 🎨 Improvements * Move Play Selected Scenes, and Add/Remove Gallery Image buttons to button toolbar. ([#1673](https://github.com/stashapp/stash/pull/1673)) * Add image and gallery counts to tag list view. ([#1672](https://github.com/stashapp/stash/pull/1672)) -* Prompt when leaving gallery edit page with unsaved changes. ([#1654](https://github.com/stashapp/stash/pull/1654)) +* Prompt when leaving gallery and image edit pages with unsaved changes. ([#1654](https://github.com/stashapp/stash/pull/1654), [#1669](https://github.com/stashapp/stash/pull/1669)) * Show largest duplicates first in scene duplicate checker. ([#1639](https://github.com/stashapp/stash/pull/1639)) * Added checkboxes to scene list view. ([#1642](https://github.com/stashapp/stash/pull/1642)) * Added keyboard shortcuts for scene queue navigation. ([#1635](https://github.com/stashapp/stash/pull/1635))