Move reader "chapters state" to "reader store"

This commit is contained in:
schroda
2025-09-20 01:33:27 +02:00
parent bada26ead6
commit 5177f9efdc
16 changed files with 220 additions and 299 deletions

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { ReaderStatePages, ReaderTransitionPageMode } from '@/features/reader/Reader.types.ts'; import { ReaderStateChapters, ReaderStatePages, ReaderTransitionPageMode } from '@/features/reader/Reader.types.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
export const READER_DEFAULT_PAGES_STATE: Omit< export const READER_DEFAULT_PAGES_STATE: Omit<
@@ -39,3 +39,22 @@ export const READER_DEFAULT_PAGES_STATE: Omit<
transitionPageMode: ReaderTransitionPageMode.NONE, transitionPageMode: ReaderTransitionPageMode.NONE,
retryFailedPagesKeyPrefix: '', retryFailedPagesKeyPrefix: '',
}; };
export const READER_DEFAULT_CHAPTERS_STATE = {
chapters: {
chapters: [],
isCurrentChapterReady: false,
visibleChapters: {
leading: 0,
trailing: 0,
lastLeadingChapterSourceOrder: 99999,
lastTrailingChapterSourceOrder: -1,
isLeadingChapterPreloadMode: true,
isTrailingChapterPreloadMode: true,
scrollIntoView: false,
resumeMode: undefined,
},
},
} satisfies {
chapters: Omit<ReaderStateChapters, 'setReaderStateChapters'>;
};

View File

@@ -18,9 +18,9 @@ import {
createReaderAutoScrollStoreSlice, createReaderAutoScrollStoreSlice,
ReaderAutoScrollStoreSlice, ReaderAutoScrollStoreSlice,
} from '@/features/reader/auto-scroll/ReaderAutoScrollStore.ts'; } from '@/features/reader/auto-scroll/ReaderAutoScrollStore.ts';
import { ReaderStatePages } from '@/features/reader/Reader.types.ts'; import { ReaderStateChapters, ReaderStatePages } from '@/features/reader/Reader.types.ts';
import { ImmerStateCreator } from '@/lib/zustand/Zustand.types.ts'; import { ImmerStateCreator } from '@/lib/zustand/Zustand.types.ts';
import { READER_DEFAULT_PAGES_STATE } from '@/features/reader/ReaderStore.constants.ts'; import { READER_DEFAULT_CHAPTERS_STATE, READER_DEFAULT_PAGES_STATE } from '@/features/reader/ReaderStore.constants.ts';
interface ReaderPagesStoreSlice { interface ReaderPagesStoreSlice {
pages: ReaderStatePages & { pages: ReaderStatePages & {
@@ -28,7 +28,17 @@ interface ReaderPagesStoreSlice {
}; };
} }
interface ReaderStore extends ReaderOverlayStoreSlice, ReaderAutoScrollStoreSlice, ReaderPagesStoreSlice { interface ReaderChaptersStoreSlice {
chapters: ReaderStateChapters & {
reset: () => void;
};
}
interface ReaderStore
extends ReaderOverlayStoreSlice,
ReaderAutoScrollStoreSlice,
ReaderPagesStoreSlice,
ReaderChaptersStoreSlice {
reset: () => void; reset: () => void;
manga: TMangaReader | undefined; manga: TMangaReader | undefined;
setManga: (manga: TMangaReader | undefined) => void; setManga: (manga: TMangaReader | undefined) => void;
@@ -94,6 +104,30 @@ const createReaderPagesStoreSlice = <T extends ReaderPagesStoreSlice>(
}, },
}); });
const createReaderChaptersStoreSlice = <T extends ReaderChaptersStoreSlice>(
...[set, get]: Parameters<ImmerStateCreator<T>>
): ReaderChaptersStoreSlice => ({
chapters: {
...READER_DEFAULT_CHAPTERS_STATE.chapters,
reset: () => set(() => ({ chapters: { ...get().chapters, ...READER_DEFAULT_CHAPTERS_STATE.chapters } })),
setReaderStateChapters: (state) =>
set((draft) => {
if (typeof state === 'function') {
draft.chapters = {
...get().chapters,
...state(get().chapters),
};
return;
}
draft.chapters = {
...get().chapters,
...state,
};
}),
},
});
export const useReaderStore = create<ReaderStore>()( export const useReaderStore = create<ReaderStore>()(
immer((set, get, store) => ({ immer((set, get, store) => ({
...DEFAULT_STATE, ...DEFAULT_STATE,
@@ -104,6 +138,7 @@ export const useReaderStore = create<ReaderStore>()(
get().overlay.reset(); get().overlay.reset();
get().autoScroll.reset(); get().autoScroll.reset();
get().pages.reset(); get().pages.reset();
get().chapters.reset();
}), }),
setManga: (manga) => setManga: (manga) =>
set((draft) => { set((draft) => {
@@ -123,6 +158,7 @@ export const useReaderStore = create<ReaderStore>()(
...createReaderOverlayStoreSlice(set, get, store), ...createReaderOverlayStoreSlice(set, get, store),
...createReaderAutoScrollStoreSlice(set, get, store), ...createReaderAutoScrollStoreSlice(set, get, store),
...createReaderPagesStoreSlice(set, get, store), ...createReaderPagesStoreSlice(set, get, store),
...createReaderChaptersStoreSlice(set, get, store),
})), })),
); );
export const useReaderStoreShallow = <T>(selector: (state: ReaderStore) => T): T => export const useReaderStoreShallow = <T>(selector: (state: ReaderStore) => T): T =>

View File

@@ -1,47 +0,0 @@
/*
* 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 { createContext, ReactNode, useContext, useMemo, useState } from 'react';
import { ReaderStateChapters } from '@/features/reader/Reader.types.ts';
export const READER_STATE_CHAPTERS_DEFAULTS: Omit<ReaderStateChapters, 'setReaderStateChapters'> = {
chapters: [],
isCurrentChapterReady: false,
visibleChapters: {
leading: 0,
trailing: 0,
lastLeadingChapterSourceOrder: 99999,
lastTrailingChapterSourceOrder: -1,
isLeadingChapterPreloadMode: true,
isTrailingChapterPreloadMode: true,
scrollIntoView: false,
resumeMode: undefined,
},
};
export const ReaderStateChaptersContext = createContext<ReaderStateChapters>({
...READER_STATE_CHAPTERS_DEFAULTS,
setReaderStateChapters: () => {},
});
export const useReaderStateChaptersContext = () => useContext(ReaderStateChaptersContext);
export const ReaderStateChaptersContextProvider = ({ children }: { children: ReactNode }) => {
const [state, setState] =
useState<Omit<ReaderStateChapters, 'setReaderStateChapters'>>(READER_STATE_CHAPTERS_DEFAULTS);
const value = useMemo(
() => ({
...state,
setReaderStateChapters: setState,
}),
[state],
);
return <ReaderStateChaptersContext.Provider value={value}>{children}</ReaderStateChaptersContext.Provider>;
};

View File

@@ -7,11 +7,8 @@
*/ */
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { ReaderStateChaptersContextProvider } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderStateSettingsContextProvider } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx'; import { ReaderStateSettingsContextProvider } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx';
export const ReaderStateContextProvider = ({ children }: { children: ReactNode }) => ( export const ReaderStateContextProvider = ({ children }: { children: ReactNode }) => (
<ReaderStateChaptersContextProvider> <ReaderStateSettingsContextProvider>{children}</ReaderStateSettingsContextProvider>
<ReaderStateSettingsContextProvider>{children}</ReaderStateSettingsContextProvider>
</ReaderStateChaptersContextProvider>
); );

View File

@@ -7,19 +7,14 @@
*/ */
import { useEffect } from 'react'; import { useEffect } from 'react';
import { ReaderStateChapters, TReaderStateSettingsContext } from '@/features/reader/Reader.types.ts'; import { TReaderStateSettingsContext } from '@/features/reader/Reader.types.ts';
import { DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG } from '@/features/reader/settings/ReaderSettingsMetadata.ts'; import { DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG } from '@/features/reader/settings/ReaderSettingsMetadata.ts';
import { READER_STATE_CHAPTERS_DEFAULTS } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { getReaderStore } from '@/features/reader/ReaderStore.ts'; import { getReaderStore } from '@/features/reader/ReaderStore.ts';
export const useReaderResetStates = ( export const useReaderResetStates = (setSettings: TReaderStateSettingsContext['setSettings']) => {
setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'],
setSettings: TReaderStateSettingsContext['setSettings'],
) => {
useEffect( useEffect(
() => () => { () => () => {
getReaderStore().reset(); getReaderStore().reset();
setReaderStateChapters(READER_STATE_CHAPTERS_DEFAULTS);
setSettings(DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG); setSettings(DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG);
}, },

View File

@@ -16,11 +16,12 @@ import {
ReaderOpenChapterLocationState, ReaderOpenChapterLocationState,
ReaderStateChapters, ReaderStateChapters,
} from '@/features/reader/Reader.types.ts'; } from '@/features/reader/Reader.types.ts';
import { READER_STATE_CHAPTERS_DEFAULTS } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { filterChapters } from '@/features/chapter/utils/ChapterList.util.tsx'; import { filterChapters } from '@/features/chapter/utils/ChapterList.util.tsx';
import { ChapterListOptions } from '@/features/chapter/Chapter.types.ts'; import { ChapterListOptions } from '@/features/chapter/Chapter.types.ts';
import { getReaderChapterFromCache } from '@/features/reader/Reader.utils.ts'; import { getReaderChapterFromCache } from '@/features/reader/Reader.utils.ts';
import { DirectionOffset } from '@/base/Base.types.ts'; import { DirectionOffset } from '@/base/Base.types.ts';
import { getReaderStore } from '@/features/reader/ReaderStore.ts';
import { READER_DEFAULT_CHAPTERS_STATE } from '@/features/reader/ReaderStore.constants.ts';
export const useReaderSetChaptersState = ( export const useReaderSetChaptersState = (
chaptersResponse: ReturnType<typeof requestManager.useGetMangaChapters<GetChaptersReaderQuery>>, chaptersResponse: ReturnType<typeof requestManager.useGetMangaChapters<GetChaptersReaderQuery>>,
@@ -28,7 +29,6 @@ export const useReaderSetChaptersState = (
mangaChapters: ReaderStateChapters['mangaChapters'], mangaChapters: ReaderStateChapters['mangaChapters'],
initialChapter: ReaderStateChapters['initialChapter'], initialChapter: ReaderStateChapters['initialChapter'],
chapterForDuplicatesHandling: ReaderStateChapters['chapterForDuplicatesHandling'], chapterForDuplicatesHandling: ReaderStateChapters['chapterForDuplicatesHandling'],
setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'],
shouldSkipDupChapters: IReaderSettings['shouldSkipDupChapters'], shouldSkipDupChapters: IReaderSettings['shouldSkipDupChapters'],
shouldSkipFilteredChapters: IReaderSettings['shouldSkipFilteredChapters'], shouldSkipFilteredChapters: IReaderSettings['shouldSkipFilteredChapters'],
chapterListOptions: ChapterListOptions, chapterListOptions: ChapterListOptions,
@@ -78,7 +78,7 @@ export const useReaderSetChaptersState = (
navigate('', { replace: true, state: { ...locationState, updateInitialChapter: undefined } }); navigate('', { replace: true, state: { ...locationState, updateInitialChapter: undefined } });
} }
setReaderStateChapters((prevState) => { getReaderStore().chapters.setReaderStateChapters((prevState) => {
const hasCurrentChapterChanged = newCurrentChapter?.id !== prevState.currentChapter?.id; const hasCurrentChapterChanged = newCurrentChapter?.id !== prevState.currentChapter?.id;
return { return {
@@ -93,13 +93,13 @@ export const useReaderSetChaptersState = (
isCurrentChapterReady: hasCurrentChapterChanged ? false : prevState.isCurrentChapterReady, isCurrentChapterReady: hasCurrentChapterChanged ? false : prevState.isCurrentChapterReady,
visibleChapters: hasInitialChapterChanged visibleChapters: hasInitialChapterChanged
? { ? {
...READER_STATE_CHAPTERS_DEFAULTS.visibleChapters, ...READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters,
lastLeadingChapterSourceOrder: lastLeadingChapterSourceOrder:
newInitialChapter?.sourceOrder ?? newInitialChapter?.sourceOrder ??
READER_STATE_CHAPTERS_DEFAULTS.visibleChapters.lastLeadingChapterSourceOrder, READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters.lastLeadingChapterSourceOrder,
lastTrailingChapterSourceOrder: lastTrailingChapterSourceOrder:
newInitialChapter?.sourceOrder ?? newInitialChapter?.sourceOrder ??
READER_STATE_CHAPTERS_DEFAULTS.visibleChapters.lastTrailingChapterSourceOrder, READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters.lastTrailingChapterSourceOrder,
isLeadingChapterPreloadMode: false, isLeadingChapterPreloadMode: false,
isTrailingChapterPreloadMode: false, isTrailingChapterPreloadMode: false,
// do not set "scrollIntoView" to "true" for the initial render // do not set "scrollIntoView" to "true" for the initial render

View File

@@ -24,9 +24,6 @@ import { makeToast } from '@/base/utils/Toast.ts';
import { MobileHeaderProps } from '@/features/reader/overlay/ReaderOverlay.types.ts'; import { MobileHeaderProps } from '@/features/reader/overlay/ReaderOverlay.types.ts';
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { AppRoutes } from '@/base/AppRoute.constants.ts';
import { ReaderStateChapters } from '@/features/reader/Reader.types.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderLibraryButton } from '@/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx'; import { ReaderLibraryButton } from '@/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx';
import { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx'; import { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx';
import { FALLBACK_CHAPTER } from '@/features/chapter/Chapter.constants.ts'; import { FALLBACK_CHAPTER } from '@/features/chapter/Chapter.constants.ts';
@@ -37,12 +34,10 @@ import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts';
const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' }; const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' };
const BaseReaderOverlayHeaderMobile = forwardRef< const BaseReaderOverlayHeaderMobile = forwardRef<HTMLDivElement, MobileHeaderProps>(({ isVisible }, ref) => {
HTMLDivElement,
MobileHeaderProps & Pick<ReaderStateChapters, 'currentChapter'>
>(({ isVisible, currentChapter }, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' }); const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' });
const currentChapter = useReaderStoreShallow((state) => state.chapters.currentChapter);
const manga = useReaderStoreShallow((state) => state.manga); const manga = useReaderStoreShallow((state) => state.manga);
const scrollbar = useReaderStoreShallow((state) => state.scrollbar); const scrollbar = useReaderStoreShallow((state) => state.scrollbar);
@@ -129,8 +124,4 @@ const BaseReaderOverlayHeaderMobile = forwardRef<
); );
}); });
export const ReaderOverlayHeaderMobile = withPropsFrom( export const ReaderOverlayHeaderMobile = memo(BaseReaderOverlayHeaderMobile);
memo(BaseReaderOverlayHeaderMobile),
[useReaderStateChaptersContext],
['currentChapter'],
);

View File

@@ -24,11 +24,10 @@ import { ReaderNavBarDesktopQuickSettings } from '@/features/reader/overlay/navi
import { ReaderNavBarDesktopActions } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx'; import { ReaderNavBarDesktopActions } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx';
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx'; import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx'; import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts';
import { IReaderSettings, ReaderStateChapters } from '@/features/reader/Reader.types.ts'; import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { ReaderExitButton } from '@/features/reader/overlay/navigation/components/ReaderExitButton.tsx'; import { ReaderExitButton } from '@/features/reader/overlay/navigation/components/ReaderExitButton.tsx';
import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts';
@@ -55,17 +54,18 @@ const BaseReaderNavBarDesktop = ({
isVisible, isVisible,
openSettings, openSettings,
setReaderNavBarWidth, setReaderNavBarWidth,
chapters,
currentChapter,
previousChapter,
nextChapter,
isStaticNav, isStaticNav,
}: ReaderNavBarDesktopProps & }: ReaderNavBarDesktopProps &
Pick<NavbarContextType, 'setReaderNavBarWidth'> & Pick<NavbarContextType, 'setReaderNavBarWidth'> &
Pick<ReaderStateChapters, 'currentChapter' | 'previousChapter' | 'nextChapter' | 'chapters'> &
Pick<IReaderSettings, 'isStaticNav'>) => { Pick<IReaderSettings, 'isStaticNav'>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const manga = useReaderStoreShallow((state) => state.manga); const manga = useReaderStoreShallow((state) => state.manga);
const { chapters, currentChapter, previousChapter, nextChapter } = useReaderStoreShallow((state) => ({
chapters: state.chapters.chapters,
currentChapter: state.chapters.currentChapter,
previousChapter: state.chapters.previousChapter,
nextChapter: state.chapters.nextChapter,
}));
const [navBarElement, setNavBarElement] = useState<HTMLDivElement | null>(); const [navBarElement, setNavBarElement] = useState<HTMLDivElement | null>();
useResizeObserver( useResizeObserver(
@@ -146,6 +146,6 @@ const BaseReaderNavBarDesktop = ({
export const ReaderNavBarDesktop = withPropsFrom( export const ReaderNavBarDesktop = withPropsFrom(
memo(BaseReaderNavBarDesktop), memo(BaseReaderNavBarDesktop),
[useNavBarContext, useReaderStateChaptersContext, ReaderService.useSettingsWithoutDefaultFlag], [useNavBarContext, ReaderService.useSettingsWithoutDefaultFlag],
['setReaderNavBarWidth', 'chapters', 'currentChapter', 'previousChapter', 'nextChapter', 'isStaticNav'], ['setReaderNavBarWidth', 'isStaticNav'],
); );

View File

@@ -15,10 +15,7 @@ import { memo, useMemo, useRef } from 'react';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts'; import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { ReaderStateChapters } from '@/features/reader/Reader.types.ts';
import { DownloadStateIndicator } from '@/base/components/downloads/DownloadStateIndicator.tsx'; import { DownloadStateIndicator } from '@/base/components/downloads/DownloadStateIndicator.tsx';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderLibraryButton } from '@/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx'; import { ReaderLibraryButton } from '@/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx';
import { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx'; import { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx';
import { CHAPTER_ACTION_TO_TRANSLATION, FALLBACK_CHAPTER } from '@/features/chapter/Chapter.constants.ts'; import { CHAPTER_ACTION_TO_TRANSLATION, FALLBACK_CHAPTER } from '@/features/chapter/Chapter.constants.ts';
@@ -26,8 +23,13 @@ import { IconBrowser } from '@/assets/icons/IconBrowser.tsx';
import { IconWebView } from '@/assets/icons/IconWebView.tsx'; import { IconWebView } from '@/assets/icons/IconWebView.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts';
import { ChapterDownloadInfo, ChapterIdInfo } from '@/features/chapter/Chapter.types.ts';
const DownloadButton = ({ currentChapter }: Required<Pick<ReaderStateChapters, 'currentChapter'>>) => { const DownloadButton = ({
currentChapter,
}: {
currentChapter: NullAndUndefined<ChapterIdInfo & ChapterDownloadInfo>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const downloadStatus = Chapters.useDownloadStatusFromCache(currentChapter?.id ?? -1); const downloadStatus = Chapters.useDownloadStatusFromCache(currentChapter?.id ?? -1);
@@ -59,76 +61,64 @@ const DownloadButton = ({ currentChapter }: Required<Pick<ReaderStateChapters, '
); );
}; };
const BaseReaderNavBarDesktopActions = memo( export const ReaderNavBarDesktopActions = memo(() => {
({ currentChapter }: Required<Pick<ReaderStateChapters, 'currentChapter'>>) => { const currentChapter = useReaderStoreShallow((state) => state.chapters.currentChapter);
const { id, isBookmarked, realUrl } = currentChapter ?? FALLBACK_CHAPTER;
const { t } = useTranslation(); const { id, isBookmarked, realUrl } = currentChapter ?? FALLBACK_CHAPTER;
const { pageLoadStates, setPageLoadStates, setRetryFailedPagesKeyPrefix } = useReaderStoreShallow((state) => ({
pageLoadStates: state.pages.pageLoadStates,
setPageLoadStates: state.pages.setPageLoadStates,
setRetryFailedPagesKeyPrefix: state.pages.setRetryFailedPagesKeyPrefix,
}));
const pageRetryKeyPrefix = useRef<number>(0); const { t } = useTranslation();
const { pageLoadStates, setPageLoadStates, setRetryFailedPagesKeyPrefix } = useReaderStoreShallow((state) => ({
pageLoadStates: state.pages.pageLoadStates,
setPageLoadStates: state.pages.setPageLoadStates,
setRetryFailedPagesKeyPrefix: state.pages.setRetryFailedPagesKeyPrefix,
}));
const haveSomePagesFailedToLoad = useMemo( const pageRetryKeyPrefix = useRef<number>(0);
() => pageLoadStates.some((pageLoadState) => pageLoadState.error),
[pageLoadStates],
);
return ( const haveSomePagesFailedToLoad = useMemo(
<Stack sx={{ flexDirection: 'row', justifyContent: 'center', gap: 1 }}> () => pageLoadStates.some((pageLoadState) => pageLoadState.error),
<ReaderLibraryButton /> [pageLoadStates],
<ReaderBookmarkButton id={id} isBookmarked={isBookmarked} /> );
<CustomTooltip title={t('reader.button.retry_load_pages')} disabled={!haveSomePagesFailedToLoad}>
<IconButton
onClick={() => {
setPageLoadStates((statePageLoadStates) =>
statePageLoadStates.map((pageLoadState) => ({
url: pageLoadState.url,
loaded: pageLoadState.loaded,
})),
);
setRetryFailedPagesKeyPrefix(`${pageRetryKeyPrefix.current}`);
pageRetryKeyPrefix.current = (pageRetryKeyPrefix.current + 1) % 1000;
}}
disabled={!haveSomePagesFailedToLoad}
color="inherit"
>
<ReplayIcon />
</IconButton>
</CustomTooltip>
<DownloadButton currentChapter={currentChapter} />
<CustomTooltip title={t('global.button.open_browser')} disabled={!realUrl}>
<IconButton
disabled={!realUrl}
href={realUrl ?? ''}
rel="noreferrer"
target="_blank"
color="inherit"
>
<IconBrowser />
</IconButton>
</CustomTooltip>
<CustomTooltip title={t('global.button.open_webview')} disabled={!realUrl}>
<IconButton
disabled={!realUrl}
href={realUrl ? requestManager.getWebviewUrl(realUrl) : ''}
rel="noreferrer"
target="_blank"
color="inherit"
>
<IconWebView />
</IconButton>
</CustomTooltip>
</Stack>
);
},
);
export const ReaderNavBarDesktopActions = withPropsFrom( return (
BaseReaderNavBarDesktopActions, <Stack sx={{ flexDirection: 'row', justifyContent: 'center', gap: 1 }}>
[useReaderStateChaptersContext], <ReaderLibraryButton />
['currentChapter'], <ReaderBookmarkButton id={id} isBookmarked={isBookmarked} />
); <CustomTooltip title={t('reader.button.retry_load_pages')} disabled={!haveSomePagesFailedToLoad}>
<IconButton
onClick={() => {
setPageLoadStates((statePageLoadStates) =>
statePageLoadStates.map((pageLoadState) => ({
url: pageLoadState.url,
loaded: pageLoadState.loaded,
})),
);
setRetryFailedPagesKeyPrefix(`${pageRetryKeyPrefix.current}`);
pageRetryKeyPrefix.current = (pageRetryKeyPrefix.current + 1) % 1000;
}}
disabled={!haveSomePagesFailedToLoad}
color="inherit"
>
<ReplayIcon />
</IconButton>
</CustomTooltip>
<DownloadButton currentChapter={currentChapter} />
<CustomTooltip title={t('global.button.open_browser')} disabled={!realUrl}>
<IconButton disabled={!realUrl} href={realUrl ?? ''} rel="noreferrer" target="_blank" color="inherit">
<IconBrowser />
</IconButton>
</CustomTooltip>
<CustomTooltip title={t('global.button.open_webview')} disabled={!realUrl}>
<IconButton
disabled={!realUrl}
href={realUrl ? requestManager.getWebviewUrl(realUrl) : ''}
rel="noreferrer"
target="_blank"
color="inherit"
>
<IconWebView />
</IconButton>
</CustomTooltip>
</Stack>
);
});

View File

@@ -23,20 +23,19 @@ import { ReaderBottomBarMobileProps } from '@/features/reader/overlay/ReaderOver
import { MobileReaderProgressBar } from '@/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx'; import { MobileReaderProgressBar } from '@/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx';
import { ReaderChapterList } from '@/features/reader/overlay/navigation/components/ReaderChapterList.tsx'; import { ReaderChapterList } from '@/features/reader/overlay/navigation/components/ReaderChapterList.tsx';
import { ReaderBottomBarMobileQuickSettings } from '@/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx'; import { ReaderBottomBarMobileQuickSettings } from '@/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderStateChapters } from '@/features/reader/Reader.types.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx'; import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx';
import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts';
const BaseReaderBottomBarMobile = ({ const BaseReaderBottomBarMobile = ({
openSettings, openSettings,
isVisible, isVisible,
currentChapter,
chapters,
topOffset = 0, topOffset = 0,
}: ReaderBottomBarMobileProps & Pick<ReaderStateChapters, 'currentChapter' | 'chapters'> & { topOffset?: number }) => { }: ReaderBottomBarMobileProps & { topOffset?: number }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentChapter, chapters } = useReaderStoreShallow((state) => ({
currentChapter: state.chapters.currentChapter,
chapters: state.chapters.chapters,
}));
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' });
@@ -130,8 +129,4 @@ const BaseReaderBottomBarMobile = ({
); );
}; };
export const ReaderBottomBarMobile = withPropsFrom( export const ReaderBottomBarMobile = memo(BaseReaderBottomBarMobile);
memo(BaseReaderBottomBarMobile),
[useReaderStateChaptersContext],
['currentChapter', 'chapters'],
);

View File

@@ -15,14 +15,13 @@ import Box from '@mui/material/Box';
import { ComponentProps, memo, useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { ComponentProps, memo, useCallback, useLayoutEffect, useMemo, useState } from 'react';
import Slide, { SlideProps } from '@mui/material/Slide'; import Slide, { SlideProps } from '@mui/material/Slide';
import { ReaderProgressBar } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.tsx'; import { ReaderProgressBar } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { import {
getPage, getPage,
getProgressBarPositionInfo, getProgressBarPositionInfo,
} from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts'; import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts';
import { IReaderSettings, ProgressBarPosition, ReaderStateChapters } from '@/features/reader/Reader.types.ts'; import { IReaderSettings, ProgressBarPosition } from '@/features/reader/Reader.types.ts';
import { ReaderProgressBarDirectionWrapper } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarDirectionWrapper.tsx'; import { ReaderProgressBarDirectionWrapper } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarDirectionWrapper.tsx';
import { useReaderProgressBarContext } from '@/features/reader/overlay/progress-bar/ReaderProgressBarContext.tsx'; import { useReaderProgressBarContext } from '@/features/reader/overlay/progress-bar/ReaderProgressBarContext.tsx';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
@@ -43,8 +42,6 @@ const PROGRESS_BAR_POSITION_TO_SLIDE_DIRECTION: Record<ProgressBarPosition, Slid
}; };
const BaseMobileReaderProgressBar = ({ const BaseMobileReaderProgressBar = ({
previousChapter,
nextChapter,
setIsMaximized, setIsMaximized,
isDragging, isDragging,
direction: readerDirection, direction: readerDirection,
@@ -52,8 +49,7 @@ const BaseMobileReaderProgressBar = ({
progressBarPositionAutoVertical, progressBarPositionAutoVertical,
topOffset = 0, topOffset = 0,
bottomOffset = 0, bottomOffset = 0,
}: Pick<ReaderStateChapters, 'previousChapter' | 'nextChapter'> & }: Pick<TReaderProgressBarContext, 'setIsMaximized' | 'isDragging'> &
Pick<TReaderProgressBarContext, 'setIsMaximized' | 'isDragging'> &
Pick<IReaderSettings, 'progressBarPosition' | 'progressBarPositionAutoVertical'> & { Pick<IReaderSettings, 'progressBarPosition' | 'progressBarPositionAutoVertical'> & {
direction: ReturnType<typeof ReaderService.useGetThemeDirection>; direction: ReturnType<typeof ReaderService.useGetThemeDirection>;
topOffset?: number; topOffset?: number;
@@ -66,6 +62,10 @@ const BaseMobileReaderProgressBar = ({
currentPageIndex: state.pages.currentPageIndex, currentPageIndex: state.pages.currentPageIndex,
pages: state.pages.pages, pages: state.pages.pages,
})); }));
const { previousChapter, nextChapter } = useReaderStore((state) => ({
previousChapter: state.chapters.previousChapter,
nextChapter: state.chapters.nextChapter,
}));
const [, setRefreshProgressBarPosition] = useState({}); const [, setRefreshProgressBarPosition] = useState({});
useResizeObserver( useResizeObserver(
@@ -354,18 +354,9 @@ const BaseMobileReaderProgressBar = ({
export const MobileReaderProgressBar = withPropsFrom( export const MobileReaderProgressBar = withPropsFrom(
memo(BaseMobileReaderProgressBar), memo(BaseMobileReaderProgressBar),
[ [
useReaderStateChaptersContext,
useReaderProgressBarContext, useReaderProgressBarContext,
() => ({ direction: ReaderService.useGetThemeDirection() }), () => ({ direction: ReaderService.useGetThemeDirection() }),
ReaderService.useSettingsWithoutDefaultFlag, ReaderService.useSettingsWithoutDefaultFlag,
], ],
[ ['setIsMaximized', 'isDragging', 'direction', 'progressBarPosition', 'progressBarPositionAutoVertical'],
'previousChapter',
'nextChapter',
'setIsMaximized',
'isDragging',
'direction',
'progressBarPosition',
'progressBarPositionAutoVertical',
],
); );

View File

@@ -30,13 +30,11 @@ import { ReaderHotkeys } from '@/features/reader/hotkeys/ReaderHotkeys.tsx';
import { import {
IReaderSettings, IReaderSettings,
IReaderSettingsWithDefaultFlag, IReaderSettingsWithDefaultFlag,
ReaderStateChapters,
TReaderStateSettingsContext, TReaderStateSettingsContext,
} from '@/features/reader/Reader.types.ts'; } from '@/features/reader/Reader.types.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx';
import { useReaderResetStates } from '@/features/reader/hooks/useReaderResetStates.ts'; import { useReaderResetStates } from '@/features/reader/hooks/useReaderResetStates.ts';
@@ -61,11 +59,6 @@ const BaseReader = ({
shouldShowReadingModePreview, shouldShowReadingModePreview,
shouldShowTapZoneLayoutPreview, shouldShowTapZoneLayoutPreview,
setSettings, setSettings,
mangaChapters,
initialChapter,
chapterForDuplicatesHandling,
currentChapter,
setReaderStateChapters,
setShowPreview, setShowPreview,
}: Pick<NavbarContextType, 'setOverride' | 'readerNavBarWidth'> & }: Pick<NavbarContextType, 'setOverride' | 'readerNavBarWidth'> &
Pick<TReaderStateSettingsContext, 'setSettings'> & Pick<TReaderStateSettingsContext, 'setSettings'> &
@@ -78,18 +71,18 @@ const BaseReader = ({
| 'shouldShowTapZoneLayoutPreview' | 'shouldShowTapZoneLayoutPreview'
> & > &
Pick<IReaderSettingsWithDefaultFlag, 'readingMode' | 'tapZoneLayout' | 'tapZoneInvertMode'> & Pick<IReaderSettingsWithDefaultFlag, 'readingMode' | 'tapZoneLayout' | 'tapZoneInvertMode'> &
Pick<
ReaderStateChapters,
| 'mangaChapters'
| 'initialChapter'
| 'chapterForDuplicatesHandling'
| 'currentChapter'
| 'setReaderStateChapters'
> &
Pick<TReaderTapZoneContext, 'setShowPreview'>) => { Pick<TReaderTapZoneContext, 'setShowPreview'>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const manga = useReaderStoreShallow((state) => state.manga); const manga = useReaderStoreShallow((state) => state.manga);
const overlay = useReaderStoreShallow((state) => state.overlay); const overlay = useReaderStoreShallow((state) => state.overlay);
const { mangaChapters, initialChapter, chapterForDuplicatesHandling, currentChapter } = useReaderStoreShallow(
(state) => ({
mangaChapters: state.chapters.mangaChapters,
initialChapter: state.chapters.initialChapter,
chapterForDuplicatesHandling: state.chapters.chapterForDuplicatesHandling,
currentChapter: state.chapters.currentChapter,
}),
);
const scrollElementRef = useRef<HTMLDivElement | null>(null); const scrollElementRef = useRef<HTMLDivElement | null>(null);
@@ -130,7 +123,7 @@ const BaseReader = ({
useReaderStore.getState().setManga(mangaResponse.data?.manga); useReaderStore.getState().setManga(mangaResponse.data?.manga);
}, [mangaResponse.data?.manga]); }, [mangaResponse.data?.manga]);
useReaderResetStates(setReaderStateChapters, setSettings); useReaderResetStates(setSettings);
useReaderSetSettingsState( useReaderSetSettingsState(
mangaResponse, mangaResponse,
defaultSettingsResponse, defaultSettingsResponse,
@@ -156,7 +149,6 @@ const BaseReader = ({
mangaChapters, mangaChapters,
initialChapter, initialChapter,
chapterForDuplicatesHandling, chapterForDuplicatesHandling,
setReaderStateChapters,
shouldSkipDupChapters, shouldSkipDupChapters,
shouldSkipFilteredChapters, shouldSkipFilteredChapters,
chapterListOptions, chapterListOptions,
@@ -263,7 +255,6 @@ export const Reader = withPropsFrom(
memo(BaseReader), memo(BaseReader),
[ [
useNavBarContext, useNavBarContext,
useReaderStateChaptersContext,
useReaderStateSettingsContext, useReaderStateSettingsContext,
() => { () => {
const { const {
@@ -299,11 +290,6 @@ export const Reader = withPropsFrom(
'shouldShowReadingModePreview', 'shouldShowReadingModePreview',
'shouldShowTapZoneLayoutPreview', 'shouldShowTapZoneLayoutPreview',
'setSettings', 'setSettings',
'mangaChapters',
'initialChapter',
'chapterForDuplicatesHandling',
'currentChapter',
'setReaderStateChapters',
'setShowPreview', 'setShowPreview',
], ],
); );

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { MutableRefObject, RefObject, useCallback, useEffect, useMemo } from 'react'; import { MutableRefObject, RefObject, useCallback, useEffect } from 'react';
import { Direction, useTheme } from '@mui/material/styles'; import { Direction, useTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { TFunction } from 'i18next'; import { TFunction } from 'i18next';
@@ -19,7 +19,6 @@ import {
} from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts'; import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts';
import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { import {
PageInViewportType, PageInViewportType,
ProgressBarPosition, ProgressBarPosition,
@@ -173,19 +172,20 @@ export class ReaderControls {
shouldInformAboutScanlatorChange, shouldInformAboutScanlatorChange,
shouldUseInfiniteScroll, shouldUseInfiniteScroll,
} = ReaderService.useSettings(); } = ReaderService.useSettings();
const {
currentChapter,
previousChapter,
nextChapter,
chapters,
visibleChapters: { lastLeadingChapterSourceOrder, lastTrailingChapterSourceOrder },
setReaderStateChapters,
} = useReaderStateChaptersContext();
const openChapter = ReaderService.useNavigateToChapter(); const openChapter = ReaderService.useNavigateToChapter();
return useCallback( return useCallback(
(offset, doTransitionCheck = true, scrollIntoView = true) => { (offset, doTransitionCheck = true, scrollIntoView = true) => {
const {
currentChapter,
previousChapter,
nextChapter,
chapters,
visibleChapters: { lastLeadingChapterSourceOrder, lastTrailingChapterSourceOrder },
setReaderStateChapters,
} = getReaderStore().chapters;
if (!currentChapter) { if (!currentChapter) {
return; return;
} }
@@ -266,13 +266,10 @@ export class ReaderControls {
}, },
[ [
t, t,
currentChapter?.id,
openChapter, openChapter,
readingMode.value, readingMode.value,
shouldInformAboutMissingChapter, shouldInformAboutMissingChapter,
shouldInformAboutScanlatorChange, shouldInformAboutScanlatorChange,
lastLeadingChapterSourceOrder,
lastTrailingChapterSourceOrder,
shouldUseInfiniteScroll, shouldUseInfiniteScroll,
], ],
); );
@@ -343,7 +340,6 @@ export class ReaderControls {
forceDirection?: Direction, forceDirection?: Direction,
hideOverlay?: boolean, hideOverlay?: boolean,
) => void { ) => void {
const { previousChapter, nextChapter } = useReaderStateChaptersContext();
const { setShowPreview } = useReaderTapZoneContext(); const { setShowPreview } = useReaderTapZoneContext();
const { readingDirection, readingMode, shouldShowTransitionPage } = ReaderService.useSettings(); const { readingDirection, readingMode, shouldShowTransitionPage } = ReaderService.useSettings();
const openChapter = ReaderControls.useOpenChapter(); const openChapter = ReaderControls.useOpenChapter();
@@ -398,7 +394,7 @@ export class ReaderControls {
isFirstPage && isFirstPage &&
(!shouldShowTransitionPage || isPreviousTransitionPageVisible) && (!shouldShowTransitionPage || isPreviousTransitionPageVisible) &&
convertedPage === 'previous' && convertedPage === 'previous' &&
!!previousChapter; !!getReaderStore().chapters.previousChapter;
if (shouldOpenPreviousChapter) { if (shouldOpenPreviousChapter) {
openChapter('previous'); openChapter('previous');
return; return;
@@ -408,7 +404,7 @@ export class ReaderControls {
isLastPage && isLastPage &&
(!shouldShowTransitionPage || isNextTransitionPageVisible) && (!shouldShowTransitionPage || isNextTransitionPageVisible) &&
convertedPage === 'next' && convertedPage === 'next' &&
!!nextChapter; !!getReaderStore().chapters.nextChapter;
if (shouldOpenNextChapter) { if (shouldOpenNextChapter) {
openChapter('next'); openChapter('next');
return; return;
@@ -442,7 +438,7 @@ export class ReaderControls {
setPageToScrollToIndex(isPreviousMode ? previousPageIndex : nextPageIndex); setPageToScrollToIndex(isPreviousMode ? previousPageIndex : nextPageIndex);
}, },
[direction, openChapter, !!previousChapter, !!nextChapter, shouldShowTransitionPage, readingMode.value], [direction, openChapter, shouldShowTransitionPage, readingMode.value],
); );
} }
@@ -451,26 +447,24 @@ export class ReaderControls {
debounceChapterUpdate?: boolean, debounceChapterUpdate?: boolean,
endReached?: boolean, endReached?: boolean,
) => void { ) => void {
const { currentChapter, chapters, previousChapter, nextChapter, visibleChapters, setReaderStateChapters } =
useReaderStateChaptersContext();
const updateChapter = ReaderService.useUpdateChapter(); const updateChapter = ReaderService.useUpdateChapter();
const { const {
settings: { downloadAheadLimit }, settings: { downloadAheadLimit },
} = useMetadataServerSettings(); } = useMetadataServerSettings();
const nextChapters = useMemo(() => {
if (!currentChapter) {
return [];
}
return Chapters.getNextChapters(currentChapter, chapters, {
offset: DirectionOffset.NEXT,
});
}, [currentChapter?.id, chapters]);
return useCallback( return useCallback(
(pageIndex, debounceChapterUpdate = true, endReached = false) => { (pageIndex, debounceChapterUpdate = true, endReached = false) => {
const { currentPageIndex, setCurrentPageIndex } = getReaderStore().pages; const {
pages: { currentPageIndex, setCurrentPageIndex },
chapters: {
currentChapter,
chapters,
previousChapter,
nextChapter,
visibleChapters,
setReaderStateChapters,
},
} = getReaderStore();
if (pageIndex === currentPageIndex && !endReached) { if (pageIndex === currentPageIndex && !endReached) {
return; return;
@@ -482,6 +476,10 @@ export class ReaderControls {
return; return;
} }
const nextChapters = Chapters.getNextChapters(currentChapter, chapters, {
offset: DirectionOffset.NEXT,
});
const direction = currentPageIndex > pageIndex ? DirectionOffset.PREVIOUS : DirectionOffset.NEXT; const direction = currentPageIndex > pageIndex ? DirectionOffset.PREVIOUS : DirectionOffset.NEXT;
ReaderService.downloadAhead(currentChapter, nextChapter, nextChapters, pageIndex, downloadAheadLimit); ReaderService.downloadAhead(currentChapter, nextChapter, nextChapters, pageIndex, downloadAheadLimit);
@@ -518,14 +516,7 @@ export class ReaderControls {
handleCurrentPageIndexChange(); handleCurrentPageIndexChange();
}, },
[ [downloadAheadLimit],
currentChapter?.id,
previousChapter?.id,
nextChapter?.id,
nextChapters,
downloadAheadLimit,
visibleChapters,
],
); );
} }

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { useCallback, useMemo } from 'react'; import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Direction, useTheme } from '@mui/material/styles'; import { Direction, useTheme } from '@mui/material/styles';
import { t as translate } from 'i18next'; import { t as translate } from 'i18next';
@@ -36,7 +36,6 @@ import { useBackButton } from '@/base/hooks/useBackButton.ts';
import { GLOBAL_READER_SETTING_KEYS } from '@/features/reader/settings/ReaderSettings.constants.tsx'; import { GLOBAL_READER_SETTING_KEYS } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { useReaderStateSettingsContext } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx'; import { useReaderStateSettingsContext } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx';
import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts';
import { import {
getChapterIdsForDownloadAhead, getChapterIdsForDownloadAhead,
@@ -155,30 +154,26 @@ export class ReaderService {
} }
static useUpdateChapter(): (patch: UpdateChapterPatchInput) => void { static useUpdateChapter(): (patch: UpdateChapterPatchInput) => void {
const { currentChapter, mangaChapters, chapters } = useReaderStateChaptersContext();
const { shouldSkipDupChapters } = ReaderService.useSettings(); const { shouldSkipDupChapters } = ReaderService.useSettings();
const { const {
settings: { deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading }, settings: { deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading },
} = useMetadataServerSettings(); } = useMetadataServerSettings();
const previousChapters = useMemo(() => {
if (!currentChapter) {
return [];
}
return Chapters.getNextChapters(currentChapter, chapters, {
offset: DirectionOffset.PREVIOUS,
});
}, [currentChapter?.id, chapters]);
return useCallback( return useCallback(
(patch) => { (patch) => {
const { manga } = getReaderStore(); const {
manga,
chapters: { currentChapter, mangaChapters, chapters },
} = getReaderStore();
if (!manga || !currentChapter || !mangaChapters) { if (!manga || !currentChapter || !mangaChapters) {
return; return;
} }
const previousChapters = Chapters.getNextChapters(currentChapter, chapters, {
offset: DirectionOffset.PREVIOUS,
});
const update = async () => { const update = async () => {
const chapterIdsToUpdate = Chapters.getIds( const chapterIdsToUpdate = Chapters.getIds(
shouldSkipDupChapters shouldSkipDupChapters
@@ -234,15 +229,7 @@ export class ReaderService {
ReaderService.getOrCreateChapterUpdateQueue(currentChapter.id).enqueue(`${currentChapter.id}`, update); ReaderService.getOrCreateChapterUpdateQueue(currentChapter.id).enqueue(`${currentChapter.id}`, update);
}, },
[ [shouldSkipDupChapters, deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading],
currentChapter?.id,
mangaChapters,
previousChapters,
shouldSkipDupChapters,
deleteChaptersWhileReading,
deleteChaptersWithBookmark,
updateProgressAfterReading,
],
); );
} }

View File

@@ -27,7 +27,6 @@ import {
PageInViewportType, PageInViewportType,
ReaderOpenChapterLocationState, ReaderOpenChapterLocationState,
ReaderResumeMode, ReaderResumeMode,
ReaderStateChapters,
ReadingDirection, ReadingDirection,
ReadingMode, ReadingMode,
} from '@/features/reader/Reader.types.ts'; } from '@/features/reader/Reader.types.ts';
@@ -49,7 +48,6 @@ import { useReaderHorizontalModeInvertXYScrolling } from '@/features/reader/view
import { useReaderHideCursorOnInactivity } from '@/features/reader/viewer/hooks/useReaderHideCursorOnInactivity.ts'; import { useReaderHideCursorOnInactivity } from '@/features/reader/viewer/hooks/useReaderHideCursorOnInactivity.ts';
import { useReaderScrollToStartOnPageChange } from '@/features/reader/viewer/hooks/useReaderScrollToStartOnPageChange.ts'; import { useReaderScrollToStartOnPageChange } from '@/features/reader/viewer/hooks/useReaderScrollToStartOnPageChange.ts';
import { useReaderHandlePageSelection } from '@/features/reader/viewer/hooks/useReaderHandlePageSelection.ts'; import { useReaderHandlePageSelection } from '@/features/reader/viewer/hooks/useReaderHandlePageSelection.ts';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { ReaderChapterViewer } from '@/features/reader/viewer/ReaderChapterViewer.tsx'; import { ReaderChapterViewer } from '@/features/reader/viewer/ReaderChapterViewer.tsx';
import { import {
getPreviousNextChapterVisibility, getPreviousNextChapterVisibility,
@@ -90,12 +88,6 @@ const BaseReaderViewer = forwardRef(
updateCurrentPageIndex, updateCurrentPageIndex,
showPreview, showPreview,
setShowPreview, setShowPreview,
initialChapter,
currentChapter,
chapters,
visibleChapters,
setReaderStateChapters,
isCurrentChapterReady,
}: Pick< }: Pick<
IReaderSettings, IReaderSettings,
| 'readingMode' | 'readingMode'
@@ -111,15 +103,6 @@ const BaseReaderViewer = forwardRef(
| 'isStaticNav' | 'isStaticNav'
> & > &
Pick<NavbarContextType, 'readerNavBarWidth'> & Pick<NavbarContextType, 'readerNavBarWidth'> &
Pick<
ReaderStateChapters,
| 'initialChapter'
| 'currentChapter'
| 'chapters'
| 'visibleChapters'
| 'setReaderStateChapters'
| 'isCurrentChapterReady'
> &
TReaderTapZoneContext & { TReaderTapZoneContext & {
updateCurrentPageIndex: ReturnType<typeof ReaderControls.useUpdateCurrentPageIndex>; updateCurrentPageIndex: ReturnType<typeof ReaderControls.useUpdateCurrentPageIndex>;
}, },
@@ -155,6 +138,21 @@ const BaseReaderViewer = forwardRef(
retryFailedPagesKeyPrefix: state.pages.retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix: state.pages.retryFailedPagesKeyPrefix,
setTransitionPageMode: state.pages.setTransitionPageMode, setTransitionPageMode: state.pages.setTransitionPageMode,
})); }));
const {
initialChapter,
currentChapter,
chapters,
visibleChapters,
setReaderStateChapters,
isCurrentChapterReady,
} = useReaderStoreShallow((state) => ({
initialChapter: state.chapters.initialChapter,
currentChapter: state.chapters.currentChapter,
chapters: state.chapters.chapters,
visibleChapters: state.chapters.visibleChapters,
setReaderStateChapters: state.chapters.setReaderStateChapters,
isCurrentChapterReady: state.chapters.isCurrentChapterReady,
}));
const { resumeMode = ReaderResumeMode.START } = useLocation<ReaderOpenChapterLocationState>().state ?? { const { resumeMode = ReaderResumeMode.START } = useLocation<ReaderOpenChapterLocationState>().state ?? {
resumeMode: ReaderResumeMode.START, resumeMode: ReaderResumeMode.START,
}; };
@@ -446,7 +444,6 @@ export const ReaderViewer = withPropsFrom(
ReaderService.useSettingsWithoutDefaultFlag, ReaderService.useSettingsWithoutDefaultFlag,
() => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }), () => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }),
useReaderTapZoneContext, useReaderTapZoneContext,
useReaderStateChaptersContext,
useNavBarContext, useNavBarContext,
], ],
[ [
@@ -465,11 +462,5 @@ export const ReaderViewer = withPropsFrom(
'updateCurrentPageIndex', 'updateCurrentPageIndex',
'showPreview', 'showPreview',
'setShowPreview', 'setShowPreview',
'initialChapter',
'currentChapter',
'chapters',
'visibleChapters',
'setReaderStateChapters',
'isCurrentChapterReady',
], ],
); );

View File

@@ -13,7 +13,6 @@ import Button from '@mui/material/Button';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { ComponentProps, memo, useMemo } from 'react'; import { ComponentProps, memo, useMemo } from 'react';
import { alpha, useTheme } from '@mui/material/styles'; import { alpha, useTheme } from '@mui/material/styles';
import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { IReaderSettings, ReaderTransitionPageMode, ReadingMode } from '@/features/reader/Reader.types.ts'; import { IReaderSettings, ReaderTransitionPageMode, ReadingMode } from '@/features/reader/Reader.types.ts';
import { isTransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { isTransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
import { useBackButton } from '@/base/hooks/useBackButton.ts'; import { useBackButton } from '@/base/hooks/useBackButton.ts';
@@ -224,7 +223,7 @@ export const ReaderTransitionPage = withPropsFrom(
memo(BaseReaderTransitionPage) as typeof BaseReaderTransitionPage, memo(BaseReaderTransitionPage) as typeof BaseReaderTransitionPage,
[ [
({ chapterId }: Pick<ComponentProps<typeof BaseReaderTransitionPage>, 'chapterId'>) => { ({ chapterId }: Pick<ComponentProps<typeof BaseReaderTransitionPage>, 'chapterId'>) => {
const { chapters } = useReaderStateChaptersContext(); const chapters = useReaderStoreShallow((state) => state.chapters.chapters);
const currentChapterIndex = useMemo( const currentChapterIndex = useMemo(
() => chapters.findIndex((chapter) => chapter.id === chapterId), () => chapters.findIndex((chapter) => chapter.id === chapterId),
@@ -248,7 +247,7 @@ export const ReaderTransitionPage = withPropsFrom(
ReaderService.useSettingsWithoutDefaultFlag, ReaderService.useSettingsWithoutDefaultFlag,
({ chapterId, type }: Pick<ComponentProps<typeof BaseReaderTransitionPage>, 'chapterId' | 'type'>) => { ({ chapterId, type }: Pick<ComponentProps<typeof BaseReaderTransitionPage>, 'chapterId' | 'type'>) => {
const handleBack = useBackButton(); const handleBack = useBackButton();
const { chapters } = useReaderStateChaptersContext(); const chapters = useReaderStoreShallow((state) => state.chapters.chapters);
const currentChapterIndex = useMemo( const currentChapterIndex = useMemo(
() => chapters.findIndex((chapter) => chapter.id === chapterId), () => chapters.findIndex((chapter) => chapter.id === chapterId),