/* * 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 IconButton from '@mui/material/IconButton'; import CloseIcon from '@mui/icons-material/Close'; import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import { useEffect, useMemo, useState } from 'react'; import Typography from '@mui/material/Typography'; import { useLocation, useNavigate } from 'react-router-dom'; import Slide from '@mui/material/Slide'; import Fade from '@mui/material/Fade'; import Zoom from '@mui/material/Zoom'; import Divider from '@mui/material/Divider'; import FormControl from '@mui/material/FormControl'; import MenuItem from '@mui/material/MenuItem'; import Tooltip from '@mui/material/Tooltip'; import { styled } from '@mui/material/styles'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import Collapse from '@mui/material/Collapse'; import { useTranslation } from 'react-i18next'; import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings } from '@/typings'; import { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions'; import { useBackButton } from '@/util/useBackButton.ts'; import { Select } from '@/components/atoms/Select.tsx'; import { getOptionForDirection } from '@/theme.ts'; import { ChapterType } from '@/lib/graphql/generated/graphql.ts'; import { MangaChapterCountInfo, MangaIdInfo } from '@/lib/data/Mangas.ts'; const Root = styled('div')({ zIndex: 10, }); const NavContainer = styled('div')(({ theme }) => ({ top: 0, left: 0, width: '300px', minWidth: '300px', height: '100vh', overflowY: 'auto', backgroundColor: theme.palette.background.default, '& header': { backgroundColor: theme.palette.action.hover, display: 'flex', alignItems: 'center', minHeight: '64px', paddingLeft: '24px', paddingRight: '24px', transition: 'left 2s ease', '& button': { flexGrow: 0, flexShrink: 0, }, '& button:nth-child(1)': { marginRight: '16px', }, '& h1': { fontSize: '1.25rem', flexGrow: 1, }, }, })); const Navigation = styled('div')({ margin: '0 16px', '& > span:nth-child(1)': { textAlign: 'center', display: 'block', marginTop: '16px', }, }); const PageNavigation = styled('div')({ display: 'flex', flexWrap: 'wrap', alignContent: 'center', alignItems: 'center', justifyContent: 'center', }); const ChapterNavigation = styled('div')({ display: 'grid', gridTemplateRows: 'auto auto auto', gridTemplateColumns: '0fr 1fr 0fr', gridTemplateAreas: '"pre current next"', gridColumnGap: '5px', margin: '10px 0', '& a': { textDecoration: 'none', color: 'inherit', display: 'flex', flexWrap: 'wrap', alignContent: 'center', }, }); const OpenDrawerButton = styled(IconButton)(({ theme }) => ({ position: 'fixed', top: 0 + 20, left: 10 + 20, height: '40px', width: '40px', borderRadius: 5, backgroundColor: theme.palette.custom.main, '&:hover': { backgroundColor: theme.palette.custom.light, }, })); interface IProps { settings: IReaderSettings; setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void; manga: MangaIdInfo & MangaChapterCountInfo; chapter: Pick; chapters: Pick[]; curPage: number; scrollToPage: (page: number) => void; openNextChapter: (offset: ChapterOffset) => void; retrievingNextChapter: boolean; } export function ReaderNavBar(props: IProps) { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation<{ prevDrawerOpen?: boolean; prevSettingsCollapseOpen?: boolean; }>(); const { prevDrawerOpen, prevSettingsCollapseOpen } = location.state ?? {}; const { settings, setSettingValue, manga, chapter, chapters, curPage, scrollToPage, openNextChapter, retrievingNextChapter, } = props; const handleBack = useBackButton(); const hasMultipleScanlators = useMemo( () => new Set(chapters.map(({ scanlator }) => scanlator)).size > 1, [chapters], ); const [drawerOpen, setDrawerOpen] = useState(settings.staticNav || prevDrawerOpen); const [updateDrawerOnRender, setUpdateDrawerOnRender] = useState(true); const [hideOpenButton, setHideOpenButton] = useState(settings.staticNav || prevDrawerOpen); const [prevScrollPos, setPrevScrollPos] = useState(0); const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(prevSettingsCollapseOpen ?? true); const disableChapterNavButtons = retrievingNextChapter; const updateSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => { // prevent closing the navBar when updating the "staticNav" setting setUpdateDrawerOnRender(key !== 'staticNav'); setSettingValue(key, value, persist); }; const updateDrawer = (open: boolean) => { setDrawerOpen(open); setHideOpenButton(open); }; const handleScroll = () => { const currentScrollPos = window.pageYOffset; if (Math.abs(currentScrollPos - prevScrollPos) > 20) { setHideOpenButton(currentScrollPos > prevScrollPos); setPrevScrollPos(currentScrollPos); } }; useEffect(() => { if (updateDrawerOnRender) { updateDrawer(settings.staticNav); } }, [settings.staticNav]); useEffect(() => { window.addEventListener('scroll', handleScroll); const rootEl: HTMLDivElement = document.querySelector('#root')!; const mainContainer: HTMLDivElement = document.querySelector('#appMainContainer')!; // main container and root div need to change styles... rootEl.style.display = 'flex'; rootEl.style.flexDirection = 'column'; mainContainer.style.display = 'none'; return () => { rootEl.style.display = 'block'; mainContainer.style.display = 'block'; window.removeEventListener('scroll', handleScroll); }; }, [handleScroll]); // handleScroll changes on every render return (
{!settings.staticNav && ( updateDrawer(false)} size="large" > {getOptionForDirection(, )} )} {chapter.name}
setSettingsCollapseOpen(!settingsCollapseOpen)} size="large" > {settingsCollapseOpen && } {!settingsCollapseOpen && } {t('reader.page_info.label.currently_on_page')} {t('reader.page_info.label.of_max_pages', { maxPages: chapter.pageCount })} openNextChapter(ChapterOffset.PREV)} > {getOptionForDirection(, )} = manga.chapters.totalCount } onClick={() => openNextChapter(ChapterOffset.NEXT)} > {getOptionForDirection(, )}
updateDrawer(true)} size="large" > {getOptionForDirection(, )}
); }