Simplify ReaderControls#useOpenPage

This commit is contained in:
schroda
2025-09-20 19:56:31 +02:00
parent f5c7379fc4
commit ca5920919e
5 changed files with 94 additions and 127 deletions

View File

@@ -20,11 +20,9 @@ import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { getReaderStore, useReaderStoreShallow } from '@/features/reader/stores/ReaderStore.ts'; import { getReaderStore, useReaderStoreShallow } from '@/features/reader/stores/ReaderStore.ts';
const BaseReaderAutoScroll = ({ const BaseReaderAutoScroll = ({
openPage,
themeDirection, themeDirection,
combinedDirection, combinedDirection,
}: { }: {
openPage: ReturnType<typeof ReaderControls.useOpenPage>;
themeDirection: Direction; themeDirection: Direction;
combinedDirection: Direction; combinedDirection: Direction;
}) => { }) => {
@@ -49,8 +47,8 @@ const BaseReaderAutoScroll = ({
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode); const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
const changePage = useCallback(() => { const changePage = useCallback(() => {
openPage('next', 'ltr'); ReaderControls.openPage('next', 'ltr');
}, [openPage]); }, []);
const automaticScrolling = useAutomaticScrolling( const automaticScrolling = useAutomaticScrolling(
isContinuousReadingModeActive ? scrollRef : changePage, isContinuousReadingModeActive ? scrollRef : changePage,
@@ -79,9 +77,8 @@ const BaseReaderAutoScroll = ({
export const ReaderAutoScroll = withPropsFrom( export const ReaderAutoScroll = withPropsFrom(
memo(BaseReaderAutoScroll), memo(BaseReaderAutoScroll),
[ [
() => ({ openPage: ReaderControls.useOpenPage() }),
() => ({ themeDirection: useTheme().direction }), () => ({ themeDirection: useTheme().direction }),
() => ({ combinedDirection: ReaderService.useGetThemeDirection() }), () => ({ combinedDirection: ReaderService.useGetThemeDirection() }),
], ],
['openPage', 'themeDirection', 'combinedDirection'], ['themeDirection', 'combinedDirection'],
); );

View File

@@ -67,10 +67,8 @@ export const ReaderHotkeys = ({
})); }));
const exitReader = ReaderService.useExit(); const exitReader = ReaderService.useExit();
const openPage = ReaderControls.useOpenPage(); useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous'));
useHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => ReaderControls.openPage('next'));
useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => openPage('previous'), [openPage]);
useHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => openPage('next'), [openPage]);
useHotkeys( useHotkeys(
hotkeys[ReaderHotkey.SCROLL_BACKWARD], hotkeys[ReaderHotkey.SCROLL_BACKWARD],
() => { () => {

View File

@@ -14,18 +14,13 @@ import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@mui/material/InputLabel';
import { Select } from '@/base/components/inputs/Select.tsx'; import { Select } from '@/base/components/inputs/Select.tsx';
import { getNextIndexFromPage, getPage } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; import { getNextIndexFromPage, getPage } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
import { ReaderControls } from '@/features/reader/services/ReaderControls.ts';
import { useGetOptionForDirection } from '@/features/theme/services/ThemeCreator.ts'; import { useGetOptionForDirection } from '@/features/theme/services/ThemeCreator.ts';
import { ReaderNavBarDesktopNextPreviousButton } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopNextPreviousButton.tsx'; import { ReaderNavBarDesktopNextPreviousButton } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopNextPreviousButton.tsx';
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/features/reader/settings/ReaderSettings.constants.tsx'; import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { useReaderStore, useReaderStoreShallow } from '@/features/reader/stores/ReaderStore.ts'; import { useReaderStore, useReaderStoreShallow } from '@/features/reader/stores/ReaderStore.ts';
import { ReaderControls } from '@/features/reader/services/ReaderControls.ts';
const BaseReaderNavBarDesktopPageNavigation = ({ const BaseReaderNavBarDesktopPageNavigation = () => {
openPage,
}: {
openPage: ReturnType<typeof ReaderControls.useOpenPage>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const getOptionForDirection = useGetOptionForDirection(); const getOptionForDirection = useGetOptionForDirection();
const { currentPageIndex, pages } = useReaderStoreShallow((state) => ({ const { currentPageIndex, pages } = useReaderStoreShallow((state) => ({
@@ -47,7 +42,7 @@ const BaseReaderNavBarDesktopPageNavigation = ({
getNextIndexFromPage(currentPage) === getNextIndexFromPage(pages.slice(-1)[0]), getNextIndexFromPage(currentPage) === getNextIndexFromPage(pages.slice(-1)[0]),
direction, direction,
)} )}
onClick={() => openPage('previous', undefined, false)} onClick={() => ReaderControls.openPage('previous', undefined, false)}
/> />
<FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}> <FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}>
<InputLabel id="reader-nav-bar-desktop-page-select">{t('reader.page_info.label.page')}</InputLabel> <InputLabel id="reader-nav-bar-desktop-page-select">{t('reader.page_info.label.page')}</InputLabel>
@@ -55,7 +50,7 @@ const BaseReaderNavBarDesktopPageNavigation = ({
labelId="reader-nav-bar-desktop-page-select" labelId="reader-nav-bar-desktop-page-select"
label={t('reader.page_info.label.page')} label={t('reader.page_info.label.page')}
value={getNextIndexFromPage(currentPage)} value={getNextIndexFromPage(currentPage)}
onChange={(e) => openPage(e.target.value as number, undefined, false)} onChange={(e) => ReaderControls.openPage(e.target.value as number, undefined, false)}
> >
{pages.map((page) => ( {pages.map((page) => (
<MenuItem key={getNextIndexFromPage(page)} value={getNextIndexFromPage(page)}> <MenuItem key={getNextIndexFromPage(page)} value={getNextIndexFromPage(page)}>
@@ -72,14 +67,10 @@ const BaseReaderNavBarDesktopPageNavigation = ({
!currentPage.primary.index, !currentPage.primary.index,
direction, direction,
)} )}
onClick={() => openPage('next', undefined, false)} onClick={() => ReaderControls.openPage('next', undefined, false)}
/> />
</Stack> </Stack>
); );
}; };
export const ReaderNavBarDesktopPageNavigation = withPropsFrom( export const ReaderNavBarDesktopPageNavigation = memo(BaseReaderNavBarDesktopPageNavigation);
memo(BaseReaderNavBarDesktopPageNavigation),
[() => ({ openPage: ReaderControls.useOpenPage() })],
['openPage'],
);

View File

@@ -28,7 +28,6 @@ import { getOptionForDirection as getOptionForDirectionImpl } from '@/features/t
import { ReaderProgressBarSlotsActionArea } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarSlotsActionArea.tsx'; import { ReaderProgressBarSlotsActionArea } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarSlotsActionArea.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { ReaderControls } from '@/features/reader/services/ReaderControls.ts'; import { ReaderControls } from '@/features/reader/services/ReaderControls.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { ReaderProgressBarSlotWrapper } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarSlotWrapper.tsx'; import { ReaderProgressBarSlotWrapper } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarSlotWrapper.tsx';
import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx'; import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx';
import { ReadingMode } from '@/features/reader/Reader.types.ts'; import { ReadingMode } from '@/features/reader/Reader.types.ts';
@@ -39,7 +38,6 @@ const BaseReaderProgressBar = ({
slots, slots,
createProgressBarSlot, createProgressBarSlot,
progressBarPosition, progressBarPosition,
openPage,
direction, direction,
fullSegmentClicks, fullSegmentClicks,
}: ReaderProgressBarProps & }: ReaderProgressBarProps &
@@ -61,7 +59,6 @@ const BaseReaderProgressBar = ({
slots?: { slots?: {
progressBarCurrentPage?: ReactNode; progressBarCurrentPage?: ReactNode;
}; };
openPage: ReturnType<typeof ReaderControls.useOpenPage>;
direction: ReturnType<typeof ReaderService.useGetThemeDirection>; direction: ReturnType<typeof ReaderService.useGetThemeDirection>;
fullSegmentClicks: boolean; fullSegmentClicks: boolean;
}) => { }) => {
@@ -114,7 +111,6 @@ const BaseReaderProgressBar = ({
); );
ReaderControls.useHandleProgressDragging( ReaderControls.useHandleProgressDragging(
openPage,
progressBarRef, progressBarRef,
isDragging, isDragging,
currentPage, currentPage,
@@ -131,7 +127,7 @@ const BaseReaderProgressBar = ({
const isTouchEvent = 'touches' in e; const isTouchEvent = 'touches' in e;
openPage( ReaderControls.openPage(
getNextIndexFromPage( getNextIndexFromPage(
getPageForMousePos( getPageForMousePos(
isTouchEvent ? e.touches[0] : e, isTouchEvent ? e.touches[0] : e,
@@ -170,7 +166,7 @@ const BaseReaderProgressBar = ({
? (slotProps?.progressBarPageTexts?.current?.sx ?? []) ? (slotProps?.progressBarPageTexts?.current?.sx ?? [])
: [slotProps?.progressBarPageTexts?.current?.sx]), : [slotProps?.progressBarPageTexts?.current?.sx]),
]} ]}
onClick={() => openPage('previous', 'ltr', false)} onClick={() => ReaderControls.openPage('previous', 'ltr', false)}
> >
{currentPage.name} {currentPage.name}
</ReaderProgressBarPageNumber> </ReaderProgressBarPageNumber>
@@ -235,7 +231,7 @@ const BaseReaderProgressBar = ({
? (slotProps?.progressBarPageTexts?.total?.sx ?? []) ? (slotProps?.progressBarPageTexts?.total?.sx ?? [])
: [slotProps?.progressBarPageTexts?.total?.sx]), : [slotProps?.progressBarPageTexts?.total?.sx]),
]} ]}
onClick={() => openPage('next', 'ltr', false)} onClick={() => ReaderControls.openPage('next', 'ltr', false)}
> >
{totalPages} {totalPages}
</ReaderProgressBarPageNumber> </ReaderProgressBarPageNumber>
@@ -244,8 +240,4 @@ const BaseReaderProgressBar = ({
); );
}; };
export const ReaderProgressBar = withPropsFrom( export const ReaderProgressBar = memo(BaseReaderProgressBar);
memo(BaseReaderProgressBar),
[() => ({ openPage: ReaderControls.useOpenPage() })],
['openPage'],
);

View File

@@ -311,106 +311,97 @@ export class ReaderControls {
}); });
} }
static useOpenPage(): ( static openPage(page: number | 'previous' | 'next', forceDirection?: Direction, hideOverlay: boolean = true): void {
page: number | 'previous' | 'next', const {
forceDirection?: Direction, pages: { currentPageIndex, setPageToScrollToIndex, pages, transitionPageMode, setTransitionPageMode },
hideOverlay?: boolean, settings: { readingDirection, readingMode, shouldShowTransitionPage },
) => void { } = getReaderStore();
return useCallback((page, forceDirection, hideOverlay: boolean = true) => {
const {
pages: { currentPageIndex, setPageToScrollToIndex, pages, transitionPageMode, setTransitionPageMode },
settings: { readingDirection, readingMode, shouldShowTransitionPage },
} = getReaderStore();
const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection.value]; const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection.value];
const convertedPage = getOptionForDirection( const convertedPage = getOptionForDirection(
page, page,
page === 'previous' ? 'next' : 'previous', page === 'previous' ? 'next' : 'previous',
forceDirection ?? direction, forceDirection ?? direction,
); );
const currentPage = getPage(currentPageIndex, pages); const currentPage = getPage(currentPageIndex, pages);
const previousPageIndex = getNextPageIndex('previous', currentPage.pagesIndex, pages); const previousPageIndex = getNextPageIndex('previous', currentPage.pagesIndex, pages);
const nextPageIndex = getNextPageIndex('next', currentPage.pagesIndex, pages); const nextPageIndex = getNextPageIndex('next', currentPage.pagesIndex, pages);
const indexOfFirstPage = getNextIndexFromPage(pages[0]); const indexOfFirstPage = getNextIndexFromPage(pages[0]);
const indexOfLastPage = getNextIndexFromPage(pages[pages.length - 1]); const indexOfLastPage = getNextIndexFromPage(pages[pages.length - 1]);
const isFirstPage = currentPage.primary.index === 0; const isFirstPage = currentPage.primary.index === 0;
const isLastPage = currentPageIndex === indexOfLastPage; const isLastPage = currentPageIndex === indexOfLastPage;
const isATransitionPageVisibleFlag = isATransitionPageVisible(transitionPageMode, readingMode.value); const isATransitionPageVisibleFlag = isATransitionPageVisible(transitionPageMode, readingMode.value);
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value); const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value);
if (hideOverlay) { if (hideOverlay) {
getReaderStore().overlay.setIsVisible(false); getReaderStore().overlay.setIsVisible(false);
getReaderStore().tapZone.setShowPreview(false); getReaderStore().tapZone.setShowPreview(false);
} }
const hideTransitionPage = () => setTransitionPageMode(ReaderTransitionPageMode.NONE); const hideTransitionPage = () => setTransitionPageMode(ReaderTransitionPageMode.NONE);
if (typeof page === 'number') { if (typeof page === 'number') {
setPageToScrollToIndex(page); setPageToScrollToIndex(page);
hideTransitionPage(); hideTransitionPage();
return; return;
} }
const areContinuousPagerTransitionPagesVisible = const areContinuousPagerTransitionPagesVisible = isContinuousReadingModeActive && isATransitionPageVisibleFlag;
isContinuousReadingModeActive && isATransitionPageVisibleFlag; const isPreviousTransitionPageVisible =
const isPreviousTransitionPageVisible = (!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.PREVIOUS) ||
(!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.PREVIOUS) || areContinuousPagerTransitionPagesVisible;
areContinuousPagerTransitionPagesVisible; const isNextTransitionPageVisible =
const isNextTransitionPageVisible = (!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.NEXT) ||
(!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.NEXT) || areContinuousPagerTransitionPagesVisible;
areContinuousPagerTransitionPagesVisible;
const shouldOpenPreviousChapter = const shouldOpenPreviousChapter =
isFirstPage && isFirstPage &&
(!shouldShowTransitionPage || isPreviousTransitionPageVisible) && (!shouldShowTransitionPage || isPreviousTransitionPageVisible) &&
convertedPage === 'previous' && convertedPage === 'previous' &&
!!getReaderStore().chapters.previousChapter; !!getReaderStore().chapters.previousChapter;
if (shouldOpenPreviousChapter) { if (shouldOpenPreviousChapter) {
ReaderControls.openChapter('previous'); ReaderControls.openChapter('previous');
return; return;
} }
const shouldOpenNextChapter = const shouldOpenNextChapter =
isLastPage && isLastPage &&
(!shouldShowTransitionPage || isNextTransitionPageVisible) && (!shouldShowTransitionPage || isNextTransitionPageVisible) &&
convertedPage === 'next' && convertedPage === 'next' &&
!!getReaderStore().chapters.nextChapter; !!getReaderStore().chapters.nextChapter;
if (shouldOpenNextChapter) { if (shouldOpenNextChapter) {
ReaderControls.openChapter('next'); ReaderControls.openChapter('next');
return; return;
} }
const isPreviousMode = convertedPage === 'previous'; const isPreviousMode = convertedPage === 'previous';
const isNextMode = convertedPage === 'next'; const isNextMode = convertedPage === 'next';
const closePreviousTransitionPage = isPreviousTransitionPageVisible && isNextMode; const closePreviousTransitionPage = isPreviousTransitionPageVisible && isNextMode;
const closeNextTransitionPage = isNextTransitionPageVisible && isPreviousMode; const closeNextTransitionPage = isNextTransitionPageVisible && isPreviousMode;
const needToHideTransitionPage = const needToHideTransitionPage =
isATransitionPageVisibleFlag && isATransitionPageVisibleFlag &&
!isContinuousReadingModeActive && !isContinuousReadingModeActive &&
(closePreviousTransitionPage || closeNextTransitionPage); (closePreviousTransitionPage || closeNextTransitionPage);
if (needToHideTransitionPage) { if (needToHideTransitionPage) {
hideTransitionPage(); hideTransitionPage();
setPageToScrollToIndex(isPreviousTransitionPageVisible ? indexOfFirstPage : indexOfLastPage); setPageToScrollToIndex(isPreviousTransitionPageVisible ? indexOfFirstPage : indexOfLastPage);
return; return;
} }
const needToOpenTransitionPage = const needToOpenTransitionPage =
((isFirstPage && isPreviousMode) || (isLastPage && isNextMode)) && !isContinuousReadingModeActive; ((isFirstPage && isPreviousMode) || (isLastPage && isNextMode)) && !isContinuousReadingModeActive;
if (needToOpenTransitionPage) { if (needToOpenTransitionPage) {
setTransitionPageMode( setTransitionPageMode(isPreviousMode ? ReaderTransitionPageMode.PREVIOUS : ReaderTransitionPageMode.NEXT);
isPreviousMode ? ReaderTransitionPageMode.PREVIOUS : ReaderTransitionPageMode.NEXT, return;
); }
return;
}
setPageToScrollToIndex(isPreviousMode ? previousPageIndex : nextPageIndex); setPageToScrollToIndex(isPreviousMode ? previousPageIndex : nextPageIndex);
}, []);
} }
static useUpdateCurrentPageIndex(): ( static useUpdateCurrentPageIndex(): (
@@ -535,7 +526,6 @@ export class ReaderControls {
scrollElement: HTMLElement | null, scrollElement: HTMLElement | null,
): (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void { ): (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void {
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
const openPage = ReaderControls.useOpenPage();
return useCallback( return useCallback(
(e) => { (e) => {
@@ -574,19 +564,18 @@ export class ReaderControls {
scrollAmount, scrollAmount,
); );
} else { } else {
openPage(action === TapZoneRegionType.PREVIOUS ? 'previous' : 'next', 'ltr'); ReaderControls.openPage(action === TapZoneRegionType.PREVIOUS ? 'previous' : 'next', 'ltr');
} }
break; break;
default: default:
throw new Error(`Unexpected "TapZoneRegionType" (${action})`); throw new Error(`Unexpected "TapZoneRegionType" (${action})`);
} }
}, },
[scrollElement, openPage, themeDirection], [scrollElement, themeDirection],
); );
} }
static useHandleProgressDragging( static useHandleProgressDragging(
openPage: ReturnType<(typeof ReaderControls)['useOpenPage']>,
progressBarRef: RefObject<HTMLDivElement | null>, progressBarRef: RefObject<HTMLDivElement | null>,
isDragging: boolean, isDragging: boolean,
currentPage: TReaderProgressCurrentPage, currentPage: TReaderProgressCurrentPage,
@@ -623,7 +612,7 @@ export class ReaderControls {
return; return;
} }
openPage(newPageIndex, undefined, false); ReaderControls.openPage(newPageIndex, undefined, false);
}; };
const handleMouseMove = (e: MouseEvent) => { const handleMouseMove = (e: MouseEvent) => {
@@ -643,6 +632,6 @@ export class ReaderControls {
document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('touchmove', handleTouchMove); document.removeEventListener('touchmove', handleTouchMove);
}; };
}, [openPage, isDragging, currentPage, pages, progressBarPosition]); }, [isDragging, currentPage, pages, progressBarPosition]);
} }
} }