From 23466cf85322da3eb1ea58000f4c4c4bc14d642d Mon Sep 17 00:00:00 2001 From: Manchewable <35658388+Manchewable@users.noreply.github.com> Date: Mon, 24 May 2021 12:16:05 -0700 Subject: [PATCH] Added some key mappings to navigate pages (#95) * Added some key mappings to navigate pages * use keyboard event codes * unused files removed * use a reference to current page * fix some bugs with Virtuoso * add keymapping for space to navigate to next page * commit my changes * fix functions not regenerating * fix partial scroll back to start of page issue Co-authored-by: Aria Moradi --- .../components/reader/pager/PagedPager.tsx | 25 +++--- .../components/reader/pager/VerticalPager.tsx | 85 ++++++++++++++----- webUI/react/src/screens/Reader.tsx | 3 +- 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/webUI/react/src/components/reader/pager/PagedPager.tsx b/webUI/react/src/components/reader/pager/PagedPager.tsx index 35fd365..b489eb2 100644 --- a/webUI/react/src/components/reader/pager/PagedPager.tsx +++ b/webUI/react/src/components/reader/pager/PagedPager.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ /* * Copyright (C) Contributors to the Suwayomi project * @@ -8,7 +7,6 @@ import { makeStyles } from '@material-ui/core/styles'; import React, { useEffect, useRef } from 'react'; -import { useHistory } from 'react-router-dom'; import Page from '../Page'; const useStyles = makeStyles({ @@ -24,13 +22,12 @@ const useStyles = makeStyles({ export default function PagedReader(props: IReaderProps) { const { - pages, settings, setCurPage, curPage, manga, chapter, nextChapter, + pages, settings, setCurPage, curPage, nextChapter, } = props; const classes = useStyles(); - const history = useHistory(); - const pageRef = useRef(null); + const selfRef = useRef(null); function nextPage() { if (curPage < pages.length - 1) { @@ -45,7 +42,11 @@ export default function PagedReader(props: IReaderProps) { } function keyboardControl(e:KeyboardEvent) { - switch (e.key) { + switch (e.code) { + case 'Space': + e.preventDefault(); + nextPage(); + break; case 'ArrowRight': nextPage(); break; @@ -66,17 +67,17 @@ export default function PagedReader(props: IReaderProps) { } useEffect(() => { - document.addEventListener('keyup', keyboardControl, false); - pageRef.current?.addEventListener('click', clickControl); + document.addEventListener('keydown', keyboardControl); + selfRef.current?.addEventListener('click', clickControl); return () => { - document.removeEventListener('keyup', keyboardControl); - pageRef.current?.removeEventListener('click', clickControl); + document.removeEventListener('keydown', keyboardControl); + selfRef.current?.removeEventListener('click', clickControl); }; - }, [curPage, pageRef]); + }, [selfRef, curPage]); return ( -
+
(null); + const selfRef = useRef(null); + const pagesRef = useRef([]); + + function nextPage() { + if (curPage < pages.length - 1) { + pagesRef.current[curPage + 1]?.scrollIntoView(); + setCurPage((page) => page + 1); + } else if (settings.loadNextonEnding) { + nextChapter(); + } + } + + function prevPage() { + if (curPage > 0) { + const rect = pagesRef.current[curPage].getBoundingClientRect(); + if (rect.y < 0 && rect.y + rect.height > 0) { + pagesRef.current[curPage]?.scrollIntoView(); + } else { + pagesRef.current[curPage - 1]?.scrollIntoView(); + setCurPage(curPage - 1); + } + } + } + + function keyboardControl(e:KeyboardEvent) { + switch (e.code) { + case 'Space': + e.preventDefault(); + nextPage(); + break; + case 'ArrowRight': + nextPage(); + break; + case 'ArrowLeft': + prevPage(); + break; + default: + break; + } + } + + function clickControl(e:MouseEvent) { + if (e.clientX > window.innerWidth / 2) { + nextPage(); + } else { + prevPage(); + } + } const handleLoadNextonEnding = () => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { nextChapter(); } }; + useEffect(() => { if (settings.loadNextonEnding) { window.addEventListener('scroll', handleLoadNextonEnding); } + document.addEventListener('keydown', keyboardControl, false); + selfRef.current?.addEventListener('click', clickControl); return () => { - window.removeEventListener('scroll', handleLoadNextonEnding); + document.removeEventListener('scroll', handleLoadNextonEnding); + document.removeEventListener('keydown', keyboardControl); + selfRef.current?.removeEventListener('click', clickControl); }; - }, []); + }, [selfRef, curPage]); useEffect(() => { - if ((chapter as IChapter).lastPageRead > -1) { - setInitialScroll((chapter as IChapter).lastPageRead); + // scroll last read page into view + let initialPage = (chapter as IChapter).lastPageRead; + if (initialPage > pages.length - 1) { + initialPage = pages.length - 1; + } + if (initialPage > -1) { + pagesRef.current[initialPage].scrollIntoView(); } }, []); - useEffect(() => { - if (initialScroll > -1) { - initialPageRef.current?.scrollIntoView(); - } - }, [initialScroll, initialPageRef.current]); - return ( -
+
{ pages.map((page) => ( { pagesRef.current[page.index] = e; }} /> )) } diff --git a/webUI/react/src/screens/Reader.tsx b/webUI/react/src/screens/Reader.tsx index 53e007c..b6dd8fb 100644 --- a/webUI/react/src/screens/Reader.tsx +++ b/webUI/react/src/screens/Reader.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ /* * Copyright (C) Contributors to the Suwayomi project * @@ -11,7 +10,6 @@ import { makeStyles } from '@material-ui/core/styles'; import React, { useContext, useEffect, useState } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import HorizontalPager from '../components/reader/pager/HorizontalPager'; -import Page from '../components/reader/Page'; import PageNumber from '../components/reader/PageNumber'; import WebtoonPager from '../components/reader/pager/PagedPager'; import VerticalPager from '../components/reader/pager/VerticalPager'; @@ -72,6 +70,7 @@ export default function Reader() { const [chapter, setChapter] = useState(initialChapter()); const [curPage, setCurPage] = useState(0); const { setOverride, setTitle } = useContext(NavbarContext); + useEffect(() => { // make sure settings has all the keys const settingsClone = cloneObject(settings) as any;