From 5a37e6cf52b2a65f849c601cf52d4ada2d744276 Mon Sep 17 00:00:00 2001 From: InfiniteTF Date: Mon, 10 May 2021 01:33:08 +0200 Subject: [PATCH] Add search modal for stash-box performer scraper (#1373) * Cache tagger fingerprint lookups between renders * Show search modal for stash-box performer scraper --- .../PerformerDetails/PerformerEditPanel.tsx | 65 +++++++------- .../PerformerDetails/PerformerScrapeModal.tsx | 10 ++- .../PerformerStashBoxModal.tsx | 84 +++++++++++++++++++ ui/v2.5/src/components/Tagger/Tagger.tsx | 6 +- 4 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index f100b3b1c..c4eb9fd80 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -24,7 +24,6 @@ import { useTagCreate, queryScrapePerformerURL, useConfiguration, - queryStashBoxPerformer, } from "src/core/StashService"; import { Icon, @@ -42,6 +41,11 @@ import { useFormik } from "formik"; import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars"; import { PerformerScrapeDialog } from "./PerformerScrapeDialog"; import PerformerScrapeModal from "./PerformerScrapeModal"; +import PerformerStashBoxModal, { IStashBox } from "./PerformerStashBoxModal"; + +const isScraper = ( + scraper: GQL.Scraper | GQL.StashBox +): scraper is GQL.Scraper => (scraper as GQL.Scraper).id !== undefined; interface IPerformerDetails { performer: Partial; @@ -64,7 +68,7 @@ export const PerformerEditPanel: React.FC = ({ const history = useHistory(); // Editing state - const [scraper, setScraper] = useState(); + const [scraper, setScraper] = useState(); const [newTags, setNewTags] = useState(); const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); @@ -495,7 +499,8 @@ export const PerformerEditPanel: React.FC = ({ } async function onScrapePerformer( - selectedPerformer: GQL.ScrapedPerformerDataFragment + selectedPerformer: GQL.ScrapedPerformerDataFragment, + selectedScraper: GQL.Scraper ) { setScraper(undefined); try { @@ -509,7 +514,7 @@ export const PerformerEditPanel: React.FC = ({ ...ret } = selectedPerformer; - const result = await queryScrapePerformer(scraper.id, ret); + const result = await queryScrapePerformer(selectedScraper.id, ret); if (!result?.data?.scrapePerformer) return; // if this is a new performer, just dump the data @@ -548,34 +553,21 @@ export const PerformerEditPanel: React.FC = ({ } } - async function onScrapeStashBoxClicked(stashBoxIndex: number) { - if (!performer.id) return; + async function onScrapeStashBox(performerResult: GQL.ScrapedScenePerformer) { + setScraper(undefined); - setIsLoading(true); - try { - const result = await queryStashBoxPerformer(stashBoxIndex, performer.id); - if (!result.data || !result.data.queryStashBoxPerformer) { - return; - } + const result: Partial = { + ...performerResult, + image: performerResult.images?.[0] ?? undefined, + country: getCountryByISO(performerResult.country), + __typename: "ScrapedPerformer", + }; - if (result.data.queryStashBoxPerformer.length > 0) { - const performerResult = - result.data.queryStashBoxPerformer[0].results[0]; - setScrapedPerformer({ - ...performerResult, - image: performerResult.images?.[0] ?? undefined, - country: getCountryByISO(performerResult.country), - __typename: "ScrapedPerformer", - }); - } else { - Toast.success({ - content: "No performers found", - }); - } - } catch (e) { - Toast.error(e); - } finally { - setIsLoading(false); + // if this is a new performer, just dump the data + if (isNew) { + updatePerformerEditStateFromScraper(result); + } else { + setScrapedPerformer(result); } } @@ -593,7 +585,7 @@ export const PerformerEditPanel: React.FC = ({
@@ -732,14 +724,21 @@ export const PerformerEditPanel: React.FC = ({ } const renderScrapeModal = () => - scraper !== undefined && ( + scraper !== undefined && isScraper(scraper) ? ( setScraper(undefined)} onSelectPerformer={onScrapePerformer} name={formik.values.name || ""} /> - ); + ) : scraper !== undefined && !isScraper(scraper) ? ( + setScraper(undefined)} + onSelectPerformer={onScrapeStashBox} + name={formik.values.name || ""} + /> + ) : undefined; function renderDeleteAlert() { return ( diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx index e8cfab3b0..fce360a5a 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx @@ -12,7 +12,10 @@ const CLASSNAME_LIST = `${CLASSNAME}-list`; interface IProps { scraper: GQL.Scraper; onHide: () => void; - onSelectPerformer: (performer: GQL.ScrapedPerformerDataFragment) => void; + onSelectPerformer: ( + performer: GQL.ScrapedPerformerDataFragment, + scraper: GQL.Scraper + ) => void; name?: string; } const PerformerScrapeModal: React.FC = ({ @@ -56,7 +59,10 @@ const PerformerScrapeModal: React.FC = ({
    {performers.map((p) => (
  • -
  • diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx new file mode 100644 index 000000000..49139d786 --- /dev/null +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx @@ -0,0 +1,84 @@ +import React, { useEffect, useRef, useState } from "react"; +import { debounce } from "lodash"; +import { Button, Form } from "react-bootstrap"; + +import * as GQL from "src/core/generated-graphql"; +import { Modal, LoadingIndicator } from "src/components/Shared"; + +const CLASSNAME = "PerformerScrapeModal"; +const CLASSNAME_LIST = `${CLASSNAME}-list`; + +export interface IStashBox extends GQL.StashBox { + index: number; +} + +interface IProps { + instance: IStashBox; + onHide: () => void; + onSelectPerformer: (performer: GQL.ScrapedScenePerformer) => void; + name?: string; +} +const PerformerStashBoxModal: React.FC = ({ + instance, + name, + onHide, + onSelectPerformer, +}) => { + const inputRef = useRef(null); + const [query, setQuery] = useState(name ?? ""); + const { data, loading } = GQL.useQueryStashBoxPerformerQuery({ + variables: { + input: { + stash_box_index: instance.index, + q: query, + }, + }, + skip: query === "", + }); + + const performers = data?.queryStashBoxPerformer?.[0].results ?? []; + + const onInputChange = debounce((input: string) => { + setQuery(input); + }, 500); + + useEffect(() => inputRef.current?.focus(), []); + + return ( + +
    + onInputChange(e.currentTarget.value)} + defaultValue={name ?? ""} + placeholder="Performer name..." + className="text-input mb-4" + ref={inputRef} + /> + {loading ? ( +
    + +
    + ) : performers.length > 0 ? ( +
      + {performers.map((p) => ( +
    • + +
    • + ))} +
    + ) : ( + query !== "" &&
    No results found.
    + )} +
    +
    + ); +}; + +export default PerformerStashBoxModal; diff --git a/ui/v2.5/src/components/Tagger/Tagger.tsx b/ui/v2.5/src/components/Tagger/Tagger.tsx index f4b089fc0..b0203ed71 100755 --- a/ui/v2.5/src/components/Tagger/Tagger.tsx +++ b/ui/v2.5/src/components/Tagger/Tagger.tsx @@ -150,6 +150,9 @@ interface ITaggerListProps { clearSubmissionQueue: (endpoint: string) => void; } +// Caches fingerprint lookups between page renders +let fingerprintCache: Record = {}; + const TaggerList: React.FC = ({ scenes, queue, @@ -181,7 +184,7 @@ const TaggerList: React.FC = ({ const [loadingFingerprints, setLoadingFingerprints] = useState(false); const [fingerprints, setFingerprints] = useState< Record - >({}); + >(fingerprintCache); const [hideUnmatched, setHideUnmatched] = useState(false); const fingerprintQueue = config.fingerprintQueue[selectedEndpoint.endpoint] ?? []; @@ -285,6 +288,7 @@ const TaggerList: React.FC = ({ }); setFingerprints(newFingerprints); + fingerprintCache = newFingerprints; setLoadingFingerprints(false); setFingerprintError(""); };