From 04837983fa1b9c719efff784003136b5ce9f293f Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Fri, 19 Mar 2021 14:52:20 +0330 Subject: [PATCH] reader ui changes --- webUI/react/src/App.tsx | 7 ++- webUI/react/src/components/MangaDetails.tsx | 7 +-- webUI/react/src/components/NavBar.tsx | 2 +- webUI/react/src/components/Page.tsx | 35 +++++++++++-- webUI/react/src/components/ReaderNavBar.tsx | 56 ++++++++++++++------- webUI/react/src/screens/Manga.tsx | 11 +++- webUI/react/src/screens/Reader.tsx | 32 +++++++++--- webUI/react/src/util/useLocalStorage.tsx | 11 ++-- 8 files changed, 122 insertions(+), 39 deletions(-) diff --git a/webUI/react/src/App.tsx b/webUI/react/src/App.tsx index 0f5a752..e5be035 100644 --- a/webUI/react/src/App.tsx +++ b/webUI/react/src/App.tsx @@ -66,7 +66,12 @@ export default function App() { - + diff --git a/webUI/react/src/components/MangaDetails.tsx b/webUI/react/src/components/MangaDetails.tsx index ae9d3e3..45c4e52 100644 --- a/webUI/react/src/components/MangaDetails.tsx +++ b/webUI/react/src/components/MangaDetails.tsx @@ -18,9 +18,10 @@ import CategorySelect from './CategorySelect'; const useStyles = (inLibrary: string) => makeStyles((theme: Theme) => ({ root: { width: '100%', - // [theme.breakpoints.up('md')]: { - // display: 'flex', - // }, + [theme.breakpoints.up('md')]: { + position: 'fixed', + width: '50vw', + }, }, top: { padding: '10px', diff --git a/webUI/react/src/components/NavBar.tsx b/webUI/react/src/components/NavBar.tsx index 5bf8685..702dedb 100644 --- a/webUI/react/src/components/NavBar.tsx +++ b/webUI/react/src/components/NavBar.tsx @@ -39,7 +39,7 @@ export default function NavBar() { {!override.status && (
- + > } function LazyImage(props: IProps) { const classes = useStyles(); - const { src, index } = props; + const { src, index, setCurPage } = props; const [imageSrc, setImagsrc] = useState(''); + const ref = useRef(null); + + const handleScroll = () => { + if (ref.current) { + const rect = ref.current.getBoundingClientRect(); + if (rect.y < 0 && rect.y + rect.height > 0) { + setCurPage(index); + } + } + }; + + useEffect(() => { + window.addEventListener('scroll', handleScroll); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, [handleScroll]); useEffect(() => { const img = new Image(); @@ -48,12 +68,17 @@ function LazyImage(props: IProps) { } return ( - {`Page + {`Page ); } export default function Page(props: IProps) { - const { src, index } = props; + const { src, index, setCurPage } = props; const classes = useStyles(); return ( @@ -67,7 +92,7 @@ export default function Page(props: IProps) {
)} > - + ); diff --git a/webUI/react/src/components/ReaderNavBar.tsx b/webUI/react/src/components/ReaderNavBar.tsx index d70fe9d..f92f444 100644 --- a/webUI/react/src/components/ReaderNavBar.tsx +++ b/webUI/react/src/components/ReaderNavBar.tsx @@ -14,10 +14,11 @@ import { useHistory } from 'react-router-dom'; import Slide from '@material-ui/core/Slide'; import Fade from '@material-ui/core/Fade'; import Zoom from '@material-ui/core/Zoom'; +import { Switch } from '@material-ui/core'; import NavBarContext from '../context/NavbarContext'; import DarkTheme from '../context/DarkTheme'; -const useStyles = makeStyles((theme: Theme) => ({ +const useStyles = (settings: IReaderSettings) => makeStyles((theme: Theme) => ({ // main container and root div need to change classes... AppMainContainer: { display: 'none', @@ -27,7 +28,7 @@ const useStyles = makeStyles((theme: Theme) => ({ }, root: { - position: 'fixed', + position: settings.staticNav ? 'sticky' : 'fixed', top: 0, left: 0, minWidth: '300px', @@ -81,9 +82,15 @@ const useStyles = makeStyles((theme: Theme) => ({ })); export interface IReaderSettings{ - + staticNav: boolean + showPageNumber: boolean } +export const defaultReaderSettings = () => ({ + staticNav: false, + showPageNumber: true, +} as IReaderSettings); + interface IProps { settings: IReaderSettings setSettings: React.Dispatch> @@ -96,14 +103,16 @@ export default function ReaderNavBar(props: IProps) { const history = useHistory(); - const [drawerOpen, setDrawerOpen] = useState(false); + const { settings, setSettings, manga } = props; + + const [drawerOpen, setDrawerOpen] = useState(false || settings.staticNav); const [hideOpenButton, setHideOpenButton] = useState(false); const [prevScrollPos, setPrevScrollPos] = useState(0); const theme = useTheme(); - const classes = useStyles(); + const classes = useStyles(settings)(); - const { settings, setSettings, manga } = props; + const setSettingValue = (key: string, value: any) => setSettings({ ...settings, [key]: value }); const handleScroll = () => { const currentScrollPos = window.pageYOffset; @@ -120,7 +129,7 @@ export default function ReaderNavBar(props: IProps) { return () => { window.removeEventListener('scroll', handleScroll); }; - }, [handleScroll]); + }, [handleScroll]);// handleScroll changes on every render useEffect(() => { window.addEventListener('scroll', handleScroll); @@ -135,7 +144,7 @@ export default function ReaderNavBar(props: IProps) { rootEl.classList.remove(classes.AppRootElment); mainContainer.classList.remove(classes.AppMainContainer); }; - }, []); + }, [handleScroll]);// handleScroll changes on every render return ( <> @@ -154,16 +163,29 @@ export default function ReaderNavBar(props: IProps) { {title} - setDrawerOpen(false)} - > - - + {!settings.staticNav + && ( + setDrawerOpen(false)} + > + + + ) } +

Static Navigation

+ setSettingValue('staticNav', e.target.checked)} + /> +

Show page number

+ setSettingValue('showPageNumber', e.target.checked)} + /> diff --git a/webUI/react/src/screens/Manga.tsx b/webUI/react/src/screens/Manga.tsx index 3196ab2..a5ff2f6 100644 --- a/webUI/react/src/screens/Manga.tsx +++ b/webUI/react/src/screens/Manga.tsx @@ -19,6 +19,15 @@ const useStyles = makeStyles((theme: Theme) => ({ }, }, + chapters: { + listStyle: 'none', + padding: 0, + [theme.breakpoints.up('md')]: { + width: '50%', + marginLeft: '50%', + }, + }, + loading: { margin: '10px 0', display: 'flex', @@ -53,7 +62,7 @@ export default function Manga() { }, []); const chapterCards = ( -
    +
      {chapters.length === 0 && (
      diff --git a/webUI/react/src/screens/Reader.tsx b/webUI/react/src/screens/Reader.tsx index 7e55774..271fbef 100644 --- a/webUI/react/src/screens/Reader.tsx +++ b/webUI/react/src/screens/Reader.tsx @@ -8,12 +8,12 @@ import { makeStyles } from '@material-ui/core/styles'; import React, { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import Page from '../components/Page'; -import ReaderNavBar, { IReaderSettings } from '../components/ReaderNavBar'; +import ReaderNavBar, { defaultReaderSettings, IReaderSettings } from '../components/ReaderNavBar'; import NavbarContext from '../context/NavbarContext'; import client from '../util/client'; import useLocalStorage from '../util/useLocalStorage'; -const useStyles = makeStyles({ +const useStyles = (settings: IReaderSettings) => makeStyles({ reader: { display: 'flex', flexDirection: 'column', @@ -24,20 +24,32 @@ const useStyles = makeStyles({ loading: { margin: '50px auto', }, + + pageNumber: { + display: settings.showPageNumber ? 'block' : 'none', + position: 'fixed', + bottom: '50px', + right: settings.staticNav ? 'calc((100vw - 325px)/2)' : 'calc((100vw - 25px)/2)', + width: '50px', + textAlign: 'center', + backgroundColor: 'rgba(0, 0, 0, 0.3)', + borderRadius: '10px', + }, }); const range = (n:number) => Array.from({ length: n }, (value, key) => key); export default function Reader() { - const classes = useStyles(); + const [settings, setSettings] = useLocalStorage('readerSettings', defaultReaderSettings); - const [settings, setSettings] = useState({}); + const classes = useStyles(settings)(); const [serverAddress] = useLocalStorage('serverBaseURL', ''); const { chapterId, mangaId } = useParams<{chapterId: string, mangaId: string}>(); const [manga, setManga] = useState({ id: +mangaId, title: '', thumbnailUrl: '' }); const [pageCount, setPageCount] = useState(-1); + const [curPage, setCurPage] = useState(0); const { setOverride, setTitle } = useContext(NavbarContext); useEffect(() => { @@ -54,7 +66,7 @@ export default function Reader() { // clean up for when we leave the reader return () => setOverride({ status: false, value:
      }); - }, [manga]); + }, [manga, settings]); useEffect(() => { setTitle('Reader'); @@ -83,8 +95,16 @@ export default function Reader() { } return (
      +
      + {`${curPage + 1} / ${pageCount}`} +
      {range(pageCount).map((index) => ( - + ))}
      ); diff --git a/webUI/react/src/util/useLocalStorage.tsx b/webUI/react/src/util/useLocalStorage.tsx index d3df299..119f73f 100644 --- a/webUI/react/src/util/useLocalStorage.tsx +++ b/webUI/react/src/util/useLocalStorage.tsx @@ -2,19 +2,20 @@ * 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 { useState, Dispatch, SetStateAction } from 'react'; +import React, { useState, Dispatch, SetStateAction } from 'react'; import storage from './localStorage'; // eslint-disable-next-line max-len -export default function useLocalStorage(key: string, defaultValue: T) : [T, Dispatch>] { - const [storedValue, setStoredValue] = useState(storage.getItem(key, defaultValue)); +export default function useLocalStorage(key: string, defaultValue: T | (() => T)) : [T, Dispatch>] { + const initialState = defaultValue instanceof Function ? defaultValue() : defaultValue; + const [storedValue, setStoredValue] = useState(storage.getItem(key, initialState)); - const setValue = (value: T | ((prevState: T) => T)) => { + 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>; return [storedValue, setValue]; }