diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx index 9d626bcb4..e1f1a4246 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx @@ -7,7 +7,10 @@ import { ScrapedTextAreaRow, } from "src/components/Shared/ScrapeDialog/ScrapeDialog"; import clone from "lodash-es/clone"; -import { ScrapeResult } from "src/components/Shared/ScrapeDialog/scrapeResult"; +import { + ObjectListScrapeResult, + ScrapeResult, +} from "src/components/Shared/ScrapeDialog/scrapeResult"; import { ScrapedPerformersRow, ScrapedStudioRow, @@ -97,9 +100,9 @@ export const GalleryScrapeDialog: React.FC = ( } const [performers, setPerformers] = useState< - ScrapeResult + ObjectListScrapeResult >( - new ScrapeResult( + new ObjectListScrapeResult( sortStoredIdObjects( props.galleryPerformers.map((p) => ({ stored_id: p.id, diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx index b08dab36b..a0cc2257a 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx @@ -12,7 +12,10 @@ import { useIntl } from "react-intl"; import { uniq } from "lodash-es"; import { Performer } from "src/components/Performers/PerformerSelect"; import { IHasStoredID, sortStoredIdObjects } from "src/utils/data"; -import { ScrapeResult } from "src/components/Shared/ScrapeDialog/scrapeResult"; +import { + ObjectListScrapeResult, + ScrapeResult, +} from "src/components/Shared/ScrapeDialog/scrapeResult"; import { ScrapedMoviesRow, ScrapedPerformersRow, @@ -117,9 +120,9 @@ export const SceneScrapeDialog: React.FC = ({ } const [performers, setPerformers] = useState< - ScrapeResult + ObjectListScrapeResult >( - new ScrapeResult( + new ObjectListScrapeResult( sortStoredIdObjects( scenePerformers.map((p) => ({ stored_id: p.id, diff --git a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx index 668d8b7ff..9ce311667 100644 --- a/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneMergeDialog.tsx @@ -25,6 +25,7 @@ import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; import { ModalComponent } from "../Shared/Modal"; import { IHasStoredID, sortStoredIdObjects } from "src/utils/data"; import { + ObjectListScrapeResult, ScrapeResult, ZeroableScrapeResult, hasScrapedValues, @@ -118,9 +119,9 @@ const SceneMergeDetails: React.FC = ({ } const [performers, setPerformers] = useState< - ScrapeResult + ObjectListScrapeResult >( - new ScrapeResult( + new ObjectListScrapeResult( sortStoredIdObjects(dest.performers.map(idToStoredID)) ) ); @@ -203,8 +204,8 @@ const SceneMergeDetails: React.FC = ({ ); setPerformers( - new ScrapeResult( - dest.performers.map(idToStoredID), + new ObjectListScrapeResult( + sortStoredIdObjects(dest.performers.map(idToStoredID)), uniqIDStoredIDs(all.map((s) => s.performers.map(idToStoredID)).flat()) ) ); diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog/scrapeResult.ts b/ui/v2.5/src/components/Shared/ScrapeDialog/scrapeResult.ts index a8ee60e1e..195541fe8 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog/scrapeResult.ts +++ b/ui/v2.5/src/components/Shared/ScrapeDialog/scrapeResult.ts @@ -1,19 +1,30 @@ -import isEqual from "lodash-es/isEqual"; +import lodashIsEqual from "lodash-es/isEqual"; import clone from "lodash-es/clone"; +import { IHasStoredID } from "src/utils/data"; export class ScrapeResult { public newValue?: T; public originalValue?: T; public scraped: boolean = false; public useNewValue: boolean = false; + private isEqual: ( + v1: T | undefined | null, + v2: T | undefined | null + ) => boolean; public constructor( originalValue?: T | null, newValue?: T | null, - useNewValue?: boolean + useNewValue?: boolean, + isEqual: ( + v1: T | undefined | null, + v2: T | undefined | null + ) => boolean = lodashIsEqual ) { this.originalValue = originalValue ?? undefined; this.newValue = newValue ?? undefined; + this.isEqual = isEqual; + // NOTE: this means that zero values are treated as null // this is incorrect for numbers and booleans, but correct for strings const hasNewValue = !!this.newValue; @@ -32,7 +43,7 @@ export class ScrapeResult { const ret = clone(this); ret.newValue = value; - ret.useNewValue = !isEqual(ret.newValue, ret.originalValue); + ret.useNewValue = !this.isEqual(ret.newValue, ret.originalValue); // #2691 - if we're setting the value, assume it should be treated as // scraped @@ -53,9 +64,13 @@ export class ZeroableScrapeResult extends ScrapeResult { public constructor( originalValue?: T | null, newValue?: T | null, - useNewValue?: boolean + useNewValue?: boolean, + isEqual: ( + v1: T | undefined | null, + v2: T | undefined | null + ) => boolean = lodashIsEqual ) { - super(originalValue, newValue, useNewValue); + super(originalValue, newValue, useNewValue, isEqual); const hasNewValue = this.newValue !== undefined; @@ -65,6 +80,32 @@ export class ZeroableScrapeResult extends ScrapeResult { } } +function storedIDsEqual( + o1: T[] | undefined | null, + o2: T[] | undefined | null +) { + return ( + !!o1 && + !!o2 && + o1.length === o2.length && + o1.every((o) => { + return o2.find((oo) => o.stored_id === oo.stored_id); + }) + ); +} + +export class ObjectListScrapeResult< + T extends IHasStoredID +> extends ScrapeResult { + public constructor( + originalValue?: T[] | null, + newValue?: T[] | null, + useNewValue?: boolean + ) { + super(originalValue, newValue, useNewValue, storedIDsEqual); + } +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function hasScrapedValues(values: ScrapeResult[]) { return values.some((r) => r.scraped);