mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Update prettier to v2.0.1 and enable for SCSS (#420)
This commit is contained in:
@@ -21,6 +21,16 @@
|
||||
"warn",
|
||||
{ "prefixWithI": "always" }
|
||||
],
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"js": "never",
|
||||
"jsx": "never",
|
||||
"ts": "never",
|
||||
"tsx": "never"
|
||||
}
|
||||
],
|
||||
"import/named": "off",
|
||||
"import/namespace": "off",
|
||||
"import/default": "off",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"extends": "stylelint-config-prettier",
|
||||
"rules": {
|
||||
"indentation": 2,
|
||||
"at-rule-empty-line-before": [ "always", {
|
||||
@@ -33,7 +34,6 @@
|
||||
"declaration-block-semicolon-space-before": "never",
|
||||
"declaration-block-single-line-max-declarations": 1,
|
||||
"declaration-block-trailing-semicolon": "always",
|
||||
"declaration-colon-newline-after": "always-multi-line",
|
||||
"declaration-colon-space-after": "always-single-line",
|
||||
"declaration-colon-space-before": "never",
|
||||
"declaration-no-important": true,
|
||||
@@ -61,7 +61,6 @@
|
||||
"no-descending-specificity": null,
|
||||
"no-invalid-double-slash-comments": true,
|
||||
"no-missing-end-of-source-newline": true,
|
||||
"number-leading-zero": "never",
|
||||
"number-max-precision": 2,
|
||||
"number-no-trailing-zeros": true,
|
||||
"order/order": [
|
||||
@@ -87,7 +86,6 @@
|
||||
"string-quotes": "double",
|
||||
"time-min-milliseconds": 100,
|
||||
"unit-blacklist": ["em"],
|
||||
"value-list-comma-newline-after": "always-multi-line",
|
||||
"value-list-comma-space-after": "always-single-line",
|
||||
"value-list-comma-space-before": "never",
|
||||
"value-no-vendor-prefix": true
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"lint": "yarn lint:css && yarn lint:js",
|
||||
"lint:js": "eslint --cache src/**/*.{ts,tsx}",
|
||||
"lint:css": "stylelint 'src/**/*.scss'",
|
||||
"format": "prettier --write \"src/**/!(generated-graphql).{js,jsx,ts,tsx}\"",
|
||||
"format": "prettier --write \"src/**/!(generated-graphql).{js,jsx,ts,tsx,scss}\"",
|
||||
"gqlgen": "gql-gen --config codegen.yml",
|
||||
"extract": "NODE_ENV=development extract-messages -l=en,de -o src/locale -d en --flat false 'src/**/!(*.test).tsx'"
|
||||
},
|
||||
@@ -80,7 +80,7 @@
|
||||
"@typescript-eslint/parser": "^2.16.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-airbnb-typescript": "^6.3.1",
|
||||
"eslint-config-prettier": "^6.9.0",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-plugin-import": "^2.20.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-react": "^7.18.0",
|
||||
@@ -88,9 +88,10 @@
|
||||
"extract-react-intl-messages": "^2.3.5",
|
||||
"node-sass": "4.13.1",
|
||||
"postcss-safe-parser": "^4.0.1",
|
||||
"prettier": "1.19.1",
|
||||
"prettier": "2.0.2",
|
||||
"react-scripts": "^3.3.1",
|
||||
"stylelint": "^13.0.0",
|
||||
"stylelint-config-prettier": "^8.0.1",
|
||||
"stylelint-order": "^4.0.0",
|
||||
"typescript": "^3.7.5"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class ErrorBoundary extends React.Component<
|
||||
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
errorInfo,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DisplayMode } from "src/models/list-filter/types";
|
||||
|
||||
export const GalleryList: React.FC = () => {
|
||||
const listData = useGalleriesList({
|
||||
renderContent
|
||||
renderContent,
|
||||
});
|
||||
|
||||
function renderContent(
|
||||
@@ -31,7 +31,7 @@ export const GalleryList: React.FC = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{result.data.findGalleries.galleries.map(gallery => (
|
||||
{result.data.findGalleries.galleries.map((gallery) => (
|
||||
<tr key={gallery.id}>
|
||||
<td>
|
||||
<Link to={`/galleries/${gallery.id}`}>
|
||||
@@ -41,9 +41,7 @@ export const GalleryList: React.FC = () => {
|
||||
className="w-100 w-sm-auto"
|
||||
src={`${gallery.files[0].path}?thumb=true`}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)}
|
||||
) : undefined}
|
||||
</Link>
|
||||
</td>
|
||||
<td className="d-none d-sm-block">
|
||||
|
||||
@@ -29,14 +29,14 @@ export const GalleryViewer: FunctionComponent<IProps> = ({ gallery }) => {
|
||||
setCurrentImage(currentImage + 1);
|
||||
}
|
||||
|
||||
const photos = gallery.files.map(file => ({
|
||||
const photos = gallery.files.map((file) => ({
|
||||
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` || "",
|
||||
width: 1,
|
||||
height: 1
|
||||
height: 1,
|
||||
}));
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Criterion,
|
||||
CriterionType,
|
||||
DurationCriterion,
|
||||
CriterionValue
|
||||
CriterionValue,
|
||||
} from "src/models/list-filter/criteria/criterion";
|
||||
import { NoneCriterion } from "src/models/list-filter/criteria/none";
|
||||
import { makeCriteria } from "src/models/list-filter/criteria/utils";
|
||||
@@ -118,7 +118,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
||||
onChange={onChangedModifierSelect}
|
||||
value={criterion.modifier}
|
||||
>
|
||||
{criterion.modifierOptions.map(c => (
|
||||
{criterion.modifierOptions.map((c) => (
|
||||
<option key={c.value} value={c.value}>
|
||||
{c.label}
|
||||
</option>
|
||||
@@ -149,15 +149,15 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
||||
<FilterSelect
|
||||
type={criterion.type}
|
||||
isMulti
|
||||
onSelect={items => {
|
||||
onSelect={(items) => {
|
||||
const newCriterion = _.cloneDeep(criterion);
|
||||
newCriterion.value = items.map(i => ({
|
||||
newCriterion.value = items.map((i) => ({
|
||||
id: i.id,
|
||||
label: i.name!
|
||||
label: i.name!,
|
||||
}));
|
||||
setCriterion(newCriterion);
|
||||
}}
|
||||
ids={criterion.value.map(labeled => labeled.id)}
|
||||
ids={criterion.value.map((labeled) => labeled.id)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
||||
onChange={onChangedSingleSelect}
|
||||
value={criterion.value.toString()}
|
||||
>
|
||||
{criterion.options.map(c => (
|
||||
{criterion.options.map((c) => (
|
||||
<option key={c.toString()} value={c.toString()}>
|
||||
{c}
|
||||
</option>
|
||||
@@ -215,7 +215,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
||||
onChange={onChangedCriteriaType}
|
||||
value={criterion.type}
|
||||
>
|
||||
{props.filter.criterionOptions.map(c => (
|
||||
{props.filter.criterionOptions.map((c) => (
|
||||
<option key={c.value} value={c.value}>
|
||||
{c.label}
|
||||
</option>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Form,
|
||||
OverlayTrigger,
|
||||
Tooltip,
|
||||
SafeAnchor
|
||||
SafeAnchor,
|
||||
} from "react-bootstrap";
|
||||
|
||||
import { Icon } from "src/components/Shared";
|
||||
@@ -106,7 +106,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
}
|
||||
|
||||
function renderSortByOptions() {
|
||||
return props.filter.sortByOptions.map(option => (
|
||||
return props.filter.sortByOptions.map((option) => (
|
||||
<Dropdown.Item
|
||||
onClick={onChangeSortBy}
|
||||
key={option}
|
||||
@@ -138,7 +138,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
return "Wall";
|
||||
}
|
||||
}
|
||||
return props.filter.displayModeOptions.map(option => (
|
||||
return props.filter.displayModeOptions.map((option) => (
|
||||
<OverlayTrigger
|
||||
key={option}
|
||||
overlay={
|
||||
@@ -157,7 +157,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
}
|
||||
|
||||
function renderFilterTags() {
|
||||
return props.filter.criteria.map(criterion => (
|
||||
return props.filter.criteria.map((criterion) => (
|
||||
<Badge
|
||||
className="tag-item"
|
||||
variant="secondary"
|
||||
@@ -219,7 +219,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
const options = [renderSelectAll(), renderSelectNone()];
|
||||
|
||||
if (props.otherOperations) {
|
||||
props.otherOperations.forEach(o => {
|
||||
props.otherOperations.forEach((o) => {
|
||||
options.push(
|
||||
<Dropdown.Item
|
||||
key={o.text}
|
||||
@@ -285,7 +285,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
||||
value={props.filter.itemsPerPage.toString()}
|
||||
className="btn-secondary filter-item col-1 d-none d-sm-inline"
|
||||
>
|
||||
{PAGE_SIZE_OPTIONS.map(s => (
|
||||
{PAGE_SIZE_OPTIONS.map((s) => (
|
||||
<option value={s} key={s}>
|
||||
{s}
|
||||
</option>
|
||||
|
||||
@@ -12,7 +12,7 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
itemsPerPage,
|
||||
currentPage,
|
||||
totalItems,
|
||||
onChangePage
|
||||
onChangePage,
|
||||
}) => {
|
||||
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Pagination: React.FC<IPaginationProps> = ({
|
||||
}
|
||||
|
||||
const pages = [...Array(endPage + 1 - startPage).keys()].map(
|
||||
i => startPage + i
|
||||
(i) => startPage + i
|
||||
);
|
||||
|
||||
const calculatePageClass = (buttonPage: number) => {
|
||||
|
||||
@@ -17,38 +17,38 @@ const menuItems: IMenuItem[] = [
|
||||
{
|
||||
icon: "play-circle",
|
||||
messageID: "scenes",
|
||||
href: "/scenes"
|
||||
href: "/scenes",
|
||||
},
|
||||
{
|
||||
href: "/movies",
|
||||
icon: "film",
|
||||
messageID: "movies"
|
||||
messageID: "movies",
|
||||
},
|
||||
{
|
||||
href: "/scenes/markers",
|
||||
icon: "map-marker-alt",
|
||||
messageID: "markers"
|
||||
messageID: "markers",
|
||||
},
|
||||
{
|
||||
href: "/galleries",
|
||||
icon: "image",
|
||||
messageID: "galleries"
|
||||
messageID: "galleries",
|
||||
},
|
||||
{
|
||||
href: "/performers",
|
||||
icon: "user",
|
||||
messageID: "performers"
|
||||
messageID: "performers",
|
||||
},
|
||||
{
|
||||
href: "/studios",
|
||||
icon: "video",
|
||||
messageID: "studios"
|
||||
messageID: "studios",
|
||||
},
|
||||
{
|
||||
href: "/tags",
|
||||
icon: "tag",
|
||||
messageID: "tags"
|
||||
}
|
||||
messageID: "tags",
|
||||
},
|
||||
];
|
||||
|
||||
export const MainNavbar: React.FC = () => {
|
||||
@@ -127,7 +127,7 @@ export const MainNavbar: React.FC = () => {
|
||||
<Navbar.Toggle className="order-0" />
|
||||
<Navbar.Collapse className="order-3 order-md-1">
|
||||
<Nav className="mr-md-auto">
|
||||
{menuItems.map(i => (
|
||||
{menuItems.map((i) => (
|
||||
<Nav.Link eventKey={i.href} as="div" key={i.href}>
|
||||
<LinkContainer
|
||||
activeClassName="active"
|
||||
|
||||
@@ -7,7 +7,7 @@ import cx from "classnames";
|
||||
import {
|
||||
DetailsEditNavbar,
|
||||
LoadingIndicator,
|
||||
Modal
|
||||
Modal,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { Table, Form } from "react-bootstrap";
|
||||
@@ -117,7 +117,7 @@ export const Movie: React.FC = () => {
|
||||
synopsis,
|
||||
url,
|
||||
front_image: frontImage,
|
||||
back_image: backImage
|
||||
back_image: backImage,
|
||||
};
|
||||
|
||||
if (!isNew) {
|
||||
@@ -183,7 +183,7 @@ export const Movie: React.FC = () => {
|
||||
<div className="row">
|
||||
<div
|
||||
className={cx("movie-details", "col", {
|
||||
"col ml-sm-5": !isNew
|
||||
"col ml-sm-5": !isNew,
|
||||
})}
|
||||
>
|
||||
{isNew && <h2>Add Movie</h2>}
|
||||
@@ -198,38 +198,38 @@ export const Movie: React.FC = () => {
|
||||
title: "Name",
|
||||
value: movie.name ?? "",
|
||||
isEditing: !!isEditing,
|
||||
onChange: setName
|
||||
onChange: setName,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Aliases",
|
||||
value: aliases,
|
||||
isEditing,
|
||||
onChange: setAliases
|
||||
onChange: setAliases,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Duration",
|
||||
value: duration,
|
||||
isEditing,
|
||||
onChange: setDuration
|
||||
onChange: setDuration,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Date (YYYY-MM-DD)",
|
||||
value: date,
|
||||
isEditing,
|
||||
onChange: setDate
|
||||
onChange: setDate,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Director",
|
||||
value: director,
|
||||
isEditing,
|
||||
onChange: setDirector
|
||||
onChange: setDirector,
|
||||
})}
|
||||
{TableUtils.renderHtmlSelect({
|
||||
title: "Rating",
|
||||
value: rating,
|
||||
isEditing,
|
||||
onChange: (value: string) => setRating(value),
|
||||
selectOptions: ["", "1", "2", "3", "4", "5"]
|
||||
selectOptions: ["", "1", "2", "3", "4", "5"],
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
@@ -12,7 +12,7 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({ movie }) => {
|
||||
function filterHook(filter: ListFilterModel) {
|
||||
const movieValue = { id: movie.id!, label: movie.name! };
|
||||
// if movie is already present, then we modify it, otherwise add
|
||||
let movieCriterion = filter.criteria.find(c => {
|
||||
let movieCriterion = filter.criteria.find((c) => {
|
||||
return c.type === "movies";
|
||||
}) as MoviesCriterion;
|
||||
|
||||
@@ -23,7 +23,7 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({ movie }) => {
|
||||
) {
|
||||
// add the movie if not present
|
||||
if (
|
||||
!movieCriterion.value.find(p => {
|
||||
!movieCriterion.value.find((p) => {
|
||||
return p.id === movie.id;
|
||||
})
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MovieCard } from "./MovieCard";
|
||||
|
||||
export const MovieList: React.FC = () => {
|
||||
const listData = useMoviesList({
|
||||
renderContent
|
||||
renderContent,
|
||||
});
|
||||
|
||||
function renderContent(
|
||||
@@ -20,7 +20,7 @@ export const MovieList: React.FC = () => {
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<div className="row justify-content-center">
|
||||
{result.data.findMovies.movies.map(p => (
|
||||
{result.data.findMovies.movies.map((p) => (
|
||||
<MovieCard key={p.id} movie={p} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ interface IPerformerCardProps {
|
||||
|
||||
export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
||||
performer,
|
||||
ageFromDate
|
||||
ageFromDate,
|
||||
}) => {
|
||||
const age = TextUtils.age(performer.birthdate, ageFromDate);
|
||||
const ageString = `${age} years old${ageFromDate ? " in this scene." : "."}`;
|
||||
|
||||
@@ -62,13 +62,13 @@ export const Performer: React.FC = () => {
|
||||
try {
|
||||
if (!isNew) {
|
||||
const result = await updatePerformer({
|
||||
variables: performerInput as GQL.PerformerUpdateInput
|
||||
variables: performerInput as GQL.PerformerUpdateInput,
|
||||
});
|
||||
if (result.data?.performerUpdate)
|
||||
setPerformer(result.data?.performerUpdate);
|
||||
} else {
|
||||
const result = await createPerformer({
|
||||
variables: performerInput as GQL.PerformerCreateInput
|
||||
variables: performerInput as GQL.PerformerCreateInput,
|
||||
});
|
||||
if (result.data?.performerCreate) {
|
||||
setPerformer(result.data.performerCreate);
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Modal,
|
||||
ImageInput,
|
||||
ScrapePerformerSuggest,
|
||||
LoadingIndicator
|
||||
LoadingIndicator,
|
||||
} from "src/components/Shared";
|
||||
import { ImageUtils, TableUtils } from "src/utils";
|
||||
import { useToast } from "src/hooks";
|
||||
@@ -33,7 +33,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
isEditing,
|
||||
onSave,
|
||||
onDelete,
|
||||
onImageChange
|
||||
onImageChange,
|
||||
}) => {
|
||||
const Toast = useToast();
|
||||
|
||||
@@ -93,7 +93,11 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
setUrl(state.url ?? undefined);
|
||||
setTwitter(state.twitter ?? undefined);
|
||||
setInstagram(state.instagram ?? undefined);
|
||||
setGender(StashService.genderToString((state as GQL.PerformerDataFragment).gender ?? undefined));
|
||||
setGender(
|
||||
StashService.genderToString(
|
||||
(state as GQL.PerformerDataFragment).gender ?? undefined
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function updatePerformerEditStateFromScraper(
|
||||
@@ -128,7 +132,9 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
useEffect(() => {
|
||||
const newQueryableScrapers = (
|
||||
Scrapers?.data?.listPerformerScrapers ?? []
|
||||
).filter(s => s.performer?.supported_scrapes.includes(GQL.ScrapeType.Name));
|
||||
).filter((s) =>
|
||||
s.performer?.supported_scrapes.includes(GQL.ScrapeType.Name)
|
||||
);
|
||||
|
||||
setQueryableScrapers(newQueryableScrapers);
|
||||
}, [Scrapers]);
|
||||
@@ -156,7 +162,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
twitter,
|
||||
instagram,
|
||||
image,
|
||||
gender: StashService.stringToGender(gender)
|
||||
gender: StashService.stringToGender(gender),
|
||||
};
|
||||
|
||||
if (!isNew) {
|
||||
@@ -226,7 +232,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
value: ethnicity,
|
||||
isEditing: !!isEditing,
|
||||
placeholder: "Ethnicity",
|
||||
onChange: setEthnicity
|
||||
onChange: setEthnicity,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -240,7 +246,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
<Popover.Content>
|
||||
<div>
|
||||
{queryableScrapers
|
||||
? queryableScrapers.map(s => (
|
||||
? queryableScrapers.map((s) => (
|
||||
<div key={s.name}>
|
||||
<Button
|
||||
key={s.name}
|
||||
@@ -278,7 +284,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
scraperId={
|
||||
isDisplayingScraperDialog ? isDisplayingScraperDialog.id : ""
|
||||
}
|
||||
onSelectPerformer={query => setScrapePerformerDetails(query)}
|
||||
onSelectPerformer={(query) => setScrapePerformerDetails(query)}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
@@ -288,8 +294,8 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
function urlScrapable(scrapedUrl: string) {
|
||||
return (
|
||||
!!scrapedUrl &&
|
||||
(Scrapers?.data?.listPerformerScrapers ?? []).some(s =>
|
||||
(s?.performer?.urls ?? []).some(u => scrapedUrl.includes(u))
|
||||
(Scrapers?.data?.listPerformerScrapers ?? []).some((s) =>
|
||||
(s?.performer?.urls ?? []).some((u) => scrapedUrl.includes(u))
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -383,7 +389,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
value: name,
|
||||
isEditing: !!isEditing,
|
||||
placeholder: "Name",
|
||||
onChange: setName
|
||||
onChange: setName,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -395,7 +401,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
value: aliases,
|
||||
isEditing: !!isEditing,
|
||||
placeholder: "Aliases",
|
||||
onChange: setAliases
|
||||
onChange: setAliases,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -424,69 +430,69 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
title: "Birthdate",
|
||||
value: birthdate,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setBirthdate
|
||||
onChange: setBirthdate,
|
||||
})}
|
||||
{renderEthnicity()}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Eye Color",
|
||||
value: eyeColor,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setEyeColor
|
||||
onChange: setEyeColor,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Country",
|
||||
value: country,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setCountry
|
||||
onChange: setCountry,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Height (cm)",
|
||||
value: height,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setHeight
|
||||
onChange: setHeight,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Measurements",
|
||||
value: measurements,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setMeasurements
|
||||
onChange: setMeasurements,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Fake Tits",
|
||||
value: fakeTits,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setFakeTits
|
||||
onChange: setFakeTits,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Career Length",
|
||||
value: careerLength,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setCareerLength
|
||||
onChange: setCareerLength,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Tattoos",
|
||||
value: tattoos,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setTattoos
|
||||
onChange: setTattoos,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Piercings",
|
||||
value: piercings,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setPiercings
|
||||
onChange: setPiercings,
|
||||
})}
|
||||
{renderURLField()}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Twitter",
|
||||
value: twitter,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setTwitter
|
||||
onChange: setTwitter,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "Instagram",
|
||||
value: instagram,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setInstagram
|
||||
onChange: setInstagram,
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IPerformerOperationsProps {
|
||||
}
|
||||
|
||||
export const PerformerOperationsPanel: React.FC<IPerformerOperationsProps> = ({
|
||||
performer
|
||||
performer,
|
||||
}) => {
|
||||
const Toast = useToast();
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ interface IPerformerDetailsProps {
|
||||
}
|
||||
|
||||
export const PerformerScenesPanel: React.FC<IPerformerDetailsProps> = ({
|
||||
performer
|
||||
performer,
|
||||
}) => {
|
||||
function filterHook(filter: ListFilterModel) {
|
||||
const performerValue = { id: performer.id!, label: performer.name! };
|
||||
// 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";
|
||||
}) as PerformersCriterion;
|
||||
|
||||
@@ -25,7 +25,7 @@ export const PerformerScenesPanel: React.FC<IPerformerDetailsProps> = ({
|
||||
) {
|
||||
// add the performer if not present
|
||||
if (
|
||||
!performerCriterion.value.find(p => {
|
||||
!performerCriterion.value.find((p) => {
|
||||
return p.id === performer.id;
|
||||
})
|
||||
) {
|
||||
|
||||
@@ -14,13 +14,13 @@ export const PerformerList: React.FC = () => {
|
||||
const otherOperations = [
|
||||
{
|
||||
text: "Open Random",
|
||||
onClick: getRandom
|
||||
}
|
||||
onClick: getRandom,
|
||||
},
|
||||
];
|
||||
|
||||
const listData = usePerformersList({
|
||||
otherOperations,
|
||||
renderContent
|
||||
renderContent,
|
||||
});
|
||||
|
||||
async function getRandom(
|
||||
@@ -56,7 +56,7 @@ export const PerformerList: React.FC = () => {
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<div className="row justify-content-center">
|
||||
{result.data.findPerformers.performers.map(p => (
|
||||
{result.data.findPerformers.performers.map((p) => (
|
||||
<PerformerCard key={p.id} performer={p} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.scrape-url-button {
|
||||
color: $text-color;
|
||||
float: right;
|
||||
margin-right: .5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
margin-left: 10px;
|
||||
|
||||
.not-favorite {
|
||||
color: rgba(191, 204, 214, .5);
|
||||
color: rgba(191, 204, 214, 0.5);
|
||||
}
|
||||
|
||||
.favorite {
|
||||
|
||||
@@ -52,7 +52,7 @@ export class ParserField {
|
||||
ParserField.DDMMYYYY,
|
||||
ParserField.DDMMYY,
|
||||
ParserField.MMDDYYYY,
|
||||
ParserField.MMDDYY
|
||||
ParserField.MMDDYY,
|
||||
];
|
||||
|
||||
static fullDateFields = [
|
||||
@@ -61,6 +61,6 @@ export class ParserField {
|
||||
ParserField.DDMMYYYY,
|
||||
ParserField.DDMMYY,
|
||||
ParserField.MMDDYYYY,
|
||||
ParserField.MMDDYY
|
||||
ParserField.MMDDYY,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
Dropdown,
|
||||
DropdownButton,
|
||||
Form,
|
||||
InputGroup
|
||||
InputGroup,
|
||||
} from "react-bootstrap";
|
||||
import { ParserField } from "./ParserField";
|
||||
import { ShowFields } from "./ShowFields";
|
||||
@@ -15,43 +15,43 @@ const builtInRecipes = [
|
||||
ignoreWords: [],
|
||||
whitespaceCharacters: "",
|
||||
capitalizeTitle: false,
|
||||
description: "Filename"
|
||||
description: "Filename",
|
||||
},
|
||||
{
|
||||
pattern: "{title}.{ext}",
|
||||
ignoreWords: [],
|
||||
whitespaceCharacters: "",
|
||||
capitalizeTitle: false,
|
||||
description: "Without extension"
|
||||
description: "Without extension",
|
||||
},
|
||||
{
|
||||
pattern: "{}.{yy}.{mm}.{dd}.{title}.XXX.{}.{ext}",
|
||||
ignoreWords: [],
|
||||
whitespaceCharacters: ".",
|
||||
capitalizeTitle: true,
|
||||
description: ""
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
pattern: "{}.{yy}.{mm}.{dd}.{title}.{ext}",
|
||||
ignoreWords: [],
|
||||
whitespaceCharacters: ".",
|
||||
capitalizeTitle: true,
|
||||
description: ""
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
pattern: "{title}.XXX.{}.{ext}",
|
||||
ignoreWords: [],
|
||||
whitespaceCharacters: ".",
|
||||
capitalizeTitle: true,
|
||||
description: ""
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
pattern: "{}.{yy}.{mm}.{dd}.{title}.{i}.{ext}",
|
||||
ignoreWords: ["cz", "fr"],
|
||||
whitespaceCharacters: ".",
|
||||
capitalizeTitle: true,
|
||||
description: "Foreign language"
|
||||
}
|
||||
description: "Foreign language",
|
||||
},
|
||||
];
|
||||
|
||||
export interface IParserInput {
|
||||
@@ -102,7 +102,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
||||
capitalizeTitle,
|
||||
page: 1,
|
||||
pageSize: props.input.pageSize,
|
||||
findClicked: props.input.findClicked
|
||||
findClicked: props.input.findClicked,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
<DropdownButton id="parser-field-select" title="Add Field">
|
||||
{validFields.map(item => (
|
||||
{validFields.map((item) => (
|
||||
<Dropdown.Item
|
||||
key={item.field}
|
||||
onSelect={() => addParserField(item)}
|
||||
@@ -207,7 +207,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
||||
id="recipe-select"
|
||||
title="Select Parser Recipe"
|
||||
>
|
||||
{builtInRecipes.map(item => (
|
||||
{builtInRecipes.map((item) => (
|
||||
<Dropdown.Item
|
||||
key={item.pattern}
|
||||
onSelect={() => setParserRecipe(item)}
|
||||
@@ -222,7 +222,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
||||
<Form.Group>
|
||||
<ShowFields
|
||||
fields={props.showFields}
|
||||
onShowFieldsChanged={fields => props.setShowFields(fields)}
|
||||
onShowFieldsChanged={(fields) => props.setShowFields(fields)}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -239,7 +239,7 @@ export const ParserInput: React.FC<IParserInputProps> = (
|
||||
defaultValue={props.input.pageSize}
|
||||
className="col-1 filter-item"
|
||||
>
|
||||
{PAGE_SIZE_OPTIONS.map(val => (
|
||||
{PAGE_SIZE_OPTIONS.map((val) => (
|
||||
<option key={val} value={val}>
|
||||
{val}
|
||||
</option>
|
||||
|
||||
@@ -19,7 +19,7 @@ const initialParserInput = {
|
||||
capitalizeTitle: true,
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
findClicked: false
|
||||
findClicked: false,
|
||||
};
|
||||
|
||||
const initialShowFieldsState = new Map<string, boolean>([
|
||||
@@ -27,7 +27,7 @@ const initialShowFieldsState = new Map<string, boolean>([
|
||||
["Date", true],
|
||||
["Performers", true],
|
||||
["Tags", true],
|
||||
["Studio", true]
|
||||
["Studio", true],
|
||||
]);
|
||||
|
||||
export const SceneFilenameParser: React.FC = () => {
|
||||
@@ -60,7 +60,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
const dateSet =
|
||||
pattern.includes("{date}") ||
|
||||
pattern.includes("{dd}") || // don't worry about other partial date fields since this should be implied
|
||||
ParserField.fullDateFields.some(f => {
|
||||
ParserField.fullDateFields.some((f) => {
|
||||
return pattern.includes(`{${f.field}}`);
|
||||
});
|
||||
const performerSet = pattern.includes("{performer}");
|
||||
@@ -72,7 +72,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
["Date", dateSet],
|
||||
["Performers", performerSet],
|
||||
["Tags", tagSet],
|
||||
["Studio", studioSet]
|
||||
["Studio", studioSet],
|
||||
]);
|
||||
|
||||
setShowFields(newShowFields);
|
||||
@@ -84,10 +84,10 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
) => {
|
||||
if (results) {
|
||||
const result = results
|
||||
.map(r => {
|
||||
.map((r) => {
|
||||
return new SceneParserResult(r);
|
||||
})
|
||||
.filter(r => !!r) as SceneParserResult[];
|
||||
.filter((r) => !!r) as SceneParserResult[];
|
||||
|
||||
setParserResult(result);
|
||||
determineFieldsToHide();
|
||||
@@ -106,24 +106,24 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
page: parserInput.page,
|
||||
per_page: parserInput.pageSize,
|
||||
sort: "path",
|
||||
direction: GQL.SortDirectionEnum.Asc
|
||||
direction: GQL.SortDirectionEnum.Asc,
|
||||
};
|
||||
|
||||
const parserInputData = {
|
||||
ignoreWords: parserInput.ignoreWords,
|
||||
whitespaceCharacters: parserInput.whitespaceCharacters,
|
||||
capitalizeTitle: parserInput.capitalizeTitle
|
||||
capitalizeTitle: parserInput.capitalizeTitle,
|
||||
};
|
||||
|
||||
StashService.queryParseSceneFilenames(parserFilter, parserInputData)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const result = response.data.parseSceneFilenames;
|
||||
if (result) {
|
||||
parseResults(result.results);
|
||||
setTotalItems(result.count);
|
||||
}
|
||||
})
|
||||
.catch(err => Toast.error(err))
|
||||
.catch((err) => Toast.error(err))
|
||||
.finally(() => setIsLoading(false));
|
||||
}
|
||||
}, [parserInput, parseResults, Toast]);
|
||||
@@ -152,8 +152,8 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
|
||||
function getScenesUpdateData() {
|
||||
return parserResult
|
||||
.filter(result => result.isChanged())
|
||||
.map(result => result.toSceneUpdateInput());
|
||||
.filter((result) => result.isChanged())
|
||||
.map((result) => result.toSceneUpdateInput());
|
||||
}
|
||||
|
||||
async function onApply() {
|
||||
@@ -170,19 +170,19 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const newAllTitleSet = !parserResult.some(r => {
|
||||
const newAllTitleSet = !parserResult.some((r) => {
|
||||
return !r.title.isSet;
|
||||
});
|
||||
const newAllDateSet = !parserResult.some(r => {
|
||||
const newAllDateSet = !parserResult.some((r) => {
|
||||
return !r.date.isSet;
|
||||
});
|
||||
const newAllPerformerSet = !parserResult.some(r => {
|
||||
const newAllPerformerSet = !parserResult.some((r) => {
|
||||
return !r.performers.isSet;
|
||||
});
|
||||
const newAllTagSet = !parserResult.some(r => {
|
||||
const newAllTagSet = !parserResult.some((r) => {
|
||||
return !r.tags.isSet;
|
||||
});
|
||||
const newAllStudioSet = !parserResult.some(r => {
|
||||
const newAllStudioSet = !parserResult.some((r) => {
|
||||
return !r.studio.isSet;
|
||||
});
|
||||
|
||||
@@ -196,7 +196,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
function onSelectAllTitleSet(selected: boolean) {
|
||||
const newResult = [...parserResult];
|
||||
|
||||
newResult.forEach(r => {
|
||||
newResult.forEach((r) => {
|
||||
r.title.isSet = selected;
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
function onSelectAllDateSet(selected: boolean) {
|
||||
const newResult = [...parserResult];
|
||||
|
||||
newResult.forEach(r => {
|
||||
newResult.forEach((r) => {
|
||||
r.date.isSet = selected;
|
||||
});
|
||||
|
||||
@@ -218,7 +218,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
function onSelectAllPerformerSet(selected: boolean) {
|
||||
const newResult = [...parserResult];
|
||||
|
||||
newResult.forEach(r => {
|
||||
newResult.forEach((r) => {
|
||||
r.performers.isSet = selected;
|
||||
});
|
||||
|
||||
@@ -229,7 +229,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
function onSelectAllTagSet(selected: boolean) {
|
||||
const newResult = [...parserResult];
|
||||
|
||||
newResult.forEach(r => {
|
||||
newResult.forEach((r) => {
|
||||
r.tags.isSet = selected;
|
||||
});
|
||||
|
||||
@@ -240,7 +240,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
function onSelectAllStudioSet(selected: boolean) {
|
||||
const newResult = [...parserResult];
|
||||
|
||||
newResult.forEach(r => {
|
||||
newResult.forEach((r) => {
|
||||
r.studio.isSet = selected;
|
||||
});
|
||||
|
||||
@@ -305,11 +305,11 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{parserResult.map(scene => (
|
||||
{parserResult.map((scene) => (
|
||||
<SceneParserRow
|
||||
scene={scene}
|
||||
key={scene.id}
|
||||
onChange={changedScene => onChange(scene, changedScene)}
|
||||
onChange={(changedScene) => onChange(scene, changedScene)}
|
||||
showFields={showFields}
|
||||
/>
|
||||
))}
|
||||
@@ -320,7 +320,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
currentPage={parserInput.page}
|
||||
itemsPerPage={parserInput.pageSize}
|
||||
totalItems={totalItems}
|
||||
onChangePage={page => onPageChanged(page)}
|
||||
onChangePage={(page) => onPageChanged(page)}
|
||||
/>
|
||||
<Button variant="primary" onClick={onApply}>
|
||||
Apply
|
||||
@@ -334,7 +334,7 @@ export const SceneFilenameParser: React.FC = () => {
|
||||
<h4>Scene Filename Parser</h4>
|
||||
<ParserInput
|
||||
input={parserInput}
|
||||
onFind={input => onFindClicked(input)}
|
||||
onFind={(input) => onFindClicked(input)}
|
||||
onPageSizeChanged={onPageSizeChanged}
|
||||
showFields={showFields}
|
||||
setShowFields={setShowFields}
|
||||
|
||||
@@ -3,12 +3,12 @@ import _ from "lodash";
|
||||
import { Form } from "react-bootstrap";
|
||||
import {
|
||||
ParseSceneFilenamesQuery,
|
||||
SlimSceneDataFragment
|
||||
SlimSceneDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import {
|
||||
PerformerSelect,
|
||||
TagSelect,
|
||||
StudioSelect
|
||||
StudioSelect,
|
||||
} from "src/components/Shared";
|
||||
import { TextUtils } from "src/utils";
|
||||
|
||||
@@ -51,8 +51,8 @@ export class SceneParserResult {
|
||||
this.filename = TextUtils.fileNameFromPath(this.scene.path);
|
||||
this.title.setOriginalValue(this.scene.title ?? undefined);
|
||||
this.date.setOriginalValue(this.scene.date ?? undefined);
|
||||
this.performers.setOriginalValue(this.scene.performers.map(p => p.id));
|
||||
this.tags.setOriginalValue(this.scene.tags.map(t => t.id));
|
||||
this.performers.setOriginalValue(this.scene.performers.map((p) => p.id));
|
||||
this.tags.setOriginalValue(this.scene.tags.map((t) => t.id));
|
||||
this.studio.setOriginalValue(this.scene.studio?.id);
|
||||
|
||||
this.title.setValue(result.title ?? undefined);
|
||||
@@ -82,10 +82,10 @@ export class SceneParserResult {
|
||||
studio_id: this.studio.isSet ? this.studio.value : this.scene.studio?.id,
|
||||
performer_ids: this.performers.isSet
|
||||
? this.performers.value
|
||||
: this.scene.performers.map(performer => performer.id),
|
||||
: this.scene.performers.map((performer) => performer.id),
|
||||
tag_ids: this.tags.isSet
|
||||
? this.tags.value
|
||||
: this.scene.tags.map(tag => tag.id)
|
||||
: this.scene.tags.map((tag) => tag.id),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -165,8 +165,8 @@ function SceneParserPerformerField(props: ISceneParserFieldProps<string[]>) {
|
||||
<PerformerSelect isDisabled isMulti ids={originalPerformers} />
|
||||
<PerformerSelect
|
||||
isMulti
|
||||
onSelect={items => {
|
||||
maybeValueChanged(items.map(i => i.id));
|
||||
onSelect={(items) => {
|
||||
maybeValueChanged(items.map((i) => i.id));
|
||||
}}
|
||||
ids={newPerformers}
|
||||
/>
|
||||
@@ -201,8 +201,8 @@ function SceneParserTagField(props: ISceneParserFieldProps<string[]>) {
|
||||
<TagSelect isDisabled isMulti ids={originalTags} />
|
||||
<TagSelect
|
||||
isMulti
|
||||
onSelect={items => {
|
||||
maybeValueChanged(items.map(i => i.id));
|
||||
onSelect={(items) => {
|
||||
maybeValueChanged(items.map((i) => i.id));
|
||||
}}
|
||||
ids={newTags}
|
||||
/>
|
||||
@@ -238,7 +238,7 @@ function SceneParserStudioField(props: ISceneParserFieldProps<string>) {
|
||||
<Form.Group className={props.className}>
|
||||
<StudioSelect isDisabled ids={originalStudio} />
|
||||
<StudioSelect
|
||||
onSelect={items => {
|
||||
onSelect={(items) => {
|
||||
maybeValueChanged(items[0].id);
|
||||
}}
|
||||
ids={newStudio}
|
||||
@@ -304,10 +304,10 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
||||
fieldName="Title"
|
||||
className="parser-field-title"
|
||||
parserResult={props.scene.title}
|
||||
onSetChanged={isSet =>
|
||||
onSetChanged={(isSet) =>
|
||||
onTitleChanged(isSet, props.scene.title.value ?? "")
|
||||
}
|
||||
onValueChanged={value =>
|
||||
onValueChanged={(value) =>
|
||||
onTitleChanged(props.scene.title.isSet, value)
|
||||
}
|
||||
/>
|
||||
@@ -318,10 +318,12 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
||||
fieldName="Date"
|
||||
className="parser-field-date"
|
||||
parserResult={props.scene.date}
|
||||
onSetChanged={isSet =>
|
||||
onSetChanged={(isSet) =>
|
||||
onDateChanged(isSet, props.scene.date.value ?? "")
|
||||
}
|
||||
onValueChanged={value => onDateChanged(props.scene.date.isSet, value)}
|
||||
onValueChanged={(value) =>
|
||||
onDateChanged(props.scene.date.isSet, value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{props.showFields.get("Performers") && (
|
||||
@@ -331,10 +333,10 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
||||
className="parser-field-performers"
|
||||
parserResult={props.scene.performers}
|
||||
originalParserResult={props.scene.performers}
|
||||
onSetChanged={set =>
|
||||
onSetChanged={(set) =>
|
||||
onPerformerIdsChanged(set, props.scene.performers.value ?? [])
|
||||
}
|
||||
onValueChanged={value =>
|
||||
onValueChanged={(value) =>
|
||||
onPerformerIdsChanged(props.scene.performers.isSet, value)
|
||||
}
|
||||
/>
|
||||
@@ -346,10 +348,10 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
||||
className="parser-field-tags"
|
||||
parserResult={props.scene.tags}
|
||||
originalParserResult={props.scene.tags}
|
||||
onSetChanged={isSet =>
|
||||
onSetChanged={(isSet) =>
|
||||
onTagIdsChanged(isSet, props.scene.tags.value ?? [])
|
||||
}
|
||||
onValueChanged={value =>
|
||||
onValueChanged={(value) =>
|
||||
onTagIdsChanged(props.scene.tags.isSet, value)
|
||||
}
|
||||
/>
|
||||
@@ -361,10 +363,10 @@ export const SceneParserRow = (props: ISceneParserRowProps) => {
|
||||
className="parser-field-studio"
|
||||
parserResult={props.scene.studio}
|
||||
originalParserResult={props.scene.studio}
|
||||
onSetChanged={set =>
|
||||
onSetChanged={(set) =>
|
||||
onStudioIdChanged(set, props.scene.studio.value ?? "")
|
||||
}
|
||||
onValueChanged={value =>
|
||||
onValueChanged={(value) =>
|
||||
onStudioIdChanged(props.scene.studio.isSet, value)
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
}
|
||||
|
||||
.form-control + .form-control {
|
||||
margin-top: .5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.badge-items {
|
||||
background-color: #e9ecef;
|
||||
margin-bottom: .25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const KeyMap = {
|
||||
NUM0: "0",
|
||||
NUM1: "1",
|
||||
NUM2: "2",
|
||||
SPACE: " "
|
||||
SPACE: " ",
|
||||
};
|
||||
|
||||
export class ScenePlayerImpl extends React.Component<
|
||||
@@ -49,7 +49,7 @@ export class ScenePlayerImpl extends React.Component<
|
||||
},
|
||||
SPACE: () => {
|
||||
this.onPause();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props: IScenePlayerProps) {
|
||||
@@ -63,15 +63,15 @@ export class ScenePlayerImpl extends React.Component<
|
||||
|
||||
this.state = {
|
||||
scrubberPosition: 0,
|
||||
config: this.makeJWPlayerConfig(props.scene)
|
||||
config: this.makeJWPlayerConfig(props.scene),
|
||||
};
|
||||
}
|
||||
|
||||
public UNSAFE_componentWillReceiveProps(props: IScenePlayerProps) {
|
||||
if (props.scene !== this.props.scene) {
|
||||
this.setState(state => ({
|
||||
this.setState((state) => ({
|
||||
...state,
|
||||
config: this.makeJWPlayerConfig(this.props.scene)
|
||||
config: this.makeJWPlayerConfig(this.props.scene),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -177,17 +177,17 @@ export class ScenePlayerImpl extends React.Component<
|
||||
tracks: [
|
||||
{
|
||||
file: scene.paths.vtt,
|
||||
kind: "thumbnails"
|
||||
kind: "thumbnails",
|
||||
},
|
||||
{
|
||||
file: scene.paths.chapters_vtt,
|
||||
kind: "chapters"
|
||||
}
|
||||
kind: "chapters",
|
||||
},
|
||||
],
|
||||
aspectratio: "16:9",
|
||||
width: "100%",
|
||||
floating: {
|
||||
dismissible: true
|
||||
dismissible: true,
|
||||
},
|
||||
cast: {},
|
||||
primary: "html5",
|
||||
@@ -199,7 +199,7 @@ export class ScenePlayerImpl extends React.Component<
|
||||
playbackRates: [0.75, 1, 1.5, 2, 3, 4],
|
||||
getDurationHook,
|
||||
seekHook,
|
||||
getCurrentTimeHook
|
||||
getCurrentTimeHook,
|
||||
};
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, {
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { Button } from "react-bootstrap";
|
||||
import axios from "axios";
|
||||
@@ -45,10 +45,7 @@ async function fetchSpriteInfo(vttPath: string) {
|
||||
const line = lines.shift();
|
||||
if (line !== undefined) {
|
||||
if (line.includes("#") && line.includes("=") && line.includes(",")) {
|
||||
const size = line
|
||||
.split("#")[1]
|
||||
.split("=")[1]
|
||||
.split(",");
|
||||
const size = line.split("#")[1].split("=")[1].split(",");
|
||||
item.x = Number(size[0]);
|
||||
item.y = Number(size[1]);
|
||||
item.w = Number(size[2]);
|
||||
@@ -120,13 +117,14 @@ export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (
|
||||
if (!scrubberSliderEl.current) {
|
||||
return;
|
||||
}
|
||||
scrubberSliderEl.current.style.transform = `translateX(${scrubberSliderEl
|
||||
.current.clientWidth / 2}px)`;
|
||||
scrubberSliderEl.current.style.transform = `translateX(${
|
||||
scrubberSliderEl.current.clientWidth / 2
|
||||
}px)`;
|
||||
}, [scrubberSliderEl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.scene.paths.vtt) return;
|
||||
fetchSpriteInfo(props.scene.paths.vtt).then(sprites => {
|
||||
fetchSpriteInfo(props.scene.paths.vtt).then((sprites) => {
|
||||
if (sprites) setSpriteItems(sprites);
|
||||
});
|
||||
}, [props.scene]);
|
||||
@@ -297,13 +295,13 @@ export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (
|
||||
tag!.clientWidth / 2;
|
||||
return {
|
||||
left: `${left}px`,
|
||||
height: 20
|
||||
height: 20,
|
||||
};
|
||||
}
|
||||
|
||||
return props.scene.scene_markers.map((marker, index) => {
|
||||
const dataAttrs = {
|
||||
"data-marker-id": index
|
||||
"data-marker-id": index,
|
||||
};
|
||||
return (
|
||||
<div
|
||||
@@ -332,13 +330,13 @@ export const ScenePlayerScrubber: React.FC<IScenePlayerScrubberProps> = (
|
||||
margin: "0px auto",
|
||||
backgroundPosition: `${-sprite.x}px ${-sprite.y}px`,
|
||||
backgroundImage: `url(${path})`,
|
||||
left: `${left}px`
|
||||
left: `${left}px`,
|
||||
};
|
||||
}
|
||||
|
||||
return spriteItems.map((spriteItem, index) => {
|
||||
const dataAttrs = {
|
||||
"data-sprite-item-id": index
|
||||
"data-sprite-item-id": index,
|
||||
};
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
cursor: grab;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
margin: 0 .5%;
|
||||
margin: 0 0.5%;
|
||||
overflow: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
position: relative;
|
||||
|
||||
@@ -20,7 +20,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
) => {
|
||||
const [previewPath, setPreviewPath] = useState<string>();
|
||||
const videoHoverHook = VideoHoverHook.useVideoHover({
|
||||
resetOnMouseLeave: false
|
||||
resetOnMouseLeave: false,
|
||||
});
|
||||
|
||||
const config = StashService.useConfiguration();
|
||||
@@ -83,7 +83,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
function maybeRenderTagPopoverButton() {
|
||||
if (props.scene.tags.length <= 0) return;
|
||||
|
||||
const popoverContent = props.scene.tags.map(tag => (
|
||||
const popoverContent = props.scene.tags.map((tag) => (
|
||||
<TagLink key={tag.id} tag={tag} />
|
||||
));
|
||||
|
||||
@@ -100,7 +100,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
function maybeRenderPerformerPopoverButton() {
|
||||
if (props.scene.performers.length <= 0) return;
|
||||
|
||||
const popoverContent = props.scene.performers.map(performer => (
|
||||
const popoverContent = props.scene.performers.map((performer) => (
|
||||
<div className="performer-tag-container row" key="performer">
|
||||
<Link
|
||||
to={`/performers/${performer.id}`}
|
||||
@@ -129,7 +129,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
function maybeRenderMoviePopoverButton() {
|
||||
if (props.scene.movies.length <= 0) return;
|
||||
|
||||
const popoverContent = props.scene.movies.map(sceneMovie => (
|
||||
const popoverContent = props.scene.movies.map((sceneMovie) => (
|
||||
<div className="movie-tag-container row" key="movie">
|
||||
<Link
|
||||
to={`/movies/${sceneMovie.movie.id}`}
|
||||
@@ -162,7 +162,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
function maybeRenderSceneMarkerPopoverButton() {
|
||||
if (props.scene.scene_markers.length <= 0) return;
|
||||
|
||||
const popoverContent = props.scene.scene_markers.map(marker => {
|
||||
const popoverContent = props.scene.scene_markers.map((marker) => {
|
||||
const markerPopover = { ...marker, scene: { id: props.scene.id } };
|
||||
return <TagLink key={marker.id} marker={markerPopover} />;
|
||||
});
|
||||
|
||||
@@ -12,13 +12,13 @@ interface IPrimaryTags {
|
||||
export const PrimaryTags: React.FC<IPrimaryTags> = ({
|
||||
sceneMarkers,
|
||||
onClickMarker,
|
||||
onEdit
|
||||
onEdit,
|
||||
}) => {
|
||||
if (!sceneMarkers?.length) return <div />;
|
||||
|
||||
const primaries: Record<string, GQL.Tag> = {};
|
||||
const primaryTags: Record<string, GQL.SceneMarkerDataFragment[]> = {};
|
||||
sceneMarkers.forEach(m => {
|
||||
sceneMarkers.forEach((m) => {
|
||||
if (primaryTags[m.primary_tag.id]) primaryTags[m.primary_tag.id].push(m);
|
||||
else {
|
||||
primaryTags[m.primary_tag.id] = [m];
|
||||
@@ -26,9 +26,9 @@ export const PrimaryTags: React.FC<IPrimaryTags> = ({
|
||||
}
|
||||
});
|
||||
|
||||
const primaryCards = Object.keys(primaryTags).map(id => {
|
||||
const markers = primaryTags[id].map(marker => {
|
||||
const tags = marker.tags.map(tag => (
|
||||
const primaryCards = Object.keys(primaryTags).map((id) => {
|
||||
const markers = primaryTags[id].map((marker) => {
|
||||
const tags = marker.tags.map((tag) => (
|
||||
<Badge key={tag.id} variant="secondary" className="tag-item">
|
||||
{tag.name}
|
||||
</Badge>
|
||||
|
||||
@@ -149,7 +149,7 @@ export const Scene: React.FC = () => {
|
||||
<Tab eventKey="scene-edit-panel" title="Edit">
|
||||
<SceneEditPanel
|
||||
scene={scene}
|
||||
onUpdate={newScene => setScene(newScene)}
|
||||
onUpdate={(newScene) => setScene(newScene)}
|
||||
onDelete={() => history.push("/scenes")}
|
||||
/>
|
||||
</Tab>
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ISceneDetailProps {
|
||||
scene: GQL.SceneDataFragment;
|
||||
}
|
||||
|
||||
export const SceneDetailPanel: React.FC<ISceneDetailProps> = props => {
|
||||
export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
|
||||
function renderDetails() {
|
||||
if (!props.scene.details || props.scene.details === "") return;
|
||||
return (
|
||||
@@ -21,7 +21,7 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = props => {
|
||||
|
||||
function renderTags() {
|
||||
if (props.scene.tags.length === 0) return;
|
||||
const tags = props.scene.tags.map(tag => (
|
||||
const tags = props.scene.tags.map((tag) => (
|
||||
<TagLink key={tag.id} tag={tag} />
|
||||
));
|
||||
return (
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
Modal,
|
||||
Icon,
|
||||
LoadingIndicator,
|
||||
ImageInput
|
||||
ImageInput,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { ImageUtils, TableUtils } from "src/utils";
|
||||
@@ -60,7 +60,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
useEffect(() => {
|
||||
const newQueryableScrapers = (
|
||||
Scrapers?.data?.listSceneScrapers ?? []
|
||||
).filter(s => s.scene?.supported_scrapes.includes(GQL.ScrapeType.Fragment));
|
||||
).filter((s) =>
|
||||
s.scene?.supported_scrapes.includes(GQL.ScrapeType.Fragment)
|
||||
);
|
||||
|
||||
setQueryableScrapers(newQueryableScrapers);
|
||||
}, [Scrapers]);
|
||||
@@ -69,7 +71,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
let changed = false;
|
||||
const newMap: MovieSceneIndexMap = new Map();
|
||||
if (movieIds) {
|
||||
movieIds.forEach(id => {
|
||||
movieIds.forEach((id) => {
|
||||
if (!movieSceneIndexes.has(id)) {
|
||||
changed = true;
|
||||
newMap.set(id, undefined);
|
||||
@@ -94,14 +96,14 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
}, [movieIds, movieSceneIndexes]);
|
||||
|
||||
function updateSceneEditState(state: Partial<GQL.SceneDataFragment>) {
|
||||
const perfIds = state.performers?.map(performer => performer.id);
|
||||
const tIds = state.tags ? state.tags.map(tag => tag.id) : undefined;
|
||||
const perfIds = state.performers?.map((performer) => performer.id);
|
||||
const tIds = state.tags ? state.tags.map((tag) => tag.id) : undefined;
|
||||
const moviIds = state.movies
|
||||
? state.movies.map(sceneMovie => sceneMovie.movie.id)
|
||||
? state.movies.map((sceneMovie) => sceneMovie.movie.id)
|
||||
: undefined;
|
||||
const movieSceneIdx: MovieSceneIndexMap = new Map();
|
||||
if (state.movies) {
|
||||
state.movies.forEach(m => {
|
||||
state.movies.forEach((m) => {
|
||||
movieSceneIdx.set(m.movie.id, m.scene_index ?? undefined);
|
||||
});
|
||||
}
|
||||
@@ -140,7 +142,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
performer_ids: performerIds,
|
||||
movies: makeMovieInputs(),
|
||||
tag_ids: tagIds,
|
||||
cover_image: coverImage
|
||||
cover_image: coverImage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,14 +151,14 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let ret = movieIds.map(id => {
|
||||
let ret = movieIds.map((id) => {
|
||||
const r: GQL.SceneMovieInput = {
|
||||
movie_id: id
|
||||
movie_id: id,
|
||||
};
|
||||
return r;
|
||||
});
|
||||
|
||||
ret = ret.map(r => {
|
||||
ret = ret.map((r) => {
|
||||
return { scene_index: movieSceneIndexes.get(r.movie_id), ...r };
|
||||
});
|
||||
|
||||
@@ -181,7 +183,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
return {
|
||||
id: props.scene.id,
|
||||
delete_file: deleteFile,
|
||||
delete_generated: deleteGenerated
|
||||
delete_generated: deleteGenerated,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
return (
|
||||
<SceneMovieTable
|
||||
movieSceneIndexes={movieSceneIndexes}
|
||||
onUpdate={items => {
|
||||
onUpdate={(items) => {
|
||||
setMovieSceneIndexes(items);
|
||||
}}
|
||||
/>
|
||||
@@ -272,7 +274,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
|
||||
return (
|
||||
<DropdownButton id="scene-scrape" title="Scrape with...">
|
||||
{queryableScrapers.map(s => (
|
||||
{queryableScrapers.map((s) => (
|
||||
<Dropdown.Item key={s.name} onClick={() => onScrapeClicked(s)}>
|
||||
{s.name}
|
||||
</Dropdown.Item>
|
||||
@@ -282,8 +284,8 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
}
|
||||
|
||||
function urlScrapable(scrapedUrl: string): boolean {
|
||||
return (Scrapers?.data?.listSceneScrapers ?? []).some(s =>
|
||||
(s?.scene?.urls ?? []).some(u => scrapedUrl.includes(u))
|
||||
return (Scrapers?.data?.listSceneScrapers ?? []).some((s) =>
|
||||
(s?.scene?.urls ?? []).some((u) => scrapedUrl.includes(u))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -313,12 +315,12 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
scene.performers &&
|
||||
scene.performers.length > 0
|
||||
) {
|
||||
const idPerfs = scene.performers.filter(p => {
|
||||
const idPerfs = scene.performers.filter((p) => {
|
||||
return p.id !== undefined && p.id !== null;
|
||||
});
|
||||
|
||||
if (idPerfs.length > 0) {
|
||||
const newIds = idPerfs.map(p => p.id);
|
||||
const newIds = idPerfs.map((p) => p.id);
|
||||
setPerformerIds(newIds as string[]);
|
||||
}
|
||||
}
|
||||
@@ -328,23 +330,23 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
scene.movies &&
|
||||
scene.movies.length > 0
|
||||
) {
|
||||
const idMovis = scene.movies.filter(p => {
|
||||
const idMovis = scene.movies.filter((p) => {
|
||||
return p.id !== undefined && p.id !== null;
|
||||
});
|
||||
|
||||
if (idMovis.length > 0) {
|
||||
const newIds = idMovis.map(p => p.id);
|
||||
const newIds = idMovis.map((p) => p.id);
|
||||
setMovieIds(newIds as string[]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tagIds?.length && scene?.tags?.length) {
|
||||
const idTags = scene.tags.filter(p => {
|
||||
const idTags = scene.tags.filter((p) => {
|
||||
return p.id !== undefined && p.id !== null;
|
||||
});
|
||||
|
||||
if (idTags.length > 0) {
|
||||
const newIds = idTags.map(p => p.id);
|
||||
const newIds = idTags.map((p) => p.id);
|
||||
setTagIds(newIds as string[]);
|
||||
}
|
||||
}
|
||||
@@ -396,7 +398,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
title: "Title",
|
||||
value: title,
|
||||
onChange: setTitle,
|
||||
isEditing: true
|
||||
isEditing: true,
|
||||
})}
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
@@ -417,7 +419,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
value: date,
|
||||
isEditing: true,
|
||||
onChange: setDate,
|
||||
placeholder: "YYYY-MM-DD"
|
||||
placeholder: "YYYY-MM-DD",
|
||||
})}
|
||||
{TableUtils.renderHtmlSelect({
|
||||
title: "Rating",
|
||||
@@ -425,7 +427,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
isEditing: true,
|
||||
onChange: (value: string) =>
|
||||
setRating(Number.parseInt(value, 10)),
|
||||
selectOptions: ["", 1, 2, 3, 4, 5]
|
||||
selectOptions: ["", 1, 2, 3, 4, 5],
|
||||
})}
|
||||
<tr>
|
||||
<td>Gallery</td>
|
||||
@@ -433,7 +435,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
<SceneGallerySelect
|
||||
sceneId={props.scene.id}
|
||||
initialId={galleryId}
|
||||
onSelect={item => setGalleryId(item ? item.id : undefined)}
|
||||
onSelect={(item) => setGalleryId(item ? item.id : undefined)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -441,7 +443,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
<td>Studio</td>
|
||||
<td>
|
||||
<StudioSelect
|
||||
onSelect={items =>
|
||||
onSelect={(items) =>
|
||||
setStudioId(items.length > 0 ? items[0]?.id : undefined)
|
||||
}
|
||||
ids={studioId ? [studioId] : []}
|
||||
@@ -453,8 +455,8 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
<td>
|
||||
<PerformerSelect
|
||||
isMulti
|
||||
onSelect={items =>
|
||||
setPerformerIds(items.map(item => item.id))
|
||||
onSelect={(items) =>
|
||||
setPerformerIds(items.map((item) => item.id))
|
||||
}
|
||||
ids={performerIds}
|
||||
/>
|
||||
@@ -465,7 +467,9 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
<td>
|
||||
<MovieSelect
|
||||
isMulti
|
||||
onSelect={items => setMovieIds(items.map(item => item.id))}
|
||||
onSelect={(items) =>
|
||||
setMovieIds(items.map((item) => item.id))
|
||||
}
|
||||
ids={movieIds}
|
||||
/>
|
||||
{renderTableMovies()}
|
||||
@@ -476,7 +480,7 @@ export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
|
||||
<td>
|
||||
<TagSelect
|
||||
isMulti
|
||||
onSelect={items => setTagIds(items.map(item => item.id))}
|
||||
onSelect={(items) => setTagIds(items.map((item) => item.id))}
|
||||
ids={tagIds}
|
||||
/>
|
||||
</td>
|
||||
|
||||
@@ -20,7 +20,7 @@ export const SceneFileInfoPanel: React.FC<ISceneFileInfoPanelProps> = (
|
||||
|
||||
function renderPath() {
|
||||
const {
|
||||
scene: { path }
|
||||
scene: { path },
|
||||
} = props;
|
||||
return (
|
||||
<div className="row">
|
||||
|
||||
@@ -6,7 +6,7 @@ import { StashService } from "src/core/StashService";
|
||||
import {
|
||||
DurationInput,
|
||||
TagSelect,
|
||||
MarkerTitleSuggest
|
||||
MarkerTitleSuggest,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { JWUtils } from "src/utils";
|
||||
@@ -27,7 +27,7 @@ interface ISceneMarkerForm {
|
||||
export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
sceneID,
|
||||
editingMarker,
|
||||
onClose
|
||||
onClose,
|
||||
}) => {
|
||||
const [sceneMarkerCreate] = StashService.useSceneMarkerCreate();
|
||||
const [sceneMarkerUpdate] = StashService.useSceneMarkerUpdate();
|
||||
@@ -40,18 +40,18 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
seconds: parseFloat(values.seconds),
|
||||
scene_id: sceneID,
|
||||
primary_tag_id: values.primaryTagId,
|
||||
tag_ids: values.tagIds
|
||||
tag_ids: values.tagIds,
|
||||
};
|
||||
if (!editingMarker) {
|
||||
sceneMarkerCreate({ variables })
|
||||
.then(onClose)
|
||||
.catch(err => Toast.error(err));
|
||||
.catch((err) => Toast.error(err));
|
||||
} else {
|
||||
const updateVariables = variables as GQL.SceneMarkerUpdateInput;
|
||||
updateVariables.id = editingMarker!.id;
|
||||
sceneMarkerUpdate({ variables: updateVariables })
|
||||
.then(onClose)
|
||||
.catch(err => Toast.error(err));
|
||||
.catch((err) => Toast.error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
|
||||
sceneMarkerDestroy({ variables: { id: editingMarker.id } })
|
||||
.then(onClose)
|
||||
.catch(err => Toast.error(err));
|
||||
.catch((err) => Toast.error(err));
|
||||
};
|
||||
const renderTitleField = (fieldProps: FieldProps<string>) => (
|
||||
<div className="col-10">
|
||||
@@ -76,7 +76,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
const renderSecondsField = (fieldProps: FieldProps<string>) => (
|
||||
<div className="col-3">
|
||||
<DurationInput
|
||||
onValueChange={s => fieldProps.form.setFieldValue("seconds", s)}
|
||||
onValueChange={(s) => fieldProps.form.setFieldValue("seconds", s)}
|
||||
onReset={() =>
|
||||
fieldProps.form.setFieldValue(
|
||||
"seconds",
|
||||
@@ -90,7 +90,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
|
||||
const renderPrimaryTagField = (fieldProps: FieldProps<string>) => (
|
||||
<TagSelect
|
||||
onSelect={tags =>
|
||||
onSelect={(tags) =>
|
||||
fieldProps.form.setFieldValue("primaryTagId", tags[0]?.id)
|
||||
}
|
||||
ids={fieldProps.field.value ? [fieldProps.field.value] : []}
|
||||
@@ -101,10 +101,10 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
const renderTagsField = (fieldProps: FieldProps<string[]>) => (
|
||||
<TagSelect
|
||||
isMulti
|
||||
onSelect={tags =>
|
||||
onSelect={(tags) =>
|
||||
fieldProps.form.setFieldValue(
|
||||
"tagIds",
|
||||
tags.map(tag => tag.id)
|
||||
tags.map((tag) => tag.id)
|
||||
)
|
||||
}
|
||||
ids={fieldProps.field.value}
|
||||
@@ -119,7 +119,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
Math.round(JWUtils.getPlayer()?.getPosition() ?? 0)
|
||||
).toString(),
|
||||
primaryTagId: editingMarker?.primary_tag.id ?? "",
|
||||
tagIds: editingMarker?.tags.map(tag => tag.id) ?? []
|
||||
tagIds: editingMarker?.tags.map((tag) => tag.id) ?? [],
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -54,7 +54,7 @@ export const SceneMarkersPanel: React.FC<ISceneMarkersPanelProps> = (
|
||||
<div className="row">
|
||||
<WallPanel
|
||||
sceneMarkers={props.scene.scene_markers}
|
||||
clickHandler={marker => {
|
||||
clickHandler={(marker) => {
|
||||
window.scrollTo(0, 0);
|
||||
onClickMarker(marker as GQL.SceneMarkerDataFragment);
|
||||
}}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface ISceneMoviePanelProps {
|
||||
export const SceneMoviePanel: FunctionComponent<ISceneMoviePanelProps> = (
|
||||
props: ISceneMoviePanelProps
|
||||
) => {
|
||||
const cards = props.scene.movies.map(sceneMovie => (
|
||||
const cards = props.scene.movies.map((sceneMovie) => (
|
||||
<MovieCard
|
||||
key={sceneMovie.movie.id}
|
||||
movie={sceneMovie.movie}
|
||||
|
||||
@@ -22,11 +22,11 @@ export const SceneMovieTable: React.FunctionComponent<IProps> = (
|
||||
|
||||
if (!!props.movieSceneIndexes && !!items) {
|
||||
props.movieSceneIndexes.forEach((index, movieId) => {
|
||||
itemsFilter = itemsFilter.concat(items.filter(x => x.id === movieId));
|
||||
itemsFilter = itemsFilter.concat(items.filter((x) => x.id === movieId));
|
||||
});
|
||||
}
|
||||
|
||||
const storeIdx = itemsFilter.map(movie => {
|
||||
const storeIdx = itemsFilter.map((movie) => {
|
||||
return props.movieSceneIndexes.get(movie.id);
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export const SceneMovieTable: React.FunctionComponent<IProps> = (
|
||||
}
|
||||
>
|
||||
{["", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"].map(
|
||||
opt => (
|
||||
(opt) => (
|
||||
<option value={opt} key={opt}>
|
||||
{opt}
|
||||
</option>
|
||||
|
||||
@@ -19,8 +19,8 @@ export const SceneOperationsPanel: FunctionComponent<IOperationsPanelProps> = (
|
||||
await generateScreenshot({
|
||||
variables: {
|
||||
id: props.scene.id,
|
||||
at
|
||||
}
|
||||
at,
|
||||
},
|
||||
});
|
||||
Toast.success({ content: "Generating screenshot" });
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface IScenePerformerPanelProps {
|
||||
export const ScenePerformerPanel: FunctionComponent<IScenePerformerPanelProps> = (
|
||||
props: IScenePerformerPanelProps
|
||||
) => {
|
||||
const cards = props.scene.performers.map(performer => (
|
||||
const cards = props.scene.performers.map((performer) => (
|
||||
<PerformerCard
|
||||
key={performer.id}
|
||||
performer={performer}
|
||||
|
||||
@@ -3,7 +3,7 @@ import _ from "lodash";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import {
|
||||
FindScenesQueryResult,
|
||||
SlimSceneDataFragment
|
||||
SlimSceneDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import { StashService } from "src/core/StashService";
|
||||
import { useScenesList } from "src/hooks";
|
||||
@@ -21,14 +21,14 @@ interface ISceneList {
|
||||
|
||||
export const SceneList: React.FC<ISceneList> = ({
|
||||
subComponent,
|
||||
filterHook
|
||||
filterHook,
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
const otherOperations = [
|
||||
{
|
||||
text: "Play Random",
|
||||
onClick: playRandom
|
||||
}
|
||||
onClick: playRandom,
|
||||
},
|
||||
];
|
||||
|
||||
const listData = useScenesList({
|
||||
@@ -37,7 +37,7 @@ export const SceneList: React.FC<ISceneList> = ({
|
||||
renderContent,
|
||||
renderSelectedOptions,
|
||||
subComponent,
|
||||
filterHook
|
||||
filterHook,
|
||||
});
|
||||
|
||||
async function playRandom(
|
||||
@@ -78,8 +78,8 @@ export const SceneList: React.FC<ISceneList> = ({
|
||||
const { scenes } = result.data.findScenes;
|
||||
|
||||
const selectedScenes: SlimSceneDataFragment[] = [];
|
||||
selectedIds.forEach(id => {
|
||||
const scene = scenes.find(s => s.id === id);
|
||||
selectedIds.forEach((id) => {
|
||||
const scene = scenes.find((s) => s.id === id);
|
||||
|
||||
if (scene) {
|
||||
selectedScenes.push(scene);
|
||||
@@ -126,7 +126,7 @@ export const SceneList: React.FC<ISceneList> = ({
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<div className="row justify-content-center">
|
||||
{result.data.findScenes.scenes.map(scene =>
|
||||
{result.data.findScenes.scenes.map((scene) =>
|
||||
renderSceneCard(scene, selectedIds, zoomIndex)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,24 +13,22 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
|
||||
props: ISceneListTableProps
|
||||
) => {
|
||||
const renderTags = (tags: GQL.Tag[]) =>
|
||||
tags.map(tag => (
|
||||
tags.map((tag) => (
|
||||
<Link key={tag.id} to={NavUtils.makeTagScenesUrl(tag)}>
|
||||
<h6>{tag.name}</h6>
|
||||
</Link>
|
||||
));
|
||||
|
||||
const renderPerformers = (performers: Partial<GQL.Performer>[]) =>
|
||||
performers.map(performer => (
|
||||
performers.map((performer) => (
|
||||
<Link key={performer.id} to={NavUtils.makePerformerScenesUrl(performer)}>
|
||||
<h6>{performer.name}</h6>
|
||||
</Link>
|
||||
));
|
||||
|
||||
const renderMovies = (movies: Partial<GQL.SceneMovie>[]) => {
|
||||
return movies.map(sceneMovie =>
|
||||
!sceneMovie.movie ? (
|
||||
undefined
|
||||
) : (
|
||||
return movies.map((sceneMovie) =>
|
||||
!sceneMovie.movie ? undefined : (
|
||||
<Link to={NavUtils.makeMovieScenesUrl(sceneMovie.movie)}>
|
||||
<h6>{sceneMovie.movie.name}</h6>
|
||||
</Link>
|
||||
|
||||
@@ -14,13 +14,13 @@ export const SceneMarkerList: React.FC = () => {
|
||||
const otherOperations = [
|
||||
{
|
||||
text: "Play Random",
|
||||
onClick: playRandom
|
||||
}
|
||||
onClick: playRandom,
|
||||
},
|
||||
];
|
||||
|
||||
const listData = useSceneMarkersList({
|
||||
otherOperations,
|
||||
renderContent
|
||||
renderContent,
|
||||
});
|
||||
|
||||
async function playRandom(
|
||||
|
||||
@@ -3,10 +3,7 @@ import { Button, Form } from "react-bootstrap";
|
||||
import _ from "lodash";
|
||||
import { StashService } from "src/core/StashService";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import {
|
||||
StudioSelect,
|
||||
LoadingIndicator
|
||||
} from "src/components/Shared";
|
||||
import { StudioSelect, LoadingIndicator } from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import MultiSet from "../Shared/MultiSet";
|
||||
|
||||
@@ -21,9 +18,13 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
const Toast = useToast();
|
||||
const [rating, setRating] = useState<string>("");
|
||||
const [studioId, setStudioId] = useState<string>();
|
||||
const [performerMode, setPerformerMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||
const [performerMode, setPerformerMode] = React.useState<
|
||||
GQL.BulkUpdateIdMode
|
||||
>(GQL.BulkUpdateIdMode.Add);
|
||||
const [performerIds, setPerformerIds] = useState<string[]>();
|
||||
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(GQL.BulkUpdateIdMode.Add);
|
||||
const [tagMode, setTagMode] = React.useState<GQL.BulkUpdateIdMode>(
|
||||
GQL.BulkUpdateIdMode.Add
|
||||
);
|
||||
const [tagIds, setTagIds] = useState<string[]>();
|
||||
|
||||
const [updateScenes] = StashService.useBulkSceneUpdate(getSceneInput());
|
||||
@@ -31,10 +32,13 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
// Network state
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
function makeBulkUpdateIds(ids: string[], mode: GQL.BulkUpdateIdMode) : GQL.BulkUpdateIds {
|
||||
function makeBulkUpdateIds(
|
||||
ids: string[],
|
||||
mode: GQL.BulkUpdateIdMode
|
||||
): GQL.BulkUpdateIds {
|
||||
return {
|
||||
mode,
|
||||
ids
|
||||
ids,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,9 +50,9 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
const aggregateTagIds = getTagIds(props.selected);
|
||||
|
||||
const sceneInput: GQL.BulkSceneUpdateInput = {
|
||||
ids: props.selected.map(scene => {
|
||||
ids: props.selected.map((scene) => {
|
||||
return scene.id;
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
// if rating is undefined
|
||||
@@ -78,19 +82,31 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
}
|
||||
|
||||
// if performerIds are empty
|
||||
if (performerMode === GQL.BulkUpdateIdMode.Set && (!performerIds || performerIds.length === 0)) {
|
||||
if (
|
||||
performerMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!performerIds || performerIds.length === 0)
|
||||
) {
|
||||
// and all scenes have the same ids,
|
||||
if (aggregatePerformerIds.length > 0) {
|
||||
// then unset the performerIds, otherwise ignore
|
||||
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||
sceneInput.performer_ids = makeBulkUpdateIds(
|
||||
performerIds || [],
|
||||
performerMode
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// if performerIds non-empty, then we are setting them
|
||||
sceneInput.performer_ids = makeBulkUpdateIds(performerIds || [], performerMode);
|
||||
sceneInput.performer_ids = makeBulkUpdateIds(
|
||||
performerIds || [],
|
||||
performerMode
|
||||
);
|
||||
}
|
||||
|
||||
// if tagIds non-empty, then we are setting them
|
||||
if (tagMode === GQL.BulkUpdateIdMode.Set && (!tagIds || tagIds.length === 0)) {
|
||||
if (
|
||||
tagMode === GQL.BulkUpdateIdMode.Set &&
|
||||
(!tagIds || tagIds.length === 0)
|
||||
) {
|
||||
// and all scenes have the same ids,
|
||||
if (aggregateTagIds.length > 0) {
|
||||
// then unset the tagIds, otherwise ignore
|
||||
@@ -157,11 +173,11 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
|
||||
state.forEach((scene: GQL.SlimSceneDataFragment) => {
|
||||
if (first) {
|
||||
ret = scene.performers ? scene.performers.map(p => p.id).sort() : [];
|
||||
ret = scene.performers ? scene.performers.map((p) => p.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const perfIds = scene.performers
|
||||
? scene.performers.map(p => p.id).sort()
|
||||
? scene.performers.map((p) => p.id).sort()
|
||||
: [];
|
||||
|
||||
if (!_.isEqual(ret, perfIds)) {
|
||||
@@ -179,10 +195,10 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
|
||||
state.forEach((scene: GQL.SlimSceneDataFragment) => {
|
||||
if (first) {
|
||||
ret = scene.tags ? scene.tags.map(t => t.id).sort() : [];
|
||||
ret = scene.tags ? scene.tags.map((t) => t.id).sort() : [];
|
||||
first = false;
|
||||
} else {
|
||||
const tIds = scene.tags ? scene.tags.map(t => t.id).sort() : [];
|
||||
const tIds = scene.tags ? scene.tags.map((t) => t.id).sort() : [];
|
||||
|
||||
if (!_.isEqual(ret, tIds)) {
|
||||
ret = [];
|
||||
@@ -204,8 +220,10 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
state.forEach((scene: GQL.SlimSceneDataFragment) => {
|
||||
const sceneRating = scene.rating?.toString() ?? "";
|
||||
const sceneStudioID = scene?.studio?.id;
|
||||
const scenePerformerIDs = (scene.performers ?? []).map(p => p.id).sort();
|
||||
const sceneTagIDs = (scene.tags ?? []).map(p => p.id).sort();
|
||||
const scenePerformerIDs = (scene.performers ?? [])
|
||||
.map((p) => p.id)
|
||||
.sort();
|
||||
const sceneTagIDs = (scene.tags ?? []).map((p) => p.id).sort();
|
||||
|
||||
if (first) {
|
||||
updateRating = sceneRating;
|
||||
@@ -238,7 +256,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
if (tagMode === GQL.BulkUpdateIdMode.Set) {
|
||||
setTagIds(updateTagIds);
|
||||
}
|
||||
|
||||
|
||||
setIsLoading(false);
|
||||
}, [props.selected, performerMode, tagMode]);
|
||||
|
||||
@@ -248,15 +266,19 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
) {
|
||||
let mode = GQL.BulkUpdateIdMode.Add;
|
||||
switch (type) {
|
||||
case "performers": mode = performerMode; break;
|
||||
case "tags": mode = tagMode; break;
|
||||
case "performers":
|
||||
mode = performerMode;
|
||||
break;
|
||||
case "tags":
|
||||
mode = tagMode;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<MultiSet
|
||||
type={type}
|
||||
onUpdate={items => {
|
||||
const itemIDs = items.map(i => i.id);
|
||||
onUpdate={(items) => {
|
||||
const itemIDs = items.map((i) => i.id);
|
||||
switch (type) {
|
||||
case "performers":
|
||||
setPerformerIds(itemIDs);
|
||||
@@ -266,10 +288,14 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
break;
|
||||
}
|
||||
}}
|
||||
onSetMode={(mode) => {
|
||||
onSetMode={(newMode) => {
|
||||
switch (type) {
|
||||
case "performers": setPerformerMode(mode); break;
|
||||
case "tags": setTagMode(mode); break;
|
||||
case "performers":
|
||||
setPerformerMode(newMode);
|
||||
break;
|
||||
case "tags":
|
||||
setTagMode(newMode);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
ids={ids ?? []}
|
||||
@@ -296,7 +322,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
setRating(event.currentTarget.value)
|
||||
}
|
||||
>
|
||||
{["", "1", "2", "3", "4", "5"].map(opt => (
|
||||
{["", "1", "2", "3", "4", "5"].map((opt) => (
|
||||
<option key={opt} value={opt}>
|
||||
{opt}
|
||||
</option>
|
||||
@@ -307,7 +333,7 @@ export const SceneSelectedOptions: React.FC<IListOperationProps> = (
|
||||
<Form.Group controlId="studio" className="operation-item">
|
||||
<Form.Label>Studio</Form.Label>
|
||||
<StudioSelect
|
||||
onSelect={items => setStudioId(items[0]?.id)}
|
||||
onSelect={(items) => setStudioId(items[0]?.id)}
|
||||
ids={studioId ? [studioId] : []}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
@@ -1,220 +1,220 @@
|
||||
.scene-popovers {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.btn {
|
||||
padding-bottom: 3px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-section {
|
||||
margin-bottom: 0;
|
||||
padding: .5rem 1rem 0 1rem;
|
||||
|
||||
&-title {
|
||||
overflow: hidden;
|
||||
overflow-wrap: normal;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.scene-card-check {
|
||||
left: .5rem;
|
||||
margin-top: -12px;
|
||||
opacity: .5;
|
||||
padding-left: 15px;
|
||||
position: absolute;
|
||||
top: .7rem;
|
||||
width: 1.2rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.performer-tag-container,
|
||||
.movie-tag-container {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.performer-tag.image,
|
||||
.movie-tag.image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
height: 150px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.operation-container {
|
||||
.operation-item {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.rating-operation {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.apply-operation {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.marker-container {
|
||||
display: "flex";
|
||||
flex-wrap: "nowrap";
|
||||
margin-bottom: "20px";
|
||||
overflow-x: "scroll";
|
||||
overflow-y: "hidden";
|
||||
white-space: "nowrap";
|
||||
}
|
||||
|
||||
.studio-logo {
|
||||
margin-top: 1rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.scene-header {
|
||||
flex-basis: auto;
|
||||
}
|
||||
|
||||
#scene-details-container {
|
||||
.tab-content {
|
||||
min-height: 15rem;
|
||||
}
|
||||
|
||||
.scene-description {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.file-info-panel {
|
||||
div {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
#details {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.primary-card {
|
||||
margin: 1rem 0;
|
||||
|
||||
&-body {
|
||||
max-height: 15rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.studio-card {
|
||||
padding: .5rem;
|
||||
|
||||
&-header {
|
||||
height: 150px;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-image {
|
||||
max-height: 150px;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
width: 320px;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-specs-overlay {
|
||||
bottom: 1rem;
|
||||
color: $text-color;
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
letter-spacing: -.03rem;
|
||||
position: absolute;
|
||||
right: .7rem;
|
||||
text-shadow: 0 0 3px #000;
|
||||
}
|
||||
|
||||
.scene-studio-overlay {
|
||||
display: block;
|
||||
font-weight: 900;
|
||||
height: 10%;
|
||||
max-width: 40%;
|
||||
opacity: .75;
|
||||
position: absolute;
|
||||
right: .7rem;
|
||||
top: .7rem;
|
||||
z-index: 9;
|
||||
|
||||
.image-thumbnail {
|
||||
height: auto;
|
||||
max-height: 50px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $text-color;
|
||||
display: inline-block;
|
||||
letter-spacing: -.03rem;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 0 3px #000;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-resolution {
|
||||
font-weight: 900;
|
||||
margin-right: .3rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.scene-card {
|
||||
&.card {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-link {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scene-specs-overlay,
|
||||
.rating-banner,
|
||||
.scene-studio-overlay {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.scene-specs-overlay,
|
||||
.rating-banner,
|
||||
.scene-studio-overlay {
|
||||
opacity: 0;
|
||||
transition: opacity .5s;
|
||||
}
|
||||
|
||||
.scene-studio-overlay:hover {
|
||||
opacity: .75;
|
||||
transition: opacity .5s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-cover {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.movie-table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.scene-popovers {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.btn {
|
||||
padding-bottom: 3px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-section {
|
||||
margin-bottom: 0;
|
||||
padding: 0.5rem 1rem 0 1rem;
|
||||
|
||||
&-title {
|
||||
overflow: hidden;
|
||||
overflow-wrap: normal;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.scene-card-check {
|
||||
left: 0.5rem;
|
||||
margin-top: -12px;
|
||||
opacity: 0.5;
|
||||
padding-left: 15px;
|
||||
position: absolute;
|
||||
top: 0.7rem;
|
||||
width: 1.2rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.performer-tag-container,
|
||||
.movie-tag-container {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.performer-tag.image,
|
||||
.movie-tag.image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
height: 150px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.operation-container {
|
||||
.operation-item {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.rating-operation {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.apply-operation {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.marker-container {
|
||||
display: "flex";
|
||||
flex-wrap: "nowrap";
|
||||
margin-bottom: "20px";
|
||||
overflow-x: "scroll";
|
||||
overflow-y: "hidden";
|
||||
white-space: "nowrap";
|
||||
}
|
||||
|
||||
.studio-logo {
|
||||
margin-top: 1rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.scene-header {
|
||||
flex-basis: auto;
|
||||
}
|
||||
|
||||
#scene-details-container {
|
||||
.tab-content {
|
||||
min-height: 15rem;
|
||||
}
|
||||
|
||||
.scene-description {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.file-info-panel {
|
||||
div {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
#details {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.primary-card {
|
||||
margin: 1rem 0;
|
||||
|
||||
&-body {
|
||||
max-height: 15rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.studio-card {
|
||||
padding: 0.5rem;
|
||||
|
||||
&-header {
|
||||
height: 150px;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-image {
|
||||
max-height: 150px;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
width: 320px;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-specs-overlay {
|
||||
bottom: 1rem;
|
||||
color: $text-color;
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.03rem;
|
||||
position: absolute;
|
||||
right: 0.7rem;
|
||||
text-shadow: 0 0 3px #000;
|
||||
}
|
||||
|
||||
.scene-studio-overlay {
|
||||
display: block;
|
||||
font-weight: 900;
|
||||
height: 10%;
|
||||
max-width: 40%;
|
||||
opacity: 0.75;
|
||||
position: absolute;
|
||||
right: 0.7rem;
|
||||
top: 0.7rem;
|
||||
z-index: 9;
|
||||
|
||||
.image-thumbnail {
|
||||
height: auto;
|
||||
max-height: 50px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $text-color;
|
||||
display: inline-block;
|
||||
letter-spacing: -0.03rem;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 0 3px #000;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-resolution {
|
||||
font-weight: 900;
|
||||
margin-right: 0.3rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.scene-card {
|
||||
&.card {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-link {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scene-specs-overlay,
|
||||
.rating-banner,
|
||||
.scene-studio-overlay {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.scene-specs-overlay,
|
||||
.rating-banner,
|
||||
.scene-studio-overlay {
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.scene-studio-overlay:hover {
|
||||
opacity: 0.75;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-cover {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.movie-table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export const SettingsAboutPanel: React.FC = () => {
|
||||
error: errorLatest,
|
||||
loading: loadingLatest,
|
||||
refetch,
|
||||
networkStatus
|
||||
networkStatus,
|
||||
} = StashService.useLatestVersion();
|
||||
|
||||
function maybeRenderTag() {
|
||||
|
||||
@@ -48,7 +48,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
logLevel,
|
||||
logAccess,
|
||||
excludes,
|
||||
scraperUserAgent
|
||||
scraperUserAgent,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -116,7 +116,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
GQL.StreamingResolutionEnum.StandardHd,
|
||||
GQL.StreamingResolutionEnum.FullHd,
|
||||
GQL.StreamingResolutionEnum.FourK,
|
||||
GQL.StreamingResolutionEnum.Original
|
||||
GQL.StreamingResolutionEnum.Original,
|
||||
].map(resolutionToString);
|
||||
|
||||
function resolutionToString(r: GQL.StreamingResolutionEnum | undefined) {
|
||||
@@ -258,7 +258,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
}
|
||||
value={resolutionToString(maxTranscodeSize)}
|
||||
>
|
||||
{transcodeQualities.map(q => (
|
||||
{transcodeQualities.map((q) => (
|
||||
<option key={q} value={q}>
|
||||
{q}
|
||||
</option>
|
||||
@@ -280,7 +280,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
}
|
||||
value={resolutionToString(maxStreamingTranscodeSize)}
|
||||
>
|
||||
{transcodeQualities.map(q => (
|
||||
{transcodeQualities.map((q) => (
|
||||
<option key={q} value={q}>
|
||||
{q}
|
||||
</option>
|
||||
@@ -382,7 +382,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
|
||||
}
|
||||
value={logLevel}
|
||||
>
|
||||
{["Debug", "Info", "Warning", "Error"].map(o => (
|
||||
{["Debug", "Info", "Warning", "Error"].map((o) => (
|
||||
<option key={o} value={o}>
|
||||
{o}
|
||||
</option>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||
showStudioAsText,
|
||||
css,
|
||||
cssEnabled,
|
||||
language
|
||||
language,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -119,7 +119,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||
<DurationInput
|
||||
className="row col col-4"
|
||||
numericValue={maximumLoopDuration}
|
||||
onValueChange={duration => setMaximumLoopDuration(duration)}
|
||||
onValueChange={(duration) => setMaximumLoopDuration(duration)}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
Maximum scene duration where scene player will loop the video - 0 to
|
||||
|
||||
@@ -73,8 +73,8 @@ export const SettingsLogsPanel: React.FC = () => {
|
||||
const { data: existingData } = StashService.useLogs();
|
||||
const [logLevel, setLogLevel] = useState<string>("Info");
|
||||
|
||||
const oldData = (existingData?.logs ?? []).map(e => new LogEntry(e));
|
||||
const newData = (data?.loggingSubscribe ?? []).map(e => new LogEntry(e));
|
||||
const oldData = (existingData?.logs ?? []).map((e) => new LogEntry(e));
|
||||
const newData = (data?.loggingSubscribe ?? []).map((e) => new LogEntry(e));
|
||||
|
||||
const filteredLogEntries = [...newData.reverse(), ...oldData]
|
||||
.filter(filterByLogLevel)
|
||||
@@ -104,9 +104,9 @@ export const SettingsLogsPanel: React.FC = () => {
|
||||
className="col-6 col-sm-2 input-control"
|
||||
as="select"
|
||||
defaultValue={logLevel}
|
||||
onChange={event => setLogLevel(event.currentTarget.value)}
|
||||
onChange={(event) => setLogLevel(event.currentTarget.value)}
|
||||
>
|
||||
{logLevels.map(level => (
|
||||
{logLevels.map((level) => (
|
||||
<option key={level} value={level}>
|
||||
{level}
|
||||
</option>
|
||||
@@ -115,7 +115,7 @@ export const SettingsLogsPanel: React.FC = () => {
|
||||
</Form.Row>
|
||||
<div className="logs">
|
||||
{maybeRenderError}
|
||||
{filteredLogEntries.map(logEntry => (
|
||||
{filteredLogEntries.map((logEntry) => (
|
||||
<LogElement logEntry={logEntry} key={logEntry.id} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ export const GenerateButton: React.FC = () => {
|
||||
sprites,
|
||||
previews,
|
||||
markers,
|
||||
transcodes
|
||||
transcodes,
|
||||
});
|
||||
Toast.success({ content: "Started generating" });
|
||||
} catch (e) {
|
||||
|
||||
@@ -128,7 +128,7 @@ export const SettingsTasksPanel: React.FC = () => {
|
||||
return {
|
||||
performers: autoTagPerformers ? wildcard : [],
|
||||
studios: autoTagStudios ? wildcard : [],
|
||||
tags: autoTagTags ? wildcard : []
|
||||
tags: autoTagTags ? wildcard : [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.logs {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
font-size: smaller;
|
||||
max-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
|
||||
@@ -32,7 +32,7 @@ export const FolderSelect: React.FC<IProps> = (props: IProps) => {
|
||||
|
||||
function onRemoveDirectory(directory: string) {
|
||||
const newSelectedDirectories = selectedDirectories.filter(
|
||||
dir => dir !== directory
|
||||
(dir) => dir !== directory
|
||||
);
|
||||
setSelectedDirectories(newSelectedDirectories);
|
||||
props.onDirectoriesChanged(newSelectedDirectories);
|
||||
@@ -66,7 +66,7 @@ export const FolderSelect: React.FC<IProps> = (props: IProps) => {
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
<ul className="folder-list">
|
||||
{selectableDirectories.map(path => {
|
||||
{selectableDirectories.map((path) => {
|
||||
return (
|
||||
<li key={path} className="folder-item">
|
||||
<Button
|
||||
@@ -96,7 +96,7 @@ export const FolderSelect: React.FC<IProps> = (props: IProps) => {
|
||||
{error ? <h1>{error.message}</h1> : ""}
|
||||
{renderDialog()}
|
||||
<Form.Group>
|
||||
{selectedDirectories.map(path => {
|
||||
{selectedDirectories.map((path) => {
|
||||
return (
|
||||
<div key={path}>
|
||||
{path}{" "}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const HoverPopover: React.FC<IHoverPopover> = ({
|
||||
className,
|
||||
placement = "top",
|
||||
onOpen,
|
||||
onClose
|
||||
onClose,
|
||||
}) => {
|
||||
const [show, setShow] = useState(false);
|
||||
const triggerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -12,7 +12,7 @@ export const ImageInput: React.FC<IImageInput> = ({
|
||||
isEditing,
|
||||
text,
|
||||
onImageChange,
|
||||
acceptSVG = false
|
||||
acceptSVG = false,
|
||||
}) => {
|
||||
if (!isEditing) return <div />;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const CLASSNAME_MESSAGE = `${CLASSNAME}-message`;
|
||||
|
||||
const LoadingIndicator: React.FC<ILoadingProps> = ({
|
||||
message,
|
||||
inline = false
|
||||
inline = false,
|
||||
}) => (
|
||||
<div className={cx(CLASSNAME, { inline })}>
|
||||
<Spinner animation="border" role="status">
|
||||
|
||||
@@ -25,7 +25,7 @@ const ModalComponent: React.FC<IModal> = ({
|
||||
header,
|
||||
cancel,
|
||||
accept,
|
||||
onHide
|
||||
onHide,
|
||||
}) => (
|
||||
<Modal keyboard={false} onHide={onHide} show={show}>
|
||||
<Modal.Header>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from "react";
|
||||
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { FilterSelect } from "./Select";
|
||||
import { Button, InputGroup } from "react-bootstrap";
|
||||
import { Icon } from "src/components/Shared";
|
||||
import { FilterSelect } from "./Select";
|
||||
|
||||
type ValidTypes =
|
||||
| GQL.SlimPerformerDataFragment
|
||||
@@ -18,13 +18,15 @@ interface IMultiSetProps {
|
||||
onSetMode: (mode: GQL.BulkUpdateIdMode) => void;
|
||||
}
|
||||
|
||||
const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps) => {
|
||||
const MultiSet: React.FunctionComponent<IMultiSetProps> = (
|
||||
props: IMultiSetProps
|
||||
) => {
|
||||
function onUpdate(items: ValidTypes[]) {
|
||||
props.onUpdate(items);
|
||||
}
|
||||
|
||||
function getModeIcon() {
|
||||
switch(props.mode) {
|
||||
switch (props.mode) {
|
||||
case GQL.BulkUpdateIdMode.Set:
|
||||
return "pencil-alt";
|
||||
case GQL.BulkUpdateIdMode.Add:
|
||||
@@ -35,7 +37,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps
|
||||
}
|
||||
|
||||
function getModeText() {
|
||||
switch(props.mode) {
|
||||
switch (props.mode) {
|
||||
case GQL.BulkUpdateIdMode.Set:
|
||||
return "Set";
|
||||
case GQL.BulkUpdateIdMode.Add:
|
||||
@@ -46,7 +48,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps
|
||||
}
|
||||
|
||||
function nextMode() {
|
||||
switch(props.mode) {
|
||||
switch (props.mode) {
|
||||
case GQL.BulkUpdateIdMode.Set:
|
||||
return GQL.BulkUpdateIdMode.Add;
|
||||
case GQL.BulkUpdateIdMode.Add:
|
||||
@@ -59,7 +61,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps
|
||||
return (
|
||||
<InputGroup className="multi-set">
|
||||
<InputGroup.Prepend>
|
||||
<Button
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => props.onSetMode(nextMode())}
|
||||
@@ -68,7 +70,7 @@ const MultiSet: React.FunctionComponent<IMultiSetProps> = (props: IMultiSetProps
|
||||
<Icon icon={getModeIcon()} className="fa-fw" />
|
||||
</Button>
|
||||
</InputGroup.Prepend>
|
||||
|
||||
|
||||
<FilterSelect
|
||||
type={props.type}
|
||||
isMulti
|
||||
|
||||
@@ -57,11 +57,11 @@ interface ISceneGallerySelect {
|
||||
const getSelectedValues = (selectedItems: ValueType<Option>) =>
|
||||
selectedItems
|
||||
? (Array.isArray(selectedItems) ? selectedItems : [selectedItems]).map(
|
||||
item => item.value
|
||||
(item) => item.value
|
||||
)
|
||||
: [];
|
||||
|
||||
export const SceneGallerySelect: React.FC<ISceneGallerySelect> = props => {
|
||||
export const SceneGallerySelect: React.FC<ISceneGallerySelect> = (props) => {
|
||||
const { data, loading } = StashService.useValidGalleriesForScene(
|
||||
props.sceneId
|
||||
);
|
||||
@@ -69,17 +69,17 @@ export const SceneGallerySelect: React.FC<ISceneGallerySelect> = props => {
|
||||
const items = (galleries.length > 0
|
||||
? [{ path: "None", id: "0" }, ...galleries]
|
||||
: []
|
||||
).map(g => ({ label: g.path, value: g.id }));
|
||||
).map((g) => ({ label: g.path, value: g.id }));
|
||||
|
||||
const onChange = (selectedItems: ValueType<Option>) => {
|
||||
const selectedItem = getSelectedValues(selectedItems)[0];
|
||||
props.onSelect(
|
||||
selectedItem ? galleries.find(g => g.id === selectedItem) : undefined
|
||||
selectedItem ? galleries.find((g) => g.id === selectedItem) : undefined
|
||||
);
|
||||
};
|
||||
|
||||
const selectedOptions: Option[] = props.initialId
|
||||
? items.filter(item => props.initialId?.indexOf(item.value) !== -1)
|
||||
? items.filter((item) => props.initialId?.indexOf(item.value) !== -1)
|
||||
: [];
|
||||
|
||||
return (
|
||||
@@ -98,7 +98,9 @@ interface IScrapePerformerSuggestProps {
|
||||
onSelectPerformer: (performer: GQL.ScrapedPerformerDataFragment) => void;
|
||||
placeholder?: string;
|
||||
}
|
||||
export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = props => {
|
||||
export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = (
|
||||
props
|
||||
) => {
|
||||
const [query, setQuery] = React.useState<string>("");
|
||||
const { data, loading } = StashService.useScrapePerformerList(
|
||||
props.scraperId,
|
||||
@@ -106,9 +108,9 @@ export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = pr
|
||||
);
|
||||
|
||||
const performers = data?.scrapePerformerList ?? [];
|
||||
const items = performers.map(item => ({
|
||||
const items = performers.map((item) => ({
|
||||
label: item.name ?? "",
|
||||
value: item.name ?? ""
|
||||
value: item.name ?? "",
|
||||
}));
|
||||
|
||||
const onInputChange = useCallback(
|
||||
@@ -119,7 +121,7 @@ export const ScrapePerformerSuggest: React.FC<IScrapePerformerSuggestProps> = pr
|
||||
);
|
||||
const onChange = (selectedItems: ValueType<Option>) => {
|
||||
const name = getSelectedValues(selectedItems)[0];
|
||||
const performer = performers.find(p => p.name === name);
|
||||
const performer = performers.find((p) => p.name === name);
|
||||
if (performer) props.onSelectPerformer(performer);
|
||||
};
|
||||
|
||||
@@ -141,16 +143,16 @@ interface IMarkerSuggestProps {
|
||||
initialMarkerTitle?: string;
|
||||
onChange: (title: string) => void;
|
||||
}
|
||||
export const MarkerTitleSuggest: React.FC<IMarkerSuggestProps> = props => {
|
||||
export const MarkerTitleSuggest: React.FC<IMarkerSuggestProps> = (props) => {
|
||||
const { data, loading } = StashService.useMarkerStrings();
|
||||
const suggestions = data?.markerStrings ?? [];
|
||||
|
||||
const onChange = (selectedItems: ValueType<Option>) =>
|
||||
props.onChange(getSelectedValues(selectedItems)[0]);
|
||||
|
||||
const items = suggestions.map(item => ({
|
||||
const items = suggestions.map((item) => ({
|
||||
label: item?.title ?? "",
|
||||
value: item?.title ?? ""
|
||||
value: item?.title ?? "",
|
||||
}));
|
||||
const initialIds = props.initialMarkerTitle ? [props.initialMarkerTitle] : [];
|
||||
return (
|
||||
@@ -167,7 +169,7 @@ export const MarkerTitleSuggest: React.FC<IMarkerSuggestProps> = props => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const FilterSelect: React.FC<IFilterProps & ITypeProps> = props =>
|
||||
export const FilterSelect: React.FC<IFilterProps & ITypeProps> = (props) =>
|
||||
props.type === "performers" ? (
|
||||
<PerformerSelect {...(props as IFilterProps)} />
|
||||
) : props.type === "studios" ? (
|
||||
@@ -178,23 +180,23 @@ export const FilterSelect: React.FC<IFilterProps & ITypeProps> = props =>
|
||||
<TagSelect {...(props as IFilterProps)} />
|
||||
);
|
||||
|
||||
export const PerformerSelect: React.FC<IFilterProps> = props => {
|
||||
export const PerformerSelect: React.FC<IFilterProps> = (props) => {
|
||||
const { data, loading } = StashService.useAllPerformersForFilter();
|
||||
|
||||
const normalizedData = data?.allPerformers ?? [];
|
||||
const items: Option[] = normalizedData.map(item => ({
|
||||
const items: Option[] = normalizedData.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name ?? ""
|
||||
label: item.name ?? "",
|
||||
}));
|
||||
const placeholder = props.noSelectionString ?? "Select performer...";
|
||||
const selectedOptions: Option[] = props.ids
|
||||
? items.filter(item => props.ids?.indexOf(item.value) !== -1)
|
||||
? items.filter((item) => props.ids?.indexOf(item.value) !== -1)
|
||||
: [];
|
||||
|
||||
const onChange = (selectedItems: ValueType<Option>) => {
|
||||
const selectedIds = getSelectedValues(selectedItems);
|
||||
props.onSelect?.(
|
||||
normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1)
|
||||
normalizedData.filter((item) => selectedIds.indexOf(item.id) !== -1)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -211,7 +213,7 @@ export const PerformerSelect: React.FC<IFilterProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
export const StudioSelect: React.FC<IFilterProps> = props => {
|
||||
export const StudioSelect: React.FC<IFilterProps> = (props) => {
|
||||
const { data, loading } = StashService.useAllStudiosForFilter();
|
||||
|
||||
const normalizedData = data?.allStudios ?? [];
|
||||
@@ -219,20 +221,20 @@ export const StudioSelect: React.FC<IFilterProps> = props => {
|
||||
const items = (normalizedData.length > 0
|
||||
? [{ name: "None", id: "0" }, ...normalizedData]
|
||||
: []
|
||||
).map(item => ({
|
||||
).map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name
|
||||
label: item.name,
|
||||
}));
|
||||
|
||||
const placeholder = props.noSelectionString ?? "Select studio...";
|
||||
const selectedOptions: Option[] = props.ids
|
||||
? items.filter(item => props.ids?.indexOf(item.value) !== -1)
|
||||
? items.filter((item) => props.ids?.indexOf(item.value) !== -1)
|
||||
: [];
|
||||
|
||||
const onChange = (selectedItems: ValueType<Option>) => {
|
||||
const selectedIds = getSelectedValues(selectedItems);
|
||||
props.onSelect?.(
|
||||
normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1)
|
||||
normalizedData.filter((item) => selectedIds.indexOf(item.id) !== -1)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -249,7 +251,7 @@ export const StudioSelect: React.FC<IFilterProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
export const MovieSelect: React.FC<IFilterProps> = props => {
|
||||
export const MovieSelect: React.FC<IFilterProps> = (props) => {
|
||||
const { data, loading } = StashService.useAllMoviesForFilter();
|
||||
|
||||
const normalizedData = data?.allMovies ?? [];
|
||||
@@ -257,20 +259,20 @@ export const MovieSelect: React.FC<IFilterProps> = props => {
|
||||
const items = (normalizedData.length > 0
|
||||
? [{ name: "None", id: "0" }, ...normalizedData]
|
||||
: []
|
||||
).map(item => ({
|
||||
).map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name
|
||||
label: item.name,
|
||||
}));
|
||||
|
||||
const placeholder = props.noSelectionString ?? "Select movie...";
|
||||
const selectedOptions: Option[] = props.ids
|
||||
? items.filter(item => props.ids?.indexOf(item.value) !== -1)
|
||||
? items.filter((item) => props.ids?.indexOf(item.value) !== -1)
|
||||
: [];
|
||||
|
||||
const onChange = (selectedItems: ValueType<Option>) => {
|
||||
const selectedIds = getSelectedValues(selectedItems);
|
||||
props.onSelect?.(
|
||||
normalizedData.filter(item => selectedIds.indexOf(item.id) !== -1)
|
||||
normalizedData.filter((item) => selectedIds.indexOf(item.id) !== -1)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -287,7 +289,7 @@ export const MovieSelect: React.FC<IFilterProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
export const TagSelect: React.FC<IFilterProps> = props => {
|
||||
export const TagSelect: React.FC<IFilterProps> = (props) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>(props.ids ?? []);
|
||||
const { data, loading: dataLoading } = StashService.useAllTagsForFilter();
|
||||
@@ -299,25 +301,25 @@ export const TagSelect: React.FC<IFilterProps> = props => {
|
||||
|
||||
const tags = data?.allTags ?? [];
|
||||
const selected = tags
|
||||
.filter(tag => selectedTags.indexOf(tag.id) !== -1)
|
||||
.map(tag => ({ value: tag.id, label: tag.name }));
|
||||
const items: Option[] = tags.map(item => ({
|
||||
.filter((tag) => selectedTags.indexOf(tag.id) !== -1)
|
||||
.map((tag) => ({ value: tag.id, label: tag.name }));
|
||||
const items: Option[] = tags.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name
|
||||
label: item.name,
|
||||
}));
|
||||
|
||||
const onCreate = async (tagName: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await createTag({
|
||||
variables: { name: tagName }
|
||||
variables: { name: tagName },
|
||||
});
|
||||
|
||||
if (result?.data?.tagCreate) {
|
||||
setSelectedIds([...selectedIds, result.data.tagCreate.id]);
|
||||
props.onSelect?.(
|
||||
[...tags, result.data.tagCreate].filter(
|
||||
item => selectedIds.indexOf(item.id) !== -1
|
||||
(item) => selectedIds.indexOf(item.id) !== -1
|
||||
)
|
||||
);
|
||||
setLoading(false);
|
||||
@@ -327,7 +329,7 @@ export const TagSelect: React.FC<IFilterProps> = props => {
|
||||
<span>
|
||||
Created tag: <b>{tagName}</b>
|
||||
</span>
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -339,7 +341,7 @@ export const TagSelect: React.FC<IFilterProps> = props => {
|
||||
const selectedValues = getSelectedValues(selectedItems);
|
||||
setSelectedIds(selectedValues);
|
||||
props.onSelect?.(
|
||||
tags.filter(item => selectedValues.indexOf(item.id) !== -1)
|
||||
tags.filter((item) => selectedValues.indexOf(item.id) !== -1)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -374,35 +376,35 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
||||
onInputChange,
|
||||
placeholder,
|
||||
showDropdown = true,
|
||||
groupHeader
|
||||
groupHeader,
|
||||
}) => {
|
||||
const defaultValue =
|
||||
items.filter(item => initialIds?.indexOf(item.value) !== -1) ?? null;
|
||||
items.filter((item) => initialIds?.indexOf(item.value) !== -1) ?? null;
|
||||
|
||||
const options = groupHeader
|
||||
? [
|
||||
{
|
||||
label: groupHeader,
|
||||
options: items
|
||||
}
|
||||
options: items,
|
||||
},
|
||||
]
|
||||
: items;
|
||||
|
||||
const styles = {
|
||||
option: (base: CSSProperties) => ({
|
||||
...base,
|
||||
color: "#000"
|
||||
color: "#000",
|
||||
}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
container: (base: CSSProperties, state: any) => ({
|
||||
...base,
|
||||
zIndex: state.isFocused ? 10 : base.zIndex
|
||||
zIndex: state.isFocused ? 10 : base.zIndex,
|
||||
}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
multiValueRemove: (base: CSSProperties, state: any) => ({
|
||||
...base,
|
||||
color: state.isFocused ? base.color : "#333333"
|
||||
})
|
||||
color: state.isFocused ? base.color : "#333333",
|
||||
}),
|
||||
};
|
||||
|
||||
const props = {
|
||||
@@ -423,8 +425,8 @@ const SelectComponent: React.FC<ISelectProps & ITypeProps> = ({
|
||||
components: {
|
||||
IndicatorSeparator: () => null,
|
||||
...((!showDropdown || isDisabled) && { DropdownIndicator: () => null }),
|
||||
...(isDisabled && { MultiValueRemove: () => null })
|
||||
}
|
||||
...(isDisabled && { MultiValueRemove: () => null }),
|
||||
},
|
||||
};
|
||||
|
||||
return creatable ? (
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
PerformerDataFragment,
|
||||
SceneMarkerDataFragment,
|
||||
TagDataFragment,
|
||||
MovieDataFragment
|
||||
MovieDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import { NavUtils, TextUtils } from "src/utils";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ export {
|
||||
FilterSelect,
|
||||
PerformerSelect,
|
||||
StudioSelect,
|
||||
TagSelect
|
||||
TagSelect,
|
||||
} from "./Select";
|
||||
|
||||
export { default as Icon } from "./Icon";
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
justify-content: left;
|
||||
|
||||
.btn {
|
||||
margin-right: .5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.delete,
|
||||
@@ -58,7 +58,7 @@
|
||||
}
|
||||
|
||||
.folder-list {
|
||||
margin-top: .5rem 0 0 0;
|
||||
margin-top: 0.5rem 0 0 0;
|
||||
max-height: 30vw;
|
||||
overflow-x: auto;
|
||||
}
|
||||
@@ -69,9 +69,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.multi-set > div.input-group-prepend + div {
|
||||
position: relative;
|
||||
.multi-set > div.input-group-prepend + div {
|
||||
flex: 1 1;
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ImageUtils, TableUtils } from "src/utils";
|
||||
import {
|
||||
DetailsEditNavbar,
|
||||
Modal,
|
||||
LoadingIndicator
|
||||
LoadingIndicator,
|
||||
} from "src/components/Shared";
|
||||
import { useToast } from "src/hooks";
|
||||
import { StudioScenesPanel } from "./StudioScenesPanel";
|
||||
@@ -83,7 +83,7 @@ export const Studio: React.FC = () => {
|
||||
const input: Partial<GQL.StudioCreateInput | GQL.StudioUpdateInput> = {
|
||||
name,
|
||||
url,
|
||||
image
|
||||
image,
|
||||
};
|
||||
|
||||
if (!isNew) {
|
||||
@@ -155,7 +155,7 @@ export const Studio: React.FC = () => {
|
||||
<div
|
||||
className={cx("studio-details", {
|
||||
"col ml-sm-5": !isNew,
|
||||
"col-8": isNew
|
||||
"col-8": isNew,
|
||||
})}
|
||||
>
|
||||
{isNew && <h2>Add Studio</h2>}
|
||||
@@ -166,13 +166,13 @@ export const Studio: React.FC = () => {
|
||||
title: "Name",
|
||||
value: studio.name ?? "",
|
||||
isEditing: !!isEditing,
|
||||
onChange: setName
|
||||
onChange: setName,
|
||||
})}
|
||||
{TableUtils.renderInputGroup({
|
||||
title: "URL",
|
||||
value: url,
|
||||
isEditing: !!isEditing,
|
||||
onChange: setUrl
|
||||
onChange: setUrl,
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
@@ -12,7 +12,7 @@ export const StudioScenesPanel: React.FC<IStudioScenesPanel> = ({ studio }) => {
|
||||
function filterHook(filter: ListFilterModel) {
|
||||
const studioValue = { id: studio.id!, label: studio.name! };
|
||||
// if studio is already present, then we modify it, otherwise add
|
||||
let studioCriterion = filter.criteria.find(c => {
|
||||
let studioCriterion = filter.criteria.find((c) => {
|
||||
return c.type === "studios";
|
||||
}) as StudiosCriterion;
|
||||
|
||||
@@ -23,7 +23,7 @@ export const StudioScenesPanel: React.FC<IStudioScenesPanel> = ({ studio }) => {
|
||||
) {
|
||||
// add the studio if not present
|
||||
if (
|
||||
!studioCriterion.value.find(p => {
|
||||
!studioCriterion.value.find((p) => {
|
||||
return p.id === studio.id;
|
||||
})
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { StudioCard } from "./StudioCard";
|
||||
|
||||
export const StudioList: React.FC = () => {
|
||||
const listData = useStudiosList({
|
||||
renderContent
|
||||
renderContent,
|
||||
});
|
||||
|
||||
function renderContent(
|
||||
@@ -19,7 +19,7 @@ export const StudioList: React.FC = () => {
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<div className="row px-xl-5 justify-content-center">
|
||||
{result.data.findStudios.studios.map(studio => (
|
||||
{result.data.findStudios.studios.map((studio) => (
|
||||
<StudioCard key={studio.id} studio={studio} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -96,7 +96,7 @@ export const TagList: React.FC = () => {
|
||||
if (!data?.allTags) return <LoadingIndicator />;
|
||||
if (error) return <div>{error.message}</div>;
|
||||
|
||||
const tagElements = data.allTags.map(tag => {
|
||||
const tagElements = data.allTags.map((tag) => {
|
||||
return (
|
||||
<div key={tag.id} className="tag-list-row row">
|
||||
<Button variant="link" onClick={() => setEditingTag(tag)}>
|
||||
@@ -154,7 +154,7 @@ export const TagList: React.FC = () => {
|
||||
accept={{
|
||||
onClick: onEdit,
|
||||
variant: "danger",
|
||||
text: editingTag?.id ? "Update" : "Create"
|
||||
text: editingTag?.id ? "Update" : "Create",
|
||||
}}
|
||||
>
|
||||
<Form.Group controlId="tag-name">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.tag-list {
|
||||
&-row {
|
||||
margin: .5rem 0;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
&-button {
|
||||
margin: 0 .5rem;
|
||||
margin: 0 0.5rem;
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
&-count {
|
||||
display: inline-block;
|
||||
margin: 0 .5rem;
|
||||
margin: 0 0.5rem;
|
||||
min-width: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const WallItem: React.FC<IWallItemProps> = (props: IWallItemProps) => {
|
||||
const [tags, setTags] = useState<JSX.Element[]>([]);
|
||||
const config = StashService.useConfiguration();
|
||||
const videoHoverHook = VideoHoverHook.useVideoHover({
|
||||
resetOnMouseLeave: true
|
||||
resetOnMouseLeave: true,
|
||||
});
|
||||
const showTextContainer =
|
||||
config.data?.configuration.interface.wallShowTitle ?? true;
|
||||
@@ -86,7 +86,7 @@ export const WallItem: React.FC<IWallItemProps> = (props: IWallItemProps) => {
|
||||
props.sceneMarker.seconds
|
||||
)}`
|
||||
);
|
||||
const thisTags = props.sceneMarker.tags.map(tag => (
|
||||
const thisTags = props.sceneMarker.tags.map((tag) => (
|
||||
<span key={tag.id} className="wall-tag">
|
||||
{tag.name}
|
||||
</span>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
.wall-overlay {
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: transform .5s ease-in-out;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.visible {
|
||||
opacity: 1;
|
||||
transition: opacity .5s ease-in-out;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
transition: opacity .5s ease-in-out;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.visible-unanimated {
|
||||
@@ -52,7 +52,7 @@
|
||||
justify-content: center;
|
||||
max-height: 253px;
|
||||
position: relative;
|
||||
transition: transform .5s;
|
||||
transition: transform 0.5s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,10 @@
|
||||
}
|
||||
|
||||
.scene-wall-item-text-container {
|
||||
background: linear-gradient(rgba(255, 255, 255, .25), rgba(255, 255, 255, .65));
|
||||
background: linear-gradient(
|
||||
rgba(255, 255, 255, 0.25),
|
||||
rgba(255, 255, 255, 0.65)
|
||||
);
|
||||
bottom: 0;
|
||||
color: #444;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -41,14 +41,14 @@ export class StashService {
|
||||
const wsUrl = `${wsPlatformUrl.toString().slice(0, -1)}/graphql`;
|
||||
|
||||
const httpLink = new HttpLink({
|
||||
uri: url
|
||||
uri: url,
|
||||
});
|
||||
|
||||
const wsLink = new WebSocketLink({
|
||||
uri: wsUrl,
|
||||
options: {
|
||||
reconnect: true
|
||||
}
|
||||
reconnect: true,
|
||||
},
|
||||
});
|
||||
|
||||
const link = split(
|
||||
@@ -66,7 +66,7 @@ export class StashService {
|
||||
StashService.cache = new InMemoryCache();
|
||||
StashService.client = new ApolloClient({
|
||||
link,
|
||||
cache: StashService.cache
|
||||
cache: StashService.cache,
|
||||
});
|
||||
|
||||
return StashService.client;
|
||||
@@ -77,14 +77,14 @@ export class StashService {
|
||||
if (StashService.cache) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cache = StashService.cache as any;
|
||||
const keyMatchers = queries.map(query => {
|
||||
const keyMatchers = queries.map((query) => {
|
||||
return new RegExp(`^${query}`);
|
||||
});
|
||||
|
||||
const rootQuery = cache.data.data.ROOT_QUERY;
|
||||
Object.keys(rootQuery).forEach(key => {
|
||||
Object.keys(rootQuery).forEach((key) => {
|
||||
if (
|
||||
keyMatchers.some(matcher => {
|
||||
keyMatchers.some((matcher) => {
|
||||
return !!key.match(matcher);
|
||||
})
|
||||
) {
|
||||
@@ -97,8 +97,8 @@ export class StashService {
|
||||
public static useFindGalleries(filter: ListFilterModel) {
|
||||
return GQL.useFindGalleriesQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter()
|
||||
}
|
||||
filter: filter.makeFindFilter(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -116,8 +116,8 @@ export class StashService {
|
||||
return GQL.useFindScenesQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
scene_filter: sceneFilter
|
||||
}
|
||||
scene_filter: sceneFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,8 +129,8 @@ export class StashService {
|
||||
query: GQL.FindScenesDocument,
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
scene_filter: sceneFilter
|
||||
}
|
||||
scene_filter: sceneFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@ export class StashService {
|
||||
return GQL.useFindSceneMarkersQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
scene_marker_filter: sceneMarkerFilter
|
||||
}
|
||||
scene_marker_filter: sceneMarkerFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -161,24 +161,24 @@ export class StashService {
|
||||
query: GQL.FindSceneMarkersDocument,
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
scene_marker_filter: sceneMarkerFilter
|
||||
}
|
||||
scene_marker_filter: sceneMarkerFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static useFindStudios(filter: ListFilterModel) {
|
||||
return GQL.useFindStudiosQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter()
|
||||
}
|
||||
filter: filter.makeFindFilter(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static useFindMovies(filter: ListFilterModel) {
|
||||
return GQL.useFindMoviesQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter()
|
||||
}
|
||||
filter: filter.makeFindFilter(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -196,8 +196,8 @@ export class StashService {
|
||||
return GQL.useFindPerformersQuery({
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
performer_filter: performerFilter
|
||||
}
|
||||
performer_filter: performerFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -209,8 +209,8 @@ export class StashService {
|
||||
query: GQL.FindPerformersDocument,
|
||||
variables: {
|
||||
filter: filter.makeFindFilter(),
|
||||
performer_filter: performerFilter
|
||||
}
|
||||
performer_filter: performerFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ export class StashService {
|
||||
"findSceneMarkers",
|
||||
"findScenes",
|
||||
"markerStrings",
|
||||
"sceneMarkerTags"
|
||||
"sceneMarkerTags",
|
||||
];
|
||||
|
||||
public static useSceneMarkerCreate() {
|
||||
@@ -257,7 +257,7 @@ export class StashService {
|
||||
public static useScrapePerformerList(scraperId: string, q: string) {
|
||||
return GQL.useScrapePerformerListQuery({
|
||||
variables: { scraper_id: scraperId, query: q },
|
||||
skip: q === ""
|
||||
skip: q === "",
|
||||
});
|
||||
}
|
||||
public static useScrapePerformer(
|
||||
@@ -265,7 +265,7 @@ export class StashService {
|
||||
scrapedPerformer: GQL.ScrapedPerformerInput
|
||||
) {
|
||||
return GQL.useScrapePerformerQuery({
|
||||
variables: { scraper_id: scraperId, scraped_performer: scrapedPerformer }
|
||||
variables: { scraper_id: scraperId, scraped_performer: scrapedPerformer },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ export class StashService {
|
||||
}
|
||||
public static useValidGalleriesForScene(sceneId: string) {
|
||||
return GQL.useValidGalleriesForSceneQuery({
|
||||
variables: { scene_id: sceneId }
|
||||
variables: { scene_id: sceneId },
|
||||
});
|
||||
}
|
||||
public static useStats() {
|
||||
@@ -308,7 +308,7 @@ export class StashService {
|
||||
public static useLatestVersion() {
|
||||
return GQL.useLatestVersionQuery({
|
||||
notifyOnNetworkStatusChange: true,
|
||||
errorPolicy: "ignore"
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ export class StashService {
|
||||
"findPerformers",
|
||||
"findScenes",
|
||||
"findSceneMarkers",
|
||||
"allPerformers"
|
||||
"allPerformers",
|
||||
];
|
||||
|
||||
public static usePerformerCreate() {
|
||||
@@ -331,7 +331,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.performerMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
public static usePerformerUpdate() {
|
||||
@@ -339,7 +339,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.performerMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
public static usePerformerDestroy() {
|
||||
@@ -347,7 +347,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.performerMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ export class StashService {
|
||||
"findSceneMarkers",
|
||||
"findStudios",
|
||||
"findMovies",
|
||||
"allTags"
|
||||
"allTags",
|
||||
// TODO - add "findTags" when it is implemented
|
||||
];
|
||||
|
||||
@@ -368,7 +368,7 @@ export class StashService {
|
||||
StashService.invalidateQueries(
|
||||
StashService.sceneMutationImpactedQueries
|
||||
),
|
||||
refetchQueries: ["AllTagsForFilter"]
|
||||
refetchQueries: ["AllTagsForFilter"],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ export class StashService {
|
||||
"findSceneMarkers",
|
||||
"findStudios",
|
||||
"findMovies",
|
||||
"allTags"
|
||||
"allTags",
|
||||
];
|
||||
|
||||
public static useBulkSceneUpdate(input: GQL.BulkSceneUpdateInput) {
|
||||
@@ -388,7 +388,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.sceneBulkMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -398,19 +398,19 @@ export class StashService {
|
||||
|
||||
public static useSceneIncrementO(id: string) {
|
||||
return GQL.useSceneIncrementOMutation({
|
||||
variables: { id }
|
||||
variables: { id },
|
||||
});
|
||||
}
|
||||
|
||||
public static useSceneDecrementO(id: string) {
|
||||
return GQL.useSceneDecrementOMutation({
|
||||
variables: { id }
|
||||
variables: { id },
|
||||
});
|
||||
}
|
||||
|
||||
public static useSceneResetO(id: string) {
|
||||
return GQL.useSceneResetOMutation({
|
||||
variables: { id }
|
||||
variables: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -420,20 +420,20 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.sceneMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
public static useSceneGenerateScreenshot() {
|
||||
return GQL.useSceneGenerateScreenshotMutation({
|
||||
update: () => StashService.invalidateQueries(["findScenes"])
|
||||
update: () => StashService.invalidateQueries(["findScenes"]),
|
||||
});
|
||||
}
|
||||
|
||||
private static studioMutationImpactedQueries = [
|
||||
"findStudios",
|
||||
"findScenes",
|
||||
"allStudios"
|
||||
"allStudios",
|
||||
];
|
||||
|
||||
public static useStudioCreate(input: GQL.StudioCreateInput) {
|
||||
@@ -442,7 +442,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.studioMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.studioMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -462,14 +462,14 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.studioMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private static movieMutationImpactedQueries = [
|
||||
"findMovies",
|
||||
"findScenes",
|
||||
"allMovies"
|
||||
"allMovies",
|
||||
];
|
||||
|
||||
public static useMovieCreate(input: GQL.MovieCreateInput) {
|
||||
@@ -478,7 +478,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.movieMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.movieMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ export class StashService {
|
||||
update: () =>
|
||||
StashService.invalidateQueries(
|
||||
StashService.movieMutationImpactedQueries
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -506,20 +506,20 @@ export class StashService {
|
||||
"findScenes",
|
||||
"findSceneMarkers",
|
||||
"sceneMarkerTags",
|
||||
"allTags"
|
||||
"allTags",
|
||||
];
|
||||
|
||||
public static useTagCreate(input: GQL.TagCreateInput) {
|
||||
return GQL.useTagCreateMutation({
|
||||
variables: input,
|
||||
refetchQueries: ["AllTags", "AllTagsForFilter"]
|
||||
refetchQueries: ["AllTags", "AllTagsForFilter"],
|
||||
// update: () => StashService.invalidateQueries(StashService.tagMutationImpactedQueries)
|
||||
});
|
||||
}
|
||||
public static useTagUpdate(input: GQL.TagUpdateInput) {
|
||||
return GQL.useTagUpdateMutation({
|
||||
variables: input,
|
||||
refetchQueries: ["AllTags", "AllTagsForFilter"]
|
||||
refetchQueries: ["AllTags", "AllTagsForFilter"],
|
||||
});
|
||||
}
|
||||
public static useTagDestroy(input: GQL.TagDestroyInput) {
|
||||
@@ -527,21 +527,21 @@ export class StashService {
|
||||
variables: input,
|
||||
refetchQueries: ["AllTags", "AllTagsForFilter"],
|
||||
update: () =>
|
||||
StashService.invalidateQueries(StashService.tagMutationImpactedQueries)
|
||||
StashService.invalidateQueries(StashService.tagMutationImpactedQueries),
|
||||
});
|
||||
}
|
||||
|
||||
public static useConfigureGeneral(input: GQL.ConfigGeneralInput) {
|
||||
return GQL.useConfigureGeneralMutation({
|
||||
variables: { input },
|
||||
refetchQueries: ["Configuration"]
|
||||
refetchQueries: ["Configuration"],
|
||||
});
|
||||
}
|
||||
|
||||
public static useConfigureInterface(input: GQL.ConfigInterfaceInput) {
|
||||
return GQL.useConfigureInterfaceMutation({
|
||||
variables: { input },
|
||||
refetchQueries: ["Configuration"]
|
||||
refetchQueries: ["Configuration"],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -555,19 +555,19 @@ export class StashService {
|
||||
|
||||
public static useLogs() {
|
||||
return GQL.useLogsQuery({
|
||||
fetchPolicy: "no-cache"
|
||||
fetchPolicy: "no-cache",
|
||||
});
|
||||
}
|
||||
|
||||
public static useJobStatus() {
|
||||
return GQL.useJobStatusQuery({
|
||||
fetchPolicy: "no-cache"
|
||||
fetchPolicy: "no-cache",
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateStopJob() {
|
||||
return StashService.client.mutate<GQL.StopJobMutation>({
|
||||
mutation: GQL.StopJobDocument
|
||||
mutation: GQL.StopJobDocument,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -575,8 +575,8 @@ export class StashService {
|
||||
return StashService.client.query<GQL.ScrapeFreeonesQuery>({
|
||||
query: GQL.ScrapeFreeonesDocument,
|
||||
variables: {
|
||||
performer_name: performerName
|
||||
}
|
||||
performer_name: performerName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -588,8 +588,8 @@ export class StashService {
|
||||
query: GQL.ScrapePerformerDocument,
|
||||
variables: {
|
||||
scraper_id: scraperId,
|
||||
scraped_performer: scrapedPerformer
|
||||
}
|
||||
scraped_performer: scrapedPerformer,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -597,8 +597,8 @@ export class StashService {
|
||||
return StashService.client.query<GQL.ScrapePerformerUrlQuery>({
|
||||
query: GQL.ScrapePerformerUrlDocument,
|
||||
variables: {
|
||||
url
|
||||
}
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -606,8 +606,8 @@ export class StashService {
|
||||
return StashService.client.query<GQL.ScrapeSceneUrlQuery>({
|
||||
query: GQL.ScrapeSceneUrlDocument,
|
||||
variables: {
|
||||
url
|
||||
}
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -619,54 +619,54 @@ export class StashService {
|
||||
query: GQL.ScrapeSceneDocument,
|
||||
variables: {
|
||||
scraper_id: scraperId,
|
||||
scene
|
||||
}
|
||||
scene,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataScan(input: GQL.ScanMetadataInput) {
|
||||
return StashService.client.mutate<GQL.MetadataScanMutation>({
|
||||
mutation: GQL.MetadataScanDocument,
|
||||
variables: { input }
|
||||
variables: { input },
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataAutoTag(input: GQL.AutoTagMetadataInput) {
|
||||
return StashService.client.mutate<GQL.MetadataAutoTagMutation>({
|
||||
mutation: GQL.MetadataAutoTagDocument,
|
||||
variables: { input }
|
||||
variables: { input },
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataGenerate(input: GQL.GenerateMetadataInput) {
|
||||
return StashService.client.mutate<GQL.MetadataGenerateMutation>({
|
||||
mutation: GQL.MetadataGenerateDocument,
|
||||
variables: { input }
|
||||
variables: { input },
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataClean() {
|
||||
return StashService.client.mutate<GQL.MetadataCleanMutation>({
|
||||
mutation: GQL.MetadataCleanDocument
|
||||
mutation: GQL.MetadataCleanDocument,
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataExport() {
|
||||
return StashService.client.mutate<GQL.MetadataExportMutation>({
|
||||
mutation: GQL.MetadataExportDocument
|
||||
mutation: GQL.MetadataExportDocument,
|
||||
});
|
||||
}
|
||||
|
||||
public static mutateMetadataImport() {
|
||||
return StashService.client.mutate<GQL.MetadataImportMutation>({
|
||||
mutation: GQL.MetadataImportDocument
|
||||
mutation: GQL.MetadataImportDocument,
|
||||
});
|
||||
}
|
||||
|
||||
public static querySceneByPathRegex(filter: GQL.FindFilterType) {
|
||||
return StashService.client.query<GQL.FindScenesByPathRegexQuery>({
|
||||
query: GQL.FindScenesByPathRegexDocument,
|
||||
variables: { filter }
|
||||
variables: { filter },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -677,26 +677,28 @@ export class StashService {
|
||||
return StashService.client.query<GQL.ParseSceneFilenamesQuery>({
|
||||
query: GQL.ParseSceneFilenamesDocument,
|
||||
variables: { filter, config },
|
||||
fetchPolicy: "network-only"
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
}
|
||||
|
||||
private static stringGenderMap = new Map<string, GQL.GenderEnum>(
|
||||
[["Male", GQL.GenderEnum.Male],
|
||||
private static stringGenderMap = new Map<string, GQL.GenderEnum>([
|
||||
["Male", GQL.GenderEnum.Male],
|
||||
["Female", GQL.GenderEnum.Female],
|
||||
["Transgender Male", GQL.GenderEnum.TransgenderMale],
|
||||
["Transgender Female", GQL.GenderEnum.TransgenderFemale],
|
||||
["Intersex", GQL.GenderEnum.Intersex]]
|
||||
);
|
||||
["Intersex", GQL.GenderEnum.Intersex],
|
||||
]);
|
||||
|
||||
public static genderToString(value?: GQL.GenderEnum) {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const foundEntry = Array.from(StashService.stringGenderMap.entries()).find((e) => {
|
||||
return e[1] === value;
|
||||
});
|
||||
const foundEntry = Array.from(StashService.stringGenderMap.entries()).find(
|
||||
(e) => {
|
||||
return e[1] === value;
|
||||
}
|
||||
);
|
||||
|
||||
if (foundEntry) {
|
||||
return foundEntry[0];
|
||||
|
||||
@@ -16,11 +16,11 @@ import {
|
||||
FindStudiosQueryResult,
|
||||
FindPerformersQueryResult,
|
||||
FindMoviesQueryResult,
|
||||
MovieDataFragment
|
||||
MovieDataFragment,
|
||||
} from "src/core/generated-graphql";
|
||||
import {
|
||||
useInterfaceLocalForage,
|
||||
IInterfaceConfig
|
||||
IInterfaceConfig,
|
||||
} from "src/hooks/LocalForage";
|
||||
import { LoadingIndicator } from "src/components/Shared";
|
||||
import { ListFilter } from "src/components/List/ListFilter";
|
||||
@@ -100,14 +100,14 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
|
||||
const updateInterfaceConfig = useCallback(
|
||||
(updatedFilter: ListFilterModel) => {
|
||||
setInterfaceState(config => {
|
||||
setInterfaceState((config) => {
|
||||
const data = { ...config } as IInterfaceConfig;
|
||||
data.queries = {
|
||||
[options.filterMode]: {
|
||||
filter: updatedFilter.makeQueryParameters(),
|
||||
itemsPerPage: updatedFilter.itemsPerPage,
|
||||
currentPage: updatedFilter.currentPage
|
||||
}
|
||||
currentPage: updatedFilter.currentPage,
|
||||
},
|
||||
};
|
||||
return data;
|
||||
});
|
||||
@@ -133,7 +133,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
sortdir: storedFilter.sortdir,
|
||||
disp: storedFilter.disp,
|
||||
perPage: storedFilter.perPage,
|
||||
...queryFilter
|
||||
...queryFilter,
|
||||
}
|
||||
: storedFilter;
|
||||
|
||||
@@ -161,7 +161,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
options.subComponent,
|
||||
options.filterMode,
|
||||
forageInitialised,
|
||||
updateInterfaceConfig
|
||||
updateInterfaceConfig,
|
||||
]);
|
||||
|
||||
function getFilter() {
|
||||
@@ -221,7 +221,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
const newFilter = _.cloneDeep(filter);
|
||||
|
||||
// Find if we are editing an existing criteria, then modify that. Or create a new one.
|
||||
const existingIndex = newFilter.criteria.findIndex(c => {
|
||||
const existingIndex = newFilter.criteria.findIndex((c) => {
|
||||
// If we modified an existing criterion, then look for the old id.
|
||||
const id = oldId || criterion.getId();
|
||||
return c.getId() === id;
|
||||
@@ -234,7 +234,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
|
||||
// Remove duplicate modifiers
|
||||
newFilter.criteria = newFilter.criteria.filter((obj, pos, arr) => {
|
||||
return arr.map(mapObj => mapObj.getId()).indexOf(obj.getId()) === pos;
|
||||
return arr.map((mapObj) => mapObj.getId()).indexOf(obj.getId()) === pos;
|
||||
});
|
||||
|
||||
newFilter.currentPage = 1;
|
||||
@@ -244,7 +244,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
function onRemoveCriterion(removedCriterion: Criterion) {
|
||||
const newFilter = _.cloneDeep(filter);
|
||||
newFilter.criteria = newFilter.criteria.filter(
|
||||
criterion => criterion.getId() !== removedCriterion.getId()
|
||||
(criterion) => criterion.getId() !== removedCriterion.getId()
|
||||
);
|
||||
newFilter.currentPage = 1;
|
||||
updateQueryParams(newFilter);
|
||||
@@ -281,7 +281,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
const subset = items.slice(start, end + 1);
|
||||
const newSelectedIds: Set<string> = new Set();
|
||||
|
||||
subset.forEach(item => {
|
||||
subset.forEach((item) => {
|
||||
newSelectedIds.add(item.id);
|
||||
});
|
||||
|
||||
@@ -293,12 +293,12 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
let thisIndex = -1;
|
||||
|
||||
if (lastClickedId) {
|
||||
startIndex = items.findIndex(item => {
|
||||
startIndex = items.findIndex((item) => {
|
||||
return item.id === lastClickedId;
|
||||
});
|
||||
}
|
||||
|
||||
thisIndex = items.findIndex(item => {
|
||||
thisIndex = items.findIndex((item) => {
|
||||
return item.id === id;
|
||||
});
|
||||
|
||||
@@ -315,7 +315,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
|
||||
function onSelectAll() {
|
||||
const newSelectedIds: Set<string> = new Set();
|
||||
items.forEach(item => {
|
||||
items.forEach((item) => {
|
||||
newSelectedIds.add(item.id);
|
||||
});
|
||||
|
||||
@@ -334,12 +334,12 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
||||
}
|
||||
|
||||
const otherOperations = options.otherOperations
|
||||
? options.otherOperations.map(o => {
|
||||
? options.otherOperations.map((o) => {
|
||||
return {
|
||||
text: o.text,
|
||||
onClick: () => {
|
||||
o.onClick(result, filter, selectedIds);
|
||||
}
|
||||
},
|
||||
};
|
||||
})
|
||||
: undefined;
|
||||
@@ -401,7 +401,7 @@ export const useScenesList = (props: IListHookOptions<FindScenesQueryResult>) =>
|
||||
getData: (result: FindScenesQueryResult) =>
|
||||
result?.data?.findScenes?.scenes ?? [],
|
||||
getCount: (result: FindScenesQueryResult) =>
|
||||
result?.data?.findScenes?.count ?? 0
|
||||
result?.data?.findScenes?.count ?? 0,
|
||||
});
|
||||
|
||||
export const useSceneMarkersList = (
|
||||
@@ -414,7 +414,7 @@ export const useSceneMarkersList = (
|
||||
getData: (result: FindSceneMarkersQueryResult) =>
|
||||
result?.data?.findSceneMarkers?.scene_markers ?? [],
|
||||
getCount: (result: FindSceneMarkersQueryResult) =>
|
||||
result?.data?.findSceneMarkers?.count ?? 0
|
||||
result?.data?.findSceneMarkers?.count ?? 0,
|
||||
});
|
||||
|
||||
export const useGalleriesList = (
|
||||
@@ -427,7 +427,7 @@ export const useGalleriesList = (
|
||||
getData: (result: FindGalleriesQueryResult) =>
|
||||
result?.data?.findGalleries?.galleries ?? [],
|
||||
getCount: (result: FindGalleriesQueryResult) =>
|
||||
result?.data?.findGalleries?.count ?? 0
|
||||
result?.data?.findGalleries?.count ?? 0,
|
||||
});
|
||||
|
||||
export const useStudiosList = (
|
||||
@@ -440,7 +440,7 @@ export const useStudiosList = (
|
||||
getData: (result: FindStudiosQueryResult) =>
|
||||
result?.data?.findStudios?.studios ?? [],
|
||||
getCount: (result: FindStudiosQueryResult) =>
|
||||
result?.data?.findStudios?.count ?? 0
|
||||
result?.data?.findStudios?.count ?? 0,
|
||||
});
|
||||
|
||||
export const usePerformersList = (
|
||||
@@ -453,7 +453,7 @@ export const usePerformersList = (
|
||||
getData: (result: FindPerformersQueryResult) =>
|
||||
result?.data?.findPerformers?.performers ?? [],
|
||||
getCount: (result: FindPerformersQueryResult) =>
|
||||
result?.data?.findPerformers?.count ?? 0
|
||||
result?.data?.findPerformers?.count ?? 0,
|
||||
});
|
||||
|
||||
export const useMoviesList = (props: IListHookOptions<FindMoviesQueryResult>) =>
|
||||
@@ -464,5 +464,5 @@ export const useMoviesList = (props: IListHookOptions<FindMoviesQueryResult>) =>
|
||||
getData: (result: FindMoviesQueryResult) =>
|
||||
result?.data?.findMovies?.movies ?? [],
|
||||
getCount: (result: FindMoviesQueryResult) =>
|
||||
result?.data?.findMovies?.count ?? 0
|
||||
result?.data?.findMovies?.count ?? 0,
|
||||
});
|
||||
|
||||
@@ -18,9 +18,9 @@ export const ToastProvider: React.FC = ({ children }) => {
|
||||
const [toasts, setToasts] = useState<IActiveToast[]>([]);
|
||||
|
||||
const removeToast = (id: number) =>
|
||||
setToasts(toasts.filter(item => item.id !== id));
|
||||
setToasts(toasts.filter((item) => item.id !== id));
|
||||
|
||||
const toastItems = toasts.map(toast => (
|
||||
const toastItems = toasts.map((toast) => (
|
||||
<Toast
|
||||
autohide
|
||||
key={toast.id}
|
||||
@@ -55,9 +55,9 @@ function createHookObject(toastFunc: (toast: IToast) => void) {
|
||||
toastFunc({
|
||||
variant: "danger",
|
||||
header: "Error",
|
||||
content: error.message ?? error.toString()
|
||||
content: error.message ?? error.toString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export class VideoHoverHook {
|
||||
return;
|
||||
}
|
||||
if (videoTag.paused && !data.isPlaying.current) {
|
||||
videoTag.play().catch(error => {
|
||||
videoTag.play().catch((error) => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ export {
|
||||
useSceneMarkersList,
|
||||
useGalleriesList,
|
||||
useStudiosList,
|
||||
usePerformersList
|
||||
usePerformersList,
|
||||
} from "./ListHook";
|
||||
|
||||
@@ -1,458 +1,465 @@
|
||||
@import "styles/theme";
|
||||
@import "styles/range";
|
||||
@import "styles/scrollbars";
|
||||
@import "src/components/Galleries/styles.scss";
|
||||
@import "src/components/List/styles.scss";
|
||||
@import "src/components/Movies/styles.scss";
|
||||
@import "src/components/Performers/styles.scss";
|
||||
@import "src/components/Scenes/styles.scss";
|
||||
@import "src/components/SceneFilenameParser/styles.scss";
|
||||
@import "src/components/ScenePlayer/styles.scss";
|
||||
@import "src/components/Settings/styles.scss";
|
||||
@import "src/components/Studios/styles.scss";
|
||||
@import "src/components/Shared/styles.scss";
|
||||
@import "src/components/Tags/styles.scss";
|
||||
@import "src/components/Wall/styles.scss";
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
color: $text-color;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
margin: 0;
|
||||
padding: 4rem 0 0 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
code,
|
||||
.code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
.input-control,
|
||||
.text-input {
|
||||
border: 0;
|
||||
box-shadow: 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0), inset 0 0 0 1px rgba(16, 22, 26, .3), inset 0 1px 1px rgba(16, 22, 26, .4);
|
||||
color: $text-color;
|
||||
|
||||
&:focus {
|
||||
border: 0;
|
||||
box-shadow: 0 0 0 1px $primary, 0 0 0 1px $primary, 0 0 0 3px rgba(19, 124, 189, .3), inset 0 0 0 1px rgba(16, 22, 26, .3), inset 0 1px 1px rgba(16, 22, 26, .4);
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input,
|
||||
.text-input:focus,
|
||||
.text-input[readonly] {
|
||||
background-color: $textfield-bg;
|
||||
}
|
||||
|
||||
.input-control,
|
||||
.input-control:focus {
|
||||
background-color: $secondary;
|
||||
}
|
||||
|
||||
textarea.text-input {
|
||||
line-height: 2.5ex;
|
||||
min-height: 12ex;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.table-list a {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.table-list table {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.table-list td,
|
||||
.table-list th {
|
||||
border-left: 1px solid #414c53;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.zoom-0 {
|
||||
width: 240px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-1 {
|
||||
width: 320px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 240px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-2 {
|
||||
width: 480px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 360px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-3 {
|
||||
width: 640px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 480px;
|
||||
}
|
||||
|
||||
.portrait {
|
||||
height: 480px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-card-video {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* this is a bit of a hack, because we can't supply direct class names
|
||||
to the react-select controls */
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
|
||||
div.react-select__control {
|
||||
background-color: $secondary;
|
||||
border-color: $secondary;
|
||||
color: $text-color;
|
||||
cursor: pointer;
|
||||
|
||||
.react-select__single-value,
|
||||
.react-select__input {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.react-select__multi-value {
|
||||
background-color: $muted-gray;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
div.react-select__menu {
|
||||
background-color: $secondary;
|
||||
color: $text-color;
|
||||
|
||||
.react-select__option {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.react-select__option--is-focused {
|
||||
background-color: #8a9ba826;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't want to override this for dialogs, which are light colored */
|
||||
.modal {
|
||||
div.react-select__control {
|
||||
background-color: #fff;
|
||||
border-color: inherit;
|
||||
color: $dark-text;
|
||||
|
||||
.react-select__single-value,
|
||||
.react-select__input {
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.react-select__multi-value {
|
||||
background-color: #fff;
|
||||
color: $dark-text;
|
||||
}
|
||||
}
|
||||
|
||||
div.react-select__menu {
|
||||
background-color: #fff;
|
||||
color: $text-color;
|
||||
|
||||
.react-select__option {
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.react-select__option--is-focused {
|
||||
background-color: rgba(167, 182, 194, .3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
|
||||
.image-thumbnail {
|
||||
height: 100px;
|
||||
min-width: 50px;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
}
|
||||
|
||||
.card-image {
|
||||
height: 30rem;
|
||||
min-width: 11.25rem;
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
background-color: $muted-gray;
|
||||
color: $dark-text;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
margin: 5px;
|
||||
padding: 2px 6px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: none;
|
||||
border: none;
|
||||
bottom: 2px;
|
||||
color: $dark-text;
|
||||
font-size: 12px;
|
||||
line-height: 1rem;
|
||||
margin-left: .5rem;
|
||||
opacity: .5;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
|
||||
&:hover {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container,
|
||||
.operation-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
|
||||
.filter-item,
|
||||
.operation-item {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.rating-5 {
|
||||
background: #ff2f39;
|
||||
}
|
||||
|
||||
.rating-4 {
|
||||
background: $red1;
|
||||
}
|
||||
|
||||
.rating-3 {
|
||||
background: $orange1;
|
||||
}
|
||||
|
||||
.rating-2 {
|
||||
background: $sepia1;
|
||||
}
|
||||
|
||||
.rating-1 {
|
||||
background: $dark-gray5;
|
||||
}
|
||||
|
||||
.rating-banner {
|
||||
color: #fff;
|
||||
display: block;
|
||||
font-size: .86rem;
|
||||
font-weight: 400;
|
||||
left: -46px;
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.6rem;
|
||||
padding: 6px 45px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-size-adjust: none;
|
||||
top: 14px;
|
||||
transform: rotate(-36deg);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #30404d;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 0 1px rgba(16, 22, 26, .4), 0 0 0 rgba(16, 22, 26, 0), 0 0 0 rgba(16, 22, 26, 0);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
left: 45%;
|
||||
max-width: 350px;
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
z-index: 1031;
|
||||
|
||||
.success {
|
||||
background-color: $success;
|
||||
}
|
||||
|
||||
.danger {
|
||||
background-color: $danger;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: $warning;
|
||||
}
|
||||
|
||||
.toast {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.toast-header {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: $text-color;
|
||||
|
||||
.close {
|
||||
color: $text-color;
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image-input {
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
input[type=file], /* FF, IE7+, chrome (except button) */
|
||||
input[type=file]::-webkit-file-upload-button { /* chromes and blink button */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[type=file] {
|
||||
display: block;
|
||||
filter: alpha(opacity=0);
|
||||
font-size: 999px;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: right;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin: 0 .4rem;
|
||||
}
|
||||
|
||||
.btn .fa-icon {
|
||||
&:last-child:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-icon {
|
||||
padding: 3px 6px;
|
||||
|
||||
img {
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
padding: .25rem 1rem;
|
||||
|
||||
.nav-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.btn {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
white-space: "pre-wrap";
|
||||
}
|
||||
|
||||
.stats {
|
||||
&-element {
|
||||
flex-grow: 1;
|
||||
margin: auto .5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3vw;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
@import "styles/theme";
|
||||
@import "styles/range";
|
||||
@import "styles/scrollbars";
|
||||
@import "src/components/Galleries/styles.scss";
|
||||
@import "src/components/List/styles.scss";
|
||||
@import "src/components/Movies/styles.scss";
|
||||
@import "src/components/Performers/styles.scss";
|
||||
@import "src/components/Scenes/styles.scss";
|
||||
@import "src/components/SceneFilenameParser/styles.scss";
|
||||
@import "src/components/ScenePlayer/styles.scss";
|
||||
@import "src/components/Settings/styles.scss";
|
||||
@import "src/components/Studios/styles.scss";
|
||||
@import "src/components/Shared/styles.scss";
|
||||
@import "src/components/Tags/styles.scss";
|
||||
@import "src/components/Wall/styles.scss";
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
color: $text-color;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
margin: 0;
|
||||
padding: 4rem 0 0 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
code,
|
||||
.code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
.input-control,
|
||||
.text-input {
|
||||
border: 0;
|
||||
box-shadow: 0 0 0 0 rgba(19, 124, 189, 0), 0 0 0 0 rgba(19, 124, 189, 0),
|
||||
0 0 0 0 rgba(19, 124, 189, 0), inset 0 0 0 1px rgba(16, 22, 26, 0.3),
|
||||
inset 0 1px 1px rgba(16, 22, 26, 0.4);
|
||||
color: $text-color;
|
||||
|
||||
&:focus {
|
||||
border: 0;
|
||||
box-shadow: 0 0 0 1px $primary, 0 0 0 1px $primary,
|
||||
0 0 0 3px rgba(19, 124, 189, 0.3), inset 0 0 0 1px rgba(16, 22, 26, 0.3),
|
||||
inset 0 1px 1px rgba(16, 22, 26, 0.4);
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input,
|
||||
.text-input:focus,
|
||||
.text-input[readonly] {
|
||||
background-color: $textfield-bg;
|
||||
}
|
||||
|
||||
.input-control,
|
||||
.input-control:focus {
|
||||
background-color: $secondary;
|
||||
}
|
||||
|
||||
textarea.text-input {
|
||||
line-height: 2.5ex;
|
||||
min-height: 12ex;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.table-list a {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.table-list table {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.table-list td,
|
||||
.table-list th {
|
||||
border-left: 1px solid #414c53;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.zoom-0 {
|
||||
width: 240px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-1 {
|
||||
width: 320px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 240px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-2 {
|
||||
width: 480px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 360px;
|
||||
}
|
||||
|
||||
.previewable.portrait {
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-3 {
|
||||
width: 640px;
|
||||
|
||||
.scene-card-video {
|
||||
max-height: 480px;
|
||||
}
|
||||
|
||||
.portrait {
|
||||
height: 480px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scene-card-video {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* this is a bit of a hack, because we can't supply direct class names
|
||||
to the react-select controls */
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
|
||||
div.react-select__control {
|
||||
background-color: $secondary;
|
||||
border-color: $secondary;
|
||||
color: $text-color;
|
||||
cursor: pointer;
|
||||
|
||||
.react-select__single-value,
|
||||
.react-select__input {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.react-select__multi-value {
|
||||
background-color: $muted-gray;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
div.react-select__menu {
|
||||
background-color: $secondary;
|
||||
color: $text-color;
|
||||
|
||||
.react-select__option {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.react-select__option--is-focused {
|
||||
background-color: #8a9ba826;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't want to override this for dialogs, which are light colored */
|
||||
.modal {
|
||||
div.react-select__control {
|
||||
background-color: #fff;
|
||||
border-color: inherit;
|
||||
color: $dark-text;
|
||||
|
||||
.react-select__single-value,
|
||||
.react-select__input {
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.react-select__multi-value {
|
||||
background-color: #fff;
|
||||
color: $dark-text;
|
||||
}
|
||||
}
|
||||
|
||||
div.react-select__menu {
|
||||
background-color: #fff;
|
||||
color: $text-color;
|
||||
|
||||
.react-select__option {
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
.react-select__option--is-focused {
|
||||
background-color: rgba(167, 182, 194, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
|
||||
.image-thumbnail {
|
||||
height: 100px;
|
||||
min-width: 50px;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
}
|
||||
|
||||
.card-image {
|
||||
height: 30rem;
|
||||
min-width: 11.25rem;
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
background-color: $muted-gray;
|
||||
color: $dark-text;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
margin: 5px;
|
||||
padding: 2px 6px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: none;
|
||||
border: none;
|
||||
bottom: 2px;
|
||||
color: $dark-text;
|
||||
font-size: 12px;
|
||||
line-height: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.5;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
|
||||
&:hover {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container,
|
||||
.operation-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
|
||||
.filter-item,
|
||||
.operation-item {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.rating-5 {
|
||||
background: #ff2f39;
|
||||
}
|
||||
|
||||
.rating-4 {
|
||||
background: $red1;
|
||||
}
|
||||
|
||||
.rating-3 {
|
||||
background: $orange1;
|
||||
}
|
||||
|
||||
.rating-2 {
|
||||
background: $sepia1;
|
||||
}
|
||||
|
||||
.rating-1 {
|
||||
background: $dark-gray5;
|
||||
}
|
||||
|
||||
.rating-banner {
|
||||
color: #fff;
|
||||
display: block;
|
||||
font-size: 0.86rem;
|
||||
font-weight: 400;
|
||||
left: -46px;
|
||||
letter-spacing: 1px;
|
||||
line-height: 1.6rem;
|
||||
padding: 6px 45px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-size-adjust: none;
|
||||
top: 14px;
|
||||
transform: rotate(-36deg);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #30404d;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 0 1px rgba(16, 22, 26, 0.4), 0 0 0 rgba(16, 22, 26, 0),
|
||||
0 0 0 rgba(16, 22, 26, 0);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
left: 45%;
|
||||
max-width: 350px;
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
z-index: 1031;
|
||||
|
||||
.success {
|
||||
background-color: $success;
|
||||
}
|
||||
|
||||
.danger {
|
||||
background-color: $danger;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: $warning;
|
||||
}
|
||||
|
||||
.toast {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.toast-header {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: $text-color;
|
||||
|
||||
.close {
|
||||
color: $text-color;
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image-input {
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
input[type="file"], /* FF, IE7+, chrome (except button) */
|
||||
input[type="file"]::-webkit-file-upload-button {
|
||||
/* chromes and blink button */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[type="file"] {
|
||||
display: block;
|
||||
filter: alpha(opacity=0);
|
||||
font-size: 999px;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: right;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin: 0 0.4rem;
|
||||
}
|
||||
|
||||
.btn .fa-icon {
|
||||
&:last-child:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-icon {
|
||||
padding: 3px 6px;
|
||||
|
||||
img {
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
padding: 0.25rem 1rem;
|
||||
|
||||
.nav-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fa-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.btn {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
white-space: "pre-wrap";
|
||||
}
|
||||
|
||||
.stats {
|
||||
&-element {
|
||||
flex-grow: 1;
|
||||
margin: auto 0.5rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3vw;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ import de from "./de.json";
|
||||
|
||||
export default {
|
||||
en,
|
||||
de
|
||||
de,
|
||||
};
|
||||
|
||||
@@ -129,7 +129,7 @@ export abstract class Criterion {
|
||||
public getLabelValue(): string {
|
||||
if (typeof this.value === "string") return this.value;
|
||||
if (typeof this.value === "number") return this.value.toString();
|
||||
return this.value.map(v => v.label).join(", ");
|
||||
return this.value.map((v) => v.label).join(", ");
|
||||
}
|
||||
|
||||
public getLabel(): string {
|
||||
@@ -221,7 +221,7 @@ export class StringCriterion extends Criterion {
|
||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull)
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||
];
|
||||
public options: string[] | undefined;
|
||||
public value: string = "";
|
||||
@@ -255,7 +255,7 @@ export class NumberCriterion extends Criterion {
|
||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull)
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||
];
|
||||
public options: number[] | undefined;
|
||||
public value: number = 0;
|
||||
@@ -287,7 +287,7 @@ export class DurationCriterion extends Criterion {
|
||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan)
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||
];
|
||||
public options: number[] | undefined;
|
||||
public value: number = 0;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { CriterionModifier } from "src/core/generated-graphql";
|
||||
import { StashService } from "src/core/StashService";
|
||||
import {
|
||||
Criterion,
|
||||
CriterionType,
|
||||
ICriterionOption,
|
||||
} from "./criterion";
|
||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
||||
|
||||
export class GenderCriterion extends Criterion {
|
||||
public type: CriterionType = "gender";
|
||||
|
||||
@@ -13,7 +13,7 @@ export class IsMissingCriterion extends Criterion {
|
||||
"gallery",
|
||||
"studio",
|
||||
"movie",
|
||||
"performers"
|
||||
"performers",
|
||||
];
|
||||
public value: string = "";
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ export class MoviesCriterion extends Criterion {
|
||||
public modifier = CriterionModifier.Includes;
|
||||
public modifierOptions = [
|
||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes)
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
||||
];
|
||||
public options: IOptionType[] = [];
|
||||
public value: ILabeledId[] = [];
|
||||
|
||||
public encodeValue() {
|
||||
return this.value.map(o => {
|
||||
return this.value.map((o) => {
|
||||
return encodeILabeledId(o);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ export class PerformersCriterion extends Criterion {
|
||||
public modifierOptions = [
|
||||
Criterion.getModifierOption(CriterionModifier.IncludesAll),
|
||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes)
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
||||
];
|
||||
public options: IOptionType[] = [];
|
||||
public value: ILabeledId[] = [];
|
||||
|
||||
public encodeValue() {
|
||||
return this.value.map(o => {
|
||||
return this.value.map((o) => {
|
||||
return encodeILabeledId(o);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class RatingCriterion extends Criterion {
|
||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull)
|
||||
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||
];
|
||||
public options: number[] = [1, 2, 3, 4, 5];
|
||||
public value: number = 0;
|
||||
|
||||
@@ -8,13 +8,13 @@ export class StudiosCriterion extends Criterion {
|
||||
public modifier = CriterionModifier.Includes;
|
||||
public modifierOptions = [
|
||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes)
|
||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
||||
];
|
||||
public options: IOptionType[] = [];
|
||||
public value: ILabeledId[] = [];
|
||||
|
||||
public encodeValue() {
|
||||
return this.value.map(o => {
|
||||
return this.value.map((o) => {
|
||||
return encodeILabeledId(o);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export class TagsCriterion extends Criterion {
|
||||
public modifierOptions = [
|
||||
Criterion.getModifierOption(GQL.CriterionModifier.IncludesAll),
|
||||
Criterion.getModifierOption(GQL.CriterionModifier.Includes),
|
||||
Criterion.getModifierOption(GQL.CriterionModifier.Excludes)
|
||||
Criterion.getModifierOption(GQL.CriterionModifier.Excludes),
|
||||
];
|
||||
public options: IOptionType[] = [];
|
||||
public value: ILabeledId[] = [];
|
||||
@@ -24,7 +24,7 @@ export class TagsCriterion extends Criterion {
|
||||
}
|
||||
|
||||
public encodeValue() {
|
||||
return this.value.map(o => {
|
||||
return this.value.map((o) => {
|
||||
return encodeILabeledId(o);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
CriterionType,
|
||||
StringCriterion,
|
||||
NumberCriterion,
|
||||
DurationCriterion
|
||||
DurationCriterion,
|
||||
} from "./criterion";
|
||||
import { FavoriteCriterion } from "./favorite";
|
||||
import { HasMarkersCriterion } from "./has-markers";
|
||||
@@ -57,11 +57,11 @@ export function makeCriteria(type: CriterionType = "none") {
|
||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan)
|
||||
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||
];
|
||||
return ret;
|
||||
}
|
||||
case "gender":
|
||||
case "gender":
|
||||
return new GenderCriterion();
|
||||
case "ethnicity":
|
||||
case "country":
|
||||
|
||||
@@ -5,8 +5,9 @@ import {
|
||||
ResolutionEnum,
|
||||
SceneFilterType,
|
||||
SceneMarkerFilterType,
|
||||
SortDirectionEnum
|
||||
SortDirectionEnum,
|
||||
} from "src/core/generated-graphql";
|
||||
import { StashService } from "src/core/StashService";
|
||||
import {
|
||||
Criterion,
|
||||
ICriterionOption,
|
||||
@@ -14,40 +15,39 @@ import {
|
||||
CriterionOption,
|
||||
NumberCriterion,
|
||||
StringCriterion,
|
||||
DurationCriterion
|
||||
DurationCriterion,
|
||||
} from "./criteria/criterion";
|
||||
import {
|
||||
FavoriteCriterion,
|
||||
FavoriteCriterionOption
|
||||
FavoriteCriterionOption,
|
||||
} from "./criteria/favorite";
|
||||
import {
|
||||
HasMarkersCriterion,
|
||||
HasMarkersCriterionOption
|
||||
HasMarkersCriterionOption,
|
||||
} from "./criteria/has-markers";
|
||||
import {
|
||||
IsMissingCriterion,
|
||||
IsMissingCriterionOption
|
||||
IsMissingCriterionOption,
|
||||
} from "./criteria/is-missing";
|
||||
import { NoneCriterionOption } from "./criteria/none";
|
||||
import {
|
||||
PerformersCriterion,
|
||||
PerformersCriterionOption
|
||||
PerformersCriterionOption,
|
||||
} from "./criteria/performers";
|
||||
import { RatingCriterion, RatingCriterionOption } from "./criteria/rating";
|
||||
import {
|
||||
ResolutionCriterion,
|
||||
ResolutionCriterionOption
|
||||
ResolutionCriterionOption,
|
||||
} from "./criteria/resolution";
|
||||
import { StudiosCriterion, StudiosCriterionOption } from "./criteria/studios";
|
||||
import {
|
||||
SceneTagsCriterionOption,
|
||||
TagsCriterion,
|
||||
TagsCriterionOption
|
||||
TagsCriterionOption,
|
||||
} from "./criteria/tags";
|
||||
import { makeCriteria } from "./criteria/utils";
|
||||
import { DisplayMode, FilterMode } from "./types";
|
||||
import { GenderCriterionOption, GenderCriterion } from "./criteria/gender";
|
||||
import { StashService } from "src/core/StashService";
|
||||
import { MoviesCriterionOption, MoviesCriterion } from "./criteria/movies";
|
||||
|
||||
interface IQueryParameters {
|
||||
@@ -64,7 +64,7 @@ const DEFAULT_PARAMS = {
|
||||
sortDirection: SortDirectionEnum.Asc,
|
||||
displayMode: DisplayMode.Grid,
|
||||
currentPage: 1,
|
||||
itemsPerPage: 40
|
||||
itemsPerPage: 40,
|
||||
};
|
||||
|
||||
// TODO: handle customCriteria
|
||||
@@ -101,12 +101,12 @@ export class ListFilterModel {
|
||||
"duration",
|
||||
"framerate",
|
||||
"bitrate",
|
||||
"random"
|
||||
"random",
|
||||
];
|
||||
this.displayModeOptions = [
|
||||
DisplayMode.Grid,
|
||||
DisplayMode.List,
|
||||
DisplayMode.Wall
|
||||
DisplayMode.Wall,
|
||||
];
|
||||
this.criterionOptions = [
|
||||
new NoneCriterionOption(),
|
||||
@@ -119,7 +119,7 @@ export class ListFilterModel {
|
||||
new TagsCriterionOption(),
|
||||
new PerformersCriterionOption(),
|
||||
new StudiosCriterionOption(),
|
||||
new MoviesCriterionOption()
|
||||
new MoviesCriterionOption(),
|
||||
];
|
||||
break;
|
||||
case FilterMode.Performers: {
|
||||
@@ -138,7 +138,7 @@ export class ListFilterModel {
|
||||
"career_length",
|
||||
"tattoos",
|
||||
"piercings",
|
||||
"aliases"
|
||||
"aliases",
|
||||
];
|
||||
|
||||
this.criterionOptions = [
|
||||
@@ -148,7 +148,7 @@ export class ListFilterModel {
|
||||
];
|
||||
|
||||
this.criterionOptions = this.criterionOptions.concat(
|
||||
numberCriteria.concat(stringCriteria).map(c => {
|
||||
numberCriteria.concat(stringCriteria).map((c) => {
|
||||
return ListFilterModel.createCriterionOption(c);
|
||||
})
|
||||
);
|
||||
@@ -179,14 +179,14 @@ export class ListFilterModel {
|
||||
"seconds",
|
||||
"scene_id",
|
||||
"random",
|
||||
"scenes_updated_at"
|
||||
"scenes_updated_at",
|
||||
];
|
||||
this.displayModeOptions = [DisplayMode.Wall];
|
||||
this.criterionOptions = [
|
||||
new NoneCriterionOption(),
|
||||
new TagsCriterionOption(),
|
||||
new SceneTagsCriterionOption(),
|
||||
new PerformersCriterionOption()
|
||||
new PerformersCriterionOption(),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
@@ -244,7 +244,7 @@ export class ListFilterModel {
|
||||
jsonParameters = [params.c];
|
||||
}
|
||||
|
||||
jsonParameters.forEach(jsonString => {
|
||||
jsonParameters.forEach((jsonString) => {
|
||||
const encodedCriterion = JSON.parse(jsonString);
|
||||
const criterion = makeCriteria(encodedCriterion.type);
|
||||
// it's possible that we have unsupported criteria. Just skip if so.
|
||||
@@ -281,7 +281,7 @@ export class ListFilterModel {
|
||||
|
||||
public makeQueryParameters(): string {
|
||||
const encodedCriteria: string[] = [];
|
||||
this.criteria.forEach(criterion => {
|
||||
this.criteria.forEach((criterion) => {
|
||||
const encodedCriterion: Partial<Criterion> = {
|
||||
type: criterion.type,
|
||||
// #394 - the presence of a # symbol results in the query URL being
|
||||
@@ -289,7 +289,7 @@ export class ListFilterModel {
|
||||
// call below, but this results in a URL that gets pretty long and ugly.
|
||||
// Instead, we'll encode the criteria values.
|
||||
value: criterion.encodeValue(),
|
||||
modifier: criterion.modifier
|
||||
modifier: criterion.modifier,
|
||||
};
|
||||
const jsonCriterion = JSON.stringify(encodedCriterion);
|
||||
encodedCriteria.push(jsonCriterion);
|
||||
@@ -312,7 +312,7 @@ export class ListFilterModel {
|
||||
this.currentPage !== DEFAULT_PARAMS.currentPage
|
||||
? this.currentPage
|
||||
: undefined,
|
||||
c: encodedCriteria
|
||||
c: encodedCriteria,
|
||||
};
|
||||
return queryString.stringify(result, { encode: false });
|
||||
}
|
||||
@@ -325,19 +325,19 @@ export class ListFilterModel {
|
||||
page: this.currentPage,
|
||||
per_page: this.itemsPerPage,
|
||||
sort: this.getSortBy(),
|
||||
direction: this.sortDirection
|
||||
direction: this.sortDirection,
|
||||
};
|
||||
}
|
||||
|
||||
public makeSceneFilter(): SceneFilterType {
|
||||
const result: SceneFilterType = {};
|
||||
this.criteria.forEach(criterion => {
|
||||
this.criteria.forEach((criterion) => {
|
||||
switch (criterion.type) {
|
||||
case "rating": {
|
||||
const ratingCrit = criterion as RatingCriterion;
|
||||
result.rating = {
|
||||
value: ratingCrit.value,
|
||||
modifier: ratingCrit.modifier
|
||||
modifier: ratingCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -345,7 +345,7 @@ export class ListFilterModel {
|
||||
const oCounterCrit = criterion as NumberCriterion;
|
||||
result.o_counter = {
|
||||
value: oCounterCrit.value,
|
||||
modifier: oCounterCrit.modifier
|
||||
modifier: oCounterCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -374,7 +374,7 @@ export class ListFilterModel {
|
||||
const durationCrit = criterion as DurationCriterion;
|
||||
result.duration = {
|
||||
value: durationCrit.value,
|
||||
modifier: durationCrit.modifier
|
||||
modifier: durationCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -387,32 +387,32 @@ export class ListFilterModel {
|
||||
case "tags": {
|
||||
const tagsCrit = criterion as TagsCriterion;
|
||||
result.tags = {
|
||||
value: tagsCrit.value.map(tag => tag.id),
|
||||
modifier: tagsCrit.modifier
|
||||
value: tagsCrit.value.map((tag) => tag.id),
|
||||
modifier: tagsCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "performers": {
|
||||
const perfCrit = criterion as PerformersCriterion;
|
||||
result.performers = {
|
||||
value: perfCrit.value.map(perf => perf.id),
|
||||
modifier: perfCrit.modifier
|
||||
value: perfCrit.value.map((perf) => perf.id),
|
||||
modifier: perfCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "studios": {
|
||||
const studCrit = criterion as StudiosCriterion;
|
||||
result.studios = {
|
||||
value: studCrit.value.map(studio => studio.id),
|
||||
modifier: studCrit.modifier
|
||||
value: studCrit.value.map((studio) => studio.id),
|
||||
modifier: studCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "movies": {
|
||||
const movCrit = criterion as MoviesCriterion;
|
||||
result.movies = {
|
||||
value: movCrit.value.map(movie => movie.id),
|
||||
modifier: movCrit.modifier
|
||||
value: movCrit.value.map((movie) => movie.id),
|
||||
modifier: movCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -424,7 +424,7 @@ export class ListFilterModel {
|
||||
|
||||
public makePerformerFilter(): PerformerFilterType {
|
||||
const result: PerformerFilterType = {};
|
||||
this.criteria.forEach(criterion => {
|
||||
this.criteria.forEach((criterion) => {
|
||||
switch (criterion.type) {
|
||||
case "favorite":
|
||||
result.filter_favorites =
|
||||
@@ -434,7 +434,7 @@ export class ListFilterModel {
|
||||
const byCrit = criterion as NumberCriterion;
|
||||
result.birth_year = {
|
||||
value: byCrit.value,
|
||||
modifier: byCrit.modifier
|
||||
modifier: byCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -447,7 +447,7 @@ export class ListFilterModel {
|
||||
const ethCrit = criterion as StringCriterion;
|
||||
result.ethnicity = {
|
||||
value: ethCrit.value,
|
||||
modifier: ethCrit.modifier
|
||||
modifier: ethCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -455,7 +455,7 @@ export class ListFilterModel {
|
||||
const cntryCrit = criterion as StringCriterion;
|
||||
result.country = {
|
||||
value: cntryCrit.value,
|
||||
modifier: cntryCrit.modifier
|
||||
modifier: cntryCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -473,7 +473,7 @@ export class ListFilterModel {
|
||||
const mCrit = criterion as StringCriterion;
|
||||
result.measurements = {
|
||||
value: mCrit.value,
|
||||
modifier: mCrit.modifier
|
||||
modifier: mCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -486,7 +486,7 @@ export class ListFilterModel {
|
||||
const clCrit = criterion as StringCriterion;
|
||||
result.career_length = {
|
||||
value: clCrit.value,
|
||||
modifier: clCrit.modifier
|
||||
modifier: clCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -507,7 +507,10 @@ export class ListFilterModel {
|
||||
}
|
||||
case "gender": {
|
||||
const gCrit = criterion as GenderCriterion;
|
||||
result.gender = { value: StashService.stringToGender(gCrit.value), modifier: gCrit.modifier };
|
||||
result.gender = {
|
||||
value: StashService.stringToGender(gCrit.value),
|
||||
modifier: gCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
// no default
|
||||
@@ -518,29 +521,29 @@ export class ListFilterModel {
|
||||
|
||||
public makeSceneMarkerFilter(): SceneMarkerFilterType {
|
||||
const result: SceneMarkerFilterType = {};
|
||||
this.criteria.forEach(criterion => {
|
||||
this.criteria.forEach((criterion) => {
|
||||
switch (criterion.type) {
|
||||
case "tags": {
|
||||
const tagsCrit = criterion as TagsCriterion;
|
||||
result.tags = {
|
||||
value: tagsCrit.value.map(tag => tag.id),
|
||||
modifier: tagsCrit.modifier
|
||||
value: tagsCrit.value.map((tag) => tag.id),
|
||||
modifier: tagsCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "sceneTags": {
|
||||
const sceneTagsCrit = criterion as TagsCriterion;
|
||||
result.scene_tags = {
|
||||
value: sceneTagsCrit.value.map(tag => tag.id),
|
||||
modifier: sceneTagsCrit.modifier
|
||||
value: sceneTagsCrit.value.map((tag) => tag.id),
|
||||
modifier: sceneTagsCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "performers": {
|
||||
const performersCrit = criterion as PerformersCriterion;
|
||||
result.performers = {
|
||||
value: performersCrit.value.map(performer => performer.id),
|
||||
modifier: performersCrit.modifier
|
||||
value: performersCrit.value.map((performer) => performer.id),
|
||||
modifier: performersCrit.modifier,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export enum DisplayMode {
|
||||
Grid,
|
||||
List,
|
||||
Wall
|
||||
Wall,
|
||||
}
|
||||
|
||||
export enum FilterMode {
|
||||
@@ -12,7 +12,7 @@ export enum FilterMode {
|
||||
Studios,
|
||||
Galleries,
|
||||
SceneMarkers,
|
||||
Movies
|
||||
Movies,
|
||||
}
|
||||
|
||||
export interface ILabeledId {
|
||||
|
||||
@@ -30,7 +30,7 @@ interface IConfig {
|
||||
function registerValidSW(swUrl: string, config?: IConfig) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
.then((registration) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
@@ -67,7 +67,7 @@ function registerValidSW(swUrl: string, config?: IConfig) {
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error("Error during service worker registration:", error);
|
||||
});
|
||||
}
|
||||
@@ -75,7 +75,7 @@ function registerValidSW(swUrl: string, config?: IConfig) {
|
||||
function checkValidServiceWorker(swUrl: string, config?: IConfig) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (
|
||||
@@ -83,7 +83,7 @@ function checkValidServiceWorker(swUrl: string, config?: IConfig) {
|
||||
(contentType != null && contentType.indexOf("javascript") === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
@@ -139,7 +139,7 @@ export function register(config?: IConfig) {
|
||||
|
||||
export function unregister() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
input[type=range] {
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
@@ -14,7 +14,7 @@ input[type=range] {
|
||||
}
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
animate: .2s;
|
||||
animate: 0.2s;
|
||||
background: #137cbd;
|
||||
border: 0 solid #000101;
|
||||
border-radius: 25px;
|
||||
@@ -41,7 +41,7 @@ input[type=range] {
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
animate: .2s;
|
||||
animate: 0.2s;
|
||||
background: #137cbd;
|
||||
border: 0 solid #000101;
|
||||
border-radius: 25px;
|
||||
@@ -62,7 +62,7 @@ input[type=range] {
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
animate: .2s;
|
||||
animate: 0.2s;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
textarea::selection,
|
||||
input::selection {
|
||||
background-color: rgba(100, 100, 100, .4);
|
||||
background-color: rgba(100, 100, 100, 0.4);
|
||||
color: $dark-text;
|
||||
}
|
||||
|
||||
@@ -24,21 +24,21 @@ body ::-webkit-scrollbar {
|
||||
}
|
||||
|
||||
body ::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, .1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
body ::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, .25);
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: color .2s ease;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
body ::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(0, 0, 0, .15);
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
body ::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(128, 135, 139, .8);
|
||||
background: rgba(128, 135, 139, 0.8);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/* Blueprint dark theme */
|
||||
|
||||
$secondary: #394b59;
|
||||
@@ -13,7 +12,7 @@ $theme-colors: (
|
||||
success: $success,
|
||||
warning: $warning,
|
||||
danger: $danger,
|
||||
dark: #394b59
|
||||
dark: #394b59,
|
||||
);
|
||||
|
||||
$body-bg: #202b33;
|
||||
@@ -25,7 +24,7 @@ $pre-color: $text-color;
|
||||
$navbar-dark-color: rgb(245, 248, 250);
|
||||
$popover-bg: $secondary;
|
||||
$dark-text: #182026;
|
||||
$textfield-bg: rgba(16, 22, 26, .3);
|
||||
$textfield-bg: rgba(16, 22, 26, 0.3);
|
||||
|
||||
@import "node_modules/bootstrap/scss/bootstrap";
|
||||
|
||||
@@ -37,7 +36,7 @@ $dark-gray5: #394b59;
|
||||
|
||||
.btn.active:not(.disabled),
|
||||
.btn.active.minimal:not(.disabled) {
|
||||
background-color: rgba(138, 155, 168, .3);
|
||||
background-color: rgba(138, 155, 168, 0.3);
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
@@ -49,12 +48,12 @@ button.minimal {
|
||||
transition: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba(138, 155, 168, .15);
|
||||
background: rgba(138, 155, 168, 0.15);
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(138, 155, 168, .3);
|
||||
background: rgba(138, 155, 168, 0.3);
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +73,7 @@ hr {
|
||||
.nav-tabs {
|
||||
border: none;
|
||||
margin: auto;
|
||||
margin-bottom: .5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.nav-link {
|
||||
border: none;
|
||||
@@ -102,7 +101,7 @@ hr {
|
||||
}
|
||||
|
||||
.table-striped tr:nth-child(odd) td {
|
||||
background: rgba(92, 112, 128, .15);
|
||||
background: rgba(92, 112, 128, 0.15);
|
||||
}
|
||||
|
||||
.table {
|
||||
@@ -120,7 +119,7 @@ hr {
|
||||
td {
|
||||
border: none;
|
||||
border-color: #414c53;
|
||||
padding: .25rem .75rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
|
||||
@@ -47,5 +47,5 @@ const stringToSeconds = (v: string) => {
|
||||
|
||||
export default {
|
||||
secondsToString,
|
||||
stringToSeconds
|
||||
stringToSeconds,
|
||||
};
|
||||
|
||||
@@ -36,6 +36,6 @@ const usePasteImage = (onLoadEnd: (this: FileReader) => void) => {
|
||||
|
||||
const Image = {
|
||||
onImageChange,
|
||||
usePasteImage
|
||||
usePasteImage,
|
||||
};
|
||||
export default Image;
|
||||
|
||||
@@ -4,5 +4,5 @@ const getPlayer = () => (window as any).jwplayer(playerID);
|
||||
|
||||
export default {
|
||||
playerID,
|
||||
getPlayer
|
||||
getPlayer,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ const makePerformerScenesUrl = (
|
||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
||||
const criterion = new PerformersCriterion();
|
||||
criterion.value = [
|
||||
{ id: performer.id, label: performer.name || `Performer ${performer.id}` }
|
||||
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||
];
|
||||
filter.criteria.push(criterion);
|
||||
return `/scenes?${filter.makeQueryParameters()}`;
|
||||
@@ -24,7 +24,7 @@ const makeStudioScenesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
||||
const criterion = new StudiosCriterion();
|
||||
criterion.value = [
|
||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` }
|
||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||
];
|
||||
filter.criteria.push(criterion);
|
||||
return `/scenes?${filter.makeQueryParameters()}`;
|
||||
@@ -35,7 +35,7 @@ const makeMovieScenesUrl = (movie: Partial<GQL.MovieDataFragment>) => {
|
||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
||||
const criterion = new MoviesCriterion();
|
||||
criterion.value = [
|
||||
{ id: movie.id, label: movie.name || `Movie ${movie.id}` }
|
||||
{ id: movie.id, label: movie.name || `Movie ${movie.id}` },
|
||||
];
|
||||
filter.criteria.push(criterion);
|
||||
return `/scenes?${filter.makeQueryParameters()}`;
|
||||
@@ -72,5 +72,5 @@ export default {
|
||||
makeTagSceneMarkersUrl,
|
||||
makeTagScenesUrl,
|
||||
makeSceneMarkerUrl,
|
||||
makeMovieScenesUrl
|
||||
makeMovieScenesUrl,
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ const renderHtmlSelect = (options: {
|
||||
options.onChange(event.currentTarget.value)
|
||||
}
|
||||
>
|
||||
{options.selectOptions.map(opt => (
|
||||
{options.selectOptions.map((opt) => (
|
||||
<option value={opt} key={opt}>
|
||||
{opt}
|
||||
</option>
|
||||
@@ -119,7 +119,7 @@ const renderFilterSelect = (options: {
|
||||
<td>
|
||||
<FilterSelect
|
||||
type={options.type}
|
||||
onSelect={items => options.onChange(items[0]?.id)}
|
||||
onSelect={(items) => options.onChange(items[0]?.id)}
|
||||
initialIds={options.initialId ? [options.initialId] : []}
|
||||
/>
|
||||
</td>
|
||||
@@ -139,7 +139,7 @@ const renderMultiSelect = (options: {
|
||||
<FilterSelect
|
||||
type={options.type}
|
||||
isMulti
|
||||
onSelect={items => options.onChange(items.map(i => i.id))}
|
||||
onSelect={(items) => options.onChange(items.map((i) => i.id))}
|
||||
initialIds={options.initialIds ?? []}
|
||||
/>
|
||||
</td>
|
||||
@@ -152,6 +152,6 @@ const Table = {
|
||||
renderInputGroup,
|
||||
renderHtmlSelect,
|
||||
renderFilterSelect,
|
||||
renderMultiSelect
|
||||
renderMultiSelect,
|
||||
};
|
||||
export default Table;
|
||||
|
||||
@@ -90,7 +90,7 @@ const TextUtils = {
|
||||
fileNameFromPath,
|
||||
age: getAge,
|
||||
bitRate,
|
||||
resolution
|
||||
resolution,
|
||||
};
|
||||
|
||||
export default TextUtils;
|
||||
|
||||
@@ -5438,10 +5438,10 @@ eslint-config-airbnb@^18.0.1:
|
||||
object.assign "^4.1.0"
|
||||
object.entries "^1.1.0"
|
||||
|
||||
eslint-config-prettier@^6.9.0:
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.9.0.tgz#430d24822e82f7deb1e22a435bfa3999fae4ad64"
|
||||
integrity sha512-k4E14HBtcLv0uqThaI6I/n1LEqROp8XaPu6SO9Z32u5NlGRC07Enu1Bh2KEFw4FNHbekH8yzbIU9kUGxbiGmCA==
|
||||
eslint-config-prettier@^6.10.1:
|
||||
version "6.10.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz#129ef9ec575d5ddc0e269667bf09defcd898642a"
|
||||
integrity sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
@@ -11027,6 +11027,11 @@ prettier@1.19.1:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
||||
prettier@2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.2.tgz#1ba8f3eb92231e769b7fcd7cb73ae1b6b74ade08"
|
||||
integrity sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==
|
||||
|
||||
pretty-bytes@^5.1.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
|
||||
@@ -13145,6 +13150,11 @@ stylehacks@^4.0.0:
|
||||
postcss "^7.0.0"
|
||||
postcss-selector-parser "^3.0.0"
|
||||
|
||||
stylelint-config-prettier@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-config-prettier/-/stylelint-config-prettier-8.0.1.tgz#ec7cdd7faabaff52ebfa56c28fed3d995ebb8cab"
|
||||
integrity sha512-RcjNW7MUaNVqONhJH4+rtlAE3ow/9SsAM0YWV0Lgu3dbTKdWTa/pQXRdFWgoHWpzUKn+9oBKR5x8JdH+20wmgw==
|
||||
|
||||
stylelint-order@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-order/-/stylelint-order-4.0.0.tgz#2a945c2198caac3ff44687d7c8582c81d044b556"
|
||||
|
||||
Reference in New Issue
Block a user