mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
performer: scrape dialog: allow selecting from multiple images (#3965)
* performer: scrape dialog: allow selecting from multiple images * Hide selector for single images --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Form,
|
||||
Col,
|
||||
@@ -15,6 +15,8 @@ import isEqual from "lodash-es/isEqual";
|
||||
import clone from "lodash-es/clone";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import {
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faCheck,
|
||||
faPencilAlt,
|
||||
faPlus,
|
||||
@@ -441,6 +443,169 @@ export const ScrapedImageRow: React.FC<IScrapedImageRowProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface IScrapedImageDialogRowProps<
|
||||
T extends ScrapeResult<string>,
|
||||
V extends IHasName
|
||||
> extends IScrapedFieldProps<string> {
|
||||
title: string;
|
||||
renderOriginalField: () => JSX.Element | undefined;
|
||||
renderNewField: () => JSX.Element | undefined;
|
||||
onChange: (value: T) => void;
|
||||
newValues?: V[];
|
||||
images: string[];
|
||||
onCreateNew?: (index: number) => void;
|
||||
}
|
||||
|
||||
export const ScrapeImageDialogRow = <
|
||||
T extends ScrapeResult<string>,
|
||||
V extends IHasName
|
||||
>(
|
||||
props: IScrapedImageDialogRowProps<T, V>
|
||||
) => {
|
||||
const [imageIndex, setImageIndex] = useState(0);
|
||||
|
||||
function hasNewValues() {
|
||||
return props.newValues && props.newValues.length > 0 && props.onCreateNew;
|
||||
}
|
||||
|
||||
function setPrev() {
|
||||
let newIdx = imageIndex - 1;
|
||||
if (newIdx < 0) {
|
||||
newIdx = props.images.length - 1;
|
||||
}
|
||||
const ret = props.result.cloneWithValue(props.images[newIdx]);
|
||||
props.onChange(ret as T);
|
||||
setImageIndex(newIdx);
|
||||
}
|
||||
|
||||
function setNext() {
|
||||
let newIdx = imageIndex + 1;
|
||||
if (newIdx >= props.images.length) {
|
||||
newIdx = 0;
|
||||
}
|
||||
const ret = props.result.cloneWithValue(props.images[newIdx]);
|
||||
props.onChange(ret as T);
|
||||
setImageIndex(newIdx);
|
||||
}
|
||||
|
||||
if (!props.result.scraped && !hasNewValues()) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function renderSelector() {
|
||||
return (
|
||||
props.images.length > 1 && (
|
||||
<div className="d-flex mt-2 image-selection">
|
||||
<Button onClick={setPrev}>
|
||||
<Icon icon={faArrowLeft} />
|
||||
</Button>
|
||||
<h5 className="flex-grow-1 px-2">
|
||||
Select performer image
|
||||
<br />
|
||||
{imageIndex + 1} of {props.images.length}
|
||||
</h5>
|
||||
<Button onClick={setNext}>
|
||||
<Icon icon={faArrowRight} />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function renderNewValues() {
|
||||
if (!hasNewValues()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ret = (
|
||||
<>
|
||||
{props.newValues!.map((t, i) => (
|
||||
<Badge
|
||||
className="tag-item"
|
||||
variant="secondary"
|
||||
key={t.name}
|
||||
onClick={() => props.onCreateNew!(i)}
|
||||
>
|
||||
{t.name}
|
||||
<Button className="minimal ml-2">
|
||||
<Icon className="fa-fw" icon={faPlus} />
|
||||
</Button>
|
||||
</Badge>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
const minCollapseLength = 10;
|
||||
|
||||
if (props.newValues!.length >= minCollapseLength) {
|
||||
return (
|
||||
<CollapseButton text={`Missing (${props.newValues!.length})`}>
|
||||
{ret}
|
||||
</CollapseButton>
|
||||
);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className="px-3 pt-3">
|
||||
<Form.Label column lg="3">
|
||||
{props.title}
|
||||
</Form.Label>
|
||||
|
||||
<Col lg="9">
|
||||
<Row>
|
||||
<Col xs="6">
|
||||
<InputGroup>{props.renderOriginalField()}</InputGroup>
|
||||
</Col>
|
||||
<Col xs="6">
|
||||
<InputGroup>
|
||||
{props.renderNewField()}
|
||||
{renderSelector()}
|
||||
</InputGroup>
|
||||
{renderNewValues()}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
interface IScrapedImagesRowProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
result: ScrapeResult<string>;
|
||||
images: string[];
|
||||
onChange: (value: ScrapeResult<string>) => void;
|
||||
}
|
||||
|
||||
export const ScrapedImagesRow: React.FC<IScrapedImagesRowProps> = (props) => {
|
||||
return (
|
||||
<ScrapeImageDialogRow
|
||||
title={props.title}
|
||||
result={props.result}
|
||||
images={props.images}
|
||||
renderOriginalField={() => (
|
||||
<ScrapedImage
|
||||
result={props.result}
|
||||
className={props.className}
|
||||
placeholder={props.title}
|
||||
/>
|
||||
)}
|
||||
renderNewField={() => (
|
||||
<ScrapedImage
|
||||
result={props.result}
|
||||
className={props.className}
|
||||
placeholder={props.title}
|
||||
isNew
|
||||
/>
|
||||
)}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface IScrapeDialogProps {
|
||||
title: string;
|
||||
existingLabel?: string;
|
||||
|
||||
Reference in New Issue
Block a user