Fix tag selector in scrape dialogs (#4526)

This commit is contained in:
WithoutPants
2024-02-06 10:26:16 +11:00
committed by GitHub
parent 3ea31aeb76
commit 217c02f181
8 changed files with 93 additions and 98 deletions

View File

@@ -252,6 +252,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({
return (
<GalleryScrapeDialog
gallery={currentGallery}
galleryTags={tags}
galleryPerformers={performers}
scraped={scrapedGallery}
onClose={(data) => {

View File

@@ -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<GQL.GalleryUpdateInput>;
galleryTags: Tag[];
galleryPerformers: Performer[];
scraped: GQL.ScrapedGallery;
onClose: (scrapedGallery?: GQL.ScrapedGallery) => void;
}
interface IHasStoredID {
stored_id?: string | null;
}
export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
gallery,
galleryTags,
galleryPerformers,
scraped,
onClose,
@@ -72,44 +70,6 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
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<GQL.ScrapedPerformer>
>(
@@ -127,10 +87,15 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
scraped.performers?.filter((t) => !t.stored_id) ?? []
);
const [tags, setTags] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(
sortIdList(gallery.tag_ids),
mapStoredIdObjects(scraped.tags ?? undefined)
const [tags, setTags] = useState<ObjectListScrapeResult<GQL.ScrapedTag>>(
new ObjectListScrapeResult<GQL.ScrapedTag>(
sortStoredIdObjects(
galleryTags.map((t) => ({
stored_id: t.id,
name: t.name,
}))
),
sortStoredIdObjects(scraped.tags ?? undefined)
)
);
const [newTags, setNewTags] = useState<GQL.ScrapedTag[]>(
@@ -198,12 +163,7 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
}
: undefined,
performers: performers.getNewValue(),
tags: tags.getNewValue()?.map((m) => {
return {
stored_id: m,
name: "",
};
}),
tags: tags.getNewValue(),
details: details.getNewValue(),
};
}

View File

@@ -394,6 +394,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
return (
<SceneScrapeDialog
scene={currentScene}
sceneTags={tags}
scenePerformers={performers}
scraped={scrapedScene}
endpoint={endpoint}

View File

@@ -28,10 +28,12 @@ import {
useCreateScrapedStudio,
useCreateScrapedTag,
} from "src/components/Shared/ScrapeDialog/createObjects";
import { Tag } from "src/components/Tags/TagSelect";
interface ISceneScrapeDialogProps {
scene: Partial<GQL.SceneUpdateInput>;
scenePerformers: Performer[];
sceneTags: Tag[];
scraped: GQL.ScrapedScene;
endpoint?: string;
@@ -41,6 +43,7 @@ interface ISceneScrapeDialogProps {
export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
scene,
scenePerformers,
sceneTags,
scraped,
onClose,
endpoint,
@@ -146,10 +149,15 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
scraped.movies?.filter((t) => !t.stored_id) ?? []
);
const [tags, setTags] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(
sortIdList(scene.tag_ids),
mapStoredIdObjects(scraped.tags ?? undefined)
const [tags, setTags] = useState<ObjectListScrapeResult<GQL.ScrapedTag>>(
new ObjectListScrapeResult<GQL.ScrapedTag>(
sortStoredIdObjects(
sceneTags.map((t) => ({
stored_id: t.id,
name: t.name,
}))
),
sortStoredIdObjects(scraped.tags ?? undefined)
)
);
const [newTags, setNewTags] = useState<GQL.ScrapedTag[]>(
@@ -240,12 +248,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
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(),

View File

@@ -112,7 +112,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
};
}
function uniqIDStoredIDs(objs: IHasStoredID[]) {
function uniqIDStoredIDs<T extends IHasStoredID>(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<ISceneMergeDetailsProps> = ({
new ScrapeResult<string[]>(sortIdList(dest.movies.map((p) => p.movie.id)))
);
const [tags, setTags] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(sortIdList(dest.tags.map((t) => t.id)))
const [tags, setTags] = useState<ObjectListScrapeResult<GQL.ScrapedTag>>(
new ObjectListScrapeResult<GQL.ScrapedTag>(
sortStoredIdObjects(dest.tags.map(idToStoredID))
)
);
const [details, setDetails] = useState<ScrapeResult<string>>(
@@ -210,9 +212,9 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
)
);
setTags(
new ScrapeResult(
dest.tags.map((p) => p.id),
uniq(all.map((s) => s.tags.map((p) => p.id)).flat())
new ObjectListScrapeResult<GQL.ScrapedTag>(
sortStoredIdObjects(dest.tags.map(idToStoredID)),
uniqIDStoredIDs(all.map((s) => s.tags.map(idToStoredID)).flat())
)
);
setDetails(
@@ -592,7 +594,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
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(),

View File

@@ -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<GQL.ScrapedTag, string>
IScrapedObjectRowImpl<GQL.ScrapedTag, GQL.ScrapedTag>
> = ({ title, result, onChange, newObjects, onCreateNew }) => {
function renderScrapedTags(
scrapeResult: ScrapeResult<string[]>,
scrapeResult: ScrapeResult<GQL.ScrapedTag[]>,
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 (
<TagSelect
isMulti
className="form-control react-select"
className="form-control"
isDisabled={!isNew}
onSelect={(items) => {
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 (
<ScrapedObjectsRow<GQL.ScrapedTag, string>
<ScrapedObjectsRow<GQL.ScrapedTag, GQL.ScrapedTag>
title={title}
result={result}
renderObjects={renderScrapedTags}

View File

@@ -69,14 +69,16 @@ export function useCreateScrapedStudio(props: IUseCreateNewStudioProps) {
return useCreateObject("studio", createNewStudio);
}
interface IUseCreateNewPerformerProps {
scrapeResult: ScrapeResult<GQL.ScrapedPerformer[]>;
setScrapeResult: (scrapeResult: ScrapeResult<GQL.ScrapedPerformer[]>) => void;
newObjects: GQL.ScrapedPerformer[];
setNewObjects: (newObject: GQL.ScrapedPerformer[]) => void;
interface IUseCreateNewObjectProps<T> {
scrapeResult: ScrapeResult<T[]>;
setScrapeResult: (scrapeResult: ScrapeResult<T[]>) => void;
newObjects: T[];
setNewObjects: (newObject: T[]) => void;
}
export function useCreateScrapedPerformer(props: IUseCreateNewPerformerProps) {
export function useCreateScrapedPerformer(
props: IUseCreateNewObjectProps<GQL.ScrapedPerformer>
) {
const [createPerformer] = usePerformerCreate();
const { scrapeResult, setScrapeResult, newObjects, setNewObjects } = props;
@@ -173,20 +175,39 @@ export function useCreateScrapedMovie(
}
export function useCreateScrapedTag(
props: IUseCreateNewObjectIDListProps<GQL.ScrapedTag>
props: IUseCreateNewObjectProps<GQL.ScrapedTag>
) {
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);
}

View File

@@ -47,9 +47,9 @@ export interface IHasStoredID {
stored_id?: string | null;
}
export function sortStoredIdObjects(
scrapedObjects?: IHasStoredID[]
): IHasStoredID[] | undefined {
export function sortStoredIdObjects<T extends IHasStoredID>(
scrapedObjects?: T[]
): T[] | undefined {
if (!scrapedObjects) {
return undefined;
}