mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
Support patching select sorting function (#4903)
* Fix return types for RegisterComponent and PatchFunction * Add support for patching TagSelect.sort * Add support for patching PerformerSelect.sort * Patch other select component sort functions * Document patchable functions/components
This commit is contained in:
parent
eec31723bd
commit
540e80c86b
8 changed files with 156 additions and 37 deletions
|
|
@ -27,7 +27,7 @@ import { useCompare } from "src/hooks/state";
|
|||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { galleryTitle } from "src/core/galleries";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
import {
|
||||
Criterion,
|
||||
CriterionValue,
|
||||
|
|
@ -49,6 +49,24 @@ type ExtraGalleryProps = {
|
|||
extraCriteria?: Array<Criterion<CriterionValue>>;
|
||||
};
|
||||
|
||||
type FindGalleriesResult = Awaited<
|
||||
ReturnType<typeof queryFindGalleriesForSelect>
|
||||
>["data"]["findGalleries"]["galleries"];
|
||||
|
||||
function sortGalleriesByRelevance(
|
||||
input: string,
|
||||
galleries: FindGalleriesResult
|
||||
) {
|
||||
return sortByRelevance(input, galleries, galleryTitle, (g) => {
|
||||
return g.files.map((f) => f.path).concat(g.folder?.path ?? []);
|
||||
});
|
||||
}
|
||||
|
||||
const gallerySelectSort = PatchFunction(
|
||||
"GallerySelect.sort",
|
||||
sortGalleriesByRelevance
|
||||
);
|
||||
|
||||
const _GallerySelect: React.FC<
|
||||
IFilterProps & IFilterValueProps<Gallery> & ExtraGalleryProps
|
||||
> = (props) => {
|
||||
|
|
@ -78,9 +96,7 @@ const _GallerySelect: React.FC<
|
|||
return !exclude.includes(gallery.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(input, ret, galleryTitle, (g) => {
|
||||
return g.files.map((f) => f.path).concat(g.folder?.path ?? []);
|
||||
}).map((gallery) => ({
|
||||
return gallerySelectSort(input, ret).map((gallery) => ({
|
||||
value: gallery.id,
|
||||
object: gallery,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { useCompare } from "src/hooks/state";
|
||||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
|
||||
export type Movie = Pick<
|
||||
|
|
@ -38,6 +38,24 @@ export type Movie = Pick<
|
|||
};
|
||||
type Option = SelectOption<Movie>;
|
||||
|
||||
type FindMoviesResult = Awaited<
|
||||
ReturnType<typeof queryFindMoviesForSelect>
|
||||
>["data"]["findMovies"]["movies"];
|
||||
|
||||
function sortMoviesByRelevance(input: string, movies: FindMoviesResult) {
|
||||
return sortByRelevance(
|
||||
input,
|
||||
movies,
|
||||
(m) => m.name,
|
||||
(m) => (m.aliases ? [m.aliases] : [])
|
||||
);
|
||||
}
|
||||
|
||||
const movieSelectSort = PatchFunction(
|
||||
"MovieSelect.sort",
|
||||
sortMoviesByRelevance
|
||||
);
|
||||
|
||||
const _MovieSelect: React.FC<
|
||||
IFilterProps &
|
||||
IFilterValueProps<Movie> & {
|
||||
|
|
@ -70,12 +88,7 @@ const _MovieSelect: React.FC<
|
|||
return !exclude.includes(movie.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(
|
||||
input,
|
||||
ret,
|
||||
(m) => m.name,
|
||||
(m) => (m.aliases ? [m.aliases] : [])
|
||||
).map((movie) => ({
|
||||
return movieSelectSort(input, ret).map((movie) => ({
|
||||
value: movie.id,
|
||||
object: movie,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { useCompare } from "src/hooks/state";
|
||||
import { Link } from "react-router-dom";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
|
||||
export type SelectObject = {
|
||||
id: string;
|
||||
|
|
@ -41,6 +41,27 @@ export type Performer = Pick<
|
|||
>;
|
||||
type Option = SelectOption<Performer>;
|
||||
|
||||
type FindPerformersResult = Awaited<
|
||||
ReturnType<typeof queryFindPerformersForSelect>
|
||||
>["data"]["findPerformers"]["performers"];
|
||||
|
||||
function sortPerformersByRelevance(
|
||||
input: string,
|
||||
performers: FindPerformersResult
|
||||
) {
|
||||
return sortByRelevance(
|
||||
input,
|
||||
performers,
|
||||
(p) => p.name,
|
||||
(p) => p.alias_list
|
||||
);
|
||||
}
|
||||
|
||||
const performerSelectSort = PatchFunction(
|
||||
"PerformerSelect.sort",
|
||||
sortPerformersByRelevance
|
||||
);
|
||||
|
||||
const _PerformerSelect: React.FC<
|
||||
IFilterProps & IFilterValueProps<Performer>
|
||||
> = (props) => {
|
||||
|
|
@ -61,11 +82,9 @@ const _PerformerSelect: React.FC<
|
|||
filter.sortBy = "name";
|
||||
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
||||
const query = await queryFindPerformersForSelect(filter);
|
||||
return sortByRelevance(
|
||||
return performerSelectSort(
|
||||
input,
|
||||
query.data.findPerformers.performers,
|
||||
(p) => p.name,
|
||||
(p) => p.alias_list
|
||||
query.data.findPerformers.performers.slice()
|
||||
).map((performer) => ({
|
||||
value: performer.id,
|
||||
object: performer,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { useCompare } from "src/hooks/state";
|
|||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { objectTitle } from "src/core/files";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
import {
|
||||
Criterion,
|
||||
CriterionValue,
|
||||
|
|
@ -48,6 +48,21 @@ type ExtraSceneProps = {
|
|||
extraCriteria?: Array<Criterion<CriterionValue>>;
|
||||
};
|
||||
|
||||
type FindScenesResult = Awaited<
|
||||
ReturnType<typeof queryFindScenesForSelect>
|
||||
>["data"]["findScenes"]["scenes"];
|
||||
|
||||
function sortScenesByRelevance(input: string, scenes: FindScenesResult) {
|
||||
return sortByRelevance(input, scenes, objectTitle, (s) => {
|
||||
return s.files.map((f) => f.path);
|
||||
});
|
||||
}
|
||||
|
||||
const sceneSelectSort = PatchFunction(
|
||||
"SceneSelect.sort",
|
||||
sortScenesByRelevance
|
||||
);
|
||||
|
||||
const _SceneSelect: React.FC<
|
||||
IFilterProps & IFilterValueProps<Scene> & ExtraSceneProps
|
||||
> = (props) => {
|
||||
|
|
@ -77,9 +92,7 @@ const _SceneSelect: React.FC<
|
|||
return !exclude.includes(scene.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(input, ret, objectTitle, (s) => {
|
||||
return s.files.map((f) => f.path);
|
||||
}).map((scene) => ({
|
||||
return sceneSelectSort(input, ret).map((scene) => ({
|
||||
value: scene.id,
|
||||
object: scene,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { useCompare } from "src/hooks/state";
|
||||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
|
||||
export type SelectObject = {
|
||||
id: string;
|
||||
|
|
@ -38,6 +38,24 @@ export type SelectObject = {
|
|||
export type Studio = Pick<GQL.Studio, "id" | "name" | "aliases" | "image_path">;
|
||||
type Option = SelectOption<Studio>;
|
||||
|
||||
type FindStudiosResult = Awaited<
|
||||
ReturnType<typeof queryFindStudiosForSelect>
|
||||
>["data"]["findStudios"]["studios"];
|
||||
|
||||
function sortStudiosByRelevance(input: string, studios: FindStudiosResult) {
|
||||
return sortByRelevance(
|
||||
input,
|
||||
studios,
|
||||
(s) => s.name,
|
||||
(s) => s.aliases
|
||||
);
|
||||
}
|
||||
|
||||
const studioSelectSort = PatchFunction(
|
||||
"StudioSelect.sort",
|
||||
sortStudiosByRelevance
|
||||
);
|
||||
|
||||
const _StudioSelect: React.FC<
|
||||
IFilterProps &
|
||||
IFilterValueProps<Studio> & {
|
||||
|
|
@ -70,12 +88,7 @@ const _StudioSelect: React.FC<
|
|||
return !exclude.includes(studio.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(
|
||||
input,
|
||||
ret,
|
||||
(s) => s.name,
|
||||
(s) => s.aliases
|
||||
).map((studio) => ({
|
||||
return studioSelectSort(input, ret).map((studio) => ({
|
||||
value: studio.id,
|
||||
object: studio,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import { useCompare } from "src/hooks/state";
|
|||
import { TagPopover } from "./TagPopover";
|
||||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
|
||||
export type SelectObject = {
|
||||
id: string;
|
||||
|
|
@ -39,6 +39,21 @@ export type SelectObject = {
|
|||
export type Tag = Pick<GQL.Tag, "id" | "name" | "aliases" | "image_path">;
|
||||
type Option = SelectOption<Tag>;
|
||||
|
||||
type FindTagsResult = Awaited<
|
||||
ReturnType<typeof queryFindTagsForSelect>
|
||||
>["data"]["findTags"]["tags"];
|
||||
|
||||
function sortTagsByRelevance(input: string, tags: FindTagsResult) {
|
||||
return sortByRelevance(
|
||||
input,
|
||||
tags,
|
||||
(t) => t.name,
|
||||
(t) => t.aliases
|
||||
);
|
||||
}
|
||||
|
||||
const tagSelectSort = PatchFunction("TagSelect.sort", sortTagsByRelevance);
|
||||
|
||||
const _TagSelect: React.FC<
|
||||
IFilterProps &
|
||||
IFilterValueProps<Tag> & {
|
||||
|
|
@ -71,12 +86,7 @@ const _TagSelect: React.FC<
|
|||
return !exclude.includes(tag.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(
|
||||
input,
|
||||
ret,
|
||||
(t) => t.name,
|
||||
(t) => t.aliases
|
||||
).map((tag) => ({
|
||||
return tagSelectSort(input, ret).map((tag) => ({
|
||||
value: tag.id,
|
||||
object: tag,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -137,7 +137,39 @@ Registers an after function. An after function is called after the render functi
|
|||
|
||||
Returns `void`.
|
||||
|
||||
#### `PluginApi.Event`
|
||||
#### Patchable components and functions
|
||||
|
||||
- `CountrySelect`
|
||||
- `DateInput`
|
||||
- `FolderSelect`
|
||||
- `GalleryIDSelect`
|
||||
- `GallerySelect`
|
||||
- `GallerySelect.sort`
|
||||
- `Icon`
|
||||
- `MovieIDSelect`
|
||||
- `MovieSelect`
|
||||
- `MovieSelect.sort`
|
||||
- `PerformerIDSelect`
|
||||
- `PerformerSelect`
|
||||
- `PerformerSelect.sort`
|
||||
- `PluginRoutes`
|
||||
- `SceneCard`
|
||||
- `SceneCard.Details`
|
||||
- `SceneCard.Image`
|
||||
- `SceneCard.Overlays`
|
||||
- `SceneCard.Popovers`
|
||||
- `SceneIDSelect`
|
||||
- `SceneSelect`
|
||||
- `SceneSelect.sort`
|
||||
- `Setting`
|
||||
- `StudioIDSelect`
|
||||
- `StudioSelect`
|
||||
- `StudioSelect.sort`
|
||||
- `TagIDSelect`
|
||||
- `TagSelect`
|
||||
- `TagSelect.sort`
|
||||
|
||||
### `PluginApi.Event`
|
||||
|
||||
Allows plugins to listen for Stash's events.
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ export function after(component: string, fn: Function) {
|
|||
afterFns[component].push(fn);
|
||||
}
|
||||
|
||||
export function RegisterComponent(component: string, fn: Function) {
|
||||
export function RegisterComponent<T extends Function>(
|
||||
component: string,
|
||||
fn: T
|
||||
) {
|
||||
// register with the plugin api
|
||||
if (components[component]) {
|
||||
throw new Error("Component " + component + " has already been registered");
|
||||
|
|
@ -49,7 +52,7 @@ export function RegisterComponent(component: string, fn: Function) {
|
|||
}
|
||||
|
||||
// patches a function to implement the before/instead/after functionality
|
||||
export function PatchFunction(name: string, fn: Function) {
|
||||
export function PatchFunction<T extends Function>(name: string, fn: T) {
|
||||
return new Proxy(fn, {
|
||||
apply(target, ctx, args) {
|
||||
let result;
|
||||
|
|
|
|||
Loading…
Reference in a new issue