Fix click/scroll/hotkey open previous/next chapter
Properly handle all combinations of theme and reader reading directions
This commit is contained in:
@@ -23,6 +23,7 @@ import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
|
|||||||
import { HotkeyScope } from '@/modules/hotkeys/Hotkeys.types.ts';
|
import { HotkeyScope } from '@/modules/hotkeys/Hotkeys.types.ts';
|
||||||
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
|
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
|
||||||
import { ScrollDirection, ScrollOffset } from '@/modules/core/Core.types.ts';
|
import { ScrollDirection, ScrollOffset } from '@/modules/core/Core.types.ts';
|
||||||
|
import { getOptionForDirection } from '@/theme.tsx';
|
||||||
|
|
||||||
const useHotkeys = (...args: Parameters<typeof useHotKeysHook>): ReturnType<typeof useHotKeysHook> => {
|
const useHotkeys = (...args: Parameters<typeof useHotKeysHook>): ReturnType<typeof useHotKeysHook> => {
|
||||||
const [keys, callback, options, dependencies] = args;
|
const [keys, callback, options, dependencies] = args;
|
||||||
@@ -44,6 +45,7 @@ export const ReaderHotkeys = ({
|
|||||||
scrollElementRef: React.MutableRefObject<HTMLElement | null>;
|
scrollElementRef: React.MutableRefObject<HTMLElement | null>;
|
||||||
}) => {
|
}) => {
|
||||||
const { direction: themeDirection } = useTheme();
|
const { direction: themeDirection } = useTheme();
|
||||||
|
const readerThemeDirection = ReaderService.useGetThemeDirection();
|
||||||
const { enableScope, disableScope } = useHotkeysContext();
|
const { enableScope, disableScope } = useHotkeysContext();
|
||||||
const { manga } = useReaderStateMangaContext();
|
const { manga } = useReaderStateMangaContext();
|
||||||
const { isVisible, setIsVisible } = useReaderOverlayContext();
|
const { isVisible, setIsVisible } = useReaderOverlayContext();
|
||||||
@@ -92,8 +94,16 @@ export const ReaderHotkeys = ({
|
|||||||
{ preventDefault: true },
|
{ preventDefault: true },
|
||||||
[readingMode.value, readingDirection.value, themeDirection, openChapter],
|
[readingMode.value, readingDirection.value, themeDirection, openChapter],
|
||||||
);
|
);
|
||||||
useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_CHAPTER], () => openChapter('previous'), [openChapter]);
|
useHotkeys(
|
||||||
useHotkeys(hotkeys[ReaderHotkey.NEXT_CHAPTER], () => openChapter('next'), [openChapter]);
|
hotkeys[ReaderHotkey.PREVIOUS_CHAPTER],
|
||||||
|
() => openChapter(getOptionForDirection('previous', 'next', readerThemeDirection)),
|
||||||
|
[openChapter, readerThemeDirection],
|
||||||
|
);
|
||||||
|
useHotkeys(
|
||||||
|
hotkeys[ReaderHotkey.NEXT_CHAPTER],
|
||||||
|
() => openChapter(getOptionForDirection('next', 'previous', readerThemeDirection)),
|
||||||
|
[openChapter, readerThemeDirection],
|
||||||
|
);
|
||||||
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () => setIsVisible(!isVisible), [isVisible]);
|
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () => setIsVisible(!isVisible), [isVisible]);
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
|
hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
|||||||
import { ReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
import { ReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
||||||
import { ReaderChapterList } from '@/modules/reader/components/overlay/navigation/ReaderChapterList.tsx';
|
import { ReaderChapterList } from '@/modules/reader/components/overlay/navigation/ReaderChapterList.tsx';
|
||||||
import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx';
|
import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx';
|
||||||
import { useGetOptionForDirection } from '@/theme.tsx';
|
import { getOptionForDirection } from '@/theme.tsx';
|
||||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||||
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
|
||||||
import { ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts';
|
import { ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
|
||||||
export const ReaderNavBarDesktopChapterNavigation = ({
|
export const ReaderNavBarDesktopChapterNavigation = ({
|
||||||
@@ -36,13 +35,10 @@ export const ReaderNavBarDesktopChapterNavigation = ({
|
|||||||
'chapters' | 'currentChapter' | 'previousChapter' | 'nextChapter'
|
'chapters' | 'currentChapter' | 'previousChapter' | 'nextChapter'
|
||||||
>) => {
|
>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { readingDirection } = ReaderService.useSettings();
|
const readerThemeDirection = ReaderService.useGetThemeDirection();
|
||||||
const getOptionForDirection = useGetOptionForDirection();
|
|
||||||
|
|
||||||
const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' });
|
const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' });
|
||||||
|
|
||||||
const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection.value];
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
popupState.close();
|
popupState.close();
|
||||||
}, [currentChapter?.id]);
|
}, [currentChapter?.id]);
|
||||||
@@ -53,16 +49,26 @@ export const ReaderNavBarDesktopChapterNavigation = ({
|
|||||||
component={Link}
|
component={Link}
|
||||||
type="previous"
|
type="previous"
|
||||||
title={t(
|
title={t(
|
||||||
getOptionForDirection('reader.button.previous_chapter', 'reader.button.next_chapter', direction),
|
getOptionForDirection(
|
||||||
|
'reader.button.previous_chapter',
|
||||||
|
'reader.button.next_chapter',
|
||||||
|
readerThemeDirection,
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
disabled={getOptionForDirection(!previousChapter, !nextChapter, direction)}
|
disabled={getOptionForDirection(!previousChapter, !nextChapter, readerThemeDirection)}
|
||||||
to={getOptionForDirection(
|
to={getOptionForDirection(
|
||||||
previousChapter && Chapters.getReaderUrl(previousChapter),
|
previousChapter && Chapters.getReaderUrl(previousChapter),
|
||||||
nextChapter && Chapters.getReaderUrl(nextChapter),
|
nextChapter && Chapters.getReaderUrl(nextChapter),
|
||||||
direction,
|
readerThemeDirection,
|
||||||
)}
|
)}
|
||||||
replace
|
replace
|
||||||
state={{ resumeMode: getOptionForDirection(ReaderResumeMode.END, ReaderResumeMode.START) }}
|
state={{
|
||||||
|
resumeMode: getOptionForDirection(
|
||||||
|
ReaderResumeMode.END,
|
||||||
|
ReaderResumeMode.START,
|
||||||
|
readerThemeDirection,
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}>
|
<FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}>
|
||||||
<InputLabel id="reader-nav-bar-desktop-chapter-select">{t('chapter.title_one')}</InputLabel>
|
<InputLabel id="reader-nav-bar-desktop-chapter-select">{t('chapter.title_one')}</InputLabel>
|
||||||
@@ -85,16 +91,26 @@ export const ReaderNavBarDesktopChapterNavigation = ({
|
|||||||
component={Link}
|
component={Link}
|
||||||
type="next"
|
type="next"
|
||||||
title={t(
|
title={t(
|
||||||
getOptionForDirection('reader.button.next_chapter', 'reader.button.previous_chapter', direction),
|
getOptionForDirection(
|
||||||
|
'reader.button.next_chapter',
|
||||||
|
'reader.button.previous_chapter',
|
||||||
|
readerThemeDirection,
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
disabled={getOptionForDirection(!nextChapter, !previousChapter, direction)}
|
disabled={getOptionForDirection(!nextChapter, !previousChapter, readerThemeDirection)}
|
||||||
to={getOptionForDirection(
|
to={getOptionForDirection(
|
||||||
nextChapter && Chapters.getReaderUrl(nextChapter),
|
nextChapter && Chapters.getReaderUrl(nextChapter),
|
||||||
previousChapter && Chapters.getReaderUrl(previousChapter),
|
previousChapter && Chapters.getReaderUrl(previousChapter),
|
||||||
direction,
|
readerThemeDirection,
|
||||||
)}
|
)}
|
||||||
replace
|
replace
|
||||||
state={{ resumeMode: getOptionForDirection(ReaderResumeMode.START, ReaderResumeMode.END) }}
|
state={{
|
||||||
|
resumeMode: getOptionForDirection(
|
||||||
|
ReaderResumeMode.START,
|
||||||
|
ReaderResumeMode.END,
|
||||||
|
readerThemeDirection,
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
{...bindPopover(popupState)}
|
{...bindPopover(popupState)}
|
||||||
|
|||||||
@@ -88,14 +88,14 @@ export class ReaderControls {
|
|||||||
const areReadingDirectionsEqual = themeDirection === themeDirectionOfReadingDirection;
|
const areReadingDirectionsEqual = themeDirection === themeDirectionOfReadingDirection;
|
||||||
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
|
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
|
||||||
|
|
||||||
const isAtStartY = element.scrollTop === 0;
|
const isAtStartY = Math.abs(element.scrollTop) <= 1;
|
||||||
const isAtEndY =
|
const isAtEndY =
|
||||||
Math.floor(element.scrollTop) === element.scrollHeight - element.clientHeight ||
|
Math.floor(element.scrollTop) === element.scrollHeight - element.clientHeight ||
|
||||||
Math.ceil(element.scrollTop) === element.scrollHeight - element.clientHeight;
|
Math.ceil(element.scrollTop) === element.scrollHeight - element.clientHeight;
|
||||||
const isAtStartX = element.scrollLeft === 0;
|
const isAtStartX = Math.abs(element.scrollLeft) <= 1;
|
||||||
const isAtEndX =
|
const isAtEndX =
|
||||||
Math.floor(Math.abs(element.scrollLeft)) === element.scrollWidth - element.clientWidth ||
|
element.scrollWidth - element.clientWidth - Math.floor(Math.abs(element.scrollLeft)) <= 1 ||
|
||||||
Math.ceil(Math.abs(element.scrollLeft)) === element.scrollWidth - element.clientWidth;
|
element.scrollWidth - element.clientWidth - Math.ceil(Math.abs(element.scrollLeft)) <= 1;
|
||||||
const isAtStartXForDirection = areReadingDirectionsEqual ? isAtStartX : isAtEndX;
|
const isAtStartXForDirection = areReadingDirectionsEqual ? isAtStartX : isAtEndX;
|
||||||
const isAtEndXForDirection = areReadingDirectionsEqual ? isAtEndX : isAtStartX;
|
const isAtEndXForDirection = areReadingDirectionsEqual ? isAtEndX : isAtStartX;
|
||||||
|
|
||||||
@@ -108,12 +108,12 @@ export class ReaderControls {
|
|||||||
switch (direction) {
|
switch (direction) {
|
||||||
case ScrollDirection.X:
|
case ScrollDirection.X:
|
||||||
if (isAtStartXForDirection && offset === ScrollOffset.BACKWARD && isContinuousReadingModeActive) {
|
if (isAtStartXForDirection && offset === ScrollOffset.BACKWARD && isContinuousReadingModeActive) {
|
||||||
openChapter('previous', 'ltr');
|
openChapter('previous');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAtEndXForDirection && offset === ScrollOffset.FORWARD && isContinuousReadingModeActive) {
|
if (isAtEndXForDirection && offset === ScrollOffset.FORWARD && isContinuousReadingModeActive) {
|
||||||
openChapter('next', 'ltr');
|
openChapter('next');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,12 +124,12 @@ export class ReaderControls {
|
|||||||
break;
|
break;
|
||||||
case ScrollDirection.Y:
|
case ScrollDirection.Y:
|
||||||
if (isAtStartY && offset === ScrollOffset.BACKWARD && isContinuousReadingModeActive) {
|
if (isAtStartY && offset === ScrollOffset.BACKWARD && isContinuousReadingModeActive) {
|
||||||
openChapter('previous', 'ltr');
|
openChapter('previous');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAtEndY && offset === ScrollOffset.FORWARD && isContinuousReadingModeActive) {
|
if (isAtEndY && offset === ScrollOffset.FORWARD && isContinuousReadingModeActive) {
|
||||||
openChapter('next', 'ltr');
|
openChapter('next');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,28 +143,27 @@ export class ReaderControls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static useOpenChapter(): (offset: 'previous' | 'next', forceDirection?: Direction) => void {
|
static useOpenChapter(): (offset: 'previous' | 'next') => void {
|
||||||
const { readingDirection, readingMode } = ReaderService.useSettings();
|
const { readingMode } = ReaderService.useSettings();
|
||||||
const { previousChapter, nextChapter } = useReaderStateChaptersContext();
|
const { previousChapter, nextChapter } = useReaderStateChaptersContext();
|
||||||
const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection.value];
|
|
||||||
|
|
||||||
const openPreviousChapter = ReaderService.useNavigateToChapter(previousChapter, ReaderResumeMode.END);
|
const openPreviousChapter = ReaderService.useNavigateToChapter(previousChapter, ReaderResumeMode.END);
|
||||||
const openNextChapter = ReaderService.useNavigateToChapter(nextChapter, ReaderResumeMode.START);
|
const openNextChapter = ReaderService.useNavigateToChapter(nextChapter, ReaderResumeMode.START);
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(offset, forceDirection = direction) => {
|
(offset) => {
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
case 'previous':
|
case 'previous':
|
||||||
getOptionForDirection(openPreviousChapter, openNextChapter, forceDirection)();
|
openPreviousChapter();
|
||||||
break;
|
break;
|
||||||
case 'next':
|
case 'next':
|
||||||
getOptionForDirection(openNextChapter, openPreviousChapter, forceDirection)();
|
openNextChapter();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unexpected "offset" (${offset})`);
|
throw new Error(`Unexpected "offset" (${offset})`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[direction, openPreviousChapter, openNextChapter, readingMode.value],
|
[openPreviousChapter, openNextChapter, readingMode.value],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,7 +389,7 @@ export class ReaderControls {
|
|||||||
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value);
|
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value);
|
||||||
const scrollDirection =
|
const scrollDirection =
|
||||||
readingMode.value === ReadingMode.CONTINUOUS_HORIZONTAL ? ScrollDirection.X : ScrollDirection.Y;
|
readingMode.value === ReadingMode.CONTINUOUS_HORIZONTAL ? ScrollDirection.X : ScrollDirection.Y;
|
||||||
|
console.log('click', action);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case TapZoneRegionType.MENU:
|
case TapZoneRegionType.MENU:
|
||||||
setIsOverlayVisible((isVisible) => isStaticNav || !isVisible);
|
setIsOverlayVisible((isVisible) => isStaticNav || !isVisible);
|
||||||
|
|||||||
Reference in New Issue
Block a user