mirror of
https://github.com/stashapp/stash.git
synced 2026-03-26 07:01:54 +01:00
Add stash ids to performer merge dialog (#6688)
* Move reused functions/components to separate files * Add alwaysShow field to ScrapedDialogRow * Add stash ids to performer merge dialog * Reuse StashIDsField in TagMergeDialog * Always show stash ids when available on scene and tag merge dialogs
This commit is contained in:
parent
f3c8e7ac9c
commit
b2179cd723
6 changed files with 79 additions and 40 deletions
|
|
@ -20,13 +20,14 @@ import { faExchangeAlt, faSignInAlt } from "@fortawesome/free-solid-svg-icons";
|
|||
import { ScrapeDialog } from "../Shared/ScrapeDialog/ScrapeDialog";
|
||||
import {
|
||||
ScrapedCustomFieldRows,
|
||||
ScrapeDialogRow,
|
||||
ScrapedImageRow,
|
||||
ScrapedInputGroupRow,
|
||||
ScrapedStringListRow,
|
||||
ScrapedTextAreaRow,
|
||||
} from "../Shared/ScrapeDialog/ScrapeDialogRow";
|
||||
import { ModalComponent } from "../Shared/Modal";
|
||||
import { sortStoredIdObjects } from "src/utils/data";
|
||||
import { sortStoredIdObjects, uniqIDStoredIDs } from "src/utils/data";
|
||||
import {
|
||||
CustomFieldScrapeResults,
|
||||
ObjectListScrapeResult,
|
||||
|
|
@ -40,6 +41,7 @@ import {
|
|||
} from "./PerformerDetails/PerformerScrapeDialog";
|
||||
import { PerformerSelect } from "./PerformerSelect";
|
||||
import { uniq } from "lodash-es";
|
||||
import { StashIDsField } from "../Shared/StashID";
|
||||
|
||||
type MergeOptions = {
|
||||
values: GQL.PerformerUpdateInput;
|
||||
|
|
@ -132,6 +134,8 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
)
|
||||
);
|
||||
|
||||
const [stashIDs, setStashIDs] = useState(new ScrapeResult<GQL.StashId[]>([]));
|
||||
|
||||
const [image, setImage] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(dest.image_path)
|
||||
);
|
||||
|
|
@ -166,6 +170,10 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
setLoading(false);
|
||||
}
|
||||
|
||||
// append dest to all so that if dest has stash_ids with the same
|
||||
// endpoint, then it will be excluded first
|
||||
const all = sources.concat(dest);
|
||||
|
||||
setName(
|
||||
new ScrapeResult(dest.name, sources.find((s) => s.name)?.name, !dest.name)
|
||||
);
|
||||
|
|
@ -297,9 +305,8 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
);
|
||||
setURLs(
|
||||
new ScrapeResult(
|
||||
dest.urls,
|
||||
sources.find((s) => s.urls)?.urls,
|
||||
!dest.urls?.length
|
||||
dest.urls ?? [],
|
||||
uniq(all.map((s) => s.urls ?? []).flat())
|
||||
)
|
||||
);
|
||||
setGender(
|
||||
|
|
@ -327,6 +334,25 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
!dest.details
|
||||
)
|
||||
);
|
||||
setTags(
|
||||
new ObjectListScrapeResult<GQL.ScrapedTag>(
|
||||
sortStoredIdObjects(dest.tags.map(idToStoredID)),
|
||||
uniqIDStoredIDs(all.map((s) => s.tags.map(idToStoredID)).flat())
|
||||
)
|
||||
);
|
||||
setStashIDs(
|
||||
new ScrapeResult(
|
||||
dest.stash_ids,
|
||||
all
|
||||
.map((s) => s.stash_ids)
|
||||
.flat()
|
||||
.filter((s, index, a) => {
|
||||
// remove entries with duplicate endpoints
|
||||
return index === a.findIndex((ss) => ss.endpoint === s.endpoint);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
setImage(
|
||||
new ScrapeResult(
|
||||
dest.image_path,
|
||||
|
|
@ -583,6 +609,19 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
result={details}
|
||||
onChange={(value) => setDetails(value)}
|
||||
/>
|
||||
<ScrapeDialogRow
|
||||
field="stash_ids"
|
||||
title={intl.formatMessage({ id: "stash_id" })}
|
||||
result={stashIDs}
|
||||
originalField={
|
||||
<StashIDsField values={stashIDs?.originalValue ?? []} />
|
||||
}
|
||||
newField={<StashIDsField values={stashIDs?.newValue ?? []} />}
|
||||
onChange={(value) => setStashIDs(value)}
|
||||
alwaysShow={
|
||||
!!stashIDs.originalValue?.length || !!stashIDs.newValue?.length
|
||||
}
|
||||
/>
|
||||
<ScrapedImageRow
|
||||
field="image"
|
||||
title={intl.formatMessage({ id: "performer_image" })}
|
||||
|
|
@ -639,6 +678,7 @@ const PerformerMergeDetails: React.FC<IPerformerMergeDetailsProps> = ({
|
|||
circumcised: stringToCircumcised(circumcised.getNewValue()),
|
||||
tag_ids: tags.getNewValue()?.map((t) => t.stored_id!),
|
||||
details: details.getNewValue(),
|
||||
stash_ids: stashIDs.getNewValue(),
|
||||
image: coverImage,
|
||||
custom_fields: {
|
||||
partial: Object.fromEntries(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { ScrapeDialog } from "../Shared/ScrapeDialog/ScrapeDialog";
|
|||
import { clone, uniq } from "lodash-es";
|
||||
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||
import { ModalComponent } from "../Shared/Modal";
|
||||
import { IHasStoredID, sortStoredIdObjects } from "src/utils/data";
|
||||
import { sortStoredIdObjects, uniqIDStoredIDs } from "src/utils/data";
|
||||
import {
|
||||
CustomFieldScrapeResults,
|
||||
ObjectListScrapeResult,
|
||||
|
|
@ -41,25 +41,7 @@ import {
|
|||
ScrapedTagsRow,
|
||||
} from "../Shared/ScrapeDialog/ScrapedObjectsRow";
|
||||
import { Scene, SceneSelect } from "src/components/Scenes/SceneSelect";
|
||||
import { StashIDPill } from "src/components/Shared/StashID";
|
||||
|
||||
interface IStashIDsField {
|
||||
values: GQL.StashId[];
|
||||
}
|
||||
|
||||
const StashIDsField: React.FC<IStashIDsField> = ({ values }) => {
|
||||
if (!values.length) return null;
|
||||
|
||||
return (
|
||||
<ul className="pl-0 mw-100">
|
||||
{values.map((v) => (
|
||||
<li key={v.stash_id} className="row no-gutters">
|
||||
<StashIDPill linkType="scenes" stashID={v} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
import { StashIDsField } from "../Shared/StashID";
|
||||
|
||||
type MergeOptions = {
|
||||
values: GQL.SceneUpdateInput;
|
||||
|
|
@ -143,12 +125,6 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
return ret;
|
||||
}
|
||||
|
||||
function uniqIDStoredIDs<T extends IHasStoredID>(objs: T[]) {
|
||||
return objs.filter((o, i) => {
|
||||
return objs.findIndex((oo) => oo.stored_id === o.stored_id) === i;
|
||||
});
|
||||
}
|
||||
|
||||
const [performers, setPerformers] = useState<
|
||||
ObjectListScrapeResult<GQL.ScrapedPerformer>
|
||||
>(
|
||||
|
|
@ -615,6 +591,9 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
}
|
||||
newField={<StashIDsField values={stashIDs?.newValue ?? []} />}
|
||||
onChange={(value) => setStashIDs(value)}
|
||||
alwaysShow={
|
||||
!!stashIDs.originalValue?.length || !!stashIDs.newValue?.length
|
||||
}
|
||||
/>
|
||||
<ScrapedImageRow
|
||||
field="cover_image"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ interface IScrapedRowProps<T> extends IScrapedFieldProps<T> {
|
|||
newField: React.ReactNode;
|
||||
onChange: (value: ScrapeResult<T>) => void;
|
||||
newValues?: React.ReactNode;
|
||||
alwaysShow?: boolean;
|
||||
}
|
||||
|
||||
export const ScrapeDialogRow = <T,>(props: IScrapedRowProps<T>) => {
|
||||
|
|
@ -51,7 +52,7 @@ export const ScrapeDialogRow = <T,>(props: IScrapedRowProps<T>) => {
|
|||
props.onChange(ret);
|
||||
}
|
||||
|
||||
if (!props.result.scraped && !props.newValues) {
|
||||
if (!props.result.scraped && !props.newValues && !props.alwaysShow) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,3 +31,21 @@ export const StashIDPill: React.FC<{
|
|||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
interface IStashIDsField {
|
||||
values: StashId[];
|
||||
}
|
||||
|
||||
export const StashIDsField: React.FC<IStashIDsField> = ({ values }) => {
|
||||
if (!values.length) return null;
|
||||
|
||||
return (
|
||||
<ul className="pl-0 mw-100">
|
||||
{values.map((v) => (
|
||||
<li key={v.stash_id} className="row no-gutters">
|
||||
<StashIDPill linkType="scenes" stashID={v} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,16 +28,8 @@ import {
|
|||
ScrapedTextAreaRow,
|
||||
} from "../Shared/ScrapeDialog/ScrapeDialogRow";
|
||||
import { ScrapedTagsRow } from "../Shared/ScrapeDialog/ScrapedObjectsRow";
|
||||
import { StringListSelect } from "../Shared/Select";
|
||||
import { ScrapeDialog } from "../Shared/ScrapeDialog/ScrapeDialog";
|
||||
|
||||
interface IStashIDsField {
|
||||
values: GQL.StashId[];
|
||||
}
|
||||
|
||||
const StashIDsField: React.FC<IStashIDsField> = ({ values }) => {
|
||||
return <StringListSelect value={values.map((v) => v.stash_id)} />;
|
||||
};
|
||||
import { StashIDsField } from "../Shared/StashID";
|
||||
|
||||
interface ITagMergeDetailsProps {
|
||||
sources: GQL.TagDataFragment[];
|
||||
|
|
@ -333,6 +325,9 @@ const TagMergeDetails: React.FC<ITagMergeDetailsProps> = ({
|
|||
}
|
||||
newField={<StashIDsField values={stashIDs?.newValue ?? []} />}
|
||||
onChange={(value) => setStashIDs(value)}
|
||||
alwaysShow={
|
||||
!!stashIDs.originalValue?.length || !!stashIDs.newValue?.length
|
||||
}
|
||||
/>
|
||||
<ScrapedImageRow
|
||||
field="image"
|
||||
|
|
|
|||
|
|
@ -70,3 +70,9 @@ export function sortStoredIdObjects<T extends IHasStoredID>(
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function uniqIDStoredIDs<T extends IHasStoredID>(objs: T[]) {
|
||||
return objs.filter((o, i) => {
|
||||
return objs.findIndex((oo) => oo.stored_id === o.stored_id) === i;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue