diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx index 16061c3cb..157e7ab4c 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx @@ -252,6 +252,7 @@ export const GalleryEditPanel: React.FC = ({ return ( { diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx index c9c807816..2c01390b3 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx @@ -7,7 +7,6 @@ import { ScrapedStringListRow, ScrapedTextAreaRow, } from "src/components/Shared/ScrapeDialog/ScrapeDialog"; -import clone from "lodash-es/clone"; import { ObjectListScrapeResult, ScrapeResult, @@ -25,21 +24,20 @@ import { useCreateScrapedTag, } from "src/components/Shared/ScrapeDialog/createObjects"; import { uniq } from "lodash-es"; +import { Tag } from "src/components/Tags/TagSelect"; interface IGalleryScrapeDialogProps { gallery: Partial; + galleryTags: Tag[]; galleryPerformers: Performer[]; scraped: GQL.ScrapedGallery; onClose: (scrapedGallery?: GQL.ScrapedGallery) => void; } -interface IHasStoredID { - stored_id?: string | null; -} - export const GalleryScrapeDialog: React.FC = ({ gallery, + galleryTags, galleryPerformers, scraped, onClose, @@ -72,44 +70,6 @@ export const GalleryScrapeDialog: React.FC = ({ scraped.studio && !scraped.studio.stored_id ? 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< ObjectListScrapeResult >( @@ -127,10 +87,15 @@ export const GalleryScrapeDialog: React.FC = ({ scraped.performers?.filter((t) => !t.stored_id) ?? [] ); - const [tags, setTags] = useState>( - new ScrapeResult( - sortIdList(gallery.tag_ids), - mapStoredIdObjects(scraped.tags ?? undefined) + const [tags, setTags] = useState>( + new ObjectListScrapeResult( + sortStoredIdObjects( + galleryTags.map((t) => ({ + stored_id: t.id, + name: t.name, + })) + ), + sortStoredIdObjects(scraped.tags ?? undefined) ) ); const [newTags, setNewTags] = useState( @@ -198,12 +163,7 @@ export const GalleryScrapeDialog: React.FC = ({ } : undefined, performers: performers.getNewValue(), - tags: tags.getNewValue()?.map((m) => { - return { - stored_id: m, - name: "", - }; - }), + tags: tags.getNewValue(), details: details.getNewValue(), }; } diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx index b74712871..9f64d041c 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx @@ -394,6 +394,7 @@ export const SceneEditPanel: React.FC = ({ return ( ; scenePerformers: Performer[]; + sceneTags: Tag[]; scraped: GQL.ScrapedScene; endpoint?: string; @@ -41,6 +43,7 @@ interface ISceneScrapeDialogProps { export const SceneScrapeDialog: React.FC = ({ scene, scenePerformers, + sceneTags, scraped, onClose, endpoint, @@ -146,10 +149,15 @@ export const SceneScrapeDialog: React.FC = ({ scraped.movies?.filter((t) => !t.stored_id) ?? [] ); - const [tags, setTags] = useState>( - new ScrapeResult( - sortIdList(scene.tag_ids), - mapStoredIdObjects(scraped.tags ?? undefined) + const [tags, setTags] = useState>( + new ObjectListScrapeResult( + sortStoredIdObjects( + sceneTags.map((t) => ({ + stored_id: t.id, + name: t.name, + })) + ), + sortStoredIdObjects(scraped.tags ?? undefined) ) ); const [newTags, setNewTags] = useState( @@ -240,12 +248,7 @@ export const SceneScrapeDialog: React.FC = ({ name: "", }; }), - tags: tags.getNewValue()?.map((m) => { - return { - stored_id: m, - name: "", - }; - }), + tags: tags.getNewValue(), details: details.getNewValue(), image: image.getNewValue(), remote_site_id: stashID.getNewValue(), diff --git a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx index e7e9a8a4d..8873fc440 100644 --- a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx @@ -112,7 +112,7 @@ const SceneMergeDetails: React.FC = ({ }; } - function uniqIDStoredIDs(objs: IHasStoredID[]) { + function uniqIDStoredIDs(objs: T[]) { return objs.filter((o, i) => { return objs.findIndex((oo) => oo.stored_id === o.stored_id) === i; }); @@ -130,8 +130,10 @@ const SceneMergeDetails: React.FC = ({ new ScrapeResult(sortIdList(dest.movies.map((p) => p.movie.id))) ); - const [tags, setTags] = useState>( - new ScrapeResult(sortIdList(dest.tags.map((t) => t.id))) + const [tags, setTags] = useState>( + new ObjectListScrapeResult( + sortStoredIdObjects(dest.tags.map(idToStoredID)) + ) ); const [details, setDetails] = useState>( @@ -210,9 +212,9 @@ const SceneMergeDetails: React.FC = ({ ) ); setTags( - new ScrapeResult( - dest.tags.map((p) => p.id), - uniq(all.map((s) => s.tags.map((p) => p.id)).flat()) + new ObjectListScrapeResult( + sortStoredIdObjects(dest.tags.map(idToStoredID)), + uniqIDStoredIDs(all.map((s) => s.tags.map(idToStoredID)).flat()) ) ); setDetails( @@ -592,7 +594,7 @@ const SceneMergeDetails: React.FC = ({ scene_index: found!.scene_index, }; }), - tag_ids: tags.getNewValue(), + tag_ids: tags.getNewValue()?.map((t) => t.stored_id!), details: details.getNewValue(), organized: organized.getNewValue(), stash_ids: stashIDs.getNewValue(), diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx index 43207e94a..f3db0b3b3 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx +++ b/ui/v2.5/src/components/Shared/ScrapeDialog/ScrapedObjectsRow.tsx @@ -1,16 +1,13 @@ import React, { useMemo } from "react"; import * as GQL from "src/core/generated-graphql"; -import { - MovieSelect, - TagSelect, - StudioSelect, -} from "src/components/Shared/Select"; +import { MovieSelect, StudioSelect } from "src/components/Shared/Select"; import { ScrapeDialogRow, IHasName, } from "src/components/Shared/ScrapeDialog/ScrapeDialog"; import { PerformerSelect } from "src/components/Performers/PerformerSelect"; import { ScrapeResult } from "src/components/Shared/ScrapeDialog/scrapeResult"; +import { TagSelect } from "src/components/Tags/TagSelect"; interface IScrapedStudioRow { title: string; @@ -230,35 +227,45 @@ export const ScrapedMoviesRow: React.FC< }; export const ScrapedTagsRow: React.FC< - IScrapedObjectRowImpl + IScrapedObjectRowImpl > = ({ title, result, onChange, newObjects, onCreateNew }) => { function renderScrapedTags( - scrapeResult: ScrapeResult, + scrapeResult: ScrapeResult, isNew?: boolean, - onChangeFn?: (value: string[]) => void + onChangeFn?: (value: GQL.ScrapedTag[]) => void ) { const resultValue = isNew ? scrapeResult.newValue : scrapeResult.originalValue; const value = resultValue ?? []; + const selectValue = value.map((p) => { + const aliases: string[] = []; + return { + id: p.stored_id ?? "", + name: p.name ?? "", + aliases, + }; + }); + return ( { if (onChangeFn) { - onChangeFn(items.map((i) => i.id)); + // map the id back to stored_id + onChangeFn(items.map((p) => ({ ...p, stored_id: p.id }))); } }} - ids={value} + values={selectValue} /> ); } return ( - + title={title} result={result} renderObjects={renderScrapedTags} diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog/createObjects.ts b/ui/v2.5/src/components/Shared/ScrapeDialog/createObjects.ts index 0ce4c23e8..10ac099af 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog/createObjects.ts +++ b/ui/v2.5/src/components/Shared/ScrapeDialog/createObjects.ts @@ -69,14 +69,16 @@ export function useCreateScrapedStudio(props: IUseCreateNewStudioProps) { return useCreateObject("studio", createNewStudio); } -interface IUseCreateNewPerformerProps { - scrapeResult: ScrapeResult; - setScrapeResult: (scrapeResult: ScrapeResult) => void; - newObjects: GQL.ScrapedPerformer[]; - setNewObjects: (newObject: GQL.ScrapedPerformer[]) => void; +interface IUseCreateNewObjectProps { + scrapeResult: ScrapeResult; + setScrapeResult: (scrapeResult: ScrapeResult) => void; + newObjects: T[]; + setNewObjects: (newObject: T[]) => void; } -export function useCreateScrapedPerformer(props: IUseCreateNewPerformerProps) { +export function useCreateScrapedPerformer( + props: IUseCreateNewObjectProps +) { const [createPerformer] = usePerformerCreate(); const { scrapeResult, setScrapeResult, newObjects, setNewObjects } = props; @@ -173,20 +175,39 @@ export function useCreateScrapedMovie( } export function useCreateScrapedTag( - props: IUseCreateNewObjectIDListProps + props: IUseCreateNewObjectProps ) { const [createTag] = useTagCreate(); + const { scrapeResult, setScrapeResult, newObjects, setNewObjects } = props; + async function createNewTag(toCreate: GQL.ScrapedTag) { - const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; + const input: GQL.TagCreateInput = { name: toCreate.name ?? "" }; + const result = await createTag({ - variables: { - input: tagInput, - }, + variables: { input }, }); - return result.data?.tagCreate?.id ?? ""; + const newValue = [...(scrapeResult.newValue ?? [])]; + if (result.data?.tagCreate) + newValue.push({ + stored_id: result.data.tagCreate.id, + name: result.data.tagCreate.name, + }); + + // add the new tag to the new tags value + const tagClone = scrapeResult.cloneWithValue(newValue); + setScrapeResult(tagClone); + + // remove the tag from the list + const newTagsClone = newObjects.concat(); + const pIndex = newTagsClone.findIndex((p) => p.name === toCreate.name); + if (pIndex === -1) throw new Error("Could not find tag to remove"); + + newTagsClone.splice(pIndex, 1); + + setNewObjects(newTagsClone); } - return useCreateNewObjectIDList("tag", props, createNewTag); + return useCreateObject("tag", createNewTag); } diff --git a/ui/v2.5/src/utils/data.ts b/ui/v2.5/src/utils/data.ts index bca1cc0f8..166602552 100644 --- a/ui/v2.5/src/utils/data.ts +++ b/ui/v2.5/src/utils/data.ts @@ -47,9 +47,9 @@ export interface IHasStoredID { stored_id?: string | null; } -export function sortStoredIdObjects( - scrapedObjects?: IHasStoredID[] -): IHasStoredID[] | undefined { +export function sortStoredIdObjects( + scrapedObjects?: T[] +): T[] | undefined { if (!scrapedObjects) { return undefined; }