Reader settings are now saved in localStorage

This commit is contained in:
DrMint 2022-10-22 01:16:47 +02:00
parent 89ad4620d6
commit 25d99ee294
11 changed files with 239 additions and 129 deletions

View File

@ -19,7 +19,7 @@ import { useLocalData } from "contexts/LocalDataContext";
export const MainPanel = (): JSX.Element => {
const is3ColumnsLayout = useIs3ColumnsLayout();
const { mainPanelReduced = false, toggleMainPanelReduced, setConfigPanelOpen } = useAppLayout();
const { mainPanelReduced, toggleMainPanelReduced, setConfigPanelOpen } = useAppLayout();
const { langui } = useLocalData();
return (

View File

@ -1,4 +1,12 @@
import React, { ReactNode, useContext, useEffect, useState } from "react";
import React, {
createContext,
Dispatch,
ReactNode,
SetStateAction,
useContext,
useEffect,
useState,
} from "react";
import { useRouter } from "next/router";
import { useLocalStorage, useSessionStorage } from "usehooks-ts";
import { isDefined } from "helpers/others";
@ -9,27 +17,27 @@ import { useScrollIntoView } from "hooks/useScrollIntoView";
interface AppLayoutState {
subPanelOpen: boolean;
toggleSubPanelOpen: () => void;
setSubPanelOpen: React.Dispatch<React.SetStateAction<AppLayoutState["subPanelOpen"]>>;
setSubPanelOpen: Dispatch<SetStateAction<AppLayoutState["subPanelOpen"]>>;
configPanelOpen: boolean;
toggleConfigPanelOpen: () => void;
setConfigPanelOpen: React.Dispatch<React.SetStateAction<AppLayoutState["configPanelOpen"]>>;
setConfigPanelOpen: Dispatch<SetStateAction<AppLayoutState["configPanelOpen"]>>;
mainPanelReduced: boolean;
toggleMainPanelReduced: () => void;
setMainPanelReduced: React.Dispatch<React.SetStateAction<AppLayoutState["mainPanelReduced"]>>;
setMainPanelReduced: Dispatch<SetStateAction<AppLayoutState["mainPanelReduced"]>>;
mainPanelOpen: boolean;
toggleMainPanelOpen: () => void;
setMainPanelOpen: React.Dispatch<React.SetStateAction<AppLayoutState["mainPanelOpen"]>>;
setMainPanelOpen: Dispatch<SetStateAction<AppLayoutState["mainPanelOpen"]>>;
menuGestures: boolean;
toggleMenuGestures: () => void;
setMenuGestures: React.Dispatch<React.SetStateAction<AppLayoutState["menuGestures"]>>;
setMenuGestures: Dispatch<SetStateAction<AppLayoutState["menuGestures"]>>;
hasDisgardedSafariWarning: boolean;
setHasDisgardedSafariWarning: React.Dispatch<
React.SetStateAction<AppLayoutState["hasDisgardedSafariWarning"]>
setHasDisgardedSafariWarning: Dispatch<
SetStateAction<AppLayoutState["hasDisgardedSafariWarning"]>
>;
}
@ -58,7 +66,7 @@ const initialState: RequiredNonNullable<AppLayoutState> = {
setHasDisgardedSafariWarning: () => null,
};
const AppLayoutContext = React.createContext<AppLayoutState>(initialState);
const AppLayoutContext = createContext<AppLayoutState>(initialState);
export const useAppLayout = (): AppLayoutState => useContext(AppLayoutContext);

View File

@ -1,17 +1,22 @@
import React, { ReactNode, useContext, useState } from "react";
import React, {
createContext,
Dispatch,
ReactNode,
SetStateAction,
useContext,
useState,
} from "react";
import { RequiredNonNullable } from "types/types";
interface ContainerQueriesState {
screenWidth: number;
setScreenWidth: React.Dispatch<React.SetStateAction<ContainerQueriesState["screenWidth"]>>;
setScreenWidth: Dispatch<SetStateAction<ContainerQueriesState["screenWidth"]>>;
contentPanelWidth: number;
setContentPanelWidth: React.Dispatch<
React.SetStateAction<ContainerQueriesState["contentPanelWidth"]>
>;
setContentPanelWidth: Dispatch<SetStateAction<ContainerQueriesState["contentPanelWidth"]>>;
subPanelWidth: number;
setSubPanelWidth: React.Dispatch<React.SetStateAction<ContainerQueriesState["subPanelWidth"]>>;
setSubPanelWidth: Dispatch<SetStateAction<ContainerQueriesState["subPanelWidth"]>>;
}
const initialState: RequiredNonNullable<ContainerQueriesState> = {
@ -25,7 +30,7 @@ const initialState: RequiredNonNullable<ContainerQueriesState> = {
setSubPanelWidth: () => null,
};
const ContainerQueriesContext = React.createContext<ContainerQueriesState>(initialState);
const ContainerQueriesContext = createContext<ContainerQueriesState>(initialState);
export const useContainerQueries = (): ContainerQueriesState => useContext(ContainerQueriesContext);

View File

@ -1,4 +1,4 @@
import React, { ReactNode, useContext, useMemo } from "react";
import React, { createContext, ReactNode, useContext, useMemo } from "react";
import { useRouter } from "next/router";
import { useFetch } from "usehooks-ts";
import {
@ -29,7 +29,7 @@ const initialState: RequiredNonNullable<LocalDataState> = {
langui: {},
};
const LocalDataContext = React.createContext<LocalDataState>(initialState);
const LocalDataContext = createContext<LocalDataState>(initialState);
export const useLocalData = (): LocalDataState => useContext(LocalDataContext);

View File

@ -1,11 +1,18 @@
import React, { ReactNode, useContext, useState } from "react";
import React, {
createContext,
Dispatch,
ReactNode,
SetStateAction,
useContext,
useState,
} from "react";
import { RequiredNonNullable } from "types/types";
interface TerminalState {
previousLines: string[];
previousCommands: string[];
setPreviousLines: React.Dispatch<React.SetStateAction<TerminalState["previousLines"]>>;
setPreviousCommands: React.Dispatch<React.SetStateAction<TerminalState["previousCommands"]>>;
setPreviousLines: Dispatch<SetStateAction<TerminalState["previousLines"]>>;
setPreviousCommands: Dispatch<SetStateAction<TerminalState["previousCommands"]>>;
}
const initialState: RequiredNonNullable<TerminalState> = {
@ -15,7 +22,7 @@ const initialState: RequiredNonNullable<TerminalState> = {
setPreviousCommands: () => null,
};
const TerminalContext = React.createContext<TerminalState>(initialState);
const TerminalContext = createContext<TerminalState>(initialState);
export const useTerminalContext = (): TerminalState => useContext(TerminalContext);

View File

@ -1,5 +1,13 @@
import { useLocalStorage } from "usehooks-ts";
import React, { ReactNode, useContext, useEffect, useLayoutEffect } from "react";
import React, {
createContext,
Dispatch,
ReactNode,
SetStateAction,
useContext,
useEffect,
useLayoutEffect,
} from "react";
import { useRouter } from "next/router";
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
import { RequiredNonNullable } from "types/types";
@ -8,32 +16,28 @@ import { useDarkMode } from "hooks/useDarkMode";
interface UserSettingsState {
fontSize: number;
setFontSize: React.Dispatch<React.SetStateAction<UserSettingsState["fontSize"]>>;
setFontSize: Dispatch<SetStateAction<UserSettingsState["fontSize"]>>;
darkMode: boolean;
toggleDarkMode: () => void;
setDarkMode: React.Dispatch<React.SetStateAction<UserSettingsState["darkMode"]>>;
setDarkMode: Dispatch<SetStateAction<UserSettingsState["darkMode"]>>;
selectedThemeMode: boolean;
toggleSelectedThemeMode: () => void;
setSelectedThemeMode: React.Dispatch<
React.SetStateAction<UserSettingsState["selectedThemeMode"]>
>;
setSelectedThemeMode: Dispatch<SetStateAction<UserSettingsState["selectedThemeMode"]>>;
dyslexic: boolean;
toggleDyslexic: () => void;
setDyslexic: React.Dispatch<React.SetStateAction<UserSettingsState["dyslexic"]>>;
setDyslexic: Dispatch<SetStateAction<UserSettingsState["dyslexic"]>>;
currency: string;
setCurrency: React.Dispatch<React.SetStateAction<UserSettingsState["currency"]>>;
setCurrency: Dispatch<SetStateAction<UserSettingsState["currency"]>>;
playerName: string;
setPlayerName: React.Dispatch<React.SetStateAction<UserSettingsState["playerName"]>>;
setPlayerName: Dispatch<SetStateAction<UserSettingsState["playerName"]>>;
preferredLanguages: string[];
setPreferredLanguages: React.Dispatch<
React.SetStateAction<UserSettingsState["preferredLanguages"]>
>;
setPreferredLanguages: Dispatch<SetStateAction<UserSettingsState["preferredLanguages"]>>;
}
const initialState: RequiredNonNullable<UserSettingsState> = {
@ -62,7 +66,7 @@ const initialState: RequiredNonNullable<UserSettingsState> = {
setPreferredLanguages: () => null,
};
const UserSettingsContext = React.createContext<UserSettingsState>(initialState);
const UserSettingsContext = createContext<UserSettingsState>(initialState);
export const useUserSettings = (): UserSettingsState => useContext(UserSettingsContext);

View File

@ -1,16 +1,11 @@
import { useEffect } from "react";
import { Dispatch, SetStateAction, useEffect } from "react";
import { useLocalStorage } from "usehooks-ts";
import { usePrefersDarkMode } from "./useMediaQuery";
export const useDarkMode = (
key: string,
initialValue: boolean
): [
boolean,
boolean,
React.Dispatch<React.SetStateAction<boolean>>,
React.Dispatch<React.SetStateAction<boolean>>
] => {
): [boolean, boolean, Dispatch<SetStateAction<boolean>>, Dispatch<SetStateAction<boolean>>] => {
const [darkMode, setDarkMode] = useLocalStorage(key, initialValue);
const prefersDarkMode = usePrefersDarkMode();
const [selectedThemeMode, setSelectedThemeMode] = useLocalStorage("selectedThemeMode", false);

View File

@ -1,11 +1,10 @@
import { useLocalStorage } from "usehooks-ts";
import { Dispatch, SetStateAction } from "react";
import { LibraryItemUserStatus } from "types/types";
export const useLibraryItemUserStatus = (): {
libraryItemUserStatus: Record<string, LibraryItemUserStatus>;
setLibraryItemUserStatus: React.Dispatch<
React.SetStateAction<Record<string, LibraryItemUserStatus>>
>;
setLibraryItemUserStatus: Dispatch<SetStateAction<Record<string, LibraryItemUserStatus>>>;
} => {
const [libraryItemUserStatus, setLibraryItemUserStatus] = useLocalStorage(
"libraryItemUserStatus",

View File

@ -0,0 +1,115 @@
import { Dispatch, SetStateAction, useCallback, useMemo } from "react";
import { useLocalStorage } from "usehooks-ts";
import { ImageQuality } from "helpers/img";
export interface FilterSettings {
paperTexture: boolean;
bookFold: boolean;
lighting: boolean;
teint: number;
dropShadow: boolean;
}
interface ReaderSettings extends FilterSettings {
pageQuality: ImageQuality;
isSidePagesEnabled: boolean;
}
const DEFAULT_READER_SETTINGS: ReaderSettings = {
bookFold: true,
lighting: true,
paperTexture: true,
teint: 0.1,
dropShadow: true,
pageQuality: ImageQuality.Large,
isSidePagesEnabled: true,
};
export const useReaderSettings = (): {
filterSettings: FilterSettings;
isSidePagesEnabled: ReaderSettings["isSidePagesEnabled"];
pageQuality: ReaderSettings["pageQuality"];
toggleBookFold: () => void;
toggleLighting: () => void;
togglePaperTexture: () => void;
toggleDropShadow: () => void;
toggleIsSidePagesEnabled: () => void;
setPageQuality: Dispatch<SetStateAction<ImageQuality>>;
setTeint: Dispatch<SetStateAction<number>>;
resetReaderSettings: () => void;
} => {
const [bookFold, setBookFold] = useLocalStorage(
"readerBookFold",
DEFAULT_READER_SETTINGS.bookFold
);
const [lighting, setLighting] = useLocalStorage(
"readerLighting",
DEFAULT_READER_SETTINGS.lighting
);
const [paperTexture, setPaperTexture] = useLocalStorage(
"readerPaperTexture",
DEFAULT_READER_SETTINGS.paperTexture
);
const [teint, setTeint] = useLocalStorage("readerTeint", DEFAULT_READER_SETTINGS.teint);
const [dropShadow, setDropShadow] = useLocalStorage(
"readerDropShadow",
DEFAULT_READER_SETTINGS.dropShadow
);
const [isSidePagesEnabled, setIsSidePagesEnabled] = useLocalStorage(
"readerIsSidePagesEnabled",
DEFAULT_READER_SETTINGS.isSidePagesEnabled
);
const [pageQuality, setPageQuality] = useLocalStorage(
"readerPageQuality",
DEFAULT_READER_SETTINGS.pageQuality
);
const toggleBookFold = useCallback(() => setBookFold((current) => !current), [setBookFold]);
const toggleLighting = useCallback(() => setLighting((current) => !current), [setLighting]);
const togglePaperTexture = useCallback(
() => setPaperTexture((current) => !current),
[setPaperTexture]
);
const toggleDropShadow = useCallback(() => setDropShadow((current) => !current), [setDropShadow]);
const toggleIsSidePagesEnabled = useCallback(
() => setIsSidePagesEnabled((current) => !current),
[setIsSidePagesEnabled]
);
const resetReaderSettings = useCallback(() => {
setBookFold(DEFAULT_READER_SETTINGS.bookFold);
setLighting(DEFAULT_READER_SETTINGS.lighting);
setPaperTexture(DEFAULT_READER_SETTINGS.paperTexture);
setTeint(DEFAULT_READER_SETTINGS.teint);
setDropShadow(DEFAULT_READER_SETTINGS.dropShadow);
setIsSidePagesEnabled(DEFAULT_READER_SETTINGS.isSidePagesEnabled);
setPageQuality(DEFAULT_READER_SETTINGS.pageQuality);
}, [
setBookFold,
setDropShadow,
setIsSidePagesEnabled,
setLighting,
setPageQuality,
setPaperTexture,
setTeint,
]);
const filterSettings = useMemo(
() => ({ bookFold, lighting, paperTexture, teint, dropShadow }),
[bookFold, dropShadow, lighting, paperTexture, teint]
);
return {
filterSettings,
isSidePagesEnabled,
pageQuality,
toggleBookFold,
toggleLighting,
togglePaperTexture,
toggleDropShadow,
toggleIsSidePagesEnabled,
setPageQuality,
setTeint,
resetReaderSettings,
};
};

View File

@ -1,10 +1,10 @@
import { useEffect, useState } from "react";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { isDefined } from "helpers/others";
export const useStateWithLocalStorage = <T>(
key: string,
initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] => {
): [T, Dispatch<SetStateAction<T>>] => {
const [value, setValue] = useState<T>(initialValue);
const [isFromLocaleStorage, setFromLocaleStorage] = useState<boolean>(false);

View File

@ -1,7 +1,6 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
import Hotkeys from "react-hot-keys";
import { useBoolean } from "usehooks-ts";
import Slider from "rc-slider";
import { useRouter } from "next/router";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
@ -45,6 +44,7 @@ import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { useFullscreen } from "hooks/useFullscreen";
import { useUserSettings } from "contexts/UserSettingsContext";
import { useLocalData } from "contexts/LocalDataContext";
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
const CUSTOM_DARK_DROPSHADOW = `
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
@ -61,29 +61,9 @@ const CUSTOM_LIGHT_DROPSHADOW = `
const SIDEPAGES_PAGE_COUNT_ON_TEXTURE = 200;
const SIDEPAGES_PAGE_WIDTH = 0.02;
const DEFAULT_READER_OPTIONS = {
pageQuality: ImageQuality.Large,
isSidePagesEnabled: true,
filterOptions: {
bookFold: true,
lighting: true,
paperTexture: true,
teint: 0.1,
dropShadow: true,
},
};
type BookType = "book" | "manga";
type DisplayMode = "double" | "single";
interface FilterOptions {
paperTexture: boolean;
bookFold: boolean;
lighting: boolean;
teint: number;
dropShadow: boolean;
}
/*
*
* PAGE
@ -113,6 +93,19 @@ const LibrarySlug = ({
const is1ColumnLayout = useIs1ColumnLayout();
const { langui } = useLocalData();
const { darkMode } = useUserSettings();
const {
filterSettings,
isSidePagesEnabled,
pageQuality,
toggleBookFold,
toggleLighting,
togglePaperTexture,
toggleDropShadow,
toggleIsSidePagesEnabled,
setPageQuality,
setTeint,
resetReaderSettings,
} = useReaderSettings();
const [currentPageIndex, setCurrentPageIndex] = useState(0);
const [currentZoom, setCurrentZoom] = useState(1);
const [isGalleryMode, setIsGalleryMode] = useState(false);
@ -120,15 +113,7 @@ const LibrarySlug = ({
is1ColumnLayout ? "single" : "double"
);
const router = useRouter();
const [filterOptions, setFilterOptions] = useState<FilterOptions>(
DEFAULT_READER_OPTIONS.filterOptions
);
const [pageQuality, setPageQuality] = useState<ImageQuality>(DEFAULT_READER_OPTIONS.pageQuality);
const {
value: isSidePagesEnabled,
toggle: toggleSidePagesEnabled,
setValue: setIsSidePagesEnabled,
} = useBoolean(DEFAULT_READER_OPTIONS.isSidePagesEnabled);
const { isFullscreen, toggleFullscreen } = useFullscreen(Ids.ContentPanel);
const effectiveDisplayMode = useMemo(
@ -254,7 +239,7 @@ const LibrarySlug = ({
}
70% 0%,
${
filterOptions.bookFold
filterSettings.bookFold
? `90% .25%,
95% .5%,
98% .8%,
@ -269,7 +254,7 @@ const LibrarySlug = ({
}
70% 100%
)`,
[filterOptions.bookFold, isSidePagesEnabled, leftSidePagesWidth]
[filterSettings.bookFold, isSidePagesEnabled, leftSidePagesWidth]
);
const rightSideClipPath = useMemo(
@ -284,7 +269,7 @@ const LibrarySlug = ({
}
30% 100%,
${
filterOptions.bookFold
filterSettings.bookFold
? `10% 99.75%,
5% 99.5%,
2% 99.2%,
@ -299,7 +284,7 @@ const LibrarySlug = ({
}
30% 0%
)`,
[filterOptions.bookFold, isSidePagesEnabled, rightSidePagesWidth]
[filterSettings.bookFold, isSidePagesEnabled, rightSidePagesWidth]
);
const pageHeight = useMemo(
@ -314,43 +299,23 @@ const LibrarySlug = ({
<div className="mt-4 grid gap-2">
<WithLabel label={langui.paper_texture}>
<Switch
value={filterOptions.paperTexture}
onClick={() =>
setFilterOptions((current) => ({ ...current, paperTexture: !current.paperTexture }))
}
/>
<Switch value={filterSettings.paperTexture} onClick={togglePaperTexture} />
</WithLabel>
<WithLabel label={langui.book_fold}>
<Switch
value={filterOptions.bookFold}
onClick={() =>
setFilterOptions((current) => ({ ...current, bookFold: !current.bookFold }))
}
/>
<Switch value={filterSettings.bookFold} onClick={toggleBookFold} />
</WithLabel>
<WithLabel label={langui.lighting}>
<Switch
value={filterOptions.lighting}
onClick={() =>
setFilterOptions((current) => ({ ...current, lighting: !current.lighting }))
}
/>
<Switch value={filterSettings.lighting} onClick={toggleLighting} />
</WithLabel>
<WithLabel label={langui.side_pages}>
<Switch value={isSidePagesEnabled} onClick={toggleSidePagesEnabled} />
<Switch value={isSidePagesEnabled} onClick={toggleIsSidePagesEnabled} />
</WithLabel>
<WithLabel label={langui.shadow}>
<Switch
value={filterOptions.dropShadow}
onClick={() =>
setFilterOptions((current) => ({ ...current, dropShadow: !current.dropShadow }))
}
/>
<Switch value={filterSettings.dropShadow} onClick={toggleDropShadow} />
</WithLabel>
</div>
@ -359,7 +324,7 @@ const LibrarySlug = ({
<Slider
min={0}
max={10}
value={filterOptions.teint * 10}
value={filterSettings.teint * 10}
onChange={(event) => {
let value = 0;
if (Array.isArray(event)) {
@ -367,10 +332,7 @@ const LibrarySlug = ({
} else {
value = event;
}
setFilterOptions((current) => ({
...current,
teint: value / 10,
}));
setTeint(value / 10);
}}
/>
</div>
@ -418,9 +380,7 @@ const LibrarySlug = ({
text={langui.reset_all_options}
icon={Icon.Replay}
onClick={() => {
setFilterOptions(DEFAULT_READER_OPTIONS.filterOptions);
setPageQuality(DEFAULT_READER_OPTIONS.pageQuality);
setIsSidePagesEnabled(DEFAULT_READER_OPTIONS.isSidePagesEnabled);
resetReaderSettings();
setDisplayMode(is1ColumnLayout ? "single" : "double");
sendAnalytics("Reader", "Reset all options");
}}
@ -428,19 +388,36 @@ const LibrarySlug = ({
</SubPanel>
),
[
langui,
langui.item,
langui.paper_texture,
langui.book_fold,
langui.lighting,
langui.side_pages,
langui.shadow,
langui.night_reader,
langui.reading_layout,
langui.single_page_view,
langui.double_page_view,
langui.quality,
langui.reset_all_options,
itemSlug,
filterOptions.paperTexture,
filterOptions.bookFold,
filterOptions.lighting,
filterOptions.dropShadow,
filterOptions.teint,
filterSettings.paperTexture,
filterSettings.bookFold,
filterSettings.lighting,
filterSettings.dropShadow,
filterSettings.teint,
togglePaperTexture,
toggleBookFold,
toggleLighting,
isSidePagesEnabled,
toggleSidePagesEnabled,
toggleIsSidePagesEnabled,
toggleDropShadow,
displayMode,
pageQuality,
setTeint,
changeDisplayMode,
setIsSidePagesEnabled,
setPageQuality,
resetReaderSettings,
is1ColumnLayout,
]
);
@ -468,7 +445,7 @@ const LibrarySlug = ({
gridAutoFlow: "column",
display: "grid",
placeContent: "center",
filter: filterOptions.dropShadow
filter: filterSettings.dropShadow
? darkMode
? CUSTOM_DARK_DROPSHADOW
: CUSTOM_LIGHT_DROPSHADOW
@ -485,7 +462,7 @@ const LibrarySlug = ({
src={firstPage}
quality={pageQuality}
/>
<PageFilters page="single" bookType={bookType} options={filterOptions} />
<PageFilters page="single" bookType={bookType} options={filterSettings} />
<div
className="absolute left-0 top-0 bottom-0 w-1/2"
onClick={() => currentZoom <= 1 && handlePageNavigation("left")}
@ -523,7 +500,7 @@ const LibrarySlug = ({
src={pageOrder === PageOrder.LeftToRight ? firstPage : secondPage}
quality={pageQuality}
/>
<PageFilters page="left" bookType={bookType} options={filterOptions} />
<PageFilters page="left" bookType={bookType} options={filterSettings} />
</div>
<div
className={cJoin(
@ -557,7 +534,7 @@ const LibrarySlug = ({
/>
)}
<PageFilters page="right" bookType={bookType} options={filterOptions} />
<PageFilters page="right" bookType={bookType} options={filterSettings} />
</div>
</>
)}
@ -647,7 +624,7 @@ const LibrarySlug = ({
[
is1ColumnLayout,
currentZoom,
filterOptions,
filterSettings,
darkMode,
pageHeight,
effectiveDisplayMode,
@ -811,7 +788,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
interface PageFiltersProps {
page: "left" | "right" | "single";
bookType: BookType;
options: FilterOptions;
options: FilterSettings;
}
const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {