= ({
}
function maybeRenderSetDefaultButton() {
- if (persistState === PersistanceLevel.ALL) {
+ if (view) {
return (
-
+
);
}
@@ -357,3 +367,36 @@ export const SavedFilterList: React.FC = ({
>
);
};
+
+export const SavedFilterDropdown: React.FC = (props) => {
+ const SavedFilterDropdownRef = React.forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+ >(({ style, className }: HTMLAttributes, ref) => (
+
+
+
+ ));
+ SavedFilterDropdownRef.displayName = "SavedFilterDropdown";
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+ );
+};
diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss
index 1c8d993a3..5b1e3b845 100644
--- a/ui/v2.5/src/components/List/styles.scss
+++ b/ui/v2.5/src/components/List/styles.scss
@@ -86,6 +86,8 @@ input[type="range"].zoom-slider {
.set-as-default-button {
float: right;
margin-right: 0.5rem;
+ padding: 0.25rem 0.5rem;
+ width: auto;
}
.LoadingIndicator {
diff --git a/ui/v2.5/src/components/List/util.ts b/ui/v2.5/src/components/List/util.ts
new file mode 100644
index 000000000..7ebe679b9
--- /dev/null
+++ b/ui/v2.5/src/components/List/util.ts
@@ -0,0 +1,32 @@
+import { useContext, useMemo } from "react";
+import { ListFilterModel } from "src/models/list-filter/filter";
+import * as GQL from "src/core/generated-graphql";
+import { ConfigurationContext } from "src/hooks/Config";
+import { View } from "./views";
+
+export function useDefaultFilter(mode: GQL.FilterMode, view?: View) {
+ const emptyFilter = useMemo(() => new ListFilterModel(mode), [mode]);
+ const { configuration: config, loading } = useContext(ConfigurationContext);
+
+ const defaultFilter = useMemo(() => {
+ if (view && config?.ui.defaultFilters?.[view]) {
+ const savedFilter = config.ui.defaultFilters[view]!;
+ const newFilter = emptyFilter.clone();
+
+ newFilter.currentPage = 1;
+ try {
+ newFilter.configureFromSavedFilter(savedFilter);
+ } catch (err) {
+ console.log(err);
+ // ignore
+ }
+ // #1507 - reset random seed when loaded
+ newFilter.randomSeed = -1;
+ return newFilter;
+ }
+ }, [view, config?.ui.defaultFilters, emptyFilter]);
+
+ const retFilter = loading ? undefined : defaultFilter ?? emptyFilter;
+
+ return { defaultFilter: retFilter, loading };
+}
diff --git a/ui/v2.5/src/components/List/views.ts b/ui/v2.5/src/components/List/views.ts
new file mode 100644
index 000000000..7e8880f9d
--- /dev/null
+++ b/ui/v2.5/src/components/List/views.ts
@@ -0,0 +1,34 @@
+export enum View {
+ Galleries = "galleries",
+ Images = "images",
+ Scenes = "scenes",
+ Movies = "movies",
+ Performers = "performers",
+ Tags = "tags",
+ SceneMarkers = "scene_markers",
+ Studios = "studios",
+
+ TagMarkers = "tag_markers",
+ TagGalleries = "tag_galleries",
+ TagScenes = "tag_scenes",
+ TagImages = "tag_images",
+ TagPerformers = "tag_performers",
+
+ PerformerScenes = "performer_scenes",
+ PerformerGalleries = "performer_galleries",
+ PerformerImages = "performer_images",
+ PerformerMovies = "performer_movies",
+ PerformerAppearsWith = "performer_appears_with",
+
+ StudioGalleries = "studio_galleries",
+ StudioImages = "studio_images",
+
+ GalleryImages = "gallery_images",
+
+ StudioScenes = "studio_scenes",
+ StudioMovies = "studio_movies",
+ StudioPerformers = "studio_performers",
+ StudioChildren = "studio_children",
+
+ MovieScenes = "movie_scenes",
+}
diff --git a/ui/v2.5/src/components/Movies/MovieDetails/MovieScenesPanel.tsx b/ui/v2.5/src/components/Movies/MovieDetails/MovieScenesPanel.tsx
index 9bfbf8b55..0ceff9b47 100644
--- a/ui/v2.5/src/components/Movies/MovieDetails/MovieScenesPanel.tsx
+++ b/ui/v2.5/src/components/Movies/MovieDetails/MovieScenesPanel.tsx
@@ -3,6 +3,7 @@ import * as GQL from "src/core/generated-graphql";
import { MoviesCriterion } from "src/models/list-filter/criteria/movies";
import { ListFilterModel } from "src/models/list-filter/filter";
import { SceneList } from "src/components/Scenes/SceneList";
+import { View } from "src/components/List/views";
interface IMovieScenesPanel {
active: boolean;
@@ -51,6 +52,7 @@ export const MovieScenesPanel: React.FC = ({
filterHook={filterHook}
defaultSort="movie_scene_number"
alterQuery={active}
+ view={View.MovieScenes}
/>
);
}
diff --git a/ui/v2.5/src/components/Movies/MovieList.tsx b/ui/v2.5/src/components/Movies/MovieList.tsx
index 55b34a783..8b42a3b73 100644
--- a/ui/v2.5/src/components/Movies/MovieList.tsx
+++ b/ui/v2.5/src/components/Movies/MovieList.tsx
@@ -11,15 +11,12 @@ import {
useFindMovies,
useMoviesDestroy,
} from "src/core/StashService";
-import {
- makeItemList,
- PersistanceLevel,
- showWhenSelected,
-} from "../List/ItemList";
+import { makeItemList, showWhenSelected } from "../List/ItemList";
import { ExportDialog } from "../Shared/ExportDialog";
import { DeleteEntityDialog } from "../Shared/DeleteEntityDialog";
import { MovieCardGrid } from "./MovieCardGrid";
import { EditMoviesDialog } from "./EditMoviesDialog";
+import { View } from "../List/views";
const MovieItemList = makeItemList({
filterMode: GQL.FilterMode.Movies,
@@ -34,10 +31,15 @@ const MovieItemList = makeItemList({
interface IMovieList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
+ view?: View;
alterQuery?: boolean;
}
-export const MovieList: React.FC = ({ filterHook, alterQuery }) => {
+export const MovieList: React.FC = ({
+ filterHook,
+ alterQuery,
+ view,
+}) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -175,7 +177,7 @@ export const MovieList: React.FC = ({ filterHook, alterQuery }) => {
{
useScrollToTopOnMount();
- return ;
+ return ;
};
const MovieRoutes: React.FC = () => {
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerGalleriesPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerGalleriesPanel.tsx
index f1ea3db2c..4424ff740 100644
--- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerGalleriesPanel.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerGalleriesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { GalleryList } from "src/components/Galleries/GalleryList";
import { usePerformerFilterHook } from "src/core/performers";
+import { View } from "src/components/List/views";
interface IPerformerDetailsProps {
active: boolean;
@@ -13,5 +14,11 @@ export const PerformerGalleriesPanel: React.FC = ({
performer,
}) => {
const filterHook = usePerformerFilterHook(performer);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerImagesPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerImagesPanel.tsx
index 478f7027f..40c2d88b8 100644
--- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerImagesPanel.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerImagesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { ImageList } from "src/components/Images/ImageList";
import { usePerformerFilterHook } from "src/core/performers";
+import { View } from "src/components/List/views";
interface IPerformerImagesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const PerformerImagesPanel: React.FC = ({
performer,
}) => {
const filterHook = usePerformerFilterHook(performer);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerMoviesPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerMoviesPanel.tsx
index 4c417bac8..0f1c8b7d5 100644
--- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerMoviesPanel.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerMoviesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { MovieList } from "src/components/Movies/MovieList";
import { usePerformerFilterHook } from "src/core/performers";
+import { View } from "src/components/List/views";
interface IPerformerDetailsProps {
active: boolean;
@@ -13,5 +14,11 @@ export const PerformerMoviesPanel: React.FC = ({
performer,
}) => {
const filterHook = usePerformerFilterHook(performer);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScenesPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScenesPanel.tsx
index d05db77c4..5eca04f6c 100644
--- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScenesPanel.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScenesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { SceneList } from "src/components/Scenes/SceneList";
import { usePerformerFilterHook } from "src/core/performers";
+import { View } from "src/components/List/views";
interface IPerformerDetailsProps {
active: boolean;
@@ -13,5 +14,11 @@ export const PerformerScenesPanel: React.FC = ({
performer,
}) => {
const filterHook = usePerformerFilterHook(performer);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/performerAppearsWithPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/performerAppearsWithPanel.tsx
index a05ec5e9f..8806bd3ab 100644
--- a/ui/v2.5/src/components/Performers/PerformerDetails/performerAppearsWithPanel.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerDetails/performerAppearsWithPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { PerformerList } from "src/components/Performers/PerformerList";
import { usePerformerFilterHook } from "src/core/performers";
+import { View } from "src/components/List/views";
interface IPerformerDetailsProps {
active: boolean;
@@ -28,6 +29,7 @@ export const PerformerAppearsWithPanel: React.FC = ({
filterHook={filterHook}
extraCriteria={extraCriteria}
alterQuery={active}
+ view={View.PerformerAppearsWith}
/>
);
};
diff --git a/ui/v2.5/src/components/Performers/PerformerList.tsx b/ui/v2.5/src/components/Performers/PerformerList.tsx
index 257079bce..9dd0ff277 100644
--- a/ui/v2.5/src/components/Performers/PerformerList.tsx
+++ b/ui/v2.5/src/components/Performers/PerformerList.tsx
@@ -9,11 +9,7 @@ import {
useFindPerformers,
usePerformersDestroy,
} from "src/core/StashService";
-import {
- makeItemList,
- PersistanceLevel,
- showWhenSelected,
-} from "../List/ItemList";
+import { makeItemList, showWhenSelected } from "../List/ItemList";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { PerformerTagger } from "../Tagger/performers/PerformerTagger";
@@ -25,6 +21,7 @@ import { EditPerformersDialog } from "./EditPerformersDialog";
import { cmToImperial, cmToInches, kgToLbs } from "src/utils/units";
import TextUtils from "src/utils/text";
import { PerformerCardGrid } from "./PerformerCardGrid";
+import { View } from "../List/views";
const PerformerItemList = makeItemList({
filterMode: GQL.FilterMode.Performers,
@@ -162,14 +159,14 @@ export const FormatPenisLength = (penis_length?: number | null) => {
interface IPerformerList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
- persistState?: PersistanceLevel;
+ view?: View;
alterQuery?: boolean;
extraCriteria?: IPerformerCardExtraCriteria;
}
export const PerformerList: React.FC = ({
filterHook,
- persistState,
+ view,
alterQuery,
extraCriteria,
}) => {
@@ -325,7 +322,7 @@ export const PerformerList: React.FC = ({
{
useScrollToTopOnMount();
- return ;
+ return ;
};
const PerformerRoutes: React.FC = () => {
diff --git a/ui/v2.5/src/components/Scenes/SceneList.tsx b/ui/v2.5/src/components/Scenes/SceneList.tsx
index 89c52f429..4ffd25040 100644
--- a/ui/v2.5/src/components/Scenes/SceneList.tsx
+++ b/ui/v2.5/src/components/Scenes/SceneList.tsx
@@ -5,11 +5,7 @@ import { useHistory } from "react-router-dom";
import Mousetrap from "mousetrap";
import * as GQL from "src/core/generated-graphql";
import { queryFindScenes, useFindScenes } from "src/core/StashService";
-import {
- makeItemList,
- PersistanceLevel,
- showWhenSelected,
-} from "../List/ItemList";
+import { makeItemList, showWhenSelected } from "../List/ItemList";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { Tagger } from "../Tagger/scenes/SceneTagger";
@@ -28,6 +24,7 @@ import { faPlay } from "@fortawesome/free-solid-svg-icons";
import { SceneMergeModal } from "./SceneMergeDialog";
import { objectTitle } from "src/core/files";
import TextUtils from "src/utils/text";
+import { View } from "../List/views";
const SceneItemList = makeItemList({
filterMode: GQL.FilterMode.Scenes,
@@ -78,14 +75,14 @@ const SceneItemList = makeItemList({
interface ISceneList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
defaultSort?: string;
- persistState?: PersistanceLevel;
+ view?: View;
alterQuery?: boolean;
}
export const SceneList: React.FC = ({
filterHook,
defaultSort,
- persistState,
+ view,
alterQuery,
}) => {
const intl = useIntl();
@@ -357,7 +354,7 @@ export const SceneList: React.FC = ({
zoomable
selectable
filterHook={filterHook}
- persistState={persistState}
+ view={view}
alterQuery={alterQuery}
otherOperations={otherOperations}
addKeybinds={addKeybinds}
diff --git a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx
index 6de661671..b81a4aecf 100644
--- a/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx
+++ b/ui/v2.5/src/components/Scenes/SceneMarkerList.tsx
@@ -9,10 +9,11 @@ import {
useFindSceneMarkers,
} from "src/core/StashService";
import NavUtils from "src/utils/navigation";
-import { makeItemList, PersistanceLevel } from "../List/ItemList";
+import { makeItemList } from "../List/ItemList";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { MarkerWallPanel } from "../Wall/WallPanel";
+import { View } from "../List/views";
const SceneMarkerItemList = makeItemList({
filterMode: GQL.FilterMode.SceneMarkers,
@@ -27,11 +28,13 @@ const SceneMarkerItemList = makeItemList({
interface ISceneMarkerList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
+ view?: View;
alterQuery?: boolean;
}
export const SceneMarkerList: React.FC = ({
filterHook,
+ view,
alterQuery,
}) => {
const intl = useIntl();
@@ -96,7 +99,7 @@ export const SceneMarkerList: React.FC = ({
return (
import("./SceneList"));
const SceneMarkerList = lazyComponent(() => import("./SceneMarkerList"));
@@ -14,7 +14,7 @@ const SceneCreate = lazyComponent(() => import("./SceneDetails/SceneCreate"));
const Scenes: React.FC = () => {
useScrollToTopOnMount();
- return ;
+ return ;
};
const SceneMarkers: React.FC = () => {
@@ -24,7 +24,7 @@ const SceneMarkers: React.FC = () => {
return (
<>
-
+
>
);
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioChildrenPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioChildrenPanel.tsx
index 8048de66a..62d773a51 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioChildrenPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioChildrenPanel.tsx
@@ -3,6 +3,7 @@ import * as GQL from "src/core/generated-graphql";
import { ParentStudiosCriterion } from "src/models/list-filter/criteria/studios";
import { ListFilterModel } from "src/models/list-filter/filter";
import { StudioList } from "../StudioList";
+import { View } from "src/components/List/views";
interface IStudioChildrenPanel {
active: boolean;
@@ -45,5 +46,12 @@ export const StudioChildrenPanel: React.FC = ({
return filter;
}
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioGalleriesPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioGalleriesPanel.tsx
index 42aca05dc..2519d41a3 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioGalleriesPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioGalleriesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { GalleryList } from "src/components/Galleries/GalleryList";
import { useStudioFilterHook } from "src/core/studios";
+import { View } from "src/components/List/views";
interface IStudioGalleriesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const StudioGalleriesPanel: React.FC = ({
studio,
}) => {
const filterHook = useStudioFilterHook(studio);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioImagesPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioImagesPanel.tsx
index c2ecb4e0f..3e6f66f25 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioImagesPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioImagesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { useStudioFilterHook } from "src/core/studios";
import { ImageList } from "src/components/Images/ImageList";
+import { View } from "src/components/List/views";
interface IStudioImagesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const StudioImagesPanel: React.FC = ({
studio,
}) => {
const filterHook = useStudioFilterHook(studio);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioMoviesPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioMoviesPanel.tsx
index 93a820242..3127f5251 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioMoviesPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioMoviesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { MovieList } from "src/components/Movies/MovieList";
import { useStudioFilterHook } from "src/core/studios";
+import { View } from "src/components/List/views";
interface IStudioMoviesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const StudioMoviesPanel: React.FC = ({
studio,
}) => {
const filterHook = useStudioFilterHook(studio);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioPerformersPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioPerformersPanel.tsx
index e13c8b2ec..0eb146a1b 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioPerformersPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioPerformersPanel.tsx
@@ -3,6 +3,7 @@ import * as GQL from "src/core/generated-graphql";
import { useStudioFilterHook } from "src/core/studios";
import { PerformerList } from "src/components/Performers/PerformerList";
import { StudiosCriterion } from "src/models/list-filter/criteria/studios";
+import { View } from "src/components/List/views";
interface IStudioPerformersPanel {
active: boolean;
@@ -34,6 +35,7 @@ export const StudioPerformersPanel: React.FC = ({
filterHook={filterHook}
extraCriteria={extraCriteria}
alterQuery={active}
+ view={View.StudioPerformers}
/>
);
};
diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioScenesPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioScenesPanel.tsx
index 84ba8751d..47663f696 100644
--- a/ui/v2.5/src/components/Studios/StudioDetails/StudioScenesPanel.tsx
+++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioScenesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { SceneList } from "src/components/Scenes/SceneList";
import { useStudioFilterHook } from "src/core/studios";
+import { View } from "src/components/List/views";
interface IStudioScenesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const StudioScenesPanel: React.FC = ({
studio,
}) => {
const filterHook = useStudioFilterHook(studio);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx
index 4e75c6405..952a6a6ee 100644
--- a/ui/v2.5/src/components/Studios/StudioList.tsx
+++ b/ui/v2.5/src/components/Studios/StudioList.tsx
@@ -9,17 +9,14 @@ import {
useFindStudios,
useStudiosDestroy,
} from "src/core/StashService";
-import {
- makeItemList,
- PersistanceLevel,
- showWhenSelected,
-} from "../List/ItemList";
+import { makeItemList, showWhenSelected } from "../List/ItemList";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { ExportDialog } from "../Shared/ExportDialog";
import { DeleteEntityDialog } from "../Shared/DeleteEntityDialog";
import { StudioTagger } from "../Tagger/studios/StudioTagger";
import { StudioCardGrid } from "./StudioCardGrid";
+import { View } from "../List/views";
const StudioItemList = makeItemList({
filterMode: GQL.FilterMode.Studios,
@@ -35,12 +32,14 @@ const StudioItemList = makeItemList({
interface IStudioList {
fromParent?: boolean;
filterHook?: (filter: ListFilterModel) => ListFilterModel;
+ view?: View;
alterQuery?: boolean;
}
export const StudioList: React.FC = ({
fromParent,
filterHook,
+ view,
alterQuery,
}) => {
const intl = useIntl();
@@ -181,7 +180,7 @@ export const StudioList: React.FC = ({
{
useScrollToTopOnMount();
- return ;
+ return ;
};
const StudioRoutes: React.FC = () => {
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagGalleriesPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagGalleriesPanel.tsx
index 203715bfb..7d46c4e31 100644
--- a/ui/v2.5/src/components/Tags/TagDetails/TagGalleriesPanel.tsx
+++ b/ui/v2.5/src/components/Tags/TagDetails/TagGalleriesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { useTagFilterHook } from "src/core/tags";
import { GalleryList } from "src/components/Galleries/GalleryList";
+import { View } from "src/components/List/views";
interface ITagGalleriesPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const TagGalleriesPanel: React.FC = ({
tag,
}) => {
const filterHook = useTagFilterHook(tag);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagImagesPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagImagesPanel.tsx
index 1c6ea2ec9..61e235499 100644
--- a/ui/v2.5/src/components/Tags/TagDetails/TagImagesPanel.tsx
+++ b/ui/v2.5/src/components/Tags/TagDetails/TagImagesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { useTagFilterHook } from "src/core/tags";
import { ImageList } from "src/components/Images/ImageList";
+import { View } from "src/components/List/views";
interface ITagImagesPanel {
active: boolean;
@@ -10,5 +11,11 @@ interface ITagImagesPanel {
export const TagImagesPanel: React.FC = ({ active, tag }) => {
const filterHook = useTagFilterHook(tag);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagMarkersPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagMarkersPanel.tsx
index 95d2d5607..2bd4658d5 100644
--- a/ui/v2.5/src/components/Tags/TagDetails/TagMarkersPanel.tsx
+++ b/ui/v2.5/src/components/Tags/TagDetails/TagMarkersPanel.tsx
@@ -6,6 +6,7 @@ import {
TagsCriterionOption,
} from "src/models/list-filter/criteria/tags";
import { SceneMarkerList } from "src/components/Scenes/SceneMarkerList";
+import { View } from "src/components/List/views";
interface ITagMarkersPanel {
active: boolean;
@@ -52,5 +53,11 @@ export const TagMarkersPanel: React.FC = ({
return filter;
}
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagPerformersPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagPerformersPanel.tsx
index 255acaf64..192b89dcc 100644
--- a/ui/v2.5/src/components/Tags/TagDetails/TagPerformersPanel.tsx
+++ b/ui/v2.5/src/components/Tags/TagDetails/TagPerformersPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { useTagFilterHook } from "src/core/tags";
import { PerformerList } from "src/components/Performers/PerformerList";
+import { View } from "src/components/List/views";
interface ITagPerformersPanel {
active: boolean;
@@ -13,5 +14,11 @@ export const TagPerformersPanel: React.FC = ({
tag,
}) => {
const filterHook = useTagFilterHook(tag);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagScenesPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagScenesPanel.tsx
index 5de9ed916..39d850d35 100644
--- a/ui/v2.5/src/components/Tags/TagDetails/TagScenesPanel.tsx
+++ b/ui/v2.5/src/components/Tags/TagDetails/TagScenesPanel.tsx
@@ -2,6 +2,7 @@ import React from "react";
import * as GQL from "src/core/generated-graphql";
import { SceneList } from "src/components/Scenes/SceneList";
import { useTagFilterHook } from "src/core/tags";
+import { View } from "src/components/List/views";
interface ITagScenesPanel {
active: boolean;
@@ -10,5 +11,11 @@ interface ITagScenesPanel {
export const TagScenesPanel: React.FC = ({ active, tag }) => {
const filterHook = useTagFilterHook(tag);
- return ;
+ return (
+
+ );
};
diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx
index 2458a273b..42a6316f9 100644
--- a/ui/v2.5/src/components/Tags/TagList.tsx
+++ b/ui/v2.5/src/components/Tags/TagList.tsx
@@ -3,11 +3,7 @@ import cloneDeep from "lodash-es/cloneDeep";
import Mousetrap from "mousetrap";
import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
-import {
- makeItemList,
- PersistanceLevel,
- showWhenSelected,
-} from "../List/ItemList";
+import { makeItemList, showWhenSelected } from "../List/ItemList";
import { Button } from "react-bootstrap";
import { Link, useHistory } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
@@ -29,6 +25,7 @@ import { tagRelationHook } from "../../core/tags";
import { faTrashAlt } from "@fortawesome/free-solid-svg-icons";
import { TagCardGrid } from "./TagCardGrid";
import { EditTagsDialog } from "./EditTagsDialog";
+import { View } from "../List/views";
interface ITagList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
@@ -363,7 +360,7 @@ export const TagList: React.FC = ({ filterHook, alterQuery }) => {
zoomable
defaultZoomIndex={0}
filterHook={filterHook}
- persistState={PersistanceLevel.ALL}
+ view={View.Tags}
alterQuery={alterQuery}
otherOperations={otherOperations}
addKeybinds={addKeybinds}
diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts
index 251df72f5..d7518b6c7 100644
--- a/ui/v2.5/src/core/StashService.ts
+++ b/ui/v2.5/src/core/StashService.ts
@@ -452,11 +452,6 @@ export const useFindSavedFilters = (mode?: GQL.FilterMode) =>
variables: { mode },
});
-export const useFindDefaultFilter = (mode: GQL.FilterMode) =>
- GQL.useFindDefaultFilterQuery({
- variables: { mode },
- });
-
/// Object Mutations
// Increases/decreases the given field of the Stats query by diff
@@ -1956,15 +1951,6 @@ export const useSaveFilter = () =>
},
});
-export const useSetDefaultFilter = () =>
- GQL.useSetDefaultFilterMutation({
- update(cache, result) {
- if (!result.data?.setDefaultFilter) return;
-
- evictQueries(cache, [GQL.FindDefaultFilterDocument]);
- },
- });
-
export const useSavedFilterDestroy = () =>
GQL.useDestroySavedFilterMutation({
update(cache, result, { variables }) {
@@ -1972,8 +1958,6 @@ export const useSavedFilterDestroy = () =>
const obj = { __typename: "SavedFilter", id: variables.input.id };
deleteObject(cache, obj, GQL.FindSavedFilterDocument);
-
- evictQueries(cache, [GQL.FindDefaultFilterDocument]);
},
});
diff --git a/ui/v2.5/src/core/config.ts b/ui/v2.5/src/core/config.ts
index 1e0d1030a..3e7585df7 100644
--- a/ui/v2.5/src/core/config.ts
+++ b/ui/v2.5/src/core/config.ts
@@ -2,7 +2,12 @@ import { IntlShape } from "react-intl";
import { ITypename } from "src/utils/data";
import { ImageWallOptions } from "src/utils/imageWall";
import { RatingSystemOptions } from "src/utils/rating";
-import { FilterMode, SortDirectionEnum } from "./generated-graphql";
+import {
+ FilterMode,
+ SavedFilterDataFragment,
+ SortDirectionEnum,
+} from "./generated-graphql";
+import { View } from "src/components/List/views";
// NOTE: double capitals aren't converted correctly in the backend
@@ -25,6 +30,10 @@ export interface ICustomFilter extends ITypename {
direction: SortDirectionEnum;
}
+export type DefaultFilters = {
+ [P in View]?: SavedFilterDataFragment;
+};
+
export type FrontPageContent = ISavedFilterRow | ICustomFilter;
export const defaultMaxOptionsShown = 200;
@@ -86,6 +95,8 @@ export interface IUIConfig {
advancedMode?: boolean;
taskDefaults?: Record;
+
+ defaultFilters?: DefaultFilters;
}
export function getFrontPageContent(
diff --git a/ui/v2.5/src/core/createClient.ts b/ui/v2.5/src/core/createClient.ts
index 045f27676..e5f502cca 100644
--- a/ui/v2.5/src/core/createClient.ts
+++ b/ui/v2.5/src/core/createClient.ts
@@ -61,9 +61,6 @@ const typePolicies: TypePolicies = {
findSavedFilter: {
read: readReference("SavedFilter"),
},
- findDefaultFilter: {
- read: readDanglingNull,
- },
},
},
Scene: {
diff --git a/ui/v2.5/src/docs/en/MigrationNotes/60.md b/ui/v2.5/src/docs/en/MigrationNotes/60.md
new file mode 100644
index 000000000..5d2feaf0e
--- /dev/null
+++ b/ui/v2.5/src/docs/en/MigrationNotes/60.md
@@ -0,0 +1 @@
+This migration moves default filters from the database into the configuration file. A backup of the current `config.yml` will be created in the same directory with the name `config.yml.59.`. The exact filename is written to the log.
\ No newline at end of file
diff --git a/ui/v2.5/src/docs/en/MigrationNotes/index.ts b/ui/v2.5/src/docs/en/MigrationNotes/index.ts
index dd4f3fcd1..5a201a091 100644
--- a/ui/v2.5/src/docs/en/MigrationNotes/index.ts
+++ b/ui/v2.5/src/docs/en/MigrationNotes/index.ts
@@ -2,10 +2,12 @@ import migration32 from "./32.md";
import migration39 from "./39.md";
import migration48 from "./48.md";
import migration58 from "./58.md";
+import migration60 from "./60.md";
export const migrationNotes: Record = {
32: migration32,
39: migration39,
48: migration48,
58: migration58,
+ 60: migration60,
};
diff --git a/ui/v2.5/src/models/list-filter/filter.ts b/ui/v2.5/src/models/list-filter/filter.ts
index cd84eaada..794fd2a7e 100644
--- a/ui/v2.5/src/models/list-filter/filter.ts
+++ b/ui/v2.5/src/models/list-filter/filter.ts
@@ -17,6 +17,7 @@ import {
SavedObjectFilter,
SavedUIOptions,
} from "./types";
+import { ListFilterOptions } from "./filter-options";
interface IDecodedParams {
perPage?: number;
@@ -49,7 +50,8 @@ const DEFAULT_PARAMS = {
// TODO: handle customCriteria
export class ListFilterModel {
- public mode: FilterMode;
+ public readonly mode: FilterMode;
+ public readonly options: ListFilterOptions;
private config?: ConfigDataFragment;
public searchTerm: string = "";
public currentPage = DEFAULT_PARAMS.currentPage;
@@ -65,19 +67,18 @@ export class ListFilterModel {
public constructor(
mode: FilterMode,
config?: ConfigDataFragment,
- defaultSort?: string,
- defaultDisplayMode?: DisplayMode,
defaultZoomIndex?: number
) {
this.mode = mode;
this.config = config;
- this.sortBy = defaultSort;
+ this.options = getFilterOptions(mode);
+ const { defaultSortBy, displayModeOptions } = this.options;
+
+ this.sortBy = defaultSortBy;
if (this.sortBy === "date") {
this.sortDirection = SortDirectionEnum.Desc;
}
- if (defaultDisplayMode !== undefined) {
- this.displayMode = defaultDisplayMode;
- }
+ this.displayMode = displayModeOptions[0];
if (defaultZoomIndex !== undefined) {
this.defaultZoomIndex = defaultZoomIndex;
this.zoomIndex = defaultZoomIndex;
diff --git a/ui/v2.5/src/models/sceneQueue.ts b/ui/v2.5/src/models/sceneQueue.ts
index 83b7e7820..ceec69090 100644
--- a/ui/v2.5/src/models/sceneQueue.ts
+++ b/ui/v2.5/src/models/sceneQueue.ts
@@ -1,6 +1,5 @@
import { FilterMode, Scene } from "src/core/generated-graphql";
import { ListFilterModel } from "./list-filter/filter";
-import { SceneListFilterOptions } from "./list-filter/scenes";
import { INamedObject } from "src/utils/navigation";
export type QueuedScene = Pick & {
@@ -97,11 +96,7 @@ export class SceneQueue {
c: params.getAll("qfc"),
};
const decoded = ListFilterModel.decodeParams(translated);
- const query = new ListFilterModel(
- FilterMode.Scenes,
- undefined,
- SceneListFilterOptions.defaultSortBy
- );
+ const query = new ListFilterModel(FilterMode.Scenes);
query.configureFromDecodedParams(decoded);
ret.query = query;
} else if (params.has("qs")) {
diff --git a/ui/v2.5/src/pluginApi.d.ts b/ui/v2.5/src/pluginApi.d.ts
index 4967eedbf..ae292ceee 100644
--- a/ui/v2.5/src/pluginApi.d.ts
+++ b/ui/v2.5/src/pluginApi.d.ts
@@ -39,7 +39,6 @@ declare namespace PluginApi {
const EnableDlnaDocument: { [key: string]: any };
const ExportObjectsDocument: { [key: string]: any };
const FilterMode: { [key: string]: any };
- const FindDefaultFilterDocument: { [key: string]: any };
const FindDuplicateScenesDocument: { [key: string]: any };
const FindGalleriesDocument: { [key: string]: any };
const FindGalleriesForSelectDocument: { [key: string]: any };
@@ -208,7 +207,6 @@ declare namespace PluginApi {
const SelectPerformerDataFragmentDoc: { [key: string]: any };
const SelectStudioDataFragmentDoc: { [key: string]: any };
const SelectTagDataFragmentDoc: { [key: string]: any };
- const SetDefaultFilterDocument: { [key: string]: any };
const SetPluginsEnabledDocument: { [key: string]: any };
const SetupDocument: { [key: string]: any };
const SlimGalleryDataFragmentDoc: { [key: string]: any };
@@ -254,7 +252,6 @@ declare namespace PluginApi {
function refetchConfigurationQuery(...args: any[]): any;
function refetchDirectoryQuery(...args: any[]): any;
function refetchDlnaStatusQuery(...args: any[]): any;
- function refetchFindDefaultFilterQuery(...args: any[]): any;
function refetchFindDuplicateScenesQuery(...args: any[]): any;
function refetchFindGalleriesForSelectQuery(...args: any[]): any;
function refetchFindGalleriesQuery(...args: any[]): any;
@@ -349,9 +346,6 @@ declare namespace PluginApi {
function useDlnaStatusSuspenseQuery(...args: any[]): any;
function useEnableDlnaMutation(...args: any[]): any;
function useExportObjectsMutation(...args: any[]): any;
- function useFindDefaultFilterLazyQuery(...args: any[]): any;
- function useFindDefaultFilterQuery(...args: any[]): any;
- function useFindDefaultFilterSuspenseQuery(...args: any[]): any;
function useFindDuplicateScenesLazyQuery(...args: any[]): any;
function useFindDuplicateScenesQuery(...args: any[]): any;
function useFindDuplicateScenesSuspenseQuery(...args: any[]): any;