From b261d80deb006aa9f3703f0acfb5890fc5a5f05b Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Thu, 3 Oct 2024 04:02:59 +0200 Subject: [PATCH] tmp - Reader overlay desktop nav bar --- public/locales/en.json | 19 ++- .../buttons/ValueRotationButton.tsx | 40 +++++ src/modules/metadata/Metadata.constants.ts | 26 ++- .../reader-deprecated/Reader.constants.ts | 12 +- src/modules/reader-deprecated/Reader.types.ts | 5 + .../overlay/navigation/ReaderChapterList.tsx | 10 +- .../desktop/ReaderNavBarDesktop.tsx | 151 ++++++++++++++++++ .../desktop/ReaderNavBarDesktopActions.tsx | 94 +++++++++++ .../ReaderNavBarDesktopChapterNavigation.tsx | 99 ++++++++++++ .../desktop/ReaderNavBarDesktopMetadata.tsx | 27 ++++ .../desktop/ReaderNavBarDesktopNextButton.tsx | 23 +++ .../ReaderNavBarDesktopNextPreviousButton.tsx | 25 +++ .../ReaderNavBarDesktopPageNavigation.tsx | 59 +++++++ .../ReaderNavBarDesktopPreviousButton.tsx | 25 +++ .../navigation/desktop/ReaderNavContainer.tsx | 18 +++ .../ReaderNavBarDesktopOffsetDoubleSpread.tsx | 34 ++++ .../ReaderNavBarDesktopPageScale.tsx | 78 +++++++++ .../ReaderNavBarDesktopQuickSettings.tsx | 57 +++++++ .../ReaderNavBarDesktopReadingDirection.tsx | 40 +++++ .../ReaderNavBarDesktopReadingMode.tsx | 50 ++++++ .../mobile/ReaderBottomBarMobile.tsx | 9 +- .../reader/types/ReaderOverlay.types.ts | 19 ++- 22 files changed, 905 insertions(+), 15 deletions(-) create mode 100644 src/modules/core/components/buttons/ValueRotationButton.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktop.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopChapterNavigation.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopMetadata.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPageNavigation.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/ReaderNavContainer.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopOffsetDoubleSpread.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopPageScale.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingDirection.tsx create mode 100644 src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingMode.tsx diff --git a/public/locales/en.json b/public/locales/en.json index b05e450300..1449582bee 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -752,9 +752,12 @@ "chapter_list": "Chapter list", "close_menu": "Close menu", "exit": "Exit reader", - "next_chapter": "Next Chapter", + "next_chapter": "Next chapter", + "next_page": "Next page", "open_menu": "Open menu", - "previous_chapter": "Previous Chapter" + "previous_chapter": "Previous chapter", + "previous_page": "Previous page", + "retry_load_pages": "Retry errored pages" }, "error": { "label": { @@ -766,7 +769,8 @@ "page_info": { "label": { "currently_on_page": "Currently on page", - "of_max_pages": "of {{maxPages}}" + "of_max_pages": "of {{maxPages}}", + "page": "Page" } }, "settings": { @@ -779,7 +783,7 @@ "label": { "fit_page_to_window": "Fit page to window", "load_next_chapter": "Load next chapter at ending", - "offset_first_page": "Offset first page", + "offset_double_spread": "Offset double spreads", "reader_type": "Reader type", "reader_width": "Reader width", "reading_direction": "Reading direction", @@ -789,6 +793,13 @@ "skip_dup_chapters": "Skip duplicate chapters", "static_navigation": "Static navigation" }, + "page_scale": { + "height": "Fit height", + "original": "Original size", + "screen": "Fit screen", + "stretch": "Stretch small pages", + "width": "Fit width" + }, "reader_type": { "label": { "continuous_horizontal": "Continues horizontal", diff --git a/src/modules/core/components/buttons/ValueRotationButton.tsx b/src/modules/core/components/buttons/ValueRotationButton.tsx new file mode 100644 index 0000000000..c307d5942a --- /dev/null +++ b/src/modules/core/components/buttons/ValueRotationButton.tsx @@ -0,0 +1,40 @@ +/* + * 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 { useTranslation } from 'react-i18next'; +import { useMemo } from 'react'; +import Button from '@mui/material/Button'; +import { ValueToDisplayData } from '@/modules/core/Core.types.ts'; + +export const ValueRotationButton = ({ + value, + values, + setValue, + valueToDisplayData, +}: { + value: Value; + values: Value[]; + setValue: (value: Value) => void; + valueToDisplayData: ValueToDisplayData; +}) => { + const { t } = useTranslation(); + + const indexOfValue = useMemo(() => values.indexOf(value), [value, values]); + + return ( + + ); +}; diff --git a/src/modules/metadata/Metadata.constants.ts b/src/modules/metadata/Metadata.constants.ts index 106b79f221..ffbb17a04e 100644 --- a/src/modules/metadata/Metadata.constants.ts +++ b/src/modules/metadata/Metadata.constants.ts @@ -7,7 +7,7 @@ */ import { AppMetadataKeys, IMetadataMigration } from '@/modules/metadata/Metadata.types.ts'; -import { ReadingMode } from '@/modules/reader-deprecated/Reader.types.ts'; +import { ReaderPageScaleMode, ReadingMode } from '@/modules/reader-deprecated/Reader.types.ts'; export const APP_METADATA_KEY_PREFIX = 'webUI_'; @@ -138,6 +138,18 @@ export const METADATA_MIGRATIONS: IMetadataMigration[] = [ oldKey: 'readerType', newKey: 'readingMode', }, + { + oldKey: 'offsetFirstPage', + newKey: 'shouldOffsetDoubleSpreads', + }, + { + oldKey: 'fitPageToWindow', + newKey: 'pageScaleMode', + }, + { + oldKey: 'scalePage', + newKey: 'shouldScalePage', + }, ], values: [ // START: readerType @@ -182,6 +194,18 @@ export const METADATA_MIGRATIONS: IMetadataMigration[] = [ newValue: `${ReadingMode.CONTINUOUS_HORIZONTAL}`, }, // END: readerType + // START: fitPageToWindow + { + key: 'fitPageToWindow', + oldValue: 'false', + newValue: `${ReaderPageScaleMode.ORIGINAL}`, + }, + { + key: 'fitPageToWindow', + oldValue: 'true', + newValue: `${ReaderPageScaleMode.SCREEN}`, + }, + // END: fitPageToWindow ], }, ]; diff --git a/src/modules/reader-deprecated/Reader.constants.ts b/src/modules/reader-deprecated/Reader.constants.ts index 142d58aca0..f7243c0e39 100644 --- a/src/modules/reader-deprecated/Reader.constants.ts +++ b/src/modules/reader-deprecated/Reader.constants.ts @@ -6,7 +6,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { IReaderSettings, ReadingDirection, ReadingMode } from '@/modules/reader-deprecated/Reader.types.ts'; +import { + IReaderSettings, + ReaderPageScaleMode, + ReadingDirection, + ReadingMode, +} from '@/modules/reader-deprecated/Reader.types.ts'; +import { TapZoneLayouts } from '@/modules/reader/types/TapZoneLayout.types.ts'; export const DEFAULT_READER_SETTINGS: IReaderSettings = { staticNav: false, @@ -18,6 +24,10 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = { readerType: 'ContinuesVertical', offsetFirstPage: false, readerWidth: 50, + tapZoneLayout: TapZoneLayouts.STANDARD, + pageScaleMode: ReaderPageScaleMode.ORIGINAL, + shouldScalePage: false, + shouldOffsetDoubleSpreads: false, readingDirection: ReadingDirection.LTR, readingMode: ReadingMode.SINGLE_PAGE, }; diff --git a/src/modules/reader-deprecated/Reader.types.ts b/src/modules/reader-deprecated/Reader.types.ts index e168d3971d..af25e8fd02 100644 --- a/src/modules/reader-deprecated/Reader.types.ts +++ b/src/modules/reader-deprecated/Reader.types.ts @@ -7,6 +7,7 @@ */ import { GetChaptersReaderQuery, MangaReaderFieldsFragment } from '@/lib/graphql/generated/graphql.ts'; +import { TapZoneLayouts } from '@/modules/reader/types/TapZoneLayout.types.ts'; export type ReaderType = | 'ContinuesVertical' @@ -49,6 +50,10 @@ export interface IReaderSettings { readerType: ReaderType; offsetFirstPage: boolean; readerWidth: number; + tapZoneLayout: TapZoneLayouts; + pageScaleMode: ReaderPageScaleMode; + shouldScalePage: boolean; + shouldOffsetDoubleSpreads: boolean; readingDirection: ReadingDirection; readingMode: ReadingMode; } diff --git a/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx b/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx index b2e1c5ea22..37aceca41c 100644 --- a/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx +++ b/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { Virtuoso } from 'react-virtuoso'; +import { Virtuoso, VirtuosoProps } from 'react-virtuoso'; import { useMemo } from 'react'; import { IChapterWithMeta } from '@/modules/chapter/components/ChapterList.tsx'; import { ChapterCard } from '@/modules/chapter/components/cards/ChapterCard.tsx'; @@ -16,12 +16,13 @@ import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts'; export const ReaderChapterList = ({ currentChapter, chapters, -}: Required>) => { + style, +}: Pick & Pick, 'style'>) => { const { data: downloaderData } = requestManager.useGetDownloadStatus(); const queue = downloaderData?.downloadStatus.queue ?? []; const currentChapterIndex = useMemo( - () => chapters.findIndex((chapter) => chapter.id === currentChapter.id), + () => chapters.findIndex((chapter) => chapter.id === currentChapter?.id), [currentChapter, chapters], ); @@ -43,8 +44,7 @@ export const ReaderChapterList = ({ { + const wasNavBarStaticRef = useRef(staticNav); + const wasNavBarStaticPreviousRef = useRef(staticNav); + + const resetWasNavBarStaticValue = wasNavBarStaticPreviousRef.current !== wasNavBarStaticRef.current && !isVisible; + if (resetWasNavBarStaticValue) { + wasNavBarStaticRef.current = false; + } + + const didNavBarStaticValueChange = wasNavBarStaticPreviousRef.current !== staticNav; + if (didNavBarStaticValueChange) { + wasNavBarStaticRef.current = wasNavBarStaticPreviousRef.current; + wasNavBarStaticPreviousRef.current = staticNav; + } + + return wasNavBarStaticRef.current; +}; + +export const ReaderNavBarDesktop = ({ isVisible, setIsVisible }: ReaderNavBarDesktopProps) => { + const { t } = useTranslation(); + const { setReaderNavBarWidth } = useNavBarContext(); + const { manga } = useReaderStateMangaContext(); + const { chapters, currentChapter, nextChapter, previousChapter } = useReaderStateChaptersContext(); + const { pages, currentPageIndex, setCurrentPageIndex } = userReaderStatePagesContext(); + + const handleBack = useBackButton(); + const getOptionForDirection = useGetOptionForDirection(); + + const updateReaderSettings = createUpdateReaderSettings(manga ?? { id: -1 }, () => + makeToast(t('reader.settings.error.label.failed_to_save_settings')), + ); + + const defaultReaderSettings = useDefaultReaderSettings(); + const readerSettings = getReaderSettingsFor(manga, defaultReaderSettings.settings); + + const [navBarElement, setNavBarElement] = useState(); + useResizeObserver( + navBarElement, + useCallback(() => { + if (!readerSettings?.staticNav) { + return; + } + + setReaderNavBarWidth(navBarElement!.clientWidth); + }, [navBarElement, readerSettings?.staticNav]), + ); + + const wasNavBarStatic = useGetPreviousNavBarStaticValue(isVisible, readerSettings.staticNav); + const changedNavBarStaticValue = wasNavBarStatic && isVisible; + const drawerTransitionDuration = changedNavBarStaticValue ? 0 : undefined; + + if (!manga || !currentChapter) { + return null; + } + + return ( + { + setIsVisible(false); + setReaderNavBarWidth(0); + }} + transitionDuration={drawerTransitionDuration} + > + setNavBarElement(ref)} + sx={{ backgroundColor: 'background.paper', pointerEvents: 'all' }} + > + + + + + {getOptionForDirection(, )} + + + + { + setReaderNavBarWidth(0); + updateReaderSettings('staticNav', !readerSettings.staticNav); + }} + color={readerSettings.staticNav ? 'primary' : 'inherit'} + > + + + + + + + + + + + + + + + + + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx new file mode 100644 index 0000000000..9f2db04f62 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx @@ -0,0 +1,94 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import Tooltip from '@mui/material/Tooltip'; +import { useTranslation } from 'react-i18next'; +import IconButton from '@mui/material/IconButton'; +import BookmarkIcon from '@mui/icons-material/Bookmark'; +import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import DownloadIcon from '@mui/icons-material/Download'; +import ReplayIcon from '@mui/icons-material/Replay'; +import { useMemo } from 'react'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { actionToTranslationKey, ChapterAction, Chapters } from '@/modules/chapter/services/Chapters.ts'; +import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts'; +import { requestManager } from '@/lib/requests/RequestManager.ts'; +import { DownloadStateIndicator } from '@/modules/core/components/DownloadStateIndicator.tsx'; +import { DownloadStatusFieldsFragment } from '@/lib/graphql/generated/graphql.ts'; + +const DownloadButton = ({ + currentChapter, + downloadChapter, +}: Required> & { + downloadChapter?: DownloadStatusFieldsFragment['queue'][number]; +}) => { + const { t } = useTranslation(); + + if (Chapters.isDownloaded(currentChapter)) { + return ( + + Chapters.performAction('delete', [currentChapter.id], {})} color="inherit"> + + + + ); + } + + if (downloadChapter) { + return ; + } + + return ( + + Chapters.performAction('download', [currentChapter.id], {})} color="inherit"> + + + + ); +}; + +export const ReaderNavBarDesktopActions = ({ + currentChapter, +}: Required>) => { + const { id, isBookmarked, realUrl } = currentChapter; + + const { t } = useTranslation(); + + const { data: downloaderData } = requestManager.useGetDownloadStatus(); + const queue = downloaderData?.downloadStatus.queue ?? []; + + const downloadChapter = useMemo( + () => queue.find((queueItem) => queueItem.chapter.id === currentChapter.id), + [queue, id], + ); + + const bookmarkAction: Extract = isBookmarked ? 'unbookmark' : 'bookmark'; + + return ( + + + Chapters.performAction(bookmarkAction, [id], {})} color="inherit"> + {isBookmarked ? : } + + + + + + + + + + + + + + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopChapterNavigation.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopChapterNavigation.tsx new file mode 100644 index 0000000000..b8d12f1058 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopChapterNavigation.tsx @@ -0,0 +1,99 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import { useTranslation } from 'react-i18next'; +import Box from '@mui/material/Box'; +import MenuItem from '@mui/material/MenuItem'; +import { ContextType } from 'react'; +import Popover from '@mui/material/Popover'; +import { bindPopover, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import { Link } from 'react-router-dom'; +import { Select } from '@/modules/core/components/inputs/Select.tsx'; +import { ReaderNavBarDesktopPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx'; +import { ReaderNavBarDesktopNextButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx'; +import { Chapters } from '@/modules/chapter/services/Chapters.ts'; +import { ReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx'; +import { ReaderChapterList } from '@/modules/reader/components/overlay/navigation/ReaderChapterList.tsx'; + +export const ReaderNavBarDesktopChapterNavigation = ({ + currentChapter, + previousChapter, + nextChapter, + chapters = [], +}: Pick< + ContextType, + 'chapters' | 'currentChapter' | 'previousChapter' | 'nextChapter' +>) => { + const { t } = useTranslation(); + + const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' }); + + if (!currentChapter) { + return null; + } + + return ( + + + + {t('chapter.title_one')} + + + + + + + + + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopMetadata.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopMetadata.tsx new file mode 100644 index 0000000000..b68837ae1b --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopMetadata.tsx @@ -0,0 +1,27 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines'; + +export const ReaderNavBarDesktopMetadata = ({ + mangaTitle, + chapterTitle, +}: { + mangaTitle: string; + chapterTitle: string; +}) => ( + + + {mangaTitle} + + + {chapterTitle} + + +); diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx new file mode 100644 index 0000000000..8172ed500d --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx @@ -0,0 +1,23 @@ +/* + * 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 KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { ComponentProps } from 'react'; +import { useGetOptionForDirection } from '@/theme.tsx'; +import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx'; + +export const ReaderNavBarDesktopNextButton = (props: ComponentProps) => { + const getOptionForDirection = useGetOptionForDirection(); + + return ( + + {getOptionForDirection(, )} + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx new file mode 100644 index 0000000000..6592fb9c31 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx @@ -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 Tooltip from '@mui/material/Tooltip'; +import { ComponentProps } from 'react'; +import { CustomIconButton } from '@/modules/core/components/buttons/CustomIconButton'; + +export const ReaderNavBarDesktopNextPreviousButton = ({ + title, + children, + ...customIconButtonProps +}: ComponentProps & { + title: string; +}) => ( + + + {children} + + +); diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPageNavigation.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPageNavigation.tsx new file mode 100644 index 0000000000..bf60909523 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPageNavigation.tsx @@ -0,0 +1,59 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import { useTranslation } from 'react-i18next'; +import MenuItem from '@mui/material/MenuItem'; +import { useMemo } from 'react'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import { Select } from '@/modules/core/components/inputs/Select.tsx'; +import { getNextPageIndex, getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; +import { ReaderNavBarDesktopPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx'; +import { ReaderNavBarDesktopNextButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextButton.tsx'; +import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts'; + +export const ReaderNavBarDesktopPageNavigation = ({ + currentPageIndex, + setCurrentPageIndex, + pages, +}: Pick) => { + const { t } = useTranslation(); + + const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]); + + return ( + + setCurrentPageIndex(getNextPageIndex('previous', currentPage[2], pages))} + /> + + {t('reader.page_info.label.page')} + + + setCurrentPageIndex(getNextPageIndex('next', currentPage[2], pages))} + /> + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx new file mode 100644 index 0000000000..4ead8e2019 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopPreviousButton.tsx @@ -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 KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { ComponentProps } from 'react'; +import { useGetOptionForDirection } from '@/theme.tsx'; +import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx'; + +export const ReaderNavBarDesktopPreviousButton = ( + props: ComponentProps, +) => { + const getOptionForDirection = useGetOptionForDirection(); + + return ( + + {getOptionForDirection(, )} + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavContainer.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavContainer.tsx new file mode 100644 index 0000000000..f00d906a7f --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavContainer.tsx @@ -0,0 +1,18 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import { styled } from '@mui/material/styles'; + +export const ReaderNavContainer = styled(Stack)({ + width: '400px', + minWidth: '400px', + maxWidth: '400px', + height: '100vh', + overflowY: 'auto', +}); diff --git a/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopOffsetDoubleSpread.tsx b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopOffsetDoubleSpread.tsx new file mode 100644 index 0000000000..3aa665777a --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopOffsetDoubleSpread.tsx @@ -0,0 +1,34 @@ +/* + * 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 { useTranslation } from 'react-i18next'; +import Button from '@mui/material/Button'; +import { OffsetDoubleSpreadIcon } from '@/assets/icons/svg/OffsetDoubleSpreadIcon.tsx'; +import { IReaderSettings } from '@/modules/reader-deprecated/Reader.types.ts'; + +export const ReaderNavBarDesktopOffsetDoubleSpread = ({ + shouldOffsetDoubleSpreads, + setShouldOffsetDoubleSpreads, +}: Pick & { + setShouldOffsetDoubleSpreads: (offset: boolean) => void; +}) => { + const { t } = useTranslation(); + + return ( + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopPageScale.tsx b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopPageScale.tsx new file mode 100644 index 0000000000..6d1e190f33 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopPageScale.tsx @@ -0,0 +1,78 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import Tooltip from '@mui/material/Tooltip'; +import { useTranslation } from 'react-i18next'; +import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap'; +import ExpandIcon from '@mui/icons-material/Expand'; +import CropOriginalIcon from '@mui/icons-material/CropOriginal'; +import FitScreenIcon from '@mui/icons-material/FitScreen'; +import { CustomIconButton } from '@/modules/core/components/buttons/CustomIconButton.tsx'; +import { ValueRotationButton } from '@/modules/core/components/buttons/ValueRotationButton.tsx'; +import { IReaderSettings, ReaderPageScaleMode } from '@/modules/reader-deprecated/Reader.types.ts'; +import { ValueToDisplayData } from '@/modules/core/Core.types'; + +const VALUE_TO_DISPLAY_DATA: ValueToDisplayData = { + [ReaderPageScaleMode.WIDTH]: { + title: 'reader.settings.page_scale.width', + icon: , + }, + [ReaderPageScaleMode.HEIGHT]: { + title: 'reader.settings.page_scale.height', + icon: , + }, + [ReaderPageScaleMode.SCREEN]: { + title: 'reader.settings.page_scale.screen', + icon: , + }, + [ReaderPageScaleMode.ORIGINAL]: { + title: 'reader.settings.page_scale.original', + icon: , + }, +}; + +const READER_PAGE_SCALE_MODE_VALUES = Object.values(ReaderPageScaleMode).filter((value) => typeof value === 'number'); + +export const ReaderNavBarDesktopPageScale = ({ + pageScaleMode, + shouldScalePage, + updateSetting, +}: Pick & { + updateSetting: >( + setting: Setting, + value: IReaderSettings[Setting], + ) => void; +}) => { + const { t } = useTranslation(); + + const isPageScalingPossible = pageScaleMode !== ReaderPageScaleMode.ORIGINAL; + + return ( + + updateSetting('pageScaleMode', value)} + valueToDisplayData={VALUE_TO_DISPLAY_DATA} + /> + {isPageScalingPossible && ( + + updateSetting('shouldScalePage', !shouldScalePage)} + sx={{ minWidth: 0 }} + variant="contained" + color={shouldScalePage ? 'secondary' : 'primary'} + > + + + + )} + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx new file mode 100644 index 0000000000..72a5d0cac5 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx @@ -0,0 +1,57 @@ +/* + * 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 Stack from '@mui/material/Stack'; +import { useTranslation } from 'react-i18next'; +import Button from '@mui/material/Button'; +import SettingsIcon from '@mui/icons-material/Settings'; +import { ReaderNavBarDesktopPageScale } from '@/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopPageScale.tsx'; +import { ReaderNavBarDesktopReadingMode } from '@/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingMode.tsx'; +import { ReaderNavBarDesktopOffsetDoubleSpread } from '@/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopOffsetDoubleSpread.tsx'; +import { ReaderNavBarDesktopReadingDirection } from '@/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingDirection.tsx'; +import { IReaderSettings } from '@/modules/reader-deprecated/Reader.types.ts'; + +export const ReaderNavBarDesktopQuickSettings = ({ + settings: { readingMode, shouldOffsetDoubleSpreads, pageScaleMode, shouldScalePage, readingDirection }, + updateSetting, +}: { + settings: IReaderSettings; + updateSetting: (setting: Setting, value: IReaderSettings[Setting]) => void; +}) => { + const { t } = useTranslation(); + + return ( + + updateSetting('readingMode', value)} + /> + updateSetting('shouldOffsetDoubleSpreads', value)} + /> + + updateSetting('readingDirection', value)} + /> + + + ); +}; diff --git a/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingDirection.tsx b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingDirection.tsx new file mode 100644 index 0000000000..b1593796b6 --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingDirection.tsx @@ -0,0 +1,40 @@ +/* + * 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 ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft'; +import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight'; +import { ValueRotationButton } from '@/modules/core/components/buttons/ValueRotationButton.tsx'; +import { IReaderSettings, ReadingDirection } from '@/modules/reader-deprecated/Reader.types.ts'; +import { ValueToDisplayData } from '@/modules/core/Core.types.ts'; + +const VALUE_TO_DISPLAY_DATA: ValueToDisplayData = { + [ReadingDirection.LTR]: { + title: 'reader.settings.reading_direction.rtl', + icon: , + }, + [ReadingDirection.RTL]: { + title: 'reader.settings.reading_direction.ltr', + icon: , + }, +}; + +const READING_DIRECTION_VALUES = Object.values(ReadingDirection).filter((value) => typeof value === 'number'); + +export const ReaderNavBarDesktopReadingDirection = ({ + readingDirection, + setReadingDirection, +}: Pick & { + setReadingDirection: (readingDirection: ReadingDirection) => void; +}) => ( + +); diff --git a/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingMode.tsx b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingMode.tsx new file mode 100644 index 0000000000..79b67f9e2b --- /dev/null +++ b/src/modules/reader/components/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopReadingMode.tsx @@ -0,0 +1,50 @@ +/* + * 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 { SinglePageIcon } from '@/assets/icons/svg/SinglePageIcon.tsx'; +import { DoublePageIcon } from '@/assets/icons/svg/DoublePageIcon.tsx'; +import { ContinuousVerticalPageIcon } from '@/assets/icons/svg/ContinuousVerticalPageIcon.tsx'; +import { ContinuousHorizontalPageIcon } from '@/assets/icons/svg/ContinuousHorizontalPageIcon.tsx'; +import { ValueRotationButton } from '@/modules/core/components/buttons/ValueRotationButton.tsx'; +import { ValueToDisplayData } from '@/modules/core/Core.types.tsx'; +import { IReaderSettings, ReadingMode } from '@/modules/reader-deprecated/Reader.types.ts'; + +const VALUE_TO_DISPLAY_DATA: ValueToDisplayData = { + [ReadingMode.SINGLE_PAGE]: { + title: 'reader.settings.reader_type.label.single_page', + icon: , + }, + [ReadingMode.DOUBLE_PAGE]: { + title: 'reader.settings.reader_type.label.double_page', + icon: , + }, + [ReadingMode.CONTINUOUS_VERTICAL]: { + title: 'reader.settings.reader_type.label.continuous_horizontal', + icon: , + }, + [ReadingMode.CONTINUOUS_HORIZONTAL]: { + title: 'reader.settings.reader_type.label.continuous_vertical', + icon: , + }, +}; + +const READING_MODE_VALUES = Object.values(ReadingMode).filter((value) => typeof value === 'number'); + +export const ReaderNavBarDesktopReadingMode = ({ + readingMode, + setReadingMode, +}: Pick & { + setReadingMode: (mode: ReadingMode) => void; +}) => ( + +); diff --git a/src/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx b/src/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx index 6ab8a2ceab..e9fe940c67 100644 --- a/src/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx +++ b/src/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx @@ -86,7 +86,14 @@ export const ReaderBottomBarMobile = ({ openSettings, isVisible }: ReaderBottomB {chapterListPopupState.isOpen && ( - + )} diff --git a/src/modules/reader/types/ReaderOverlay.types.ts b/src/modules/reader/types/ReaderOverlay.types.ts index 82870decc9..e51227b86e 100644 --- a/src/modules/reader/types/ReaderOverlay.types.ts +++ b/src/modules/reader/types/ReaderOverlay.types.ts @@ -7,7 +7,12 @@ */ import { ChapterType, MangaType } from '@/lib/graphql/generated/graphql.ts'; -import { ChapterBookmarkInfo, ChapterRealUrlInfo } from '@/modules/chapter/services/Chapters.ts'; +import { + ChapterBookmarkInfo, + ChapterMangaInfo, + ChapterNumberInfo, + ChapterRealUrlInfo, +} from '@/modules/chapter/services/Chapters.ts'; export interface BaseReaderOverlayProps { isVisible: boolean; @@ -16,9 +21,17 @@ export interface BaseReaderOverlayProps { export interface MobileHeaderProps extends Pick { manga: Pick; - chapter: Pick & ChapterBookmarkInfo & ChapterRealUrlInfo; + chapter: Pick & + ChapterBookmarkInfo & + ChapterRealUrlInfo & + ChapterNumberInfo & + ChapterMangaInfo; } -export interface ReaderBottomBarMobileProps extends Omit { +interface ReaderNavBarBaseProps extends BaseReaderOverlayProps { openSettings: () => void; } + +export interface ReaderBottomBarMobileProps extends Omit {} + +export interface ReaderNavBarDesktopProps extends ReaderNavBarBaseProps {}