mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Filter improvement exploration
Changed the rating filter to allow for more than just an equality check. This progresses #29.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -10858,6 +10858,35 @@ func (e *executableSchema) GenerateMetadataInputMiddleware(ctx context.Context,
|
|||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnmarshalIntCriterionInput(v interface{}) (IntCriterionInput, error) {
|
||||||
|
var it IntCriterionInput
|
||||||
|
var asMap = v.(map[string]interface{})
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "value":
|
||||||
|
var err error
|
||||||
|
it.Value, err = graphql.UnmarshalInt(v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "modifier":
|
||||||
|
var err error
|
||||||
|
err = (&it.Modifier).UnmarshalGQL(v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *executableSchema) IntCriterionInputMiddleware(ctx context.Context, obj *IntCriterionInput) (*IntCriterionInput, error) {
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
func UnmarshalPerformerCreateInput(v interface{}) (PerformerCreateInput, error) {
|
func UnmarshalPerformerCreateInput(v interface{}) (PerformerCreateInput, error) {
|
||||||
var it PerformerCreateInput
|
var it PerformerCreateInput
|
||||||
var asMap = v.(map[string]interface{})
|
var asMap = v.(map[string]interface{})
|
||||||
@@ -11303,9 +11332,9 @@ func UnmarshalSceneFilterType(v interface{}) (SceneFilterType, error) {
|
|||||||
switch k {
|
switch k {
|
||||||
case "rating":
|
case "rating":
|
||||||
var err error
|
var err error
|
||||||
var ptr1 int
|
var ptr1 IntCriterionInput
|
||||||
if v != nil {
|
if v != nil {
|
||||||
ptr1, err = graphql.UnmarshalInt(v)
|
ptr1, err = UnmarshalIntCriterionInput(v)
|
||||||
it.Rating = &ptr1
|
it.Rating = &ptr1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11392,6 +11421,14 @@ func UnmarshalSceneFilterType(v interface{}) (SceneFilterType, error) {
|
|||||||
|
|
||||||
func (e *executableSchema) SceneFilterTypeMiddleware(ctx context.Context, obj *SceneFilterType) (*SceneFilterType, error) {
|
func (e *executableSchema) SceneFilterTypeMiddleware(ctx context.Context, obj *SceneFilterType) (*SceneFilterType, error) {
|
||||||
|
|
||||||
|
if obj.Rating != nil {
|
||||||
|
var err error
|
||||||
|
obj.Rating, err = e.IntCriterionInputMiddleware(ctx, obj.Rating)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12301,7 +12338,7 @@ input SceneMarkerFilterType {
|
|||||||
|
|
||||||
input SceneFilterType {
|
input SceneFilterType {
|
||||||
"""Filter by rating"""
|
"""Filter by rating"""
|
||||||
rating: Int
|
rating: IntCriterionInput
|
||||||
"""Filter by resolution"""
|
"""Filter by resolution"""
|
||||||
resolution: ResolutionEnum
|
resolution: ResolutionEnum
|
||||||
"""Filter to only include scenes which have markers. ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `"""
|
"""Filter to only include scenes which have markers. ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `"""
|
||||||
@@ -12316,6 +12353,28 @@ input SceneFilterType {
|
|||||||
performer_id: ID
|
performer_id: ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CriterionModifier {
|
||||||
|
"""="""
|
||||||
|
EQUALS,
|
||||||
|
"""!="""
|
||||||
|
NOT_EQUALS,
|
||||||
|
""">"""
|
||||||
|
GREATER_THAN,
|
||||||
|
"""<"""
|
||||||
|
LESS_THAN,
|
||||||
|
"""IS NULL"""
|
||||||
|
IS_NULL,
|
||||||
|
"""IS NOT NULL"""
|
||||||
|
NOT_NULL,
|
||||||
|
INCLUDES,
|
||||||
|
EXCLUDES,
|
||||||
|
}
|
||||||
|
|
||||||
|
input IntCriterionInput {
|
||||||
|
value: Int!
|
||||||
|
modifier: CriterionModifier!
|
||||||
|
}
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Config
|
# Config
|
||||||
#######################################
|
#######################################
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ type GenerateMetadataInput struct {
|
|||||||
Transcodes bool `json:"transcodes"`
|
Transcodes bool `json:"transcodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IntCriterionInput struct {
|
||||||
|
Value int `json:"value"`
|
||||||
|
Modifier CriterionModifier `json:"modifier"`
|
||||||
|
}
|
||||||
|
|
||||||
type MarkerStringsResultType struct {
|
type MarkerStringsResultType struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
@@ -144,7 +149,7 @@ type SceneFileType struct {
|
|||||||
|
|
||||||
type SceneFilterType struct {
|
type SceneFilterType struct {
|
||||||
// Filter by rating
|
// Filter by rating
|
||||||
Rating *int `json:"rating"`
|
Rating *IntCriterionInput `json:"rating"`
|
||||||
// Filter by resolution
|
// Filter by resolution
|
||||||
Resolution *ResolutionEnum `json:"resolution"`
|
Resolution *ResolutionEnum `json:"resolution"`
|
||||||
// Filter to only include scenes which have markers. `true` or `false`
|
// Filter to only include scenes which have markers. `true` or `false`
|
||||||
@@ -270,6 +275,65 @@ type TagUpdateInput struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CriterionModifier string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// =
|
||||||
|
CriterionModifierEquals CriterionModifier = "EQUALS"
|
||||||
|
// !=
|
||||||
|
CriterionModifierNotEquals CriterionModifier = "NOT_EQUALS"
|
||||||
|
// >
|
||||||
|
CriterionModifierGreaterThan CriterionModifier = "GREATER_THAN"
|
||||||
|
// <
|
||||||
|
CriterionModifierLessThan CriterionModifier = "LESS_THAN"
|
||||||
|
// IS NULL
|
||||||
|
CriterionModifierIsNull CriterionModifier = "IS_NULL"
|
||||||
|
// IS NOT NULL
|
||||||
|
CriterionModifierNotNull CriterionModifier = "NOT_NULL"
|
||||||
|
CriterionModifierIncludes CriterionModifier = "INCLUDES"
|
||||||
|
CriterionModifierExcludes CriterionModifier = "EXCLUDES"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AllCriterionModifier = []CriterionModifier{
|
||||||
|
CriterionModifierEquals,
|
||||||
|
CriterionModifierNotEquals,
|
||||||
|
CriterionModifierGreaterThan,
|
||||||
|
CriterionModifierLessThan,
|
||||||
|
CriterionModifierIsNull,
|
||||||
|
CriterionModifierNotNull,
|
||||||
|
CriterionModifierIncludes,
|
||||||
|
CriterionModifierExcludes,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e CriterionModifier) IsValid() bool {
|
||||||
|
switch e {
|
||||||
|
case CriterionModifierEquals, CriterionModifierNotEquals, CriterionModifierGreaterThan, CriterionModifierLessThan, CriterionModifierIsNull, CriterionModifierNotNull, CriterionModifierIncludes, CriterionModifierExcludes:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e CriterionModifier) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CriterionModifier) UnmarshalGQL(v interface{}) error {
|
||||||
|
str, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("enums must be strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = CriterionModifier(str)
|
||||||
|
if !e.IsValid() {
|
||||||
|
return fmt.Errorf("%s is not a valid CriterionModifier", str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e CriterionModifier) MarshalGQL(w io.Writer) {
|
||||||
|
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||||
|
}
|
||||||
|
|
||||||
type ResolutionEnum string
|
type ResolutionEnum string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -164,8 +164,11 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rating := sceneFilter.Rating; rating != nil {
|
if rating := sceneFilter.Rating; rating != nil {
|
||||||
whereClauses = append(whereClauses, "rating = ?")
|
clause, count := getIntCriterionWhereClause("rating", *sceneFilter.Rating)
|
||||||
args = append(args, *sceneFilter.Rating)
|
whereClauses = append(whereClauses, clause)
|
||||||
|
if count == 1 {
|
||||||
|
args = append(args, sceneFilter.Rating.Value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil {
|
if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/stashapp/stash/pkg/database"
|
"github.com/stashapp/stash/pkg/database"
|
||||||
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -104,6 +105,48 @@ func getInBinding(length int) string {
|
|||||||
return "(" + bindings + ")"
|
return "(" + bindings + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCriterionModifierBinding(criterionModifier CriterionModifier, value interface{}) (string, int) {
|
||||||
|
var length int
|
||||||
|
switch x := value.(type) {
|
||||||
|
case []string:
|
||||||
|
length = len(x)
|
||||||
|
case []int:
|
||||||
|
length = len(x)
|
||||||
|
default:
|
||||||
|
length = 1
|
||||||
|
logger.Debugf("unsupported type: %T\n", x)
|
||||||
|
}
|
||||||
|
if modifier := criterionModifier.String(); criterionModifier.IsValid() {
|
||||||
|
switch modifier {
|
||||||
|
case "EQUALS":
|
||||||
|
return "= ?", 1
|
||||||
|
case "NOT_EQUALS":
|
||||||
|
return "!= ?", 1
|
||||||
|
case "GREATER_THAN":
|
||||||
|
return "> ?", 1
|
||||||
|
case "LESS_THAN":
|
||||||
|
return "< ?", 1
|
||||||
|
case "IS_NULL":
|
||||||
|
return "IS NULL", 0
|
||||||
|
case "NOT_NULL":
|
||||||
|
return "IS NOT NULL", 0
|
||||||
|
case "INCLUDES":
|
||||||
|
return "IN "+getInBinding(length), length // TODO?
|
||||||
|
case "EXCLUDES":
|
||||||
|
return "NOT IN "+getInBinding(length), length // TODO?
|
||||||
|
default:
|
||||||
|
logger.Errorf("todo")
|
||||||
|
return "= ?", 1 // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "= ?", 1 // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIntCriterionWhereClause(column string, input IntCriterionInput) (string, int) {
|
||||||
|
binding, count := getCriterionModifierBinding(input.Modifier, input.Value)
|
||||||
|
return column+" "+binding, count
|
||||||
|
}
|
||||||
|
|
||||||
func runIdsQuery(query string, args []interface{}) ([]int, error) {
|
func runIdsQuery(query string, args []interface{}) ([]int, error) {
|
||||||
var result []struct {
|
var result []struct {
|
||||||
Int int `db:"id"`
|
Int int `db:"id"`
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ input SceneMarkerFilterType {
|
|||||||
|
|
||||||
input SceneFilterType {
|
input SceneFilterType {
|
||||||
"""Filter by rating"""
|
"""Filter by rating"""
|
||||||
rating: Int
|
rating: IntCriterionInput
|
||||||
"""Filter by resolution"""
|
"""Filter by resolution"""
|
||||||
resolution: ResolutionEnum
|
resolution: ResolutionEnum
|
||||||
"""Filter to only include scenes which have markers. `true` or `false`"""
|
"""Filter to only include scenes which have markers. `true` or `false`"""
|
||||||
@@ -372,6 +372,28 @@ input SceneFilterType {
|
|||||||
performer_id: ID
|
performer_id: ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CriterionModifier {
|
||||||
|
"""="""
|
||||||
|
EQUALS,
|
||||||
|
"""!="""
|
||||||
|
NOT_EQUALS,
|
||||||
|
""">"""
|
||||||
|
GREATER_THAN,
|
||||||
|
"""<"""
|
||||||
|
LESS_THAN,
|
||||||
|
"""IS NULL"""
|
||||||
|
IS_NULL,
|
||||||
|
"""IS NOT NULL"""
|
||||||
|
NOT_NULL,
|
||||||
|
INCLUDES,
|
||||||
|
EXCLUDES,
|
||||||
|
}
|
||||||
|
|
||||||
|
input IntCriterionInput {
|
||||||
|
value: Int!
|
||||||
|
modifier: CriterionModifier!
|
||||||
|
}
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Config
|
# Config
|
||||||
#######################################
|
#######################################
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
||||||
import { isArray } from "util";
|
import { isArray } from "util";
|
||||||
|
import { CriterionModifier } from "../../core/generated-graphql";
|
||||||
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
|
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
|
||||||
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
||||||
import { PerformersCriterion } from "../../models/list-filter/criteria/performers";
|
import { PerformersCriterion } from "../../models/list-filter/criteria/performers";
|
||||||
@@ -18,7 +19,7 @@ import { ListFilterModel } from "../../models/list-filter/filter";
|
|||||||
import { FilterMultiSelect } from "../select/FilterMultiSelect";
|
import { FilterMultiSelect } from "../select/FilterMultiSelect";
|
||||||
|
|
||||||
interface IAddFilterProps {
|
interface IAddFilterProps {
|
||||||
onAddCriterion: (criterion: Criterion) => void;
|
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
editingCriterion?: Criterion;
|
editingCriterion?: Criterion;
|
||||||
@@ -43,6 +44,12 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
setCriterion(newCriterion);
|
setCriterion(newCriterion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChangedModifierSelect(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
const newCriterion = _.cloneDeep(criterion);
|
||||||
|
newCriterion.modifier = event.target.value as any;
|
||||||
|
setCriterion(newCriterion);
|
||||||
|
}
|
||||||
|
|
||||||
function onChangedSingleSelect(event: React.ChangeEvent<HTMLSelectElement>) {
|
function onChangedSingleSelect(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
const newCriterion = _.cloneDeep(criterion);
|
const newCriterion = _.cloneDeep(criterion);
|
||||||
newCriterion.value = event.target.value;
|
newCriterion.value = event.target.value;
|
||||||
@@ -54,7 +61,8 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
const value = singleValueSelect.current.props.defaultValue;
|
const value = singleValueSelect.current.props.defaultValue;
|
||||||
if (value === undefined || value === "" || typeof value === "number") { criterion.value = criterion.options[0]; }
|
if (value === undefined || value === "" || typeof value === "number") { criterion.value = criterion.options[0]; }
|
||||||
}
|
}
|
||||||
props.onAddCriterion(criterion);
|
const oldId = !!props.editingCriterion ? props.editingCriterion.getId() : undefined;
|
||||||
|
props.onAddCriterion(criterion, oldId);
|
||||||
onToggle();
|
onToggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +77,25 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
const maybeRenderFilterPopoverContents = () => {
|
const maybeRenderFilterPopoverContents = () => {
|
||||||
if (criterion.type === "none") { return; }
|
if (criterion.type === "none") { return; }
|
||||||
|
|
||||||
|
function renderModifier() {
|
||||||
|
if (criterion.modifierOptions.length === 0) { return; }
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<HTMLSelect
|
||||||
|
options={criterion.modifierOptions}
|
||||||
|
onChange={onChangedModifierSelect}
|
||||||
|
defaultValue={criterion.modifier}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renderSelect() {
|
function renderSelect() {
|
||||||
|
// Hide the value select if the modifier is "IsNull" or "NotNull"
|
||||||
|
if (criterion.modifier === CriterionModifier.IsNull || criterion.modifier === CriterionModifier.NotNull) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isArray(criterion.value)) {
|
if (isArray(criterion.value)) {
|
||||||
let type: "performers" | "studios" | "tags" | "" = "";
|
let type: "performers" | "studios" | "tags" | "" = "";
|
||||||
if (criterion instanceof PerformersCriterion) {
|
if (criterion instanceof PerformersCriterion) {
|
||||||
@@ -103,7 +129,12 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <FormGroup>{renderSelect()}</FormGroup>;
|
return (
|
||||||
|
<FormGroup>
|
||||||
|
{renderModifier()}
|
||||||
|
{renderSelect()}
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function maybeRenderFilterSelect() {
|
function maybeRenderFilterSelect() {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface IListFilterProps {
|
|||||||
onChangeSortDirection: (sortDirection: "asc" | "desc") => void;
|
onChangeSortDirection: (sortDirection: "asc" | "desc") => void;
|
||||||
onChangeSortBy: (sortBy: string) => void;
|
onChangeSortBy: (sortBy: string) => void;
|
||||||
onChangeDisplayMode: (displayMode: DisplayMode) => void;
|
onChangeDisplayMode: (displayMode: DisplayMode) => void;
|
||||||
onAddCriterion: (criterion: Criterion) => void;
|
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
|
||||||
onRemoveCriterion: (criterion: Criterion) => void;
|
onRemoveCriterion: (criterion: Criterion) => void;
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
}
|
}
|
||||||
@@ -67,8 +67,8 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
props.onChangeDisplayMode(displayMode);
|
props.onChangeDisplayMode(displayMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAddCriterion(criterion: Criterion) {
|
function onAddCriterion(criterion: Criterion, oldId?: string) {
|
||||||
props.onAddCriterion(criterion);
|
props.onAddCriterion(criterion, oldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCancelAddCriterion() {
|
function onCancelAddCriterion() {
|
||||||
@@ -122,7 +122,7 @@ export const ListFilter: FunctionComponent<IListFilterProps> = (props: IListFilt
|
|||||||
function renderFilterTags() {
|
function renderFilterTags() {
|
||||||
return props.filter.criteria.map((criterion) => (
|
return props.filter.criteria.map((criterion) => (
|
||||||
<Tag
|
<Tag
|
||||||
key={criterion.type}
|
key={criterion.getId()}
|
||||||
className="tag-item"
|
className="tag-item"
|
||||||
itemID={criterion.getId()}
|
itemID={criterion.getId()}
|
||||||
interactive={true}
|
interactive={true}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
// Generated in 2019-03-24T09:16:39-07:00
|
// Generated in 2019-03-24T13:29:34-07:00
|
||||||
export type Maybe<T> = T | undefined;
|
export type Maybe<T> = T | undefined;
|
||||||
|
|
||||||
export interface SceneFilterType {
|
export interface SceneFilterType {
|
||||||
/** Filter by rating */
|
/** Filter by rating */
|
||||||
rating?: Maybe<number>;
|
rating?: Maybe<IntCriterionInput>;
|
||||||
/** Filter by resolution */
|
/** Filter by resolution */
|
||||||
resolution?: Maybe<ResolutionEnum>;
|
resolution?: Maybe<ResolutionEnum>;
|
||||||
/** Filter to only include scenes which have markers. `true` or `false` */
|
/** Filter to only include scenes which have markers. `true` or `false` */
|
||||||
@@ -19,6 +19,12 @@ export interface SceneFilterType {
|
|||||||
performer_id?: Maybe<string>;
|
performer_id?: Maybe<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IntCriterionInput {
|
||||||
|
value: number;
|
||||||
|
|
||||||
|
modifier: CriterionModifier;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FindFilterType {
|
export interface FindFilterType {
|
||||||
q?: Maybe<string>;
|
q?: Maybe<string>;
|
||||||
|
|
||||||
@@ -222,6 +228,17 @@ export interface ConfigGeneralInput {
|
|||||||
generatedPath?: Maybe<string>;
|
generatedPath?: Maybe<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum CriterionModifier {
|
||||||
|
Equals = "EQUALS",
|
||||||
|
NotEquals = "NOT_EQUALS",
|
||||||
|
GreaterThan = "GREATER_THAN",
|
||||||
|
LessThan = "LESS_THAN",
|
||||||
|
IsNull = "IS_NULL",
|
||||||
|
NotNull = "NOT_NULL",
|
||||||
|
Includes = "INCLUDES",
|
||||||
|
Excludes = "EXCLUDES"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ResolutionEnum {
|
export enum ResolutionEnum {
|
||||||
Low = "LOW",
|
Low = "LOW",
|
||||||
Standard = "STANDARD",
|
Standard = "STANDARD",
|
||||||
|
|||||||
@@ -117,14 +117,26 @@ export class ListHook {
|
|||||||
setFilter(newFilter);
|
setFilter(newFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAddCriterion(criterion: Criterion) {
|
function onAddCriterion(criterion: Criterion, oldId?: string) {
|
||||||
const newFilter = _.cloneDeep(filter);
|
const newFilter = _.cloneDeep(filter);
|
||||||
const existingIndex = newFilter.criteria.findIndex((c) => c.getId() === criterion.getId());
|
|
||||||
|
// Find if we are editing an existing criteria, then modify that. Or create a new one.
|
||||||
|
const existingIndex = newFilter.criteria.findIndex((c) => {
|
||||||
|
// If we modified an existing criterion, then look for the old id.
|
||||||
|
const id = !!oldId ? oldId : criterion.getId();
|
||||||
|
return c.getId() === id;
|
||||||
|
});
|
||||||
if (existingIndex === -1) {
|
if (existingIndex === -1) {
|
||||||
newFilter.criteria.push(criterion);
|
newFilter.criteria.push(criterion);
|
||||||
} else {
|
} else {
|
||||||
newFilter.criteria[existingIndex] = criterion;
|
newFilter.criteria[existingIndex] = criterion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove duplicate modifiers
|
||||||
|
newFilter.criteria = newFilter.criteria.filter((obj, pos, arr) => {
|
||||||
|
return arr.map((mapObj: any) => mapObj.getId()).indexOf(obj.getId()) === pos;
|
||||||
|
});
|
||||||
|
|
||||||
newFilter.currentPage = 1;
|
newFilter.currentPage = 1;
|
||||||
setFilter(newFilter);
|
setFilter(newFilter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { isArray } from "util";
|
import { isArray } from "util";
|
||||||
import { ILabeledId } from "../types";
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
|
import { ILabeledId, ILabeledValue } from "../types";
|
||||||
|
|
||||||
export type CriterionType =
|
export type CriterionType =
|
||||||
"none" |
|
"none" |
|
||||||
@@ -13,15 +14,6 @@ export type CriterionType =
|
|||||||
"performers" |
|
"performers" |
|
||||||
"studios";
|
"studios";
|
||||||
|
|
||||||
export enum CriterionModifier {
|
|
||||||
Equals,
|
|
||||||
NotEquals,
|
|
||||||
GreaterThan,
|
|
||||||
LessThan,
|
|
||||||
Inclusive,
|
|
||||||
Exclusive,
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class Criterion<Option = any, Value = any> {
|
export abstract class Criterion<Option = any, Value = any> {
|
||||||
public static getLabel(type: CriterionType = "none"): string {
|
public static getLabel(type: CriterionType = "none"): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -38,9 +30,23 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getModifierOption(modifier: CriterionModifier = CriterionModifier.Equals): ILabeledValue {
|
||||||
|
switch (modifier) {
|
||||||
|
case CriterionModifier.Equals: return {value: CriterionModifier.Equals, label: "Equals"};
|
||||||
|
case CriterionModifier.NotEquals: return {value: CriterionModifier.NotEquals, label: "Not Equals"};
|
||||||
|
case CriterionModifier.GreaterThan: return {value: CriterionModifier.GreaterThan, label: "Greater Than"};
|
||||||
|
case CriterionModifier.LessThan: return {value: CriterionModifier.LessThan, label: "Less Than"};
|
||||||
|
case CriterionModifier.IsNull: return {value: CriterionModifier.IsNull, label: "Is NULL"};
|
||||||
|
case CriterionModifier.NotNull: return {value: CriterionModifier.NotNull, label: "Not NULL"};
|
||||||
|
case CriterionModifier.Includes: return {value: CriterionModifier.Includes, label: "Includes"};
|
||||||
|
case CriterionModifier.Excludes: return {value: CriterionModifier.Excludes, label: "Excludes"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract type: CriterionType;
|
public abstract type: CriterionType;
|
||||||
public abstract parameterName: string;
|
public abstract parameterName: string;
|
||||||
public abstract modifier: CriterionModifier;
|
public abstract modifier: CriterionModifier;
|
||||||
|
public abstract modifierOptions: ILabeledValue[];
|
||||||
public abstract options: Option[];
|
public abstract options: Option[];
|
||||||
public abstract value: Value;
|
public abstract value: Value;
|
||||||
|
|
||||||
@@ -51,13 +57,17 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
case CriterionModifier.NotEquals: modifierString = "is not"; break;
|
case CriterionModifier.NotEquals: modifierString = "is not"; break;
|
||||||
case CriterionModifier.GreaterThan: modifierString = "is greater than"; break;
|
case CriterionModifier.GreaterThan: modifierString = "is greater than"; break;
|
||||||
case CriterionModifier.LessThan: modifierString = "is less than"; break;
|
case CriterionModifier.LessThan: modifierString = "is less than"; break;
|
||||||
case CriterionModifier.Inclusive: modifierString = "includes"; break;
|
case CriterionModifier.IsNull: modifierString = "is null"; break;
|
||||||
case CriterionModifier.Exclusive: modifierString = "exculdes"; break;
|
case CriterionModifier.NotNull: modifierString = "is not null"; break;
|
||||||
|
case CriterionModifier.Includes: modifierString = "includes"; break;
|
||||||
|
case CriterionModifier.Excludes: modifierString = "exculdes"; break;
|
||||||
default: modifierString = "";
|
default: modifierString = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
let valueString: string;
|
let valueString: string;
|
||||||
if (isArray(this.value) && this.value.length > 0) {
|
if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
|
||||||
|
valueString = "";
|
||||||
|
} else if (isArray(this.value) && this.value.length > 0) {
|
||||||
let items = this.value;
|
let items = this.value;
|
||||||
if ((this.value as ILabeledId[])[0].label) {
|
if ((this.value as ILabeledId[])[0].label) {
|
||||||
items = this.value.map((item) => item.label) as any;
|
items = this.value.map((item) => item.label) as any;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,7 @@ export class FavoriteCriterion extends Criterion<string, string> {
|
|||||||
public type: CriterionType = "favorite";
|
public type: CriterionType = "favorite";
|
||||||
public parameterName: string = "filter_favorites";
|
public parameterName: string = "filter_favorites";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
public options: string[] = [true.toString(), false.toString()];
|
||||||
public value: string = "";
|
public value: string = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,7 @@ export class HasMarkersCriterion extends Criterion<string, string> {
|
|||||||
public type: CriterionType = "hasMarkers";
|
public type: CriterionType = "hasMarkers";
|
||||||
public parameterName: string = "has_markers";
|
public parameterName: string = "has_markers";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: string[] = [true.toString(), false.toString()];
|
public options: string[] = [true.toString(), false.toString()];
|
||||||
public value: string = "";
|
public value: string = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,7 @@ export class IsMissingCriterion extends Criterion<string, string> {
|
|||||||
public type: CriterionType = "isMissing";
|
public type: CriterionType = "isMissing";
|
||||||
public parameterName: string = "is_missing";
|
public parameterName: string = "is_missing";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: string[] = ["title", "url", "date", "gallery", "studio", "performers"];
|
public options: string[] = ["title", "url", "date", "gallery", "studio", "performers"];
|
||||||
public value: string = "";
|
public value: string = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,7 @@ export class NoneCriterion extends Criterion<any, any> {
|
|||||||
public type: CriterionType = "none";
|
public type: CriterionType = "none";
|
||||||
public parameterName: string = "";
|
public parameterName: string = "";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: any;
|
public options: any;
|
||||||
public value: any;
|
public value: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ILabeledId } from "../types";
|
import { ILabeledId } from "../types";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -16,6 +16,7 @@ export class PerformersCriterion extends Criterion<IOptionType, ILabeledId[]> {
|
|||||||
public type: CriterionType = "performers";
|
public type: CriterionType = "performers";
|
||||||
public parameterName: string = "performers";
|
public parameterName: string = "performers";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: IOptionType[] = [];
|
public options: IOptionType[] = [];
|
||||||
public value: ILabeledId[] = [];
|
public value: ILabeledId[] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,14 @@ export class RatingCriterion extends Criterion<number, number> { // TODO <number
|
|||||||
public type: CriterionType = "rating";
|
public type: CriterionType = "rating";
|
||||||
public parameterName: string = "rating";
|
public parameterName: string = "rating";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.IsNull),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.NotNull),
|
||||||
|
];
|
||||||
public options: number[] = [1, 2, 3, 4, 5];
|
public options: number[] = [1, 2, 3, 4, 5];
|
||||||
public value: number = 0;
|
public value: number = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -9,6 +9,7 @@ export class ResolutionCriterion extends Criterion<string, string> { // TODO <st
|
|||||||
public type: CriterionType = "resolution";
|
public type: CriterionType = "resolution";
|
||||||
public parameterName: string = "resolution";
|
public parameterName: string = "resolution";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: string[] = ["240p", "480p", "720p", "1080p", "4k"];
|
public options: string[] = ["240p", "480p", "720p", "1080p", "4k"];
|
||||||
public value: string = "";
|
public value: string = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ILabeledId } from "../types";
|
import { ILabeledId } from "../types";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -16,6 +16,7 @@ export class StudiosCriterion extends Criterion<IOptionType, ILabeledId[]> {
|
|||||||
public type: CriterionType = "studios";
|
public type: CriterionType = "studios";
|
||||||
public parameterName: string = "studios";
|
public parameterName: string = "studios";
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: IOptionType[] = [];
|
public options: IOptionType[] = [];
|
||||||
public value: ILabeledId[] = [];
|
public value: ILabeledId[] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as GQL from "../../../core/generated-graphql";
|
import * as GQL from "../../../core/generated-graphql";
|
||||||
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ILabeledId } from "../types";
|
import { ILabeledId } from "../types";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
CriterionModifier,
|
|
||||||
CriterionType,
|
CriterionType,
|
||||||
ICriterionOption,
|
ICriterionOption,
|
||||||
} from "./criterion";
|
} from "./criterion";
|
||||||
@@ -11,6 +11,7 @@ export class TagsCriterion extends Criterion<GQL.AllTagsForFilterAllTags, ILabel
|
|||||||
public type: CriterionType;
|
public type: CriterionType;
|
||||||
public parameterName: string;
|
public parameterName: string;
|
||||||
public modifier = CriterionModifier.Equals;
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [];
|
||||||
public options: GQL.AllTagsForFilterAllTags[] = [];
|
public options: GQL.AllTagsForFilterAllTags[] = [];
|
||||||
public value: ILabeledId[] = [];
|
public value: ILabeledId[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ export class ListFilterModel {
|
|||||||
const encodedCriterion = JSON.parse(jsonString);
|
const encodedCriterion = JSON.parse(jsonString);
|
||||||
const criterion = makeCriteria(encodedCriterion.type);
|
const criterion = makeCriteria(encodedCriterion.type);
|
||||||
criterion.value = encodedCriterion.value;
|
criterion.value = encodedCriterion.value;
|
||||||
|
criterion.modifier = encodedCriterion.modifier;
|
||||||
this.criteria.push(criterion);
|
this.criteria.push(criterion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,6 +169,7 @@ export class ListFilterModel {
|
|||||||
const encodedCriterion: any = {};
|
const encodedCriterion: any = {};
|
||||||
encodedCriterion.type = criterion.type;
|
encodedCriterion.type = criterion.type;
|
||||||
encodedCriterion.value = criterion.value;
|
encodedCriterion.value = criterion.value;
|
||||||
|
encodedCriterion.modifier = criterion.modifier;
|
||||||
const jsonCriterion = JSON.stringify(encodedCriterion);
|
const jsonCriterion = JSON.stringify(encodedCriterion);
|
||||||
encodedCriteria.push(jsonCriterion);
|
encodedCriteria.push(jsonCriterion);
|
||||||
});
|
});
|
||||||
@@ -200,7 +202,8 @@ export class ListFilterModel {
|
|||||||
this.criteria.forEach((criterion) => {
|
this.criteria.forEach((criterion) => {
|
||||||
switch (criterion.type) {
|
switch (criterion.type) {
|
||||||
case "rating":
|
case "rating":
|
||||||
result.rating = (criterion as RatingCriterion).value;
|
const crit = criterion as RatingCriterion;
|
||||||
|
result.rating = { value: crit.value, modifier: crit.modifier };
|
||||||
break;
|
break;
|
||||||
case "resolution": {
|
case "resolution": {
|
||||||
switch ((criterion as ResolutionCriterion).value) {
|
switch ((criterion as ResolutionCriterion).value) {
|
||||||
|
|||||||
@@ -16,3 +16,8 @@ export interface ILabeledId {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ILabeledValue {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user