From fb70d8994081c87e35d134e7afc3d7aab7d6ba8d Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:41:05 +0200 Subject: [PATCH] Move reader "tap zone state" to "reader store" --- src/base/contexts/AppContext.tsx | 5 +-- src/features/reader/ReaderStore.ts | 9 ++++- .../reader/contexts/ReaderContextProvider.tsx | 14 -------- src/features/reader/hotkeys/ReaderHotkeys.tsx | 6 ++-- src/features/reader/screens/Reader.tsx | 5 ++- .../reader/services/ReaderControls.ts | 9 ++--- .../components/ReaderSettingsTabs.tsx | 30 +++++++---------- .../reader/tap-zones/ReaderTapZoneContext.tsx | 25 -------------- .../reader/tap-zones/ReaderTapZoneStore.tsx | 33 +++++++++++++++++++ .../reader/tap-zones/TapZoneLayout.tsx | 16 +++------ src/features/reader/viewer/ReaderViewer.tsx | 23 +++++-------- 11 files changed, 74 insertions(+), 101 deletions(-) delete mode 100644 src/features/reader/contexts/ReaderContextProvider.tsx delete mode 100644 src/features/reader/tap-zones/ReaderTapZoneContext.tsx create mode 100644 src/features/reader/tap-zones/ReaderTapZoneStore.tsx diff --git a/src/base/contexts/AppContext.tsx b/src/base/contexts/AppContext.tsx index dc1405e6..c1b515d2 100644 --- a/src/base/contexts/AppContext.tsx +++ b/src/base/contexts/AppContext.tsx @@ -13,7 +13,6 @@ import { QueryParamProvider } from 'use-query-params'; import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6'; import { SnackbarProvider } from 'notistack'; import { ActiveDeviceContextProvider } from '@/features/device/DeviceContext.tsx'; -import { ReaderContextProvider } from '@/features/reader/contexts/ReaderContextProvider.tsx'; import { AppHotkeysProvider } from '@/features/hotkeys/AppHotkeysProvider.tsx'; import { SnackbarWithDescription } from '@/base/components/feedback/SnackbarWithDescription.tsx'; import { AppPageHistoryContextProvider } from '@/base/contexts/AppPageHistoryContext.tsx'; @@ -43,9 +42,7 @@ export const AppContext: React.FC = ({ children }) => ( error: SnackbarWithDescription, }} > - - {children} - + {children} diff --git a/src/features/reader/ReaderStore.ts b/src/features/reader/ReaderStore.ts index e3e39f65..13bb7ec3 100644 --- a/src/features/reader/ReaderStore.ts +++ b/src/features/reader/ReaderStore.ts @@ -30,6 +30,10 @@ import { createReaderProgressBarStoreSlice, ReaderProgressBarStoreSlice, } from '@/features/reader/overlay/progress-bar/ReaderProgressBarStore.tsx'; +import { + createReaderTapZoneStoreSlice, + ReaderTapZoneStoreSlice, +} from '@/features/reader/tap-zones/ReaderTapZoneStore.tsx'; interface ReaderPagesStoreSlice { pages: ReaderStatePages & { @@ -48,7 +52,8 @@ interface ReaderStore ReaderAutoScrollStoreSlice, ReaderPagesStoreSlice, ReaderChaptersStoreSlice, - ReaderProgressBarStoreSlice { + ReaderProgressBarStoreSlice, + ReaderTapZoneStoreSlice { reset: () => void; manga: TMangaReader | undefined; setManga: (manga: TMangaReader | undefined) => void; @@ -157,6 +162,7 @@ export const useReaderStore = create()( ...DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG, }; get().progressBar.reset(); + get().tapZone.reset(); }), setManga: (manga) => set((draft) => { @@ -188,6 +194,7 @@ export const useReaderStore = create()( ...createReaderPagesStoreSlice(set, get, store), ...createReaderChaptersStoreSlice(set, get, store), ...createReaderProgressBarStoreSlice(set, get, store), + ...createReaderTapZoneStoreSlice(set, get, store), })), ); export const useReaderStoreShallow = (selector: (state: ReaderStore) => T): T => diff --git a/src/features/reader/contexts/ReaderContextProvider.tsx b/src/features/reader/contexts/ReaderContextProvider.tsx deleted file mode 100644 index b616dd1e..00000000 --- a/src/features/reader/contexts/ReaderContextProvider.tsx +++ /dev/null @@ -1,14 +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 { ReactNode } from 'react'; -import { ReaderTapZoneContextProvider } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; - -export const ReaderContextProvider = ({ children }: { children?: ReactNode }) => ( - {children} -); diff --git a/src/features/reader/hotkeys/ReaderHotkeys.tsx b/src/features/reader/hotkeys/ReaderHotkeys.tsx index a2dc344a..0e1526e3 100644 --- a/src/features/reader/hotkeys/ReaderHotkeys.tsx +++ b/src/features/reader/hotkeys/ReaderHotkeys.tsx @@ -24,7 +24,6 @@ import { HotkeyScope } from '@/features/hotkeys/Hotkeys.types.ts'; import { ReaderControls } from '@/features/reader/services/ReaderControls.ts'; import { ScrollOffset } from '@/base/Base.types.ts'; import { getOptionForDirection } from '@/features/theme/services/ThemeCreator.ts'; -import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { getReaderStore, useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; const useHotkeys = (...args: Parameters): ReturnType => { @@ -66,7 +65,6 @@ export const ReaderHotkeys = ({ const { hotkeys } = useReaderStoreShallow((state) => ({ hotkeys: state.settings.hotkeys, })); - const { setShowPreview } = useReaderTapZoneContext(); const exitReader = ReaderService.useExit(); const openChapter = ReaderControls.useOpenChapter(); @@ -99,7 +97,7 @@ export const ReaderHotkeys = ({ themeDirection, scrollElementRef.current, openChapter, - setShowPreview, + getReaderStore().tapZone.setShowPreview, scrollAmount, ); }, @@ -131,7 +129,7 @@ export const ReaderHotkeys = ({ themeDirection, scrollElementRef.current, openChapter, - setShowPreview, + getReaderStore().tapZone.setShowPreview, scrollAmount, ); }, diff --git a/src/features/reader/screens/Reader.tsx b/src/features/reader/screens/Reader.tsx index b809ecc4..4174cf94 100644 --- a/src/features/reader/screens/Reader.tsx +++ b/src/features/reader/screens/Reader.tsx @@ -28,7 +28,6 @@ import { ReaderHotkeys } from '@/features/reader/hotkeys/ReaderHotkeys.tsx'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; -import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { useReaderResetStates } from '@/features/reader/hooks/useReaderResetStates.ts'; import { useReaderSetSettingsState } from '@/features/reader/hooks/useReaderSetSettingsState.ts'; import { useReaderShowSettingPreviewOnChange } from '@/features/reader/hooks/useReaderShowSettingPreviewOnChange.ts'; @@ -42,8 +41,7 @@ import { ReaderAutoScroll } from '@/features/reader/auto-scroll/ReaderAutoScroll const BaseReader = ({ setOverride, readerNavBarWidth, - setShowPreview, -}: Pick & Pick) => { +}: Pick) => { const { t } = useTranslation(); const manga = useReaderStoreShallow((state) => state.manga); const overlay = useReaderStoreShallow((state) => state.overlay); @@ -76,6 +74,7 @@ const BaseReader = ({ shouldShowTapZoneLayoutPreview: state.settings.shouldShowTapZoneLayoutPreview, setSettings: state.settings.setSettings, })); + const setShowPreview = useReaderStore((state) => state.tapZone.setShowPreview); const scrollElementRef = useRef(null); diff --git a/src/features/reader/services/ReaderControls.ts b/src/features/reader/services/ReaderControls.ts index 6e022342..a310d413 100644 --- a/src/features/reader/services/ReaderControls.ts +++ b/src/features/reader/services/ReaderControls.ts @@ -35,7 +35,6 @@ import { isEndOfPageInViewport, isPageInViewport, } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; -import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { TapZoneRegionType, TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { ReaderTapZoneService } from '@/features/reader/tap-zones/ReaderTapZoneService.ts'; import { isContinuousReadingMode } from '@/features/reader/settings/ReaderSettings.utils.tsx'; @@ -334,7 +333,6 @@ export class ReaderControls { forceDirection?: Direction, hideOverlay?: boolean, ) => void { - const { setShowPreview } = useReaderTapZoneContext(); const openChapter = ReaderControls.useOpenChapter(); return useCallback( @@ -371,7 +369,7 @@ export class ReaderControls { if (hideOverlay) { getReaderStore().overlay.setIsVisible(false); - setShowPreview(false); + getReaderStore().tapZone.setShowPreview(false); } const hideTransitionPage = () => setTransitionPageMode(ReaderTransitionPageMode.NONE); @@ -565,7 +563,6 @@ export class ReaderControls { scrollElement: HTMLElement | null, ): (event: React.MouseEvent) => void { const { direction: themeDirection } = useTheme(); - const { setShowPreview } = useReaderTapZoneContext(); const openPage = ReaderControls.useOpenPage(); const openChapter = ReaderControls.useOpenChapter(); @@ -582,7 +579,7 @@ export class ReaderControls { const rectRelativeY = e.clientY - rect.top; const action = ReaderTapZoneService.getAction(rectRelativeX, rectRelativeY); - setShowPreview(false); + getReaderStore().tapZone.setShowPreview(false); const isContinuousReadingModeActive = isContinuousReadingMode(readingMode.value); const scrollDirection = @@ -603,7 +600,7 @@ export class ReaderControls { themeDirection, scrollElement, openChapter, - setShowPreview, + getReaderStore().tapZone.setShowPreview, scrollAmount, ); } else { diff --git a/src/features/reader/settings/components/ReaderSettingsTabs.tsx b/src/features/reader/settings/components/ReaderSettingsTabs.tsx index 485cb6c1..891fbad7 100644 --- a/src/features/reader/settings/components/ReaderSettingsTabs.tsx +++ b/src/features/reader/settings/components/ReaderSettingsTabs.tsx @@ -13,7 +13,6 @@ import { TabsMenu } from '@/base/components/tabs/TabsMenu.tsx'; import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { MediaQuery } from '@/base/utils/MediaQuery.tsx'; import { IReaderSettings, IReaderSettingsWithDefaultFlag } from '@/features/reader/Reader.types.ts'; -import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { applyStyles } from '@/base/utils/ApplyStyles.ts'; import { READER_SETTING_TABS, ReaderSettingTab } from '@/features/reader/settings/ReaderSettings.constants.tsx'; import { TabPanel } from '@/base/components/tabs/TabPanel.tsx'; @@ -23,8 +22,8 @@ import { ReaderFilterSettings } from '@/features/reader/filters/settings/ReaderF import { ReaderBehaviourSettings } from '@/features/reader/settings/behaviour/ReaderBehaviourSettings.tsx'; import { ReaderDefaultLayoutSettings } from '@/features/reader/settings/layout/ReaderDefaultLayoutSettings.tsx'; import { ReaderHotkeysSettings } from '@/features/reader/hotkeys/settings/ReaderHotkeysSettings.tsx'; -import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; +import { useReaderStore } from '@/features/reader/ReaderStore.ts'; const BaseReaderSettingsTabs = ({ activeTab, @@ -33,21 +32,20 @@ const BaseReaderSettingsTabs = ({ settings, updateSetting, deleteSetting, - setShowPreview, mode: overlayMode, setTransparent, -}: Pick & - Pick, 'mode'> & { - activeTab: number; - setActiveTab: (tab: number) => void; - settings: IReaderSettingsWithDefaultFlag; - updateSetting: (...args: Parameters) => void; - areDefaultSettings?: boolean; - deleteSetting: (setting: keyof IReaderSettings) => void; - setTransparent?: (transparent: boolean) => void; - }) => { +}: Pick, 'mode'> & { + activeTab: number; + setActiveTab: (tab: number) => void; + settings: IReaderSettingsWithDefaultFlag; + updateSetting: (...args: Parameters) => void; + areDefaultSettings?: boolean; + deleteSetting: (setting: keyof IReaderSettings) => void; + setTransparent?: (transparent: boolean) => void; +}) => { const { t } = useTranslation(); const isTouchDevice = MediaQuery.useIsTouchDevice(); + const setShowPreview = useReaderStore((state) => state.tapZone.setShowPreview); return ( <> @@ -186,8 +184,4 @@ const BaseReaderSettingsTabs = ({ ); }; -export const ReaderSettingsTabs = withPropsFrom( - BaseReaderSettingsTabs, - [useReaderTapZoneContext, ReaderService.useOverlayMode], - ['setShowPreview', 'mode'], -); +export const ReaderSettingsTabs = withPropsFrom(BaseReaderSettingsTabs, [ReaderService.useOverlayMode], ['mode']); diff --git a/src/features/reader/tap-zones/ReaderTapZoneContext.tsx b/src/features/reader/tap-zones/ReaderTapZoneContext.tsx deleted file mode 100644 index 0d879964..00000000 --- a/src/features/reader/tap-zones/ReaderTapZoneContext.tsx +++ /dev/null @@ -1,25 +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, useContext, ReactNode, useMemo, useState } from 'react'; -import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; - -export const ReaderTapZoneContext = createContext({ - showPreview: false, - setShowPreview: () => undefined, -}); - -export const useReaderTapZoneContext = () => useContext(ReaderTapZoneContext); - -export const ReaderTapZoneContextProvider = ({ children }: { children: ReactNode }) => { - const [showPreview, setShowPreview] = useState(false); - - const value = useMemo(() => ({ showPreview, setShowPreview }), [showPreview]); - - return {children}; -}; diff --git a/src/features/reader/tap-zones/ReaderTapZoneStore.tsx b/src/features/reader/tap-zones/ReaderTapZoneStore.tsx new file mode 100644 index 00000000..359ccc2a --- /dev/null +++ b/src/features/reader/tap-zones/ReaderTapZoneStore.tsx @@ -0,0 +1,33 @@ +/* + * 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 { ImmerStateCreator } from '@/lib/zustand/Zustand.types.ts'; +import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; + +export interface ReaderTapZoneStoreSlice { + tapZone: TReaderTapZoneContext & { + reset: () => void; + }; +} + +const DEFAULT_STATE = { + showPreview: false, +} satisfies Pick; + +export const createReaderTapZoneStoreSlice = ( + ...[set, get]: Parameters> +): ReaderTapZoneStoreSlice => ({ + tapZone: { + ...DEFAULT_STATE, + setShowPreview: (showPreview) => + set((draft) => { + draft.tapZone.showPreview = showPreview; + }), + reset: () => set(() => ({ tapZone: { ...get().tapZone, ...DEFAULT_STATE } })), + }, +}); diff --git a/src/features/reader/tap-zones/TapZoneLayout.tsx b/src/features/reader/tap-zones/TapZoneLayout.tsx index c7c839a8..b5909e2b 100644 --- a/src/features/reader/tap-zones/TapZoneLayout.tsx +++ b/src/features/reader/tap-zones/TapZoneLayout.tsx @@ -9,28 +9,24 @@ import Box from '@mui/material/Box'; import { useCallback, useLayoutEffect, useState } from 'react'; import { useTheme } from '@mui/material/styles'; -import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { ReaderTapZoneService } from '@/features/reader/tap-zones/ReaderTapZoneService.ts'; import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx'; import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx'; import { ReadingDirection } from '@/features/reader/Reader.types.ts'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; -import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; -import { useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; +import { useReaderStore, useReaderStoreShallow } from '@/features/reader/ReaderStore.ts'; const CANVAS_ID = 'reader-tap-zone-layout-canvas'; -const BaseTapZoneLayout = ({ - readerNavBarWidth, - showPreview, -}: Pick & Pick) => { +const BaseTapZoneLayout = ({ readerNavBarWidth }: Pick) => { const theme = useTheme(); const { tapZoneLayout, tapZoneInvertMode, readingDirection } = useReaderStoreShallow((state) => ({ tapZoneLayout: state.settings.tapZoneLayout.value, tapZoneInvertMode: state.settings.tapZoneInvertMode.value, readingDirection: state.settings.readingDirection.value, })); + const showPreview = useReaderStore((state) => state.tapZone.showPreview); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); @@ -71,8 +67,4 @@ const BaseTapZoneLayout = ({ ); }; -export const TapZoneLayout = withPropsFrom( - BaseTapZoneLayout, - [useNavBarContext, useReaderTapZoneContext], - ['readerNavBarWidth', 'showPreview'], -); +export const TapZoneLayout = withPropsFrom(BaseTapZoneLayout, [useNavBarContext], ['readerNavBarWidth']); diff --git a/src/features/reader/viewer/ReaderViewer.tsx b/src/features/reader/viewer/ReaderViewer.tsx index 54271f77..eac38085 100644 --- a/src/features/reader/viewer/ReaderViewer.tsx +++ b/src/features/reader/viewer/ReaderViewer.tsx @@ -38,8 +38,6 @@ import { import { useMouseDragScroll } from '@/base/hooks/useMouseDragScroll.tsx'; import { applyStyles } from '@/base/utils/ApplyStyles.ts'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; -import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; -import { useReaderTapZoneContext } from '@/features/reader/tap-zones/ReaderTapZoneContext.tsx'; import { useReaderAutoScroll } from '@/features/reader/auto-scroll/hooks/useReaderAutoScroll.ts'; import { useReaderHideOverlayOnUserScroll } from '@/features/reader/overlay/hooks/useReaderHideOverlayOnUserScroll.ts'; import { useReaderHorizontalModeInvertXYScrolling } from '@/features/reader/viewer/hooks/useReaderHorizontalModeInvertXYScrolling.ts'; @@ -73,12 +71,9 @@ const BaseReaderViewer = forwardRef( { readerNavBarWidth, updateCurrentPageIndex, - showPreview, - setShowPreview, - }: Pick & - TReaderTapZoneContext & { - updateCurrentPageIndex: ReturnType; - }, + }: Pick & { + updateCurrentPageIndex: ReturnType; + }, ref: ForwardedRef, ) => { @@ -149,6 +144,10 @@ const BaseReaderViewer = forwardRef( shouldStretchPage: state.settings.shouldStretchPage.value, isStaticNav: state.settings.isStaticNav, })); + const { showPreview, setShowPreview } = useReaderStoreShallow((state) => ({ + showPreview: state.tapZone.showPreview, + setShowPreview: state.tapZone.setShowPreview, + })); const { resumeMode = ReaderResumeMode.START } = useLocation().state ?? { resumeMode: ReaderResumeMode.START, }; @@ -435,10 +434,6 @@ const BaseReaderViewer = forwardRef( export const ReaderViewer = withPropsFrom( memo(BaseReaderViewer), - [ - () => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }), - useReaderTapZoneContext, - useNavBarContext, - ], - ['readerNavBarWidth', 'updateCurrentPageIndex', 'showPreview', 'setShowPreview'], + [() => ({ updateCurrentPageIndex: ReaderControls.useUpdateCurrentPageIndex() }), useNavBarContext], + ['readerNavBarWidth', 'updateCurrentPageIndex'], );