Feature/cleanup files (#528)
* Move menu components into new dir * Move chapter components into new dir * Extract menu helper functions * Move "SelectionFAB" to "collection" dir * Move debounce hook to global utils dir * Rename manga "hooks" to "useRefreshManga"
This commit is contained in:
@@ -21,7 +21,7 @@ import { ContinueReadingButton } from '@/components/manga/ContinueReadingButton.
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { MangaOptionButton } from '@/components/manga/MangaOptionButton.tsx';
|
||||
import { MangaActionMenuItems, SingleModeProps } from '@/components/manga/MangaActionMenuItems.tsx';
|
||||
import { Menu } from '@/components/manga/Menu.tsx';
|
||||
import { Menu } from '@/components/menu/Menu.tsx';
|
||||
|
||||
const BottomGradient = styled('div')({
|
||||
position: 'absolute',
|
||||
|
||||
@@ -15,7 +15,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import BookmarkRemove from '@mui/icons-material/BookmarkRemove';
|
||||
import BookmarkAdd from '@mui/icons-material/BookmarkAdd';
|
||||
import DoneAll from '@mui/icons-material/DoneAll';
|
||||
import { t as translate } from 'i18next';
|
||||
import { useMemo } from 'react';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import {
|
||||
@@ -27,28 +26,10 @@ import {
|
||||
Chapters,
|
||||
} from '@/lib/data/Chapters.ts';
|
||||
import { TChapter } from '@/typings.ts';
|
||||
import { MenuItem } from '@/components/manga/MenuItem.tsx';
|
||||
import { IChapterWithMeta } from '@/components/manga/ChapterList.tsx';
|
||||
import { MenuItem } from '@/components/menu/MenuItem.tsx';
|
||||
import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx';
|
||||
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
||||
|
||||
const createGetMenuItemTitle =
|
||||
(isSingleMode: boolean) =>
|
||||
(action: ChapterAction, count: number): string => {
|
||||
const countSuffix = count > 0 ? ` (${count})` : '';
|
||||
return `${translate(
|
||||
actionToTranslationKey[action].action[isSingleMode ? 'single' : 'selected'],
|
||||
)}${countSuffix}`;
|
||||
};
|
||||
|
||||
const createShouldShowMenuItem =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeVisible: boolean = false): boolean =>
|
||||
isSingleMode ? shouldBeVisible : true;
|
||||
|
||||
const createIsMenuItemDisabled =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeDisabled: boolean): boolean =>
|
||||
isSingleMode ? false : shouldBeDisabled;
|
||||
import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts';
|
||||
|
||||
type BaseProps = { onClose: () => void };
|
||||
|
||||
@@ -80,7 +61,7 @@ export const ChapterActionMenuItems = ({
|
||||
const isSingleMode = !!chapter;
|
||||
const { isDownloaded, isRead, isBookmarked } = chapter ?? {};
|
||||
|
||||
const getMenuItemTitle = createGetMenuItemTitle(isSingleMode);
|
||||
const getMenuItemTitle = createGetMenuItemTitle(isSingleMode, actionToTranslationKey);
|
||||
const shouldShowMenuItem = createShouldShowMenuItem(isSingleMode);
|
||||
const isMenuItemDisabled = createIsMenuItemDisabled(isSingleMode);
|
||||
|
||||
@@ -18,12 +18,12 @@ import React, { TouchEvent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
|
||||
import { getUploadDateString } from '@/util/date';
|
||||
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator';
|
||||
import { getUploadDateString } from '@/util/date.ts';
|
||||
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator.tsx';
|
||||
import { DownloadType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { TChapter } from '@/typings.ts';
|
||||
import { ChapterActionMenuItems } from '@/components/manga/ChapterActionMenuItems.tsx';
|
||||
import { Menu } from '@/components/manga/Menu.tsx';
|
||||
import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx';
|
||||
import { Menu } from '@/components/menu/Menu.tsx';
|
||||
|
||||
interface IProps {
|
||||
chapter: TChapter;
|
||||
@@ -14,21 +14,21 @@ import { useTranslation } from 'react-i18next';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import DoneAllIcon from '@mui/icons-material/DoneAll';
|
||||
import { TChapter, TManga } from '@/typings';
|
||||
import { TChapter, TManga } from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { ChapterCard } from '@/components/manga/ChapterCard';
|
||||
import { ResumeFab } from '@/components/manga/ResumeFAB';
|
||||
import { filterAndSortChapters, useChapterOptions } from '@/components/manga/util';
|
||||
import { EmptyView } from '@/components/util/EmptyView';
|
||||
import { ChaptersToolbarMenu } from '@/components/manga/ChaptersToolbarMenu';
|
||||
import { SelectionFAB } from '@/components/manga/SelectionFAB';
|
||||
import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab';
|
||||
import { ChapterCard } from '@/components/chapter/ChapterCard.tsx';
|
||||
import { ResumeFab } from '@/components/manga/ResumeFAB.tsx';
|
||||
import { filterAndSortChapters, useChapterOptions } from '@/components/chapter/util.tsx';
|
||||
import { EmptyView } from '@/components/util/EmptyView.tsx';
|
||||
import { ChaptersToolbarMenu } from '@/components/chapter/ChaptersToolbarMenu.tsx';
|
||||
import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx';
|
||||
import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab.tsx';
|
||||
import { DownloadType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { SelectableCollectionSelectAll } from '@/components/collection/SelectableCollectionSelectAll.tsx';
|
||||
import { Chapters } from '@/lib/data/Chapters.ts';
|
||||
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
|
||||
import { ChapterActionMenuItems } from '@/components/manga/ChapterActionMenuItems.tsx';
|
||||
import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx';
|
||||
|
||||
const ChapterListHeader = styled(Stack)(({ theme }) => ({
|
||||
margin: 8,
|
||||
@@ -9,12 +9,12 @@
|
||||
import { RadioGroup } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChapterListOptions, ChapterOptionsReducerAction, TranslationKey } from '@/typings';
|
||||
import { RadioInput } from '@/components/atoms/RadioInput';
|
||||
import { SortRadioInput } from '@/components/atoms/SortRadioInput';
|
||||
import { ThreeStateCheckboxInput } from '@/components/atoms/ThreeStateCheckboxInput';
|
||||
import { OptionsTabs } from '@/components/molecules/OptionsTabs';
|
||||
import { SORT_OPTIONS } from '@/components/manga/util';
|
||||
import { ChapterListOptions, ChapterOptionsReducerAction, TranslationKey } from '@/typings.ts';
|
||||
import { RadioInput } from '@/components/atoms/RadioInput.tsx';
|
||||
import { SortRadioInput } from '@/components/atoms/SortRadioInput.tsx';
|
||||
import { ThreeStateCheckboxInput } from '@/components/atoms/ThreeStateCheckboxInput.tsx';
|
||||
import { OptionsTabs } from '@/components/molecules/OptionsTabs.tsx';
|
||||
import { SORT_OPTIONS } from '@/components/chapter/util.tsx';
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
@@ -10,9 +10,9 @@ import FilterList from '@mui/icons-material/FilterList';
|
||||
import { IconButton, Tooltip } from '@mui/material';
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChapterListOptions, ChapterOptionsReducerAction } from '@/typings';
|
||||
import { ChapterOptions } from '@/components/manga/ChapterOptions';
|
||||
import { isFilterActive } from '@/components/manga/util';
|
||||
import { ChapterListOptions, ChapterOptionsReducerAction } from '@/typings.ts';
|
||||
import { ChapterOptions } from '@/components/chapter/ChapterOptions.tsx';
|
||||
import { isFilterActive } from '@/components/chapter/util.tsx';
|
||||
|
||||
interface IProps {
|
||||
options: ChapterListOptions;
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
NullAndUndefined,
|
||||
TChapter,
|
||||
TranslationKey,
|
||||
} from '@/typings';
|
||||
import { useReducerLocalStorage } from '@/util/useLocalStorage';
|
||||
} from '@/typings.ts';
|
||||
import { useReducerLocalStorage } from '@/util/useLocalStorage.tsx';
|
||||
|
||||
const defaultChapterOptions: ChapterListOptions = {
|
||||
active: false,
|
||||
@@ -11,9 +11,9 @@ import { Fab, Box, styled } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
|
||||
import { DEFAULT_FAB_STYLE } from '@/components/util/StyledFab';
|
||||
import { DEFAULT_FAB_STYLE } from '@/components/util/StyledFab.tsx';
|
||||
import { TranslationKey } from '@/typings.ts';
|
||||
import { Menu } from '@/components/manga/Menu.tsx';
|
||||
import { Menu } from '@/components/menu/Menu.tsx';
|
||||
|
||||
interface SelectionFABProps {
|
||||
children: (handleClose: () => void, setHideMenu: (hide: boolean) => void) => JSX.Element;
|
||||
@@ -15,34 +15,15 @@ import { useTranslation } from 'react-i18next';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import Label from '@mui/icons-material/Label';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { t as translate } from 'i18next';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { actionToTranslationKey, MangaAction, MangaDownloadInfo, Mangas, MangaUnreadInfo } from '@/lib/data/Mangas.ts';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { CategorySelect } from '@/components/navbar/action/CategorySelect.tsx';
|
||||
import { MenuItem } from '@/components/manga/MenuItem.tsx';
|
||||
import { MenuItem } from '@/components/menu/MenuItem.tsx';
|
||||
import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts';
|
||||
|
||||
const ACTION_DISABLES_SELECTION_MODE: MangaAction[] = ['remove_from_library'] as const;
|
||||
|
||||
const createGetMenuItemTitle =
|
||||
(isSingleMode: boolean) =>
|
||||
(action: MangaAction, count: number): string => {
|
||||
const countSuffix = count > 0 ? ` (${count})` : '';
|
||||
return `${translate(
|
||||
actionToTranslationKey[action].action[isSingleMode ? 'single' : 'selected'],
|
||||
)}${countSuffix}`;
|
||||
};
|
||||
|
||||
const createShouldShowMenuItem =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeVisible: boolean = false): boolean =>
|
||||
isSingleMode ? shouldBeVisible : true;
|
||||
|
||||
const createIsMenuItemDisabled =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeDisabled: boolean): boolean =>
|
||||
isSingleMode ? false : shouldBeDisabled;
|
||||
|
||||
type BaseProps = { onClose: (selectionModeState: boolean) => void; setHideMenu: (hide: boolean) => void };
|
||||
|
||||
export type SingleModeProps = {
|
||||
@@ -65,7 +46,7 @@ export const MangaActionMenuItems = ({ manga, handleSelection, selectedMangas =
|
||||
|
||||
const isSingleMode = !!manga;
|
||||
|
||||
const getMenuItemTitle = createGetMenuItemTitle(isSingleMode);
|
||||
const getMenuItemTitle = createGetMenuItemTitle(isSingleMode, actionToTranslationKey);
|
||||
const shouldShowMenuItem = createShouldShowMenuItem(isSingleMode);
|
||||
const isMenuItemDisabled = createIsMenuItemDisabled(isSingleMode);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
|
||||
export const useRefreshManga = (mangaId: string) => {
|
||||
@@ -22,19 +22,3 @@ export const useRefreshManga = (mangaId: string) => {
|
||||
|
||||
return [handleRefresh, { loading: fetchingOnline }] as const;
|
||||
};
|
||||
|
||||
export const useDebounce = <Value>(value: Value, delay: number): Value => {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [value, delay]);
|
||||
|
||||
return debouncedValue;
|
||||
};
|
||||
42
src/components/menu/util.ts
Normal file
42
src/components/menu/util.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 { t as translate } from 'i18next';
|
||||
import { TranslationKey } from '@/typings.ts';
|
||||
|
||||
export const createGetMenuItemTitle =
|
||||
<Action extends string>(
|
||||
isSingleMode: boolean,
|
||||
actionToTranslationKey: Record<
|
||||
Action,
|
||||
{
|
||||
action: {
|
||||
single: TranslationKey;
|
||||
selected: TranslationKey;
|
||||
};
|
||||
success: TranslationKey;
|
||||
error: TranslationKey;
|
||||
}
|
||||
>,
|
||||
) =>
|
||||
(action: Action, count: number): string => {
|
||||
const countSuffix = count > 0 ? ` (${count})` : '';
|
||||
return `${translate(
|
||||
actionToTranslationKey[action].action[isSingleMode ? 'single' : 'selected'],
|
||||
)}${countSuffix}`;
|
||||
};
|
||||
|
||||
export const createShouldShowMenuItem =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeVisible: boolean = false): boolean =>
|
||||
isSingleMode ? shouldBeVisible : true;
|
||||
|
||||
export const createIsMenuItemDisabled =
|
||||
(isSingleMode: boolean) =>
|
||||
(shouldBeDisabled: boolean): boolean =>
|
||||
isSingleMode ? false : shouldBeDisabled;
|
||||
@@ -9,7 +9,7 @@
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import { FormControl, Input, InputAdornment, InputLabel } from '@mui/material';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDebounce } from '@/components/manga/hooks';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
|
||||
interface Props {
|
||||
state: string;
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useSelectableCollection } from '@/components/collection/useSelectableCo
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { SelectableCollectionSelectMode } from '@/components/collection/SelectableCollectionSelectMode.tsx';
|
||||
import { useGetVisibleLibraryMangas } from '@/components/library/useGetVisibleLibraryMangas.ts';
|
||||
import { SelectionFAB } from '@/components/manga/SelectionFAB.tsx';
|
||||
import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx';
|
||||
import { PARTIAL_MANGA_FIELDS } from '@/lib/graphql/Fragments.ts';
|
||||
import { MangaActionMenuItems } from '@/components/manga/MangaActionMenuItems.tsx';
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import { useParams } from 'react-router-dom';
|
||||
import { isNetworkRequestInFlight } from '@apollo/client/core/networkStatus';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext';
|
||||
import { ChapterList } from '@/components/manga/ChapterList';
|
||||
import { useRefreshManga } from '@/components/manga/hooks';
|
||||
import { ChapterList } from '@/components/chapter/ChapterList.tsx';
|
||||
import { useRefreshManga } from '@/components/manga/useRefreshManga.ts';
|
||||
import { MangaDetails } from '@/components/manga/MangaDetails';
|
||||
import { MangaToolbarMenu } from '@/components/manga/MangaToolbarMenu';
|
||||
import { EmptyView } from '@/components/util/EmptyView';
|
||||
|
||||
@@ -27,7 +27,7 @@ import { VerticalPager } from '@/components/reader/pager/VerticalPager';
|
||||
import { ReaderNavBar } from '@/components/navbar/ReaderNavBar';
|
||||
import { makeToast } from '@/components/util/Toast';
|
||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||
import { useDebounce } from '@/components/manga/hooks.ts';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { translateExtensionLanguage } from '@/screens/util/Extensions';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
import { LangSelect } from '@/components/navbar/action/LangSelect';
|
||||
import { MangaGrid } from '@/components/MangaGrid';
|
||||
import { useDebounce } from '@/components/manga/hooks';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||
|
||||
type SourceLoadingState = { isLoading: boolean; hasResults: boolean; emptySearch: boolean };
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
AbortableApolloUseMutationPaginatedResponse,
|
||||
SPECIAL_ED_SOURCES,
|
||||
} from '@/lib/requests/RequestManager.ts';
|
||||
import { useDebounce } from '@/components/manga/hooks';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext';
|
||||
import { SourceGridLayout } from '@/components/source/GridLayouts';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
|
||||
25
src/util/useDebounce.ts
Normal file
25
src/util/useDebounce.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
|
||||
export const useDebounce = <Value>(value: Value, delay: number): Value => {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [value, delay]);
|
||||
|
||||
return debouncedValue;
|
||||
};
|
||||
Reference in New Issue
Block a user