mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Customize recommendations (#2592)
* refactored common code in recommendation row * Implement front page options in config * Allow customisation from front page * Rename recommendations to front page * Add generic UI settings * Support adding premade filters Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
168
ui/v2.5/src/components/FrontPage/Control.tsx
Normal file
168
ui/v2.5/src/components/FrontPage/Control.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import {
|
||||
FrontPageContent,
|
||||
ICustomFilter,
|
||||
ISavedFilterRow,
|
||||
} from "src/core/config";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { useFindSavedFilter } from "src/core/StashService";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { GalleryRecommendationRow } from "../Galleries/GalleryRecommendationRow";
|
||||
import { ImageRecommendationRow } from "../Images/ImageRecommendationRow";
|
||||
import { MovieRecommendationRow } from "../Movies/MovieRecommendationRow";
|
||||
import { PerformerRecommendationRow } from "../Performers/PerformerRecommendationRow";
|
||||
import { SceneRecommendationRow } from "../Scenes/SceneRecommendationRow";
|
||||
import { StudioRecommendationRow } from "../Studios/StudioRecommendationRow";
|
||||
|
||||
interface IFilter {
|
||||
mode: GQL.FilterMode;
|
||||
filter: ListFilterModel;
|
||||
header: string;
|
||||
}
|
||||
|
||||
const RecommendationRow: React.FC<IFilter> = ({ mode, filter, header }) => {
|
||||
function isTouchEnabled() {
|
||||
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
||||
}
|
||||
|
||||
const isTouch = isTouchEnabled();
|
||||
|
||||
switch (mode) {
|
||||
case GQL.FilterMode.Scenes:
|
||||
return (
|
||||
<SceneRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
case GQL.FilterMode.Studios:
|
||||
return (
|
||||
<StudioRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
case GQL.FilterMode.Movies:
|
||||
return (
|
||||
<MovieRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
case GQL.FilterMode.Performers:
|
||||
return (
|
||||
<PerformerRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
case GQL.FilterMode.Galleries:
|
||||
return (
|
||||
<GalleryRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
case GQL.FilterMode.Images:
|
||||
return (
|
||||
<ImageRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
interface ISavedFilterResults {
|
||||
savedFilterID: string;
|
||||
}
|
||||
|
||||
const SavedFilterResults: React.FC<ISavedFilterResults> = ({
|
||||
savedFilterID,
|
||||
}) => {
|
||||
const { loading, data } = useFindSavedFilter(savedFilterID.toString());
|
||||
|
||||
const filter = useMemo(() => {
|
||||
if (!data?.findSavedFilter) return;
|
||||
|
||||
const { mode, filter: filterJSON } = data.findSavedFilter;
|
||||
|
||||
const ret = new ListFilterModel(mode);
|
||||
ret.currentPage = 1;
|
||||
ret.configureFromQueryParameters(JSON.parse(filterJSON));
|
||||
ret.randomSeed = -1;
|
||||
return ret;
|
||||
}, [data?.findSavedFilter]);
|
||||
|
||||
if (loading || !data?.findSavedFilter || !filter) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const { name, mode } = data.findSavedFilter;
|
||||
|
||||
return <RecommendationRow mode={mode} filter={filter} header={name} />;
|
||||
};
|
||||
|
||||
interface ICustomFilterProps {
|
||||
customFilter: ICustomFilter;
|
||||
}
|
||||
|
||||
const CustomFilterResults: React.FC<ICustomFilterProps> = ({
|
||||
customFilter,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const filter = useMemo(() => {
|
||||
const itemsPerPage = 25;
|
||||
const ret = new ListFilterModel(customFilter.mode);
|
||||
ret.sortBy = customFilter.sortBy;
|
||||
ret.sortDirection = customFilter.direction;
|
||||
ret.itemsPerPage = itemsPerPage;
|
||||
ret.currentPage = 1;
|
||||
ret.randomSeed = -1;
|
||||
return ret;
|
||||
}, [customFilter]);
|
||||
|
||||
const header = customFilter.message
|
||||
? intl.formatMessage(
|
||||
{ id: customFilter.message.id },
|
||||
customFilter.message.values
|
||||
)
|
||||
: customFilter.title ?? "";
|
||||
|
||||
return (
|
||||
<RecommendationRow
|
||||
mode={customFilter.mode}
|
||||
filter={filter}
|
||||
header={header}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
content: FrontPageContent;
|
||||
}
|
||||
|
||||
export const Control: React.FC<IProps> = ({ content }) => {
|
||||
switch (content.__typename) {
|
||||
case "SavedFilter":
|
||||
return (
|
||||
<SavedFilterResults
|
||||
savedFilterID={(content as ISavedFilterRow).savedFilterId.toString()}
|
||||
/>
|
||||
);
|
||||
case "CustomFilter":
|
||||
return <CustomFilterResults customFilter={content as ICustomFilter} />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user