import React, { useEffect, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import * as yup from "yup"; import Mousetrap from "mousetrap"; import { Icon } from "src/components/Shared/Icon"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { StudioSelect } from "src/components/Shared/Select"; import { DetailsEditNavbar } from "src/components/Shared/DetailsEditNavbar"; import { Button, Form, Col, Row } from "react-bootstrap"; import FormUtils from "src/utils/form"; import ImageUtils from "src/utils/image"; import { getStashIDs } from "src/utils/stashIds"; import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; import { useFormik } from "formik"; import { Prompt } from "react-router-dom"; import { StringListInput } from "../../Shared/StringListInput"; import { faTrashAlt } from "@fortawesome/free-solid-svg-icons"; import { useRatingKeybinds } from "src/hooks/keybinds"; import { ConfigurationContext } from "src/hooks/Config"; import isEqual from "lodash-es/isEqual"; import { useToast } from "src/hooks/Toast"; interface IStudioEditPanel { studio: Partial; onSubmit: (studio: GQL.StudioCreateInput) => Promise; onCancel: () => void; onDelete: () => void; setImage: (image?: string | null) => void; setEncodingImage: (loading: boolean) => void; } export const StudioEditPanel: React.FC = ({ studio, onSubmit, onCancel, onDelete, setImage, setEncodingImage, }) => { const intl = useIntl(); const Toast = useToast(); const isNew = studio.id === undefined; const { configuration } = React.useContext(ConfigurationContext); // Network state const [isLoading, setIsLoading] = useState(false); const schema = yup.object({ name: yup.string().required(), url: yup.string().ensure(), details: yup.string().ensure(), parent_id: yup.string().required().nullable(), rating100: yup.number().nullable().defined(), aliases: yup .array(yup.string().required()) .defined() .test({ name: "unique", test: (value, context) => { const aliases = [context.parent.name, ...value]; const dupes = aliases .map((e, i, a) => { if (a.indexOf(e) !== i) { return String(i - 1); } else { return null; } }) .filter((e) => e !== null) as string[]; if (dupes.length === 0) return true; return new yup.ValidationError(dupes.join(" "), value, "aliases"); }, }), ignore_auto_tag: yup.boolean().defined(), stash_ids: yup.mixed().defined(), image: yup.string().nullable().optional(), }); const initialValues = { id: studio.id, name: studio.name ?? "", url: studio.url ?? "", details: studio.details ?? "", parent_id: studio.parent_studio?.id ?? null, rating100: studio.rating100 ?? null, aliases: studio.aliases ?? [], ignore_auto_tag: studio.ignore_auto_tag ?? false, stash_ids: getStashIDs(studio.stash_ids), }; type InputValues = yup.InferType; const formik = useFormik({ initialValues, enableReinitialize: true, validationSchema: schema, onSubmit: (values) => onSave(values), }); const encodingImage = ImageUtils.usePasteImage((imageData) => formik.setFieldValue("image", imageData) ); useEffect(() => { setImage(formik.values.image); }, [formik.values.image, setImage]); useEffect(() => { setEncodingImage(encodingImage); }, [setEncodingImage, encodingImage]); function setRating(v: number) { formik.setFieldValue("rating100", v); } useRatingKeybinds( true, configuration?.ui?.ratingSystemOptions?.type, setRating ); // set up hotkeys useEffect(() => { Mousetrap.bind("s s", () => { if (formik.dirty) { formik.submitForm(); } }); return () => { Mousetrap.unbind("s s"); }; }); async function onSave(input: InputValues) { setIsLoading(true); try { await onSubmit(input); formik.resetForm(); } catch (e) { Toast.error(e); } setIsLoading(false); } function onImageLoad(imageData: string | null) { formik.setFieldValue("image", imageData); } function onImageChange(event: React.FormEvent) { ImageUtils.onImageChange(event, onImageLoad); } const removeStashID = (stashID: GQL.StashIdInput) => { formik.setFieldValue( "stash_ids", (formik.values.stash_ids ?? []).filter( (s) => !(s.endpoint === stashID.endpoint && s.stash_id === stashID.stash_id) ) ); }; function renderStashIDs() { if (!formik.values.stash_ids?.length) { return; } return ( StashIDs
    {formik.values.stash_ids.map((stashID) => { const base = stashID.endpoint.match(/https?:\/\/.*?\//)?.[0]; const link = base ? ( {stashID.stash_id} ) : ( stashID.stash_id ); return (
  • {link}
  • ); })}
); } const aliasErrors = Array.isArray(formik.errors.aliases) ? formik.errors.aliases[0] : formik.errors.aliases; const aliasErrorMsg = aliasErrors ? intl.formatMessage({ id: "validation.aliases_must_be_unique" }) : undefined; const aliasErrorIdx = aliasErrors?.split(" ").map((e) => parseInt(e)); if (isLoading) return ; return ( <> { // Check if it's a redirect after studio creation if (action === "PUSH" && location.pathname.startsWith("/studios/")) return true; return intl.formatMessage({ id: "dialogs.unsaved_changes" }); }} />
{FormUtils.renderLabel({ title: intl.formatMessage({ id: "name" }), })} {formik.errors.name} {FormUtils.renderLabel({ title: intl.formatMessage({ id: "url" }), })} {formik.errors.url} {FormUtils.renderLabel({ title: intl.formatMessage({ id: "details" }), })} {formik.errors.details} {FormUtils.renderLabel({ title: intl.formatMessage({ id: "parent_studios" }), })} formik.setFieldValue( "parent_id", items.length > 0 ? items[0]?.id : null ) } ids={formik.values.parent_id ? [formik.values.parent_id] : []} excludeIds={studio.id ? [studio.id] : []} /> {FormUtils.renderLabel({ title: intl.formatMessage({ id: "rating" }), })} formik.setFieldValue("rating100", value ?? null) } /> {renderStashIDs()} formik.setFieldValue("aliases", value)} errors={aliasErrorMsg} errorIdx={aliasErrorIdx} />

onImageLoad(null)} onDelete={onDelete} acceptSVG /> ); };