mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Filter criterion fixes (#4090)
* Reorder * Remove PhashDuplicateCriterion * Improve DurationInput * Register abloop outside of player init function * Remove none criterion * Typing improvements * Move makeCriteria to ListFilterModel * Separate PathCriterionOption * Add makeCriterion arg to StringCriterionOption * Remove unused options args * Add DurationCriterionOption * Use createNumberCriterionOption * Add StringBooleanCriterion
This commit is contained in:
@@ -30,7 +30,7 @@ export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({
|
||||
// if galleries is already present, then we modify it, otherwise add
|
||||
let galleryCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "galleries";
|
||||
}) as GalleriesCriterion;
|
||||
}) as GalleriesCriterion | undefined;
|
||||
|
||||
if (
|
||||
galleryCriterion &&
|
||||
|
||||
@@ -33,7 +33,7 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
|
||||
// if galleries is already present, then we modify it, otherwise add
|
||||
let galleryCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "galleries";
|
||||
}) as GalleriesCriterion;
|
||||
}) as GalleriesCriterion | undefined;
|
||||
|
||||
if (
|
||||
galleryCriterion &&
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
DateCriterion,
|
||||
TimestampCriterion,
|
||||
BooleanCriterion,
|
||||
PathCriterionOption,
|
||||
} from "src/models/list-filter/criteria/criterion";
|
||||
import { useIntl } from "react-intl";
|
||||
import {
|
||||
@@ -47,6 +46,7 @@ import TagsFilter from "./Filters/TagsFilter";
|
||||
import { PhashCriterion } from "src/models/list-filter/criteria/phash";
|
||||
import { PhashFilter } from "./Filters/PhashFilter";
|
||||
import cx from "classnames";
|
||||
import { PathCriterion } from "src/models/list-filter/criteria/path";
|
||||
|
||||
interface IGenericCriterionEditor {
|
||||
criterion: Criterion<CriterionValue>;
|
||||
@@ -175,7 +175,7 @@ const GenericCriterionEditor: React.FC<IGenericCriterionEditor> = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
if (criterion.criterionOption instanceof PathCriterionOption) {
|
||||
if (criterion instanceof PathCriterion) {
|
||||
return (
|
||||
<PathFilter criterion={criterion} onValueChanged={onValueChanged} />
|
||||
);
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
Criterion,
|
||||
CriterionOption,
|
||||
} from "src/models/list-filter/criteria/criterion";
|
||||
import { makeCriteria } from "src/models/list-filter/criteria/factory";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { ConfigurationContext } from "src/hooks/Config";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
@@ -243,17 +242,11 @@ export const EditFilterDialog: React.FC<IEditFilterProps> = ({
|
||||
}, [currentFilter.mode]);
|
||||
|
||||
const criterionOptions = useMemo(() => {
|
||||
const filteredOptions = filterOptions.criterionOptions.filter((o) => {
|
||||
return o.type !== "none";
|
||||
});
|
||||
|
||||
filteredOptions.sort((a, b) => {
|
||||
return [...filterOptions.criterionOptions].sort((a, b) => {
|
||||
return intl
|
||||
.formatMessage({ id: a.messageID })
|
||||
.localeCompare(intl.formatMessage({ id: b.messageID }));
|
||||
});
|
||||
|
||||
return filteredOptions;
|
||||
}, [intl, filterOptions.criterionOptions]);
|
||||
|
||||
const optionSelected = useCallback(
|
||||
@@ -270,11 +263,11 @@ export const EditFilterDialog: React.FC<IEditFilterProps> = ({
|
||||
if (existing) {
|
||||
setCriterion(existing);
|
||||
} else {
|
||||
const newCriterion = makeCriteria(filter.mode, option.type);
|
||||
const newCriterion = filter.makeCriterion(option.type);
|
||||
setCriterion(newCriterion);
|
||||
}
|
||||
},
|
||||
[filter.mode, criteria]
|
||||
[filter, criteria]
|
||||
);
|
||||
|
||||
const ui = (configuration?.ui ?? {}) as IUIConfig;
|
||||
|
||||
@@ -17,67 +17,50 @@ export const DurationFilter: React.FC<IDurationFilterProps> = ({
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
function onChanged(valueAsNumber: number, property: "value" | "value2") {
|
||||
function onChanged(v: number | undefined, property: "value" | "value2") {
|
||||
const { value } = criterion;
|
||||
value[property] = valueAsNumber;
|
||||
value[property] = v;
|
||||
onValueChanged(value);
|
||||
}
|
||||
|
||||
let equalsControl: JSX.Element | null = null;
|
||||
if (
|
||||
criterion.modifier === CriterionModifier.Equals ||
|
||||
criterion.modifier === CriterionModifier.NotEquals
|
||||
) {
|
||||
equalsControl = (
|
||||
function renderTop() {
|
||||
let placeholder: string;
|
||||
if (
|
||||
criterion.modifier === CriterionModifier.GreaterThan ||
|
||||
criterion.modifier === CriterionModifier.Between ||
|
||||
criterion.modifier === CriterionModifier.NotBetween
|
||||
) {
|
||||
placeholder = intl.formatMessage({ id: "criterion.greater_than" });
|
||||
} else if (criterion.modifier === CriterionModifier.LessThan) {
|
||||
placeholder = intl.formatMessage({ id: "criterion.less_than" });
|
||||
} else {
|
||||
placeholder = intl.formatMessage({ id: "criterion.value" });
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Group>
|
||||
<DurationInput
|
||||
numericValue={criterion.value?.value}
|
||||
onValueChange={(v: number) => onChanged(v, "value")}
|
||||
placeholder={intl.formatMessage({ id: "criterion.value" })}
|
||||
value={criterion.value?.value}
|
||||
setValue={(v) => onChanged(v, "value")}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
|
||||
let lowerControl: JSX.Element | null = null;
|
||||
if (
|
||||
criterion.modifier === CriterionModifier.GreaterThan ||
|
||||
criterion.modifier === CriterionModifier.Between ||
|
||||
criterion.modifier === CriterionModifier.NotBetween
|
||||
) {
|
||||
lowerControl = (
|
||||
<Form.Group>
|
||||
<DurationInput
|
||||
numericValue={criterion.value?.value}
|
||||
onValueChange={(v: number) => onChanged(v, "value")}
|
||||
placeholder={intl.formatMessage({ id: "criterion.greater_than" })}
|
||||
/>
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
function renderBottom() {
|
||||
if (
|
||||
criterion.modifier !== CriterionModifier.Between &&
|
||||
criterion.modifier !== CriterionModifier.NotBetween
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let upperControl: JSX.Element | null = null;
|
||||
if (
|
||||
criterion.modifier === CriterionModifier.LessThan ||
|
||||
criterion.modifier === CriterionModifier.Between ||
|
||||
criterion.modifier === CriterionModifier.NotBetween
|
||||
) {
|
||||
upperControl = (
|
||||
return (
|
||||
<Form.Group>
|
||||
<DurationInput
|
||||
numericValue={
|
||||
criterion.modifier === CriterionModifier.LessThan
|
||||
? criterion.value?.value
|
||||
: criterion.value?.value2
|
||||
}
|
||||
onValueChange={(v: number) =>
|
||||
onChanged(
|
||||
v,
|
||||
criterion.modifier === CriterionModifier.LessThan
|
||||
? "value"
|
||||
: "value2"
|
||||
)
|
||||
}
|
||||
value={criterion.value?.value2}
|
||||
setValue={(v) => onChanged(v, "value2")}
|
||||
placeholder={intl.formatMessage({ id: "criterion.less_than" })}
|
||||
/>
|
||||
</Form.Group>
|
||||
@@ -86,9 +69,8 @@ export const DurationFilter: React.FC<IDurationFilterProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{equalsControl}
|
||||
{lowerControl}
|
||||
{upperControl}
|
||||
{renderTop()}
|
||||
{renderBottom()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -135,10 +135,10 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||
}
|
||||
|
||||
if (state.duration) {
|
||||
formik.setFieldValue(
|
||||
"duration",
|
||||
DurationUtils.stringToSeconds(state.duration)
|
||||
);
|
||||
const seconds = DurationUtils.stringToSeconds(state.duration);
|
||||
if (seconds !== undefined) {
|
||||
formik.setFieldValue("duration", seconds);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.date) {
|
||||
@@ -402,10 +402,8 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||
</Form.Label>
|
||||
<Col xs={fieldXS} xl={fieldXL}>
|
||||
<DurationInput
|
||||
numericValue={formik.values.duration ?? undefined}
|
||||
onValueChange={(valueAsNumber) => {
|
||||
formik.setFieldValue("duration", valueAsNumber ?? null);
|
||||
}}
|
||||
value={formik.values.duration ?? undefined}
|
||||
setValue={(v) => formik.setFieldValue("duration", v ?? null)}
|
||||
/>
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
@@ -18,7 +18,7 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({
|
||||
// if movie is already present, then we modify it, otherwise add
|
||||
let movieCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "movies";
|
||||
}) as MoviesCriterion;
|
||||
}) as MoviesCriterion | undefined;
|
||||
|
||||
if (
|
||||
movieCriterion &&
|
||||
|
||||
@@ -8,7 +8,6 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import videojs, { VideoJsPlayer, VideoJsPlayerOptions } from "video.js";
|
||||
import abLoopPlugin from "videojs-abloop";
|
||||
import useScript from "src/hooks/useScript";
|
||||
import "videojs-contrib-dash";
|
||||
import "videojs-mobile-ui";
|
||||
@@ -24,12 +23,6 @@ import "./big-buttons";
|
||||
import "./track-activity";
|
||||
import "./vrmode";
|
||||
import cx from "classnames";
|
||||
// @ts-ignore
|
||||
import airplay from "@silvermine/videojs-airplay";
|
||||
// @ts-ignore
|
||||
import chromecast from "@silvermine/videojs-chromecast";
|
||||
airplay(videojs);
|
||||
chromecast(videojs);
|
||||
import {
|
||||
useSceneSaveActivity,
|
||||
useSceneIncrementPlayCount,
|
||||
@@ -47,6 +40,17 @@ import { languageMap } from "src/utils/caption";
|
||||
import { VIDEO_PLAYER_ID } from "./util";
|
||||
import { IUIConfig } from "src/core/config";
|
||||
|
||||
// @ts-ignore
|
||||
import airplay from "@silvermine/videojs-airplay";
|
||||
// @ts-ignore
|
||||
import chromecast from "@silvermine/videojs-chromecast";
|
||||
import abLoopPlugin from "videojs-abloop";
|
||||
|
||||
// register videojs plugins
|
||||
airplay(videojs);
|
||||
chromecast(videojs);
|
||||
abLoopPlugin(window, videojs);
|
||||
|
||||
function handleHotkeys(player: VideoJsPlayer, event: videojs.KeyboardEvent) {
|
||||
function seekStep(step: number) {
|
||||
const time = player.currentTime() + step;
|
||||
@@ -378,8 +382,6 @@ export const ScenePlayer: React.FC<IScenePlayerProps> = ({
|
||||
videoEl.classList.add("vjs-big-play-centered");
|
||||
videoRef.current!.appendChild(videoEl);
|
||||
|
||||
abLoopPlugin(window, videojs);
|
||||
|
||||
const vjs = videojs(videoEl, options);
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
|
||||
@@ -145,15 +145,14 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
||||
</Form.Label>
|
||||
<div className="col-sm-8 col-xl-12">
|
||||
<DurationInput
|
||||
onValueChange={(s) => formik.setFieldValue("seconds", s)}
|
||||
value={formik.values.seconds ?? 0}
|
||||
setValue={(v) => formik.setFieldValue("seconds", v ?? null)}
|
||||
onReset={() =>
|
||||
formik.setFieldValue(
|
||||
"seconds",
|
||||
Math.round(getPlayerPosition() ?? 0)
|
||||
)
|
||||
}
|
||||
numericValue={formik.values.seconds}
|
||||
mandatory
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -356,8 +356,8 @@ export const SettingsInterfacePanel: React.FC = () => {
|
||||
onChange={(v) => saveInterface({ maximumLoopDuration: v })}
|
||||
renderField={(value, setValue) => (
|
||||
<DurationInput
|
||||
numericValue={value}
|
||||
onValueChange={(duration) => setValue(duration ?? 0)}
|
||||
value={value}
|
||||
setValue={(duration) => setValue(duration ?? 0)}
|
||||
/>
|
||||
)}
|
||||
renderValue={(v) => {
|
||||
|
||||
@@ -43,17 +43,13 @@ export const SettingsServicesPanel: React.FC = () => {
|
||||
} = React.useContext(SettingStateContext);
|
||||
|
||||
// undefined to hide dialog, true for enable, false for disable
|
||||
const [enableDisable, setEnableDisable] = useState<boolean | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [enableDisable, setEnableDisable] = useState<boolean>();
|
||||
|
||||
const [enableUntilRestart, setEnableUntilRestart] = useState<boolean>(false);
|
||||
const [enableDuration, setEnableDuration] = useState<number | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [enableDuration, setEnableDuration] = useState<number>(0);
|
||||
|
||||
const [ipEntry, setIPEntry] = useState<string>("");
|
||||
const [tempIP, setTempIP] = useState<string | undefined>();
|
||||
const [tempIP, setTempIP] = useState<string>();
|
||||
|
||||
const { data: statusData, loading, refetch: statusRefetch } = useDLNAStatus();
|
||||
|
||||
@@ -273,8 +269,8 @@ export const SettingsServicesPanel: React.FC = () => {
|
||||
|
||||
<Form.Group id="temp-enable-duration">
|
||||
<DurationInput
|
||||
numericValue={enableDuration ?? 0}
|
||||
onValueChange={(v) => setEnableDuration(v ?? 0)}
|
||||
value={enableDuration}
|
||||
setValue={(v) => setEnableDuration(v ?? 0)}
|
||||
disabled={enableUntilRestart}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
@@ -315,8 +311,8 @@ export const SettingsServicesPanel: React.FC = () => {
|
||||
|
||||
<Form.Group id="temp-enable-duration">
|
||||
<DurationInput
|
||||
numericValue={enableDuration ?? 0}
|
||||
onValueChange={(v) => setEnableDuration(v ?? 0)}
|
||||
value={enableDuration}
|
||||
setValue={(v) => setEnableDuration(v ?? 0)}
|
||||
disabled={enableUntilRestart}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
|
||||
@@ -3,67 +3,58 @@ import {
|
||||
faChevronUp,
|
||||
faClock,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Button, ButtonGroup, InputGroup, Form } from "react-bootstrap";
|
||||
import { Icon } from "./Icon";
|
||||
import DurationUtils from "src/utils/duration";
|
||||
|
||||
interface IProps {
|
||||
disabled?: boolean;
|
||||
numericValue: number | undefined;
|
||||
mandatory?: boolean;
|
||||
onValueChange(
|
||||
valueAsNumber: number | undefined,
|
||||
valueAsString?: string
|
||||
): void;
|
||||
value: number | undefined;
|
||||
setValue(value: number | undefined): void;
|
||||
onReset?(): void;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
||||
const [value, setValue] = useState<string | undefined>(
|
||||
props.numericValue !== undefined
|
||||
? DurationUtils.secondsToString(props.numericValue)
|
||||
: undefined
|
||||
);
|
||||
export const DurationInput: React.FC<IProps> = ({
|
||||
disabled,
|
||||
value,
|
||||
setValue,
|
||||
onReset,
|
||||
className,
|
||||
placeholder,
|
||||
}) => {
|
||||
const [tmpValue, setTmpValue] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (props.numericValue !== undefined || props.mandatory) {
|
||||
setValue(DurationUtils.secondsToString(props.numericValue ?? 0));
|
||||
} else {
|
||||
setValue(undefined);
|
||||
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
setTmpValue(e.currentTarget.value);
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
if (tmpValue !== undefined) {
|
||||
setValue(DurationUtils.stringToSeconds(tmpValue));
|
||||
setTmpValue(undefined);
|
||||
}
|
||||
}, [props.numericValue, props.mandatory]);
|
||||
}
|
||||
|
||||
function increment() {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let seconds = DurationUtils.stringToSeconds(value);
|
||||
seconds += 1;
|
||||
props.onValueChange(seconds, DurationUtils.secondsToString(seconds));
|
||||
setTmpValue(undefined);
|
||||
setValue((value ?? 0) + 1);
|
||||
}
|
||||
|
||||
function decrement() {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let seconds = DurationUtils.stringToSeconds(value);
|
||||
seconds -= 1;
|
||||
props.onValueChange(seconds, DurationUtils.secondsToString(seconds));
|
||||
setTmpValue(undefined);
|
||||
setValue((value ?? 0) - 1);
|
||||
}
|
||||
|
||||
function renderButtons() {
|
||||
if (!props.disabled) {
|
||||
if (!disabled) {
|
||||
return (
|
||||
<ButtonGroup vertical>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="duration-button"
|
||||
disabled={props.disabled}
|
||||
onClick={() => increment()}
|
||||
>
|
||||
<Icon icon={faChevronUp} />
|
||||
@@ -71,7 +62,6 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="duration-button"
|
||||
disabled={props.disabled}
|
||||
onClick={() => decrement()}
|
||||
>
|
||||
<Icon icon={faChevronDown} />
|
||||
@@ -81,46 +71,33 @@ export const DurationInput: React.FC<IProps> = (props: IProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
if (props.onReset) {
|
||||
props.onReset();
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRenderReset() {
|
||||
if (props.onReset) {
|
||||
if (onReset) {
|
||||
return (
|
||||
<Button variant="secondary" onClick={onReset}>
|
||||
<Button variant="secondary" onClick={() => onReset()}>
|
||||
<Icon icon={faClock} />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let inputValue = "";
|
||||
if (tmpValue !== undefined) {
|
||||
inputValue = tmpValue;
|
||||
} else if (value !== undefined) {
|
||||
inputValue = DurationUtils.secondsToString(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`duration-input ${props.className}`}>
|
||||
<div className={`duration-input ${className}`}>
|
||||
<InputGroup>
|
||||
<Form.Control
|
||||
className="duration-control text-input"
|
||||
disabled={props.disabled}
|
||||
value={value}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(e.currentTarget.value)
|
||||
}
|
||||
onBlur={() => {
|
||||
if (props.mandatory || (value !== undefined && value !== "")) {
|
||||
props.onValueChange(DurationUtils.stringToSeconds(value), value);
|
||||
} else {
|
||||
props.onValueChange(undefined);
|
||||
}
|
||||
}}
|
||||
placeholder={
|
||||
!props.disabled
|
||||
? props.placeholder
|
||||
? `${props.placeholder} (hh:mm:ss)`
|
||||
: "hh:mm:ss"
|
||||
: undefined
|
||||
}
|
||||
disabled={disabled}
|
||||
value={inputValue}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
placeholder={placeholder ? `${placeholder} (hh:mm:ss)` : "hh:mm:ss"}
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
{maybeRenderReset()}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const StudioChildrenPanel: React.FC<IStudioChildrenPanel> = ({
|
||||
// if studio is already present, then we modify it, otherwise add
|
||||
let parentStudioCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "parents";
|
||||
}) as ParentStudiosCriterion;
|
||||
}) as ParentStudiosCriterion | undefined;
|
||||
|
||||
if (
|
||||
parentStudioCriterion &&
|
||||
|
||||
@@ -21,7 +21,7 @@ export const TagMarkersPanel: React.FC<ITagMarkersPanel> = ({
|
||||
// if tag is already present, then we modify it, otherwise add
|
||||
let tagCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "tags";
|
||||
}) as TagsCriterion;
|
||||
}) as TagsCriterion | undefined;
|
||||
|
||||
if (
|
||||
tagCriterion &&
|
||||
|
||||
@@ -16,7 +16,7 @@ export const usePerformerFilterHook = (
|
||||
// if performers is already present, then we modify it, otherwise add
|
||||
let performerCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "performers";
|
||||
}) as PerformersCriterion;
|
||||
}) as PerformersCriterion | undefined;
|
||||
|
||||
if (performerCriterion) {
|
||||
if (
|
||||
|
||||
@@ -12,7 +12,7 @@ export const useStudioFilterHook = (studio: GQL.StudioDataFragment) => {
|
||||
// if studio is already present, then we modify it, otherwise add
|
||||
let studioCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "studios";
|
||||
}) as StudiosCriterion;
|
||||
}) as StudiosCriterion | undefined;
|
||||
|
||||
if (studioCriterion) {
|
||||
// we should be showing studio only. Remove other values
|
||||
|
||||
@@ -17,7 +17,7 @@ export const useTagFilterHook = (tag: GQL.TagDataFragment) => {
|
||||
// if tag is already present, then we modify it, otherwise add
|
||||
let tagCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "tags";
|
||||
}) as TagsCriterion;
|
||||
}) as TagsCriterion | undefined;
|
||||
|
||||
if (tagCriterion) {
|
||||
if (
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import { CriterionModifier } from "src/core/generated-graphql";
|
||||
import { languageMap, valueToCode } from "src/utils/caption";
|
||||
import { CriterionType } from "../types";
|
||||
import { CriterionOption, StringCriterion } from "./criterion";
|
||||
|
||||
const languageStrings = Array.from(languageMap.values());
|
||||
|
||||
class CaptionsCriterionOptionType extends CriterionOption {
|
||||
constructor(value: CriterionType) {
|
||||
super({
|
||||
messageID: value,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Includes,
|
||||
options: languageStrings,
|
||||
makeCriterion: () => new CaptionCriterion(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const CaptionsCriterionOption = new CaptionsCriterionOptionType(
|
||||
"captions"
|
||||
);
|
||||
export const CaptionsCriterionOption = new CriterionOption({
|
||||
messageID: "captions",
|
||||
type: "captions",
|
||||
modifierOptions: [
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Includes,
|
||||
options: languageStrings,
|
||||
makeCriterion: () => new CaptionCriterion(),
|
||||
});
|
||||
|
||||
export class CaptionCriterion extends StringCriterion {
|
||||
constructor() {
|
||||
super(CaptionsCriterionOption);
|
||||
}
|
||||
|
||||
protected toCriterionInput() {
|
||||
const value = valueToCode(this.value) ?? "";
|
||||
|
||||
@@ -36,8 +31,4 @@ export class CaptionCriterion extends StringCriterion {
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(CaptionsCriterionOption);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import { CriterionOption, MultiStringCriterion } from "./criterion";
|
||||
export const CircumcisedCriterionOption = new CriterionOption({
|
||||
messageID: "circumcised",
|
||||
type: "circumcised",
|
||||
options: circumcisedStrings,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
options: circumcisedStrings,
|
||||
makeCriterion: () => new CircumcisedCriterion(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import { IntlShape } from "react-intl";
|
||||
import { CriterionModifier } from "src/core/generated-graphql";
|
||||
import { getCountryByISO } from "src/utils/country";
|
||||
import {
|
||||
CriterionOption,
|
||||
StringCriterion,
|
||||
StringCriterionOption,
|
||||
} from "./criterion";
|
||||
import { StringCriterion, StringCriterionOption } from "./criterion";
|
||||
|
||||
export const CountryCriterionOption = new CriterionOption({
|
||||
messageID: "country",
|
||||
type: "country",
|
||||
modifierOptions: StringCriterionOption.modifierOptions,
|
||||
defaultModifier: StringCriterionOption.defaultModifier,
|
||||
makeCriterion: () => new CountryCriterion(),
|
||||
inputType: StringCriterionOption.inputType,
|
||||
});
|
||||
export const CountryCriterionOption = new StringCriterionOption(
|
||||
"country",
|
||||
"country",
|
||||
() => new CountryCriterion()
|
||||
);
|
||||
|
||||
export class CountryCriterion extends StringCriterion {
|
||||
constructor() {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/* eslint-disable consistent-return */
|
||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
||||
|
||||
import { IntlShape } from "react-intl";
|
||||
import {
|
||||
CriterionModifier,
|
||||
HierarchicalMultiCriterionInput,
|
||||
IntCriterionInput,
|
||||
MultiCriterionInput,
|
||||
PHashDuplicationCriterionInput,
|
||||
DateCriterionInput,
|
||||
TimestampCriterionInput,
|
||||
ConfigDataFragment,
|
||||
@@ -152,22 +149,18 @@ export abstract class Criterion<V extends CriterionValue> {
|
||||
this.modifier = encodedCriterion.modifier;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public apply(outputFilter: Record<string, any>) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
public apply(outputFilter: Record<string, unknown>) {
|
||||
outputFilter[this.criterionOption.type] = this.toCriterionInput();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected toCriterionInput(): any {
|
||||
protected toCriterionInput(): unknown {
|
||||
return {
|
||||
value: this.value,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toSavedFilter(outputFilter: Record<string, any>) {
|
||||
public toSavedFilter(outputFilter: Record<string, unknown>) {
|
||||
outputFilter[this.criterionOption.type] = {
|
||||
value: this.value,
|
||||
modifier: this.modifier,
|
||||
@@ -226,274 +219,13 @@ export class CriterionOption {
|
||||
}
|
||||
}
|
||||
|
||||
export class StringCriterionOption extends CriterionOption {
|
||||
public static readonly modifierOptions = [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
CriterionModifier.MatchesRegex,
|
||||
CriterionModifier.NotMatchesRegex,
|
||||
];
|
||||
|
||||
public static readonly defaultModifier = CriterionModifier.Equals;
|
||||
public static readonly inputType = "text";
|
||||
|
||||
constructor(messageID: string, type: CriterionType, options?: Option[]) {
|
||||
super({
|
||||
messageID,
|
||||
type,
|
||||
modifierOptions: StringCriterionOption.modifierOptions,
|
||||
defaultModifier: StringCriterionOption.defaultModifier,
|
||||
options,
|
||||
inputType: StringCriterionOption.inputType,
|
||||
makeCriterion: () => new StringCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createStringCriterionOption(
|
||||
type: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new StringCriterionOption(messageID ?? type, type);
|
||||
}
|
||||
|
||||
export class StringCriterion extends Criterion<string> {
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, "");
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
return (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull ||
|
||||
this.value.length > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class MultiStringCriterion extends Criterion<string[]> {
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, []);
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
return this.value.join(", ");
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
return (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull ||
|
||||
this.value.length > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class MandatoryStringCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType, options?: Option[]) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.MatchesRegex,
|
||||
CriterionModifier.NotMatchesRegex,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
options,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new StringCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createMandatoryStringCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new MandatoryStringCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class PathCriterionOption extends StringCriterionOption {}
|
||||
|
||||
export function createPathCriterionOption(
|
||||
type: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new PathCriterionOption(messageID ?? type, type);
|
||||
}
|
||||
|
||||
export class BooleanCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
options: [true.toString(), false.toString()],
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new BooleanCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class BooleanCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): boolean {
|
||||
return this.value === "true";
|
||||
}
|
||||
|
||||
public isValid() {
|
||||
return this.value === "true" || this.value === "false";
|
||||
}
|
||||
}
|
||||
|
||||
export function createBooleanCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new BooleanCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class NumberCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType, options?: Option[]) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
options,
|
||||
inputType: "number",
|
||||
makeCriterion: () => new NumberCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class NullNumberCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "number",
|
||||
makeCriterion: () => new NumberCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createNumberCriterionOption(value: CriterionType) {
|
||||
return new NumberCriterionOption(value, value);
|
||||
}
|
||||
|
||||
export function createNullNumberCriterionOption(value: CriterionType) {
|
||||
return new NullNumberCriterionOption(value, value);
|
||||
}
|
||||
|
||||
export class NumberCriterion extends Criterion<INumberValue> {
|
||||
public get value(): INumberValue {
|
||||
return this._value;
|
||||
}
|
||||
public set value(newValue: number | INumberValue) {
|
||||
// backwards compatibility - if this.value is a number, use that
|
||||
if (typeof newValue !== "object") {
|
||||
this._value = {
|
||||
value: newValue,
|
||||
value2: undefined,
|
||||
};
|
||||
} else {
|
||||
this._value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
protected toCriterionInput(): IntCriterionInput {
|
||||
return {
|
||||
modifier: this.modifier,
|
||||
value: this.value?.value ?? 0,
|
||||
value2: this.value?.value2,
|
||||
};
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
const { value, value2 } = this.value;
|
||||
if (
|
||||
this.modifier === CriterionModifier.Between ||
|
||||
this.modifier === CriterionModifier.NotBetween
|
||||
) {
|
||||
return `${value}, ${value2 ?? 0}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
if (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { value, value2 } = this.value;
|
||||
if (value === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
value2 === undefined &&
|
||||
(this.modifier === CriterionModifier.Between ||
|
||||
this.modifier === CriterionModifier.NotBetween)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, { value: undefined, value2: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
export class ILabeledIdCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
includeAll: boolean,
|
||||
inputType: InputType
|
||||
inputType: InputType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
const modifierOptions = [
|
||||
CriterionModifier.Includes,
|
||||
@@ -513,8 +245,10 @@ export class ILabeledIdCriterionOption extends CriterionOption {
|
||||
type: value,
|
||||
modifierOptions,
|
||||
defaultModifier,
|
||||
makeCriterion: () => new ILabeledIdCriterion(this),
|
||||
inputType,
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new ILabeledIdCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -689,8 +423,231 @@ export class IHierarchicalLabeledIdCriterion extends Criterion<IHierarchicalLabe
|
||||
}
|
||||
}
|
||||
|
||||
export class MandatoryNumberCriterionOption extends CriterionOption {
|
||||
export class StringCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
CriterionModifier.MatchesRegex,
|
||||
CriterionModifier.NotMatchesRegex,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "text",
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new StringCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createStringCriterionOption(
|
||||
type: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new StringCriterionOption(messageID ?? type, type);
|
||||
}
|
||||
|
||||
export class MandatoryStringCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.Includes,
|
||||
CriterionModifier.Excludes,
|
||||
CriterionModifier.MatchesRegex,
|
||||
CriterionModifier.NotMatchesRegex,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new StringCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createMandatoryStringCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new MandatoryStringCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class StringCriterion extends Criterion<string> {
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, "");
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
return (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull ||
|
||||
this.value.length > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class MultiStringCriterion extends Criterion<string[]> {
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, []);
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
return this.value.join(", ");
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
return (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull ||
|
||||
this.value.length > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class BooleanCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
options: ["true", "false"],
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new BooleanCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createBooleanCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new BooleanCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class BooleanCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): boolean {
|
||||
return this.value === "true";
|
||||
}
|
||||
|
||||
public isValid() {
|
||||
return this.value === "true" || this.value === "false";
|
||||
}
|
||||
}
|
||||
|
||||
export class StringBooleanCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
options: ["true", "false"],
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new StringBooleanCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class StringBooleanCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public isValid() {
|
||||
return this.value === "true" || this.value === "false";
|
||||
}
|
||||
}
|
||||
|
||||
export class NumberCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "number",
|
||||
makeCriterion: () => new NumberCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createNumberCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new NumberCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class NullNumberCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.Equals,
|
||||
CriterionModifier.NotEquals,
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
CriterionModifier.IsNull,
|
||||
CriterionModifier.NotNull,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "number",
|
||||
makeCriterion: () => new NumberCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createNullNumberCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new NullNumberCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class MandatoryNumberCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
value: CriterionType,
|
||||
makeCriterion?: () => Criterion<CriterionValue>
|
||||
) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
@@ -704,7 +661,9 @@ export class MandatoryNumberCriterionOption extends CriterionOption {
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
inputType: "number",
|
||||
makeCriterion: () => new NumberCriterion(this),
|
||||
makeCriterion: makeCriterion
|
||||
? makeCriterion
|
||||
: () => new NumberCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -716,6 +675,84 @@ export function createMandatoryNumberCriterionOption(
|
||||
return new MandatoryNumberCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class NumberCriterion extends Criterion<INumberValue> {
|
||||
public get value(): INumberValue {
|
||||
return this._value;
|
||||
}
|
||||
public set value(newValue: number | INumberValue) {
|
||||
// backwards compatibility - if this.value is a number, use that
|
||||
if (typeof newValue !== "object") {
|
||||
this._value = {
|
||||
value: newValue,
|
||||
value2: undefined,
|
||||
};
|
||||
} else {
|
||||
this._value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
protected toCriterionInput(): IntCriterionInput {
|
||||
return {
|
||||
modifier: this.modifier,
|
||||
value: this.value?.value ?? 0,
|
||||
value2: this.value?.value2,
|
||||
};
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
const { value, value2 } = this.value;
|
||||
if (
|
||||
this.modifier === CriterionModifier.Between ||
|
||||
this.modifier === CriterionModifier.NotBetween
|
||||
) {
|
||||
return `${value}, ${value2 ?? 0}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
if (
|
||||
this.modifier === CriterionModifier.IsNull ||
|
||||
this.modifier === CriterionModifier.NotNull
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { value, value2 } = this.value;
|
||||
if (value === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
value2 === undefined &&
|
||||
(this.modifier === CriterionModifier.Between ||
|
||||
this.modifier === CriterionModifier.NotBetween)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, { value: undefined, value2: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
export class DurationCriterionOption extends MandatoryNumberCriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super(messageID, value, () => new DurationCriterion(this));
|
||||
}
|
||||
}
|
||||
|
||||
export function createDurationCriterionOption(
|
||||
value: CriterionType,
|
||||
messageID?: string
|
||||
) {
|
||||
return new DurationCriterionOption(messageID ?? value, value);
|
||||
}
|
||||
|
||||
export class DurationCriterion extends Criterion<INumberValue> {
|
||||
constructor(type: CriterionOption) {
|
||||
super(type, { value: undefined, value2: undefined });
|
||||
@@ -730,17 +767,16 @@ export class DurationCriterion extends Criterion<INumberValue> {
|
||||
}
|
||||
|
||||
protected getLabelValue(_intl: IntlShape) {
|
||||
return this.modifier === CriterionModifier.Between ||
|
||||
const value = DurationUtils.secondsToString(this.value.value ?? 0);
|
||||
const value2 = DurationUtils.secondsToString(this.value.value2 ?? 0);
|
||||
if (
|
||||
this.modifier === CriterionModifier.Between ||
|
||||
this.modifier === CriterionModifier.NotBetween
|
||||
? `${DurationUtils.secondsToString(
|
||||
this.value.value ?? 0
|
||||
)} ${DurationUtils.secondsToString(this.value.value2 ?? 0)}`
|
||||
: this.modifier === CriterionModifier.GreaterThan ||
|
||||
this.modifier === CriterionModifier.LessThan ||
|
||||
this.modifier === CriterionModifier.Equals ||
|
||||
this.modifier === CriterionModifier.NotEquals
|
||||
? DurationUtils.secondsToString(this.value.value ?? 0)
|
||||
: "?";
|
||||
) {
|
||||
return `${value}, ${value2}`;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public isValid(): boolean {
|
||||
@@ -768,16 +804,8 @@ export class DurationCriterion extends Criterion<INumberValue> {
|
||||
}
|
||||
}
|
||||
|
||||
export class PhashDuplicateCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): PHashDuplicationCriterionInput {
|
||||
return {
|
||||
duplicated: this.value === "true",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class DateCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType, options?: Option[]) {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
@@ -792,7 +820,6 @@ export class DateCriterionOption extends CriterionOption {
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
options,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new DateCriterion(this),
|
||||
});
|
||||
@@ -857,7 +884,7 @@ export class DateCriterion extends Criterion<IDateValue> {
|
||||
}
|
||||
|
||||
export class TimestampCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType, options?: Option[]) {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
@@ -870,7 +897,6 @@ export class TimestampCriterionOption extends CriterionOption {
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.GreaterThan,
|
||||
options,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new TimestampCriterion(this),
|
||||
});
|
||||
@@ -881,6 +907,28 @@ export function createTimestampCriterionOption(value: CriterionType) {
|
||||
return new TimestampCriterionOption(value, value);
|
||||
}
|
||||
|
||||
export class MandatoryTimestampCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.GreaterThan,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new TimestampCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createMandatoryTimestampCriterionOption(value: CriterionType) {
|
||||
return new MandatoryTimestampCriterionOption(value, value);
|
||||
}
|
||||
|
||||
export class TimestampCriterion extends Criterion<ITimestampValue> {
|
||||
public encodeValue() {
|
||||
return {
|
||||
@@ -944,26 +992,3 @@ export class TimestampCriterion extends Criterion<ITimestampValue> {
|
||||
super(type, { value: "", value2: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
export class MandatoryTimestampCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, value: CriterionType, options?: Option[]) {
|
||||
super({
|
||||
messageID,
|
||||
type: value,
|
||||
modifierOptions: [
|
||||
CriterionModifier.GreaterThan,
|
||||
CriterionModifier.LessThan,
|
||||
CriterionModifier.Between,
|
||||
CriterionModifier.NotBetween,
|
||||
],
|
||||
defaultModifier: CriterionModifier.GreaterThan,
|
||||
options,
|
||||
inputType: "text",
|
||||
makeCriterion: () => new TimestampCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createMandatoryTimestampCriterionOption(value: CriterionType) {
|
||||
return new MandatoryTimestampCriterionOption(value, value);
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { SceneListFilterOptions } from "../scenes";
|
||||
import { MovieListFilterOptions } from "../movies";
|
||||
import { GalleryListFilterOptions } from "../galleries";
|
||||
import { PerformerListFilterOptions } from "../performers";
|
||||
import { ImageListFilterOptions } from "../images";
|
||||
import { SceneMarkerListFilterOptions } from "../scene-markers";
|
||||
import { StudioListFilterOptions } from "../studios";
|
||||
import { TagListFilterOptions } from "../tags";
|
||||
import { CriterionType } from "../types";
|
||||
|
||||
const filterModeOptions = {
|
||||
[GQL.FilterMode.Galleries]: GalleryListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Images]: ImageListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Movies]: MovieListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Performers]: PerformerListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.SceneMarkers]: SceneMarkerListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Scenes]: SceneListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Studios]: StudioListFilterOptions.criterionOptions,
|
||||
[GQL.FilterMode.Tags]: TagListFilterOptions.criterionOptions,
|
||||
};
|
||||
|
||||
export function makeCriteria(
|
||||
mode: GQL.FilterMode,
|
||||
type: CriterionType,
|
||||
config?: GQL.ConfigDataFragment
|
||||
) {
|
||||
const criterionOptions = filterModeOptions[mode];
|
||||
|
||||
const option = criterionOptions.find((o) => o.type === type);
|
||||
|
||||
if (!option) {
|
||||
throw new Error(`Unknown criterion parameter name: ${type}`);
|
||||
}
|
||||
|
||||
return option?.makeCriterion(config);
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import { BooleanCriterion, BooleanCriterionOption } from "./criterion";
|
||||
|
||||
export const FavoriteCriterionOption = new BooleanCriterionOption(
|
||||
"favourite",
|
||||
"filter_favorites"
|
||||
"filter_favorites",
|
||||
() => new FavoriteCriterion()
|
||||
);
|
||||
|
||||
export class FavoriteCriterion extends BooleanCriterion {
|
||||
@@ -13,7 +14,8 @@ export class FavoriteCriterion extends BooleanCriterion {
|
||||
|
||||
export const PerformerFavoriteCriterionOption = new BooleanCriterionOption(
|
||||
"performer_favorite",
|
||||
"performer_favorite"
|
||||
"performer_favorite",
|
||||
() => new PerformerFavoriteCriterion()
|
||||
);
|
||||
|
||||
export class PerformerFavoriteCriterion extends BooleanCriterion {
|
||||
|
||||
@@ -2,15 +2,16 @@ import { ILabeledIdCriterion, ILabeledIdCriterionOption } from "./criterion";
|
||||
|
||||
const inputType = "galleries";
|
||||
|
||||
const galleriesCriterionOption = new ILabeledIdCriterionOption(
|
||||
export const GalleriesCriterionOption = new ILabeledIdCriterionOption(
|
||||
"galleries",
|
||||
"galleries",
|
||||
true,
|
||||
inputType
|
||||
inputType,
|
||||
() => new GalleriesCriterion()
|
||||
);
|
||||
|
||||
export class GalleriesCriterion extends ILabeledIdCriterion {
|
||||
constructor() {
|
||||
super(galleriesCriterionOption);
|
||||
super(GalleriesCriterionOption);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { CriterionOption, StringCriterion } from "./criterion";
|
||||
import {
|
||||
StringBooleanCriterion,
|
||||
StringBooleanCriterionOption,
|
||||
} from "./criterion";
|
||||
|
||||
export const HasChaptersCriterionOption = new CriterionOption({
|
||||
messageID: "hasChapters",
|
||||
type: "has_chapters",
|
||||
options: [true.toString(), false.toString()],
|
||||
makeCriterion: () => new HasChaptersCriterion(),
|
||||
});
|
||||
export const HasChaptersCriterionOption = new StringBooleanCriterionOption(
|
||||
"hasChapters",
|
||||
"has_chapters",
|
||||
() => new HasChaptersCriterion()
|
||||
);
|
||||
|
||||
export class HasChaptersCriterion extends StringCriterion {
|
||||
export class HasChaptersCriterion extends StringBooleanCriterion {
|
||||
constructor() {
|
||||
super(HasChaptersCriterionOption);
|
||||
}
|
||||
|
||||
protected toCriterionInput(): string {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { CriterionOption, StringCriterion } from "./criterion";
|
||||
import {
|
||||
StringBooleanCriterion,
|
||||
StringBooleanCriterionOption,
|
||||
} from "./criterion";
|
||||
|
||||
export const HasMarkersCriterionOption = new CriterionOption({
|
||||
messageID: "hasMarkers",
|
||||
type: "has_markers",
|
||||
options: [true.toString(), false.toString()],
|
||||
makeCriterion: () => new HasMarkersCriterion(),
|
||||
});
|
||||
export const HasMarkersCriterionOption = new StringBooleanCriterionOption(
|
||||
"hasMarkers",
|
||||
"has_markers",
|
||||
() => new HasMarkersCriterion()
|
||||
);
|
||||
|
||||
export class HasMarkersCriterion extends StringCriterion {
|
||||
export class HasMarkersCriterion extends StringBooleanCriterion {
|
||||
constructor() {
|
||||
super(HasMarkersCriterionOption);
|
||||
}
|
||||
|
||||
protected toCriterionInput(): string {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { BooleanCriterion, BooleanCriterionOption } from "./criterion";
|
||||
|
||||
export const InteractiveCriterionOption = new BooleanCriterionOption(
|
||||
"interactive",
|
||||
"interactive"
|
||||
"interactive",
|
||||
() => new InteractiveCriterion()
|
||||
);
|
||||
|
||||
export class InteractiveCriterion extends BooleanCriterion {
|
||||
|
||||
@@ -3,26 +3,25 @@ import { CriterionType } from "../types";
|
||||
import { CriterionOption, StringCriterion, Option } from "./criterion";
|
||||
|
||||
export class IsMissingCriterion extends StringCriterion {
|
||||
public modifierOptions = [];
|
||||
|
||||
protected toCriterionInput(): string {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
class IsMissingCriterionOptionClass extends CriterionOption {
|
||||
class IsMissingCriterionOption extends CriterionOption {
|
||||
constructor(messageID: string, type: CriterionType, options: Option[]) {
|
||||
super({
|
||||
messageID,
|
||||
type,
|
||||
options,
|
||||
modifierOptions: [],
|
||||
defaultModifier: CriterionModifier.Equals,
|
||||
makeCriterion: () => new IsMissingCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const SceneIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
export const SceneIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
[
|
||||
@@ -40,14 +39,16 @@ export const SceneIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
]
|
||||
);
|
||||
|
||||
export const ImageIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
export const ImageIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
["title", "galleries", "studio", "performers", "tags"]
|
||||
);
|
||||
|
||||
export const PerformerIsMissingCriterionOption =
|
||||
new IsMissingCriterionOptionClass("isMissing", "is_missing", [
|
||||
export const PerformerIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
[
|
||||
"url",
|
||||
"twitter",
|
||||
"instagram",
|
||||
@@ -67,33 +68,28 @@ export const PerformerIsMissingCriterionOption =
|
||||
"image",
|
||||
"details",
|
||||
"stash_id",
|
||||
]);
|
||||
]
|
||||
);
|
||||
|
||||
export const GalleryIsMissingCriterionOption =
|
||||
new IsMissingCriterionOptionClass("isMissing", "is_missing", [
|
||||
"title",
|
||||
"details",
|
||||
"url",
|
||||
"date",
|
||||
"studio",
|
||||
"performers",
|
||||
"tags",
|
||||
"scenes",
|
||||
]);
|
||||
export const GalleryIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
["title", "details", "url", "date", "studio", "performers", "tags", "scenes"]
|
||||
);
|
||||
|
||||
export const TagIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
export const TagIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
["image"]
|
||||
);
|
||||
|
||||
export const StudioIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
export const StudioIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
["image", "stash_id", "details"]
|
||||
);
|
||||
|
||||
export const MovieIsMissingCriterionOption = new IsMissingCriterionOptionClass(
|
||||
export const MovieIsMissingCriterionOption = new IsMissingCriterionOption(
|
||||
"isMissing",
|
||||
"is_missing",
|
||||
["front_image", "back_image", "scenes"]
|
||||
|
||||
@@ -6,7 +6,8 @@ export const MoviesCriterionOption = new ILabeledIdCriterionOption(
|
||||
"movies",
|
||||
"movies",
|
||||
false,
|
||||
inputType
|
||||
inputType,
|
||||
() => new MoviesCriterion()
|
||||
);
|
||||
|
||||
export class MoviesCriterion extends ILabeledIdCriterion {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Criterion, StringCriterionOption } from "./criterion";
|
||||
|
||||
export const NoneCriterionOption = new StringCriterionOption("none", "none");
|
||||
export class NoneCriterion extends Criterion<string> {
|
||||
constructor() {
|
||||
super(NoneCriterionOption, "none");
|
||||
}
|
||||
|
||||
protected getLabelValue(): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import { BooleanCriterion, BooleanCriterionOption } from "./criterion";
|
||||
|
||||
export const OrganizedCriterionOption = new BooleanCriterionOption(
|
||||
"organized",
|
||||
"organized"
|
||||
"organized",
|
||||
() => new OrganizedCriterion()
|
||||
);
|
||||
|
||||
export class OrganizedCriterion extends BooleanCriterion {
|
||||
|
||||
13
ui/v2.5/src/models/list-filter/criteria/path.ts
Normal file
13
ui/v2.5/src/models/list-filter/criteria/path.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { StringCriterion, StringCriterionOption } from "./criterion";
|
||||
|
||||
export const PathCriterionOption = new StringCriterionOption(
|
||||
"path",
|
||||
"path",
|
||||
() => new PathCriterion()
|
||||
);
|
||||
|
||||
export class PathCriterion extends StringCriterion {
|
||||
constructor() {
|
||||
super(PathCriterionOption);
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@ export const PerformersCriterionOption = new CriterionOption({
|
||||
type: "performers",
|
||||
modifierOptions,
|
||||
defaultModifier,
|
||||
makeCriterion: () => new PerformersCriterion(),
|
||||
inputType,
|
||||
makeCriterion: () => new PerformersCriterion(),
|
||||
});
|
||||
|
||||
export class PerformersCriterion extends Criterion<ILabeledValueListValue> {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import {
|
||||
CriterionModifier,
|
||||
PhashDistanceCriterionInput,
|
||||
PHashDuplicationCriterionInput,
|
||||
} from "src/core/generated-graphql";
|
||||
import { IPhashDistanceValue } from "../types";
|
||||
import {
|
||||
BooleanCriterionOption,
|
||||
Criterion,
|
||||
CriterionOption,
|
||||
PhashDuplicateCriterion,
|
||||
StringCriterion,
|
||||
} from "./criterion";
|
||||
|
||||
export const PhashCriterionOption = new CriterionOption({
|
||||
@@ -56,8 +57,14 @@ export const DuplicatedCriterionOption = new BooleanCriterionOption(
|
||||
() => new DuplicatedCriterion()
|
||||
);
|
||||
|
||||
export class DuplicatedCriterion extends PhashDuplicateCriterion {
|
||||
export class DuplicatedCriterion extends StringCriterion {
|
||||
constructor() {
|
||||
super(DuplicatedCriterionOption);
|
||||
}
|
||||
|
||||
protected toCriterionInput(): PHashDuplicationCriterionInput {
|
||||
return {
|
||||
duplicated: this.value === "true",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ConfigDataFragment,
|
||||
CriterionModifier,
|
||||
IntCriterionInput,
|
||||
} from "../../../core/generated-graphql";
|
||||
} from "src/core/generated-graphql";
|
||||
import { INumberValue } from "../types";
|
||||
import { Criterion, CriterionOption } from "./criterion";
|
||||
import { IUIConfig } from "src/core/config";
|
||||
|
||||
@@ -11,20 +11,7 @@ import {
|
||||
StringCriterion,
|
||||
} from "./criterion";
|
||||
|
||||
abstract class AbstractResolutionCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): ResolutionCriterionInput | undefined {
|
||||
const value = stringToResolution(this.value);
|
||||
|
||||
if (value !== undefined) {
|
||||
return {
|
||||
value,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResolutionCriterionOptionType extends CriterionOption {
|
||||
class BaseResolutionCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
value: CriterionType,
|
||||
makeCriterion: () => Criterion<CriterionValue>
|
||||
@@ -44,23 +31,37 @@ class ResolutionCriterionOptionType extends CriterionOption {
|
||||
}
|
||||
}
|
||||
|
||||
export const ResolutionCriterionOption = new ResolutionCriterionOptionType(
|
||||
class BaseResolutionCriterion extends StringCriterion {
|
||||
protected toCriterionInput(): ResolutionCriterionInput | undefined {
|
||||
const value = stringToResolution(this.value);
|
||||
|
||||
if (value !== undefined) {
|
||||
return {
|
||||
value,
|
||||
modifier: this.modifier,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ResolutionCriterionOption = new BaseResolutionCriterionOption(
|
||||
"resolution",
|
||||
() => new ResolutionCriterion()
|
||||
);
|
||||
export class ResolutionCriterion extends AbstractResolutionCriterion {
|
||||
|
||||
export class ResolutionCriterion extends BaseResolutionCriterion {
|
||||
constructor() {
|
||||
super(ResolutionCriterionOption);
|
||||
}
|
||||
}
|
||||
|
||||
export const AverageResolutionCriterionOption =
|
||||
new ResolutionCriterionOptionType(
|
||||
new BaseResolutionCriterionOption(
|
||||
"average_resolution",
|
||||
() => new AverageResolutionCriterion()
|
||||
);
|
||||
|
||||
export class AverageResolutionCriterion extends AbstractResolutionCriterion {
|
||||
export class AverageResolutionCriterion extends BaseResolutionCriterion {
|
||||
constructor() {
|
||||
super(AverageResolutionCriterionOption);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ export const StudiosCriterionOption = new CriterionOption({
|
||||
type: "studios",
|
||||
modifierOptions,
|
||||
defaultModifier,
|
||||
makeCriterion: () => new StudiosCriterion(),
|
||||
inputType,
|
||||
makeCriterion: () => new StudiosCriterion(),
|
||||
});
|
||||
|
||||
export class StudiosCriterion extends IHierarchicalLabeledIdCriterion {
|
||||
@@ -34,8 +34,10 @@ export const ParentStudiosCriterionOption = new ILabeledIdCriterionOption(
|
||||
"parent_studios",
|
||||
"parents",
|
||||
false,
|
||||
inputType
|
||||
inputType,
|
||||
() => new ParentStudiosCriterion()
|
||||
);
|
||||
|
||||
export class ParentStudiosCriterion extends ILabeledIdCriterion {
|
||||
constructor() {
|
||||
super(ParentStudiosCriterionOption);
|
||||
|
||||
@@ -20,7 +20,7 @@ const withoutEqualsModifierOptions = [
|
||||
const defaultModifier = CriterionModifier.IncludesAll;
|
||||
const inputType = "tags";
|
||||
|
||||
export class TagsCriterionOptionClass extends CriterionOption {
|
||||
class BaseTagsCriterionOption extends CriterionOption {
|
||||
constructor(
|
||||
messageID: string,
|
||||
type: CriterionType,
|
||||
@@ -31,37 +31,37 @@ export class TagsCriterionOptionClass extends CriterionOption {
|
||||
type,
|
||||
modifierOptions,
|
||||
defaultModifier,
|
||||
makeCriterion: () => new TagsCriterion(this),
|
||||
inputType,
|
||||
makeCriterion: () => new TagsCriterion(this),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const TagsCriterionOption = new TagsCriterionOptionClass(
|
||||
export const TagsCriterionOption = new BaseTagsCriterionOption(
|
||||
"tags",
|
||||
"tags",
|
||||
defaultModifierOptions
|
||||
);
|
||||
|
||||
export const SceneTagsCriterionOption = new TagsCriterionOptionClass(
|
||||
export const SceneTagsCriterionOption = new BaseTagsCriterionOption(
|
||||
"scene_tags",
|
||||
"scene_tags",
|
||||
defaultModifierOptions
|
||||
);
|
||||
|
||||
export const PerformerTagsCriterionOption = new TagsCriterionOptionClass(
|
||||
export const PerformerTagsCriterionOption = new BaseTagsCriterionOption(
|
||||
"performer_tags",
|
||||
"performer_tags",
|
||||
withoutEqualsModifierOptions
|
||||
);
|
||||
|
||||
export const ParentTagsCriterionOption = new TagsCriterionOptionClass(
|
||||
export const ParentTagsCriterionOption = new BaseTagsCriterionOption(
|
||||
"parent_tags",
|
||||
"parents",
|
||||
withoutEqualsModifierOptions
|
||||
);
|
||||
|
||||
export const ChildTagsCriterionOption = new TagsCriterionOptionClass(
|
||||
export const ChildTagsCriterionOption = new BaseTagsCriterionOption(
|
||||
"sub_tags",
|
||||
"children",
|
||||
withoutEqualsModifierOptions
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
SortDirectionEnum,
|
||||
} from "src/core/generated-graphql";
|
||||
import { Criterion, CriterionValue } from "./criteria/criterion";
|
||||
import { makeCriteria } from "./criteria/factory";
|
||||
import { getFilterOptions } from "./factory";
|
||||
import { CriterionType, DisplayMode } from "./types";
|
||||
|
||||
interface IDecodedParams {
|
||||
@@ -128,16 +128,9 @@ export class ListFilterModel {
|
||||
for (const jsonString of params.c) {
|
||||
try {
|
||||
const encodedCriterion = JSON.parse(jsonString);
|
||||
const criterion = makeCriteria(
|
||||
this.mode,
|
||||
encodedCriterion.type,
|
||||
this.config
|
||||
);
|
||||
// it's possible that we have unsupported criteria. Just skip if so.
|
||||
if (criterion) {
|
||||
criterion.setFromEncodedCriterion(encodedCriterion);
|
||||
this.criteria.push(criterion);
|
||||
}
|
||||
const criterion = this.makeCriterion(encodedCriterion.type);
|
||||
criterion.setFromEncodedCriterion(encodedCriterion);
|
||||
this.criteria.push(criterion);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Failed to parse encoded criterion:", err);
|
||||
@@ -280,16 +273,9 @@ export class ListFilterModel {
|
||||
this.criteria = [];
|
||||
if (objectFilter) {
|
||||
Object.keys(objectFilter).forEach((key) => {
|
||||
const criterion = makeCriteria(
|
||||
this.mode,
|
||||
key as CriterionType,
|
||||
this.config
|
||||
);
|
||||
// it's possible that we have unsupported criteria. Just skip if so.
|
||||
if (criterion) {
|
||||
criterion.setFromEncodedCriterion(objectFilter[key]);
|
||||
this.criteria.push(criterion);
|
||||
}
|
||||
const criterion = this.makeCriterion(key as CriterionType);
|
||||
criterion.setFromEncodedCriterion(objectFilter[key]);
|
||||
this.criteria.push(criterion);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -426,6 +412,18 @@ export class ListFilterModel {
|
||||
return query.join("&");
|
||||
}
|
||||
|
||||
public makeCriterion(type: CriterionType) {
|
||||
const { criterionOptions } = getFilterOptions(this.mode);
|
||||
|
||||
const option = criterionOptions.find((o) => o.type === type);
|
||||
|
||||
if (!option) {
|
||||
throw new Error(`Unknown criterion parameter name: ${type}`);
|
||||
}
|
||||
|
||||
return option.makeCriterion(this.config);
|
||||
}
|
||||
|
||||
// TODO: These don't support multiple of the same criteria, only the last one set is used.
|
||||
|
||||
public makeFindFilter(): FindFilterType {
|
||||
@@ -439,8 +437,7 @@ export class ListFilterModel {
|
||||
}
|
||||
|
||||
public makeFilter() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const output: Record<string, any> = {};
|
||||
const output: Record<string, unknown> = {};
|
||||
this.criteria.forEach((criterion) => {
|
||||
criterion.apply(output);
|
||||
});
|
||||
@@ -449,8 +446,7 @@ export class ListFilterModel {
|
||||
}
|
||||
|
||||
public makeSavedFindFilter() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const output: Record<string, any> = {};
|
||||
const output: Record<string, unknown> = {};
|
||||
this.criteria.forEach((criterion) => {
|
||||
criterion.toSavedFilter(output);
|
||||
});
|
||||
@@ -458,8 +454,7 @@ export class ListFilterModel {
|
||||
return output;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public makeUIOptions(): Record<string, any> {
|
||||
public makeUIOptions(): Record<string, unknown> {
|
||||
return {
|
||||
display_mode: this.displayMode,
|
||||
zoom_index: this.zoomIndex,
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
createStringCriterionOption,
|
||||
createDateCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
createPathCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { GalleryIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
@@ -19,6 +18,7 @@ import {
|
||||
import { ListFilterOptions, MediaSortByOptions } from "./filter-options";
|
||||
import { DisplayMode } from "./types";
|
||||
import { RatingCriterionOption } from "./criteria/rating";
|
||||
import { PathCriterionOption } from "./criteria/path";
|
||||
|
||||
const defaultSortBy = "path";
|
||||
|
||||
@@ -44,7 +44,7 @@ const displayModeOptions = [
|
||||
const criterionOptions = [
|
||||
createStringCriterionOption("title"),
|
||||
createStringCriterionOption("details"),
|
||||
createPathCriterionOption("path"),
|
||||
PathCriterionOption,
|
||||
createStringCriterionOption("checksum", "media_info.checksum"),
|
||||
RatingCriterionOption,
|
||||
OrganizedCriterionOption,
|
||||
@@ -52,13 +52,13 @@ const criterionOptions = [
|
||||
GalleryIsMissingCriterionOption,
|
||||
TagsCriterionOption,
|
||||
HasChaptersCriterionOption,
|
||||
createStringCriterionOption("tag_count"),
|
||||
createMandatoryNumberCriterionOption("tag_count"),
|
||||
PerformerTagsCriterionOption,
|
||||
PerformersCriterionOption,
|
||||
createStringCriterionOption("performer_count"),
|
||||
createMandatoryNumberCriterionOption("performer_count"),
|
||||
createMandatoryNumberCriterionOption("performer_age"),
|
||||
PerformerFavoriteCriterionOption,
|
||||
createStringCriterionOption("image_count"),
|
||||
createMandatoryNumberCriterionOption("image_count"),
|
||||
StudiosCriterionOption,
|
||||
createStringCriterionOption("url"),
|
||||
createMandatoryNumberCriterionOption("file_count", "zip_file_count"),
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
createStringCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
createDateCriterionOption,
|
||||
createPathCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { ImageIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { OrganizedCriterionOption } from "./criteria/organized";
|
||||
import { PathCriterionOption } from "./criteria/path";
|
||||
import { PerformersCriterionOption } from "./criteria/performers";
|
||||
import { RatingCriterionOption } from "./criteria/rating";
|
||||
import { ResolutionCriterionOption } from "./criteria/resolution";
|
||||
@@ -34,7 +34,7 @@ const displayModeOptions = [DisplayMode.Grid, DisplayMode.Wall];
|
||||
const criterionOptions = [
|
||||
createStringCriterionOption("title"),
|
||||
createMandatoryStringCriterionOption("checksum", "media_info.checksum"),
|
||||
createPathCriterionOption("path"),
|
||||
PathCriterionOption,
|
||||
OrganizedCriterionOption,
|
||||
createMandatoryNumberCriterionOption("o_counter"),
|
||||
ResolutionCriterionOption,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
createMandatoryNumberCriterionOption,
|
||||
createStringCriterionOption,
|
||||
createDateCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
createDurationCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { MovieIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { StudiosCriterionOption } from "./criteria/studios";
|
||||
@@ -29,7 +29,7 @@ const criterionOptions = [
|
||||
createStringCriterionOption("name"),
|
||||
createStringCriterionOption("director"),
|
||||
createStringCriterionOption("synopsis"),
|
||||
createMandatoryNumberCriterionOption("duration"),
|
||||
createDurationCriterionOption("duration"),
|
||||
RatingCriterionOption,
|
||||
PerformersCriterionOption,
|
||||
createDateCriterionOption("date"),
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
createBooleanCriterionOption,
|
||||
createDateCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
NumberCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { GenderCriterionOption } from "./criteria/gender";
|
||||
@@ -94,9 +93,9 @@ const criterionOptions = [
|
||||
createMandatoryNumberCriterionOption("gallery_count"),
|
||||
createMandatoryNumberCriterionOption("o_counter"),
|
||||
createBooleanCriterionOption("ignore_auto_tag"),
|
||||
new NumberCriterionOption("height", "height_cm"),
|
||||
...numberCriteria.map((c) => createNumberCriterionOption(c)),
|
||||
CountryCriterionOption,
|
||||
createNumberCriterionOption("height_cm", "height"),
|
||||
...numberCriteria.map((c) => createNumberCriterionOption(c)),
|
||||
...stringCriteria.map((c) => createStringCriterionOption(c)),
|
||||
createDateCriterionOption("birthdate"),
|
||||
createDateCriterionOption("death_date"),
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
createStringCriterionOption,
|
||||
createDateCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
createPathCriterionOption,
|
||||
createDurationCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { HasMarkersCriterionOption } from "./criteria/has-markers";
|
||||
import { SceneIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
@@ -28,6 +28,7 @@ import { PerformerFavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { CaptionsCriterionOption } from "./criteria/captions";
|
||||
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||
import { RatingCriterionOption } from "./criteria/rating";
|
||||
import { PathCriterionOption } from "./criteria/path";
|
||||
|
||||
const defaultSortBy = "date";
|
||||
const sortByOptions = [
|
||||
@@ -60,7 +61,7 @@ const displayModeOptions = [
|
||||
const criterionOptions = [
|
||||
createStringCriterionOption("title"),
|
||||
createStringCriterionOption("code", "scene_code"),
|
||||
createPathCriterionOption("path"),
|
||||
PathCriterionOption,
|
||||
createStringCriterionOption("details"),
|
||||
createStringCriterionOption("director"),
|
||||
createMandatoryStringCriterionOption("oshash", "media_info.hash"),
|
||||
@@ -73,9 +74,9 @@ const criterionOptions = [
|
||||
ResolutionCriterionOption,
|
||||
createStringCriterionOption("video_codec"),
|
||||
createStringCriterionOption("audio_codec"),
|
||||
createMandatoryNumberCriterionOption("duration"),
|
||||
createMandatoryNumberCriterionOption("resume_time"),
|
||||
createMandatoryNumberCriterionOption("play_duration"),
|
||||
createDurationCriterionOption("duration"),
|
||||
createDurationCriterionOption("resume_time"),
|
||||
createDurationCriterionOption("play_duration"),
|
||||
createMandatoryNumberCriterionOption("play_count"),
|
||||
HasMarkersCriterionOption,
|
||||
SceneIsMissingCriterionOption,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// NOTE: add new enum values to the end, to ensure existing data
|
||||
|
||||
// is not impacted
|
||||
export enum DisplayMode {
|
||||
Grid,
|
||||
@@ -60,38 +59,51 @@ export interface IPhashDistanceValue {
|
||||
}
|
||||
|
||||
export function criterionIsHierarchicalLabelValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
value: unknown
|
||||
): value is IHierarchicalLabelValue {
|
||||
return typeof value === "object" && "items" in value && "depth" in value;
|
||||
return (
|
||||
typeof value === "object" && !!value && "items" in value && "depth" in value
|
||||
);
|
||||
}
|
||||
|
||||
export function criterionIsNumberValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
): value is INumberValue {
|
||||
return typeof value === "object" && "value" in value && "value2" in value;
|
||||
export function criterionIsNumberValue(value: unknown): value is INumberValue {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"value" in value &&
|
||||
"value2" in value
|
||||
);
|
||||
}
|
||||
|
||||
export function criterionIsStashIDValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
value: unknown
|
||||
): value is IStashIDValue {
|
||||
return typeof value === "object" && "endpoint" in value && "stashID" in value;
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"endpoint" in value &&
|
||||
"stashID" in value
|
||||
);
|
||||
}
|
||||
|
||||
export function criterionIsDateValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
): value is IDateValue {
|
||||
return typeof value === "object" && "value" in value && "value2" in value;
|
||||
export function criterionIsDateValue(value: unknown): value is IDateValue {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"value" in value &&
|
||||
"value2" in value
|
||||
);
|
||||
}
|
||||
|
||||
export function criterionIsTimestampValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any
|
||||
value: unknown
|
||||
): value is ITimestampValue {
|
||||
return typeof value === "object" && "value" in value && "value2" in value;
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"value" in value &&
|
||||
"value2" in value
|
||||
);
|
||||
}
|
||||
|
||||
export interface IOptionType {
|
||||
@@ -101,7 +113,6 @@ export interface IOptionType {
|
||||
}
|
||||
|
||||
export type CriterionType =
|
||||
| "none"
|
||||
| "path"
|
||||
| "rating"
|
||||
| "rating100"
|
||||
|
||||
@@ -16,13 +16,13 @@ const secondsToString = (seconds: number) => {
|
||||
|
||||
const stringToSeconds = (v?: string) => {
|
||||
if (!v) {
|
||||
return 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const splits = v.split(":");
|
||||
|
||||
if (splits.length > 3) {
|
||||
return 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let seconds = 0;
|
||||
@@ -30,12 +30,12 @@ const stringToSeconds = (v?: string) => {
|
||||
while (splits.length > 0) {
|
||||
const thisSplit = splits.pop();
|
||||
if (thisSplit === undefined) {
|
||||
return 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const thisInt = parseInt(thisSplit, 10);
|
||||
if (Number.isNaN(thisInt)) {
|
||||
return 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
seconds += factor * thisInt;
|
||||
|
||||
@@ -79,29 +79,14 @@ const renderInputGroup = (options: {
|
||||
};
|
||||
|
||||
const renderDurationInput = (options: {
|
||||
value: string | undefined;
|
||||
value: number | undefined;
|
||||
isEditing: boolean;
|
||||
url?: string;
|
||||
asString?: boolean;
|
||||
onChange: (value: string | undefined) => void;
|
||||
onChange: (value: number | undefined) => void;
|
||||
}) => {
|
||||
let numericValue: number | undefined;
|
||||
if (options.value) {
|
||||
if (!options.asString) {
|
||||
try {
|
||||
numericValue = Number.parseInt(options.value, 10);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
numericValue = DurationUtils.stringToSeconds(options.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.isEditing) {
|
||||
let durationString;
|
||||
if (numericValue !== undefined) {
|
||||
durationString = DurationUtils.secondsToString(numericValue);
|
||||
if (options.value !== undefined) {
|
||||
durationString = DurationUtils.secondsToString(options.value);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -113,19 +98,11 @@ const renderDurationInput = (options: {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DurationInput
|
||||
disabled={!options.isEditing}
|
||||
numericValue={numericValue}
|
||||
onValueChange={(valueAsNumber: number, valueAsString?: string) => {
|
||||
let value = valueAsString;
|
||||
if (!options.asString) {
|
||||
value =
|
||||
valueAsNumber !== undefined ? valueAsNumber.toString() : undefined;
|
||||
}
|
||||
|
||||
options.onChange(value);
|
||||
}}
|
||||
value={options.value}
|
||||
setValue={(v) => options.onChange(v)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -86,10 +86,9 @@ const renderInputGroup = (options: {
|
||||
const renderDurationInput = (options: {
|
||||
title: string;
|
||||
placeholder?: string;
|
||||
value: string | undefined;
|
||||
value: number | undefined;
|
||||
isEditing: boolean;
|
||||
asString?: boolean;
|
||||
onChange: (value: string | undefined) => void;
|
||||
onChange: (value: number | undefined) => void;
|
||||
labelProps?: FormLabelProps;
|
||||
inputProps?: ColProps;
|
||||
}) => {
|
||||
|
||||
@@ -41,10 +41,9 @@ const renderInputGroup = (options: {
|
||||
const renderDurationInput = (options: {
|
||||
title: string;
|
||||
placeholder?: string;
|
||||
value: string | undefined;
|
||||
value: number | undefined;
|
||||
isEditing: boolean;
|
||||
asString?: boolean;
|
||||
onChange: (value: string | undefined) => void;
|
||||
onChange: (value: number | undefined) => void;
|
||||
}) => {
|
||||
return (
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user