mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
UI filter refactor (#1406)
* Refactor Criterion * Separate filter options from filter * Rename utils to factory * Sort sort by options by alphabetical * Refactor criterion options * Simplify list filter options * Refactor i8n * Simplify ILabeledIdCriterion
This commit is contained in:
@@ -44,13 +44,27 @@ const intlFormats = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function languageMessageString(language: string) {
|
||||||
|
return language.replace(/-/, "");
|
||||||
|
}
|
||||||
|
|
||||||
export const App: React.FC = () => {
|
export const App: React.FC = () => {
|
||||||
const config = useConfiguration();
|
const config = useConfiguration();
|
||||||
const { data: systemStatusData } = useSystemStatus();
|
const { data: systemStatusData } = useSystemStatus();
|
||||||
const language = config.data?.configuration?.interface?.language ?? "en-GB";
|
const defaultLocale = "en-GB";
|
||||||
const messageLanguage = language.replace(/-/, "");
|
const language =
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
config.data?.configuration?.interface?.language ?? defaultLocale;
|
||||||
const messages = flattenMessages((locales as any)[messageLanguage]);
|
const defaultMessageLanguage = languageMessageString(defaultLocale);
|
||||||
|
const messageLanguage = languageMessageString(language);
|
||||||
|
|
||||||
|
// use en-GB as default messages if any messages aren't found in the chosen language
|
||||||
|
const mergedMessages = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
...(locales as any)[defaultMessageLanguage],
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
...(locales as any)[messageLanguage],
|
||||||
|
};
|
||||||
|
const messages = flattenMessages(mergedMessages);
|
||||||
|
|
||||||
const setupMatch = useRouteMatch(["/setup", "/migrate"]);
|
const setupMatch = useRouteMatch(["/setup", "/migrate"]);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
* Added [DLNA server](/settings?tab=dlna). ([#1364](https://github.com/stashapp/stash/pull/1364))
|
* Added [DLNA server](/settings?tab=dlna). ([#1364](https://github.com/stashapp/stash/pull/1364))
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Filter modifiers and sort by options are now sorted alphabetically. ([#1406](https://github.com/stashapp/stash/pull/1406))
|
||||||
* Add `CreatedAt` and `UpdatedAt` (and `FileModTime` where applicable) to API objects. ([#1421](https://github.com/stashapp/stash/pull/1421))
|
* Add `CreatedAt` and `UpdatedAt` (and `FileModTime` where applicable) to API objects. ([#1421](https://github.com/stashapp/stash/pull/1421))
|
||||||
* Add Studios Performer filter criterion. ([#1405](https://github.com/stashapp/stash/pull/1405))
|
* Add Studios Performer filter criterion. ([#1405](https://github.com/stashapp/stash/pull/1405))
|
||||||
* Add `subtractDays` post-process scraper action. ([#1399](https://github.com/stashapp/stash/pull/1399))
|
* Add `subtractDays` post-process scraper action. ([#1399](https://github.com/stashapp/stash/pull/1399))
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({ gallery }) => {
|
|||||||
};
|
};
|
||||||
// if galleries is already present, then we modify it, otherwise add
|
// if galleries is already present, then we modify it, otherwise add
|
||||||
let galleryCriterion = filter.criteria.find((c) => {
|
let galleryCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "galleries";
|
return c.criterionOption.value === "galleries";
|
||||||
}) as GalleriesCriterion;
|
}) as GalleriesCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
// if galleries is already present, then we modify it, otherwise add
|
// if galleries is already present, then we modify it, otherwise add
|
||||||
let galleryCriterion = filter.criteria.find((c) => {
|
let galleryCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "galleries";
|
return c.criterionOption.value === "galleries";
|
||||||
}) as GalleriesCriterion;
|
}) as GalleriesCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -5,20 +5,24 @@ import Mousetrap from "mousetrap";
|
|||||||
import { Icon, FilterSelect, DurationInput } from "src/components/Shared";
|
import { Icon, FilterSelect, DurationInput } from "src/components/Shared";
|
||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier } from "src/core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
|
||||||
CriterionType,
|
|
||||||
DurationCriterion,
|
DurationCriterion,
|
||||||
CriterionValue,
|
CriterionValue,
|
||||||
|
Criterion,
|
||||||
} from "src/models/list-filter/criteria/criterion";
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import { NoneCriterion } from "src/models/list-filter/criteria/none";
|
import { NoneCriterion } from "src/models/list-filter/criteria/none";
|
||||||
import { makeCriteria } from "src/models/list-filter/criteria/utils";
|
import { makeCriteria } from "src/models/list-filter/criteria/factory";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterOptions } from "src/models/list-filter/filter-options";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import { CriterionType } from "src/models/list-filter/types";
|
||||||
|
|
||||||
interface IAddFilterProps {
|
interface IAddFilterProps {
|
||||||
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
|
onAddCriterion: (
|
||||||
|
criterion: Criterion<CriterionValue>,
|
||||||
|
oldId?: string
|
||||||
|
) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
filter: ListFilterModel;
|
filterOptions: ListFilterOptions;
|
||||||
editingCriterion?: Criterion;
|
editingCriterion?: Criterion<CriterionValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddFilter: React.FC<IAddFilterProps> = (
|
export const AddFilter: React.FC<IAddFilterProps> = (
|
||||||
@@ -27,10 +31,14 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
const defaultValue = useRef<string | number | undefined>();
|
const defaultValue = useRef<string | number | undefined>();
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [criterion, setCriterion] = useState<Criterion>(new NoneCriterion());
|
const [criterion, setCriterion] = useState<Criterion<CriterionValue>>(
|
||||||
|
new NoneCriterion()
|
||||||
|
);
|
||||||
|
|
||||||
const valueStage = useRef<CriterionValue>(criterion.value);
|
const valueStage = useRef<CriterionValue>(criterion.value);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
// configure keyboard shortcuts
|
// configure keyboard shortcuts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind("f", () => setIsOpen(true));
|
Mousetrap.bind("f", () => setIsOpen(true));
|
||||||
@@ -114,7 +122,7 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maybeRenderFilterPopoverContents = () => {
|
const maybeRenderFilterPopoverContents = () => {
|
||||||
if (criterion.type === "none") {
|
if (criterion.criterionOption.value === "none") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,19 +157,19 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
|
|
||||||
if (Array.isArray(criterion.value)) {
|
if (Array.isArray(criterion.value)) {
|
||||||
if (
|
if (
|
||||||
criterion.type !== "performers" &&
|
criterion.criterionOption.value !== "performers" &&
|
||||||
criterion.type !== "studios" &&
|
criterion.criterionOption.value !== "studios" &&
|
||||||
criterion.type !== "parent_studios" &&
|
criterion.criterionOption.value !== "parent_studios" &&
|
||||||
criterion.type !== "tags" &&
|
criterion.criterionOption.value !== "tags" &&
|
||||||
criterion.type !== "sceneTags" &&
|
criterion.criterionOption.value !== "sceneTags" &&
|
||||||
criterion.type !== "performerTags" &&
|
criterion.criterionOption.value !== "performerTags" &&
|
||||||
criterion.type !== "movies"
|
criterion.criterionOption.value !== "movies"
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterSelect
|
<FilterSelect
|
||||||
type={criterion.type}
|
type={criterion.criterionOption.value}
|
||||||
isMulti
|
isMulti
|
||||||
onSelect={(items) => {
|
onSelect={(items) => {
|
||||||
const newCriterion = _.cloneDeep(criterion);
|
const newCriterion = _.cloneDeep(criterion);
|
||||||
@@ -223,18 +231,28 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
if (props.editingCriterion) {
|
if (props.editingCriterion) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = props.filterOptions.criterionOptions
|
||||||
|
.map((c) => {
|
||||||
|
return {
|
||||||
|
value: c.value,
|
||||||
|
text: intl.formatMessage({ id: c.messageID }),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.text.localeCompare(b.text));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Group controlId="filter">
|
<Form.Group controlId="filter">
|
||||||
<Form.Label>Filter</Form.Label>
|
<Form.Label>Filter</Form.Label>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
onChange={onChangedCriteriaType}
|
onChange={onChangedCriteriaType}
|
||||||
value={criterion.type}
|
value={criterion.criterionOption.value}
|
||||||
className="btn-secondary"
|
className="btn-secondary"
|
||||||
>
|
>
|
||||||
{props.filter.criterionOptions.map((c) => (
|
{options.map((c) => (
|
||||||
<option key={c.value} value={c.value}>
|
<option key={c.value} value={c.value}>
|
||||||
{c.label}
|
{c.text}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
@@ -263,7 +281,10 @@ export const AddFilter: React.FC<IAddFilterProps> = (
|
|||||||
</div>
|
</div>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button onClick={onAddFilter} disabled={criterion.type === "none"}>
|
<Button
|
||||||
|
onClick={onAddFilter}
|
||||||
|
disabled={criterion.criterionOption.value === "none"}
|
||||||
|
>
|
||||||
{title}
|
{title}
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Footer>
|
</Modal.Footer>
|
||||||
|
|||||||
@@ -16,10 +16,15 @@ import {
|
|||||||
} from "react-bootstrap";
|
} from "react-bootstrap";
|
||||||
|
|
||||||
import { Icon } from "src/components/Shared";
|
import { Icon } from "src/components/Shared";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { DisplayMode } from "src/models/list-filter/types";
|
import { DisplayMode } from "src/models/list-filter/types";
|
||||||
import { useFocus } from "src/utils";
|
import { useFocus } from "src/utils";
|
||||||
|
import { ListFilterOptions } from "src/models/list-filter/filter-options";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import {
|
||||||
|
Criterion,
|
||||||
|
CriterionValue,
|
||||||
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import { AddFilter } from "./AddFilter";
|
import { AddFilter } from "./AddFilter";
|
||||||
|
|
||||||
interface IListFilterOperation {
|
interface IListFilterOperation {
|
||||||
@@ -38,6 +43,7 @@ interface IListFilterProps {
|
|||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
otherOperations?: IListFilterOperation[];
|
otherOperations?: IListFilterOperation[];
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
|
filterOptions: ListFilterOptions;
|
||||||
itemsSelected?: boolean;
|
itemsSelected?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,9 +64,11 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
const [editingCriterion, setEditingCriterion] = useState<
|
const [editingCriterion, setEditingCriterion] = useState<
|
||||||
Criterion | undefined
|
Criterion<CriterionValue> | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind("/", (e) => {
|
Mousetrap.bind("/", (e) => {
|
||||||
setQueryFocus();
|
setQueryFocus();
|
||||||
@@ -69,17 +77,17 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
|
|
||||||
Mousetrap.bind("r", () => onReshuffleRandomSort());
|
Mousetrap.bind("r", () => onReshuffleRandomSort());
|
||||||
Mousetrap.bind("v g", () => {
|
Mousetrap.bind("v g", () => {
|
||||||
if (props.filter.displayModeOptions.includes(DisplayMode.Grid)) {
|
if (props.filterOptions.displayModeOptions.includes(DisplayMode.Grid)) {
|
||||||
onChangeDisplayMode(DisplayMode.Grid);
|
onChangeDisplayMode(DisplayMode.Grid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Mousetrap.bind("v l", () => {
|
Mousetrap.bind("v l", () => {
|
||||||
if (props.filter.displayModeOptions.includes(DisplayMode.List)) {
|
if (props.filterOptions.displayModeOptions.includes(DisplayMode.List)) {
|
||||||
onChangeDisplayMode(DisplayMode.List);
|
onChangeDisplayMode(DisplayMode.List);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Mousetrap.bind("v w", () => {
|
Mousetrap.bind("v w", () => {
|
||||||
if (props.filter.displayModeOptions.includes(DisplayMode.Wall)) {
|
if (props.filterOptions.displayModeOptions.includes(DisplayMode.Wall)) {
|
||||||
onChangeDisplayMode(DisplayMode.Wall);
|
onChangeDisplayMode(DisplayMode.Wall);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -160,9 +168,9 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
props.onFilterUpdate(newFilter);
|
props.onFilterUpdate(newFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeSortBy(event: React.MouseEvent<HTMLAnchorElement>) {
|
function onChangeSortBy(eventKey: string | null) {
|
||||||
const newFilter = _.cloneDeep(props.filter);
|
const newFilter = _.cloneDeep(props.filter);
|
||||||
newFilter.sortBy = event.currentTarget.text;
|
newFilter.sortBy = eventKey ?? undefined;
|
||||||
newFilter.currentPage = 1;
|
newFilter.currentPage = 1;
|
||||||
props.onFilterUpdate(newFilter);
|
props.onFilterUpdate(newFilter);
|
||||||
}
|
}
|
||||||
@@ -180,7 +188,10 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
props.onFilterUpdate(newFilter);
|
props.onFilterUpdate(newFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAddCriterion(criterion: Criterion, oldId?: string) {
|
function onAddCriterion(
|
||||||
|
criterion: Criterion<CriterionValue>,
|
||||||
|
oldId?: string
|
||||||
|
) {
|
||||||
const newFilter = _.cloneDeep(props.filter);
|
const newFilter = _.cloneDeep(props.filter);
|
||||||
|
|
||||||
// Find if we are editing an existing criteria, then modify that. Or create a new one.
|
// Find if we are editing an existing criteria, then modify that. Or create a new one.
|
||||||
@@ -208,7 +219,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
setEditingCriterion(undefined);
|
setEditingCriterion(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRemoveCriterion(removedCriterion: Criterion) {
|
function onRemoveCriterion(removedCriterion: Criterion<CriterionValue>) {
|
||||||
const newFilter = _.cloneDeep(props.filter);
|
const newFilter = _.cloneDeep(props.filter);
|
||||||
newFilter.criteria = newFilter.criteria.filter(
|
newFilter.criteria = newFilter.criteria.filter(
|
||||||
(criterion) => criterion.getId() !== removedCriterion.getId()
|
(criterion) => criterion.getId() !== removedCriterion.getId()
|
||||||
@@ -218,7 +229,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let removedCriterionId = "";
|
let removedCriterionId = "";
|
||||||
function onRemoveCriterionTag(criterion?: Criterion) {
|
function onRemoveCriterionTag(criterion?: Criterion<CriterionValue>) {
|
||||||
if (!criterion) {
|
if (!criterion) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -227,7 +238,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
onRemoveCriterion(criterion);
|
onRemoveCriterion(criterion);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickCriterionTag(criterion?: Criterion) {
|
function onClickCriterionTag(criterion?: Criterion<CriterionValue>) {
|
||||||
if (!criterion || removedCriterionId !== "") {
|
if (!criterion || removedCriterionId !== "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -235,15 +246,24 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderSortByOptions() {
|
function renderSortByOptions() {
|
||||||
return props.filter.sortByOptions.map((option) => (
|
return props.filterOptions.sortByOptions
|
||||||
<Dropdown.Item
|
.map((o) => {
|
||||||
onClick={onChangeSortBy}
|
return {
|
||||||
key={option}
|
message: intl.formatMessage({ id: o.messageID }),
|
||||||
className="bg-secondary text-white"
|
value: o.value,
|
||||||
>
|
};
|
||||||
{option}
|
})
|
||||||
</Dropdown.Item>
|
.sort((a, b) => a.message.localeCompare(b.message))
|
||||||
));
|
.map((option) => (
|
||||||
|
<Dropdown.Item
|
||||||
|
onSelect={onChangeSortBy}
|
||||||
|
key={option.value}
|
||||||
|
className="bg-secondary text-white"
|
||||||
|
eventKey={option.value}
|
||||||
|
>
|
||||||
|
{option.message}
|
||||||
|
</Dropdown.Item>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDisplayModeOptions() {
|
function renderDisplayModeOptions() {
|
||||||
@@ -272,7 +292,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return props.filter.displayModeOptions.map((option) => (
|
return props.filterOptions.displayModeOptions.map((option) => (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
key={option}
|
key={option}
|
||||||
overlay={
|
overlay={
|
||||||
@@ -298,7 +318,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
key={criterion.getId()}
|
key={criterion.getId()}
|
||||||
onClick={() => onClickCriterionTag(criterion)}
|
onClick={() => onClickCriterionTag(criterion)}
|
||||||
>
|
>
|
||||||
{criterion.getLabel()}
|
{criterion.getLabel(intl)}
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => onRemoveCriterionTag(criterion)}
|
onClick={() => onRemoveCriterionTag(criterion)}
|
||||||
@@ -450,6 +470,10 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
|
const currentSortBy = props.filterOptions.sortByOptions.find(
|
||||||
|
(o) => o.value === props.filter.sortBy
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonToolbar className="align-items-center justify-content-center mb-2">
|
<ButtonToolbar className="align-items-center justify-content-center mb-2">
|
||||||
@@ -465,7 +489,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
|
|
||||||
<InputGroup.Append>
|
<InputGroup.Append>
|
||||||
<AddFilter
|
<AddFilter
|
||||||
filter={props.filter}
|
filterOptions={props.filterOptions}
|
||||||
onAddCriterion={onAddCriterion}
|
onAddCriterion={onAddCriterion}
|
||||||
onCancel={onCancelAddCriterion}
|
onCancel={onCancelAddCriterion}
|
||||||
editingCriterion={editingCriterion}
|
editingCriterion={editingCriterion}
|
||||||
@@ -475,7 +499,7 @@ export const ListFilter: React.FC<IListFilterProps> = (
|
|||||||
|
|
||||||
<Dropdown as={ButtonGroup} className="mr-2">
|
<Dropdown as={ButtonGroup} className="mr-2">
|
||||||
<Dropdown.Toggle split variant="secondary" id="more-menu">
|
<Dropdown.Toggle split variant="secondary" id="more-menu">
|
||||||
{props.filter.sortBy}
|
{intl.formatMessage({ id: currentSortBy?.messageID })}
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu className="bg-secondary text-white">
|
<Dropdown.Menu className="bg-secondary text-white">
|
||||||
{renderSortByOptions()}
|
{renderSortByOptions()}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({ movie }) => {
|
|||||||
const movieValue = { id: movie.id!, label: movie.name! };
|
const movieValue = { id: movie.id!, label: movie.name! };
|
||||||
// if movie is already present, then we modify it, otherwise add
|
// 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";
|
return c.criterionOption.value === "movies";
|
||||||
}) as MoviesCriterion;
|
}) as MoviesCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ import {
|
|||||||
TruncatedText,
|
TruncatedText,
|
||||||
} from "src/components/Shared";
|
} from "src/components/Shared";
|
||||||
import { Button, ButtonGroup } from "react-bootstrap";
|
import { Button, ButtonGroup } from "react-bootstrap";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
import {
|
||||||
|
Criterion,
|
||||||
|
CriterionValue,
|
||||||
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||||
|
|
||||||
export interface IPerformerCardExtraCriteria {
|
export interface IPerformerCardExtraCriteria {
|
||||||
scenes: Criterion[];
|
scenes: Criterion<CriterionValue>[];
|
||||||
images: Criterion[];
|
images: Criterion<CriterionValue>[];
|
||||||
galleries: Criterion[];
|
galleries: Criterion<CriterionValue>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPerformerCardProps {
|
interface IPerformerCardProps {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { ScenePlayer } from "src/components/ScenePlayer";
|
|||||||
import { TextUtils, JWUtils } from "src/utils";
|
import { TextUtils, JWUtils } from "src/utils";
|
||||||
import Mousetrap from "mousetrap";
|
import Mousetrap from "mousetrap";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { FilterMode } from "src/models/list-filter/types";
|
|
||||||
import { SceneQueue } from "src/models/sceneQueue";
|
import { SceneQueue } from "src/models/sceneQueue";
|
||||||
import { QueueViewer } from "./QueueViewer";
|
import { QueueViewer } from "./QueueViewer";
|
||||||
import { SceneMarkersPanel } from "./SceneMarkersPanel";
|
import { SceneMarkersPanel } from "./SceneMarkersPanel";
|
||||||
@@ -220,10 +219,7 @@ export const Scene: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterCopy = Object.assign(
|
const filterCopy = Object.assign(new ListFilterModel(), sceneQueue.query);
|
||||||
new ListFilterModel(FilterMode.Scenes),
|
|
||||||
sceneQueue.query
|
|
||||||
);
|
|
||||||
const newStart = queueStart - filterCopy.itemsPerPage;
|
const newStart = queueStart - filterCopy.itemsPerPage;
|
||||||
filterCopy.currentPage = Math.ceil(newStart / filterCopy.itemsPerPage);
|
filterCopy.currentPage = Math.ceil(newStart / filterCopy.itemsPerPage);
|
||||||
const query = await queryFindScenes(filterCopy);
|
const query = await queryFindScenes(filterCopy);
|
||||||
@@ -244,10 +240,7 @@ export const Scene: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterCopy = Object.assign(
|
const filterCopy = Object.assign(new ListFilterModel(), sceneQueue.query);
|
||||||
new ListFilterModel(FilterMode.Scenes),
|
|
||||||
sceneQueue.query
|
|
||||||
);
|
|
||||||
const newStart = queueStart + queueScenes.length;
|
const newStart = queueStart + queueScenes.length;
|
||||||
filterCopy.currentPage = Math.ceil(newStart / filterCopy.itemsPerPage);
|
filterCopy.currentPage = Math.ceil(newStart / filterCopy.itemsPerPage);
|
||||||
const query = await queryFindScenes(filterCopy);
|
const query = await queryFindScenes(filterCopy);
|
||||||
@@ -284,10 +277,7 @@ export const Scene: React.FC = () => {
|
|||||||
const pages = Math.ceil(queueTotal / query.itemsPerPage);
|
const pages = Math.ceil(queueTotal / query.itemsPerPage);
|
||||||
const page = Math.floor(Math.random() * pages) + 1;
|
const page = Math.floor(Math.random() * pages) + 1;
|
||||||
const index = Math.floor(Math.random() * query.itemsPerPage);
|
const index = Math.floor(Math.random() * query.itemsPerPage);
|
||||||
const filterCopy = Object.assign(
|
const filterCopy = Object.assign(new ListFilterModel(), sceneQueue.query);
|
||||||
new ListFilterModel(FilterMode.Scenes),
|
|
||||||
sceneQueue.query
|
|
||||||
);
|
|
||||||
filterCopy.currentPage = page;
|
filterCopy.currentPage = page;
|
||||||
const queryResults = await queryFindScenes(filterCopy);
|
const queryResults = await queryFindScenes(filterCopy);
|
||||||
if (queryResults.data.findScenes.scenes.length > index) {
|
if (queryResults.data.findScenes.scenes.length > index) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const StudioChildrenPanel: React.FC<IStudioChildrenPanel> = ({
|
|||||||
const studioValue = { id: studio.id!, label: studio.name! };
|
const studioValue = { id: studio.id!, label: studio.name! };
|
||||||
// if studio is already present, then we modify it, otherwise add
|
// if studio is already present, then we modify it, otherwise add
|
||||||
let parentStudioCriterion = filter.criteria.find((c) => {
|
let parentStudioCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "parent_studios";
|
return c.criterionOption.value === "parent_studios";
|
||||||
}) as ParentStudiosCriterion;
|
}) as ParentStudiosCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
import {
|
||||||
|
TagsCriterion,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "src/models/list-filter/criteria/tags";
|
||||||
import { SceneMarkerList } from "src/components/Scenes/SceneMarkerList";
|
import { SceneMarkerList } from "src/components/Scenes/SceneMarkerList";
|
||||||
|
|
||||||
interface ITagMarkersPanel {
|
interface ITagMarkersPanel {
|
||||||
@@ -13,7 +16,7 @@ export const TagMarkersPanel: React.FC<ITagMarkersPanel> = ({ tag }) => {
|
|||||||
const tagValue = { id: tag.id!, label: tag.name! };
|
const tagValue = { id: tag.id!, label: tag.name! };
|
||||||
// if tag is already present, then we modify it, otherwise add
|
// if tag is already present, then we modify it, otherwise add
|
||||||
let tagCriterion = filter.criteria.find((c) => {
|
let tagCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "tags";
|
return c.criterionOption.value === "tags";
|
||||||
}) as TagsCriterion;
|
}) as TagsCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -33,7 +36,7 @@ export const TagMarkersPanel: React.FC<ITagMarkersPanel> = ({ tag }) => {
|
|||||||
tagCriterion.modifier = GQL.CriterionModifier.IncludesAll;
|
tagCriterion.modifier = GQL.CriterionModifier.IncludesAll;
|
||||||
} else {
|
} else {
|
||||||
// overwrite
|
// overwrite
|
||||||
tagCriterion = new TagsCriterion("tags");
|
tagCriterion = new TagsCriterion(TagsCriterionOption);
|
||||||
tagCriterion.value = [tagValue];
|
tagCriterion.value = [tagValue];
|
||||||
filter.criteria.push(tagCriterion);
|
filter.criteria.push(tagCriterion);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const useFindGalleries = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindGalleriesQuery({
|
GQL.useFindGalleriesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
gallery_filter: filter.makeGalleryFilter(),
|
gallery_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export const queryFindGalleries = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindGalleriesDocument,
|
query: GQL.FindGalleriesDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
gallery_filter: filter.makeImageFilter(),
|
gallery_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ export const useFindScenes = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindScenesQuery({
|
GQL.useFindScenesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
scene_filter: filter.makeSceneFilter(),
|
scene_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ export const queryFindScenes = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindScenesDocument,
|
query: GQL.FindScenesDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
scene_filter: filter.makeSceneFilter(),
|
scene_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ export const useFindSceneMarkers = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindSceneMarkersQuery({
|
GQL.useFindSceneMarkersQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
scene_marker_filter: filter.makeSceneMarkerFilter(),
|
scene_marker_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ export const queryFindSceneMarkers = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindSceneMarkersDocument,
|
query: GQL.FindSceneMarkersDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
scene_marker_filter: filter.makeSceneMarkerFilter(),
|
scene_marker_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ export const useFindImages = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindImagesQuery({
|
GQL.useFindImagesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
image_filter: filter.makeImageFilter(),
|
image_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export const queryFindImages = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindImagesDocument,
|
query: GQL.FindImagesDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
image_filter: filter.makeImageFilter(),
|
image_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ export const useFindStudios = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindStudiosQuery({
|
GQL.useFindStudiosQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
studio_filter: filter.makeStudioFilter(),
|
studio_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ export const queryFindStudios = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindStudiosDocument,
|
query: GQL.FindStudiosDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
studio_filter: filter.makeStudioFilter(),
|
studio_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ export const useFindMovies = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindMoviesQuery({
|
GQL.useFindMoviesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
movie_filter: filter.makeMovieFilter(),
|
movie_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export const queryFindMovies = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindMoviesDocument,
|
query: GQL.FindMoviesDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
movie_filter: filter.makeMovieFilter(),
|
movie_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ export const useFindPerformers = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindPerformersQuery({
|
GQL.useFindPerformersQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
performer_filter: filter.makePerformerFilter(),
|
performer_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ export const useFindTags = (filter: ListFilterModel) =>
|
|||||||
GQL.useFindTagsQuery({
|
GQL.useFindTagsQuery({
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
tag_filter: filter.makeTagFilter(),
|
tag_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ export const queryFindTags = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindTagsDocument,
|
query: GQL.FindTagsDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
tag_filter: filter.makeTagFilter(),
|
tag_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ export const queryFindPerformers = (filter: ListFilterModel) =>
|
|||||||
query: GQL.FindPerformersDocument,
|
query: GQL.FindPerformersDocument,
|
||||||
variables: {
|
variables: {
|
||||||
filter: filter.makeFindFilter(),
|
filter: filter.makeFindFilter(),
|
||||||
performer_filter: filter.makePerformerFilter(),
|
performer_filter: filter.makeFilter(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const performerFilterHook = (
|
|||||||
const performerValue = { id: performer.id!, label: performer.name! };
|
const performerValue = { id: performer.id!, label: performer.name! };
|
||||||
// if performers is already present, then we modify it, otherwise add
|
// if performers is already present, then we modify it, otherwise add
|
||||||
let performerCriterion = filter.criteria.find((c) => {
|
let performerCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "performers";
|
return c.criterionOption.value === "performers";
|
||||||
}) as PerformersCriterion;
|
}) as PerformersCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const studioFilterHook = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
const studioValue = { id: studio.id!, label: studio.name! };
|
const studioValue = { id: studio.id!, label: studio.name! };
|
||||||
// if studio is already present, then we modify it, otherwise add
|
// 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";
|
return c.criterionOption.value === "studios";
|
||||||
}) as StudiosCriterion;
|
}) as StudiosCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
import {
|
||||||
|
TagsCriterion,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "src/models/list-filter/criteria/tags";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
|
|
||||||
export const tagFilterHook = (tag: GQL.TagDataFragment) => {
|
export const tagFilterHook = (tag: GQL.TagDataFragment) => {
|
||||||
@@ -7,7 +10,7 @@ export const tagFilterHook = (tag: GQL.TagDataFragment) => {
|
|||||||
const tagValue = { id: tag.id, label: tag.name };
|
const tagValue = { id: tag.id, label: tag.name };
|
||||||
// if tag is already present, then we modify it, otherwise add
|
// if tag is already present, then we modify it, otherwise add
|
||||||
let tagCriterion = filter.criteria.find((c) => {
|
let tagCriterion = filter.criteria.find((c) => {
|
||||||
return c.type === "tags";
|
return c.criterionOption.value === "tags";
|
||||||
}) as TagsCriterion;
|
}) as TagsCriterion;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -27,7 +30,7 @@ export const tagFilterHook = (tag: GQL.TagDataFragment) => {
|
|||||||
tagCriterion.modifier = GQL.CriterionModifier.IncludesAll;
|
tagCriterion.modifier = GQL.CriterionModifier.IncludesAll;
|
||||||
} else {
|
} else {
|
||||||
// overwrite
|
// overwrite
|
||||||
tagCriterion = new TagsCriterion("tags");
|
tagCriterion = new TagsCriterion(TagsCriterionOption);
|
||||||
tagCriterion.value = [tagValue];
|
tagCriterion.value = [tagValue];
|
||||||
filter.criteria.push(tagCriterion);
|
filter.criteria.push(tagCriterion);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import {
|
|||||||
} from "src/core/StashService";
|
} from "src/core/StashService";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { FilterMode } from "src/models/list-filter/types";
|
import { FilterMode } from "src/models/list-filter/types";
|
||||||
|
import { ListFilterOptions } from "src/models/list-filter/filter-options";
|
||||||
|
import { getFilterOptions } from "src/models/list-filter/factory";
|
||||||
|
|
||||||
const getSelectedData = <I extends IDataItem>(
|
const getSelectedData = <I extends IDataItem>(
|
||||||
result: I[],
|
result: I[],
|
||||||
@@ -141,6 +143,7 @@ interface IQuery<T extends IQueryResult, T2 extends IDataItem> {
|
|||||||
|
|
||||||
interface IRenderListProps {
|
interface IRenderListProps {
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
|
filterOptions: ListFilterOptions;
|
||||||
onChangePage: (page: number) => void;
|
onChangePage: (page: number) => void;
|
||||||
updateQueryParams: (filter: ListFilterModel) => void;
|
updateQueryParams: (filter: ListFilterModel) => void;
|
||||||
}
|
}
|
||||||
@@ -151,6 +154,7 @@ const RenderList = <
|
|||||||
>({
|
>({
|
||||||
defaultZoomIndex,
|
defaultZoomIndex,
|
||||||
filter,
|
filter,
|
||||||
|
filterOptions,
|
||||||
onChangePage,
|
onChangePage,
|
||||||
addKeybinds,
|
addKeybinds,
|
||||||
useData,
|
useData,
|
||||||
@@ -406,6 +410,7 @@ const RenderList = <
|
|||||||
onEdit={renderEditDialog ? onEdit : undefined}
|
onEdit={renderEditDialog ? onEdit : undefined}
|
||||||
onDelete={renderDeleteDialog ? onDelete : undefined}
|
onDelete={renderDeleteDialog ? onDelete : undefined}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
|
filterOptions={filterOptions}
|
||||||
/>
|
/>
|
||||||
{isEditDialogOpen &&
|
{isEditDialogOpen &&
|
||||||
renderEditDialog &&
|
renderEditDialog &&
|
||||||
@@ -432,6 +437,8 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
options: IListHookOptions<QueryResult, QueryData> &
|
options: IListHookOptions<QueryResult, QueryData> &
|
||||||
IQuery<QueryResult, QueryData>
|
IQuery<QueryResult, QueryData>
|
||||||
): IListHookData => {
|
): IListHookData => {
|
||||||
|
const filterOptions = getFilterOptions(options.filterMode);
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [interfaceState, setInterfaceState] = useInterfaceLocalForage();
|
const [interfaceState, setInterfaceState] = useInterfaceLocalForage();
|
||||||
@@ -445,9 +452,8 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
|
|
||||||
const [filter, setFilter] = useState<ListFilterModel>(
|
const [filter, setFilter] = useState<ListFilterModel>(
|
||||||
new ListFilterModel(
|
new ListFilterModel(
|
||||||
options.filterMode,
|
|
||||||
queryString.parse(location.search),
|
queryString.parse(location.search),
|
||||||
options.defaultSort
|
options.defaultSort ?? filterOptions.defaultSortBy
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -509,7 +515,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
}
|
}
|
||||||
: activeFilter;
|
: activeFilter;
|
||||||
|
|
||||||
const newFilter = new ListFilterModel(options.filterMode, query);
|
const newFilter = new ListFilterModel(query);
|
||||||
|
|
||||||
// Compare constructed filter with current filter.
|
// Compare constructed filter with current filter.
|
||||||
// If different it's the result of navigation, and we update the filter.
|
// If different it's the result of navigation, and we update the filter.
|
||||||
@@ -569,6 +575,7 @@ const useList = <QueryResult extends IQueryResult, QueryData extends IDataItem>(
|
|||||||
const { contentTemplate, onSelectChange } = RenderList({
|
const { contentTemplate, onSelectChange } = RenderList({
|
||||||
...options,
|
...options,
|
||||||
filter: renderFilter,
|
filter: renderFilter,
|
||||||
|
filterOptions,
|
||||||
onChangePage,
|
onChangePage,
|
||||||
updateQueryParams,
|
updateQueryParams,
|
||||||
});
|
});
|
||||||
|
|||||||
3
ui/v2.5/src/locale/README.md
Normal file
3
ui/v2.5/src/locale/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Use `en-GB.json` by default. This should have _all_ message IDs in it. Only add to other json files if the value is different to `en-GB` since it will fall back to use it if the message ID is not found in the chosen language.
|
||||||
|
|
||||||
|
Try to keep message IDs in alphabetical order for ease of reference.
|
||||||
@@ -1,20 +1,85 @@
|
|||||||
{
|
{
|
||||||
|
"age": "Age",
|
||||||
|
"aliases": "Aliases",
|
||||||
|
"average_resolution": "Average Resolution",
|
||||||
|
"birth_year": "Birth Year",
|
||||||
|
"birthdate": "Birthdate",
|
||||||
|
"bitrate": "Bit Rate",
|
||||||
|
"career_length": "Career Length",
|
||||||
|
"country": "Country",
|
||||||
|
"created_at": "Created At",
|
||||||
|
"criterion_modifier": {
|
||||||
|
"equals": "is",
|
||||||
|
"not_equals": "is not",
|
||||||
|
"greater_than": "is greater than",
|
||||||
|
"less_than": "is less than",
|
||||||
|
"is_null": "is null",
|
||||||
|
"not_null": "is not null",
|
||||||
|
"includes": "includes",
|
||||||
|
"includes_all": "includes all",
|
||||||
|
"excludes": "excludes",
|
||||||
|
"matches_regex": "matches regex",
|
||||||
|
"not_matches_regex": "not matches regex"
|
||||||
|
},
|
||||||
|
"date": "Date",
|
||||||
|
"death_year": "Death Year",
|
||||||
"developmentVersion": "Development Version",
|
"developmentVersion": "Development Version",
|
||||||
|
"donate": "Donate",
|
||||||
|
"duration": "Duration",
|
||||||
|
"ethnicity": "Ethnicity",
|
||||||
|
"eye_color": "Eye Colour",
|
||||||
|
"fake_tits": "Fake Tits",
|
||||||
|
"favourite": "Favourite",
|
||||||
|
"file_mod_time": "File Modification Time",
|
||||||
|
"filesize": "File Size",
|
||||||
|
"framerate": "Frame Rate",
|
||||||
|
"galleries": "Galleries",
|
||||||
|
"gallery_count": "Gallery Count",
|
||||||
|
"gender": "Gender",
|
||||||
|
"hair_color": "Hair Colour",
|
||||||
|
"hasMarkers": "Has Markers",
|
||||||
|
"height": "Height",
|
||||||
|
"image_count": "Image Count",
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
"images-size": "Images size",
|
"images-size": "Images size",
|
||||||
"galleries": "Galleries",
|
"isMissing": "Is Missing",
|
||||||
|
"interactive": "Interactive",
|
||||||
"library-size": "Library size",
|
"library-size": "Library size",
|
||||||
|
"marker_count": "Marker Count",
|
||||||
"markers": "Markers",
|
"markers": "Markers",
|
||||||
|
"measurements": "Measurements",
|
||||||
|
"movie_scene_number": "Movie Scene Number",
|
||||||
"movies": "Movies",
|
"movies": "Movies",
|
||||||
|
"name": "Name",
|
||||||
"new": "New",
|
"new": "New",
|
||||||
|
"none": "None",
|
||||||
|
"o_counter": "O-Counter",
|
||||||
"organized": "Organised",
|
"organized": "Organised",
|
||||||
|
"parent_studios": "Parent Studios",
|
||||||
|
"path": "Path",
|
||||||
|
"performerTags": "Performer Tags",
|
||||||
|
"performer_count": "Performer Count",
|
||||||
"performers": "Performers",
|
"performers": "Performers",
|
||||||
|
"piercings": "Piercings",
|
||||||
|
"random": "Random",
|
||||||
|
"rating": "Rating",
|
||||||
|
"resolution": "Resolution",
|
||||||
|
"sceneTagger": "Scene Tagger",
|
||||||
|
"sceneTags": "Scene Tags",
|
||||||
|
"scene_count": "Scene Count",
|
||||||
|
"scene_id": "Scene ID",
|
||||||
"scenes": "Scenes",
|
"scenes": "Scenes",
|
||||||
"scenes-size": "Scenes size",
|
"scenes-size": "Scenes size",
|
||||||
|
"scenes_updated_at": "Scene Updated At",
|
||||||
|
"seconds": "Seconds",
|
||||||
|
"stash_id": "Stash ID",
|
||||||
"studios": "Studios",
|
"studios": "Studios",
|
||||||
|
"tag_count": "Tag Count",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
|
"tattoos": "Tattoos",
|
||||||
|
"title": "Title",
|
||||||
"up-dir": "Up a directory",
|
"up-dir": "Up a directory",
|
||||||
"favourite": "FAVOURITE",
|
"updated_at": "Updated At",
|
||||||
"sceneTagger": "Scene Tagger",
|
"url": "URL",
|
||||||
"donate": "Donate"
|
"weight": "Weight"
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,6 @@
|
|||||||
{
|
{
|
||||||
"developmentVersion": "Development Version",
|
"eye_color": "Eye Color",
|
||||||
"images": "Images",
|
"favourite": "Favorite",
|
||||||
"images-size": "Images size",
|
"hair_color": "Hair Color",
|
||||||
"galleries": "Galleries",
|
"organized": "Organized"
|
||||||
"library-size": "Library size",
|
}
|
||||||
"markers": "Markers",
|
|
||||||
"movies": "Movies",
|
|
||||||
"new": "New",
|
|
||||||
"organized": "Organized",
|
|
||||||
"performers": "Performers",
|
|
||||||
"scenes": "Scenes",
|
|
||||||
"scenes-size": "Scenes size",
|
|
||||||
"studios": "Studios",
|
|
||||||
"tags": "Tags",
|
|
||||||
"up-dir": "Up a directory",
|
|
||||||
"favourite": "FAVORITE",
|
|
||||||
"sceneTagger": "Scene Tagger",
|
|
||||||
"donate": "Donate"
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"developmentVersion": "開發版本",
|
"developmentVersion": "開發版本",
|
||||||
|
"donate": "贊助",
|
||||||
|
"favourite": "最愛",
|
||||||
"images": "圖片",
|
"images": "圖片",
|
||||||
"images-size": "圖片大小",
|
"images-size": "圖片大小",
|
||||||
"galleries": "圖庫",
|
"galleries": "圖庫",
|
||||||
@@ -11,10 +13,8 @@
|
|||||||
"performers": "演員",
|
"performers": "演員",
|
||||||
"scenes": "場景",
|
"scenes": "場景",
|
||||||
"scenes-size": "場景收藏大小",
|
"scenes-size": "場景收藏大小",
|
||||||
|
"sceneTagger": "標記場景",
|
||||||
"studios": "工作室",
|
"studios": "工作室",
|
||||||
"tags": "標籤",
|
"tags": "標籤",
|
||||||
"up-dir": "往上一層",
|
"up-dir": "往上一層"
|
||||||
"favourite": "最愛",
|
|
||||||
"sceneTagger": "標記場景",
|
|
||||||
"donate": "贊助"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionOption, StringCriterion } from "./criterion";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class CountryCriterion extends Criterion {
|
const countryCriterionOption = new CriterionOption("country", "country");
|
||||||
public type: CriterionType = "country";
|
|
||||||
public parameterName: string = "performers";
|
|
||||||
public modifier = CriterionModifier.Equals;
|
|
||||||
public modifierOptions = [];
|
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CountryCriterionOption implements ICriterionOption {
|
export class CountryCriterion extends StringCriterion {
|
||||||
public label: string = Criterion.getLabel("performers");
|
constructor() {
|
||||||
public value: CriterionType = "country";
|
super(countryCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,164 +1,24 @@
|
|||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
|
|
||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { IntlShape } from "react-intl";
|
||||||
|
import {
|
||||||
|
CriterionModifier,
|
||||||
|
MultiCriterionInput,
|
||||||
|
} from "src/core/generated-graphql";
|
||||||
import DurationUtils from "src/utils/duration";
|
import DurationUtils from "src/utils/duration";
|
||||||
import { ILabeledId, ILabeledValue, IOptionType } from "../types";
|
import {
|
||||||
|
CriterionType,
|
||||||
export type CriterionType =
|
encodeILabeledId,
|
||||||
| "none"
|
ILabeledId,
|
||||||
| "path"
|
ILabeledValue,
|
||||||
| "rating"
|
IOptionType,
|
||||||
| "organized"
|
} from "../types";
|
||||||
| "o_counter"
|
|
||||||
| "resolution"
|
|
||||||
| "average_resolution"
|
|
||||||
| "duration"
|
|
||||||
| "favorite"
|
|
||||||
| "hasMarkers"
|
|
||||||
| "sceneIsMissing"
|
|
||||||
| "imageIsMissing"
|
|
||||||
| "performerIsMissing"
|
|
||||||
| "galleryIsMissing"
|
|
||||||
| "tagIsMissing"
|
|
||||||
| "studioIsMissing"
|
|
||||||
| "movieIsMissing"
|
|
||||||
| "tags"
|
|
||||||
| "sceneTags"
|
|
||||||
| "performerTags"
|
|
||||||
| "tag_count"
|
|
||||||
| "performers"
|
|
||||||
| "studios"
|
|
||||||
| "movies"
|
|
||||||
| "galleries"
|
|
||||||
| "birth_year"
|
|
||||||
| "age"
|
|
||||||
| "ethnicity"
|
|
||||||
| "country"
|
|
||||||
| "hair_color"
|
|
||||||
| "eye_color"
|
|
||||||
| "height"
|
|
||||||
| "weight"
|
|
||||||
| "measurements"
|
|
||||||
| "fake_tits"
|
|
||||||
| "career_length"
|
|
||||||
| "tattoos"
|
|
||||||
| "piercings"
|
|
||||||
| "aliases"
|
|
||||||
| "gender"
|
|
||||||
| "parent_studios"
|
|
||||||
| "scene_count"
|
|
||||||
| "marker_count"
|
|
||||||
| "image_count"
|
|
||||||
| "gallery_count"
|
|
||||||
| "performer_count"
|
|
||||||
| "death_year"
|
|
||||||
| "url"
|
|
||||||
| "stash_id"
|
|
||||||
| "interactive";
|
|
||||||
|
|
||||||
type Option = string | number | IOptionType;
|
type Option = string | number | IOptionType;
|
||||||
export type CriterionValue = string | number | ILabeledId[];
|
export type CriterionValue = string | number | ILabeledId[];
|
||||||
|
|
||||||
export abstract class Criterion {
|
// V = criterion value type
|
||||||
public static getLabel(type: CriterionType = "none") {
|
export abstract class Criterion<V extends CriterionValue> {
|
||||||
switch (type) {
|
|
||||||
case "none":
|
|
||||||
return "None";
|
|
||||||
case "path":
|
|
||||||
return "Path";
|
|
||||||
case "rating":
|
|
||||||
return "Rating";
|
|
||||||
case "organized":
|
|
||||||
return "Organized";
|
|
||||||
case "o_counter":
|
|
||||||
return "O-Counter";
|
|
||||||
case "resolution":
|
|
||||||
return "Resolution";
|
|
||||||
case "average_resolution":
|
|
||||||
return "Average Resolution";
|
|
||||||
case "duration":
|
|
||||||
return "Duration";
|
|
||||||
case "favorite":
|
|
||||||
return "Favorite";
|
|
||||||
case "hasMarkers":
|
|
||||||
return "Has Markers";
|
|
||||||
case "sceneIsMissing":
|
|
||||||
case "imageIsMissing":
|
|
||||||
case "performerIsMissing":
|
|
||||||
case "galleryIsMissing":
|
|
||||||
case "tagIsMissing":
|
|
||||||
case "studioIsMissing":
|
|
||||||
case "movieIsMissing":
|
|
||||||
return "Is Missing";
|
|
||||||
case "tags":
|
|
||||||
return "Tags";
|
|
||||||
case "sceneTags":
|
|
||||||
return "Scene Tags";
|
|
||||||
case "performerTags":
|
|
||||||
return "Performer Tags";
|
|
||||||
case "tag_count":
|
|
||||||
return "Tag Count";
|
|
||||||
case "performers":
|
|
||||||
return "Performers";
|
|
||||||
case "studios":
|
|
||||||
return "Studios";
|
|
||||||
case "movies":
|
|
||||||
return "Movies";
|
|
||||||
case "galleries":
|
|
||||||
return "Galleries";
|
|
||||||
case "birth_year":
|
|
||||||
return "Birth Year";
|
|
||||||
case "death_year":
|
|
||||||
return "Death Year";
|
|
||||||
case "age":
|
|
||||||
return "Age";
|
|
||||||
case "ethnicity":
|
|
||||||
return "Ethnicity";
|
|
||||||
case "country":
|
|
||||||
return "Country";
|
|
||||||
case "hair_color":
|
|
||||||
return "Hair Color";
|
|
||||||
case "eye_color":
|
|
||||||
return "Eye Color";
|
|
||||||
case "height":
|
|
||||||
return "Height";
|
|
||||||
case "weight":
|
|
||||||
return "Weight";
|
|
||||||
case "measurements":
|
|
||||||
return "Measurements";
|
|
||||||
case "fake_tits":
|
|
||||||
return "Fake Tits";
|
|
||||||
case "career_length":
|
|
||||||
return "Career Length";
|
|
||||||
case "tattoos":
|
|
||||||
return "Tattoos";
|
|
||||||
case "piercings":
|
|
||||||
return "Piercings";
|
|
||||||
case "aliases":
|
|
||||||
return "Aliases";
|
|
||||||
case "gender":
|
|
||||||
return "Gender";
|
|
||||||
case "parent_studios":
|
|
||||||
return "Parent Studios";
|
|
||||||
case "scene_count":
|
|
||||||
return "Scene Count";
|
|
||||||
case "marker_count":
|
|
||||||
return "Marker Count";
|
|
||||||
case "image_count":
|
|
||||||
return "Image Count";
|
|
||||||
case "gallery_count":
|
|
||||||
return "Gallery Count";
|
|
||||||
case "performer_count":
|
|
||||||
return "Performer Count";
|
|
||||||
case "url":
|
|
||||||
return "URL";
|
|
||||||
case "stash_id":
|
|
||||||
return "StashID";
|
|
||||||
case "interactive":
|
|
||||||
return "Interactive";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getModifierOption(
|
public static getModifierOption(
|
||||||
modifier: CriterionModifier = CriterionModifier.Equals
|
modifier: CriterionModifier = CriterionModifier.Equals
|
||||||
): ILabeledValue {
|
): ILabeledValue {
|
||||||
@@ -194,60 +54,62 @@ export abstract class Criterion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract type: CriterionType;
|
public criterionOption: CriterionOption;
|
||||||
public abstract parameterName: string;
|
|
||||||
public abstract modifier: CriterionModifier;
|
public abstract modifier: CriterionModifier;
|
||||||
public abstract modifierOptions: ILabeledValue[];
|
public abstract modifierOptions: ILabeledValue[];
|
||||||
public abstract options: Option[] | undefined;
|
public abstract options: Option[] | undefined;
|
||||||
public abstract value: CriterionValue;
|
public abstract value: V;
|
||||||
public inputType: "number" | "text" | undefined;
|
public inputType: "number" | "text" | undefined;
|
||||||
|
|
||||||
public getLabelValue(): string {
|
public abstract getLabelValue(): string;
|
||||||
if (typeof this.value === "string") return this.value;
|
|
||||||
if (typeof this.value === "number") return this.value.toString();
|
constructor(type: CriterionOption) {
|
||||||
return this.value.map((v) => v.label).join(", ");
|
this.criterionOption = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLabel(): string {
|
public getLabel(intl: IntlShape): string {
|
||||||
let modifierString: string;
|
let modifierMessageID: string;
|
||||||
switch (this.modifier) {
|
switch (this.modifier) {
|
||||||
case CriterionModifier.Equals:
|
case CriterionModifier.Equals:
|
||||||
modifierString = "is";
|
modifierMessageID = "criterion_modifier.equals";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.NotEquals:
|
case CriterionModifier.NotEquals:
|
||||||
modifierString = "is not";
|
modifierMessageID = "criterion_modifier.not_equals";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.GreaterThan:
|
case CriterionModifier.GreaterThan:
|
||||||
modifierString = "is greater than";
|
modifierMessageID = "criterion_modifier.greater_than";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.LessThan:
|
case CriterionModifier.LessThan:
|
||||||
modifierString = "is less than";
|
modifierMessageID = "criterion_modifier.less_than";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.IsNull:
|
case CriterionModifier.IsNull:
|
||||||
modifierString = "is null";
|
modifierMessageID = "criterion_modifier.is_null";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.NotNull:
|
case CriterionModifier.NotNull:
|
||||||
modifierString = "is not null";
|
modifierMessageID = "criterion_modifier.not_null";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.Includes:
|
case CriterionModifier.Includes:
|
||||||
modifierString = "includes";
|
modifierMessageID = "criterion_modifier.includes";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.IncludesAll:
|
case CriterionModifier.IncludesAll:
|
||||||
modifierString = "includes all";
|
modifierMessageID = "criterion_modifier.includes_all";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.Excludes:
|
case CriterionModifier.Excludes:
|
||||||
modifierString = "excludes";
|
modifierMessageID = "criterion_modifier.excludes";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.MatchesRegex:
|
case CriterionModifier.MatchesRegex:
|
||||||
modifierString = "matches regex";
|
modifierMessageID = "criterion_modifier.matches_regex";
|
||||||
break;
|
break;
|
||||||
case CriterionModifier.NotMatchesRegex:
|
case CriterionModifier.NotMatchesRegex:
|
||||||
modifierString = "not matches regex";
|
modifierMessageID = "criterion_modifier.not_matches_regex";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
modifierString = "";
|
modifierMessageID = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modifierString = modifierMessageID
|
||||||
|
? intl.formatMessage({ id: modifierMessageID })
|
||||||
|
: "";
|
||||||
let valueString = "";
|
let valueString = "";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -257,47 +119,64 @@ export abstract class Criterion {
|
|||||||
valueString = this.getLabelValue();
|
valueString = this.getLabelValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${Criterion.getLabel(this.type)} ${modifierString} ${valueString}`;
|
return `${intl.formatMessage({
|
||||||
|
id: this.criterionOption.messageID,
|
||||||
|
})} ${modifierString} ${valueString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getId(): string {
|
public getId(): string {
|
||||||
return `${this.parameterName}-${this.modifier.toString()}`; // TODO add values?
|
return `${this.criterionOption.parameterName}-${this.modifier.toString()}`; // TODO add values?
|
||||||
}
|
}
|
||||||
|
|
||||||
private static replaceSpecialCharacter(str: string, c: string) {
|
public encodeValue(): V {
|
||||||
return str.replaceAll(c, encodeURIComponent(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
public encodeValue(): CriterionValue {
|
|
||||||
// replace certain characters
|
|
||||||
if (typeof this.value === "string") {
|
|
||||||
let ret = this.value;
|
|
||||||
ret = Criterion.replaceSpecialCharacter(ret, "&");
|
|
||||||
ret = Criterion.replaceSpecialCharacter(ret, "+");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICriterionOption {
|
public toJSON() {
|
||||||
label: string;
|
const encodedCriterion = {
|
||||||
value: CriterionType;
|
type: this.criterionOption.value,
|
||||||
}
|
// #394 - the presence of a # symbol results in the query URL being
|
||||||
|
// malformed. We could set encode: true in the queryString.stringify
|
||||||
|
// call below, but this results in a URL that gets pretty long and ugly.
|
||||||
|
// Instead, we'll encode the criteria values.
|
||||||
|
value: this.encodeValue(),
|
||||||
|
modifier: this.modifier,
|
||||||
|
};
|
||||||
|
return JSON.stringify(encodedCriterion);
|
||||||
|
}
|
||||||
|
|
||||||
export class CriterionOption implements ICriterionOption {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
public label: string;
|
public apply(outputFilter: Record<string, any>) {
|
||||||
public value: CriterionType;
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
outputFilter[this.criterionOption.parameterName] = this.toCriterionInput();
|
||||||
|
}
|
||||||
|
|
||||||
constructor(label: string, value: CriterionType) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
this.label = label;
|
protected toCriterionInput(): any {
|
||||||
this.value = value;
|
return {
|
||||||
|
value: this.value,
|
||||||
|
modifier: this.modifier,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StringCriterion extends Criterion {
|
export class CriterionOption {
|
||||||
public type: CriterionType;
|
public readonly messageID: string;
|
||||||
public parameterName: string;
|
public readonly value: CriterionType;
|
||||||
|
public readonly parameterName: string;
|
||||||
|
|
||||||
|
constructor(messageID: string, value: CriterionType, parameterName?: string) {
|
||||||
|
this.messageID = messageID;
|
||||||
|
this.value = value;
|
||||||
|
this.parameterName = parameterName ?? value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCriterionOption(value: CriterionType) {
|
||||||
|
return new CriterionOption(value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StringCriterion extends Criterion<string> {
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [
|
public modifierOptions = [
|
||||||
StringCriterion.getModifierOption(CriterionModifier.Equals),
|
StringCriterion.getModifierOption(CriterionModifier.Equals),
|
||||||
@@ -316,18 +195,23 @@ export class StringCriterion extends Criterion {
|
|||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(type: CriterionType, parameterName?: string, options?: string[]) {
|
public encodeValue() {
|
||||||
super();
|
// replace certain characters
|
||||||
|
let ret = this.value;
|
||||||
|
ret = StringCriterion.replaceSpecialCharacter(ret, "&");
|
||||||
|
ret = StringCriterion.replaceSpecialCharacter(ret, "+");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static replaceSpecialCharacter(str: string, c: string) {
|
||||||
|
return str.replaceAll(c, encodeURIComponent(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: CriterionOption, options?: string[]) {
|
||||||
|
super(type);
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.inputType = "text";
|
this.inputType = "text";
|
||||||
|
|
||||||
if (parameterName) {
|
|
||||||
this.parameterName = parameterName;
|
|
||||||
} else {
|
|
||||||
this.parameterName = type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,9 +226,20 @@ export class MandatoryStringCriterion extends StringCriterion {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NumberCriterion extends Criterion {
|
export class BooleanCriterion extends StringCriterion {
|
||||||
public type: CriterionType;
|
public modifier = CriterionModifier.Equals;
|
||||||
public parameterName: string;
|
public modifierOptions = [];
|
||||||
|
|
||||||
|
constructor(type: CriterionOption) {
|
||||||
|
super(type, [true.toString(), false.toString()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): boolean {
|
||||||
|
return this.value === "true";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NumberCriterion extends Criterion<number> {
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [
|
public modifierOptions = [
|
||||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
@@ -361,17 +256,51 @@ export class NumberCriterion extends Criterion {
|
|||||||
return this.value.toString();
|
return this.value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(type: CriterionType, parameterName?: string, options?: number[]) {
|
constructor(type: CriterionOption, options?: number[]) {
|
||||||
super();
|
super(type);
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.inputType = "number";
|
this.inputType = "number";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parameterName) {
|
export abstract class ILabeledIdCriterion extends Criterion<ILabeledId[]> {
|
||||||
this.parameterName = parameterName;
|
public modifier = CriterionModifier.IncludesAll;
|
||||||
} else {
|
public modifierOptions = [
|
||||||
this.parameterName = type;
|
Criterion.getModifierOption(CriterionModifier.IncludesAll),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Includes),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Excludes),
|
||||||
|
];
|
||||||
|
|
||||||
|
public options: IOptionType[] = [];
|
||||||
|
public value: ILabeledId[] = [];
|
||||||
|
|
||||||
|
public getLabelValue(): string {
|
||||||
|
return this.value.map((v) => v.label).join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): MultiCriterionInput {
|
||||||
|
return {
|
||||||
|
value: this.value.map((v) => v.id),
|
||||||
|
modifier: this.modifier,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public encodeValue() {
|
||||||
|
return this.value.map((o) => {
|
||||||
|
return encodeILabeledId(o);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: CriterionOption, includeAll: boolean) {
|
||||||
|
super(type);
|
||||||
|
|
||||||
|
if (!includeAll) {
|
||||||
|
this.modifier = CriterionModifier.Includes;
|
||||||
|
this.modifierOptions = [
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Includes),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Excludes),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,9 +314,7 @@ export class MandatoryNumberCriterion extends NumberCriterion {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DurationCriterion extends Criterion {
|
export class DurationCriterion extends Criterion<number> {
|
||||||
public type: CriterionType;
|
|
||||||
public parameterName: string;
|
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [
|
public modifierOptions = [
|
||||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
@@ -398,12 +325,10 @@ export class DurationCriterion extends Criterion {
|
|||||||
public options: number[] | undefined;
|
public options: number[] | undefined;
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
|
|
||||||
constructor(type: CriterionType, parameterName?: string, options?: number[]) {
|
constructor(type: CriterionOption, options?: number[]) {
|
||||||
super();
|
super(type);
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.parameterName = parameterName ?? type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLabelValue() {
|
public getLabelValue() {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable consistent-return, default-case */
|
/* eslint-disable consistent-return, default-case */
|
||||||
import {
|
import {
|
||||||
CriterionType,
|
|
||||||
StringCriterion,
|
StringCriterion,
|
||||||
NumberCriterion,
|
NumberCriterion,
|
||||||
DurationCriterion,
|
DurationCriterion,
|
||||||
MandatoryStringCriterion,
|
MandatoryStringCriterion,
|
||||||
MandatoryNumberCriterion,
|
MandatoryNumberCriterion,
|
||||||
|
CriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
import { OrganizedCriterion } from "./organized";
|
import { OrganizedCriterion } from "./organized";
|
||||||
import { FavoriteCriterion } from "./favorite";
|
import { FavoriteCriterion } from "./favorite";
|
||||||
@@ -24,10 +24,16 @@ import { PerformersCriterion } from "./performers";
|
|||||||
import { RatingCriterion } from "./rating";
|
import { RatingCriterion } from "./rating";
|
||||||
import { AverageResolutionCriterion, ResolutionCriterion } from "./resolution";
|
import { AverageResolutionCriterion, ResolutionCriterion } from "./resolution";
|
||||||
import { StudiosCriterion, ParentStudiosCriterion } from "./studios";
|
import { StudiosCriterion, ParentStudiosCriterion } from "./studios";
|
||||||
import { TagsCriterion } from "./tags";
|
import {
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
SceneTagsCriterionOption,
|
||||||
|
TagsCriterion,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "./tags";
|
||||||
import { GenderCriterion } from "./gender";
|
import { GenderCriterion } from "./gender";
|
||||||
import { MoviesCriterion } from "./movies";
|
import { MoviesCriterion } from "./movies";
|
||||||
import { GalleriesCriterion } from "./galleries";
|
import { GalleriesCriterion } from "./galleries";
|
||||||
|
import { CriterionType } from "../types";
|
||||||
import { InteractiveCriterion } from "./interactive";
|
import { InteractiveCriterion } from "./interactive";
|
||||||
|
|
||||||
export function makeCriteria(type: CriterionType = "none") {
|
export function makeCriteria(type: CriterionType = "none") {
|
||||||
@@ -35,7 +41,7 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "none":
|
case "none":
|
||||||
return new NoneCriterion();
|
return new NoneCriterion();
|
||||||
case "path":
|
case "path":
|
||||||
return new MandatoryStringCriterion(type, type);
|
return new MandatoryStringCriterion(new CriterionOption(type, type));
|
||||||
case "rating":
|
case "rating":
|
||||||
return new RatingCriterion();
|
return new RatingCriterion();
|
||||||
case "organized":
|
case "organized":
|
||||||
@@ -47,13 +53,13 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "gallery_count":
|
case "gallery_count":
|
||||||
case "performer_count":
|
case "performer_count":
|
||||||
case "tag_count":
|
case "tag_count":
|
||||||
return new MandatoryNumberCriterion(type, type);
|
return new MandatoryNumberCriterion(new CriterionOption(type, type));
|
||||||
case "resolution":
|
case "resolution":
|
||||||
return new ResolutionCriterion();
|
return new ResolutionCriterion();
|
||||||
case "average_resolution":
|
case "average_resolution":
|
||||||
return new AverageResolutionCriterion();
|
return new AverageResolutionCriterion();
|
||||||
case "duration":
|
case "duration":
|
||||||
return new DurationCriterion(type, type);
|
return new DurationCriterion(new CriterionOption(type, type));
|
||||||
case "favorite":
|
case "favorite":
|
||||||
return new FavoriteCriterion();
|
return new FavoriteCriterion();
|
||||||
case "hasMarkers":
|
case "hasMarkers":
|
||||||
@@ -73,11 +79,11 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "movieIsMissing":
|
case "movieIsMissing":
|
||||||
return new MovieIsMissingCriterion();
|
return new MovieIsMissingCriterion();
|
||||||
case "tags":
|
case "tags":
|
||||||
return new TagsCriterion("tags");
|
return new TagsCriterion(TagsCriterionOption);
|
||||||
case "sceneTags":
|
case "sceneTags":
|
||||||
return new TagsCriterion("sceneTags");
|
return new TagsCriterion(SceneTagsCriterionOption);
|
||||||
case "performerTags":
|
case "performerTags":
|
||||||
return new TagsCriterion("performerTags");
|
return new TagsCriterion(PerformerTagsCriterionOption);
|
||||||
case "performers":
|
case "performers":
|
||||||
return new PerformersCriterion();
|
return new PerformersCriterion();
|
||||||
case "studios":
|
case "studios":
|
||||||
@@ -91,9 +97,9 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "birth_year":
|
case "birth_year":
|
||||||
case "death_year":
|
case "death_year":
|
||||||
case "weight":
|
case "weight":
|
||||||
return new NumberCriterion(type, type);
|
return new NumberCriterion(new CriterionOption(type, type));
|
||||||
case "age":
|
case "age":
|
||||||
return new MandatoryNumberCriterion(type, type);
|
return new MandatoryNumberCriterion(new CriterionOption(type, type));
|
||||||
case "gender":
|
case "gender":
|
||||||
return new GenderCriterion();
|
return new GenderCriterion();
|
||||||
case "ethnicity":
|
case "ethnicity":
|
||||||
@@ -109,7 +115,7 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "aliases":
|
case "aliases":
|
||||||
case "url":
|
case "url":
|
||||||
case "stash_id":
|
case "stash_id":
|
||||||
return new StringCriterion(type, type);
|
return new StringCriterion(new CriterionOption(type, type));
|
||||||
case "interactive":
|
case "interactive":
|
||||||
return new InteractiveCriterion();
|
return new InteractiveCriterion();
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { BooleanCriterion, CriterionOption } from "./criterion";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class FavoriteCriterion extends Criterion {
|
export const FavoriteCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "favorite";
|
"favourite",
|
||||||
public parameterName: string = "filter_favorites";
|
"favorite",
|
||||||
public modifier = CriterionModifier.Equals;
|
"filter_favorites"
|
||||||
public modifierOptions = [];
|
);
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FavoriteCriterionOption implements ICriterionOption {
|
export class FavoriteCriterion extends BooleanCriterion {
|
||||||
public label: string = Criterion.getLabel("favorite");
|
constructor() {
|
||||||
public value: CriterionType = "favorite";
|
super(FavoriteCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,9 @@
|
|||||||
import * as GQL from "src/core/generated-graphql";
|
import { CriterionOption, ILabeledIdCriterion } from "./criterion";
|
||||||
import { ILabeledId, IOptionType, encodeILabeledId } from "../types";
|
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class GalleriesCriterion extends Criterion {
|
const galleriesCriterionOption = new CriterionOption("galleries", "galleries");
|
||||||
public type: CriterionType = "galleries";
|
|
||||||
public parameterName: string = "galleries";
|
|
||||||
public modifier = GQL.CriterionModifier.IncludesAll;
|
|
||||||
public modifierOptions = [
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.IncludesAll),
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.Includes),
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.Excludes),
|
|
||||||
];
|
|
||||||
public options: IOptionType[] = [];
|
|
||||||
public value: ILabeledId[] = [];
|
|
||||||
|
|
||||||
public encodeValue() {
|
export class GalleriesCriterion extends ILabeledIdCriterion {
|
||||||
return this.value.map((o) => {
|
constructor() {
|
||||||
return encodeILabeledId(o);
|
super(galleriesCriterionOption, true);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GalleriesCriterionOption implements ICriterionOption {
|
|
||||||
public label: string = Criterion.getLabel("galleries");
|
|
||||||
public value: CriterionType = "galleries";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import {
|
||||||
import { getGenderStrings } from "src/core/StashService";
|
CriterionModifier,
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
GenderCriterionInput,
|
||||||
|
} from "src/core/generated-graphql";
|
||||||
|
import { getGenderStrings, stringToGender } from "src/core/StashService";
|
||||||
|
import { CriterionOption, StringCriterion } from "./criterion";
|
||||||
|
|
||||||
export class GenderCriterion extends Criterion {
|
export const GenderCriterionOption = new CriterionOption("gender", "gender");
|
||||||
public type: CriterionType = "gender";
|
|
||||||
public parameterName: string = "gender";
|
export class GenderCriterion extends StringCriterion {
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [];
|
public modifierOptions = [];
|
||||||
public options: string[] = getGenderStrings();
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GenderCriterionOption implements ICriterionOption {
|
constructor() {
|
||||||
public label: string = Criterion.getLabel("gender");
|
super(GenderCriterionOption, getGenderStrings());
|
||||||
public value: CriterionType = "gender";
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): GenderCriterionInput {
|
||||||
|
return {
|
||||||
|
value: stringToGender(this.value),
|
||||||
|
modifier: this.modifier,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionOption, StringCriterion } from "./criterion";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class HasMarkersCriterion extends Criterion {
|
export const HasMarkersCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "hasMarkers";
|
"hasMarkers",
|
||||||
public parameterName: string = "has_markers";
|
"hasMarkers",
|
||||||
public modifier = CriterionModifier.Equals;
|
"has_markers"
|
||||||
public modifierOptions = [];
|
);
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HasMarkersCriterionOption implements ICriterionOption {
|
export class HasMarkersCriterion extends StringCriterion {
|
||||||
public label: string = Criterion.getLabel("hasMarkers");
|
constructor() {
|
||||||
public value: CriterionType = "hasMarkers";
|
super(HasMarkersCriterionOption, [true.toString(), false.toString()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): string {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { BooleanCriterion, CriterionOption } from "./criterion";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class InteractiveCriterion extends Criterion {
|
export const InteractiveCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "interactive";
|
"organized",
|
||||||
public parameterName: string = "interactive";
|
"organized"
|
||||||
public modifier = CriterionModifier.Equals;
|
);
|
||||||
public modifierOptions = [];
|
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InteractiveCriterionOption implements ICriterionOption {
|
export class InteractiveCriterion extends BooleanCriterion {
|
||||||
public label: string = Criterion.getLabel("interactive");
|
constructor() {
|
||||||
public value: CriterionType = "interactive";
|
super(InteractiveCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,123 +1,145 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier } from "src/core/generated-graphql";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
import { CriterionOption, StringCriterion } from "./criterion";
|
||||||
|
|
||||||
export abstract class IsMissingCriterion extends Criterion {
|
export abstract class IsMissingCriterion extends StringCriterion {
|
||||||
public parameterName: string = "is_missing";
|
|
||||||
public modifierOptions = [];
|
public modifierOptions = [];
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public value: string = "";
|
|
||||||
|
protected toCriterionInput(): string {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SceneIsMissingCriterionOption = new CriterionOption(
|
||||||
|
"isMissing",
|
||||||
|
"sceneIsMissing",
|
||||||
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class SceneIsMissingCriterion extends IsMissingCriterion {
|
export class SceneIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "sceneIsMissing";
|
constructor() {
|
||||||
public options: string[] = [
|
super(SceneIsMissingCriterionOption, [
|
||||||
"title",
|
"title",
|
||||||
"details",
|
"details",
|
||||||
"url",
|
"url",
|
||||||
"date",
|
"date",
|
||||||
"galleries",
|
"galleries",
|
||||||
"studio",
|
"studio",
|
||||||
"movie",
|
"movie",
|
||||||
"performers",
|
"performers",
|
||||||
"tags",
|
"tags",
|
||||||
];
|
"stash_id",
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SceneIsMissingCriterionOption implements ICriterionOption {
|
export const ImageIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("sceneIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "sceneIsMissing";
|
"imageIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class ImageIsMissingCriterion extends IsMissingCriterion {
|
export class ImageIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "imageIsMissing";
|
constructor() {
|
||||||
public options: string[] = [
|
super(ImageIsMissingCriterionOption, [
|
||||||
"title",
|
"title",
|
||||||
"galleries",
|
"galleries",
|
||||||
"studio",
|
"studio",
|
||||||
"performers",
|
"performers",
|
||||||
"tags",
|
"tags",
|
||||||
];
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageIsMissingCriterionOption implements ICriterionOption {
|
export const PerformerIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("imageIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "imageIsMissing";
|
"performerIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class PerformerIsMissingCriterion extends IsMissingCriterion {
|
export class PerformerIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "performerIsMissing";
|
constructor() {
|
||||||
public options: string[] = [
|
super(PerformerIsMissingCriterionOption, [
|
||||||
"url",
|
"url",
|
||||||
"twitter",
|
"twitter",
|
||||||
"instagram",
|
"instagram",
|
||||||
"ethnicity",
|
"ethnicity",
|
||||||
"country",
|
"country",
|
||||||
"hair_color",
|
"hair_color",
|
||||||
"eye_color",
|
"eye_color",
|
||||||
"height",
|
"height",
|
||||||
"weight",
|
"weight",
|
||||||
"measurements",
|
"measurements",
|
||||||
"fake_tits",
|
"fake_tits",
|
||||||
"career_length",
|
"career_length",
|
||||||
"tattoos",
|
"tattoos",
|
||||||
"piercings",
|
"piercings",
|
||||||
"aliases",
|
"aliases",
|
||||||
"gender",
|
"gender",
|
||||||
"image",
|
"image",
|
||||||
"details",
|
"details",
|
||||||
];
|
"stash_id",
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerformerIsMissingCriterionOption implements ICriterionOption {
|
export const GalleryIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("performerIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "performerIsMissing";
|
"galleryIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class GalleryIsMissingCriterion extends IsMissingCriterion {
|
export class GalleryIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "galleryIsMissing";
|
constructor() {
|
||||||
public options: string[] = [
|
super(GalleryIsMissingCriterionOption, [
|
||||||
"title",
|
"title",
|
||||||
"details",
|
"details",
|
||||||
"url",
|
"url",
|
||||||
"date",
|
"date",
|
||||||
"studio",
|
"studio",
|
||||||
"performers",
|
"performers",
|
||||||
"tags",
|
"tags",
|
||||||
"scenes",
|
"scenes",
|
||||||
];
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GalleryIsMissingCriterionOption implements ICriterionOption {
|
export const TagIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("galleryIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "galleryIsMissing";
|
"tagIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class TagIsMissingCriterion extends IsMissingCriterion {
|
export class TagIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "tagIsMissing";
|
constructor() {
|
||||||
public options: string[] = ["image"];
|
super(TagIsMissingCriterionOption, ["image"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TagIsMissingCriterionOption implements ICriterionOption {
|
export const StudioIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("tagIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "tagIsMissing";
|
"studioIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class StudioIsMissingCriterion extends IsMissingCriterion {
|
export class StudioIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "studioIsMissing";
|
constructor() {
|
||||||
public options: string[] = ["image", "details"];
|
super(StudioIsMissingCriterionOption, ["image", "stash_id", "details"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StudioIsMissingCriterionOption implements ICriterionOption {
|
export const MovieIsMissingCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("studioIsMissing");
|
"isMissing",
|
||||||
public value: CriterionType = "studioIsMissing";
|
"movieIsMissing",
|
||||||
}
|
"is_missing"
|
||||||
|
);
|
||||||
|
|
||||||
export class MovieIsMissingCriterion extends IsMissingCriterion {
|
export class MovieIsMissingCriterion extends IsMissingCriterion {
|
||||||
public type: CriterionType = "movieIsMissing";
|
constructor() {
|
||||||
public options: string[] = ["front_image", "back_image", "scenes"];
|
super(MovieIsMissingCriterionOption, [
|
||||||
}
|
"front_image",
|
||||||
|
"back_image",
|
||||||
export class MovieIsMissingCriterionOption implements ICriterionOption {
|
"scenes",
|
||||||
public label: string = Criterion.getLabel("movieIsMissing");
|
]);
|
||||||
public value: CriterionType = "movieIsMissing";
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,9 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionOption, ILabeledIdCriterion } from "./criterion";
|
||||||
import { ILabeledId, encodeILabeledId } from "../types";
|
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
interface IOptionType {
|
export const MoviesCriterionOption = new CriterionOption("movies", "movies");
|
||||||
id: string;
|
|
||||||
name?: string;
|
|
||||||
image_path?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MoviesCriterion extends Criterion {
|
export class MoviesCriterion extends ILabeledIdCriterion {
|
||||||
public type: CriterionType = "movies";
|
constructor() {
|
||||||
public parameterName: string = "movies";
|
super(MoviesCriterionOption, false);
|
||||||
public modifier = CriterionModifier.Includes;
|
|
||||||
public modifierOptions = [
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
|
||||||
];
|
|
||||||
public options: IOptionType[] = [];
|
|
||||||
public value: ILabeledId[] = [];
|
|
||||||
|
|
||||||
public encodeValue() {
|
|
||||||
return this.value.map((o) => {
|
|
||||||
return encodeILabeledId(o);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MoviesCriterionOption implements ICriterionOption {
|
|
||||||
public label: string = Criterion.getLabel("movies");
|
|
||||||
public value: CriterionType = "movies";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier } from "src/core/generated-graphql";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
import { Criterion, CriterionOption } from "./criterion";
|
||||||
|
|
||||||
export class NoneCriterion extends Criterion {
|
export const NoneCriterionOption = new CriterionOption("none", "none");
|
||||||
public type: CriterionType = "none";
|
export class NoneCriterion extends Criterion<string> {
|
||||||
public parameterName: string = "";
|
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [];
|
public modifierOptions = [];
|
||||||
public options: undefined;
|
public options: undefined;
|
||||||
public value: string = "none";
|
public value: string = "none";
|
||||||
}
|
|
||||||
|
|
||||||
export class NoneCriterionOption implements ICriterionOption {
|
constructor() {
|
||||||
public label: string = Criterion.getLabel("none");
|
super(NoneCriterionOption);
|
||||||
public value: CriterionType = "none";
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
public getLabelValue(): string {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { BooleanCriterion, CriterionOption } from "./criterion";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class OrganizedCriterion extends Criterion {
|
export const OrganizedCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "organized";
|
"organized",
|
||||||
public parameterName: string = "organized";
|
"organized"
|
||||||
public modifier = CriterionModifier.Equals;
|
);
|
||||||
public modifierOptions = [];
|
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
|
||||||
public value: string = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OrganizedCriterionOption implements ICriterionOption {
|
export class OrganizedCriterion extends BooleanCriterion {
|
||||||
public label: string = Criterion.getLabel("organized");
|
constructor() {
|
||||||
public value: CriterionType = "organized";
|
super(OrganizedCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,12 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionOption, ILabeledIdCriterion } from "./criterion";
|
||||||
import { ILabeledId, IOptionType, encodeILabeledId } from "../types";
|
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class PerformersCriterion extends Criterion {
|
export const PerformersCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "performers";
|
"performers",
|
||||||
public parameterName: string = "performers";
|
"performers"
|
||||||
public modifier = CriterionModifier.IncludesAll;
|
);
|
||||||
public modifierOptions = [
|
|
||||||
Criterion.getModifierOption(CriterionModifier.IncludesAll),
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
|
||||||
];
|
|
||||||
public options: IOptionType[] = [];
|
|
||||||
public value: ILabeledId[] = [];
|
|
||||||
|
|
||||||
public encodeValue() {
|
export class PerformersCriterion extends ILabeledIdCriterion {
|
||||||
return this.value.map((o) => {
|
constructor() {
|
||||||
return encodeILabeledId(o);
|
super(PerformersCriterionOption, true);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerformersCriterionOption implements ICriterionOption {
|
|
||||||
public label: string = Criterion.getLabel("performers");
|
|
||||||
public value: CriterionType = "performers";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier } from "src/core/generated-graphql";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
import { Criterion, CriterionOption, NumberCriterion } from "./criterion";
|
||||||
|
|
||||||
export class RatingCriterion extends Criterion {
|
export const RatingCriterionOption = new CriterionOption("rating", "rating");
|
||||||
public type: CriterionType = "rating";
|
|
||||||
public parameterName: string = "rating";
|
export class RatingCriterion extends NumberCriterion {
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [
|
public modifierOptions = [
|
||||||
Criterion.getModifierOption(CriterionModifier.Equals),
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
@@ -13,11 +13,8 @@ export class RatingCriterion extends Criterion {
|
|||||||
Criterion.getModifierOption(CriterionModifier.IsNull),
|
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||||
Criterion.getModifierOption(CriterionModifier.NotNull),
|
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||||
];
|
];
|
||||||
public options: number[] = [1, 2, 3, 4, 5];
|
|
||||||
public value: number = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RatingCriterionOption implements ICriterionOption {
|
constructor() {
|
||||||
public label: string = Criterion.getLabel("rating");
|
super(RatingCriterionOption, [1, 2, 3, 4, 5]);
|
||||||
public value: CriterionType = "rating";
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,80 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionModifier, ResolutionEnum } from "src/core/generated-graphql";
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
import { CriterionOption, StringCriterion } from "./criterion";
|
||||||
|
|
||||||
export class ResolutionCriterion extends Criterion {
|
abstract class AbstractResolutionCriterion extends StringCriterion {
|
||||||
public type: CriterionType = "resolution";
|
|
||||||
public parameterName: string = "resolution";
|
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
public modifierOptions = [];
|
public modifierOptions = [];
|
||||||
public options: string[] = [
|
|
||||||
"144p",
|
constructor(type: CriterionOption) {
|
||||||
"240p",
|
super(type, [
|
||||||
"360p",
|
"144p",
|
||||||
"480p",
|
"240p",
|
||||||
"540p",
|
"360p",
|
||||||
"720p",
|
"480p",
|
||||||
"1080p",
|
"540p",
|
||||||
"1440p",
|
"720p",
|
||||||
"4k",
|
"1080p",
|
||||||
"5k",
|
"1440p",
|
||||||
"6k",
|
"4k",
|
||||||
"8k",
|
"5k",
|
||||||
];
|
"6k",
|
||||||
public value: string = "";
|
"8k",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toCriterionInput(): ResolutionEnum | undefined {
|
||||||
|
switch (this.value) {
|
||||||
|
case "144p":
|
||||||
|
return ResolutionEnum.VeryLow;
|
||||||
|
case "240p":
|
||||||
|
return ResolutionEnum.Low;
|
||||||
|
case "360p":
|
||||||
|
return ResolutionEnum.R360P;
|
||||||
|
case "480p":
|
||||||
|
return ResolutionEnum.Standard;
|
||||||
|
case "540p":
|
||||||
|
return ResolutionEnum.WebHd;
|
||||||
|
case "720p":
|
||||||
|
return ResolutionEnum.StandardHd;
|
||||||
|
case "1080p":
|
||||||
|
return ResolutionEnum.FullHd;
|
||||||
|
case "1440p":
|
||||||
|
return ResolutionEnum.QuadHd;
|
||||||
|
case "1920p":
|
||||||
|
return ResolutionEnum.VrHd;
|
||||||
|
case "4k":
|
||||||
|
return ResolutionEnum.FourK;
|
||||||
|
case "5k":
|
||||||
|
return ResolutionEnum.FiveK;
|
||||||
|
case "6k":
|
||||||
|
return ResolutionEnum.SixK;
|
||||||
|
case "8k":
|
||||||
|
return ResolutionEnum.EightK;
|
||||||
|
// no default
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResolutionCriterionOption implements ICriterionOption {
|
export const ResolutionCriterionOption = new CriterionOption(
|
||||||
public label: string = Criterion.getLabel("resolution");
|
"resolution",
|
||||||
public value: CriterionType = "resolution";
|
"resolution"
|
||||||
|
);
|
||||||
|
export class ResolutionCriterion extends AbstractResolutionCriterion {
|
||||||
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(ResolutionCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AverageResolutionCriterion extends ResolutionCriterion {
|
export const AverageResolutionCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "average_resolution";
|
"average_resolution",
|
||||||
public parameterName: string = "average_resolution";
|
"average_resolution"
|
||||||
}
|
);
|
||||||
|
|
||||||
export class AverageResolutionCriterionOption extends ResolutionCriterionOption {
|
export class AverageResolutionCriterion extends AbstractResolutionCriterion {
|
||||||
public label: string = Criterion.getLabel("average_resolution");
|
constructor() {
|
||||||
public value: CriterionType = "average_resolution";
|
super(AverageResolutionCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,25 @@
|
|||||||
import { CriterionModifier } from "src/core/generated-graphql";
|
import { CriterionOption, ILabeledIdCriterion } from "./criterion";
|
||||||
import { ILabeledId, IOptionType, encodeILabeledId } from "../types";
|
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class StudiosCriterion extends Criterion {
|
abstract class AbstractStudiosCriterion extends ILabeledIdCriterion {
|
||||||
public type: CriterionType = "studios";
|
constructor(type: CriterionOption) {
|
||||||
public parameterName: string = "studios";
|
super(type, false);
|
||||||
public modifier = CriterionModifier.Includes;
|
|
||||||
public modifierOptions = [
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Includes),
|
|
||||||
Criterion.getModifierOption(CriterionModifier.Excludes),
|
|
||||||
];
|
|
||||||
public options: IOptionType[] = [];
|
|
||||||
public value: ILabeledId[] = [];
|
|
||||||
|
|
||||||
public encodeValue() {
|
|
||||||
return this.value.map((o) => {
|
|
||||||
return encodeILabeledId(o);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StudiosCriterionOption implements ICriterionOption {
|
export const StudiosCriterionOption = new CriterionOption("studios", "studios");
|
||||||
public label: string = Criterion.getLabel("studios");
|
export class StudiosCriterion extends AbstractStudiosCriterion {
|
||||||
public value: CriterionType = "studios";
|
constructor() {
|
||||||
|
super(StudiosCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParentStudiosCriterion extends StudiosCriterion {
|
export const ParentStudiosCriterionOption = new CriterionOption(
|
||||||
public type: CriterionType = "parent_studios";
|
"parent_studios",
|
||||||
public parameterName: string = "parents";
|
"parent_studios",
|
||||||
}
|
"parents"
|
||||||
|
);
|
||||||
export class ParentStudiosCriterionOption implements ICriterionOption {
|
export class ParentStudiosCriterion extends AbstractStudiosCriterion {
|
||||||
public label: string = Criterion.getLabel("parent_studios");
|
constructor() {
|
||||||
public value: CriterionType = "parent_studios";
|
super(ParentStudiosCriterionOption);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,19 @@
|
|||||||
import * as GQL from "src/core/generated-graphql";
|
import { CriterionOption, ILabeledIdCriterion } from "./criterion";
|
||||||
import { ILabeledId, IOptionType, encodeILabeledId } from "../types";
|
|
||||||
import { Criterion, CriterionType, ICriterionOption } from "./criterion";
|
|
||||||
|
|
||||||
export class TagsCriterion extends Criterion {
|
export class TagsCriterion extends ILabeledIdCriterion {
|
||||||
public type: CriterionType;
|
constructor(type: CriterionOption) {
|
||||||
public parameterName: string;
|
super(type, true);
|
||||||
public modifier = GQL.CriterionModifier.IncludesAll;
|
|
||||||
public modifierOptions = [
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.IncludesAll),
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.Includes),
|
|
||||||
Criterion.getModifierOption(GQL.CriterionModifier.Excludes),
|
|
||||||
];
|
|
||||||
public options: IOptionType[] = [];
|
|
||||||
public value: ILabeledId[] = [];
|
|
||||||
|
|
||||||
constructor(type: "tags" | "sceneTags" | "performerTags") {
|
|
||||||
super();
|
|
||||||
this.type = type;
|
|
||||||
this.parameterName = type;
|
|
||||||
if (type === "sceneTags") {
|
|
||||||
this.parameterName = "scene_tags";
|
|
||||||
}
|
|
||||||
if (type === "performerTags") {
|
|
||||||
this.parameterName = "performer_tags";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public encodeValue() {
|
|
||||||
return this.value.map((o) => {
|
|
||||||
return encodeILabeledId(o);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TagsCriterionOption implements ICriterionOption {
|
export const TagsCriterionOption = new CriterionOption("tags", "tags");
|
||||||
public label: string = Criterion.getLabel("tags");
|
export const SceneTagsCriterionOption = new CriterionOption(
|
||||||
public value: CriterionType = "tags";
|
"sceneTags",
|
||||||
}
|
"sceneTags",
|
||||||
|
"scene_tags"
|
||||||
export class SceneTagsCriterionOption implements ICriterionOption {
|
);
|
||||||
public label: string = Criterion.getLabel("sceneTags");
|
export const PerformerTagsCriterionOption = new CriterionOption(
|
||||||
public value: CriterionType = "sceneTags";
|
"performerTags",
|
||||||
}
|
"performerTags",
|
||||||
|
"performer_tags"
|
||||||
export class PerformerTagsCriterionOption implements ICriterionOption {
|
);
|
||||||
public label: string = Criterion.getLabel("performerTags");
|
|
||||||
public value: CriterionType = "performerTags";
|
|
||||||
}
|
|
||||||
|
|||||||
31
ui/v2.5/src/models/list-filter/factory.ts
Normal file
31
ui/v2.5/src/models/list-filter/factory.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { GalleryListFilterOptions } from "./galleries";
|
||||||
|
import { ImageListFilterOptions } from "./images";
|
||||||
|
import { MovieListFilterOptions } from "./movies";
|
||||||
|
import { PerformerListFilterOptions } from "./performers";
|
||||||
|
import { SceneMarkerListFilterOptions } from "./scene-markers";
|
||||||
|
import { SceneListFilterOptions } from "./scenes";
|
||||||
|
import { StudioListFilterOptions } from "./studios";
|
||||||
|
import { TagListFilterOptions } from "./tags";
|
||||||
|
import { FilterMode } from "./types";
|
||||||
|
|
||||||
|
export function getFilterOptions(mode: FilterMode): ListFilterOptions {
|
||||||
|
switch (mode) {
|
||||||
|
case FilterMode.Scenes:
|
||||||
|
return SceneListFilterOptions;
|
||||||
|
case FilterMode.Performers:
|
||||||
|
return PerformerListFilterOptions;
|
||||||
|
case FilterMode.Studios:
|
||||||
|
return StudioListFilterOptions;
|
||||||
|
case FilterMode.Galleries:
|
||||||
|
return GalleryListFilterOptions;
|
||||||
|
case FilterMode.SceneMarkers:
|
||||||
|
return SceneMarkerListFilterOptions;
|
||||||
|
case FilterMode.Movies:
|
||||||
|
return MovieListFilterOptions;
|
||||||
|
case FilterMode.Tags:
|
||||||
|
return TagListFilterOptions;
|
||||||
|
case FilterMode.Images:
|
||||||
|
return ImageListFilterOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
ui/v2.5/src/models/list-filter/filter-options.ts
Normal file
37
ui/v2.5/src/models/list-filter/filter-options.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { CriterionOption } from "./criteria/criterion";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
interface ISortByOption {
|
||||||
|
messageID: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListFilterOptions {
|
||||||
|
public readonly defaultSortBy: string = "";
|
||||||
|
public readonly sortByOptions: ISortByOption[] = [];
|
||||||
|
public readonly displayModeOptions: DisplayMode[] = [];
|
||||||
|
public readonly criterionOptions: CriterionOption[] = [];
|
||||||
|
|
||||||
|
public static createSortBy(value: string) {
|
||||||
|
return {
|
||||||
|
messageID: value,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
defaultSortBy: string,
|
||||||
|
sortByOptions: ISortByOption[],
|
||||||
|
displayModeOptions: DisplayMode[],
|
||||||
|
criterionOptions: CriterionOption[]
|
||||||
|
) {
|
||||||
|
this.defaultSortBy = defaultSortBy;
|
||||||
|
this.sortByOptions = [
|
||||||
|
...sortByOptions,
|
||||||
|
ListFilterOptions.createSortBy("created_at"),
|
||||||
|
ListFilterOptions.createSortBy("updated_at"),
|
||||||
|
];
|
||||||
|
this.displayModeOptions = displayModeOptions;
|
||||||
|
this.criterionOptions = criterionOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
63
ui/v2.5/src/models/list-filter/galleries.ts
Normal file
63
ui/v2.5/src/models/list-filter/galleries.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { GalleryIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { OrganizedCriterionOption } from "./criteria/organized";
|
||||||
|
import { PerformersCriterionOption } from "./criteria/performers";
|
||||||
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
|
import { AverageResolutionCriterionOption } from "./criteria/resolution";
|
||||||
|
import { StudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import {
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "./criteria/tags";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "path";
|
||||||
|
|
||||||
|
const sortByOptions = [
|
||||||
|
"date",
|
||||||
|
"path",
|
||||||
|
"file_mod_time",
|
||||||
|
"tag_count",
|
||||||
|
"performer_count",
|
||||||
|
"title",
|
||||||
|
"random",
|
||||||
|
]
|
||||||
|
.map(ListFilterOptions.createSortBy)
|
||||||
|
.concat([
|
||||||
|
{
|
||||||
|
messageID: "image_count",
|
||||||
|
value: "images_count",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const displayModeOptions = [
|
||||||
|
DisplayMode.Grid,
|
||||||
|
DisplayMode.List,
|
||||||
|
DisplayMode.Wall,
|
||||||
|
];
|
||||||
|
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
createCriterionOption("path"),
|
||||||
|
RatingCriterionOption,
|
||||||
|
OrganizedCriterionOption,
|
||||||
|
AverageResolutionCriterionOption,
|
||||||
|
GalleryIsMissingCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
createCriterionOption("tag_count"),
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
PerformersCriterionOption,
|
||||||
|
createCriterionOption("performer_count"),
|
||||||
|
createCriterionOption("image_count"),
|
||||||
|
StudiosCriterionOption,
|
||||||
|
createCriterionOption("url"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const GalleryListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
51
ui/v2.5/src/models/list-filter/images.ts
Normal file
51
ui/v2.5/src/models/list-filter/images.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { ImageIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { OrganizedCriterionOption } from "./criteria/organized";
|
||||||
|
import { PerformersCriterionOption } from "./criteria/performers";
|
||||||
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
|
import { ResolutionCriterionOption } from "./criteria/resolution";
|
||||||
|
import { StudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import {
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "./criteria/tags";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "path";
|
||||||
|
|
||||||
|
const sortByOptions = [
|
||||||
|
"title",
|
||||||
|
"path",
|
||||||
|
"rating",
|
||||||
|
"o_counter",
|
||||||
|
"filesize",
|
||||||
|
"file_mod_time",
|
||||||
|
"tag_count",
|
||||||
|
"performer_count",
|
||||||
|
"random",
|
||||||
|
].map(ListFilterOptions.createSortBy);
|
||||||
|
|
||||||
|
const displayModeOptions = [DisplayMode.Grid, DisplayMode.Wall];
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
createCriterionOption("path"),
|
||||||
|
RatingCriterionOption,
|
||||||
|
OrganizedCriterionOption,
|
||||||
|
createCriterionOption("o_counter"),
|
||||||
|
ResolutionCriterionOption,
|
||||||
|
ImageIsMissingCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
createCriterionOption("tag_count"),
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
PerformersCriterionOption,
|
||||||
|
createCriterionOption("performer_count"),
|
||||||
|
StudiosCriterionOption,
|
||||||
|
];
|
||||||
|
export const ImageListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
31
ui/v2.5/src/models/list-filter/movies.ts
Normal file
31
ui/v2.5/src/models/list-filter/movies.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { MovieIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { StudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "name";
|
||||||
|
|
||||||
|
const sortByOptions = ["name", "random"]
|
||||||
|
.map(ListFilterOptions.createSortBy)
|
||||||
|
.concat([
|
||||||
|
{
|
||||||
|
messageID: "scene_count",
|
||||||
|
value: "scenes_count",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const displayModeOptions = [DisplayMode.Grid];
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
StudiosCriterionOption,
|
||||||
|
MovieIsMissingCriterionOption,
|
||||||
|
createCriterionOption("url"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const MovieListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
77
ui/v2.5/src/models/list-filter/performers.ts
Normal file
77
ui/v2.5/src/models/list-filter/performers.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||||
|
import { GenderCriterionOption } from "./criteria/gender";
|
||||||
|
import { PerformerIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
|
import { StudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import { TagsCriterionOption } from "./criteria/tags";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { CriterionType, DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "name";
|
||||||
|
const sortByOptions = [
|
||||||
|
"name",
|
||||||
|
"height",
|
||||||
|
"birthdate",
|
||||||
|
"tag_count",
|
||||||
|
"random",
|
||||||
|
"rating",
|
||||||
|
]
|
||||||
|
.map(ListFilterOptions.createSortBy)
|
||||||
|
.concat([
|
||||||
|
{
|
||||||
|
messageID: "scene_count",
|
||||||
|
value: "scenes_count",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const displayModeOptions = [
|
||||||
|
DisplayMode.Grid,
|
||||||
|
DisplayMode.List,
|
||||||
|
DisplayMode.Tagger,
|
||||||
|
];
|
||||||
|
|
||||||
|
const numberCriteria: CriterionType[] = [
|
||||||
|
"birth_year",
|
||||||
|
"death_year",
|
||||||
|
"age",
|
||||||
|
"weight",
|
||||||
|
];
|
||||||
|
|
||||||
|
const stringCriteria: CriterionType[] = [
|
||||||
|
"ethnicity",
|
||||||
|
"country",
|
||||||
|
"hair_color",
|
||||||
|
"eye_color",
|
||||||
|
"height",
|
||||||
|
"measurements",
|
||||||
|
"fake_tits",
|
||||||
|
"career_length",
|
||||||
|
"tattoos",
|
||||||
|
"piercings",
|
||||||
|
"aliases",
|
||||||
|
"stash_id",
|
||||||
|
];
|
||||||
|
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
FavoriteCriterionOption,
|
||||||
|
GenderCriterionOption,
|
||||||
|
PerformerIsMissingCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
RatingCriterionOption,
|
||||||
|
StudiosCriterionOption,
|
||||||
|
createCriterionOption("url"),
|
||||||
|
createCriterionOption("tag_count"),
|
||||||
|
createCriterionOption("scene_count"),
|
||||||
|
createCriterionOption("image_count"),
|
||||||
|
createCriterionOption("gallery_count"),
|
||||||
|
...numberCriteria.concat(stringCriteria).map((c) => createCriterionOption(c)),
|
||||||
|
];
|
||||||
|
export const PerformerListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
28
ui/v2.5/src/models/list-filter/scene-markers.ts
Normal file
28
ui/v2.5/src/models/list-filter/scene-markers.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { PerformersCriterionOption } from "./criteria/performers";
|
||||||
|
import { SceneTagsCriterionOption, TagsCriterionOption } from "./criteria/tags";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "title";
|
||||||
|
const sortByOptions = [
|
||||||
|
"title",
|
||||||
|
"seconds",
|
||||||
|
"scene_id",
|
||||||
|
"random",
|
||||||
|
"scenes_updated_at",
|
||||||
|
].map(ListFilterOptions.createSortBy);
|
||||||
|
const displayModeOptions = [DisplayMode.Wall];
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
SceneTagsCriterionOption,
|
||||||
|
PerformersCriterionOption,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SceneMarkerListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
73
ui/v2.5/src/models/list-filter/scenes.ts
Normal file
73
ui/v2.5/src/models/list-filter/scenes.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { HasMarkersCriterionOption } from "./criteria/has-markers";
|
||||||
|
import { SceneIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { MoviesCriterionOption } from "./criteria/movies";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { OrganizedCriterionOption } from "./criteria/organized";
|
||||||
|
import { PerformersCriterionOption } from "./criteria/performers";
|
||||||
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
|
import { ResolutionCriterionOption } from "./criteria/resolution";
|
||||||
|
import { StudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import { InteractiveCriterionOption } from "./criteria/interactive";
|
||||||
|
import {
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "./criteria/tags";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "date";
|
||||||
|
const sortByOptions = [
|
||||||
|
"title",
|
||||||
|
"path",
|
||||||
|
"rating",
|
||||||
|
"organized",
|
||||||
|
"o_counter",
|
||||||
|
"date",
|
||||||
|
"filesize",
|
||||||
|
"file_mod_time",
|
||||||
|
"duration",
|
||||||
|
"framerate",
|
||||||
|
"bitrate",
|
||||||
|
"tag_count",
|
||||||
|
"performer_count",
|
||||||
|
"random",
|
||||||
|
"movie_scene_number",
|
||||||
|
"interactive",
|
||||||
|
].map(ListFilterOptions.createSortBy);
|
||||||
|
|
||||||
|
const displayModeOptions = [
|
||||||
|
DisplayMode.Grid,
|
||||||
|
DisplayMode.List,
|
||||||
|
DisplayMode.Wall,
|
||||||
|
DisplayMode.Tagger,
|
||||||
|
];
|
||||||
|
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
createCriterionOption("path"),
|
||||||
|
RatingCriterionOption,
|
||||||
|
OrganizedCriterionOption,
|
||||||
|
createCriterionOption("o_counter"),
|
||||||
|
ResolutionCriterionOption,
|
||||||
|
createCriterionOption("duration"),
|
||||||
|
HasMarkersCriterionOption,
|
||||||
|
SceneIsMissingCriterionOption,
|
||||||
|
TagsCriterionOption,
|
||||||
|
createCriterionOption("tag_count"),
|
||||||
|
PerformerTagsCriterionOption,
|
||||||
|
PerformersCriterionOption,
|
||||||
|
createCriterionOption("performer_count"),
|
||||||
|
StudiosCriterionOption,
|
||||||
|
MoviesCriterionOption,
|
||||||
|
createCriterionOption("url"),
|
||||||
|
createCriterionOption("stash_id"),
|
||||||
|
InteractiveCriterionOption,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SceneListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
45
ui/v2.5/src/models/list-filter/studios.ts
Normal file
45
ui/v2.5/src/models/list-filter/studios.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { RatingCriterionOption } from "./criteria/rating";
|
||||||
|
import { ParentStudiosCriterionOption } from "./criteria/studios";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "name";
|
||||||
|
const sortByOptions = ["name", "random", "rating"]
|
||||||
|
.map(ListFilterOptions.createSortBy)
|
||||||
|
.concat([
|
||||||
|
{
|
||||||
|
messageID: "gallery_count",
|
||||||
|
value: "galleries_count",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageID: "image_count",
|
||||||
|
value: "images_count",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageID: "scene_count",
|
||||||
|
value: "scenes_count",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const displayModeOptions = [DisplayMode.Grid];
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
ParentStudiosCriterionOption,
|
||||||
|
StudioIsMissingCriterionOption,
|
||||||
|
RatingCriterionOption,
|
||||||
|
createCriterionOption("scene_count"),
|
||||||
|
createCriterionOption("image_count"),
|
||||||
|
createCriterionOption("gallery_count"),
|
||||||
|
createCriterionOption("url"),
|
||||||
|
createCriterionOption("stash_id"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const StudioListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
52
ui/v2.5/src/models/list-filter/tags.ts
Normal file
52
ui/v2.5/src/models/list-filter/tags.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { createCriterionOption } from "./criteria/criterion";
|
||||||
|
import { TagIsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
|
import { NoneCriterionOption } from "./criteria/none";
|
||||||
|
import { ListFilterOptions } from "./filter-options";
|
||||||
|
import { DisplayMode } from "./types";
|
||||||
|
|
||||||
|
const defaultSortBy = "name";
|
||||||
|
// scene markers count has been disabled for now due to performance
|
||||||
|
// issues
|
||||||
|
const sortByOptions = [
|
||||||
|
"name",
|
||||||
|
"random",
|
||||||
|
/* "scene_markers_count" */
|
||||||
|
]
|
||||||
|
.map(ListFilterOptions.createSortBy)
|
||||||
|
.concat([
|
||||||
|
{
|
||||||
|
messageID: "gallery_count",
|
||||||
|
value: "galleries_count",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageID: "image_count",
|
||||||
|
value: "images_count",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageID: "performer_count",
|
||||||
|
value: "performers_count",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageID: "scene_count",
|
||||||
|
value: "scenes_count",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const displayModeOptions = [DisplayMode.Grid, DisplayMode.List];
|
||||||
|
const criterionOptions = [
|
||||||
|
NoneCriterionOption,
|
||||||
|
TagIsMissingCriterionOption,
|
||||||
|
createCriterionOption("scene_count"),
|
||||||
|
createCriterionOption("image_count"),
|
||||||
|
createCriterionOption("gallery_count"),
|
||||||
|
createCriterionOption("performer_count"),
|
||||||
|
// marker count has been disabled for now due to performance issues
|
||||||
|
// ListFilterModel.createCriterionOption("marker_count"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TagListFilterOptions = new ListFilterOptions(
|
||||||
|
defaultSortBy,
|
||||||
|
sortByOptions,
|
||||||
|
displayModeOptions,
|
||||||
|
criterionOptions
|
||||||
|
);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// NOTE: add new enum values to the end, to ensure existing data
|
// NOTE: add new enum values to the end, to ensure existing data
|
||||||
|
|
||||||
// is not impacted
|
// is not impacted
|
||||||
export enum DisplayMode {
|
export enum DisplayMode {
|
||||||
Grid,
|
Grid,
|
||||||
@@ -39,3 +40,55 @@ export interface IOptionType {
|
|||||||
name?: string;
|
name?: string;
|
||||||
image_path?: string;
|
image_path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CriterionType =
|
||||||
|
| "none"
|
||||||
|
| "path"
|
||||||
|
| "rating"
|
||||||
|
| "organized"
|
||||||
|
| "o_counter"
|
||||||
|
| "resolution"
|
||||||
|
| "average_resolution"
|
||||||
|
| "duration"
|
||||||
|
| "favorite"
|
||||||
|
| "hasMarkers"
|
||||||
|
| "sceneIsMissing"
|
||||||
|
| "imageIsMissing"
|
||||||
|
| "performerIsMissing"
|
||||||
|
| "galleryIsMissing"
|
||||||
|
| "tagIsMissing"
|
||||||
|
| "studioIsMissing"
|
||||||
|
| "movieIsMissing"
|
||||||
|
| "tags"
|
||||||
|
| "sceneTags"
|
||||||
|
| "performerTags"
|
||||||
|
| "tag_count"
|
||||||
|
| "performers"
|
||||||
|
| "studios"
|
||||||
|
| "movies"
|
||||||
|
| "galleries"
|
||||||
|
| "birth_year"
|
||||||
|
| "age"
|
||||||
|
| "ethnicity"
|
||||||
|
| "country"
|
||||||
|
| "hair_color"
|
||||||
|
| "eye_color"
|
||||||
|
| "height"
|
||||||
|
| "weight"
|
||||||
|
| "measurements"
|
||||||
|
| "fake_tits"
|
||||||
|
| "career_length"
|
||||||
|
| "tattoos"
|
||||||
|
| "piercings"
|
||||||
|
| "aliases"
|
||||||
|
| "gender"
|
||||||
|
| "parent_studios"
|
||||||
|
| "scene_count"
|
||||||
|
| "marker_count"
|
||||||
|
| "image_count"
|
||||||
|
| "gallery_count"
|
||||||
|
| "performer_count"
|
||||||
|
| "death_year"
|
||||||
|
| "url"
|
||||||
|
| "stash_id"
|
||||||
|
| "interactive";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { RouteComponentProps } from "react-router-dom";
|
||||||
import { ListFilterModel } from "./list-filter/filter";
|
import { ListFilterModel } from "./list-filter/filter";
|
||||||
import { FilterMode } from "./list-filter/types";
|
import { SceneListFilterOptions } from "./list-filter/scenes";
|
||||||
|
|
||||||
interface IQueryParameters {
|
interface IQueryParameters {
|
||||||
qsort?: string;
|
qsort?: string;
|
||||||
@@ -27,10 +27,7 @@ export class SceneQueue {
|
|||||||
public static fromListFilterModel(filter: ListFilterModel) {
|
public static fromListFilterModel(filter: ListFilterModel) {
|
||||||
const ret = new SceneQueue();
|
const ret = new SceneQueue();
|
||||||
|
|
||||||
const filterCopy = Object.assign(
|
const filterCopy = Object.assign(new ListFilterModel(), filter);
|
||||||
new ListFilterModel(filter.filterMode),
|
|
||||||
filter
|
|
||||||
);
|
|
||||||
filterCopy.itemsPerPage = 40;
|
filterCopy.itemsPerPage = 40;
|
||||||
|
|
||||||
ret.originalQueryPage = filter.currentPage;
|
ret.originalQueryPage = filter.currentPage;
|
||||||
@@ -98,8 +95,8 @@ export class SceneQueue {
|
|||||||
|
|
||||||
if (parsed.qfp) {
|
if (parsed.qfp) {
|
||||||
const query = new ListFilterModel(
|
const query = new ListFilterModel(
|
||||||
FilterMode.Scenes,
|
translated as queryString.ParsedQuery,
|
||||||
translated as queryString.ParsedQuery
|
SceneListFilterOptions.defaultSortBy
|
||||||
);
|
);
|
||||||
ret.query = query;
|
ret.query = query;
|
||||||
} else if (parsed.qs) {
|
} else if (parsed.qs) {
|
||||||
|
|||||||
@@ -5,13 +5,21 @@ import {
|
|||||||
StudiosCriterion,
|
StudiosCriterion,
|
||||||
ParentStudiosCriterion,
|
ParentStudiosCriterion,
|
||||||
} from "src/models/list-filter/criteria/studios";
|
} from "src/models/list-filter/criteria/studios";
|
||||||
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
import {
|
||||||
|
TagsCriterion,
|
||||||
|
TagsCriterionOption,
|
||||||
|
} from "src/models/list-filter/criteria/tags";
|
||||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { FilterMode } from "src/models/list-filter/types";
|
|
||||||
import { MoviesCriterion } from "src/models/list-filter/criteria/movies";
|
import { MoviesCriterion } from "src/models/list-filter/criteria/movies";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
import {
|
||||||
|
Criterion,
|
||||||
|
CriterionValue,
|
||||||
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
|
|
||||||
function addExtraCriteria(dest: Criterion[], src?: Criterion[]) {
|
function addExtraCriteria(
|
||||||
|
dest: Criterion<CriterionValue>[],
|
||||||
|
src?: Criterion<CriterionValue>[]
|
||||||
|
) {
|
||||||
if (src && src.length > 0) {
|
if (src && src.length > 0) {
|
||||||
dest.push(...src);
|
dest.push(...src);
|
||||||
}
|
}
|
||||||
@@ -19,10 +27,10 @@ function addExtraCriteria(dest: Criterion[], src?: Criterion[]) {
|
|||||||
|
|
||||||
const makePerformerScenesUrl = (
|
const makePerformerScenesUrl = (
|
||||||
performer: Partial<GQL.PerformerDataFragment>,
|
performer: Partial<GQL.PerformerDataFragment>,
|
||||||
extraCriteria?: Criterion[]
|
extraCriteria?: Criterion<CriterionValue>[]
|
||||||
) => {
|
) => {
|
||||||
if (!performer.id) return "#";
|
if (!performer.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new PerformersCriterion();
|
const criterion = new PerformersCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||||
@@ -34,10 +42,10 @@ const makePerformerScenesUrl = (
|
|||||||
|
|
||||||
const makePerformerImagesUrl = (
|
const makePerformerImagesUrl = (
|
||||||
performer: Partial<GQL.PerformerDataFragment>,
|
performer: Partial<GQL.PerformerDataFragment>,
|
||||||
extraCriteria?: Criterion[]
|
extraCriteria?: Criterion<CriterionValue>[]
|
||||||
) => {
|
) => {
|
||||||
if (!performer.id) return "#";
|
if (!performer.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Images);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new PerformersCriterion();
|
const criterion = new PerformersCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||||
@@ -49,10 +57,10 @@ const makePerformerImagesUrl = (
|
|||||||
|
|
||||||
const makePerformerGalleriesUrl = (
|
const makePerformerGalleriesUrl = (
|
||||||
performer: Partial<GQL.PerformerDataFragment>,
|
performer: Partial<GQL.PerformerDataFragment>,
|
||||||
extraCriteria?: Criterion[]
|
extraCriteria?: Criterion<CriterionValue>[]
|
||||||
) => {
|
) => {
|
||||||
if (!performer.id) return "#";
|
if (!performer.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Galleries);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new PerformersCriterion();
|
const criterion = new PerformersCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||||
@@ -66,7 +74,7 @@ const makePerformersCountryUrl = (
|
|||||||
performer: Partial<GQL.PerformerDataFragment>
|
performer: Partial<GQL.PerformerDataFragment>
|
||||||
) => {
|
) => {
|
||||||
if (!performer.id) return "#";
|
if (!performer.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Performers);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new CountryCriterion();
|
const criterion = new CountryCriterion();
|
||||||
criterion.value = `${performer.country}`;
|
criterion.value = `${performer.country}`;
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
@@ -75,7 +83,7 @@ const makePerformersCountryUrl = (
|
|||||||
|
|
||||||
const makeStudioScenesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
const makeStudioScenesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
if (!studio.id) return "#";
|
if (!studio.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new StudiosCriterion();
|
const criterion = new StudiosCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
@@ -86,7 +94,7 @@ const makeStudioScenesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
|
|
||||||
const makeStudioImagesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
const makeStudioImagesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
if (!studio.id) return "#";
|
if (!studio.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Images);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new StudiosCriterion();
|
const criterion = new StudiosCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
@@ -97,7 +105,7 @@ const makeStudioImagesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
|
|
||||||
const makeStudioGalleriesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
const makeStudioGalleriesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
if (!studio.id) return "#";
|
if (!studio.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Galleries);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new StudiosCriterion();
|
const criterion = new StudiosCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
@@ -108,7 +116,7 @@ const makeStudioGalleriesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
|
|
||||||
const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
if (!studio.id) return "#";
|
if (!studio.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Studios);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new ParentStudiosCriterion();
|
const criterion = new ParentStudiosCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
@@ -119,7 +127,7 @@ const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
|
|
||||||
const makeMovieScenesUrl = (movie: Partial<GQL.MovieDataFragment>) => {
|
const makeMovieScenesUrl = (movie: Partial<GQL.MovieDataFragment>) => {
|
||||||
if (!movie.id) return "#";
|
if (!movie.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new MoviesCriterion();
|
const criterion = new MoviesCriterion();
|
||||||
criterion.value = [
|
criterion.value = [
|
||||||
{ id: movie.id, label: movie.name || `Movie ${movie.id}` },
|
{ id: movie.id, label: movie.name || `Movie ${movie.id}` },
|
||||||
@@ -130,8 +138,8 @@ const makeMovieScenesUrl = (movie: Partial<GQL.MovieDataFragment>) => {
|
|||||||
|
|
||||||
const makeTagScenesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
const makeTagScenesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
||||||
if (!tag.id) return "#";
|
if (!tag.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Scenes);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new TagsCriterion("tags");
|
const criterion = new TagsCriterion(TagsCriterionOption);
|
||||||
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
return `/scenes?${filter.makeQueryParameters()}`;
|
return `/scenes?${filter.makeQueryParameters()}`;
|
||||||
@@ -139,8 +147,8 @@ const makeTagScenesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
|||||||
|
|
||||||
const makeTagPerformersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
const makeTagPerformersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
||||||
if (!tag.id) return "#";
|
if (!tag.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Performers);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new TagsCriterion("tags");
|
const criterion = new TagsCriterion(TagsCriterionOption);
|
||||||
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
return `/performers?${filter.makeQueryParameters()}`;
|
return `/performers?${filter.makeQueryParameters()}`;
|
||||||
@@ -148,8 +156,8 @@ const makeTagPerformersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
|||||||
|
|
||||||
const makeTagSceneMarkersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
const makeTagSceneMarkersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
||||||
if (!tag.id) return "#";
|
if (!tag.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.SceneMarkers);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new TagsCriterion("tags");
|
const criterion = new TagsCriterion(TagsCriterionOption);
|
||||||
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
return `/scenes/markers?${filter.makeQueryParameters()}`;
|
return `/scenes/markers?${filter.makeQueryParameters()}`;
|
||||||
@@ -157,8 +165,8 @@ const makeTagSceneMarkersUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
|||||||
|
|
||||||
const makeTagGalleriesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
const makeTagGalleriesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
||||||
if (!tag.id) return "#";
|
if (!tag.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Galleries);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new TagsCriterion("tags");
|
const criterion = new TagsCriterion(TagsCriterionOption);
|
||||||
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
return `/galleries?${filter.makeQueryParameters()}`;
|
return `/galleries?${filter.makeQueryParameters()}`;
|
||||||
@@ -166,8 +174,8 @@ const makeTagGalleriesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
|||||||
|
|
||||||
const makeTagImagesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
const makeTagImagesUrl = (tag: Partial<GQL.TagDataFragment>) => {
|
||||||
if (!tag.id) return "#";
|
if (!tag.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Images);
|
const filter = new ListFilterModel();
|
||||||
const criterion = new TagsCriterion("tags");
|
const criterion = new TagsCriterion(TagsCriterionOption);
|
||||||
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }];
|
||||||
filter.criteria.push(criterion);
|
filter.criteria.push(criterion);
|
||||||
return `/images?${filter.makeQueryParameters()}`;
|
return `/images?${filter.makeQueryParameters()}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user