mirror of
https://github.com/stashapp/stash.git
synced 2026-01-16 05:02:44 +01:00
Filter performers/tags/studios list by current filter (#5920)
This commit is contained in:
parent
e95c1bbc76
commit
574fd680c9
4 changed files with 267 additions and 77 deletions
|
|
@ -15,7 +15,13 @@ import {
|
|||
ILabeledValueListValue,
|
||||
} from "src/models/list-filter/types";
|
||||
import { Option } from "./SidebarListFilter";
|
||||
import { CriterionModifier } from "src/core/generated-graphql";
|
||||
import {
|
||||
CriterionModifier,
|
||||
FilterMode,
|
||||
InputMaybe,
|
||||
IntCriterionInput,
|
||||
SceneFilterType,
|
||||
} from "src/core/generated-graphql";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
interface ILabeledIdFilterProps {
|
||||
|
|
@ -316,11 +322,18 @@ export function useCriterion(
|
|||
}
|
||||
|
||||
export function useQueryState(
|
||||
useQuery: (q: string, skip: boolean) => ILoadResults<ILabeledId[]>,
|
||||
useQuery: (
|
||||
q: string,
|
||||
filter: ListFilterModel,
|
||||
skip: boolean
|
||||
) => ILoadResults<ILabeledId[]>,
|
||||
filter: ListFilterModel,
|
||||
skip: boolean
|
||||
) {
|
||||
const [query, setQuery] = useState("");
|
||||
const { results: queryResults } = useCacheResults(useQuery(query, skip));
|
||||
const { results: queryResults } = useCacheResults(
|
||||
useQuery(query, filter, skip)
|
||||
);
|
||||
|
||||
return { query, setQuery, queryResults };
|
||||
}
|
||||
|
|
@ -418,7 +431,11 @@ export function useLabeledIdFilterState(props: {
|
|||
option: CriterionOption;
|
||||
filter: ListFilterModel;
|
||||
setFilter: (f: ListFilterModel) => void;
|
||||
useQuery: (q: string, skip: boolean) => ILoadResults<ILabeledId[]>;
|
||||
useQuery: (
|
||||
q: string,
|
||||
filter: ListFilterModel,
|
||||
skip: boolean
|
||||
) => ILoadResults<ILabeledId[]>;
|
||||
singleValue?: boolean;
|
||||
hierarchical?: boolean;
|
||||
includeSubMessageID?: string;
|
||||
|
|
@ -436,7 +453,11 @@ export function useLabeledIdFilterState(props: {
|
|||
// defer querying until the user opens the filter
|
||||
const [skip, setSkip] = useState(true);
|
||||
|
||||
const { query, setQuery, queryResults } = useQueryState(useQuery, skip);
|
||||
const { query, setQuery, queryResults } = useQueryState(
|
||||
useQuery,
|
||||
filter,
|
||||
skip
|
||||
);
|
||||
|
||||
const { criterion, setCriterion } = useCriterion(option, filter, setFilter);
|
||||
|
||||
|
|
@ -475,3 +496,39 @@ export function useLabeledIdFilterState(props: {
|
|||
onOpen,
|
||||
};
|
||||
}
|
||||
|
||||
export function makeQueryVariables(query: string, extraProps: {}) {
|
||||
return {
|
||||
filter: {
|
||||
q: query,
|
||||
per_page: 200,
|
||||
},
|
||||
...extraProps,
|
||||
};
|
||||
}
|
||||
|
||||
interface IFilterType {
|
||||
scenes_filter?: InputMaybe<SceneFilterType>;
|
||||
scene_count?: InputMaybe<IntCriterionInput>;
|
||||
}
|
||||
|
||||
export function setObjectFilter(
|
||||
out: IFilterType,
|
||||
mode: FilterMode,
|
||||
relatedFilterOutput: SceneFilterType
|
||||
) {
|
||||
const empty = Object.keys(relatedFilterOutput).length === 0;
|
||||
|
||||
switch (mode) {
|
||||
case FilterMode.Scenes:
|
||||
// if empty, only get objects with scenes
|
||||
if (empty) {
|
||||
out.scene_count = {
|
||||
modifier: CriterionModifier.GreaterThan,
|
||||
value: 0,
|
||||
};
|
||||
}
|
||||
out.scenes_filter = relatedFilterOutput;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
import React, { ReactNode, useMemo } from "react";
|
||||
import { PerformersCriterion } from "src/models/list-filter/criteria/performers";
|
||||
import { useFindPerformersForSelectQuery } from "src/core/generated-graphql";
|
||||
import {
|
||||
CriterionModifier,
|
||||
FindPerformersForSelectQueryVariables,
|
||||
PerformerDataFragment,
|
||||
PerformerFilterType,
|
||||
useFindPerformersForSelectQuery,
|
||||
} from "src/core/generated-graphql";
|
||||
import { ObjectsFilter } from "./SelectableFilter";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { CriterionOption } from "src/models/list-filter/criteria/criterion";
|
||||
import { useLabeledIdFilterState } from "./LabeledIdFilter";
|
||||
import {
|
||||
makeQueryVariables,
|
||||
setObjectFilter,
|
||||
useLabeledIdFilterState,
|
||||
} from "./LabeledIdFilter";
|
||||
import { SidebarListFilter } from "./SidebarListFilter";
|
||||
|
||||
interface IPerformersFilter {
|
||||
|
|
@ -13,34 +23,74 @@ interface IPerformersFilter {
|
|||
setCriterion: (c: PerformersCriterion) => void;
|
||||
}
|
||||
|
||||
function usePerformerQuery(query: string, skip?: boolean) {
|
||||
interface IHasModifier {
|
||||
modifier: CriterionModifier;
|
||||
}
|
||||
|
||||
function queryVariables(
|
||||
query: string,
|
||||
f?: ListFilterModel
|
||||
): FindPerformersForSelectQueryVariables {
|
||||
const performerFilter: PerformerFilterType = {};
|
||||
|
||||
if (f) {
|
||||
const filterOutput = f.makeFilter();
|
||||
|
||||
// if performer modifier is includes, take it out of the filter
|
||||
if (
|
||||
(filterOutput.performers as IHasModifier)?.modifier ===
|
||||
CriterionModifier.Includes
|
||||
) {
|
||||
delete filterOutput.performers;
|
||||
|
||||
// TODO - look for same in AND?
|
||||
}
|
||||
|
||||
setObjectFilter(performerFilter, f.mode, filterOutput);
|
||||
}
|
||||
|
||||
return makeQueryVariables(query, { performer_filter: performerFilter });
|
||||
}
|
||||
|
||||
function sortResults(
|
||||
query: string,
|
||||
performers?: Pick<PerformerDataFragment, "name" | "alias_list" | "id">[]
|
||||
) {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
performers ?? [],
|
||||
(p) => p.name,
|
||||
(p) => p.alias_list
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function usePerformerQueryFilter(
|
||||
query: string,
|
||||
f?: ListFilterModel,
|
||||
skip?: boolean
|
||||
) {
|
||||
const { data, loading } = useFindPerformersForSelectQuery({
|
||||
variables: {
|
||||
filter: {
|
||||
q: query,
|
||||
per_page: 200,
|
||||
},
|
||||
},
|
||||
variables: queryVariables(query, f),
|
||||
skip,
|
||||
});
|
||||
|
||||
const results = useMemo(() => {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
data?.findPerformers.performers ?? [],
|
||||
(p) => p.name,
|
||||
(p) => p.alias_list
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}, [data, query]);
|
||||
const results = useMemo(
|
||||
() => sortResults(query, data?.findPerformers.performers),
|
||||
[data, query]
|
||||
);
|
||||
|
||||
return { results, loading };
|
||||
}
|
||||
|
||||
function usePerformerQuery(query: string, skip?: boolean) {
|
||||
return usePerformerQueryFilter(query, undefined, skip);
|
||||
}
|
||||
|
||||
const PerformersFilter: React.FC<IPerformersFilter> = ({
|
||||
criterion,
|
||||
setCriterion,
|
||||
|
|
@ -64,7 +114,7 @@ export const SidebarPerformersFilter: React.FC<{
|
|||
filter,
|
||||
setFilter,
|
||||
option,
|
||||
useQuery: usePerformerQuery,
|
||||
useQuery: usePerformerQueryFilter,
|
||||
});
|
||||
|
||||
return <SidebarListFilter {...state} title={title} />;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import React, { ReactNode, useMemo } from "react";
|
||||
import { useFindStudiosForSelectQuery } from "src/core/generated-graphql";
|
||||
import {
|
||||
StudioDataFragment,
|
||||
StudioFilterType,
|
||||
useFindStudiosForSelectQuery,
|
||||
} from "src/core/generated-graphql";
|
||||
import { HierarchicalObjectsFilter } from "./SelectableFilter";
|
||||
import { StudiosCriterion } from "src/models/list-filter/criteria/studios";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { CriterionOption } from "src/models/list-filter/criteria/criterion";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { useLabeledIdFilterState } from "./LabeledIdFilter";
|
||||
import {
|
||||
makeQueryVariables,
|
||||
setObjectFilter,
|
||||
useLabeledIdFilterState,
|
||||
} from "./LabeledIdFilter";
|
||||
import { SidebarListFilter } from "./SidebarListFilter";
|
||||
|
||||
interface IStudiosFilter {
|
||||
|
|
@ -13,34 +21,63 @@ interface IStudiosFilter {
|
|||
setCriterion: (c: StudiosCriterion) => void;
|
||||
}
|
||||
|
||||
function useStudioQuery(query: string, skip?: boolean) {
|
||||
function queryVariables(query: string, f?: ListFilterModel) {
|
||||
const studioFilter: StudioFilterType = {};
|
||||
|
||||
if (f) {
|
||||
const filterOutput = f.makeFilter();
|
||||
|
||||
// always remove studio filter from the filter
|
||||
// since modifier is includes
|
||||
delete filterOutput.studios;
|
||||
|
||||
// TODO - look for same in AND?
|
||||
|
||||
setObjectFilter(studioFilter, f.mode, filterOutput);
|
||||
}
|
||||
|
||||
return makeQueryVariables(query, { studio_filter: studioFilter });
|
||||
}
|
||||
|
||||
function sortResults(
|
||||
query: string,
|
||||
studios: Pick<StudioDataFragment, "id" | "name" | "aliases">[]
|
||||
) {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
studios ?? [],
|
||||
(s) => s.name,
|
||||
(s) => s.aliases
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function useStudioQueryFilter(
|
||||
query: string,
|
||||
filter?: ListFilterModel,
|
||||
skip?: boolean
|
||||
) {
|
||||
const { data, loading } = useFindStudiosForSelectQuery({
|
||||
variables: {
|
||||
filter: {
|
||||
q: query,
|
||||
per_page: 200,
|
||||
},
|
||||
},
|
||||
variables: queryVariables(query, filter),
|
||||
skip,
|
||||
});
|
||||
|
||||
const results = useMemo(() => {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
data?.findStudios.studios ?? [],
|
||||
(s) => s.name,
|
||||
(s) => s.aliases
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}, [data, query]);
|
||||
const results = useMemo(
|
||||
() => sortResults(query, data?.findStudios.studios ?? []),
|
||||
[data?.findStudios.studios, query]
|
||||
);
|
||||
|
||||
return { results, loading };
|
||||
}
|
||||
|
||||
function useStudioQuery(query: string, skip?: boolean) {
|
||||
return useStudioQueryFilter(query, undefined, skip);
|
||||
}
|
||||
|
||||
const StudiosFilter: React.FC<IStudiosFilter> = ({
|
||||
criterion,
|
||||
setCriterion,
|
||||
|
|
@ -65,7 +102,7 @@ export const SidebarStudiosFilter: React.FC<{
|
|||
filter,
|
||||
setFilter,
|
||||
option,
|
||||
useQuery: useStudioQuery,
|
||||
useQuery: useStudioQueryFilter,
|
||||
singleValue: true,
|
||||
hierarchical: true,
|
||||
includeSubMessageID: "subsidiary_studios",
|
||||
|
|
|
|||
|
|
@ -1,46 +1,92 @@
|
|||
import React, { ReactNode, useMemo } from "react";
|
||||
import { useFindTagsForSelectQuery } from "src/core/generated-graphql";
|
||||
import {
|
||||
CriterionModifier,
|
||||
TagDataFragment,
|
||||
TagFilterType,
|
||||
useFindTagsForSelectQuery,
|
||||
} from "src/core/generated-graphql";
|
||||
import { HierarchicalObjectsFilter } from "./SelectableFilter";
|
||||
import { StudiosCriterion } from "src/models/list-filter/criteria/studios";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { CriterionOption } from "src/models/list-filter/criteria/criterion";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { useLabeledIdFilterState } from "./LabeledIdFilter";
|
||||
import {
|
||||
makeQueryVariables,
|
||||
setObjectFilter,
|
||||
useLabeledIdFilterState,
|
||||
} from "./LabeledIdFilter";
|
||||
import { SidebarListFilter } from "./SidebarListFilter";
|
||||
import { TagsCriterion } from "src/models/list-filter/criteria/tags";
|
||||
|
||||
interface ITagsFilter {
|
||||
criterion: StudiosCriterion;
|
||||
setCriterion: (c: StudiosCriterion) => void;
|
||||
criterion: TagsCriterion;
|
||||
setCriterion: (c: TagsCriterion) => void;
|
||||
}
|
||||
|
||||
function useTagQuery(query: string, skip?: boolean) {
|
||||
interface IHasModifier {
|
||||
modifier: CriterionModifier;
|
||||
}
|
||||
|
||||
function queryVariables(query: string, f?: ListFilterModel) {
|
||||
const tagFilter: TagFilterType = {};
|
||||
|
||||
if (f) {
|
||||
const filterOutput = f.makeFilter();
|
||||
|
||||
// if tag modifier is includes, take it out of the filter
|
||||
if (
|
||||
(filterOutput.tags as IHasModifier)?.modifier ===
|
||||
CriterionModifier.Includes
|
||||
) {
|
||||
delete filterOutput.tags;
|
||||
|
||||
// TODO - look for same in AND?
|
||||
}
|
||||
|
||||
setObjectFilter(tagFilter, f.mode, filterOutput);
|
||||
}
|
||||
|
||||
return makeQueryVariables(query, { tag_filter: tagFilter });
|
||||
}
|
||||
|
||||
function sortResults(
|
||||
query: string,
|
||||
tags: Pick<TagDataFragment, "id" | "name" | "aliases">[]
|
||||
) {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
tags ?? [],
|
||||
(t) => t.name,
|
||||
(t) => t.aliases
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function useTagQueryFilter(
|
||||
query: string,
|
||||
filter?: ListFilterModel,
|
||||
skip?: boolean
|
||||
) {
|
||||
const { data, loading } = useFindTagsForSelectQuery({
|
||||
variables: {
|
||||
filter: {
|
||||
q: query,
|
||||
per_page: 200,
|
||||
},
|
||||
},
|
||||
variables: queryVariables(query, filter),
|
||||
skip,
|
||||
});
|
||||
|
||||
const results = useMemo(() => {
|
||||
return sortByRelevance(
|
||||
query,
|
||||
data?.findTags.tags ?? [],
|
||||
(t) => t.name,
|
||||
(t) => t.aliases
|
||||
).map((p) => {
|
||||
return {
|
||||
id: p.id,
|
||||
label: p.name,
|
||||
};
|
||||
});
|
||||
}, [data, query]);
|
||||
const results = useMemo(
|
||||
() => sortResults(query, data?.findTags.tags ?? []),
|
||||
[data, query]
|
||||
);
|
||||
|
||||
return { results, loading };
|
||||
}
|
||||
|
||||
function useTagQuery(query: string, skip?: boolean) {
|
||||
return useTagQueryFilter(query, undefined, skip);
|
||||
}
|
||||
|
||||
const TagsFilter: React.FC<ITagsFilter> = ({ criterion, setCriterion }) => {
|
||||
return (
|
||||
<HierarchicalObjectsFilter
|
||||
|
|
@ -61,7 +107,7 @@ export const SidebarTagsFilter: React.FC<{
|
|||
filter,
|
||||
setFilter,
|
||||
option,
|
||||
useQuery: useTagQuery,
|
||||
useQuery: useTagQueryFilter,
|
||||
hierarchical: true,
|
||||
includeSubMessageID: "sub_tags",
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue