From 13e6456190f3b344e51c1161b9e6088d2a74291a Mon Sep 17 00:00:00 2001 From: amr Date: Thu, 7 Apr 2022 12:53:14 +0200 Subject: [PATCH] unified library options (#168) * Unified the way options are being handled. now everything uses the LibraryOptionsContext, removed the no-longer used useLibraryOptions hook * removed query from library options. since it doesn't make sense for this to be cached * fixed a bug in the useLocalStorage implementation --- .../context/LibraryOptionsContext.tsx | 33 +++++++++--- src/components/library/LibraryMangaGrid.tsx | 12 +++-- src/components/library/LibraryOptions.tsx | 27 +++++----- .../library/LibraryOptionsProvider.tsx | 33 +++++++++--- src/typings.d.ts | 9 +++- src/util/useLibraryOptions.ts | 51 ------------------- src/util/useLocalStorage.tsx | 17 ++++--- 7 files changed, 92 insertions(+), 90 deletions(-) delete mode 100644 src/util/useLibraryOptions.ts diff --git a/src/components/context/LibraryOptionsContext.tsx b/src/components/context/LibraryOptionsContext.tsx index 7fd57282..9f04840b 100644 --- a/src/components/context/LibraryOptionsContext.tsx +++ b/src/components/context/LibraryOptionsContext.tsx @@ -8,16 +8,37 @@ import React, { useContext } from 'react'; type ContextType = { - // display options - options: LibraryDisplayOptions; - setOptions: React.Dispatch>; + options: LibraryOptions; + + setOption: ( + name: Name, + value: React.SetStateAction + ) => void; + + setOptions: React.Dispatch>; + + active: boolean + activeSort: boolean +}; + +export const DefaultLibraryOptions: LibraryOptions = { + showDownloadBadge: false, + showUnreadBadge: false, + gridLayout: 0, + SourcegridLayout: 0, + + downloaded: undefined, + sortDesc: undefined, + sorts: undefined, + unread: undefined, }; const LibraryOptionsContext = React.createContext({ - options: { - showDownloadBadge: false, showUnreadBadge: false, gridLayout: 0, SourcegridLayout: 0, - }, + options: DefaultLibraryOptions, + setOption: () => {}, setOptions: () => {}, + active: false, + activeSort: false, }); export default LibraryOptionsContext; diff --git a/src/components/library/LibraryMangaGrid.tsx b/src/components/library/LibraryMangaGrid.tsx index c906523b..373c222b 100644 --- a/src/components/library/LibraryMangaGrid.tsx +++ b/src/components/library/LibraryMangaGrid.tsx @@ -8,8 +8,8 @@ import React from 'react'; import MangaGrid, { IMangaGridProps } from 'components/MangaGrid'; -import useLibraryOptions from 'util/useLibraryOptions'; import { useLibraryOptionsContext } from 'components/context/LibraryOptionsContext'; +import { StringParam, useQueryParam } from 'use-query-params'; const FILTERED_OUT_MESSAGE = 'There are no Manga matching this filter'; @@ -42,7 +42,9 @@ function queryFilter(query: NullAndUndefined, { title }: IMangaCard): bo } function filterManga(mangas: IMangaCard[]): IMangaCard[] { - const { downloaded, unread, query } = useLibraryOptions(); + const [query] = useQueryParam('query', StringParam); + + const { options: { downloaded, unread } } = useLibraryOptionsContext(); return mangas .filter((manga) => downloadedFilter(downloaded, manga) && unreadFilter(unread, manga) @@ -64,7 +66,7 @@ function toSortID(a: IMangaCard, b: IMangaCard): number { } function sortManga(mangas: IMangaCard[]): IMangaCard[] { - const { sorts, sortDesc } = useLibraryOptions(); + const { options: { sorts, sortDesc } } = useLibraryOptionsContext(); return (sorts === 'sortID' || sorts === undefined) && !sortDesc ? mangas : mangas.sort((a, b) => { const c = sortDesc === true ? b : a; const d = sortDesc === true ? a : b; @@ -80,8 +82,8 @@ export default function LibraryMangaGrid(props: IMangaGridProps) { mangas, isLoading, hasNextPage, lastPageNum, setLastPageNum, message, } = props; - const { options } = useLibraryOptionsContext(); - const { active, query } = useLibraryOptions(); + const [query] = useQueryParam('query', StringParam); + const { options, active } = useLibraryOptionsContext(); const filteredManga = filterManga(mangas); const sortedManga = sortManga(filteredManga); const DoneManga = sortedManga.map((ele) => { diff --git a/src/components/library/LibraryOptions.tsx b/src/components/library/LibraryOptions.tsx index 60937b16..3f5e9239 100644 --- a/src/components/library/LibraryOptions.tsx +++ b/src/components/library/LibraryOptions.tsx @@ -23,7 +23,6 @@ import { ListItemText, Radio, } from '@mui/material'; -import useLibraryOptions from 'util/useLibraryOptions'; import ThreeStateCheckbox from 'components/util/ThreeStateCheckbox'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; @@ -31,9 +30,8 @@ import TabPanel from 'components/util/TabPanel'; import { useLibraryOptionsContext } from 'components/context/LibraryOptionsContext'; function filtersTab(currentTab: number) { - const { - downloaded, setDownloaded, unread, setUnread, - } = useLibraryOptions(); + const { options: { unread, downloaded }, setOption } = useLibraryOptionsContext(); + return ( @@ -42,7 +40,7 @@ function filtersTab(currentTab: number) { setOption('unread', change)} /> )} label="Unread" @@ -52,7 +50,7 @@ function filtersTab(currentTab: number) { setOption('downloaded', change)} /> )} label="Downloaded" @@ -63,16 +61,15 @@ function filtersTab(currentTab: number) { } function sortsTab(currentTab: number) { - const { - sorts, setSorts, sortDesc, setSortDesc, - } = useLibraryOptions(); + const { options: { sorts, sortDesc }, setOption } = useLibraryOptionsContext(); - const handleChange = (event: - React.MouseEvent, index: string) => { + const handleChange = (event: React.MouseEvent, index: string) => { if (sorts === index) { - setSortDesc(!sortDesc); - } else { setSortDesc(false); } - setSorts(index); + setOption('sortDesc', (sortDes) => !sortDes); + } else { + setOption('sortDesc', false); + } + setOption('sorts', index); }; return ( @@ -208,7 +205,7 @@ function Options() { export default function LibraryOptions() { const [filtersOpen, setFiltersOpen] = React.useState(false); - const { active } = useLibraryOptions(); + const { active } = useLibraryOptionsContext(); return ( <> ('libraryOptions', - { - showDownloadBadge: false, showUnreadBadge: false, gridLayout: 0, SourcegridLayout: 0, - }); + const [options, setOptions] = useLocalStorage('libraryOptions', DefaultLibraryOptions); + + function setOption( + option: Name, + value: React.SetStateAction, + ) { + setOptions((opts) => ({ + ...opts, + [option]: typeof value === 'function' ? value(opts[option]) : value, + })); + } + + // TODO remove these fields when we have a better way to handle them + // eslint-disable-next-line eqeqeq + const active = !(options.unread == undefined) || !(options.downloaded == undefined); + // eslint-disable-next-line eqeqeq + const activeSort = (options.sortDesc != undefined) || (options.sorts != undefined); return ( - + {children} ); diff --git a/src/typings.d.ts b/src/typings.d.ts index f6d1528f..327a7af3 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -262,9 +262,16 @@ type ChapterOptionsReducerAction = | { type: 'sortReverse' } | { type: 'showChapterNumber' }; -interface LibraryDisplayOptions { +interface LibraryOptions { + // display options showDownloadBadge: boolean showUnreadBadge: boolean gridLayout: number SourcegridLayout:number + + // filter options + downloaded: NullAndUndefined + unread: NullAndUndefined + sorts: NullAndUndefined + sortDesc: NullAndUndefined } diff --git a/src/util/useLibraryOptions.ts b/src/util/useLibraryOptions.ts deleted file mode 100644 index 5bcafbe1..00000000 --- a/src/util/useLibraryOptions.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) Contributors to the Suwayomi project - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -import { BooleanParam, useQueryParam, StringParam } from 'use-query-params'; - -interface IUseLibraryOptions { - downloaded: NullAndUndefined - setDownloaded: (downloaded: NullAndUndefined) => void - unread: NullAndUndefined - setUnread: (unread: NullAndUndefined) => void - query: NullAndUndefined - setQuery: (query: NullAndUndefined) => void - active: boolean - activeSort: boolean - sorts: NullAndUndefined - setSorts: (sorts: NullAndUndefined) => void - sortDesc: NullAndUndefined - setSortDesc: (sortDesc: NullAndUndefined) => void -} - -export default function useLibraryOptions(): IUseLibraryOptions { - const [downloaded, setDownloaded] = useQueryParam('downloaded', BooleanParam); - const [unread, setUnread] = useQueryParam('unread', BooleanParam); - const [query, setQuery] = useQueryParam('query', StringParam); - const [sorts, setSorts] = useQueryParam('sorts', StringParam); - const [sortDesc, setSortDesc] = useQueryParam('sortDesc', BooleanParam); - - // eslint-disable-next-line eqeqeq - const active = !(unread == undefined) || !(downloaded == undefined); - // eslint-disable-next-line eqeqeq - const activeSort = (sortDesc != undefined) || (sorts != undefined); - return { - downloaded, - setDownloaded, - unread, - setUnread, - query, - setQuery, - active, - activeSort, - sorts, - setSorts, - sortDesc, - setSortDesc, - }; -} diff --git a/src/util/useLocalStorage.tsx b/src/util/useLocalStorage.tsx index 2b0d2dbe..83b97719 100644 --- a/src/util/useLocalStorage.tsx +++ b/src/util/useLocalStorage.tsx @@ -11,6 +11,7 @@ import React, { SetStateAction, useReducer, Reducer, + useCallback, } from 'react'; import storage from './localStorage'; @@ -24,12 +25,16 @@ export default function useLocalStorage( storage.getItem(key, initialState), ); - const setValue = ((value: T | ((prevState: T) => T)) => { - // Allow value to be a function so we have same API as useState - const valueToStore = value instanceof Function ? value(storedValue) : value; - setStoredValue(valueToStore); - storage.setItem(key, valueToStore); - }) as React.Dispatch>; + const setValue = useCallback>>( + ((value) => { + setStoredValue((prevValue) => { + // Allow value to be a function so we have same API as useState + const valueToStore = value instanceof Function ? value(prevValue) : value; + storage.setItem(key, valueToStore); + return valueToStore; + }); + }), [key], + ); return [storedValue, setValue]; }