mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Prettier
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Table } from "react-bootstrap";
|
import { Table } from "react-bootstrap";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import {
|
import { FindGalleriesQueryResult } from "src/core/generated-graphql";
|
||||||
FindGalleriesQueryResult,
|
|
||||||
} from "src/core/generated-graphql";
|
|
||||||
import { useGalleriesList } from "src/hooks";
|
import { useGalleriesList } from "src/hooks";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { DisplayMode } from "src/models/list-filter/types";
|
import { DisplayMode } from "src/models/list-filter/types";
|
||||||
@@ -38,7 +36,10 @@ export const GalleryList: React.FC = () => {
|
|||||||
<td>
|
<td>
|
||||||
<Link to={`/galleries/${gallery.id}`}>
|
<Link to={`/galleries/${gallery.id}`}>
|
||||||
{gallery.files.length > 0 ? (
|
{gallery.files.length > 0 ? (
|
||||||
<img alt={gallery.title ?? ''} src={`${gallery.files[0].path}?thumb=true`} />
|
<img
|
||||||
|
alt={gallery.title ?? ""}
|
||||||
|
src={`${gallery.files[0].path}?thumb=true`}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
undefined
|
undefined
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import Lightbox from 'react-images';
|
import Lightbox from "react-images";
|
||||||
import Gallery from "react-photo-gallery";
|
import Gallery from "react-photo-gallery";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ export const GalleryViewer: FunctionComponent<IProps> = ({ gallery }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const photos = gallery.files.map(file => ({
|
const photos = gallery.files.map(file => ({
|
||||||
src: file.path ?? '',
|
src: file.path ?? "",
|
||||||
caption: file.name ?? ''
|
caption: file.name ?? ""
|
||||||
}));
|
}));
|
||||||
const thumbs = gallery.files.map(file => ({
|
const thumbs = gallery.files.map(file => ({
|
||||||
src: `${file.path}?thumb=true` || "",
|
src: `${file.path}?thumb=true` || "",
|
||||||
@@ -48,7 +48,9 @@ export const GalleryViewer: FunctionComponent<IProps> = ({ gallery }) => {
|
|||||||
onClickPrev={gotoPrevious}
|
onClickPrev={gotoPrevious}
|
||||||
onClickNext={gotoNext}
|
onClickNext={gotoNext}
|
||||||
currentImage={currentImage}
|
currentImage={currentImage}
|
||||||
onClickImage={() => window.open(photos[currentImage].src ?? '', "_blank")}
|
onClickImage={() =>
|
||||||
|
window.open(photos[currentImage].src ?? "", "_blank")
|
||||||
|
}
|
||||||
isOpen={lightboxIsOpen}
|
isOpen={lightboxIsOpen}
|
||||||
width={9999}
|
width={9999}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,7 +4,13 @@ import { StashService } from "src/core/StashService";
|
|||||||
|
|
||||||
export const SettingsAboutPanel: React.FC = () => {
|
export const SettingsAboutPanel: React.FC = () => {
|
||||||
const { data, error, loading } = StashService.useVersion();
|
const { data, error, loading } = StashService.useVersion();
|
||||||
const { data: dataLatest, error: errorLatest, loading: loadingLatest, refetch, networkStatus } = StashService.useLatestVersion();
|
const {
|
||||||
|
data: dataLatest,
|
||||||
|
error: errorLatest,
|
||||||
|
loading: loadingLatest,
|
||||||
|
refetch,
|
||||||
|
networkStatus
|
||||||
|
} = StashService.useLatestVersion();
|
||||||
|
|
||||||
function maybeRenderTag() {
|
function maybeRenderTag() {
|
||||||
if (!data || !data.version || !data.version.version) {
|
if (!data || !data.version || !data.version.version) {
|
||||||
@@ -19,28 +25,34 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function maybeRenderLatestVersion() {
|
function maybeRenderLatestVersion() {
|
||||||
if (!dataLatest || !dataLatest.latestversion || !dataLatest.latestversion.shorthash || !dataLatest.latestversion.url) { return; }
|
if (
|
||||||
|
!dataLatest ||
|
||||||
|
!dataLatest.latestversion ||
|
||||||
|
!dataLatest.latestversion.shorthash ||
|
||||||
|
!dataLatest.latestversion.url
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!data || !data.version || !data.version.hash) {
|
if (!data || !data.version || !data.version.hash) {
|
||||||
return (
|
return <>{dataLatest.latestversion.shorthash}</>;
|
||||||
<>{dataLatest.latestversion.shorthash}</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.version.hash !== dataLatest.latestversion.shorthash) {
|
if (data.version.hash !== dataLatest.latestversion.shorthash) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<strong>{dataLatest.latestversion.shorthash} [NEW] </strong><a href={dataLatest.latestversion.url}>Download</a>
|
<strong>{dataLatest.latestversion.shorthash} [NEW] </strong>
|
||||||
|
<a href={dataLatest.latestversion.url}>Download</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <>{dataLatest.latestversion.shorthash}</>;
|
||||||
<>{dataLatest.latestversion.shorthash}</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLatestVersion() {
|
function renderLatestVersion() {
|
||||||
if (!data || !data.version || !data.version.version) { return; } // if there is no "version" latest version check is obviously not supported
|
if (!data || !data.version || !data.version.version) {
|
||||||
|
return;
|
||||||
|
} // if there is no "version" latest version check is obviously not supported
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -49,7 +61,9 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
<td>{maybeRenderLatestVersion()} </td>
|
<td>{maybeRenderLatestVersion()} </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><Button onClick={() => refetch()}>Check for new version</Button></td>
|
<td>
|
||||||
|
<Button onClick={() => refetch()}>Check for new version</Button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
@@ -84,16 +98,54 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
<Table>
|
<Table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Stash home at <a href="https://github.com/stashapp/stash" rel="noopener noreferrer" target="_blank">Github</a></td>
|
<td>
|
||||||
|
Stash home at{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/stashapp/stash"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Github
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Stash <a href="https://github.com/stashapp/stash/wiki" rel="noopener noreferrer" target="_blank">Wiki</a> page</td>
|
<td>
|
||||||
|
Stash{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/stashapp/stash/wiki"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Wiki
|
||||||
|
</a>{" "}
|
||||||
|
page
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Join our <a href="https://discord.gg/2TsNFKt" rel="noopener noreferrer" target="_blank">Discord</a> channel</td>
|
<td>
|
||||||
|
Join our{" "}
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/2TsNFKt"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Discord
|
||||||
|
</a>{" "}
|
||||||
|
channel
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Support us through <a href="https://opencollective.com/stashapp" rel="noopener noreferrer" target="_blank">Open Collective</a></td>
|
<td>
|
||||||
|
Support us through{" "}
|
||||||
|
<a
|
||||||
|
href="https://opencollective.com/stashapp"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Open Collective
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
@@ -101,7 +153,11 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
{error && <span>{error.message}</span>}
|
{error && <span>{error.message}</span>}
|
||||||
{errorLatest && <span>{errorLatest.message}</span>}
|
{errorLatest && <span>{errorLatest.message}</span>}
|
||||||
{renderVersion()}
|
{renderVersion()}
|
||||||
{!dataLatest || loadingLatest || networkStatus === 4 ? <Spinner animation="border" variant="light" /> : <>{renderLatestVersion()}</>}
|
{!dataLatest || loadingLatest || networkStatus === 4 ? (
|
||||||
|
<Spinner animation="border" variant="light" />
|
||||||
|
) : (
|
||||||
|
<>{renderLatestVersion()}</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
|||||||
setDatabasePath(conf.general.databasePath);
|
setDatabasePath(conf.general.databasePath);
|
||||||
setGeneratedPath(conf.general.generatedPath);
|
setGeneratedPath(conf.general.generatedPath);
|
||||||
setMaxTranscodeSize(conf.general.maxTranscodeSize ?? undefined);
|
setMaxTranscodeSize(conf.general.maxTranscodeSize ?? undefined);
|
||||||
setMaxStreamingTranscodeSize(conf.general.maxStreamingTranscodeSize ?? undefined);
|
setMaxStreamingTranscodeSize(
|
||||||
|
conf.general.maxStreamingTranscodeSize ?? undefined
|
||||||
|
);
|
||||||
setUsername(conf.general.username);
|
setUsername(conf.general.username);
|
||||||
setPassword(conf.general.password);
|
setPassword(conf.general.password);
|
||||||
setLogFile(conf.general.logFile ?? undefined);
|
setLogFile(conf.general.logFile ?? undefined);
|
||||||
|
|||||||
@@ -24,10 +24,8 @@ interface IProps {
|
|||||||
onImageChange: (event: React.FormEvent<HTMLInputElement>) => void;
|
onImageChange: (event: React.FormEvent<HTMLInputElement>) => void;
|
||||||
|
|
||||||
// TODO: only for performers. make generic
|
// TODO: only for performers. make generic
|
||||||
scrapers?: Pick<GQL.Scraper, 'id' | 'name'>[];
|
scrapers?: Pick<GQL.Scraper, "id" | "name">[];
|
||||||
onDisplayScraperDialog?: (
|
onDisplayScraperDialog?: (scraper: Pick<GQL.Scraper, "id" | "name">) => void;
|
||||||
scraper: Pick<GQL.Scraper, 'id' | 'name'>
|
|
||||||
) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailsEditNavbar: React.FC<IProps> = (props: IProps) => {
|
export const DetailsEditNavbar: React.FC<IProps> = (props: IProps) => {
|
||||||
@@ -95,9 +93,7 @@ export const DetailsEditNavbar: React.FC<IProps> = (props: IProps) => {
|
|||||||
? props.scrapers.map(s => (
|
? props.scrapers.map(s => (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() =>
|
onClick={() => props.onDisplayScraperDialog?.(s)}
|
||||||
props.onDisplayScraperDialog?.(s)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{s.name}
|
{s.name}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
||||||
const [value, setValue] = useState<string>(DurationUtils.secondsToString(props.numericValue));
|
const [value, setValue] = useState<string>(
|
||||||
|
DurationUtils.secondsToString(props.numericValue)
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(DurationUtils.secondsToString(props.numericValue));
|
setValue(DurationUtils.secondsToString(props.numericValue));
|
||||||
}, [props.numericValue]);
|
}, [props.numericValue]);
|
||||||
|
|
||||||
|
|
||||||
function increment() {
|
function increment() {
|
||||||
let seconds = DurationUtils.stringToSeconds(value);
|
let seconds = DurationUtils.stringToSeconds(value);
|
||||||
seconds += 1;
|
seconds += 1;
|
||||||
@@ -66,7 +67,9 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
|||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e: any) => setValue(e.target.value)}
|
onChange={(e: any) => setValue(e.target.value)}
|
||||||
onBlur={() => props.onValueChange(DurationUtils.stringToSeconds(value))}
|
onBlur={() =>
|
||||||
|
props.onValueChange(DurationUtils.stringToSeconds(value))
|
||||||
|
}
|
||||||
placeholder="hh:mm:ss"
|
placeholder="hh:mm:ss"
|
||||||
/>
|
/>
|
||||||
<InputGroup.Append>
|
<InputGroup.Append>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Spinner } from 'react-bootstrap';
|
import { Spinner } from "react-bootstrap";
|
||||||
|
|
||||||
interface ILoadingProps {
|
interface ILoadingProps {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLASSNAME = 'LoadingIndicator';
|
const CLASSNAME = "LoadingIndicator";
|
||||||
const CLASSNAME_MESSAGE = `${CLASSNAME}-message`;
|
const CLASSNAME_MESSAGE = `${CLASSNAME}-message`;
|
||||||
|
|
||||||
const LoadingIndicator: React.FC<ILoadingProps> = ({ message }) => (
|
const LoadingIndicator: React.FC<ILoadingProps> = ({ message }) => (
|
||||||
@@ -13,9 +13,7 @@ const LoadingIndicator: React.FC<ILoadingProps> = ({ message }) => (
|
|||||||
<Spinner animation="border" role="status">
|
<Spinner animation="border" role="status">
|
||||||
<span className="sr-only">Loading...</span>
|
<span className="sr-only">Loading...</span>
|
||||||
</Spinner>
|
</Spinner>
|
||||||
<h4 className={CLASSNAME_MESSAGE}>
|
<h4 className={CLASSNAME_MESSAGE}>{message}</h4>
|
||||||
{ message }
|
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ interface ISceneGallerySelect {
|
|||||||
initialId?: string;
|
initialId?: string;
|
||||||
sceneId: string;
|
sceneId: string;
|
||||||
onSelect: (
|
onSelect: (
|
||||||
item: GQL.ValidGalleriesForSceneQuery["validGalleriesForScene"][0] | undefined
|
item:
|
||||||
|
| GQL.ValidGalleriesForSceneQuery["validGalleriesForScene"][0]
|
||||||
|
| undefined
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,9 +80,7 @@ export const SceneGallerySelect: React.FC<ISceneGallerySelect> = props => {
|
|||||||
|
|
||||||
interface IScrapePerformerSuggestProps {
|
interface IScrapePerformerSuggestProps {
|
||||||
scraperId: string;
|
scraperId: string;
|
||||||
onSelectPerformer: (
|
onSelectPerformer: (performer: GQL.ScrapedPerformerDataFragment) => void;
|
||||||
performer: GQL.ScrapedPerformerDataFragment
|
|
||||||
) => void;
|
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = props => {
|
export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = props => {
|
||||||
@@ -105,8 +105,7 @@ export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = pr
|
|||||||
const onChange = (selectedItems: ValueType<Option>) => {
|
const onChange = (selectedItems: ValueType<Option>) => {
|
||||||
const name = getSelectedValues(selectedItems)[0];
|
const name = getSelectedValues(selectedItems)[0];
|
||||||
const performer = performers.find(p => p.name === name);
|
const performer = performers.find(p => p.name === name);
|
||||||
if(performer)
|
if (performer) props.onSelectPerformer(performer);
|
||||||
props.onSelectPerformer(performer)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -239,7 +238,7 @@ export const TagSelect: React.FC<IFilterProps> = props => {
|
|||||||
variables: { name: tagName }
|
variables: { name: tagName }
|
||||||
});
|
});
|
||||||
|
|
||||||
if(result?.data?.tagCreate) {
|
if (result?.data?.tagCreate) {
|
||||||
setSelectedIds([...selectedIds, result.data.tagCreate.id]);
|
setSelectedIds([...selectedIds, result.data.tagCreate.id]);
|
||||||
props.onSelect(
|
props.onSelect(
|
||||||
[...tags, result.data.tagCreate].filter(
|
[...tags, result.data.tagCreate].filter(
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ export { DetailsEditNavbar } from "./DetailsEditNavbar";
|
|||||||
export { DurationInput } from "./DurationInput";
|
export { DurationInput } from "./DurationInput";
|
||||||
export { TagLink } from "./TagLink";
|
export { TagLink } from "./TagLink";
|
||||||
export { HoverPopover } from "./HoverPopover";
|
export { HoverPopover } from "./HoverPopover";
|
||||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
export { default as LoadingIndicator } from "./LoadingIndicator";
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import { LoadingIndicator } from "src/components/Shared";
|
|||||||
export const Stats: React.FC = () => {
|
export const Stats: React.FC = () => {
|
||||||
const { data, error, loading } = StashService.useStats();
|
const { data, error, loading } = StashService.useStats();
|
||||||
|
|
||||||
if (loading || !data)
|
if (loading || !data) return <LoadingIndicator message="Loading..." />;
|
||||||
return <LoadingIndicator message="Loading..." />
|
|
||||||
|
|
||||||
if (error)
|
if (error) return <span>error.message</span>;
|
||||||
return <span>error.message</span> ;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-75 m-auto">
|
<div className="w-75 m-auto">
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export const Studio: React.FC = () => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const result = await createStudio();
|
const result = await createStudio();
|
||||||
if(result.data?.studioCreate?.id)
|
if (result.data?.studioCreate?.id)
|
||||||
history.push(`/studios/${result.data.studioCreate.id}`);
|
history.push(`/studios/${result.data.studioCreate.id}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { FindStudiosQueryResult } from "src/core/generated-graphql";
|
||||||
FindStudiosQueryResult
|
|
||||||
} from "src/core/generated-graphql";
|
|
||||||
import { useStudiosList } from "src/hooks";
|
import { useStudiosList } from "src/hooks";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { DisplayMode } from "src/models/list-filter/types";
|
import { DisplayMode } from "src/models/list-filter/types";
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
numericValue={criterion.value ? criterion.value : 0}
|
numericValue={criterion.value ? criterion.value : 0}
|
||||||
onValueChange={onChangedDuration}
|
onValueChange={onChangedDuration}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Form.Control
|
<Form.Control
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
value={props.filter.searchTerm}
|
value={props.filter.searchTerm}
|
||||||
onChange={onChangeQuery}
|
onChange={onChangeQuery}
|
||||||
className="filter-item"
|
className="filter-item"
|
||||||
style={{ width: 'inherit' }}
|
style={{ width: "inherit" }}
|
||||||
/>
|
/>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ export const PerformerCard: React.FC<IPerformerCardProps> = (
|
|||||||
<div className="card-section">
|
<div className="card-section">
|
||||||
<h5 className="text-truncate">{props.performer.name}</h5>
|
<h5 className="text-truncate">{props.performer.name}</h5>
|
||||||
{age !== 0 ? <div className="text-muted">{ageString}</div> : ""}
|
{age !== 0 ? <div className="text-muted">{ageString}</div> : ""}
|
||||||
<div className="text-muted">Stars in {props.performer.scene_count}{" "}
|
<div className="text-muted">
|
||||||
|
Stars in {props.performer.scene_count}{" "}
|
||||||
<Link to={NavUtils.makePerformerScenesUrl(props.performer)}>
|
<Link to={NavUtils.makePerformerScenesUrl(props.performer)}>
|
||||||
scenes
|
scenes
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import { Button, Spinner, Tabs, Tab } from "react-bootstrap";
|
|||||||
import { useParams, useHistory } from "react-router-dom";
|
import { useParams, useHistory } from "react-router-dom";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import {
|
import { Icon } from "src/components/Shared";
|
||||||
Icon,
|
import { useToast } from "src/hooks";
|
||||||
} from "src/components/Shared";
|
|
||||||
import { useToast } from 'src/hooks';
|
|
||||||
import { TextUtils } from "src/utils";
|
import { TextUtils } from "src/utils";
|
||||||
import Lightbox from 'react-images';
|
import Lightbox from "react-images";
|
||||||
import { IconName } from "@fortawesome/fontawesome-svg-core";
|
import { IconName } from "@fortawesome/fontawesome-svg-core";
|
||||||
import { PerformerDetailsPanel } from './PerformerDetailsPanel';
|
import { PerformerDetailsPanel } from "./PerformerDetailsPanel";
|
||||||
import { PerformerOperationsPanel } from './PerformerOperationsPanel';
|
import { PerformerOperationsPanel } from "./PerformerOperationsPanel";
|
||||||
import { PerformerScenesPanel } from './PerformerScenesPanel';
|
import { PerformerScenesPanel } from "./PerformerScenesPanel";
|
||||||
|
|
||||||
export const Performer: React.FC = () => {
|
export const Performer: React.FC = () => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
@@ -23,7 +21,9 @@ export const Performer: React.FC = () => {
|
|||||||
const isNew = id === "new";
|
const isNew = id === "new";
|
||||||
|
|
||||||
// Performer state
|
// Performer state
|
||||||
const [performer, setPerformer] = useState<Partial<GQL.PerformerDataFragment>>({});
|
const [performer, setPerformer] = useState<
|
||||||
|
Partial<GQL.PerformerDataFragment>
|
||||||
|
>({});
|
||||||
const [imagePreview, setImagePreview] = useState<string>();
|
const [imagePreview, setImagePreview] = useState<string>();
|
||||||
const [lightboxIsOpen, setLightboxIsOpen] = useState(false);
|
const [lightboxIsOpen, setLightboxIsOpen] = useState(false);
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ export const Performer: React.FC = () => {
|
|||||||
const [createPerformer] = StashService.usePerformerCreate();
|
const [createPerformer] = StashService.usePerformerCreate();
|
||||||
const [deletePerformer] = StashService.usePerformerDestroy();
|
const [deletePerformer] = StashService.usePerformerDestroy();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
if (data?.findPerformer) setPerformer(data.findPerformer);
|
if (data?.findPerformer) setPerformer(data.findPerformer);
|
||||||
@@ -54,16 +53,24 @@ export const Performer: React.FC = () => {
|
|||||||
|
|
||||||
if (error) return <div>{error.message}</div>;
|
if (error) return <div>{error.message}</div>;
|
||||||
|
|
||||||
async function onSave(performerInput: Partial<GQL.PerformerCreateInput> | Partial<GQL.PerformerUpdateInput>) {
|
async function onSave(
|
||||||
|
performerInput:
|
||||||
|
| Partial<GQL.PerformerCreateInput>
|
||||||
|
| Partial<GQL.PerformerUpdateInput>
|
||||||
|
) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
const result = await updatePerformer({variables: performerInput as GQL.PerformerUpdateInput});
|
const result = await updatePerformer({
|
||||||
if(result.data?.performerUpdate)
|
variables: performerInput as GQL.PerformerUpdateInput
|
||||||
|
});
|
||||||
|
if (result.data?.performerUpdate)
|
||||||
setPerformer(result.data?.performerUpdate);
|
setPerformer(result.data?.performerUpdate);
|
||||||
} else {
|
} else {
|
||||||
const result = await createPerformer({variables: performerInput as GQL.PerformerCreateInput});
|
const result = await createPerformer({
|
||||||
if(result.data?.performerCreate) {
|
variables: performerInput as GQL.PerformerCreateInput
|
||||||
|
});
|
||||||
|
if (result.data?.performerCreate) {
|
||||||
setPerformer(result.data.performerCreate);
|
setPerformer(result.data.performerCreate);
|
||||||
history.push(`/performers/${result.data.performerCreate.id}`);
|
history.push(`/performers/${result.data.performerCreate.id}`);
|
||||||
}
|
}
|
||||||
@@ -77,7 +84,7 @@ export const Performer: React.FC = () => {
|
|||||||
async function onDelete() {
|
async function onDelete() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
await deletePerformer({variables: { id }});
|
await deletePerformer({ variables: { id } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toast.error(e);
|
Toast.error(e);
|
||||||
}
|
}
|
||||||
@@ -112,7 +119,7 @@ export const Performer: React.FC = () => {
|
|||||||
<PerformerScenesPanel performer={performer} />
|
<PerformerScenesPanel performer={performer} />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab eventKey="edit" title="Edit">
|
<Tab eventKey="edit" title="Edit">
|
||||||
{ renderEditPanel() }
|
{renderEditPanel()}
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab eventKey="operations" title="Operations">
|
<Tab eventKey="operations" title="Operations">
|
||||||
<PerformerOperationsPanel performer={performer} />
|
<PerformerOperationsPanel performer={performer} />
|
||||||
@@ -151,7 +158,7 @@ export const Performer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFavorite(v : boolean) {
|
function setFavorite(v: boolean) {
|
||||||
performer.favorite = v;
|
performer.favorite = v;
|
||||||
onSave(performer);
|
onSave(performer);
|
||||||
}
|
}
|
||||||
@@ -165,7 +172,7 @@ export const Performer: React.FC = () => {
|
|||||||
<Icon icon={icon} />
|
<Icon icon={icon} />
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +182,8 @@ export const Performer: React.FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
className={performer.favorite ? "favorite" : "not-favorite"}
|
className={performer.favorite ? "favorite" : "not-favorite"}
|
||||||
onClick={() => setFavorite(!performer.favorite)}
|
onClick={() => setFavorite(!performer.favorite)}
|
||||||
><Icon icon="heart" />
|
>
|
||||||
|
<Icon icon="heart" />
|
||||||
</Button>
|
</Button>
|
||||||
{maybeRenderURL(performer.url ?? undefined)}
|
{maybeRenderURL(performer.url ?? undefined)}
|
||||||
{/* TODO - render instagram and twitter links with icons */}
|
{/* TODO - render instagram and twitter links with icons */}
|
||||||
@@ -188,7 +196,7 @@ export const Performer: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="columns is-multiline no-spacing">
|
<div className="columns is-multiline no-spacing">
|
||||||
<div className="column is-half details-image-container">
|
<div className="column is-half details-image-container">
|
||||||
<img className="performer" src={imagePreview} alt='Performer' />
|
<img className="performer" src={imagePreview} alt="Performer" />
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-half details-detail-container">
|
<div className="column is-half details-detail-container">
|
||||||
{renderTabs()}
|
{renderTabs()}
|
||||||
@@ -197,7 +205,7 @@ export const Performer: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const photos = [{src: imagePreview || "", caption: "Image"}];
|
const photos = [{ src: imagePreview || "", caption: "Image" }];
|
||||||
|
|
||||||
function openLightbox() {
|
function openLightbox() {
|
||||||
setLightboxIsOpen(true);
|
setLightboxIsOpen(true);
|
||||||
@@ -216,7 +224,7 @@ export const Performer: React.FC = () => {
|
|||||||
<div id="performer-page">
|
<div id="performer-page">
|
||||||
<div className="details-image-container">
|
<div className="details-image-container">
|
||||||
<Button variant="link" onClick={openLightbox}>
|
<Button variant="link" onClick={openLightbox}>
|
||||||
<img className="performer" src={imagePreview} alt='Performer' />
|
<img className="performer" src={imagePreview} alt="Performer" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="performer-head">
|
<div className="performer-head">
|
||||||
@@ -229,9 +237,7 @@ export const Performer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="performer-body">
|
<div className="performer-body">
|
||||||
<div className="details-detail-container">
|
<div className="details-detail-container">{renderTabs()}</div>
|
||||||
{renderTabs()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Lightbox
|
<Lightbox
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
/* eslint-disable react/no-this-in-sfc */
|
/* eslint-disable react/no-this-in-sfc */
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Button, Form, Popover, OverlayTrigger, Spinner, Table } from "react-bootstrap";
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
Popover,
|
||||||
|
OverlayTrigger,
|
||||||
|
Spinner,
|
||||||
|
Table
|
||||||
|
} from "react-bootstrap";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import {
|
import { Icon, Modal, ScrapePerformerSuggest } from "src/components/Shared";
|
||||||
Icon,
|
|
||||||
Modal,
|
|
||||||
ScrapePerformerSuggest
|
|
||||||
} from "src/components/Shared";
|
|
||||||
import { ImageUtils, TableUtils } from "src/utils";
|
import { ImageUtils, TableUtils } from "src/utils";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
|
|
||||||
@@ -16,20 +19,34 @@ interface IPerformerDetails {
|
|||||||
performer: Partial<GQL.PerformerDataFragment>;
|
performer: Partial<GQL.PerformerDataFragment>;
|
||||||
isNew?: boolean;
|
isNew?: boolean;
|
||||||
isEditing?: boolean;
|
isEditing?: boolean;
|
||||||
onSave?: (performer: Partial<GQL.PerformerCreateInput> | Partial<GQL.PerformerUpdateInput>) => void;
|
onSave?: (
|
||||||
|
performer:
|
||||||
|
| Partial<GQL.PerformerCreateInput>
|
||||||
|
| Partial<GQL.PerformerUpdateInput>
|
||||||
|
) => void;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
onImageChange?: (image: string) => void;
|
onImageChange?: (image: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer, isNew, isEditing, onSave, onDelete, onImageChange }) => {
|
export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||||
|
performer,
|
||||||
|
isNew,
|
||||||
|
isEditing,
|
||||||
|
onSave,
|
||||||
|
onDelete,
|
||||||
|
onImageChange
|
||||||
|
}) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
|
|
||||||
// Editing state
|
// Editing state
|
||||||
const [isDisplayingScraperDialog, setIsDisplayingScraperDialog] = useState<GQL.Scraper>();
|
const [isDisplayingScraperDialog, setIsDisplayingScraperDialog] = useState<
|
||||||
const [scrapePerformerDetails, setScrapePerformerDetails] = useState<GQL.ScrapedPerformerDataFragment>();
|
GQL.Scraper
|
||||||
|
>();
|
||||||
|
const [scrapePerformerDetails, setScrapePerformerDetails] = useState<
|
||||||
|
GQL.ScrapedPerformerDataFragment
|
||||||
|
>();
|
||||||
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
// Editing performer state
|
// Editing performer state
|
||||||
const [image, setImage] = useState<string>();
|
const [image, setImage] = useState<string>();
|
||||||
const [name, setName] = useState<string>();
|
const [name, setName] = useState<string>();
|
||||||
@@ -55,7 +72,6 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
const Scrapers = StashService.useListPerformerScrapers();
|
const Scrapers = StashService.useListPerformerScrapers();
|
||||||
const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);
|
const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);
|
||||||
|
|
||||||
|
|
||||||
function updatePerformerEditState(
|
function updatePerformerEditState(
|
||||||
state: Partial<GQL.PerformerDataFragment | GQL.ScrapedPerformer>
|
state: Partial<GQL.PerformerDataFragment | GQL.ScrapedPerformer>
|
||||||
) {
|
) {
|
||||||
@@ -91,19 +107,17 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEditing)
|
if (isEditing) ImageUtils.usePasteImage(onImageLoad);
|
||||||
ImageUtils.usePasteImage(onImageLoad);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newQueryableScrapers = (Scrapers?.data?.listPerformerScrapers ?? []).filter(s => (
|
const newQueryableScrapers = (
|
||||||
s.performer?.supported_scrapes.includes(GQL.ScrapeType.Name)
|
Scrapers?.data?.listPerformerScrapers ?? []
|
||||||
));
|
).filter(s => s.performer?.supported_scrapes.includes(GQL.ScrapeType.Name));
|
||||||
|
|
||||||
setQueryableScrapers(newQueryableScrapers);
|
setQueryableScrapers(newQueryableScrapers);
|
||||||
}, [Scrapers]);
|
}, [Scrapers]);
|
||||||
|
|
||||||
if (isLoading)
|
if (isLoading) return <Spinner animation="border" variant="light" />;
|
||||||
return <Spinner animation="border" variant="light" />;
|
|
||||||
|
|
||||||
function getPerformerInput() {
|
function getPerformerInput() {
|
||||||
const performerInput: Partial<
|
const performerInput: Partial<
|
||||||
@@ -138,9 +152,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
ImageUtils.onImageChange(event, onImageLoad);
|
ImageUtils.onImageChange(event, onImageLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDisplayFreeOnesDialog(
|
function onDisplayFreeOnesDialog(scraper: GQL.Scraper) {
|
||||||
scraper: GQL.Scraper
|
|
||||||
) {
|
|
||||||
setIsDisplayingScraperDialog(scraper);
|
setIsDisplayingScraperDialog(scraper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,9 +224,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
? queryableScrapers.map(s => (
|
? queryableScrapers.map(s => (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() =>
|
onClick={() => onDisplayFreeOnesDialog(s)}
|
||||||
onDisplayFreeOnesDialog(s)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{s.name}
|
{s.name}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -299,15 +309,30 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button className="edit-button" variant="primary" onClick={() => onSave?.(getPerformerInput())}>Save</Button>
|
<Button
|
||||||
{!isNew ? <Button className="edit-button" variant="danger" onClick={() => setIsDeleteAlertOpen(true)}>Delete</Button> : ''}
|
className="edit-button"
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => onSave?.(getPerformerInput())}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
{!isNew ? (
|
||||||
|
<Button
|
||||||
|
className="edit-button"
|
||||||
|
variant="danger"
|
||||||
|
onClick={() => setIsDeleteAlertOpen(true)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
{renderScraperMenu()}
|
{renderScraperMenu()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function renderDeleteAlert() {
|
function renderDeleteAlert() {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -316,34 +341,50 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({ performer,
|
|||||||
accept={{ text: "Delete", variant: "danger", onClick: onDelete }}
|
accept={{ text: "Delete", variant: "danger", onClick: onDelete }}
|
||||||
cancel={{ onClick: () => setIsDeleteAlertOpen(false) }}
|
cancel={{ onClick: () => setIsDeleteAlertOpen(false) }}
|
||||||
>
|
>
|
||||||
<p>
|
<p>Are you sure you want to delete {name}?</p>
|
||||||
Are you sure you want to delete {name}?
|
|
||||||
</p>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderImageInput() {
|
function renderImageInput() {
|
||||||
if (!isEditing) { return; }
|
if (!isEditing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Image</td>
|
<td>Image</td>
|
||||||
<td><Form.Control type="file" onChange={onImageChangeHandler} accept=".jpg,.jpeg" /></td>
|
<td>
|
||||||
|
<Form.Control
|
||||||
|
type="file"
|
||||||
|
onChange={onImageChangeHandler}
|
||||||
|
accept=".jpg,.jpeg"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeRenderName() {
|
function maybeRenderName() {
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return TableUtils.renderInputGroup(
|
return TableUtils.renderInputGroup({
|
||||||
{title: "Name", value: name, isEditing: !!isEditing, placeholder: "Name", onChange: setName});
|
title: "Name",
|
||||||
|
value: name,
|
||||||
|
isEditing: !!isEditing,
|
||||||
|
placeholder: "Name",
|
||||||
|
onChange: setName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeRenderAliases() {
|
function maybeRenderAliases() {
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return TableUtils.renderInputGroup(
|
return TableUtils.renderInputGroup({
|
||||||
{title: "Aliases", value: aliases, isEditing: !!isEditing, placeholder: "Aliases", onChange: setAliases});
|
title: "Aliases",
|
||||||
|
value: aliases,
|
||||||
|
isEditing: !!isEditing,
|
||||||
|
placeholder: "Aliases",
|
||||||
|
onChange: setAliases
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import {
|
import { Button } from "react-bootstrap";
|
||||||
Button,
|
|
||||||
} from "react-bootstrap";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
|
|
||||||
interface IPerformerOperationsProps {
|
interface IPerformerOperationsProps {
|
||||||
performer: Partial<GQL.PerformerDataFragment>
|
performer: Partial<GQL.PerformerDataFragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PerformerOperationsPanel: React.FC<IPerformerOperationsProps> = ({ performer }) => {
|
export const PerformerOperationsPanel: React.FC<IPerformerOperationsProps> = ({
|
||||||
|
performer
|
||||||
|
}) => {
|
||||||
const Toast = useToast();
|
const Toast = useToast();
|
||||||
|
|
||||||
async function onAutoTag() {
|
async function onAutoTag() {
|
||||||
@@ -18,7 +18,7 @@ export const PerformerOperationsPanel: React.FC<IPerformerOperationsProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await StashService.queryMetadataAutoTag({ performers: [performer.id]});
|
await StashService.queryMetadataAutoTag({ performers: [performer.id] });
|
||||||
Toast.success({ content: "Started auto tagging" });
|
Toast.success({ content: "Started auto tagging" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toast.error(e);
|
Toast.error(e);
|
||||||
|
|||||||
@@ -5,25 +5,30 @@ import { ListFilterModel } from "src/models/list-filter/filter";
|
|||||||
import { SceneList } from "../../scenes/SceneList";
|
import { SceneList } from "../../scenes/SceneList";
|
||||||
|
|
||||||
interface IPerformerDetailsProps {
|
interface IPerformerDetailsProps {
|
||||||
performer: Partial<GQL.PerformerDataFragment>
|
performer: Partial<GQL.PerformerDataFragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PerformerScenesPanel: React.FC<IPerformerDetailsProps> = ({ performer }) => {
|
export const PerformerScenesPanel: React.FC<IPerformerDetailsProps> = ({
|
||||||
|
performer
|
||||||
|
}) => {
|
||||||
function filterHook(filter: ListFilterModel) {
|
function filterHook(filter: ListFilterModel) {
|
||||||
const performerValue = {id: performer.id!, label: performer.name!};
|
const performerValue = { id: performer.id!, label: performer.name! };
|
||||||
// if performers is already present, then we modify it, otherwise add
|
// if performers is already present, then we modify it, otherwise add
|
||||||
let performerCriterion = filter.criteria.find((c) => {
|
let performerCriterion = filter.criteria.find(c => {
|
||||||
return c.type === "performers";
|
return c.type === "performers";
|
||||||
});
|
});
|
||||||
|
|
||||||
if (performerCriterion &&
|
if (
|
||||||
|
performerCriterion &&
|
||||||
(performerCriterion.modifier === GQL.CriterionModifier.IncludesAll ||
|
(performerCriterion.modifier === GQL.CriterionModifier.IncludesAll ||
|
||||||
performerCriterion.modifier === GQL.CriterionModifier.Includes)) {
|
performerCriterion.modifier === GQL.CriterionModifier.Includes)
|
||||||
|
) {
|
||||||
// add the performer if not present
|
// add the performer if not present
|
||||||
if (!performerCriterion.value.find((p : any) => {
|
if (
|
||||||
|
!performerCriterion.value.find((p: any) => {
|
||||||
return p.id === performer.id;
|
return p.id === performer.id;
|
||||||
})) {
|
})
|
||||||
|
) {
|
||||||
performerCriterion.value.push(performerValue);
|
performerCriterion.value.push(performerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,10 +43,5 @@ export const PerformerScenesPanel: React.FC<IPerformerDetailsProps> = ({ perform
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <SceneList subComponent filterHook={filterHook} />;
|
||||||
<SceneList
|
};
|
||||||
subComponent
|
|
||||||
filterHook={filterHook}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import {
|
import { FindPerformersQueryResult } from "src/core/generated-graphql";
|
||||||
FindPerformersQueryResult,
|
|
||||||
} from "src/core/generated-graphql";
|
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import { usePerformersList } from "src/hooks";
|
import { usePerformersList } from "src/hooks";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const config = StashService.useConfiguration();
|
const config = StashService.useConfiguration();
|
||||||
const showStudioAsText = config?.data?.configuration.interface.showStudioAsText ?? false;
|
const showStudioAsText =
|
||||||
|
config?.data?.configuration.interface.showStudioAsText ?? false;
|
||||||
|
|
||||||
function maybeRenderRatingBanner() {
|
function maybeRenderRatingBanner() {
|
||||||
if (!props.scene.rating) {
|
if (!props.scene.rating) {
|
||||||
@@ -52,7 +53,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
)}
|
)}
|
||||||
{ (props.scene.file.duration ?? 0) >= 1
|
{(props.scene.file.duration ?? 0) >= 1
|
||||||
? TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0)
|
? TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0)
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -221,7 +222,13 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
|||||||
: TextUtils.fileNameFromPath(props.scene.path)}
|
: TextUtils.fileNameFromPath(props.scene.path)}
|
||||||
</h4>
|
</h4>
|
||||||
<span>{props.scene.date}</span>
|
<span>{props.scene.date}</span>
|
||||||
<p>{TextUtils.truncate(props.scene.details ?? "", 100, "... (continued)")}</p>
|
<p>
|
||||||
|
{TextUtils.truncate(
|
||||||
|
props.scene.details ?? "",
|
||||||
|
100,
|
||||||
|
"... (continued)"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{maybeRenderPopoverButtonGroup()}
|
{maybeRenderPopoverButtonGroup()}
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
const [deleteScene] = StashService.useSceneDestroy(getSceneDeleteInput());
|
const [deleteScene] = StashService.useSceneDestroy(getSceneDeleteInput());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newQueryableScrapers = (Scrapers?.data?.listSceneScrapers ?? []).filter(s => (
|
const newQueryableScrapers = (
|
||||||
s.scene?.supported_scrapes.includes(GQL.ScrapeType.Fragment)
|
Scrapers?.data?.listSceneScrapers ?? []
|
||||||
));
|
).filter(s => s.scene?.supported_scrapes.includes(GQL.ScrapeType.Fragment));
|
||||||
|
|
||||||
setQueryableScrapers(newQueryableScrapers);
|
setQueryableScrapers(newQueryableScrapers);
|
||||||
}, [Scrapers]);
|
}, [Scrapers]);
|
||||||
@@ -108,7 +108,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await updateScene();
|
const result = await updateScene();
|
||||||
if(result.data?.sceneUpdate) {
|
if (result.data?.sceneUpdate) {
|
||||||
props.onUpdate(result.data.sceneUpdate);
|
props.onUpdate(result.data.sceneUpdate);
|
||||||
Toast.success({ content: "Updated scene" });
|
Toast.success({ content: "Updated scene" });
|
||||||
}
|
}
|
||||||
@@ -202,9 +202,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
ImageUtils.onImageChange(event, onImageLoad);
|
ImageUtils.onImageChange(event, onImageLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onScrapeClicked(
|
async function onScrapeClicked(scraper: GQL.Scraper) {
|
||||||
scraper: GQL.Scraper
|
|
||||||
) {
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await StashService.queryScrapeScene(
|
const result = await StashService.queryScrapeScene(
|
||||||
@@ -418,7 +416,11 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Collapse in={isCoverImageOpen}>
|
<Collapse in={isCoverImageOpen}>
|
||||||
<div>
|
<div>
|
||||||
<img className="scene-cover" src={coverImagePreview} alt="Scene cover" />
|
<img
|
||||||
|
className="scene-cover"
|
||||||
|
src={coverImagePreview}
|
||||||
|
alt="Scene cover"
|
||||||
|
/>
|
||||||
<Form.Group className="test" controlId="cover">
|
<Form.Group className="test" controlId="cover">
|
||||||
<Form.Control
|
<Form.Control
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Stream</td>
|
<td>Stream</td>
|
||||||
<td>
|
<td>
|
||||||
<a href={props.scene.paths.stream ?? ''}>{props.scene.paths.stream}</a>{" "}
|
<a href={props.scene.paths.stream ?? ""}>
|
||||||
|
{props.scene.paths.stream}
|
||||||
|
</a>{" "}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -51,7 +53,9 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
|||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>File Size</td>
|
<td>File Size</td>
|
||||||
<td>{TextUtils.fileSize(parseInt(props.scene.file.size ?? '0', 10))}</td>
|
<td>
|
||||||
|
{TextUtils.fileSize(parseInt(props.scene.file.size ?? "0", 10))}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,12 +114,16 @@ class SceneParserResult {
|
|||||||
public studioId: ParserResult<string> = new ParserResult();
|
public studioId: ParserResult<string> = new ParserResult();
|
||||||
public tags: ParserResult<GQL.Tag[]> = new ParserResult();
|
public tags: ParserResult<GQL.Tag[]> = new ParserResult();
|
||||||
public tagIds: ParserResult<string[]> = new ParserResult();
|
public tagIds: ParserResult<string[]> = new ParserResult();
|
||||||
public performers: ParserResult<Partial<GQL.Performer>[]> = new ParserResult();
|
public performers: ParserResult<
|
||||||
|
Partial<GQL.Performer>[]
|
||||||
|
> = new ParserResult();
|
||||||
public performerIds: ParserResult<string[]> = new ParserResult();
|
public performerIds: ParserResult<string[]> = new ParserResult();
|
||||||
|
|
||||||
public scene: GQL.SlimSceneDataFragment;
|
public scene: GQL.SlimSceneDataFragment;
|
||||||
|
|
||||||
constructor(result: GQL.ParseSceneFilenamesQuery["parseSceneFilenames"]["results"][0]) {
|
constructor(
|
||||||
|
result: GQL.ParseSceneFilenamesQuery["parseSceneFilenames"]["results"][0]
|
||||||
|
) {
|
||||||
this.scene = result.scene;
|
this.scene = result.scene;
|
||||||
|
|
||||||
this.id = this.scene.id;
|
this.id = this.scene.id;
|
||||||
@@ -141,12 +145,15 @@ class SceneParserResult {
|
|||||||
|
|
||||||
if (result.performer_ids) {
|
if (result.performer_ids) {
|
||||||
this.performers.setValue(
|
this.performers.setValue(
|
||||||
(result.performer_ids ?? []).map(p => ({
|
(result.performer_ids ?? []).map(
|
||||||
|
p =>
|
||||||
|
({
|
||||||
id: p,
|
id: p,
|
||||||
name: "",
|
name: "",
|
||||||
favorite: false,
|
favorite: false,
|
||||||
image_path: ""
|
image_path: ""
|
||||||
} as GQL.Performer))
|
} as GQL.Performer)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +350,9 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
}, [parserInput]);
|
}, [parserInput]);
|
||||||
|
|
||||||
const parseResults = useCallback(
|
const parseResults = useCallback(
|
||||||
(results: GQL.ParseSceneFilenamesQuery["parseSceneFilenames"]["results"]) => {
|
(
|
||||||
|
results: GQL.ParseSceneFilenamesQuery["parseSceneFilenames"]["results"]
|
||||||
|
) => {
|
||||||
if (results) {
|
if (results) {
|
||||||
const result = results
|
const result = results
|
||||||
.map(r => {
|
.map(r => {
|
||||||
@@ -897,7 +906,9 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
fieldName="Title"
|
fieldName="Title"
|
||||||
className="parser-field-title"
|
className="parser-field-title"
|
||||||
parserResult={props.scene.title}
|
parserResult={props.scene.title}
|
||||||
onSetChanged={set => onTitleChanged(set, props.scene.title.value ?? undefined)}
|
onSetChanged={set =>
|
||||||
|
onTitleChanged(set, props.scene.title.value ?? undefined)
|
||||||
|
}
|
||||||
onValueChanged={value => onTitleChanged(props.scene.title.set, value)}
|
onValueChanged={value => onTitleChanged(props.scene.title.set, value)}
|
||||||
renderOriginalInputField={renderOriginalInputGroup}
|
renderOriginalInputField={renderOriginalInputGroup}
|
||||||
renderNewInputField={renderNewInputGroup}
|
renderNewInputField={renderNewInputGroup}
|
||||||
@@ -907,7 +918,9 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
fieldName="Date"
|
fieldName="Date"
|
||||||
className="parser-field-date"
|
className="parser-field-date"
|
||||||
parserResult={props.scene.date}
|
parserResult={props.scene.date}
|
||||||
onSetChanged={set => onDateChanged(set, props.scene.date.value ?? undefined)}
|
onSetChanged={set =>
|
||||||
|
onDateChanged(set, props.scene.date.value ?? undefined)
|
||||||
|
}
|
||||||
onValueChanged={value => onDateChanged(props.scene.date.set, value)}
|
onValueChanged={value => onDateChanged(props.scene.date.set, value)}
|
||||||
renderOriginalInputField={renderOriginalInputGroup}
|
renderOriginalInputField={renderOriginalInputGroup}
|
||||||
renderNewInputField={renderNewInputGroup}
|
renderNewInputField={renderNewInputGroup}
|
||||||
@@ -919,7 +932,10 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
parserResult={props.scene.performerIds}
|
parserResult={props.scene.performerIds}
|
||||||
originalParserResult={props.scene.performers}
|
originalParserResult={props.scene.performers}
|
||||||
onSetChanged={set =>
|
onSetChanged={set =>
|
||||||
onPerformerIdsChanged(set, props.scene.performerIds.value ?? undefined)
|
onPerformerIdsChanged(
|
||||||
|
set,
|
||||||
|
props.scene.performerIds.value ?? undefined
|
||||||
|
)
|
||||||
}
|
}
|
||||||
onValueChanged={value =>
|
onValueChanged={value =>
|
||||||
onPerformerIdsChanged(props.scene.performerIds.set, value)
|
onPerformerIdsChanged(props.scene.performerIds.set, value)
|
||||||
@@ -933,7 +949,9 @@ export const SceneFilenameParser: React.FC = () => {
|
|||||||
className="parser-field-tags"
|
className="parser-field-tags"
|
||||||
parserResult={props.scene.tagIds}
|
parserResult={props.scene.tagIds}
|
||||||
originalParserResult={props.scene.tags}
|
originalParserResult={props.scene.tags}
|
||||||
onSetChanged={set => onTagIdsChanged(set, props.scene.tagIds.value ?? undefined)}
|
onSetChanged={set =>
|
||||||
|
onTagIdsChanged(set, props.scene.tagIds.value ?? undefined)
|
||||||
|
}
|
||||||
onValueChanged={value =>
|
onValueChanged={value =>
|
||||||
onTagIdsChanged(props.scene.tagIds.set, value)
|
onTagIdsChanged(props.scene.tagIds.set, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ interface ISceneList {
|
|||||||
filterHook?: (filter: ListFilterModel) => ListFilterModel;
|
filterHook?: (filter: ListFilterModel) => ListFilterModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SceneList: React.FC<ISceneList> = ({ subComponent, filterHook }) => {
|
export const SceneList: React.FC<ISceneList> = ({
|
||||||
|
subComponent,
|
||||||
|
filterHook
|
||||||
|
}) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const otherOperations = [
|
const otherOperations = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import {
|
import { FindSceneMarkersQueryResult } from "src/core/generated-graphql";
|
||||||
FindSceneMarkersQueryResult
|
|
||||||
} from "src/core/generated-graphql";
|
|
||||||
import { StashService } from "src/core/StashService";
|
import { StashService } from "src/core/StashService";
|
||||||
import { NavUtils } from "src/utils";
|
import { NavUtils } from "src/utils";
|
||||||
import { useSceneMarkersList } from "src/hooks";
|
import { useSceneMarkersList } from "src/hooks";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import ApolloClient from "apollo-client";
|
import ApolloClient from "apollo-client";
|
||||||
import { WebSocketLink } from "apollo-link-ws";
|
import { WebSocketLink } from "apollo-link-ws";
|
||||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
import { InMemoryCache } from "apollo-cache-inmemory";
|
||||||
import { HttpLink } from 'apollo-link-http';
|
import { HttpLink } from "apollo-link-http";
|
||||||
import { split } from 'apollo-link';
|
import { split } from "apollo-link";
|
||||||
import { getMainDefinition } from "apollo-utilities";
|
import { getMainDefinition } from "apollo-utilities";
|
||||||
import { ListFilterModel } from "../models/list-filter/filter";
|
import { ListFilterModel } from "../models/list-filter/filter";
|
||||||
import * as GQL from "./generated-graphql";
|
import * as GQL from "./generated-graphql";
|
||||||
@@ -45,7 +45,10 @@ export class StashService {
|
|||||||
const link = split(
|
const link = split(
|
||||||
({ query }) => {
|
({ query }) => {
|
||||||
const definition = getMainDefinition(query);
|
const definition = getMainDefinition(query);
|
||||||
return definition.kind === "OperationDefinition" && definition.operation === "subscription";
|
return (
|
||||||
|
definition.kind === "OperationDefinition" &&
|
||||||
|
definition.operation === "subscription"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
wsLink,
|
wsLink,
|
||||||
httpLink
|
httpLink
|
||||||
@@ -267,7 +270,9 @@ export class StashService {
|
|||||||
return GQL.useAllStudiosForFilterQuery();
|
return GQL.useAllStudiosForFilterQuery();
|
||||||
}
|
}
|
||||||
public static useValidGalleriesForScene(sceneId: string) {
|
public static useValidGalleriesForScene(sceneId: string) {
|
||||||
return GQL.useValidGalleriesForSceneQuery({ variables: { scene_id: sceneId } });
|
return GQL.useValidGalleriesForSceneQuery({
|
||||||
|
variables: { scene_id: sceneId }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public static useStats() {
|
public static useStats() {
|
||||||
return GQL.useStatsQuery();
|
return GQL.useStatsQuery();
|
||||||
@@ -275,8 +280,12 @@ export class StashService {
|
|||||||
public static useVersion() {
|
public static useVersion() {
|
||||||
return GQL.useVersionQuery();
|
return GQL.useVersionQuery();
|
||||||
}
|
}
|
||||||
public static useLatestVersion() { return GQL.useLatestVersionQuery({ notifyOnNetworkStatusChange: true, errorPolicy: 'ignore' }); }
|
public static useLatestVersion() {
|
||||||
|
return GQL.useLatestVersionQuery({
|
||||||
|
notifyOnNetworkStatusChange: true,
|
||||||
|
errorPolicy: "ignore"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static useConfiguration() {
|
public static useConfiguration() {
|
||||||
return GQL.useConfigurationQuery();
|
return GQL.useConfigurationQuery();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
const [filter, setFilter] = useState<ListFilterModel>(
|
const [filter, setFilter] = useState<ListFilterModel>(
|
||||||
new ListFilterModel(
|
new ListFilterModel(
|
||||||
options.filterMode,
|
options.filterMode,
|
||||||
options.subComponent ? '' : queryString.parse(history.location.search)
|
options.subComponent ? "" : queryString.parse(history.location.search)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
@@ -318,8 +318,10 @@ export const useScenesList = (props: IListHookOptions<FindScenesQueryResult>) =>
|
|||||||
...props,
|
...props,
|
||||||
filterMode: FilterMode.Scenes,
|
filterMode: FilterMode.Scenes,
|
||||||
useData: StashService.useFindScenes,
|
useData: StashService.useFindScenes,
|
||||||
getData: (result: FindScenesQueryResult) => result?.data?.findScenes?.scenes ?? [],
|
getData: (result: FindScenesQueryResult) =>
|
||||||
getCount: (result: FindScenesQueryResult) => result?.data?.findScenes?.count ?? 0
|
result?.data?.findScenes?.scenes ?? [],
|
||||||
|
getCount: (result: FindScenesQueryResult) =>
|
||||||
|
result?.data?.findScenes?.count ?? 0
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useSceneMarkersList = (
|
export const useSceneMarkersList = (
|
||||||
@@ -335,7 +337,9 @@ export const useSceneMarkersList = (
|
|||||||
result?.data?.findSceneMarkers?.count ?? 0
|
result?.data?.findSceneMarkers?.count ?? 0
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useGalleriesList = (props: IListHookOptions<FindGalleriesQueryResult>) =>
|
export const useGalleriesList = (
|
||||||
|
props: IListHookOptions<FindGalleriesQueryResult>
|
||||||
|
) =>
|
||||||
useList<FindGalleriesQueryResult, GalleryDataFragment>({
|
useList<FindGalleriesQueryResult, GalleryDataFragment>({
|
||||||
...props,
|
...props,
|
||||||
filterMode: FilterMode.Galleries,
|
filterMode: FilterMode.Galleries,
|
||||||
@@ -346,16 +350,22 @@ export const useGalleriesList = (props: IListHookOptions<FindGalleriesQueryResul
|
|||||||
result?.data?.findGalleries?.count ?? 0
|
result?.data?.findGalleries?.count ?? 0
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useStudiosList = (props: IListHookOptions<FindStudiosQueryResult>) =>
|
export const useStudiosList = (
|
||||||
|
props: IListHookOptions<FindStudiosQueryResult>
|
||||||
|
) =>
|
||||||
useList<FindStudiosQueryResult, StudioDataFragment>({
|
useList<FindStudiosQueryResult, StudioDataFragment>({
|
||||||
...props,
|
...props,
|
||||||
filterMode: FilterMode.Studios,
|
filterMode: FilterMode.Studios,
|
||||||
useData: StashService.useFindStudios,
|
useData: StashService.useFindStudios,
|
||||||
getData: (result: FindStudiosQueryResult) => result?.data?.findStudios?.studios ?? [],
|
getData: (result: FindStudiosQueryResult) =>
|
||||||
getCount: (result: FindStudiosQueryResult) => result?.data?.findStudios?.count ?? 0
|
result?.data?.findStudios?.studios ?? [],
|
||||||
|
getCount: (result: FindStudiosQueryResult) =>
|
||||||
|
result?.data?.findStudios?.count ?? 0
|
||||||
});
|
});
|
||||||
|
|
||||||
export const usePerformersList = (props: IListHookOptions<FindPerformersQueryResult>) =>
|
export const usePerformersList = (
|
||||||
|
props: IListHookOptions<FindPerformersQueryResult>
|
||||||
|
) =>
|
||||||
useList<FindPerformersQueryResult, PerformerDataFragment>({
|
useList<FindPerformersQueryResult, PerformerDataFragment>({
|
||||||
...props,
|
...props,
|
||||||
filterMode: FilterMode.Performers,
|
filterMode: FilterMode.Performers,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
|
|
||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier } from "src/core/generated-graphql";
|
||||||
import { DurationUtils } from 'src/utils';
|
import { DurationUtils } from "src/utils";
|
||||||
import { ILabeledId, ILabeledValue } from "../types";
|
import { ILabeledId, ILabeledValue } from "../types";
|
||||||
|
|
||||||
export type CriterionType =
|
export type CriterionType =
|
||||||
@@ -38,7 +38,8 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
return "Rating";
|
return "Rating";
|
||||||
case "resolution":
|
case "resolution":
|
||||||
return "Resolution";
|
return "Resolution";
|
||||||
case "duration": return "Duration";
|
case "duration":
|
||||||
|
return "Duration";
|
||||||
case "favorite":
|
case "favorite":
|
||||||
return "Favorite";
|
return "Favorite";
|
||||||
case "hasMarkers":
|
case "hasMarkers":
|
||||||
@@ -149,7 +150,10 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
|
|
||||||
let valueString = "";
|
let valueString = "";
|
||||||
|
|
||||||
if (this.modifier !== CriterionModifier.IsNull && this.modifier !== CriterionModifier.NotNull) {
|
if (
|
||||||
|
this.modifier !== CriterionModifier.IsNull &&
|
||||||
|
this.modifier !== CriterionModifier.NotNull
|
||||||
|
) {
|
||||||
valueString = this.getLabelValue();
|
valueString = this.getLabelValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,12 +272,12 @@ export class DurationCriterion extends Criterion<number, number> {
|
|||||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||||
Criterion.getModifierOption(CriterionModifier.LessThan),
|
Criterion.getModifierOption(CriterionModifier.LessThan)
|
||||||
];
|
];
|
||||||
public options: number[] | undefined;
|
public options: number[] | undefined;
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
|
|
||||||
constructor(type : CriterionType, parameterName?: string, options? : number[]) {
|
constructor(type: CriterionType, parameterName?: string, options?: number[]) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import * as GQL from "src/core/generated-graphql";
|
|||||||
import { ILabeledId } from "../types";
|
import { ILabeledId } from "../types";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
||||||
|
|
||||||
export class TagsCriterion extends Criterion<
|
export class TagsCriterion extends Criterion<GQL.Tag, ILabeledId[]> {
|
||||||
GQL.Tag,
|
|
||||||
ILabeledId[]
|
|
||||||
> {
|
|
||||||
public type: CriterionType;
|
public type: CriterionType;
|
||||||
public parameterName: string;
|
public parameterName: string;
|
||||||
public modifier = GQL.CriterionModifier.IncludesAll;
|
public modifier = GQL.CriterionModifier.IncludesAll;
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
return new RatingCriterion();
|
return new RatingCriterion();
|
||||||
case "resolution":
|
case "resolution":
|
||||||
return new ResolutionCriterion();
|
return new ResolutionCriterion();
|
||||||
case "duration": return new DurationCriterion(type, type);
|
case "duration":
|
||||||
|
return new DurationCriterion(type, type);
|
||||||
case "favorite":
|
case "favorite":
|
||||||
return new FavoriteCriterion();
|
return new FavoriteCriterion();
|
||||||
case "hasMarkers":
|
case "hasMarkers":
|
||||||
@@ -41,7 +42,8 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "studios":
|
case "studios":
|
||||||
return new StudiosCriterion();
|
return new StudiosCriterion();
|
||||||
|
|
||||||
case "birth_year": return new NumberCriterion(type, type);
|
case "birth_year":
|
||||||
|
return new NumberCriterion(type, type);
|
||||||
case "age": {
|
case "age": {
|
||||||
const ret = new NumberCriterion(type, type);
|
const ret = new NumberCriterion(type, type);
|
||||||
// null/not null doesn't make sense for these criteria
|
// null/not null doesn't make sense for these criteria
|
||||||
|
|||||||
@@ -299,7 +299,10 @@ export class ListFilterModel {
|
|||||||
}
|
}
|
||||||
case "duration": {
|
case "duration": {
|
||||||
const durationCrit = criterion as DurationCriterion;
|
const durationCrit = criterion as DurationCriterion;
|
||||||
result.duration = { value: durationCrit.value, modifier: durationCrit.modifier }
|
result.duration = {
|
||||||
|
value: durationCrit.value,
|
||||||
|
modifier: durationCrit.modifier
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "hasMarkers":
|
case "hasMarkers":
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import TextUtils from "./text";
|
import TextUtils from "./text";
|
||||||
|
|
||||||
const secondsToString = (seconds : number) => {
|
const secondsToString = (seconds: number) => {
|
||||||
let ret = TextUtils.secondsToTimestamp(seconds);
|
let ret = TextUtils.secondsToTimestamp(seconds);
|
||||||
|
|
||||||
if (ret.startsWith("00:")) {
|
if (ret.startsWith("00:")) {
|
||||||
@@ -12,9 +12,9 @@ const secondsToString = (seconds : number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
};
|
||||||
|
|
||||||
const stringToSeconds = (v : string) => {
|
const stringToSeconds = (v: string) => {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ const stringToSeconds = (v : string) => {
|
|||||||
|
|
||||||
let seconds = 0;
|
let seconds = 0;
|
||||||
let factor = 1;
|
let factor = 1;
|
||||||
while(splits.length > 0) {
|
while (splits.length > 0) {
|
||||||
const thisSplit = splits.pop();
|
const thisSplit = splits.pop();
|
||||||
if (thisSplit === undefined) {
|
if (thisSplit === undefined) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -43,7 +43,7 @@ const stringToSeconds = (v : string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return seconds;
|
return seconds;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
secondsToString,
|
secondsToString,
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ export { default as ImageUtils } from "./image";
|
|||||||
export { default as NavUtils } from "./navigation";
|
export { default as NavUtils } from "./navigation";
|
||||||
export { default as TableUtils } from "./table";
|
export { default as TableUtils } from "./table";
|
||||||
export { default as TextUtils } from "./text";
|
export { default as TextUtils } from "./text";
|
||||||
export { default as DurationUtils } from './duration';
|
export { default as DurationUtils } from "./duration";
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ const renderInputGroup = (options: {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
isEditing: boolean;
|
isEditing: boolean;
|
||||||
asURL?: boolean,
|
asURL?: boolean;
|
||||||
urlPrefix?: string,
|
urlPrefix?: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
}) => (
|
}) => (
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const fileNameFromPath = (path: string) => {
|
|||||||
return path.replace(/^.*[\\/]/, "");
|
return path.replace(/^.*[\\/]/, "");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAge = (dateString?: string|null, fromDateString?: string) => {
|
const getAge = (dateString?: string | null, fromDateString?: string) => {
|
||||||
if (!dateString) return 0;
|
if (!dateString) return 0;
|
||||||
|
|
||||||
const birthdate = new Date(dateString);
|
const birthdate = new Date(dateString);
|
||||||
|
|||||||
Reference in New Issue
Block a user