Use HOC for reader context usage

This commit is contained in:
schroda
2024-12-21 03:46:55 +01:00
parent 34f484a946
commit f5642af219
27 changed files with 997 additions and 459 deletions

4
src/UtilTypes.d.ts vendored
View File

@@ -33,3 +33,7 @@ type TupleUnion<U extends string | number | symbol, R extends any[] = []> = {
type TransformRecordToWithDefaultFlag<T extends Record<string, any>> = { type TransformRecordToWithDefaultFlag<T extends Record<string, any>> = {
[K in keyof T]: { value: T[K]; isDefault: boolean }; [K in keyof T]: { value: T[K]; isDefault: boolean };
}; };
type MergeObjectsArray<T extends object[]> = T extends [infer F, ...infer R]
? F & MergeObjects<R extends object[] ? R : []>
: {};

View File

@@ -0,0 +1,32 @@
/*
* 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 { ComponentType, forwardRef } from 'react';
type PropsSourceCreator<T> = () => T;
export const withPropsFrom = <
ComponentProps extends Record<string, any>,
SourceProps extends Record<string, any>[],
SourcePropKeys extends keyof (ComponentProps | MergeObjectsArray<SourceProps>),
>(
Component: ComponentType<ComponentProps>,
propsSources: { [K in keyof SourceProps]: PropsSourceCreator<SourceProps[K]> },
sourcePropKeys: SourcePropKeys[],
) =>
forwardRef<HTMLElement, Omit<ComponentProps, SourcePropKeys>>((props, ref) => {
const sourceProps = propsSources.reduce((acc, propsSource) => ({ ...acc, ...propsSource() }), {});
const selectedProps = Object.fromEntries(
Object.entries(sourceProps).filter(([key]) => sourcePropKeys.includes(key as SourcePropKeys)),
) as Pick<MergeObjectsArray<SourceProps>, SourcePropKeys>;
const combinedProps = { ...props, ...selectedProps } as unknown as ComponentProps;
return <Component {...combinedProps} ref={ref} />;
});

View File

@@ -11,29 +11,46 @@ import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ProgressBarType, ReadingDirection } from '@/modules/reader/types/Reader.types.ts'; import {
IReaderSettings,
ProgressBarType,
ReadingDirection,
TReaderScrollbarContext,
} from '@/modules/reader/types/Reader.types.ts';
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx'; import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
import { getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; import { getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx'; import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx'; import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { reverseString } from '@/util/Strings.ts'; import { reverseString } from '@/util/Strings.ts';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { ReaderStatePages, TReaderProgressBarContext } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderPageNumber = () => { const BaseReaderPageNumber = ({
const { isDesktop } = ReaderService.useOverlayMode(); isDesktop,
const { scrollbarXSize } = useReaderScrollbarContext(); scrollbarXSize,
const { readerNavBarWidth } = useNavBarContext(); readerNavBarWidth,
const { isMaximized } = useReaderProgressBarContext(); isMaximized,
const { currentPageIndex, pages, totalPages } = userReaderStatePagesContext(); currentPageIndex,
const { progressBarType, shouldShowPageNumber, readingDirection } = ReaderService.useSettings(); pages,
totalPages,
progressBarType,
shouldShowPageNumber,
readingDirection,
}: Pick<TReaderScrollbarContext, 'scrollbarXSize'> &
Pick<ReturnType<typeof ReaderService.useOverlayMode>, 'isDesktop'> &
Pick<NavbarContextType, 'readerNavBarWidth'> &
Pick<TReaderProgressBarContext, 'isMaximized'> &
Pick<ReaderStatePages, 'currentPageIndex' | 'pages' | 'totalPages'> &
Pick<IReaderSettings, 'progressBarType' | 'shouldShowPageNumber' | 'readingDirection'>) => {
const pageName = useMemo(() => { const pageName = useMemo(() => {
const currentPageName = getPage(currentPageIndex, pages).name; const currentPageName = getPage(currentPageIndex, pages).name;
const SEPARATOR = '/'; const SEPARATOR = '/';
const tmpPageName = `${currentPageName}${SEPARATOR}${totalPages}`; const tmpPageName = `${currentPageName}${SEPARATOR}${totalPages}`;
return readingDirection.value === ReadingDirection.LTR ? tmpPageName : reverseString(tmpPageName, SEPARATOR); return readingDirection === ReadingDirection.LTR ? tmpPageName : reverseString(tmpPageName, SEPARATOR);
}, [currentPageIndex, pages, totalPages, readingDirection.value]); }, [currentPageIndex, pages, totalPages, readingDirection]);
if (!shouldShowPageNumber) { if (!shouldShowPageNumber) {
return null; return null;
@@ -74,3 +91,27 @@ export const ReaderPageNumber = () => {
</Stack> </Stack>
); );
}; };
export const ReaderPageNumber = withPropsFrom(
BaseReaderPageNumber,
[
ReaderService.useOverlayMode,
useReaderScrollbarContext,
useNavBarContext,
useReaderProgressBarContext,
userReaderStatePagesContext,
ReaderService.useSettingsWithoutDefaultFlag,
],
[
'isDesktop',
'scrollbarXSize',
'readerNavBarWidth',
'isMaximized',
'currentPageIndex',
'pages',
'totalPages',
'progressBarType',
'shouldShowPageNumber',
'readingDirection',
],
);

View File

@@ -9,18 +9,19 @@
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
export const ReaderRGBAFilter = () => { const BaseReaderRGBAFilter = ({
const { readerNavBarWidth } = useNavBarContext(); readerNavBarWidth,
const {
customFilter: { customFilter: {
rgba: { rgba: {
value: { red, green, blue, alpha }, value: { red, green, blue, alpha },
enabled, enabled,
}, },
}, },
} = ReaderService.useSettings(); }: Pick<NavbarContextType, 'readerNavBarWidth'> & Pick<IReaderSettings, 'customFilter'>) => {
if (!enabled) { if (!enabled) {
return null; return null;
} }
@@ -39,3 +40,9 @@ export const ReaderRGBAFilter = () => {
/> />
); );
}; };
export const ReaderRGBAFilter = withPropsFrom(
BaseReaderRGBAFilter,
[useNavBarContext, ReaderService.useSettingsWithoutDefaultFlag],
['readerNavBarWidth', 'customFilter'],
);

View File

@@ -14,15 +14,23 @@ import { ReaderTapZoneService } from '@/modules/reader/services/ReaderTapZoneSer
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx'; import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReadingDirection } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
const CANVAS_ID = 'reader-tap-zone-layout-canvas'; const CANVAS_ID = 'reader-tap-zone-layout-canvas';
export const TapZoneLayout = () => { const BaseTapZoneLayout = ({
readerNavBarWidth,
showPreview,
tapZoneLayout,
tapZoneInvertMode,
readingDirection,
}: Pick<NavbarContextType, 'readerNavBarWidth'> &
Pick<TReaderTapZoneContext, 'showPreview'> &
Pick<IReaderSettings, 'tapZoneLayout' | 'tapZoneInvertMode' | 'readingDirection'>) => {
const theme = useTheme(); const theme = useTheme();
const { readerNavBarWidth } = useNavBarContext();
const { showPreview } = useReaderTapZoneContext();
const { tapZoneLayout, readingDirection, tapZoneInvertMode } = ReaderService.useSettings();
const [width, setWidth] = useState(0); const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0); const [height, setHeight] = useState(0);
@@ -35,10 +43,10 @@ export const TapZoneLayout = () => {
}, [tapZoneLayoutElement]), }, [tapZoneLayoutElement]),
); );
const canvas = ReaderTapZoneService.getOrCreateCanvas(tapZoneLayout.value, width, height, theme.typography.h3, { const canvas = ReaderTapZoneService.getOrCreateCanvas(tapZoneLayout, width, height, theme.typography.h3, {
vertical: tapZoneInvertMode.value.vertical, vertical: tapZoneInvertMode.vertical,
horizontal: tapZoneInvertMode.value.horizontal, horizontal: tapZoneInvertMode.horizontal,
isRTL: readingDirection.value === ReadingDirection.RTL, isRTL: readingDirection === ReadingDirection.RTL,
}); });
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -62,3 +70,9 @@ export const TapZoneLayout = () => {
</Box> </Box>
); );
}; };
export const TapZoneLayout = withPropsFrom(
BaseTapZoneLayout,
[useNavBarContext, useReaderTapZoneContext, ReaderService.useSettingsWithoutDefaultFlag],
['readerNavBarWidth', 'showPreview', 'tapZoneLayout', 'tapZoneInvertMode', 'readingDirection'],
);

View File

@@ -16,10 +16,15 @@ import { ReaderNavBarDesktop } from '@/modules/reader/components/overlay/navigat
import { ReaderOverlayHeaderMobile } from '@/modules/reader/components/overlay/ReaderOverlayHeaderMobile.tsx'; import { ReaderOverlayHeaderMobile } from '@/modules/reader/components/overlay/ReaderOverlayHeaderMobile.tsx';
import { ReaderBottomBarMobile } from '@/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx'; import { ReaderBottomBarMobile } from '@/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobile.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderOverlay = ({ isVisible }: BaseReaderOverlayProps & MobileHeaderProps) => { const BaseReaderOverlay = ({
const { isDesktop, isMobile } = ReaderService.useOverlayMode(); isVisible,
isDesktop,
isMobile,
}: BaseReaderOverlayProps &
MobileHeaderProps &
Pick<ReturnType<typeof ReaderService.useOverlayMode>, 'isDesktop' | 'isMobile'>) => {
const [areSettingsOpen, setAreSettingsOpen] = useState(false); const [areSettingsOpen, setAreSettingsOpen] = useState(false);
return ( return (
@@ -44,3 +49,9 @@ export const ReaderOverlay = ({ isVisible }: BaseReaderOverlayProps & MobileHead
</Box> </Box>
); );
}; };
export const ReaderOverlay = withPropsFrom(
BaseReaderOverlay,
[ReaderService.useOverlayMode],
['isDesktop', 'isMobile'],
);

View File

@@ -28,21 +28,32 @@ import { actionToTranslationKey, ChapterAction, Chapters } from '@/modules/chapt
import { useBackButton } from '@/modules/core/hooks/useBackButton.ts'; import { useBackButton } from '@/modules/core/hooks/useBackButton.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts';
import { MobileHeaderProps } from '@/modules/reader/types/ReaderOverlay.types.ts'; import { MobileHeaderProps } from '@/modules/reader/types/ReaderOverlay.types.ts';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder'; import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import {
ReaderStateChapters,
TReaderScrollbarContext,
TReaderStateMangaContext,
} from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
const DEFAULT_MANGA = { id: -1, title: '' }; const DEFAULT_MANGA = { id: -1, title: '' };
const DEFAULT_CHAPTER = { id: -1, name: '', realUrl: '', isBookmarked: false }; const DEFAULT_CHAPTER = { id: -1, name: '', realUrl: '', isBookmarked: false };
export const ReaderOverlayHeaderMobile = ({ isVisible }: MobileHeaderProps) => { const BaseReaderOverlayHeaderMobile = ({
isVisible,
manga,
currentChapter,
scrollbarYSize,
}: MobileHeaderProps &
Pick<TReaderStateMangaContext, 'manga'> &
Pick<ReaderStateChapters, 'currentChapter'> &
Pick<TReaderScrollbarContext, 'scrollbarYSize'>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { manga } = useReaderStateMangaContext();
const { currentChapter } = useReaderStateChaptersContext();
const getOptionForDirection = useGetOptionForDirection(); const getOptionForDirection = useGetOptionForDirection();
const { scrollbarYSize } = useReaderScrollbarContext();
const handleBack = useBackButton(); const handleBack = useBackButton();
const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' }); const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' });
@@ -128,3 +139,9 @@ export const ReaderOverlayHeaderMobile = ({ isVisible }: MobileHeaderProps) => {
</Slide> </Slide>
); );
}; };
export const ReaderOverlayHeaderMobile = withPropsFrom(
BaseReaderOverlayHeaderMobile,
[useReaderStateMangaContext, useReaderStateChaptersContext, useReaderScrollbarContext],
['manga', 'currentChapter', 'scrollbarYSize'],
);

View File

@@ -32,6 +32,10 @@ import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/R
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts'; import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { ReaderStateChapters, TReaderStateMangaContext } from '@/modules/reader/types/Reader.types.ts';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const useGetPreviousNavBarStaticValue = (isVisible: boolean, isStaticNav: boolean) => { const useGetPreviousNavBarStaticValue = (isVisible: boolean, isStaticNav: boolean) => {
const wasNavBarStaticRef = useRef(isStaticNav); const wasNavBarStaticRef = useRef(isStaticNav);
@@ -52,17 +56,35 @@ const useGetPreviousNavBarStaticValue = (isVisible: boolean, isStaticNav: boolea
}; };
const DEFAULT_MANGA: MangaIdInfo = { id: -1 }; const DEFAULT_MANGA: MangaIdInfo = { id: -1 };
export const ReaderNavBarDesktop = ({ isVisible, openSettings }: ReaderNavBarDesktopProps) => { const BaseReaderNavBarDesktop = ({
isVisible,
openSettings,
setReaderNavBarWidth,
manga,
chapters,
currentChapter,
previousChapter,
nextChapter,
pages,
currentPageIndex,
pageLoadStates,
setPageLoadStates,
setRetryFailedPagesKeyPrefix,
exit,
}: ReaderNavBarDesktopProps &
Pick<NavbarContextType, 'setReaderNavBarWidth'> &
Pick<TReaderStateMangaContext, 'manga'> &
Pick<ReaderStateChapters, 'currentChapter' | 'previousChapter' | 'nextChapter' | 'chapters'> &
Pick<
ReaderStatePages,
'pages' | 'currentPageIndex' | 'pageLoadStates' | 'setPageLoadStates' | 'setRetryFailedPagesKeyPrefix'
> & {
exit: ReturnType<typeof ReaderService.useExit>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setReaderNavBarWidth } = useNavBarContext();
const { manga } = useReaderStateMangaContext();
const { chapters, currentChapter, nextChapter, previousChapter } = useReaderStateChaptersContext();
const { pages, currentPageIndex, pageLoadStates, setPageLoadStates, setRetryFailedPagesKeyPrefix } =
userReaderStatePagesContext();
const getOptionForDirection = useGetOptionForDirection(); const getOptionForDirection = useGetOptionForDirection();
const exit = ReaderService.useExit();
const updateReaderSettings = ReaderService.useCreateUpdateSetting(manga ?? DEFAULT_MANGA); const updateReaderSettings = ReaderService.useCreateUpdateSetting(manga ?? DEFAULT_MANGA);
const settings = ReaderService.useSettings(); const settings = ReaderService.useSettings();
@@ -154,3 +176,28 @@ export const ReaderNavBarDesktop = ({ isVisible, openSettings }: ReaderNavBarDes
</Drawer> </Drawer>
); );
}; };
export const ReaderNavBarDesktop = withPropsFrom(
BaseReaderNavBarDesktop,
[
useNavBarContext,
useReaderStateMangaContext,
useReaderStateChaptersContext,
userReaderStatePagesContext,
() => ({ exit: ReaderService.useExit() }),
],
[
'setReaderNavBarWidth',
'manga',
'chapters',
'currentChapter',
'previousChapter',
'nextChapter',
'pages',
'currentPageIndex',
'pageLoadStates',
'setPageLoadStates',
'setRetryFailedPagesKeyPrefix',
'exit',
],
);

View File

@@ -23,16 +23,20 @@ import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts'
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts'; import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts'; import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderNavBarDesktopChapterNavigation = ({ const BaseReaderNavBarDesktopChapterNavigation = ({
currentChapter, currentChapter,
previousChapter, previousChapter,
nextChapter, nextChapter,
chapters = [], chapters = [],
}: Pick<ReaderStateChapters, 'chapters' | 'currentChapter' | 'previousChapter' | 'nextChapter'>) => { readerThemeDirection,
openChapter,
}: Pick<ReaderStateChapters, 'chapters' | 'currentChapter' | 'previousChapter' | 'nextChapter'> & {
readerThemeDirection: ReturnType<typeof ReaderService.useGetThemeDirection>;
openChapter: ReturnType<typeof ReaderControls.useOpenChapter>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const readerThemeDirection = ReaderService.useGetThemeDirection();
const openChapter = ReaderControls.useOpenChapter();
const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' }); const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' });
@@ -111,3 +115,16 @@ export const ReaderNavBarDesktopChapterNavigation = ({
</Stack> </Stack>
); );
}; };
export const ReaderNavBarDesktopChapterNavigation = withPropsFrom(
BaseReaderNavBarDesktopChapterNavigation,
[
() => ({
readerThemeDirection: ReaderService.useGetThemeDirection(),
}),
() => ({
openChapter: ReaderControls.useOpenChapter(),
}),
],
['readerThemeDirection', 'openChapter'],
);

View File

@@ -20,18 +20,23 @@ import { useGetOptionForDirection } from '@/modules/theme/services/ThemeCreator.
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx'; import { ReaderNavBarDesktopNextPreviousButton } from '@/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopNextPreviousButton.tsx';
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx'; import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderNavBarDesktopPageNavigation = ({ const BaseReaderNavBarDesktopPageNavigation = ({
currentPageIndex, currentPageIndex,
pages, pages,
}: Pick<ReaderStatePages, 'currentPageIndex' | 'pages'>) => { readingDirection,
openPage,
}: Pick<ReaderStatePages, 'currentPageIndex' | 'pages'> &
Pick<IReaderSettings, 'readingDirection'> & {
openPage: ReturnType<typeof ReaderControls.useOpenPage>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const openPage = ReaderControls.useOpenPage();
const { readingDirection } = ReaderService.useSettings();
const getOptionForDirection = useGetOptionForDirection(); const getOptionForDirection = useGetOptionForDirection();
const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]); const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]);
const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection.value]; const direction = READING_DIRECTION_TO_THEME_DIRECTION[readingDirection];
return ( return (
<Stack sx={{ flexDirection: 'row', gap: 1 }} dir="ltr"> <Stack sx={{ flexDirection: 'row', gap: 1 }} dir="ltr">
@@ -73,3 +78,9 @@ export const ReaderNavBarDesktopPageNavigation = ({
</Stack> </Stack>
); );
}; };
export const ReaderNavBarDesktopPageNavigation = withPropsFrom(
BaseReaderNavBarDesktopPageNavigation,
[() => ({ openPage: ReaderControls.useOpenPage() }), ReaderService.useSettingsWithoutDefaultFlag],
['readingDirection', 'openPage'],
);

View File

@@ -25,11 +25,20 @@ import { ReaderChapterList } from '@/modules/reader/components/overlay/navigatio
import { ReaderBottomBarMobileQuickSettings } from '@/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobileQuickSettings.tsx'; import { ReaderBottomBarMobileQuickSettings } from '@/modules/reader/components/overlay/navigation/mobile/ReaderBottomBarMobileQuickSettings.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx'; import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx'; import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { ReaderStateChapters, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderBottomBarMobile = ({ openSettings, isVisible }: ReaderBottomBarMobileProps) => { const BaseReaderBottomBarMobile = ({
openSettings,
isVisible,
currentChapter,
chapters,
scrollbarXSize,
scrollbarYSize,
}: ReaderBottomBarMobileProps &
Pick<ReaderStateChapters, 'currentChapter' | 'chapters'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentChapter, chapters } = useReaderStateChaptersContext();
const { scrollbarXSize, scrollbarYSize } = useReaderScrollbarContext();
const chapterListPopupState = usePopupState({ variant: 'dialog', popupId: 'reader-chapter-list-dialog' }); const chapterListPopupState = usePopupState({ variant: 'dialog', popupId: 'reader-chapter-list-dialog' });
const quickSettingsPopupState = usePopupState({ variant: 'dialog', popupId: 'reader-quick-settings-dialog' }); const quickSettingsPopupState = usePopupState({ variant: 'dialog', popupId: 'reader-quick-settings-dialog' });
@@ -112,3 +121,9 @@ export const ReaderBottomBarMobile = ({ openSettings, isVisible }: ReaderBottomB
</> </>
); );
}; };
export const ReaderBottomBarMobile = withPropsFrom(
BaseReaderBottomBarMobile,
[useReaderStateChaptersContext, useReaderScrollbarContext],
['currentChapter', 'chapters', 'scrollbarXSize', 'scrollbarYSize'],
);

View File

@@ -13,11 +13,16 @@ import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx'; import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts'; import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { DefaultSettingFootnote } from '@/modules/reader/components/settings/DefaultSettingFootnote.tsx'; import { DefaultSettingFootnote } from '@/modules/reader/components/settings/DefaultSettingFootnote.tsx';
import { IReaderSettingsWithDefaultFlag, TReaderStateMangaContext } from '@/modules/reader/types/Reader.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const DEFAULT_MANGA: MangaIdInfo = { id: -1 }; const DEFAULT_MANGA: MangaIdInfo = { id: -1 };
export const ReaderBottomBarMobileQuickSettings = () => { const BaseReaderBottomBarMobileQuickSettings = ({
const { manga } = useReaderStateMangaContext(); manga,
const { readingMode, readingDirection } = ReaderService.useSettings(); readingMode,
readingDirection,
}: Pick<TReaderStateMangaContext, 'manga'> &
Pick<IReaderSettingsWithDefaultFlag, 'readingMode' | 'readingDirection'>) => {
const deleteSetting = ReaderService.useCreateDeleteSetting(manga ?? DEFAULT_MANGA); const deleteSetting = ReaderService.useCreateDeleteSetting(manga ?? DEFAULT_MANGA);
if (!manga) { if (!manga) {
@@ -42,3 +47,9 @@ export const ReaderBottomBarMobileQuickSettings = () => {
</Stack> </Stack>
); );
}; };
export const ReaderBottomBarMobileQuickSettings = withPropsFrom(
BaseReaderBottomBarMobileQuickSettings,
[useReaderStateMangaContext, ReaderService.useSettings],
['manga', 'readingMode', 'readingDirection'],
);

View File

@@ -11,8 +11,7 @@ import { ReactNode, useCallback, useMemo, useRef } from 'react';
import ClickAwayListener from '@mui/material/ClickAwayListener'; import ClickAwayListener from '@mui/material/ClickAwayListener';
import { TypographyProps } from '@mui/material/Typography'; import { TypographyProps } from '@mui/material/Typography';
import { StackProps } from '@mui/material/Stack'; import { StackProps } from '@mui/material/Stack';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx'; import { ReaderProgressBarProps, TReaderProgressBarContext } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { ReaderProgressBarProps } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { ReaderProgressBarPageNumber } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarPageNumber.tsx'; import { ReaderProgressBarPageNumber } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarPageNumber.tsx';
import { ReaderProgressBarContainer } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarContainer.tsx'; import { ReaderProgressBarContainer } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarContainer.tsx';
import { ReaderProgressBarRoot } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarRoot.tsx'; import { ReaderProgressBarRoot } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarRoot.tsx';
@@ -30,8 +29,10 @@ import { getOptionForDirection as getOptionForDirectionImpl } from '@/modules/th
import { ReaderProgressBarSlotsActionArea } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarSlotsActionArea.tsx'; import { ReaderProgressBarSlotsActionArea } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarSlotsActionArea.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts'; import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx';
export const ReaderProgressBar = ({ const BaseReaderProgressBar = ({
totalPages, totalPages,
pages, pages,
pageLoadStates, pageLoadStates,
@@ -40,7 +41,12 @@ export const ReaderProgressBar = ({
slots, slots,
createProgressBarSlot, createProgressBarSlot,
progressBarPosition, progressBarPosition,
}: ReaderProgressBarProps & { isDragging,
setIsDragging,
openPage,
direction,
}: ReaderProgressBarProps &
Pick<TReaderProgressBarContext, 'isDragging' | 'setIsDragging'> & {
createProgressBarSlot: ( createProgressBarSlot: (
page: ReaderProgressBarProps['pages'][number], page: ReaderProgressBarProps['pages'][number],
pageLoadStates: ReaderProgressBarProps['pageLoadStates'], pageLoadStates: ReaderProgressBarProps['pageLoadStates'],
@@ -63,11 +69,9 @@ export const ReaderProgressBar = ({
slots?: { slots?: {
progressBarCurrentPage?: ReactNode; progressBarCurrentPage?: ReactNode;
}; };
}) => { openPage: ReturnType<typeof ReaderControls.useOpenPage>;
const { isDragging, setIsDragging } = useReaderProgressBarContext(); direction: ReturnType<typeof ReaderService.useGetThemeDirection>;
const openPage = ReaderControls.useOpenPage(); }) => {
const direction = ReaderService.useGetThemeDirection();
const progressBarRef = useRef<HTMLDivElement | null>(null); const progressBarRef = useRef<HTMLDivElement | null>(null);
const isHorizontalPosition = getProgressBarPositionInfo(progressBarPosition).isHorizontal; const isHorizontalPosition = getProgressBarPositionInfo(progressBarPosition).isHorizontal;
@@ -212,3 +216,15 @@ export const ReaderProgressBar = ({
</ReaderProgressBarContainer> </ReaderProgressBarContainer>
); );
}; };
export const ReaderProgressBar = withPropsFrom(
BaseReaderProgressBar,
[
useReaderProgressBarContext,
() => ({ openPage: ReaderControls.useOpenPage() }),
() => ({
direction: ReaderService.useGetThemeDirection(),
}),
],
['isDragging', 'setIsDragging', 'openPage', 'direction'],
);

View File

@@ -16,9 +16,15 @@ import { ThemeMode } from '@/modules/theme/contexts/ThemeModeContext.tsx';
import { createTheme } from '@/modules/theme/services/ThemeCreator.ts'; import { createTheme } from '@/modules/theme/services/ThemeCreator.ts';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { DIRECTION_TO_CACHE } from '@/modules/theme/ThemeDirectionCache.ts'; import { DIRECTION_TO_CACHE } from '@/modules/theme/ThemeDirectionCache.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderProgressBarDirectionWrapper = ({ children }: { children: ReactNode }) => { const BaseReaderProgressBarDirectionWrapper = ({
const direction = ReaderService.useGetThemeDirection(); children,
direction,
}: {
children: ReactNode;
direction: ReturnType<typeof ReaderService.useGetThemeDirection>;
}) => {
const [appTheme] = useLocalStorage<AppThemes>('appTheme', 'default'); const [appTheme] = useLocalStorage<AppThemes>('appTheme', 'default');
const [themeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM); const [themeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM);
const [pureBlackMode] = useLocalStorage<boolean>('pureBlackMode', false); const [pureBlackMode] = useLocalStorage<boolean>('pureBlackMode', false);
@@ -40,3 +46,9 @@ export const ReaderProgressBarDirectionWrapper = ({ children }: { children: Reac
</CacheProvider> </CacheProvider>
); );
}; };
export const ReaderProgressBarDirectionWrapper = withPropsFrom(
BaseReaderProgressBarDirectionWrapper,
[() => ({ direction: ReaderService.useGetThemeDirection() })],
['direction'],
);

View File

@@ -20,15 +20,22 @@ import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/R
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; import { getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx';
import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts'; import { getOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts';
import { ProgressBarPosition, ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts'; import { ProgressBarPosition, ReaderResumeMode, ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
import { ReaderProgressBarDirectionWrapper } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarDirectionWrapper.tsx'; import { ReaderProgressBarDirectionWrapper } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarDirectionWrapper.tsx';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx'; import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx';
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx'; import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
import { TReaderProgressBarContext } from '@/modules/reader/types/ReaderProgressBar.types.ts';
export const MobileReaderProgressBar = () => { const BaseMobileReaderProgressBar = ({
const { nextChapter, previousChapter } = useReaderStateChaptersContext(); previousChapter,
const { isVisible } = useReaderOverlayContext(); nextChapter,
const { setIsMaximized } = useReaderProgressBarContext(); isVisible,
setIsMaximized,
}: Pick<ReaderStateChapters, 'previousChapter' | 'nextChapter'> &
Pick<TReaderOverlayContext, 'isVisible'> &
Pick<TReaderProgressBarContext, 'setIsMaximized'>) => {
const pagesState = userReaderStatePagesContext(); const pagesState = userReaderStatePagesContext();
const { currentPageIndex, pages } = pagesState; const { currentPageIndex, pages } = pagesState;
@@ -177,3 +184,9 @@ export const MobileReaderProgressBar = () => {
</ReaderProgressBarDirectionWrapper> </ReaderProgressBarDirectionWrapper>
); );
}; };
export const MobileReaderProgressBar = withPropsFrom(
BaseMobileReaderProgressBar,
[useReaderStateChaptersContext, useReaderOverlayContext, useReaderProgressBarContext],
['previousChapter', 'nextChapter', 'isVisible', 'setIsMaximized'],
);

View File

@@ -9,26 +9,40 @@
import { alpha, darken, lighten, useTheme } from '@mui/material/styles'; import { alpha, darken, lighten, useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { ReaderProgressBar } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBar.tsx'; import { ReaderProgressBar } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBar.tsx';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx';
import { ReaderProgressBarSlot } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarSlot.tsx'; import { ReaderProgressBarSlot } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarSlot.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx'; import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ProgressBarType } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ProgressBarType, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { getPage, getProgressBarPositionInfo } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; import { getPage, getProgressBarPositionInfo } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx';
import { ReaderProgressBarDirectionWrapper } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarDirectionWrapper.tsx'; import { ReaderProgressBarDirectionWrapper } from '@/modules/reader/components/overlay/progress-bar/ReaderProgressBarDirectionWrapper.tsx';
import { TReaderProgressBarContext } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { useReaderProgressBarContext } from '@/modules/reader/contexts/ReaderProgressBarContext.tsx';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx'; import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
export const StandardReaderProgressBar = () => { const BaseStandardReaderProgressBar = ({
readerNavBarWidth,
isMaximized,
setIsMaximized,
isDragging,
progressBarType,
progressBarSize,
progressBarPosition,
readerDirection,
scrollbarXSize,
scrollbarYSize,
}: Pick<NavbarContextType, 'readerNavBarWidth'> &
Pick<TReaderProgressBarContext, 'isMaximized' | 'setIsMaximized' | 'isDragging'> &
Pick<IReaderSettings, 'progressBarType' | 'progressBarSize' | 'progressBarPosition'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & {
readerDirection: ReturnType<typeof ReaderService.useGetThemeDirection>;
}) => {
const theme = useTheme(); const theme = useTheme();
const pagesState = userReaderStatePagesContext(); const pagesState = userReaderStatePagesContext();
const { currentPageIndex, pages } = pagesState; const { currentPageIndex, pages } = pagesState;
const { readerNavBarWidth } = useNavBarContext();
const { isMaximized, setIsMaximized, isDragging } = useReaderProgressBarContext();
const { progressBarType, progressBarSize, progressBarPosition } = ReaderService.useSettings();
const readerDirection = ReaderService.useGetThemeDirection();
const { scrollbarXSize, scrollbarYSize } = useReaderScrollbarContext();
const currentPagesIndex = getPage(currentPageIndex, pages).pagesIndex; const currentPagesIndex = getPage(currentPageIndex, pages).pagesIndex;
@@ -255,3 +269,26 @@ export const StandardReaderProgressBar = () => {
</ReaderProgressBarDirectionWrapper> </ReaderProgressBarDirectionWrapper>
); );
}; };
export const StandardReaderProgressBar = withPropsFrom(
BaseStandardReaderProgressBar,
[
useNavBarContext,
useReaderProgressBarContext,
ReaderService.useSettingsWithoutDefaultFlag,
() => ({ readerDirection: ReaderService.useGetThemeDirection() }),
useReaderScrollbarContext,
],
[
'readerNavBarWidth',
'isMaximized',
'setIsMaximized',
'isDragging',
'progressBarType',
'progressBarSize',
'progressBarPosition',
'readerDirection',
'scrollbarXSize',
'scrollbarYSize',
],
);

View File

@@ -23,28 +23,30 @@ import { ReaderFilterSettings } from '@/modules/reader/components/settings/filte
import { ReaderBehaviourSettings } from '@/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx'; import { ReaderBehaviourSettings } from '@/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx';
import { ReaderDefaultLayoutSettings } from '@/modules/reader/components/settings/layout/ReaderDefaultLayoutSettings.tsx'; import { ReaderDefaultLayoutSettings } from '@/modules/reader/components/settings/layout/ReaderDefaultLayoutSettings.tsx';
import { ReaderHotkeysSettings } from '@/modules/reader/components/settings/hotkeys/ReaderHotkeysSettings.tsx'; import { ReaderHotkeysSettings } from '@/modules/reader/components/settings/hotkeys/ReaderHotkeysSettings.tsx';
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderSettingsTabs = ({ const BaseReaderSettingsTabs = ({
activeTab, activeTab,
setActiveTab, setActiveTab,
areDefaultSettings, areDefaultSettings,
settings, settings,
updateSetting, updateSetting,
deleteSetting, deleteSetting,
}: { setShowPreview,
mode: overlayMode,
}: Pick<TReaderTapZoneContext, 'setShowPreview'> &
Pick<ReturnType<typeof ReaderService.useOverlayMode>, 'mode'> & {
activeTab: number; activeTab: number;
setActiveTab: (tab: number) => void; setActiveTab: (tab: number) => void;
settings: IReaderSettingsWithDefaultFlag; settings: IReaderSettingsWithDefaultFlag;
updateSetting: (...args: OmitFirst<Parameters<typeof ReaderService.updateSetting>>) => void; updateSetting: (...args: OmitFirst<Parameters<typeof ReaderService.updateSetting>>) => void;
areDefaultSettings?: boolean; areDefaultSettings?: boolean;
deleteSetting: (setting: keyof IReaderSettings) => void; deleteSetting: (setting: keyof IReaderSettings) => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setShowPreview } = useReaderTapZoneContext();
const isTouchDevice = MediaQuery.useIsTouchDevice(); const isTouchDevice = MediaQuery.useIsTouchDevice();
const { mode: overlayMode } = ReaderService.useOverlayMode();
return ( return (
<> <>
<TabsMenu <TabsMenu
@@ -177,3 +179,9 @@ export const ReaderSettingsTabs = ({
</> </>
); );
}; };
export const ReaderSettingsTabs = withPropsFrom(
BaseReaderSettingsTabs,
[useReaderTapZoneContext, ReaderService.useOverlayMode],
['setShowPreview', 'mode'],
);

View File

@@ -9,7 +9,7 @@
import { ComponentProps, ForwardedRef, forwardRef } from 'react'; import { ComponentProps, ForwardedRef, forwardRef } from 'react';
import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx'; import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReaderCustomFilter } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderCustomFilter, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts';
import { import {
getImageMarginStyling, getImageMarginStyling,
getImagePlaceholderStyling, getImagePlaceholderStyling,
@@ -18,6 +18,7 @@ import {
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx'; import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx'; import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const getCustomFilterString = (customFilter: ReaderCustomFilter): string => const getCustomFilterString = (customFilter: ReaderCustomFilter): string =>
Object.keys(customFilter) Object.keys(customFilter)
@@ -48,7 +49,7 @@ const getCustomFilterString = (customFilter: ReaderCustomFilter): string =>
}) })
.join(' '); .join(' ');
export const ReaderPage = forwardRef( const BaseReaderPage = forwardRef(
( (
{ {
display, display,
@@ -56,8 +57,20 @@ export const ReaderPage = forwardRef(
position, position,
marginTop, marginTop,
shouldLoad, shouldLoad,
readingMode,
customFilter,
pageScaleMode,
shouldStretchPage,
readerWidth,
scrollbarXSize,
scrollbarYSize,
...props ...props
}: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle'> & { }: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle'> &
Pick<
IReaderSettings,
'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth'
> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & {
display: boolean; display: boolean;
doublePage?: boolean; doublePage?: boolean;
position?: 'left' | 'right'; position?: 'left' | 'right';
@@ -65,9 +78,6 @@ export const ReaderPage = forwardRef(
}, },
ref: ForwardedRef<HTMLImageElement | null>, ref: ForwardedRef<HTMLImageElement | null>,
) => { ) => {
const { readingMode, customFilter, pageScaleMode, shouldStretchPage, readerWidth } =
ReaderService.useSettings();
const { scrollbarXSize, scrollbarYSize } = useReaderScrollbarContext();
const isTabletWidth = MediaQuery.useIsTabletWidth(); const isTabletWidth = MediaQuery.useIsTabletWidth();
if (!display && !shouldLoad) { if (!display && !shouldLoad) {
@@ -83,10 +93,10 @@ export const ReaderPage = forwardRef(
spinnerStyle={{ spinnerStyle={{
backgroundColor: 'background.paper', backgroundColor: 'background.paper',
...getImagePlaceholderStyling( ...getImagePlaceholderStyling(
readingMode.value, readingMode,
shouldStretchPage.value, shouldStretchPage,
pageScaleMode.value, pageScaleMode,
readerWidth.value, readerWidth,
scrollbarXSize, scrollbarXSize,
scrollbarYSize, scrollbarYSize,
doublePage, doublePage,
@@ -99,11 +109,11 @@ export const ReaderPage = forwardRef(
}} }}
imgStyle={{ imgStyle={{
...getImageWidthStyling( ...getImageWidthStyling(
readingMode.value, readingMode,
shouldStretchPage.value, shouldStretchPage,
pageScaleMode.value, pageScaleMode,
doublePage, doublePage,
readerWidth.value, readerWidth,
true, true,
), ),
display: 'block', display: 'block',
@@ -130,3 +140,17 @@ export const ReaderPage = forwardRef(
); );
}, },
); );
export const ReaderPage = withPropsFrom(
BaseReaderPage,
[ReaderService.useSettingsWithoutDefaultFlag, useReaderScrollbarContext],
[
'readingMode',
'customFilter',
'pageScaleMode',
'shouldStretchPage',
'readerWidth',
'scrollbarXSize',
'scrollbarYSize',
],
);

View File

@@ -18,12 +18,14 @@ import { TChapterReader } from '@/modules/chapter/Chapter.types.ts';
import { import {
IReaderSettings, IReaderSettings,
ReaderPageScaleMode, ReaderPageScaleMode,
ReaderStateChapters,
ReaderTransitionPageMode, ReaderTransitionPageMode,
ReadingMode, ReadingMode,
TReaderScrollbarContext,
TReaderStateMangaContext,
} from '@/modules/reader/types/Reader.types.ts'; } from '@/modules/reader/types/Reader.types.ts';
import { isTransitionPageVisible } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { isTransitionPageVisible } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { useBackButton } from '@/modules/core/hooks/useBackButton.ts'; import { useBackButton } from '@/modules/core/hooks/useBackButton.ts';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { import {
isContinuousReadingMode, isContinuousReadingMode,
@@ -31,6 +33,8 @@ import {
} from '@/modules/reader/utils/ReaderSettings.utils.tsx'; } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const ChapterInfo = ({ const ChapterInfo = ({
title, title,
@@ -58,22 +62,29 @@ const ChapterInfo = ({
); );
}; };
export const ReaderTransitionPage = ({ const BaseReaderTransitionPage = ({
type, type,
mode, mode,
readingMode, readingMode,
pageScaleMode, pageScaleMode,
}: Pick<IReaderSettings, 'readingMode' | 'pageScaleMode'> & { manga,
currentChapter,
previousChapter,
nextChapter,
scrollbarXSize,
scrollbarYSize,
readerNavBarWidth,
}: Pick<IReaderSettings, 'readingMode' | 'pageScaleMode'> &
Pick<TReaderStateMangaContext, 'manga'> &
Pick<ReaderStateChapters, 'currentChapter' | 'previousChapter' | 'nextChapter'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> &
Pick<NavbarContextType, 'readerNavBarWidth'> & {
type: Exclude<ReaderTransitionPageMode, ReaderTransitionPageMode.NONE | ReaderTransitionPageMode.BOTH>; type: Exclude<ReaderTransitionPageMode, ReaderTransitionPageMode.NONE | ReaderTransitionPageMode.BOTH>;
mode: ReaderTransitionPageMode; mode: ReaderTransitionPageMode;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const handleBack = useBackButton(); const handleBack = useBackButton();
const { manga } = useReaderStateMangaContext();
const { currentChapter, nextChapter, previousChapter } = useReaderStateChaptersContext();
const { scrollbarXSize, scrollbarYSize } = useReaderScrollbarContext();
const { readerNavBarWidth } = useNavBarContext();
const isPreviousType = type === ReaderTransitionPageMode.PREVIOUS; const isPreviousType = type === ReaderTransitionPageMode.PREVIOUS;
const isNextType = type === ReaderTransitionPageMode.NEXT; const isNextType = type === ReaderTransitionPageMode.NEXT;
@@ -191,3 +202,17 @@ export const ReaderTransitionPage = ({
</Stack> </Stack>
); );
}; };
export const ReaderTransitionPage = withPropsFrom(
BaseReaderTransitionPage,
[useReaderStateChaptersContext, useReaderScrollbarContext, useNavBarContext],
[
'manga',
'currentChapter',
'previousChapter',
'nextChapter',
'scrollbarXSize',
'scrollbarYSize',
'readerNavBarWidth',
],
);

View File

@@ -19,7 +19,13 @@ import {
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { PageInViewportType, ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts'; import {
IReaderSettings,
PageInViewportType,
ReadingDirection,
ReadingMode,
TReaderScrollbarContext,
} from '@/modules/reader/types/Reader.types.ts';
import { ReaderPagedPager } from '@/modules/reader/components/viewer/pager/ReaderPagedPager.tsx'; import { ReaderPagedPager } from '@/modules/reader/components/viewer/pager/ReaderPagedPager.tsx';
import { ReaderDoublePagedPager } from '@/modules/reader/components/viewer/pager/ReaderDoublePagedPager.tsx'; import { ReaderDoublePagedPager } from '@/modules/reader/components/viewer/pager/ReaderDoublePagedPager.tsx';
import { ReaderHorizontalPager } from '@/modules/reader/components/viewer/pager/ReaderHorizontalPager.tsx'; import { ReaderHorizontalPager } from '@/modules/reader/components/viewer/pager/ReaderHorizontalPager.tsx';
@@ -43,6 +49,9 @@ import { useMouseDragScroll } from '@/modules/core/hooks/useMouseDragScroll.tsx'
import { DirectionOffset } from '@/Base.types.ts'; import { DirectionOffset } from '@/Base.types.ts';
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx'; import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const READING_MODE_TO_IN_VIEWPORT_TYPE: Record<ReadingMode, PageInViewportType> = { const READING_MODE_TO_IN_VIEWPORT_TYPE: Record<ReadingMode, PageInViewportType> = {
[ReadingMode.SINGLE_PAGE]: PageInViewportType.X, [ReadingMode.SINGLE_PAGE]: PageInViewportType.X,
@@ -52,8 +61,9 @@ const READING_MODE_TO_IN_VIEWPORT_TYPE: Record<ReadingMode, PageInViewportType>
[ReadingMode.WEBTOON]: PageInViewportType.Y, [ReadingMode.WEBTOON]: PageInViewportType.Y,
}; };
export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | null>) => { const BaseReaderViewer = forwardRef(
const { (
{
currentPageIndex, currentPageIndex,
pageToScrollToIndex, pageToScrollToIndex,
pages, pages,
@@ -64,17 +74,41 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
setPageLoadStates, setPageLoadStates,
transitionPageMode, transitionPageMode,
retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix,
} = userReaderStatePagesContext(); readingMode,
shouldOffsetDoubleSpreads,
readingDirection,
setScrollbarXSize,
setScrollbarYSize,
isVisible: isOverlayVisible,
setIsVisible: setIsOverlayVisible,
updateCurrentPageIndex,
}: Pick<
ReaderStatePages,
| 'currentPageIndex'
| 'pageToScrollToIndex'
| 'pages'
| 'setPages'
| 'totalPages'
| 'pageUrls'
| 'pageLoadStates'
| 'setPageLoadStates'
| 'transitionPageMode'
| 'retryFailedPagesKeyPrefix'
> &
Pick<IReaderSettings, 'readingMode' | 'shouldOffsetDoubleSpreads' | 'readingDirection'> &
Pick<TReaderScrollbarContext, 'setScrollbarXSize' | 'setScrollbarYSize'> &
Pick<TReaderOverlayContext, 'isVisible' | 'setIsVisible'> & {
updateCurrentPageIndex: ReturnType<typeof ReaderControls.useUpdateCurrentPageIndex>;
},
ref: ForwardedRef<HTMLDivElement | null>,
) => {
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
const { readingMode, shouldOffsetDoubleSpreads, readingDirection } = ReaderService.useSettings();
const { setScrollbarXSize, setScrollbarYSize } = useReaderScrollbarContext();
const { isVisible: isOverlayVisible, setIsVisible: setIsOverlayVisible } = useReaderOverlayContext();
const updateCurrentPageIndex = ReaderControls.useUpdateCurrentPageIndex();
const scrollElementRef = useRef<HTMLDivElement | null>(null); const scrollElementRef = useRef<HTMLDivElement | null>(null);
useImperativeHandle(ref, () => scrollElementRef.current!); useImperativeHandle(ref, () => scrollElementRef.current!);
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value); const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
const isDragging = useMouseDragScroll(isContinuousReadingModeActive ? scrollElementRef : undefined); const isDragging = useMouseDragScroll(isContinuousReadingModeActive ? scrollElementRef : undefined);
const scrollbarXSize = MediaQuery.useGetScrollbarSize('width', scrollElementRef.current); const scrollbarXSize = MediaQuery.useGetScrollbarSize('width', scrollElementRef.current);
@@ -86,7 +120,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
const handleClick = ReaderControls.useHandleClick(scrollElementRef.current); const handleClick = ReaderControls.useHandleClick(scrollElementRef.current);
const [wasDoublePageMode, setWasDoublePageMode] = useState(readingMode.value === ReadingMode.DOUBLE_PAGE); const [wasDoublePageMode, setWasDoublePageMode] = useState(readingMode === ReadingMode.DOUBLE_PAGE);
const [pagesToSpreadState, setPagesToSpreadState] = useState(Array(totalPages).fill(false)); const [pagesToSpreadState, setPagesToSpreadState] = useState(Array(totalPages).fill(false));
const imageRefs = useRef<(HTMLElement | null)[]>(pages.map(() => null)); const imageRefs = useRef<(HTMLElement | null)[]>(pages.map(() => null));
@@ -97,20 +131,20 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
return pages; return pages;
} }
if (readingMode.value === ReadingMode.DOUBLE_PAGE) { if (readingMode === ReadingMode.DOUBLE_PAGE) {
return getDoublePageModePages( return getDoublePageModePages(
pageUrls, pageUrls,
pagesToSpreadState, pagesToSpreadState,
shouldOffsetDoubleSpreads.value, shouldOffsetDoubleSpreads,
readingDirection.value, readingDirection,
); );
} }
return pages; return pages;
}, [pagesToSpreadState, readingMode.value, shouldOffsetDoubleSpreads.value, readingDirection.value]); }, [pagesToSpreadState, readingMode, shouldOffsetDoubleSpreads, readingDirection]);
const Pager = useMemo(() => { const Pager = useMemo(() => {
switch (readingMode.value) { switch (readingMode) {
case ReadingMode.SINGLE_PAGE: case ReadingMode.SINGLE_PAGE:
return ReaderPagedPager; return ReaderPagedPager;
case ReadingMode.DOUBLE_PAGE: case ReadingMode.DOUBLE_PAGE:
@@ -121,11 +155,11 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
case ReadingMode.CONTINUOUS_HORIZONTAL: case ReadingMode.CONTINUOUS_HORIZONTAL:
return ReaderHorizontalPager; return ReaderHorizontalPager;
default: default:
throw new Error(`Unexpected "ReadingMode" (${readingMode.value})`); throw new Error(`Unexpected "ReadingMode" (${readingMode})`);
} }
}, [readingMode.value]); }, [readingMode]);
const inViewportType = READING_MODE_TO_IN_VIEWPORT_TYPE[readingMode.value]; const inViewportType = READING_MODE_TO_IN_VIEWPORT_TYPE[readingMode];
// reset spread state // reset spread state
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -134,7 +168,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
// optionally convert pages to normal or double page mode // optionally convert pages to normal or double page mode
useLayoutEffect(() => { useLayoutEffect(() => {
const convertPagesToNormalPageMode = wasDoublePageMode && readingMode.value !== ReadingMode.DOUBLE_PAGE; const convertPagesToNormalPageMode = wasDoublePageMode && readingMode !== ReadingMode.DOUBLE_PAGE;
if (convertPagesToNormalPageMode) { if (convertPagesToNormalPageMode) {
setWasDoublePageMode(false); setWasDoublePageMode(false);
setPages(createPagesData(pageUrls)); setPages(createPagesData(pageUrls));
@@ -142,15 +176,15 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
return; return;
} }
const convertPagesToDoublePageMode = readingMode.value === ReadingMode.DOUBLE_PAGE; const convertPagesToDoublePageMode = readingMode === ReadingMode.DOUBLE_PAGE;
if (convertPagesToDoublePageMode) { if (convertPagesToDoublePageMode) {
if (!wasDoublePageMode) { if (!wasDoublePageMode) {
updateCurrentPageIndex(getNextIndexFromPage(getPage(currentPageIndex, actualPages))); updateCurrentPageIndex(getNextIndexFromPage(getPage(currentPageIndex, actualPages)));
} }
setPages(actualPages); setPages(actualPages);
setWasDoublePageMode(readingMode.value === ReadingMode.DOUBLE_PAGE); setWasDoublePageMode(readingMode === ReadingMode.DOUBLE_PAGE);
} }
}, [actualPages, readingMode.value]); }, [actualPages, readingMode]);
// handle user page selection // handle user page selection
useLayoutEffect(() => { useLayoutEffect(() => {
@@ -163,7 +197,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
imageRef?.scrollIntoView({ imageRef?.scrollIntoView({
block: 'start', block: 'start',
inline: getScrollIntoViewInlineOption(directionOffset, themeDirection, readingDirection.value), inline: getScrollIntoViewInlineOption(directionOffset, themeDirection, readingDirection),
}); });
} }
@@ -204,7 +238,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
// invert x and y scrolling for the continuous horizontal reading mode // invert x and y scrolling for the continuous horizontal reading mode
useEffect(() => { useEffect(() => {
if (readingMode.value !== ReadingMode.CONTINUOUS_HORIZONTAL) { if (readingMode !== ReadingMode.CONTINUOUS_HORIZONTAL) {
return () => {}; return () => {};
} }
@@ -223,13 +257,13 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
} }
scrollElementRef.current?.scrollBy({ scrollElementRef.current?.scrollBy({
left: readingDirection.value === ReadingDirection.LTR ? e.deltaY : e.deltaY * -1, left: readingDirection === ReadingDirection.LTR ? e.deltaY : e.deltaY * -1,
}); });
}; };
scrollElementRef.current.addEventListener('wheel', handleScroll); scrollElementRef.current.addEventListener('wheel', handleScroll);
return () => scrollElementRef.current?.removeEventListener('wheel', handleScroll); return () => scrollElementRef.current?.removeEventListener('wheel', handleScroll);
}, [readingMode.value, readingDirection.value]); }, [readingMode, readingDirection]);
// hide overlay on user triggered scroll // hide overlay on user triggered scroll
useEffect(() => { useEffect(() => {
@@ -254,7 +288,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
width: '100%', width: '100%',
height: '100%', height: '100%',
overflow: 'auto', overflow: 'auto',
...applyStyles(isContinuousVerticalReadingMode(readingMode.value), { alignItems: 'center' }), ...applyStyles(isContinuousVerticalReadingMode(readingMode), { alignItems: 'center' }),
}} }}
onClick={(e) => !isDragging && handleClick(e)} onClick={(e) => !isDragging && handleClick(e)}
onScroll={() => onScroll={() =>
@@ -263,7 +297,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
totalPages - 1, totalPages - 1,
updateCurrentPageIndex, updateCurrentPageIndex,
inViewportType, inViewportType,
readingDirection.value, readingDirection,
) )
} }
> >
@@ -279,7 +313,7 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
const page = actualPages[pagesIndex]; const page = actualPages[pagesIndex];
const { index, url } = isPrimary ? page.primary : page.secondary!; const { index, url } = isPrimary ? page.primary : page.secondary!;
if (readingMode.value === ReadingMode.DOUBLE_PAGE) { if (readingMode === ReadingMode.DOUBLE_PAGE) {
const img = new Image(); const img = new Image();
img.onload = () => { img.onload = () => {
setPagesToSpreadState((prevState) => prevState.toSpliced(index, 1, isSpreadPage(img))); setPagesToSpreadState((prevState) => prevState.toSpliced(index, 1, isSpreadPage(img)));
@@ -299,4 +333,36 @@ export const ReaderViewer = forwardRef((_, ref: ForwardedRef<HTMLDivElement | nu
/> />
</Stack> </Stack>
); );
}); },
);
export const ReaderViewer = withPropsFrom(
BaseReaderViewer,
[
userReaderStatePagesContext,
ReaderService.useSettingsWithoutDefaultFlag,
useReaderScrollbarContext,
useReaderOverlayContext,
() => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }),
],
[
'currentPageIndex',
'pageToScrollToIndex',
'pages',
'setPages',
'totalPages',
'pageUrls',
'pageLoadStates',
'setPageLoadStates',
'transitionPageMode',
'retryFailedPagesKeyPrefix',
'readingMode',
'shouldOffsetDoubleSpreads',
'readingDirection',
'setScrollbarXSize',
'setScrollbarYSize',
'isVisible',
'setIsVisible',
'updateCurrentPageIndex',
],
);

View File

@@ -11,17 +11,27 @@ import Box, { BoxProps } from '@mui/material/Box';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { getImageWidthStyling, getPageIndexesToLoad } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { getImageWidthStyling, getPageIndexesToLoad } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts'; import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { ReaderPagerProps, ReaderTransitionPageMode } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReaderTransitionPageMode } from '@/modules/reader/types/Reader.types.ts';
import { ReaderTransitionPage } from '@/modules/reader/components/viewer/ReaderTransitionPage.tsx'; import { ReaderTransitionPage } from '@/modules/reader/components/viewer/ReaderTransitionPage.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const BasePager = ({ const BaseBasePager = ({
currentPageIndex, currentPageIndex,
pages, pages,
transitionPageMode, transitionPageMode,
imageRefs, imageRefs,
createPage, createPage,
slots, slots,
}: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> & { readingMode,
pageScaleMode,
shouldStretchPage,
readerWidth,
imagePreLoadAmount,
}: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> &
Pick<
IReaderSettings,
'readingMode' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth' | 'imagePreLoadAmount'
> & {
createPage: ( createPage: (
page: ReaderStatePages['pages'][number], page: ReaderStatePages['pages'][number],
pagesIndex: number, pagesIndex: number,
@@ -30,10 +40,7 @@ export const BasePager = ({
setRef: (element: HTMLElement | null) => void, setRef: (element: HTMLElement | null) => void,
) => ReactNode; ) => ReactNode;
slots?: { boxProps?: BoxProps }; slots?: { boxProps?: BoxProps };
}) => { }) => {
const { readingMode, pageScaleMode, shouldStretchPage, readerWidth, imagePreLoadAmount } =
ReaderService.useSettings();
const previousCurrentPageIndex = useRef(-1); const previousCurrentPageIndex = useRef(-1);
const pagesIndexesToRender = useMemo( const pagesIndexesToRender = useMemo(
() => getPageIndexesToLoad(currentPageIndex, pages, previousCurrentPageIndex.current, imagePreLoadAmount), () => getPageIndexesToLoad(currentPageIndex, pages, previousCurrentPageIndex.current, imagePreLoadAmount),
@@ -48,20 +55,14 @@ export const BasePager = ({
{...slots?.boxProps} {...slots?.boxProps}
sx={[ sx={[
...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]), ...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]),
getImageWidthStyling( getImageWidthStyling(readingMode, shouldStretchPage, pageScaleMode, false, readerWidth),
readingMode.value,
shouldStretchPage.value,
pageScaleMode.value,
false,
readerWidth.value,
),
]} ]}
> >
<ReaderTransitionPage <ReaderTransitionPage
type={ReaderTransitionPageMode.PREVIOUS} type={ReaderTransitionPageMode.PREVIOUS}
mode={transitionPageMode} mode={transitionPageMode}
readingMode={readingMode.value} readingMode={readingMode}
pageScaleMode={pageScaleMode.value} pageScaleMode={pageScaleMode}
/> />
{pages.map((page, pagesIndex) => {pages.map((page, pagesIndex) =>
createPage( createPage(
@@ -78,9 +79,15 @@ export const BasePager = ({
<ReaderTransitionPage <ReaderTransitionPage
type={ReaderTransitionPageMode.NEXT} type={ReaderTransitionPageMode.NEXT}
mode={transitionPageMode} mode={transitionPageMode}
readingMode={readingMode.value} readingMode={readingMode}
pageScaleMode={pageScaleMode.value} pageScaleMode={pageScaleMode}
/> />
</Box> </Box>
); );
}; };
export const BasePager = withPropsFrom(
BaseBasePager,
[ReaderService.useSettingsWithoutDefaultFlag],
['readingMode', 'pageScaleMode', 'shouldStretchPage', 'readerWidth', 'imagePreLoadAmount'],
);

View File

@@ -10,10 +10,11 @@ import { Direction, useTheme } from '@mui/material/styles';
import { Fragment, useMemo } from 'react'; import { Fragment, useMemo } from 'react';
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx'; import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { getNextIndexFromPage, getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; import { getNextIndexFromPage, getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
const getPagePosition = ( const getPagePosition = (
pageType: 'first' | 'second', pageType: 'first' | 'second',
@@ -37,20 +38,20 @@ const getPagePosition = (
return isLtrReadingDirection ? 'right' : 'left'; return isLtrReadingDirection ? 'right' : 'left';
}; };
export const ReaderDoublePagedPager = ({ const BaseReaderDoublePagedPager = ({
onLoad, onLoad,
onError, onError,
pageLoadStates, pageLoadStates,
retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix,
readingDirection,
...props ...props
}: ReaderPagerProps) => { }: ReaderPagerProps & Pick<IReaderSettings, 'readingDirection'>) => {
const { currentPageIndex, pages, totalPages } = props; const { currentPageIndex, pages, totalPages } = props;
const { readingDirection } = ReaderService.useSettings();
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]); const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]);
const isLtrReadingDirection = readingDirection.value === ReadingDirection.LTR; const isLtrReadingDirection = readingDirection === ReadingDirection.LTR;
return ( return (
<BasePager <BasePager
@@ -75,9 +76,7 @@ export const ReaderDoublePagedPager = ({
currentPage.primary.index, currentPage.primary.index,
totalPages, totalPages,
pageLoadStates[primary.index].error ? retryFailedPagesKeyPrefix : undefined, pageLoadStates[primary.index].error ? retryFailedPagesKeyPrefix : undefined,
hasSecondaryPage hasSecondaryPage ? getPagePosition('first', themeDirection, readingDirection) : undefined,
? getPagePosition('first', themeDirection, readingDirection.value)
: undefined,
hasSecondaryPage, hasSecondaryPage,
)} )}
{hasSecondaryPage && {hasSecondaryPage &&
@@ -90,7 +89,7 @@ export const ReaderDoublePagedPager = ({
currentSecondaryPageIndex, currentSecondaryPageIndex,
totalPages, totalPages,
pageLoadStates[secondary.index].error ? retryFailedPagesKeyPrefix : undefined, pageLoadStates[secondary.index].error ? retryFailedPagesKeyPrefix : undefined,
getPagePosition('second', themeDirection, readingDirection.value), getPagePosition('second', themeDirection, readingDirection),
true, true,
)} )}
</Fragment> </Fragment>
@@ -115,3 +114,9 @@ export const ReaderDoublePagedPager = ({
/> />
); );
}; };
export const ReaderDoublePagedPager = withPropsFrom(
BaseReaderDoublePagedPager,
[ReaderService.useSettingsWithoutDefaultFlag],
['readingDirection'],
);

View File

@@ -10,22 +10,24 @@ import { useTheme } from '@mui/material/styles';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx'; import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderHorizontalPager = ({ const BaseReaderHorizontalPager = ({
onLoad, onLoad,
onError, onError,
pageLoadStates, pageLoadStates,
retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix,
pageGap,
readingDirection,
...props ...props
}: ReaderPagerProps) => { }: ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'>) => {
const { currentPageIndex, totalPages } = props; const { currentPageIndex, totalPages } = props;
const { pageGap, readingDirection } = ReaderService.useSettings();
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
const isLtrReadingDirection = readingDirection.value === ReadingDirection.LTR; const isLtrReadingDirection = readingDirection === ReadingDirection.LTR;
return ( return (
<BasePager <BasePager
@@ -53,7 +55,7 @@ export const ReaderHorizontalPager = ({
display: 'flex', display: 'flex',
flexWrap: 'nowrap', flexWrap: 'nowrap',
alignItems: 'center', alignItems: 'center',
gap: `${pageGap.value}px`, gap: `${pageGap}px`,
...applyStyles(themeDirection === 'ltr', { ...applyStyles(themeDirection === 'ltr', {
flexDirection: isLtrReadingDirection ? 'row' : 'row-reverse', flexDirection: isLtrReadingDirection ? 'row' : 'row-reverse',
justifyContent: isLtrReadingDirection ? 'flex-start' : 'flex-end', justifyContent: isLtrReadingDirection ? 'flex-start' : 'flex-end',
@@ -68,3 +70,9 @@ export const ReaderHorizontalPager = ({
/> />
); );
}; };
export const ReaderHorizontalPager = withPropsFrom(
BaseReaderHorizontalPager,
[ReaderService.useSettingsWithoutDefaultFlag],
['pageGap', 'readingDirection'],
);

View File

@@ -7,22 +7,24 @@
*/ */
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx'; import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
import { ReaderPagerProps, ReadingMode } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
export const ReaderVerticalPager = ({ const BaseReaderVerticalPager = ({
onLoad, onLoad,
onError, onError,
pageLoadStates, pageLoadStates,
retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix,
readingMode,
pageGap,
...props ...props
}: ReaderPagerProps) => { }: ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingMode'>) => {
const { currentPageIndex, totalPages } = props; const { currentPageIndex, totalPages } = props;
const { readingMode, pageGap } = ReaderService.useSettings(); const isWebtoonMode = readingMode === ReadingMode.WEBTOON;
const isWebtoonMode = readingMode.value === ReadingMode.WEBTOON; const actualPageGap = isWebtoonMode ? 0 : pageGap;
const actualPageGap = isWebtoonMode ? 0 : pageGap.value;
return ( return (
<BasePager <BasePager
@@ -46,3 +48,9 @@ export const ReaderVerticalPager = ({
/> />
); );
}; };
export const ReaderVerticalPager = withPropsFrom(
BaseReaderVerticalPager,
[ReaderService.useSettingsWithoutDefaultFlag],
['pageGap', 'readingMode'],
);

View File

@@ -7,7 +7,7 @@
*/ */
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@@ -24,7 +24,6 @@ import { LoadingPlaceholder } from '@/modules/core/components/placeholder/Loadin
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx'; import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx'; import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts'; import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts';
import { Chapters } from '@/modules/chapter/services/Chapters.ts'; import { Chapters } from '@/modules/chapter/services/Chapters.ts';
import { DirectionOffset } from '@/Base.types.ts'; import { DirectionOffset } from '@/Base.types.ts';
@@ -38,38 +37,72 @@ import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { READER_BACKGROUND_TO_COLOR } from '@/modules/reader/constants/ReaderSettings.constants.tsx'; import { READER_BACKGROUND_TO_COLOR } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
import { createPageData, createPagesData } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { createPageData, createPagesData } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { ReaderHotkeys } from '@/modules/reader/components/ReaderHotkeys.tsx'; import { ReaderHotkeys } from '@/modules/reader/components/ReaderHotkeys.tsx';
import { ReaderResumeMode, ReaderTransitionPageMode } from '@/modules/reader/types/Reader.types.ts'; import {
IReaderSettings,
ReaderResumeMode,
ReaderStateChapters,
ReaderTransitionPageMode,
TReaderStateMangaContext,
TReaderStateSettingsContext,
} from '@/modules/reader/types/Reader.types.ts';
import { getInitialReaderPageIndex } from '@/modules/reader/utils/Reader.utils.ts'; import { getInitialReaderPageIndex } from '@/modules/reader/utils/Reader.utils.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
export const Reader = () => { const BaseReader = ({
const { t } = useTranslation(); setTitle,
const { setTitle, setOverride, readerNavBarWidth } = useNavBarContext(); setOverride,
const { isVisible: isOverlayVisible, setIsVisible: setIsOverlayVisible } = useReaderOverlayContext(); readerNavBarWidth,
const { manga, setManga } = useReaderStateMangaContext(); isVisible: isOverlayVisible,
const { setIsVisible: setIsOverlayVisible,
settings: { shouldSkipDupChapters }, manga,
setManga,
shouldSkipDupChapters,
backgroundColor,
setSettings, setSettings,
} = useReaderStateSettingsContext(); initialChapter,
const { initialChapter, currentChapter, chapters, setReaderStateChapters } = useReaderStateChaptersContext(); currentChapter,
const { chapters,
setReaderStateChapters,
firstPageUrl,
totalPages,
setTotalPages, setTotalPages,
setCurrentPageIndex, setCurrentPageIndex,
setPageToScrollToIndex, setPageToScrollToIndex,
pages,
setPages, setPages,
setPageUrls, setPageUrls,
setPageLoadStates, setPageLoadStates,
setTransitionPageMode, setTransitionPageMode,
} = userReaderStatePagesContext(); }: Pick<NavbarContextType, 'setTitle' | 'setOverride' | 'readerNavBarWidth'> &
Pick<TReaderOverlayContext, 'isVisible' | 'setIsVisible'> &
Pick<TReaderStateMangaContext, 'manga' | 'setManga'> &
Pick<TReaderStateSettingsContext, 'setSettings'> &
Pick<IReaderSettings, 'shouldSkipDupChapters' | 'backgroundColor'> &
Pick<ReaderStateChapters, 'initialChapter' | 'currentChapter' | 'chapters' | 'setReaderStateChapters'> &
Pick<
ReaderStatePages,
| 'totalPages'
| 'setTotalPages'
| 'setCurrentPageIndex'
| 'setPageToScrollToIndex'
| 'setPages'
| 'setPageUrls'
| 'setPageLoadStates'
| 'setTransitionPageMode'
> & {
firstPageUrl?: string;
}) => {
const { t } = useTranslation();
const { resumeMode } = useLocation<{ const { resumeMode } = useLocation<{
resumeMode: ReaderResumeMode; resumeMode: ReaderResumeMode;
}>().state ?? { resumeMode: ReaderResumeMode.START }; }>().state ?? { resumeMode: ReaderResumeMode.START };
const scrollElementRef = useRef<HTMLDivElement | null>(null); const scrollElementRef = useRef<HTMLDivElement | null>(null);
const { backgroundColor } = ReaderService.useSettings();
const { chapterIndex: paramChapterIndex, mangaId: paramMangaId } = useParams<{ const { chapterIndex: paramChapterIndex, mangaId: paramMangaId } = useParams<{
chapterIndex: string; chapterIndex: string;
mangaId: string; mangaId: string;
@@ -321,7 +354,7 @@ export const Reader = () => {
return null; return null;
} }
const isPlaceholderPageState = pages.length === 1 && pages[0].primary.url === requestManager.getBaseUrl(); const isPlaceholderPageState = totalPages === 1 && firstPageUrl === requestManager.getBaseUrl();
if (isPlaceholderPageState) { if (isPlaceholderPageState) {
return null; return null;
} }
@@ -350,3 +383,43 @@ export const Reader = () => {
</> </>
); );
}; };
export const Reader = withPropsFrom(
memo(BaseReader),
[
useNavBarContext,
useReaderOverlayContext,
useReaderStateMangaContext,
useReaderStateChaptersContext,
useReaderStateSettingsContext,
ReaderService.useSettingsWithoutDefaultFlag,
userReaderStatePagesContext,
() => ({
firstPageUrl: userReaderStatePagesContext().pages[0].primary.url,
}),
],
[
'setTitle',
'setOverride',
'readerNavBarWidth',
'isVisible',
'setIsVisible',
'manga',
'setManga',
'shouldSkipDupChapters',
'setSettings',
'initialChapter',
'currentChapter',
'chapters',
'setReaderStateChapters',
'firstPageUrl',
'totalPages',
'setTotalPages',
'setCurrentPageIndex',
'setPageToScrollToIndex',
'setPages',
'setPageUrls',
'setPageLoadStates',
'setTransitionPageMode',
],
);

View File

@@ -412,9 +412,9 @@ export class ReaderControls {
}); });
}; };
clearTimeout(this.updateCurrentPageTimeout); clearTimeout(ReaderControls.updateCurrentPageTimeout);
if (debounceChapterUpdate) { if (debounceChapterUpdate) {
this.updateCurrentPageTimeout = setTimeout(handleCurrentPageIndexChange, 1000); ReaderControls.updateCurrentPageTimeout = setTimeout(handleCurrentPageIndexChange, 1000);
return; return;
} }
@@ -480,7 +480,7 @@ export class ReaderControls {
break; break;
case TapZoneRegionType.PREVIOUS: case TapZoneRegionType.PREVIOUS:
if (isContinuousReadingModeActive) { if (isContinuousReadingModeActive) {
this.scroll( ReaderControls.scroll(
ScrollOffset.BACKWARD, ScrollOffset.BACKWARD,
scrollDirection, scrollDirection,
readingMode.value, readingMode.value,
@@ -496,7 +496,7 @@ export class ReaderControls {
break; break;
case TapZoneRegionType.NEXT: case TapZoneRegionType.NEXT:
if (isContinuousReadingModeActive) { if (isContinuousReadingModeActive) {
this.scroll( ReaderControls.scroll(
ScrollOffset.FORWARD, ScrollOffset.FORWARD,
scrollDirection, scrollDirection,
readingMode.value, readingMode.value,

View File

@@ -22,7 +22,10 @@ import {
ReadingMode, ReadingMode,
} from '@/modules/reader/types/Reader.types.ts'; } from '@/modules/reader/types/Reader.types.ts';
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx'; import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
import { updateReaderSettings } from '@/modules/reader/services/ReaderSettingsMetadata.ts'; import {
convertFromReaderSettingsWithDefaultFlag,
updateReaderSettings,
} from '@/modules/reader/services/ReaderSettingsMetadata.ts';
import { MangaIdInfo } from '@/modules/manga/Manga.types.ts'; import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { getMetadataKey } from '@/modules/metadata/services/MetadataReader.ts'; import { getMetadataKey } from '@/modules/metadata/services/MetadataReader.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
@@ -64,11 +67,11 @@ export class ReaderService {
private static chapterUpdateQueues: Map<ChapterIdInfo['id'], Queue> = new Map(); private static chapterUpdateQueues: Map<ChapterIdInfo['id'], Queue> = new Map();
private static getOrCreateChapterUpdateQueue(id: ChapterIdInfo['id']): Queue { private static getOrCreateChapterUpdateQueue(id: ChapterIdInfo['id']): Queue {
if (!this.chapterUpdateQueues.has(id)) { if (!ReaderService.chapterUpdateQueues.has(id)) {
this.chapterUpdateQueues.set(id, new Queue(1)); ReaderService.chapterUpdateQueues.set(id, new Queue(1));
} }
return this.chapterUpdateQueues.get(id)!; return ReaderService.chapterUpdateQueues.get(id)!;
} }
static useNavigateToChapter(chapter?: TChapterReader, resumeMode?: ReaderResumeMode): () => void { static useNavigateToChapter(chapter?: TChapterReader, resumeMode?: ReaderResumeMode): () => void {
@@ -95,7 +98,7 @@ export class ReaderService {
): void { ): void {
const key = `${currentChapter.id}_${nextChapter?.id}_${pageIndex}_${downloadAheadLimit}`; const key = `${currentChapter.id}_${nextChapter?.id}_${pageIndex}_${downloadAheadLimit}`;
this.downloadAheadQueue.enqueue(key, async () => { ReaderService.downloadAheadQueue.enqueue(key, async () => {
const chapterIdsForDownloadAhead = getChapterIdsForDownloadAhead( const chapterIdsForDownloadAhead = getChapterIdsForDownloadAhead(
currentChapter, currentChapter,
nextChapter, nextChapter,
@@ -119,7 +122,7 @@ export class ReaderService {
static useUpdateChapter(): (patch: UpdateChapterPatchInput) => void { static useUpdateChapter(): (patch: UpdateChapterPatchInput) => void {
const { manga } = useReaderStateMangaContext(); const { manga } = useReaderStateMangaContext();
const { initialChapter, currentChapter, mangaChapters } = useReaderStateChaptersContext(); const { initialChapter, currentChapter, mangaChapters } = useReaderStateChaptersContext();
const { shouldSkipDupChapters } = this.useSettings(); const { shouldSkipDupChapters } = ReaderService.useSettings();
const { const {
settings: { deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading }, settings: { deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading },
} = useMetadataServerSettings(); } = useMetadataServerSettings();
@@ -193,7 +196,9 @@ export class ReaderService {
.response.catch(defaultPromiseErrorHandler('ReaderService::useUpdateChapter')); .response.catch(defaultPromiseErrorHandler('ReaderService::useUpdateChapter'));
}; };
this.getOrCreateChapterUpdateQueue(currentChapter.id).enqueue(`${currentChapter.id}`, () => update()); ReaderService.getOrCreateChapterUpdateQueue(currentChapter.id).enqueue(`${currentChapter.id}`, () =>
update(),
);
}, },
[ [
manga?.id, manga?.id,
@@ -212,6 +217,10 @@ export class ReaderService {
return useReaderStateSettingsContext().settings; return useReaderStateSettingsContext().settings;
} }
static useSettingsWithoutDefaultFlag(): IReaderSettings {
return convertFromReaderSettingsWithDefaultFlag(ReaderService.useSettings());
}
static useGetThemeDirection(): Direction { static useGetThemeDirection(): Direction {
const { direction } = useTheme(); const { direction } = useTheme();
const { readingDirection } = ReaderService.useSettings(); const { readingDirection } = ReaderService.useSettings();
@@ -323,11 +332,11 @@ export class ReaderService {
manga: MangaIdInfo, manga: MangaIdInfo,
profile?: ReadingMode, profile?: ReadingMode,
): ( ): (
...args: OmitFirst<Parameters<typeof this.updateSetting<Setting>>> ...args: OmitFirst<Parameters<typeof ReaderService.updateSetting<Setting>>>
) => ReturnType<typeof this.updateSetting<Setting>> { ) => ReturnType<typeof ReaderService.updateSetting<Setting>> {
return useCallback( return useCallback(
(setting, value, commit, isGlobal) => (setting, value, commit, isGlobal) =>
this.updateSetting<Setting>(manga, setting, value, commit, isGlobal, profile), ReaderService.updateSetting<Setting>(manga, setting, value, commit, isGlobal, profile),
[manga, profile], [manga, profile],
); );
} }
@@ -336,17 +345,17 @@ export class ReaderService {
manga: MangaIdInfo, manga: MangaIdInfo,
profile?: ReadingMode, profile?: ReadingMode,
): ( ): (
...args: OmitFirst<Parameters<typeof this.deleteSetting<Setting>>> ...args: OmitFirst<Parameters<typeof ReaderService.deleteSetting<Setting>>>
) => ReturnType<typeof this.deleteSetting<Setting>> { ) => ReturnType<typeof ReaderService.deleteSetting<Setting>> {
return useCallback( return useCallback(
(setting, isGlobal) => this.deleteSetting<Setting>(manga, setting, isGlobal, profile?.toString()), (setting, isGlobal) => ReaderService.deleteSetting<Setting>(manga, setting, isGlobal, profile?.toString()),
[manga, profile], [manga, profile],
); );
} }
static useOverlayMode(): { mode: ReaderOverlayMode; isDesktop: boolean; isMobile: boolean } { static useOverlayMode(): { mode: ReaderOverlayMode; isDesktop: boolean; isMobile: boolean } {
const isTouchDevice = MediaQuery.useIsTouchDevice(); const isTouchDevice = MediaQuery.useIsTouchDevice();
const { overlayMode } = this.useSettings(); const { overlayMode } = ReaderService.useSettings();
const isAutoModeSelected = overlayMode === ReaderOverlayMode.AUTO; const isAutoModeSelected = overlayMode === ReaderOverlayMode.AUTO;
const isDesktopModeSelected = overlayMode === ReaderOverlayMode.DESKTOP; const isDesktopModeSelected = overlayMode === ReaderOverlayMode.DESKTOP;
@@ -364,7 +373,7 @@ export class ReaderService {
static useExit(): () => void { static useExit(): () => void {
const { manga } = useReaderStateMangaContext(); const { manga } = useReaderStateMangaContext();
const { exitMode } = this.useSettings(); const { exitMode } = ReaderService.useSettings();
const handleBack = useBackButton(); const handleBack = useBackButton();
const navigate = useNavigate(); const navigate = useNavigate();