From 12dd973179e8599fd34782b7f958c09d9609343d Mon Sep 17 00:00:00 2001
From: schroda <50052685+schroda@users.noreply.github.com>
Date: Sun, 24 Sep 2023 21:32:23 +0200
Subject: [PATCH] Use gql for "mangas" VI - source mangas filter
---
src/components/source/SourceOptions.tsx | 76 ++++++++++---------
.../source/filters/CheckBoxFilter.tsx | 2 +-
src/components/source/filters/GroupFilter.tsx | 9 ++-
.../source/filters/SelectFilter.tsx | 64 +---------------
src/components/source/filters/SortFilter.tsx | 6 +-
src/components/source/filters/TextFilter.tsx | 2 +-
.../source/filters/TriStateFilter.tsx | 34 ++++++++-
src/lib/requests/RequestManager.ts | 38 ----------
src/screens/SourceMangas.tsx | 71 +++++++----------
src/typings.ts | 26 +------
10 files changed, 113 insertions(+), 215 deletions(-)
diff --git a/src/components/source/SourceOptions.tsx b/src/components/source/SourceOptions.tsx
index ec462b68..00f67678 100644
--- a/src/components/source/SourceOptions.tsx
+++ b/src/components/source/SourceOptions.tsx
@@ -10,7 +10,7 @@ import FilterListIcon from '@mui/icons-material/FilterList';
import { Button, Stack, Box } from '@mui/material';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { ISourceFilters, IState } from '@/typings';
+import { SourceFilters } from '@/typings';
import OptionsPanel from '@/components/molecules/OptionsPanel';
import CheckBoxFilter from '@/components/source/filters/CheckBoxFilter';
import HeaderFilter from '@/components/source/filters/HeaderFilter';
@@ -25,14 +25,14 @@ import SeperatorFilter from '@/components/source/filters/SeparatorFilter';
import StyledFab from '@/components/util/StyledFab';
interface IFilters {
- sourceFilter: ISourceFilters[];
+ sourceFilter: SourceFilters[];
updateFilterValue: Function;
group: number | undefined;
update: any;
}
interface IFilters1 {
- sourceFilter: ISourceFilters[];
+ sourceFilter: SourceFilters[];
updateFilterValue: Function;
resetFilterValue: Function;
setTriggerUpdate: Function;
@@ -42,85 +42,89 @@ interface IFilters1 {
export function Options({ sourceFilter, group, updateFilterValue, update }: IFilters) {
return (
- {sourceFilter.map((e: ISourceFilters, index) => {
+ {sourceFilter.map((e, index) => {
let checkif = update.find(
(el: { group: number | undefined; position: number }) =>
el.group === group && el.position === index,
);
checkif = checkif ? checkif.state : checkif;
switch (e.type) {
- case 'CheckBox':
+ case 'CheckBoxFilter':
return (
);
- case 'Group':
+ case 'GroupFilter':
return (
);
- case 'Header':
- return ;
- case 'Select':
+ case 'HeaderFilter':
+ return ;
+ case 'SelectFilter':
return (
);
- case 'Separator':
- return ;
- case 'Sort':
+ case 'SeparatorFilter':
+ return ;
+ case 'SortFilter':
return (
);
- case 'Text':
+ case 'TextFilter':
return (
);
- case 'TriState':
+ case 'TriStateFilter':
return (
);
default:
- return ;
+ throw new Error(`Unknown source filter "${e}"`);
}
})}
diff --git a/src/components/source/filters/CheckBoxFilter.tsx b/src/components/source/filters/CheckBoxFilter.tsx
index 125da405..8219ae71 100644
--- a/src/components/source/filters/CheckBoxFilter.tsx
+++ b/src/components/source/filters/CheckBoxFilter.tsx
@@ -27,7 +27,7 @@ const CheckBoxFilter: React.FC = (props: Props) => {
const upd = update.filter(
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
);
- updateFilterValue([...upd, { position, state: event.target.checked.toString(), group }]);
+ updateFilterValue([...upd, { type: 'checkBoxState', position, state: event.target.checked, group }]);
};
if (state !== undefined) {
diff --git a/src/components/source/filters/GroupFilter.tsx b/src/components/source/filters/GroupFilter.tsx
index 2a4db407..f1cf7589 100644
--- a/src/components/source/filters/GroupFilter.tsx
+++ b/src/components/source/filters/GroupFilter.tsx
@@ -10,11 +10,14 @@ import { ExpandLess, ExpandMore } from '@mui/icons-material';
import { Collapse, ListItemButton, ListItemText, Stack, Box } from '@mui/material';
import React from 'react';
// eslint-disable-next-line import/no-cycle
-import { ISourceFilters } from '@/typings';
+import { SourceFilters } from '@/typings';
import { Options } from '@/components/source/SourceOptions';
+// type ExcludeByName = T extends { __typename?: K } ? never : T;
+type PickByName = T extends { __typename?: K } ? T : never;
+
interface Props {
- state: ISourceFilters[];
+ state: PickByName['filters'];
name: string;
position: number;
updateFilterValue: Function;
@@ -36,7 +39,7 @@ const GroupFilter: React.FC = (props: Props) => {
{/* Container is moved outside 2, so content has to go inside 4 */}
{
- const vall = values.map((e) => e.displayname).indexOf(`${event.target.value}`);
- setval(vall);
- const upd = update.filter(
- (e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
- );
- updateFilterValue([...upd, { position, state: vall.toString(), group }]);
- };
-
- const rett = values.map((e: Selected) => (
-
- ));
- return (
-
- {name}
-
-
- );
- }
- return null;
-}
-
function noSelect(
values: string[],
name: string,
@@ -84,7 +40,7 @@ function noSelect(
const upd = update.filter(
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
);
- updateFilterValue([...upd, { position, state: vall.toString(), group }]);
+ updateFilterValue([...upd, { type: 'selectState', position, state: vall, group }]);
};
const rett = values.map((value: string) => (
@@ -104,21 +60,7 @@ function noSelect(
return null;
}
-const SelectFilter: React.FC = ({
- values,
- name,
- state,
- selected,
- position,
- updateFilterValue,
- update,
- group,
-}) => {
- if (selected === undefined) {
- return noSelect(values, name, state, position, updateFilterValue, update, group);
- }
-
- return hasSelect(values, name, state, position, updateFilterValue, update, group);
-};
+const SelectFilter: React.FC = ({ values, name, state, position, updateFilterValue, update, group }) =>
+ noSelect(values, name, state, position, updateFilterValue, update, group);
export default SelectFilter;
diff --git a/src/components/source/filters/SortFilter.tsx b/src/components/source/filters/SortFilter.tsx
index beb4f6a3..4748cf29 100644
--- a/src/components/source/filters/SortFilter.tsx
+++ b/src/components/source/filters/SortFilter.tsx
@@ -9,13 +9,13 @@
import { ExpandLess, ExpandMore } from '@mui/icons-material';
import { Collapse, ListItemButton, ListItemText, Stack, Box } from '@mui/material';
import React from 'react';
-import { IState } from '@/typings';
import SortRadioInput from '@/components/atoms/SortRadioInput';
+import { SortSelectionInput } from '@/lib/graphql/generated/graphql.ts';
interface Props {
values: any;
name: string;
- state: IState;
+ state: SortSelectionInput;
position: number;
group: number | undefined;
updateFilterValue: Function;
@@ -45,7 +45,7 @@ const SortFilter: React.FC = (props: Props) => {
const upd = update.filter(
(e: { position: number; group: number | undefined }) => !(position === e.position && group === e.group),
);
- updateFilterValue([...upd, { position, state: JSON.stringify(tmp), group }]);
+ updateFilterValue([...upd, { type: 'sortState', position, state: tmp, group }]);
};
return (
diff --git a/src/components/source/filters/TextFilter.tsx b/src/components/source/filters/TextFilter.tsx
index 7dd82065..7b62304b 100644
--- a/src/components/source/filters/TextFilter.tsx
+++ b/src/components/source/filters/TextFilter.tsx
@@ -29,7 +29,7 @@ const TextFilter: React.FC = (props) => {
const upd = update.filter(
(el: { position: number; group: number | undefined }) => !(position === el.position && group === el.group),
);
- updateFilterValue([...upd, { position, state: inputText, group }]);
+ updateFilterValue([...upd, { type: 'textState', position, state: inputText, group }]);
}, [inputText]);
if (state !== undefined) {
diff --git a/src/components/source/filters/TriStateFilter.tsx b/src/components/source/filters/TriStateFilter.tsx
index e9270b25..70a53094 100644
--- a/src/components/source/filters/TriStateFilter.tsx
+++ b/src/components/source/filters/TriStateFilter.tsx
@@ -8,9 +8,10 @@
import React from 'react';
import ThreeStateCheckboxInput from '@/components/atoms/ThreeStateCheckboxInput';
+import { TriState } from '@/lib/graphql/generated/graphql.ts';
interface Props {
- state: number;
+ state: TriState;
name: string;
position: number;
group: number | undefined;
@@ -18,9 +19,35 @@ interface Props {
update: any;
}
+const convertTriStateToNumber = (triState: TriState): number => {
+ switch (triState) {
+ case TriState.Ignore:
+ return 0;
+ case TriState.Include:
+ return 1;
+ case TriState.Exclude:
+ return 2;
+ default:
+ throw new Error(`Unexpected TriState ${triState}`);
+ }
+};
+
+const convertNumberToTriState = (state: number): TriState => {
+ switch (state) {
+ case 0:
+ return TriState.Ignore;
+ case 1:
+ return TriState.Include;
+ case 2:
+ return TriState.Exclude;
+ default:
+ throw new Error(`Unexpected state number ${state}`);
+ }
+};
+
const TriStateFilter: React.FC = (props) => {
const { state, name, position, group, updateFilterValue, update } = props;
- const [val, setval] = React.useState(Number(state));
+ const [val, setval] = React.useState(convertTriStateToNumber(state));
const handleChange = (checked: boolean | null | undefined) => {
// eslint-disable-next-line no-nested-ternary
@@ -32,8 +59,9 @@ const TriStateFilter: React.FC = (props) => {
updateFilterValue([
...upd,
{
+ type: 'triState',
position,
- state: newState.toString(),
+ state: convertNumberToTriState(newState),
group,
},
]);
diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts
index 2c61c5d2..7721e253 100644
--- a/src/lib/requests/RequestManager.ts
+++ b/src/lib/requests/RequestManager.ts
@@ -28,10 +28,8 @@ import {
BackupValidationResult,
IChapter,
IMangaChapter,
- ISourceFilters,
PaginatedList,
SourcePreferences,
- SourceSearchResult,
} from '@/typings.ts';
import { HttpMethod as DefaultHttpMethod, IRestClient, RestClient } from '@/lib/requests/client/RestClient.ts';
import storage from '@/util/localStorage.tsx';
@@ -1208,22 +1206,6 @@ export class RequestManager {
return this.doRequest(HttpMethod.POST, `source/${sourceId}/preferences`, { data: { position, value } });
}
- public useGetSourceFilters(
- sourceId: string,
- reset?: boolean,
- swrOptions?: SWROptions,
- ): AbortableSWRResponse {
- return this.doRequest(HttpMethod.SWR_GET, `source/${sourceId}/filters`, { swrOptions });
- }
-
- public setSourceFilters(sourceId: string, filters: { position: number; state: string }[]): AbortableAxiosResponse {
- return this.doRequest(HttpMethod.POST, `source/${sourceId}/filters`, { data: filters });
- }
-
- public resetSourceFilters(sourceId: string): AbortableAxiosResponse {
- return this.doRequest(HttpMethod.GET, `source/${sourceId}/filters?reset=true`);
- }
-
public useSourceSearch(
source: string,
query?: string,
@@ -1241,26 +1223,6 @@ export class RequestManager {
);
}
- public useSourceQuickSearch(
- sourceId: string,
- searchTerm: string,
- filters: { position: number; state: string }[],
- initialPages?: number,
- swrOptions?: SWRInfiniteOptions,
- ): AbortableSWRInfiniteResponse {
- return this.doRequest(HttpMethod.SWR_POST_INFINITE, '', {
- data: { searchTerm, filter: filters },
- swrOptions: {
- getEndpoint: (page, previousData) =>
- previousData?.hasNextPage ?? true
- ? `source/${sourceId}/quick-search?searchTerm=${searchTerm}&pageNum=${page + 1}`
- : null,
- initialSize: initialPages,
- ...swrOptions,
- } as typeof swrOptions,
- });
- }
-
public useGetManga(
mangaId: number | string,
options?: QueryHookOptions,
diff --git a/src/screens/SourceMangas.tsx b/src/screens/SourceMangas.tsx
index 46d780ff..f3e3d1c4 100644
--- a/src/screens/SourceMangas.tsx
+++ b/src/screens/SourceMangas.tsx
@@ -74,6 +74,7 @@ export enum SourceContentType {
}
interface IPos {
+ type: 'selectState' | 'textState' | 'checkBoxState' | 'triState' | 'sortState';
position: number;
state: any;
group?: number;
@@ -127,30 +128,30 @@ const useSourceManga = (
result = requestManager.useSourceSearch(sourceId, searchTerm ?? '', undefined, initialPages);
break;
case SourceContentType.FILTER:
- result = requestManager.useSourceSearch(sourceId, undefined, [], initialPages);
- // TODO - update filters to gql
- // result = requestManager.useSourceQuickSearch(
- // sourceId,
- // '',
- // filters.map((filter) => {
- // const { position, state, group } = filter;
- //
- // const isPartOfGroup = group !== undefined;
- // if (isPartOfGroup) {
- // return {
- // position: group,
- // state: JSON.stringify({
- // position,
- // state,
- // }),
- // };
- // }
- //
- // return filter;
- // }),
- // initialPages,
- // { disableCache: true },
- // );
+ result = requestManager.useSourceSearch(
+ sourceId,
+ undefined,
+ filters.map((filter) => {
+ const { position, state, group } = filter;
+
+ const isPartOfGroup = group !== undefined;
+ if (isPartOfGroup) {
+ return {
+ position: group,
+ groupChange: {
+ position,
+ [filter.type]: state,
+ },
+ };
+ }
+
+ return {
+ position,
+ [filter.type]: state,
+ };
+ }),
+ initialPages,
+ );
break;
default:
throw new Error(`Unknown ContentType "${contentType}"`);
@@ -222,10 +223,9 @@ export default function SourceMangas() {
const mangas = (data?.fetchSourceManga.mangas as MangaType[]) ?? [];
const hasNextPage = data?.fetchSourceManga.hasNextPage ?? false;
- const { data: filters = [], mutate: mutateFilters } = requestManager.useGetSourceFilters(sourceId);
const { data: sourceData } = requestManager.useGetSource(sourceId);
const source = sourceData?.source;
- const [triggerDataRefresh, setTriggerDataRefresh] = useState(false);
+ const filters = source?.filters ?? [];
const message = !isLoading ? t(SOURCE_CONTENT_TYPE_TO_ERROR_MSG_KEY[contentType]) : undefined;
const isLocalSource = sourceId === '0';
@@ -272,14 +272,6 @@ export default function SourceMangas() {
const resetFilters = useCallback(async () => {
setDialogFiltersToApply([]);
setFiltersToApply([]);
- try {
- // required since previous implementation used to set the filters on server side (server caches them), thus, it has to be made sure that they are reset
- await requestManager.resetSourceFilters(sourceId);
- mutateFilters();
- } catch (error) {
- // ignore
- }
- setTriggerDataRefresh(true);
}, [sourceId]);
useEffect(
@@ -297,16 +289,6 @@ export default function SourceMangas() {
[searchTerm, contentType],
);
- // TODO - check when fixing filters
- useEffect(() => {
- if (!triggerDataRefresh) {
- return;
- }
-
- // refreshData();
- setTriggerDataRefresh(false);
- }, [triggerDataRefresh]);
-
useEffect(() => {
setTitle(source?.displayName ?? t('source.title'));
setAction(
@@ -378,7 +360,6 @@ export default function SourceMangas() {
updateFilterValue={setDialogFiltersToApply}
setTriggerUpdate={() => {
setFiltersToApply(dialogFiltersToApply);
- setTriggerDataRefresh(true);
}}
resetFilterValue={resetFilters}
update={dialogFiltersToApply}
diff --git a/src/typings.ts b/src/typings.ts
index fc08e7ae..ca0669cc 100644
--- a/src/typings.ts
+++ b/src/typings.ts
@@ -10,7 +10,7 @@ import { OverridableComponent } from '@mui/material/OverridableComponent';
import { SvgIconTypeMap } from '@mui/material/SvgIcon/SvgIcon';
import { ParseKeys } from 'i18next';
import { Location } from 'react-router-dom';
-import { ExtensionType, MangaType, MetaType } from '@/lib/graphql/generated/graphql.ts';
+import { ExtensionType, GetSourceQuery, MangaType, MetaType } from '@/lib/graphql/generated/graphql.ts';
export type RecursivePartial = {
[P in keyof T]?: T[P] extends (infer U)[]
@@ -43,29 +43,7 @@ export interface ISource {
displayName: string;
}
-export interface ISourceFilters {
- type: string;
- filter: ISourceFilter;
-}
-
-export interface ISourceFilter {
- name: string;
- state: number | string | boolean | ISourceFilters[] | IState;
- values?: string[];
- displayValues?: string[];
- selected?: ISelected;
-}
-
-export interface ISelected {
- displayname: string;
- value: string;
- _value: string;
-}
-
-export interface IState {
- ascending: boolean;
- index: number;
-}
+export type SourceFilters = GetSourceQuery['source']['filters'][number];
export interface IMetadataMigration {
appKeyPrefix?: { oldPrefix: string; newPrefix: string };