import React, { useState } from "react"; import { StudioSelect, PerformerSelect } from "src/components/Shared"; import * as GQL from "src/core/generated-graphql"; import { TagSelect } from "src/components/Shared/Select"; import { ScrapeDialog, ScrapeDialogRow, ScrapeResult, ScrapedInputGroupRow, ScrapedTextAreaRow, } from "src/components/Shared/ScrapeDialog"; import _ from "lodash"; import { useStudioCreate, usePerformerCreate, useTagCreate, makePerformerCreateInput, } from "src/core/StashService"; import { useToast } from "src/hooks"; function renderScrapedStudio( result: ScrapeResult, isNew?: boolean, onChange?: (value: string) => void ) { const resultValue = isNew ? result.newValue : result.originalValue; const value = resultValue ? [resultValue] : []; return ( { if (onChange) { onChange(items[0]?.id); } }} ids={value} /> ); } function renderScrapedStudioRow( result: ScrapeResult, onChange: (value: ScrapeResult) => void, newStudio?: GQL.ScrapedSceneStudio, onCreateNew?: (value: GQL.ScrapedSceneStudio) => void ) { return ( renderScrapedStudio(result)} renderNewField={() => renderScrapedStudio(result, true, (value) => onChange(result.cloneWithValue(value)) ) } onChange={onChange} newValues={newStudio ? [newStudio] : undefined} onCreateNew={onCreateNew} /> ); } function renderScrapedPerformers( result: ScrapeResult, isNew?: boolean, onChange?: (value: string[]) => void ) { const resultValue = isNew ? result.newValue : result.originalValue; const value = resultValue ?? []; return ( { if (onChange) { onChange(items.map((i) => i.id)); } }} ids={value} /> ); } function renderScrapedPerformersRow( result: ScrapeResult, onChange: (value: ScrapeResult) => void, newPerformers: GQL.ScrapedScenePerformer[], onCreateNew?: (value: GQL.ScrapedScenePerformer) => void ) { return ( renderScrapedPerformers(result)} renderNewField={() => renderScrapedPerformers(result, true, (value) => onChange(result.cloneWithValue(value)) ) } onChange={onChange} newValues={newPerformers} onCreateNew={onCreateNew} /> ); } function renderScrapedTags( result: ScrapeResult, isNew?: boolean, onChange?: (value: string[]) => void ) { const resultValue = isNew ? result.newValue : result.originalValue; const value = resultValue ?? []; return ( { if (onChange) { onChange(items.map((i) => i.id)); } }} ids={value} /> ); } function renderScrapedTagsRow( result: ScrapeResult, onChange: (value: ScrapeResult) => void, newTags: GQL.ScrapedSceneTag[], onCreateNew?: (value: GQL.ScrapedSceneTag) => void ) { return ( renderScrapedTags(result)} renderNewField={() => renderScrapedTags(result, true, (value) => onChange(result.cloneWithValue(value)) ) } newValues={newTags} onChange={onChange} onCreateNew={onCreateNew} /> ); } interface IGalleryScrapeDialogProps { gallery: Partial; scraped: GQL.ScrapedGallery; onClose: (scrapedGallery?: GQL.ScrapedGallery) => void; } interface IHasStoredID { stored_id?: string | null; } export const GalleryScrapeDialog: React.FC = ( props: IGalleryScrapeDialogProps ) => { const [title, setTitle] = useState>( new ScrapeResult(props.gallery.title, props.scraped.title) ); const [url, setURL] = useState>( new ScrapeResult(props.gallery.url, props.scraped.url) ); const [date, setDate] = useState>( new ScrapeResult(props.gallery.date, props.scraped.date) ); const [studio, setStudio] = useState>( new ScrapeResult( props.gallery.studio_id, props.scraped.studio?.stored_id ) ); const [newStudio, setNewStudio] = useState< GQL.ScrapedSceneStudio | undefined >( props.scraped.studio && !props.scraped.studio.stored_id ? props.scraped.studio : undefined ); function mapStoredIdObjects( scrapedObjects?: IHasStoredID[] ): string[] | undefined { if (!scrapedObjects) { return undefined; } const ret = scrapedObjects .map((p) => p.stored_id) .filter((p) => { return p !== undefined && p !== null; }) as string[]; if (ret.length === 0) { return undefined; } // sort by id numerically ret.sort((a, b) => { return parseInt(a, 10) - parseInt(b, 10); }); return ret; } function sortIdList(idList?: string[] | null) { if (!idList) { return; } const ret = _.clone(idList); // sort by id numerically ret.sort((a, b) => { return parseInt(a, 10) - parseInt(b, 10); }); return ret; } const [performers, setPerformers] = useState>( new ScrapeResult( sortIdList(props.gallery.performer_ids), mapStoredIdObjects(props.scraped.performers ?? undefined) ) ); const [newPerformers, setNewPerformers] = useState< GQL.ScrapedScenePerformer[] >(props.scraped.performers?.filter((t) => !t.stored_id) ?? []); const [tags, setTags] = useState>( new ScrapeResult( sortIdList(props.gallery.tag_ids), mapStoredIdObjects(props.scraped.tags ?? undefined) ) ); const [newTags, setNewTags] = useState( props.scraped.tags?.filter((t) => !t.stored_id) ?? [] ); const [details, setDetails] = useState>( new ScrapeResult(props.gallery.details, props.scraped.details) ); const [createStudio] = useStudioCreate({ name: "" }); const [createPerformer] = usePerformerCreate(); const [createTag] = useTagCreate(); const Toast = useToast(); // don't show the dialog if nothing was scraped if ( [title, url, date, studio, performers, tags, details].every( (r) => !r.scraped ) ) { props.onClose(); return <>; } async function createNewStudio(toCreate: GQL.ScrapedSceneStudio) { try { const result = await createStudio({ variables: { input: { name: toCreate.name, url: toCreate.url, }, }, }); // set the new studio as the value setStudio(studio.cloneWithValue(result.data!.studioCreate!.id)); setNewStudio(undefined); Toast.success({ content: ( Created studio: {toCreate.name} ), }); } catch (e) { Toast.error(e); } } async function createNewPerformer(toCreate: GQL.ScrapedScenePerformer) { const input = makePerformerCreateInput(toCreate); try { const result = await createPerformer({ variables: { input }, }); // add the new performer to the new performers value const performerClone = performers.cloneWithValue(performers.newValue); if (!performerClone.newValue) { performerClone.newValue = []; } performerClone.newValue.push(result.data!.performerCreate!.id); setPerformers(performerClone); // remove the performer from the list const newPerformersClone = newPerformers.concat(); const pIndex = newPerformersClone.indexOf(toCreate); newPerformersClone.splice(pIndex, 1); setNewPerformers(newPerformersClone); Toast.success({ content: ( Created performer: {toCreate.name} ), }); } catch (e) { Toast.error(e); } } async function createNewTag(toCreate: GQL.ScrapedSceneTag) { let tagInput: GQL.TagCreateInput = { name: "" }; try { tagInput = Object.assign(tagInput, toCreate); const result = await createTag({ variables: { input: tagInput, }, }); // add the new tag to the new tags value const tagClone = tags.cloneWithValue(tags.newValue); if (!tagClone.newValue) { tagClone.newValue = []; } tagClone.newValue.push(result.data!.tagCreate!.id); setTags(tagClone); // remove the tag from the list const newTagsClone = newTags.concat(); const pIndex = newTagsClone.indexOf(toCreate); newTagsClone.splice(pIndex, 1); setNewTags(newTagsClone); Toast.success({ content: ( Created tag: {toCreate.name} ), }); } catch (e) { Toast.error(e); } } function makeNewScrapedItem(): GQL.ScrapedGalleryDataFragment { const newStudioValue = studio.getNewValue(); return { title: title.getNewValue(), url: url.getNewValue(), date: date.getNewValue(), studio: newStudioValue ? { stored_id: newStudioValue, name: "", } : undefined, performers: performers.getNewValue()?.map((p) => { return { stored_id: p, name: "", }; }), tags: tags.getNewValue()?.map((m) => { return { stored_id: m, name: "", }; }), details: details.getNewValue(), }; } function renderScrapeRows() { return ( <> setTitle(value)} /> setURL(value)} /> setDate(value)} /> {renderScrapedStudioRow( studio, (value) => setStudio(value), newStudio, createNewStudio )} {renderScrapedPerformersRow( performers, (value) => setPerformers(value), newPerformers, createNewPerformer )} {renderScrapedTagsRow( tags, (value) => setTags(value), newTags, createNewTag )} setDetails(value)} /> ); } return ( { props.onClose(apply ? makeNewScrapedItem() : undefined); }} /> ); };