Refactor and fix stashbox submit dialog (#4355)

This commit is contained in:
DingDongSoLong4 2023-12-12 02:45:52 +02:00 committed by GitHub
parent d37de0e49b
commit 74ddfa47e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 179 additions and 128 deletions

View file

@ -1,101 +1,131 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useMutation, DocumentNode } from "@apollo/client"; import { Form } from "react-bootstrap";
import { Button, Form } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql"; import * as GQL from "src/core/generated-graphql";
import {
mutateSubmitStashBoxPerformerDraft,
mutateSubmitStashBoxSceneDraft,
} from "src/core/StashService";
import { ModalComponent } from "src/components/Shared/Modal"; import { ModalComponent } from "src/components/Shared/Modal";
import { getStashboxBase } from "src/utils/stashbox"; import { getStashboxBase } from "src/utils/stashbox";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { faPaperPlane } from "@fortawesome/free-solid-svg-icons"; import { faPaperPlane } from "@fortawesome/free-solid-svg-icons";
interface IProps { interface IProps {
show: boolean; type: "scene" | "performer";
entity: { entity: Pick<
name?: string | null; GQL.SceneDataFragment | GQL.PerformerDataFragment,
id: string; "id" | "stash_ids"
title?: string | null; >;
stash_ids: { stash_id: string; endpoint: string }[];
};
boxes: Pick<GQL.StashBox, "name" | "endpoint">[]; boxes: Pick<GQL.StashBox, "name" | "endpoint">[];
query: DocumentNode; show: boolean;
onHide: () => void; onHide: () => void;
} }
type Variables =
| GQL.SubmitStashBoxSceneDraftMutationVariables
| GQL.SubmitStashBoxPerformerDraftMutationVariables;
type Query =
| GQL.SubmitStashBoxSceneDraftMutation
| GQL.SubmitStashBoxPerformerDraftMutation;
const isSceneDraft = (
query: Query | null
): query is GQL.SubmitStashBoxSceneDraftMutation =>
(query as GQL.SubmitStashBoxSceneDraftMutation).submitStashBoxSceneDraft !==
undefined;
const getResponseId = (query: Query | null) =>
isSceneDraft(query)
? query.submitStashBoxSceneDraft
: query?.submitStashBoxPerformerDraft;
export const SubmitStashBoxDraft: React.FC<IProps> = ({ export const SubmitStashBoxDraft: React.FC<IProps> = ({
show, type,
boxes, boxes,
entity, entity,
query, show,
onHide, onHide,
}) => { }) => {
const [submit, { data, error, loading }] = useMutation<Query, Variables>(
query
);
const [selectedBoxIndex, setSelectedBoxIndex] = useState(0);
const intl = useIntl(); const intl = useIntl();
const handleSubmit = () => { const [selectedBoxIndex, setSelectedBoxIndex] = useState(0);
submit({ const [loading, setLoading] = useState(false);
variables: { const [error, setError] = useState<string>();
input: { const [reviewUrl, setReviewUrl] = useState<string>();
// this can be undefined, if e.g. boxes is empty
// since we aren't using noUncheckedIndexedAccess, add undefined explicitly
const selectedBox: (typeof boxes)[number] | undefined =
boxes[selectedBoxIndex];
// #4354: reset state when shown, or if any props change
useEffect(() => {
if (show) {
setSelectedBoxIndex(0);
setLoading(false);
setError(undefined);
setReviewUrl(undefined);
}
}, [show, type, boxes, entity]);
async function doSubmit() {
if (type === "scene") {
const r = await mutateSubmitStashBoxSceneDraft({
id: entity.id, id: entity.id,
stash_box_index: selectedBoxIndex, stash_box_index: selectedBoxIndex,
},
},
}); });
}; return r.data?.submitStashBoxSceneDraft;
} else if (type === "performer") {
const selectedBox = const r = await mutateSubmitStashBoxPerformerDraft({
boxes.length > selectedBoxIndex ? boxes[selectedBoxIndex] : undefined; id: entity.id,
stash_box_index: selectedBoxIndex,
const handleSelectBox = (e: React.ChangeEvent<HTMLSelectElement>) => });
setSelectedBoxIndex(Number.parseInt(e.currentTarget.value) ?? 0); return r.data?.submitStashBoxPerformerDraft;
}
if (!selectedBox) {
return <></>;
} }
// If the scene has an attached stash_id from that endpoint, the operation will be an update async function onSubmit() {
const isUpdate = if (!selectedBox) return;
entity.stash_ids.find((id) => id.endpoint === selectedBox.endpoint) !==
undefined;
try {
setLoading(true);
const responseId = await doSubmit();
const stashboxBase = getStashboxBase(selectedBox.endpoint);
if (responseId) {
setReviewUrl(`${stashboxBase}drafts/${responseId}`);
} else {
// if the mutation returned a null id but didn't error, then just link to the drafts page
setReviewUrl(`${stashboxBase}drafts`);
}
} catch (e) {
if (e instanceof Error && e.message) {
setError(e.message);
} else {
setError(String(e));
}
} finally {
setLoading(false);
}
}
function renderContents() {
if (error !== undefined) {
return ( return (
<ModalComponent
icon={faPaperPlane}
header={intl.formatMessage({ id: "actions.submit_stash_box" })}
isRunning={loading}
show={show}
accept={{
onClick: onHide,
}}
>
{data === undefined ? (
<> <>
<h6 className="mt-2">
<FormattedMessage id="stashbox.submission_failed" />
</h6>
<div>{error}</div>
</>
);
} else if (reviewUrl !== undefined) {
return (
<>
<h6>
<FormattedMessage id="stashbox.submission_successful" />
</h6>
<div>
<a target="_blank" rel="noreferrer noopener" href={reviewUrl}>
<FormattedMessage
id="stashbox.go_review_draft"
values={{ endpoint_name: selectedBox?.name }}
/>
</a>
</div>
</>
);
} else {
return (
<Form.Group className="form-row align-items-end"> <Form.Group className="form-row align-items-end">
<Form.Label className="col-6"> <Form.Label className="col-6">
<FormattedMessage id="stashbox.selected_stash_box" />: <FormattedMessage id="stashbox.selected_stash_box" />:
</Form.Label> </Form.Label>
<Form.Control <Form.Control
as="select" as="select"
onChange={handleSelectBox} onChange={(e) => setSelectedBoxIndex(Number(e.currentTarget.value))}
value={selectedBoxIndex} value={selectedBoxIndex}
className="col-6 input-control" className="col-6 input-control"
> >
@ -106,54 +136,58 @@ export const SubmitStashBoxDraft: React.FC<IProps> = ({
))} ))}
</Form.Control> </Form.Control>
</Form.Group> </Form.Group>
<div className="text-right"> );
{isUpdate && ( }
<span className="mr-2"> }
function getFooterProps() {
if (error !== undefined || reviewUrl !== undefined) {
return {
accept: {
onClick: () => onHide(),
},
};
}
// If the scene has an attached stash_id from that endpoint, the operation will be an update
const isUpdate =
entity.stash_ids.find((id) => id.endpoint === selectedBox?.endpoint) !==
undefined;
return {
footerButtons: isUpdate && !loading && (
<span className="mr-2 align-middle">
<FormattedMessage <FormattedMessage
id="stashbox.submit_update" id="stashbox.submit_update"
values={{ endpoint_name: boxes[selectedBoxIndex].name }} values={{ endpoint_name: selectedBox?.name }}
/> />
</span> </span>
)} ),
<Button accept: {
onClick={handleSubmit} onClick: () => onSubmit(),
variant={isUpdate ? "primary" : "success"} text: intl.formatMessage({
id: isUpdate ? "actions.submit_update" : "actions.submit",
}),
variant: isUpdate ? "primary" : "success",
},
cancel: {
onClick: () => onHide(),
variant: "secondary",
},
};
}
return (
<ModalComponent
icon={faPaperPlane}
header={intl.formatMessage({ id: "actions.submit_stash_box" })}
isRunning={loading}
show={show}
onHide={onHide}
disabled={!selectedBox}
{...getFooterProps()}
> >
<FormattedMessage {renderContents()}
id={`actions.${isUpdate ? "submit_update" : "submit"}`}
/>{" "}
</Button>
</div>
</>
) : (
<>
<h6>
<FormattedMessage id="stashbox.submission_successful" />
</h6>
<div>
<a
target="_blank"
rel="noreferrer noopener"
href={`${getStashboxBase(
boxes[selectedBoxIndex].endpoint
)}drafts/${getResponseId(data)}`}
>
<FormattedMessage
id="stashbox.go_review_draft"
values={{ endpoint_name: boxes[selectedBoxIndex].name }}
/>
</a>
</div>
</>
)}
{error !== undefined && (
<>
<h6 className="mt-2">
<FormattedMessage id="stashbox.submission_failed" />
</h6>
<div>{error.message}</div>
</>
)}
</ModalComponent> </ModalComponent>
); );
}; };

View file

@ -24,9 +24,9 @@ export const PerformerSubmitButton: React.FC<IPerformerOperationsProps> = ({
<FormattedMessage id="actions.submit_stash_box" /> <FormattedMessage id="actions.submit_stash_box" />
</Button> </Button>
<SubmitStashBoxDraft <SubmitStashBoxDraft
type="performer"
boxes={boxes} boxes={boxes}
entity={performer} entity={performer}
query={GQL.SubmitStashBoxPerformerDraftDocument}
show={showDraftModal} show={showDraftModal}
onHide={() => setShowDraftModal(false)} onHide={() => setShowDraftModal(false)}
/> />

View file

@ -531,9 +531,9 @@ const ScenePage: React.FC<IProps> = ({
</Button> </Button>
</div> </div>
<SubmitStashBoxDraft <SubmitStashBoxDraft
type="scene"
boxes={boxes} boxes={boxes}
entity={scene} entity={scene}
query={GQL.SubmitStashBoxSceneDraftDocument}
show={showDraftModal} show={showDraftModal}
onHide={() => setShowDraftModal(false)} onHide={() => setShowDraftModal(false)}
/> />

View file

@ -1,12 +1,13 @@
import React from "react"; import React from "react";
import { Button, Modal, Spinner, ModalProps } from "react-bootstrap"; import { Button, Modal, Spinner, ModalProps } from "react-bootstrap";
import { ButtonVariant } from "react-bootstrap/types";
import { Icon } from "./Icon"; import { Icon } from "./Icon";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
interface IButton { interface IButton {
text?: string; text?: string;
variant?: "danger" | "primary" | "secondary"; variant?: ButtonVariant;
onClick?: () => void; onClick?: () => void;
} }

View file

@ -1945,6 +1945,22 @@ export const queryScrapeGalleryURL = (url: string) =>
fetchPolicy: "network-only", fetchPolicy: "network-only",
}); });
export const mutateSubmitStashBoxSceneDraft = (
input: GQL.StashBoxDraftSubmissionInput
) =>
client.mutate<GQL.SubmitStashBoxSceneDraftMutation>({
mutation: GQL.SubmitStashBoxSceneDraftDocument,
variables: { input },
});
export const mutateSubmitStashBoxPerformerDraft = (
input: GQL.StashBoxDraftSubmissionInput
) =>
client.mutate<GQL.SubmitStashBoxPerformerDraftMutation>({
mutation: GQL.SubmitStashBoxPerformerDraftDocument,
variables: { input },
});
/// Packages /// Packages
export const useInstalledScraperPackages = GQL.useInstalledScraperPackagesQuery; export const useInstalledScraperPackages = GQL.useInstalledScraperPackagesQuery;
export const useInstalledScraperPackagesStatus = export const useInstalledScraperPackagesStatus =