Support StashIDs in scrape dialog (#1955)

This commit is contained in:
WithoutPants
2021-11-05 12:10:29 +11:00
committed by GitHub
parent 392b28915a
commit dbfd92f9a8
3 changed files with 140 additions and 65 deletions

View File

@@ -8,6 +8,7 @@
* Added interface options to disable creating performers/studios/tags from dropdown selectors. ([#1814](https://github.com/stashapp/stash/pull/1814))
### 🎨 Improvements
* Added stash-id to scene scrape dialog. ([#1955](https://github.com/stashapp/stash/pull/1955))
* Reworked main navbar and positioned at bottom for mobile devices. ([#1769](https://github.com/stashapp/stash/pull/1769))
* Show files being deleted in the Delete dialogs. ([#1852](https://github.com/stashapp/stash/pull/1852))
* Added specific page titles. ([#1831](https://github.com/stashapp/stash/pull/1831))

View File

@@ -73,6 +73,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
setIsScraperQueryModalOpen,
] = useState<boolean>(false);
const [scrapedScene, setScrapedScene] = useState<GQL.ScrapedScene | null>();
const [endpoint, setEndpoint] = useState<string | undefined>();
const [coverImagePreview, setCoverImagePreview] = useState<
string | undefined
@@ -299,6 +300,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
}
// assume one returned scene
setScrapedScene(result.data.scrapeSingleScene[0]);
setEndpoint(s.stash_box_endpoint ?? undefined);
} catch (e) {
Toast.error(e);
} finally {
@@ -338,6 +340,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
function onScrapeQueryClicked(s: GQL.ScraperSourceInput) {
setScraper(s);
setEndpoint(s.stash_box_endpoint ?? undefined);
setIsScraperQueryModalOpen(true);
}
@@ -376,6 +379,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
<SceneScrapeDialog
scene={currentScene}
scraped={scrapedScene}
endpoint={endpoint}
onClose={(s) => onScrapeDialogClosed(s)}
/>
);
@@ -396,7 +400,12 @@ export const SceneEditPanel: React.FC<IProps> = ({
{stashBoxes.map((s, index) => (
<Dropdown.Item
key={s.endpoint}
onClick={() => onScrapeQueryClicked({ stash_box_index: index })}
onClick={() =>
onScrapeQueryClicked({
stash_box_index: index,
stash_box_endpoint: s.endpoint,
})
}
>
{stashboxDisplayName(s.name, index)}
</Dropdown.Item>
@@ -463,7 +472,12 @@ export const SceneEditPanel: React.FC<IProps> = ({
{stashBoxes.map((s, index) => (
<Dropdown.Item
key={s.endpoint}
onClick={() => onScrapeClicked({ stash_box_index: index })}
onClick={() =>
onScrapeClicked({
stash_box_index: index,
stash_box_endpoint: s.endpoint,
})
}
>
{stashboxDisplayName(s.name, index)}
</Dropdown.Item>
@@ -555,6 +569,34 @@ export const SceneEditPanel: React.FC<IProps> = ({
formik.setFieldValue("cover_image", updatedScene.image);
setCoverImagePreview(updatedScene.image);
}
if (updatedScene.remote_site_id && endpoint) {
let found = false;
formik.setFieldValue(
"stash_ids",
formik.values.stash_ids.map((s) => {
if (s.endpoint === endpoint) {
found = true;
return {
endpoint,
stash_id: updatedScene.remote_site_id,
};
}
return s;
})
);
if (!found) {
formik.setFieldValue(
"stash_ids",
formik.values.stash_ids.concat({
endpoint,
stash_id: updatedScene.remote_site_id,
})
);
}
}
}
async function onScrapeSceneURL() {
@@ -771,41 +813,51 @@ export const SceneEditPanel: React.FC<IProps> = ({
/>
</Col>
</Form.Group>
<Form.Group controlId="details">
<Form.Label>StashIDs</Form.Label>
<ul className="pl-0">
{formik.values.stash_ids.map((stashID) => {
const base = stashID.endpoint.match(/https?:\/\/.*?\//)?.[0];
const link = base ? (
<a
href={`${base}scenes/${stashID.stash_id}`}
target="_blank"
rel="noopener noreferrer"
>
{stashID.stash_id}
</a>
) : (
stashID.stash_id
);
return (
<li key={stashID.stash_id} className="row no-gutters">
<Button
variant="danger"
className="mr-2 py-0"
title={intl.formatMessage(
{ id: "actions.delete_entity" },
{ entityType: intl.formatMessage({ id: "stash_id" }) }
)}
onClick={() => removeStashID(stashID)}
{formik.values.stash_ids.length ? (
<Form.Group controlId="stashIDs">
<Form.Label>
<FormattedMessage id="stash_ids" />
</Form.Label>
<ul className="pl-0">
{formik.values.stash_ids.map((stashID) => {
const base = stashID.endpoint.match(
/https?:\/\/.*?\//
)?.[0];
const link = base ? (
<a
href={`${base}scenes/${stashID.stash_id}`}
target="_blank"
rel="noopener noreferrer"
>
<Icon icon="trash-alt" />
</Button>
{link}
</li>
);
})}
</ul>
</Form.Group>
{stashID.stash_id}
</a>
) : (
stashID.stash_id
);
return (
<li key={stashID.stash_id} className="row no-gutters">
<Button
variant="danger"
className="mr-2 py-0"
title={intl.formatMessage(
{ id: "actions.delete_entity" },
{
entityType: intl.formatMessage({
id: "stash_id",
}),
}
)}
onClick={() => removeStashID(stashID)}
>
<Icon icon="trash-alt" />
</Button>
{link}
</li>
);
})}
</ul>
</Form.Group>
) : undefined}
</div>
<div className="col-12 col-lg-5 col-xl-12">
<Form.Group controlId="details">

View File

@@ -230,6 +230,7 @@ function renderScrapedTagsRow(
interface ISceneScrapeDialogProps {
scene: Partial<GQL.SceneUpdateInput>;
scraped: GQL.ScrapedScene;
endpoint?: string;
onClose: (scrapedScene?: GQL.ScrapedScene) => void;
}
@@ -238,28 +239,33 @@ interface IHasStoredID {
stored_id?: string | null;
}
export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
props: ISceneScrapeDialogProps
) => {
export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
scene,
scraped,
onClose,
endpoint,
}) => {
const [title, setTitle] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(props.scene.title, props.scraped.title)
new ScrapeResult<string>(scene.title, scraped.title)
);
const [url, setURL] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(props.scene.url, props.scraped.url)
new ScrapeResult<string>(scene.url, scraped.url)
);
const [date, setDate] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(props.scene.date, props.scraped.date)
new ScrapeResult<string>(scene.date, scraped.date)
);
const [studio, setStudio] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(
props.scene.studio_id,
props.scraped.studio?.stored_id
)
new ScrapeResult<string>(scene.studio_id, scraped.studio?.stored_id)
);
const [newStudio, setNewStudio] = useState<GQL.ScrapedStudio | undefined>(
props.scraped.studio && !props.scraped.studio.stored_id
? props.scraped.studio
: undefined
scraped.studio && !scraped.studio.stored_id ? scraped.studio : undefined
);
const [stashID, setStashID] = useState(
new ScrapeResult<string>(
scene.stash_ids?.find((s) => s.endpoint === endpoint)?.stash_id,
scraped.remote_site_id
)
);
function mapStoredIdObjects(
@@ -302,39 +308,39 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
const [performers, setPerformers] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(
sortIdList(props.scene.performer_ids),
mapStoredIdObjects(props.scraped.performers ?? undefined)
sortIdList(scene.performer_ids),
mapStoredIdObjects(scraped.performers ?? undefined)
)
);
const [newPerformers, setNewPerformers] = useState<GQL.ScrapedPerformer[]>(
props.scraped.performers?.filter((t) => !t.stored_id) ?? []
scraped.performers?.filter((t) => !t.stored_id) ?? []
);
const [movies, setMovies] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(
sortIdList(props.scene.movies?.map((p) => p.movie_id)),
mapStoredIdObjects(props.scraped.movies ?? undefined)
sortIdList(scene.movies?.map((p) => p.movie_id)),
mapStoredIdObjects(scraped.movies ?? undefined)
)
);
const [newMovies, setNewMovies] = useState<GQL.ScrapedMovie[]>(
props.scraped.movies?.filter((t) => !t.stored_id) ?? []
scraped.movies?.filter((t) => !t.stored_id) ?? []
);
const [tags, setTags] = useState<ScrapeResult<string[]>>(
new ScrapeResult<string[]>(
sortIdList(props.scene.tag_ids),
mapStoredIdObjects(props.scraped.tags ?? undefined)
sortIdList(scene.tag_ids),
mapStoredIdObjects(scraped.tags ?? undefined)
)
);
const [newTags, setNewTags] = useState<GQL.ScrapedTag[]>(
props.scraped.tags?.filter((t) => !t.stored_id) ?? []
scraped.tags?.filter((t) => !t.stored_id) ?? []
);
const [details, setDetails] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(props.scene.details, props.scraped.details)
new ScrapeResult<string>(scene.details, scraped.details)
);
const [image, setImage] = useState<ScrapeResult<string>>(
new ScrapeResult<string>(props.scene.cover_image, props.scraped.image)
new ScrapeResult<string>(scene.cover_image, scraped.image)
);
const [createStudio] = useStudioCreate();
@@ -347,11 +353,20 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
// don't show the dialog if nothing was scraped
if (
[title, url, date, studio, performers, movies, tags, details, image].every(
(r) => !r.scraped
)
[
title,
url,
date,
studio,
performers,
movies,
tags,
details,
image,
stashID,
].every((r) => !r.scraped)
) {
props.onClose();
onClose();
return <></>;
}
@@ -535,6 +550,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
}),
details: details.getNewValue(),
image: image.getNewValue(),
remote_site_id: stashID.getNewValue(),
};
}
@@ -590,6 +606,12 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
result={details}
onChange={(value) => setDetails(value)}
/>
<ScrapedInputGroupRow
title={intl.formatMessage({ id: "stash_id" })}
result={stashID}
locked
onChange={(value) => setStashID(value)}
/>
<ScrapedImageRow
title={intl.formatMessage({ id: "cover_image" })}
className="scene-cover"
@@ -608,7 +630,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = (
)}
renderScrapeRows={renderScrapeRows}
onClose={(apply) => {
props.onClose(apply ? makeNewScrapedItem() : undefined);
onClose(apply ? makeNewScrapedItem() : undefined);
}}
/>
);