diff --git a/src/features/reader/ReaderStore.constants.ts b/src/features/reader/ReaderStore.constants.ts index dbb68b55..bdd699bd 100644 --- a/src/features/reader/ReaderStore.constants.ts +++ b/src/features/reader/ReaderStore.constants.ts @@ -6,7 +6,7 @@ * 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'; export const READER_DEFAULT_PAGES_STATE: Omit< @@ -39,3 +39,22 @@ export const READER_DEFAULT_PAGES_STATE: Omit< transitionPageMode: ReaderTransitionPageMode.NONE, 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; +}; diff --git a/src/features/reader/ReaderStore.ts b/src/features/reader/ReaderStore.ts index 9dd142f9..b12422a6 100644 --- a/src/features/reader/ReaderStore.ts +++ b/src/features/reader/ReaderStore.ts @@ -18,9 +18,9 @@ import { createReaderAutoScrollStoreSlice, ReaderAutoScrollStoreSlice, } 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 { 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 { 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; manga: TMangaReader | undefined; setManga: (manga: TMangaReader | undefined) => void; @@ -94,6 +104,30 @@ const createReaderPagesStoreSlice = ( }, }); +const createReaderChaptersStoreSlice = ( + ...[set, get]: Parameters> +): 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()( immer((set, get, store) => ({ ...DEFAULT_STATE, @@ -104,6 +138,7 @@ export const useReaderStore = create()( get().overlay.reset(); get().autoScroll.reset(); get().pages.reset(); + get().chapters.reset(); }), setManga: (manga) => set((draft) => { @@ -123,6 +158,7 @@ export const useReaderStore = create()( ...createReaderOverlayStoreSlice(set, get, store), ...createReaderAutoScrollStoreSlice(set, get, store), ...createReaderPagesStoreSlice(set, get, store), + ...createReaderChaptersStoreSlice(set, get, store), })), ); export const useReaderStoreShallow = (selector: (state: ReaderStore) => T): T => diff --git a/src/features/reader/contexts/state/ReaderStateChaptersContext.tsx b/src/features/reader/contexts/state/ReaderStateChaptersContext.tsx deleted file mode 100644 index 610b7dc9..00000000 --- a/src/features/reader/contexts/state/ReaderStateChaptersContext.tsx +++ /dev/null @@ -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 = { - chapters: [], - isCurrentChapterReady: false, - visibleChapters: { - leading: 0, - trailing: 0, - lastLeadingChapterSourceOrder: 99999, - lastTrailingChapterSourceOrder: -1, - isLeadingChapterPreloadMode: true, - isTrailingChapterPreloadMode: true, - scrollIntoView: false, - resumeMode: undefined, - }, -}; - -export const ReaderStateChaptersContext = createContext({ - ...READER_STATE_CHAPTERS_DEFAULTS, - setReaderStateChapters: () => {}, -}); - -export const useReaderStateChaptersContext = () => useContext(ReaderStateChaptersContext); - -export const ReaderStateChaptersContextProvider = ({ children }: { children: ReactNode }) => { - const [state, setState] = - useState>(READER_STATE_CHAPTERS_DEFAULTS); - - const value = useMemo( - () => ({ - ...state, - setReaderStateChapters: setState, - }), - [state], - ); - - return {children}; -}; diff --git a/src/features/reader/contexts/state/ReaderStateContextProvider.tsx b/src/features/reader/contexts/state/ReaderStateContextProvider.tsx index 0ed00a49..f506cdc1 100644 --- a/src/features/reader/contexts/state/ReaderStateContextProvider.tsx +++ b/src/features/reader/contexts/state/ReaderStateContextProvider.tsx @@ -7,11 +7,8 @@ */ import { ReactNode } from 'react'; -import { ReaderStateChaptersContextProvider } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx'; import { ReaderStateSettingsContextProvider } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx'; export const ReaderStateContextProvider = ({ children }: { children: ReactNode }) => ( - - {children} - + {children} ); diff --git a/src/features/reader/hooks/useReaderResetStates.ts b/src/features/reader/hooks/useReaderResetStates.ts index e0eb60b9..0a903338 100644 --- a/src/features/reader/hooks/useReaderResetStates.ts +++ b/src/features/reader/hooks/useReaderResetStates.ts @@ -7,19 +7,14 @@ */ 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 { READER_STATE_CHAPTERS_DEFAULTS } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx'; import { getReaderStore } from '@/features/reader/ReaderStore.ts'; -export const useReaderResetStates = ( - setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'], - setSettings: TReaderStateSettingsContext['setSettings'], -) => { +export const useReaderResetStates = (setSettings: TReaderStateSettingsContext['setSettings']) => { useEffect( () => () => { getReaderStore().reset(); - setReaderStateChapters(READER_STATE_CHAPTERS_DEFAULTS); setSettings(DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG); }, diff --git a/src/features/reader/hooks/useReaderSetChaptersState.ts b/src/features/reader/hooks/useReaderSetChaptersState.ts index c27f791f..10bacdd5 100644 --- a/src/features/reader/hooks/useReaderSetChaptersState.ts +++ b/src/features/reader/hooks/useReaderSetChaptersState.ts @@ -16,11 +16,12 @@ import { ReaderOpenChapterLocationState, ReaderStateChapters, } 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 { ChapterListOptions } from '@/features/chapter/Chapter.types.ts'; import { getReaderChapterFromCache } from '@/features/reader/Reader.utils.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 = ( chaptersResponse: ReturnType>, @@ -28,7 +29,6 @@ export const useReaderSetChaptersState = ( mangaChapters: ReaderStateChapters['mangaChapters'], initialChapter: ReaderStateChapters['initialChapter'], chapterForDuplicatesHandling: ReaderStateChapters['chapterForDuplicatesHandling'], - setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'], shouldSkipDupChapters: IReaderSettings['shouldSkipDupChapters'], shouldSkipFilteredChapters: IReaderSettings['shouldSkipFilteredChapters'], chapterListOptions: ChapterListOptions, @@ -78,7 +78,7 @@ export const useReaderSetChaptersState = ( navigate('', { replace: true, state: { ...locationState, updateInitialChapter: undefined } }); } - setReaderStateChapters((prevState) => { + getReaderStore().chapters.setReaderStateChapters((prevState) => { const hasCurrentChapterChanged = newCurrentChapter?.id !== prevState.currentChapter?.id; return { @@ -93,13 +93,13 @@ export const useReaderSetChaptersState = ( isCurrentChapterReady: hasCurrentChapterChanged ? false : prevState.isCurrentChapterReady, visibleChapters: hasInitialChapterChanged ? { - ...READER_STATE_CHAPTERS_DEFAULTS.visibleChapters, + ...READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters, lastLeadingChapterSourceOrder: newInitialChapter?.sourceOrder ?? - READER_STATE_CHAPTERS_DEFAULTS.visibleChapters.lastLeadingChapterSourceOrder, + READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters.lastLeadingChapterSourceOrder, lastTrailingChapterSourceOrder: newInitialChapter?.sourceOrder ?? - READER_STATE_CHAPTERS_DEFAULTS.visibleChapters.lastTrailingChapterSourceOrder, + READER_DEFAULT_CHAPTERS_STATE.chapters.visibleChapters.lastTrailingChapterSourceOrder, isLeadingChapterPreloadMode: false, isTrailingChapterPreloadMode: false, // do not set "scrollIntoView" to "true" for the initial render diff --git a/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx b/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx index e9f7068c..9cb4dd18 100644 --- a/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx +++ b/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx @@ -24,9 +24,6 @@ import { makeToast } from '@/base/utils/Toast.ts'; import { MobileHeaderProps } from '@/features/reader/overlay/ReaderOverlay.types.ts'; import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx'; 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 { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx'; 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 BaseReaderOverlayHeaderMobile = forwardRef< - HTMLDivElement, - MobileHeaderProps & Pick ->(({ isVisible, currentChapter }, ref) => { +const BaseReaderOverlayHeaderMobile = forwardRef(({ isVisible }, ref) => { const { t } = useTranslation(); const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' }); + const currentChapter = useReaderStoreShallow((state) => state.chapters.currentChapter); const manga = useReaderStoreShallow((state) => state.manga); const scrollbar = useReaderStoreShallow((state) => state.scrollbar); @@ -129,8 +124,4 @@ const BaseReaderOverlayHeaderMobile = forwardRef< ); }); -export const ReaderOverlayHeaderMobile = withPropsFrom( - memo(BaseReaderOverlayHeaderMobile), - [useReaderStateChaptersContext], - ['currentChapter'], -); +export const ReaderOverlayHeaderMobile = memo(BaseReaderOverlayHeaderMobile); diff --git a/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx b/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx index feb119b0..82c804e0 100644 --- a/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx +++ b/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx @@ -24,11 +24,10 @@ import { ReaderNavBarDesktopQuickSettings } from '@/features/reader/overlay/navi import { ReaderNavBarDesktopActions } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx'; import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.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 { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx'; 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 { ReaderExitButton } from '@/features/reader/overlay/navigation/components/ReaderExitButton.tsx'; import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; @@ -55,17 +54,18 @@ const BaseReaderNavBarDesktop = ({ isVisible, openSettings, setReaderNavBarWidth, - chapters, - currentChapter, - previousChapter, - nextChapter, isStaticNav, }: ReaderNavBarDesktopProps & Pick & - Pick & Pick) => { const { t } = useTranslation(); 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(); useResizeObserver( @@ -146,6 +146,6 @@ const BaseReaderNavBarDesktop = ({ export const ReaderNavBarDesktop = withPropsFrom( memo(BaseReaderNavBarDesktop), - [useNavBarContext, useReaderStateChaptersContext, ReaderService.useSettingsWithoutDefaultFlag], - ['setReaderNavBarWidth', 'chapters', 'currentChapter', 'previousChapter', 'nextChapter', 'isStaticNav'], + [useNavBarContext, ReaderService.useSettingsWithoutDefaultFlag], + ['setReaderNavBarWidth', 'isStaticNav'], ); diff --git a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx index fc912af4..2ec08d48 100644 --- a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx +++ b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx @@ -15,10 +15,7 @@ import { memo, useMemo, useRef } from 'react'; import DeleteIcon from '@mui/icons-material/Delete'; import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; 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 { 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 { ReaderBookmarkButton } from '@/features/reader/overlay/navigation/components/ReaderBookmarkButton.tsx'; 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 { requestManager } from '@/lib/requests/RequestManager.ts'; import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; +import { ChapterDownloadInfo, ChapterIdInfo } from '@/features/chapter/Chapter.types.ts'; -const DownloadButton = ({ currentChapter }: Required>) => { +const DownloadButton = ({ + currentChapter, +}: { + currentChapter: NullAndUndefined; +}) => { const { t } = useTranslation(); const downloadStatus = Chapters.useDownloadStatusFromCache(currentChapter?.id ?? -1); @@ -59,76 +61,64 @@ const DownloadButton = ({ currentChapter }: Required>) => { - const { id, isBookmarked, realUrl } = currentChapter ?? FALLBACK_CHAPTER; +export const ReaderNavBarDesktopActions = memo(() => { + const currentChapter = useReaderStoreShallow((state) => state.chapters.currentChapter); - const { t } = useTranslation(); - const { pageLoadStates, setPageLoadStates, setRetryFailedPagesKeyPrefix } = useReaderStoreShallow((state) => ({ - pageLoadStates: state.pages.pageLoadStates, - setPageLoadStates: state.pages.setPageLoadStates, - setRetryFailedPagesKeyPrefix: state.pages.setRetryFailedPagesKeyPrefix, - })); + const { id, isBookmarked, realUrl } = currentChapter ?? FALLBACK_CHAPTER; - const pageRetryKeyPrefix = useRef(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( - () => pageLoadStates.some((pageLoadState) => pageLoadState.error), - [pageLoadStates], - ); + const pageRetryKeyPrefix = useRef(0); - return ( - - - - - { - setPageLoadStates((statePageLoadStates) => - statePageLoadStates.map((pageLoadState) => ({ - url: pageLoadState.url, - loaded: pageLoadState.loaded, - })), - ); - setRetryFailedPagesKeyPrefix(`${pageRetryKeyPrefix.current}`); - pageRetryKeyPrefix.current = (pageRetryKeyPrefix.current + 1) % 1000; - }} - disabled={!haveSomePagesFailedToLoad} - color="inherit" - > - - - - - - - - - - - - - - - - ); - }, -); + const haveSomePagesFailedToLoad = useMemo( + () => pageLoadStates.some((pageLoadState) => pageLoadState.error), + [pageLoadStates], + ); -export const ReaderNavBarDesktopActions = withPropsFrom( - BaseReaderNavBarDesktopActions, - [useReaderStateChaptersContext], - ['currentChapter'], -); + return ( + + + + + { + setPageLoadStates((statePageLoadStates) => + statePageLoadStates.map((pageLoadState) => ({ + url: pageLoadState.url, + loaded: pageLoadState.loaded, + })), + ); + setRetryFailedPagesKeyPrefix(`${pageRetryKeyPrefix.current}`); + pageRetryKeyPrefix.current = (pageRetryKeyPrefix.current + 1) % 1000; + }} + disabled={!haveSomePagesFailedToLoad} + color="inherit" + > + + + + + + + + + + + + + + + + ); +}); diff --git a/src/features/reader/overlay/navigation/mobile/ReaderBottomBarMobile.tsx b/src/features/reader/overlay/navigation/mobile/ReaderBottomBarMobile.tsx index c8661a99..8ef4c911 100644 --- a/src/features/reader/overlay/navigation/mobile/ReaderBottomBarMobile.tsx +++ b/src/features/reader/overlay/navigation/mobile/ReaderBottomBarMobile.tsx @@ -23,20 +23,19 @@ import { ReaderBottomBarMobileProps } from '@/features/reader/overlay/ReaderOver import { MobileReaderProgressBar } from '@/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx'; import { ReaderChapterList } from '@/features/reader/overlay/navigation/components/ReaderChapterList.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 { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; const BaseReaderBottomBarMobile = ({ openSettings, isVisible, - currentChapter, - chapters, topOffset = 0, -}: ReaderBottomBarMobileProps & Pick & { topOffset?: number }) => { +}: ReaderBottomBarMobileProps & { topOffset?: number }) => { 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 quickSettingsPopupState = usePopupState({ variant: 'dialog', popupId: 'reader-quick-settings-dialog' }); @@ -130,8 +129,4 @@ const BaseReaderBottomBarMobile = ({ ); }; -export const ReaderBottomBarMobile = withPropsFrom( - memo(BaseReaderBottomBarMobile), - [useReaderStateChaptersContext], - ['currentChapter', 'chapters'], -); +export const ReaderBottomBarMobile = memo(BaseReaderBottomBarMobile); diff --git a/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx b/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx index 845e741b..b9b80112 100644 --- a/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx +++ b/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx @@ -15,14 +15,13 @@ import Box from '@mui/material/Box'; import { ComponentProps, memo, useCallback, useLayoutEffect, useMemo, useState } from 'react'; import Slide, { SlideProps } from '@mui/material/Slide'; 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 { getPage, getProgressBarPositionInfo, } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; 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 { useReaderProgressBarContext } from '@/features/reader/overlay/progress-bar/ReaderProgressBarContext.tsx'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; @@ -43,8 +42,6 @@ const PROGRESS_BAR_POSITION_TO_SLIDE_DIRECTION: Record & - Pick & +}: Pick & Pick & { direction: ReturnType; topOffset?: number; @@ -66,6 +62,10 @@ const BaseMobileReaderProgressBar = ({ currentPageIndex: state.pages.currentPageIndex, pages: state.pages.pages, })); + const { previousChapter, nextChapter } = useReaderStore((state) => ({ + previousChapter: state.chapters.previousChapter, + nextChapter: state.chapters.nextChapter, + })); const [, setRefreshProgressBarPosition] = useState({}); useResizeObserver( @@ -354,18 +354,9 @@ const BaseMobileReaderProgressBar = ({ export const MobileReaderProgressBar = withPropsFrom( memo(BaseMobileReaderProgressBar), [ - useReaderStateChaptersContext, useReaderProgressBarContext, () => ({ direction: ReaderService.useGetThemeDirection() }), ReaderService.useSettingsWithoutDefaultFlag, ], - [ - 'previousChapter', - 'nextChapter', - 'setIsMaximized', - 'isDragging', - 'direction', - 'progressBarPosition', - 'progressBarPositionAutoVertical', - ], + ['setIsMaximized', 'isDragging', 'direction', 'progressBarPosition', 'progressBarPositionAutoVertical'], ); diff --git a/src/features/reader/screens/Reader.tsx b/src/features/reader/screens/Reader.tsx index a069e5a6..ac39ce10 100644 --- a/src/features/reader/screens/Reader.tsx +++ b/src/features/reader/screens/Reader.tsx @@ -30,13 +30,11 @@ import { ReaderHotkeys } from '@/features/reader/hotkeys/ReaderHotkeys.tsx'; import { IReaderSettings, IReaderSettingsWithDefaultFlag, - ReaderStateChapters, TReaderStateSettingsContext, } from '@/features/reader/Reader.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; 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 { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { useReaderResetStates } from '@/features/reader/hooks/useReaderResetStates.ts'; @@ -61,11 +59,6 @@ const BaseReader = ({ shouldShowReadingModePreview, shouldShowTapZoneLayoutPreview, setSettings, - mangaChapters, - initialChapter, - chapterForDuplicatesHandling, - currentChapter, - setReaderStateChapters, setShowPreview, }: Pick & Pick & @@ -78,18 +71,18 @@ const BaseReader = ({ | 'shouldShowTapZoneLayoutPreview' > & Pick & - Pick< - ReaderStateChapters, - | 'mangaChapters' - | 'initialChapter' - | 'chapterForDuplicatesHandling' - | 'currentChapter' - | 'setReaderStateChapters' - > & Pick) => { const { t } = useTranslation(); const manga = useReaderStoreShallow((state) => state.manga); 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(null); @@ -130,7 +123,7 @@ const BaseReader = ({ useReaderStore.getState().setManga(mangaResponse.data?.manga); }, [mangaResponse.data?.manga]); - useReaderResetStates(setReaderStateChapters, setSettings); + useReaderResetStates(setSettings); useReaderSetSettingsState( mangaResponse, defaultSettingsResponse, @@ -156,7 +149,6 @@ const BaseReader = ({ mangaChapters, initialChapter, chapterForDuplicatesHandling, - setReaderStateChapters, shouldSkipDupChapters, shouldSkipFilteredChapters, chapterListOptions, @@ -263,7 +255,6 @@ export const Reader = withPropsFrom( memo(BaseReader), [ useNavBarContext, - useReaderStateChaptersContext, useReaderStateSettingsContext, () => { const { @@ -299,11 +290,6 @@ export const Reader = withPropsFrom( 'shouldShowReadingModePreview', 'shouldShowTapZoneLayoutPreview', 'setSettings', - 'mangaChapters', - 'initialChapter', - 'chapterForDuplicatesHandling', - 'currentChapter', - 'setReaderStateChapters', 'setShowPreview', ], ); diff --git a/src/features/reader/services/ReaderControls.ts b/src/features/reader/services/ReaderControls.ts index 6dd0c867..b2108e35 100644 --- a/src/features/reader/services/ReaderControls.ts +++ b/src/features/reader/services/ReaderControls.ts @@ -6,7 +6,7 @@ * 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 { useTranslation } from 'react-i18next'; import { TFunction } from 'i18next'; @@ -19,7 +19,6 @@ import { } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts'; -import { useReaderStateChaptersContext } from '@/features/reader/contexts/state/ReaderStateChaptersContext.tsx'; import { PageInViewportType, ProgressBarPosition, @@ -173,19 +172,20 @@ export class ReaderControls { shouldInformAboutScanlatorChange, shouldUseInfiniteScroll, } = ReaderService.useSettings(); - const { - currentChapter, - previousChapter, - nextChapter, - chapters, - visibleChapters: { lastLeadingChapterSourceOrder, lastTrailingChapterSourceOrder }, - setReaderStateChapters, - } = useReaderStateChaptersContext(); const openChapter = ReaderService.useNavigateToChapter(); return useCallback( (offset, doTransitionCheck = true, scrollIntoView = true) => { + const { + currentChapter, + previousChapter, + nextChapter, + chapters, + visibleChapters: { lastLeadingChapterSourceOrder, lastTrailingChapterSourceOrder }, + setReaderStateChapters, + } = getReaderStore().chapters; + if (!currentChapter) { return; } @@ -266,13 +266,10 @@ export class ReaderControls { }, [ t, - currentChapter?.id, openChapter, readingMode.value, shouldInformAboutMissingChapter, shouldInformAboutScanlatorChange, - lastLeadingChapterSourceOrder, - lastTrailingChapterSourceOrder, shouldUseInfiniteScroll, ], ); @@ -343,7 +340,6 @@ export class ReaderControls { forceDirection?: Direction, hideOverlay?: boolean, ) => void { - const { previousChapter, nextChapter } = useReaderStateChaptersContext(); const { setShowPreview } = useReaderTapZoneContext(); const { readingDirection, readingMode, shouldShowTransitionPage } = ReaderService.useSettings(); const openChapter = ReaderControls.useOpenChapter(); @@ -398,7 +394,7 @@ export class ReaderControls { isFirstPage && (!shouldShowTransitionPage || isPreviousTransitionPageVisible) && convertedPage === 'previous' && - !!previousChapter; + !!getReaderStore().chapters.previousChapter; if (shouldOpenPreviousChapter) { openChapter('previous'); return; @@ -408,7 +404,7 @@ export class ReaderControls { isLastPage && (!shouldShowTransitionPage || isNextTransitionPageVisible) && convertedPage === 'next' && - !!nextChapter; + !!getReaderStore().chapters.nextChapter; if (shouldOpenNextChapter) { openChapter('next'); return; @@ -442,7 +438,7 @@ export class ReaderControls { 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, endReached?: boolean, ) => void { - const { currentChapter, chapters, previousChapter, nextChapter, visibleChapters, setReaderStateChapters } = - useReaderStateChaptersContext(); const updateChapter = ReaderService.useUpdateChapter(); const { settings: { downloadAheadLimit }, } = useMetadataServerSettings(); - const nextChapters = useMemo(() => { - if (!currentChapter) { - return []; - } - - return Chapters.getNextChapters(currentChapter, chapters, { - offset: DirectionOffset.NEXT, - }); - }, [currentChapter?.id, chapters]); - return useCallback( (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) { return; @@ -482,6 +476,10 @@ export class ReaderControls { return; } + const nextChapters = Chapters.getNextChapters(currentChapter, chapters, { + offset: DirectionOffset.NEXT, + }); + const direction = currentPageIndex > pageIndex ? DirectionOffset.PREVIOUS : DirectionOffset.NEXT; ReaderService.downloadAhead(currentChapter, nextChapter, nextChapters, pageIndex, downloadAheadLimit); @@ -518,14 +516,7 @@ export class ReaderControls { handleCurrentPageIndexChange(); }, - [ - currentChapter?.id, - previousChapter?.id, - nextChapter?.id, - nextChapters, - downloadAheadLimit, - visibleChapters, - ], + [downloadAheadLimit], ); } diff --git a/src/features/reader/services/ReaderService.ts b/src/features/reader/services/ReaderService.ts index 7c0eadcc..86e214a5 100644 --- a/src/features/reader/services/ReaderService.ts +++ b/src/features/reader/services/ReaderService.ts @@ -6,7 +6,7 @@ * 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 { Direction, useTheme } from '@mui/material/styles'; 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 { useReaderStateSettingsContext } from '@/features/reader/contexts/state/ReaderStateSettingsContext.tsx'; 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 { getChapterIdsForDownloadAhead, @@ -155,30 +154,26 @@ export class ReaderService { } static useUpdateChapter(): (patch: UpdateChapterPatchInput) => void { - const { currentChapter, mangaChapters, chapters } = useReaderStateChaptersContext(); const { shouldSkipDupChapters } = ReaderService.useSettings(); const { settings: { deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading }, } = useMetadataServerSettings(); - const previousChapters = useMemo(() => { - if (!currentChapter) { - return []; - } - - return Chapters.getNextChapters(currentChapter, chapters, { - offset: DirectionOffset.PREVIOUS, - }); - }, [currentChapter?.id, chapters]); - return useCallback( (patch) => { - const { manga } = getReaderStore(); + const { + manga, + chapters: { currentChapter, mangaChapters, chapters }, + } = getReaderStore(); if (!manga || !currentChapter || !mangaChapters) { return; } + const previousChapters = Chapters.getNextChapters(currentChapter, chapters, { + offset: DirectionOffset.PREVIOUS, + }); + const update = async () => { const chapterIdsToUpdate = Chapters.getIds( shouldSkipDupChapters @@ -234,15 +229,7 @@ export class ReaderService { ReaderService.getOrCreateChapterUpdateQueue(currentChapter.id).enqueue(`${currentChapter.id}`, update); }, - [ - currentChapter?.id, - mangaChapters, - previousChapters, - shouldSkipDupChapters, - deleteChaptersWhileReading, - deleteChaptersWithBookmark, - updateProgressAfterReading, - ], + [shouldSkipDupChapters, deleteChaptersWhileReading, deleteChaptersWithBookmark, updateProgressAfterReading], ); } diff --git a/src/features/reader/viewer/ReaderViewer.tsx b/src/features/reader/viewer/ReaderViewer.tsx index e7cd03a1..825e80e1 100644 --- a/src/features/reader/viewer/ReaderViewer.tsx +++ b/src/features/reader/viewer/ReaderViewer.tsx @@ -27,7 +27,6 @@ import { PageInViewportType, ReaderOpenChapterLocationState, ReaderResumeMode, - ReaderStateChapters, ReadingDirection, ReadingMode, } 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 { useReaderScrollToStartOnPageChange } from '@/features/reader/viewer/hooks/useReaderScrollToStartOnPageChange.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 { getPreviousNextChapterVisibility, @@ -90,12 +88,6 @@ const BaseReaderViewer = forwardRef( updateCurrentPageIndex, showPreview, setShowPreview, - initialChapter, - currentChapter, - chapters, - visibleChapters, - setReaderStateChapters, - isCurrentChapterReady, }: Pick< IReaderSettings, | 'readingMode' @@ -111,15 +103,6 @@ const BaseReaderViewer = forwardRef( | 'isStaticNav' > & Pick & - Pick< - ReaderStateChapters, - | 'initialChapter' - | 'currentChapter' - | 'chapters' - | 'visibleChapters' - | 'setReaderStateChapters' - | 'isCurrentChapterReady' - > & TReaderTapZoneContext & { updateCurrentPageIndex: ReturnType; }, @@ -155,6 +138,21 @@ const BaseReaderViewer = forwardRef( retryFailedPagesKeyPrefix: state.pages.retryFailedPagesKeyPrefix, 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().state ?? { resumeMode: ReaderResumeMode.START, }; @@ -446,7 +444,6 @@ export const ReaderViewer = withPropsFrom( ReaderService.useSettingsWithoutDefaultFlag, () => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }), useReaderTapZoneContext, - useReaderStateChaptersContext, useNavBarContext, ], [ @@ -465,11 +462,5 @@ export const ReaderViewer = withPropsFrom( 'updateCurrentPageIndex', 'showPreview', 'setShowPreview', - 'initialChapter', - 'currentChapter', - 'chapters', - 'visibleChapters', - 'setReaderStateChapters', - 'isCurrentChapterReady', ], ); diff --git a/src/features/reader/viewer/components/ReaderTransitionPage.tsx b/src/features/reader/viewer/components/ReaderTransitionPage.tsx index adb06766..90c87a71 100644 --- a/src/features/reader/viewer/components/ReaderTransitionPage.tsx +++ b/src/features/reader/viewer/components/ReaderTransitionPage.tsx @@ -13,7 +13,6 @@ import Button from '@mui/material/Button'; import { Link } from 'react-router-dom'; import { ComponentProps, memo, useMemo } from 'react'; 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 { isTransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { useBackButton } from '@/base/hooks/useBackButton.ts'; @@ -224,7 +223,7 @@ export const ReaderTransitionPage = withPropsFrom( memo(BaseReaderTransitionPage) as typeof BaseReaderTransitionPage, [ ({ chapterId }: Pick, 'chapterId'>) => { - const { chapters } = useReaderStateChaptersContext(); + const chapters = useReaderStoreShallow((state) => state.chapters.chapters); const currentChapterIndex = useMemo( () => chapters.findIndex((chapter) => chapter.id === chapterId), @@ -248,7 +247,7 @@ export const ReaderTransitionPage = withPropsFrom( ReaderService.useSettingsWithoutDefaultFlag, ({ chapterId, type }: Pick, 'chapterId' | 'type'>) => { const handleBack = useBackButton(); - const { chapters } = useReaderStateChaptersContext(); + const chapters = useReaderStoreShallow((state) => state.chapters.chapters); const currentChapterIndex = useMemo( () => chapters.findIndex((chapter) => chapter.id === chapterId),