mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Improve scrape url button UX (#1903)
* change button label to icon * add URLField component
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
StudioSelect,
|
||||
Icon,
|
||||
LoadingIndicator,
|
||||
URLField,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { useFormik } from "formik";
|
||||
@@ -384,21 +385,6 @@ export const GalleryEditPanel: React.FC<
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRenderScrapeButton() {
|
||||
if (!formik.values.url || !urlScrapable(formik.values.url)) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
className="minimal scrape-url-button"
|
||||
onClick={onScrapeGalleryURL}
|
||||
title="Scrape"
|
||||
>
|
||||
<Icon className="fa-fw" icon="file-download" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextField(field: string, title: string, placeholder?: string) {
|
||||
return (
|
||||
<Form.Group controlId={title} as={Row}>
|
||||
@@ -461,15 +447,12 @@ export const GalleryEditPanel: React.FC<
|
||||
<Form.Label className="col-form-label">
|
||||
<FormattedMessage id="url" />
|
||||
</Form.Label>
|
||||
<div className="float-right scrape-button-container">
|
||||
{maybeRenderScrapeButton()}
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={9}>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
<URLField
|
||||
{...formik.getFieldProps("url")}
|
||||
onScrapeClick={onScrapeGalleryURL}
|
||||
urlScrapable={urlScrapable}
|
||||
isInvalid={!!formik.getFieldMeta("url").error}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -10,19 +10,12 @@ import {
|
||||
import {
|
||||
LoadingIndicator,
|
||||
StudioSelect,
|
||||
Icon,
|
||||
DetailsEditNavbar,
|
||||
DurationInput,
|
||||
URLField,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import {
|
||||
Modal as BSModal,
|
||||
Form,
|
||||
Button,
|
||||
Col,
|
||||
Row,
|
||||
InputGroup,
|
||||
} from "react-bootstrap";
|
||||
import { Modal as BSModal, Form, Button, Col, Row } from "react-bootstrap";
|
||||
import { DurationUtils, FormUtils, ImageUtils } from "src/utils";
|
||||
import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars";
|
||||
import { useFormik } from "formik";
|
||||
@@ -257,21 +250,6 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||
);
|
||||
}
|
||||
|
||||
function maybeRenderScrapeButton() {
|
||||
const { url } = formik.values;
|
||||
if (!url || !urlScrapable(url)) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
className="minimal scrape-url-button"
|
||||
onClick={() => onScrapeMovieURL()}
|
||||
>
|
||||
<Icon icon="file-upload" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function maybeRenderScrapeDialog() {
|
||||
if (!scrapedMovie) {
|
||||
return;
|
||||
@@ -468,14 +446,11 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||
title: intl.formatMessage({ id: "url" }),
|
||||
})}
|
||||
<Col xs={9}>
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
{...formik.getFieldProps("url")}
|
||||
/>
|
||||
<InputGroup.Append>{maybeRenderScrapeButton()}</InputGroup.Append>
|
||||
</InputGroup>
|
||||
<URLField
|
||||
{...formik.getFieldProps("url")}
|
||||
onScrapeClick={onScrapeMovieURL}
|
||||
urlScrapable={urlScrapable}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Col,
|
||||
InputGroup,
|
||||
Row,
|
||||
Badge,
|
||||
Dropdown,
|
||||
} from "react-bootstrap";
|
||||
import { Button, Form, Col, Row, Badge, Dropdown } from "react-bootstrap";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import Mousetrap from "mousetrap";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
@@ -28,6 +20,7 @@ import {
|
||||
CollapseButton,
|
||||
Modal,
|
||||
TagSelect,
|
||||
URLField,
|
||||
} from "src/components/Shared";
|
||||
import { ImageUtils, getStashIDs } from "src/utils";
|
||||
import { getCountryByISO } from "src/utils/country";
|
||||
@@ -662,19 +655,6 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
setScraper(undefined);
|
||||
}
|
||||
|
||||
function maybeRenderScrapeButton() {
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
disabled={!urlScrapable(formik.values.url)}
|
||||
className="scrape-url-button text-input"
|
||||
onClick={() => onScrapePerformerURL()}
|
||||
>
|
||||
<Icon icon="file-upload" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function renderButtons() {
|
||||
return (
|
||||
<Row>
|
||||
@@ -963,14 +943,11 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
<FormattedMessage id="url" />
|
||||
</Form.Label>
|
||||
<Col xs={fieldXS} xl={fieldXL}>
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
placeholder="URL"
|
||||
{...formik.getFieldProps("url")}
|
||||
/>
|
||||
<InputGroup.Append>{maybeRenderScrapeButton()}</InputGroup.Append>
|
||||
</InputGroup>
|
||||
<URLField
|
||||
{...formik.getFieldProps("url")}
|
||||
onScrapeClick={onScrapePerformerURL}
|
||||
urlScrapable={urlScrapable}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
Icon,
|
||||
LoadingIndicator,
|
||||
ImageInput,
|
||||
URLField,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { ImageUtils, FormUtils, TextUtils, getStashIDs } from "src/utils";
|
||||
@@ -574,21 +575,6 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRenderScrapeButton() {
|
||||
if (!formik.values.url || !urlScrapable(formik.values.url)) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
className="minimal scrape-url-button"
|
||||
onClick={onScrapeSceneURL}
|
||||
title="Scrape"
|
||||
>
|
||||
<Icon className="fa-fw" icon="file-download" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextField(field: string, title: string, placeholder?: string) {
|
||||
return (
|
||||
<Form.Group controlId={title} as={Row}>
|
||||
@@ -652,15 +638,12 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
||||
<Form.Label className="col-form-label">
|
||||
<FormattedMessage id="url" />
|
||||
</Form.Label>
|
||||
<div className="float-right scrape-button-container">
|
||||
{maybeRenderScrapeButton()}
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={9}>
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
<URLField
|
||||
{...formik.getFieldProps("url")}
|
||||
onScrapeClick={onScrapeSceneURL}
|
||||
urlScrapable={urlScrapable}
|
||||
isInvalid={!!formik.getFieldMeta("url").error}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -500,15 +500,6 @@ input[type="range"].blue-slider {
|
||||
font-size: 1.3em;
|
||||
height: calc(1.5em + 0.75rem + 2px);
|
||||
}
|
||||
|
||||
.scrape-button-container {
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.scrape-url-button {
|
||||
color: $text-color;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.scene-markers-panel {
|
||||
|
||||
44
ui/v2.5/src/components/Shared/URLField.tsx
Normal file
44
ui/v2.5/src/components/Shared/URLField.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Button, InputGroup, Form } from "react-bootstrap";
|
||||
import { Icon } from "src/components/Shared";
|
||||
import { FormikHandlers } from "formik";
|
||||
|
||||
interface IProps {
|
||||
value: string;
|
||||
name: string;
|
||||
onChange: FormikHandlers["handleChange"];
|
||||
onBlur: FormikHandlers["handleBlur"];
|
||||
onScrapeClick(): void;
|
||||
urlScrapable(url: string): boolean;
|
||||
isInvalid?: boolean;
|
||||
}
|
||||
|
||||
export const URLField: React.FC<IProps> = (props: IProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<InputGroup className="mr-2 flex-grow-1">
|
||||
<Form.Control
|
||||
className="text-input"
|
||||
placeholder={intl.formatMessage({ id: "url" })}
|
||||
value={props.value}
|
||||
name={props.name}
|
||||
onChange={props.onChange}
|
||||
onBlur={props.onBlur}
|
||||
isInvalid={props.isInvalid}
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
<Button
|
||||
className="scrape-url-button text-input"
|
||||
variant="secondary"
|
||||
onClick={props.onScrapeClick}
|
||||
disabled={!props.value || !props.urlScrapable(props.value)}
|
||||
title={intl.formatMessage({ id: "actions.scrape" })}
|
||||
>
|
||||
<Icon icon="file-download" />
|
||||
</Button>
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
);
|
||||
};
|
||||
@@ -19,4 +19,5 @@ export { ExportDialog } from "./ExportDialog";
|
||||
export { default as DeleteEntityDialog } from "./DeleteEntityDialog";
|
||||
export { IndeterminateCheckbox } from "./IndeterminateCheckbox";
|
||||
export { OperationButton } from "./OperationButton";
|
||||
export { URLField } from "./URLField";
|
||||
export const TITLE_SUFFIX = " | Stash";
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"save_delete_settings": "Use these options by default when deleting",
|
||||
"save_filter": "Save filter",
|
||||
"scan": "Scan",
|
||||
"scrape": "Scrape",
|
||||
"scrape_with": "Scrape with…",
|
||||
"scrape_query": "Scrape query",
|
||||
"scrape_scene_fragment": "Scrape by fragment",
|
||||
|
||||
Reference in New Issue
Block a user