diff --git a/src/features/reader/auto-scroll/ReaderAutoScroll.tsx b/src/features/reader/auto-scroll/ReaderAutoScroll.tsx index 6bf1fe8d..af4168e6 100644 --- a/src/features/reader/auto-scroll/ReaderAutoScroll.tsx +++ b/src/features/reader/auto-scroll/ReaderAutoScroll.tsx @@ -31,10 +31,7 @@ const BaseReaderAutoScroll = ({ themeDirection: Direction; combinedDirection: Direction; }) => { - const { scrollRef, direction } = useReaderAutoScrollStore((state) => ({ - scrollRef: state.scrollRef, - direction: state.direction, - })); + const { scrollRef, direction } = useReaderAutoScrollStore('scrollRef', 'direction'); const { readingMode, autoScroll } = useReaderSettingsStore((state) => ({ readingMode: state.readingMode.value, autoScroll: state.autoScroll, diff --git a/src/features/reader/auto-scroll/hooks/useReaderAutoScroll.ts b/src/features/reader/auto-scroll/hooks/useReaderAutoScroll.ts index 361f70e6..677b6b45 100644 --- a/src/features/reader/auto-scroll/hooks/useReaderAutoScroll.ts +++ b/src/features/reader/auto-scroll/hooks/useReaderAutoScroll.ts @@ -11,9 +11,7 @@ import type { IReaderSettings } from '@/features/reader/Reader.types.ts'; import { getReaderAutoScrollStore, useReaderAutoScrollStore } from '@/features/reader/stores/ReaderStore.ts'; export const useReaderAutoScroll = (isOverlayVisible: boolean, isStaticNav: IReaderSettings['isStaticNav']): void => { - const { isPaused } = useReaderAutoScrollStore((state) => ({ - isPaused: state.isPaused, - })); + const isPaused = useReaderAutoScrollStore('isPaused'); useEffect(() => { const { pause, resume } = getReaderAutoScrollStore(); diff --git a/src/features/reader/auto-scroll/settings/quick-setting/ReaderNavBarDesktopAutoScroll.tsx b/src/features/reader/auto-scroll/settings/quick-setting/ReaderNavBarDesktopAutoScroll.tsx index 63d609dc..feaa479b 100644 --- a/src/features/reader/auto-scroll/settings/quick-setting/ReaderNavBarDesktopAutoScroll.tsx +++ b/src/features/reader/auto-scroll/settings/quick-setting/ReaderNavBarDesktopAutoScroll.tsx @@ -28,7 +28,7 @@ export const ReaderNavBarDesktopAutoScroll = ({ setAutoScroll: (newAutoScroll: IReaderSettings['autoScroll'], commit: boolean) => void; }) => { const { t } = useLingui(); - const isActive = useReaderAutoScrollStore((state) => state.isActive); + const isActive = useReaderAutoScrollStore('isActive'); const updateTimeout = useRef(undefined); diff --git a/src/features/reader/hotkeys/ReaderHotkeys.tsx b/src/features/reader/hotkeys/ReaderHotkeys.tsx index f31eb8fa..fbdf6324 100644 --- a/src/features/reader/hotkeys/ReaderHotkeys.tsx +++ b/src/features/reader/hotkeys/ReaderHotkeys.tsx @@ -66,9 +66,7 @@ export const ReaderHotkeys = ({ }) => { const readerThemeDirection = ReaderService.useGetThemeDirection(); const { enableScope, disableScope } = useHotkeysContext(); - const { hotkeys } = useReaderSettingsStore((state) => ({ - hotkeys: state.hotkeys, - })); + const hotkeys = useReaderSettingsStore('hotkeys'); const exitReader = ReaderService.useExit(); useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous')); diff --git a/src/features/reader/overlay/ReaderOverlay.tsx b/src/features/reader/overlay/ReaderOverlay.tsx index ed775148..1cb52cf4 100644 --- a/src/features/reader/overlay/ReaderOverlay.tsx +++ b/src/features/reader/overlay/ReaderOverlay.tsx @@ -23,7 +23,7 @@ const BaseReaderOverlay = ({ isDesktop, isMobile, }: Pick, 'isDesktop' | 'isMobile'>) => { - const isVisible = useReaderOverlayStore((state) => state.isVisible); + const isVisible = useReaderOverlayStore('isVisible'); const [areSettingsOpen, setAreSettingsOpen] = useState(false); diff --git a/src/features/reader/overlay/components/ReaderPageNumber.tsx b/src/features/reader/overlay/components/ReaderPageNumber.tsx index c84cc880..b2f92424 100644 --- a/src/features/reader/overlay/components/ReaderPageNumber.tsx +++ b/src/features/reader/overlay/components/ReaderPageNumber.tsx @@ -30,17 +30,13 @@ const BaseReaderPageNumber = ({ }: Pick, 'isDesktop'> & Pick) => { const scrollbar = useReaderScrollbarStore((state) => state); - const { currentPageIndex, pages, totalPages } = useReaderPagesStore((state) => ({ - currentPageIndex: state.currentPageIndex, - pages: state.pages, - totalPages: state.totalPages, - })); + const { currentPageIndex, pages, totalPages } = useReaderPagesStore('currentPageIndex', 'pages', 'totalPages'); const { readingDirection, shouldShowPageNumber, progressBarType } = useReaderSettingsStore((state) => ({ readingDirection: state.readingDirection.value, shouldShowPageNumber: state.shouldShowPageNumber, progressBarType: state.progressBarType, })); - const isMaximized = useReaderProgressBarStore((state) => state.isMaximized); + const isMaximized = useReaderProgressBarStore('isMaximized'); const pageName = useMemo(() => { const currentPageName = getPage(currentPageIndex, pages).name; diff --git a/src/features/reader/overlay/hooks/useReaderHideOverlayOnUserScroll.ts b/src/features/reader/overlay/hooks/useReaderHideOverlayOnUserScroll.ts index 60912960..60052a84 100644 --- a/src/features/reader/overlay/hooks/useReaderHideOverlayOnUserScroll.ts +++ b/src/features/reader/overlay/hooks/useReaderHideOverlayOnUserScroll.ts @@ -18,9 +18,7 @@ export const useReaderHideOverlayOnUserScroll = ( isOverlayVisible: boolean, scrollElementRef: MutableRefObject, ) => { - const { showPreview } = useReaderTapZoneStore((state) => ({ - showPreview: state.showPreview, - })); + const showPreview = useReaderTapZoneStore('showPreview'); useEffect(() => { const handleScroll = () => { diff --git a/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx b/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx index cecd19cd..07970431 100644 --- a/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx +++ b/src/features/reader/overlay/mobile/ReaderOverlayHeaderMobile.tsx @@ -42,9 +42,9 @@ const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' }; const BaseReaderOverlayHeaderMobile = ({ isVisible, ref }: MobileHeaderProps & { ref?: Ref }) => { const { t } = useLingui(); const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' }); - const currentChapter = useReaderChaptersStore((state) => state.currentChapter); + const currentChapter = useReaderChaptersStore('currentChapter'); - const manga = useReaderStore((state) => state.manga); + const manga = useReaderStore('manga'); const scrollbar = useReaderScrollbarStore((state) => state); const { id: mangaId, title } = manga ?? DEFAULT_MANGA; diff --git a/src/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx b/src/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx index 2b2febd9..7d92be40 100644 --- a/src/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx +++ b/src/features/reader/overlay/navigation/components/ReaderLibraryButton.tsx @@ -23,7 +23,7 @@ const ACTION_FALLBACK_MANGA = { }; export const ReaderLibraryButton = memo(() => { - const manga = useReaderStore((state) => state.manga); + const manga = useReaderStore('manga'); const { inLibrary } = manga ?? ACTION_FALLBACK_MANGA; diff --git a/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx b/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx index 07faaef9..15dbf60c 100644 --- a/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx +++ b/src/features/reader/overlay/navigation/desktop/ReaderNavBarDesktop.tsx @@ -59,7 +59,7 @@ const BaseReaderNavBarDesktop = ({ setReaderNavBarWidth, }: ReaderNavBarDesktopProps & Pick) => { const { t } = useLingui(); - const manga = useReaderStore((state) => state.manga); + const manga = useReaderStore('manga'); const { chapters, currentChapterId, @@ -77,7 +77,7 @@ const BaseReaderNavBarDesktop = ({ previousChapter: state.previousChapter, nextChapter: state.nextChapter, })); - const isStaticNav = useReaderSettingsStore((state) => state.isStaticNav); + const isStaticNav = useReaderSettingsStore('isStaticNav'); const [navBarElement, setNavBarElement] = useState(); useResizeObserver( diff --git a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx index 8f03b12c..3541770b 100644 --- a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx +++ b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopActions.tsx @@ -72,7 +72,7 @@ export const ReaderNavBarDesktopActions = memo(() => { })); const { t } = useLingui(); - const pageLoadStates = useReaderPagesStore((state) => state.pageLoadStates); + const pageLoadStates = useReaderPagesStore('pageLoadStates'); const pageRetryKeyPrefix = useRef(0); diff --git a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopPageNavigation.tsx b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopPageNavigation.tsx index 42d397e4..f5e14121 100644 --- a/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopPageNavigation.tsx +++ b/src/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopPageNavigation.tsx @@ -23,10 +23,7 @@ import { ReaderControls } from '@/features/reader/services/ReaderControls.ts'; const BaseReaderNavBarDesktopPageNavigation = () => { const { t } = useLingui(); const getOptionForDirection = useGetOptionForDirection(); - const { currentPageIndex, pages } = useReaderPagesStore((state) => ({ - currentPageIndex: state.currentPageIndex, - pages: state.pages, - })); + const { currentPageIndex, pages } = useReaderPagesStore('currentPageIndex', 'pages'); const readingDirection = useReaderSettingsStore((state) => state.readingDirection.value); const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]); diff --git a/src/features/reader/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx b/src/features/reader/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx index 52310ce9..3d032894 100644 --- a/src/features/reader/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx +++ b/src/features/reader/overlay/navigation/desktop/quick-settings/ReaderNavBarDesktopQuickSettings.tsx @@ -23,14 +23,14 @@ import { useReaderSettingsStore } from '@/features/reader/stores/ReaderStore.ts' const BaseReaderNavBarDesktopQuickSettings = ({ openSettings }: Pick) => { const { t } = useLingui(); const { readingMode, shouldOffsetDoubleSpreads, pageScaleMode, shouldStretchPage, readingDirection, autoScroll } = - useReaderSettingsStore((state) => ({ - readingMode: state.readingMode, - shouldOffsetDoubleSpreads: state.shouldOffsetDoubleSpreads, - pageScaleMode: state.pageScaleMode, - shouldStretchPage: state.shouldStretchPage, - readingDirection: state.readingDirection, - autoScroll: state.autoScroll, - })); + useReaderSettingsStore( + 'readingMode', + 'shouldOffsetDoubleSpreads', + 'pageScaleMode', + 'shouldStretchPage', + 'readingDirection', + 'autoScroll', + ); return ( diff --git a/src/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx b/src/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx index 2933b82a..13ab896e 100644 --- a/src/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx +++ b/src/features/reader/overlay/navigation/mobile/quick-settings/ReaderBottomBarMobileQuickSettings.tsx @@ -23,12 +23,12 @@ import { const BaseReaderBottomBarMobileQuickSettings = () => { const { t } = useLingui(); - const isActive = useReaderAutoScrollStore((state) => state.isActive); - const { readingMode, readingDirection, autoScroll } = useReaderSettingsStore((state) => ({ - readingMode: state.readingMode, - readingDirection: state.readingDirection, - autoScroll: state.autoScroll, - })); + const isActive = useReaderAutoScrollStore('isActive'); + const { readingMode, readingDirection, autoScroll } = useReaderSettingsStore( + 'readingMode', + 'readingDirection', + 'autoScroll', + ); return ( diff --git a/src/features/reader/overlay/progress-bar/ReaderProgressBar.tsx b/src/features/reader/overlay/progress-bar/ReaderProgressBar.tsx index 3c0d517a..1cf947c5 100644 --- a/src/features/reader/overlay/progress-bar/ReaderProgressBar.tsx +++ b/src/features/reader/overlay/progress-bar/ReaderProgressBar.tsx @@ -68,14 +68,14 @@ const BaseReaderProgressBar = ({ direction: ReturnType; fullSegmentClicks: boolean; }) => { - const { pages, pageLoadStates, totalPages, currentPageIndex } = useReaderPagesStore((state) => ({ - pages: state.pages, - pageLoadStates: state.pageLoadStates, - totalPages: state.totalPages, - currentPageIndex: state.currentPageIndex, - })); + const { pages, pageLoadStates, totalPages, currentPageIndex } = useReaderPagesStore( + 'pages', + 'pageLoadStates', + 'totalPages', + 'currentPageIndex', + ); const readingMode = useReaderSettingsStore((state) => state.readingMode.value); - const isDragging = useReaderProgressBarStore((state) => state.isDragging); + const isDragging = useReaderProgressBarStore('isDragging'); const progressBarRef = useRef(null); const draggingDetectionTimeout = useRef(undefined); diff --git a/src/features/reader/overlay/progress-bar/desktop/StandardReaderProgressBar.tsx b/src/features/reader/overlay/progress-bar/desktop/StandardReaderProgressBar.tsx index 930e3b38..6e908580 100644 --- a/src/features/reader/overlay/progress-bar/desktop/StandardReaderProgressBar.tsx +++ b/src/features/reader/overlay/progress-bar/desktop/StandardReaderProgressBar.tsx @@ -37,18 +37,15 @@ const BaseStandardReaderProgressBar = ({ const theme = useTheme(); const scrollbar = useReaderScrollbarStore((state) => state); - const totalPages = useReaderPagesStore((state) => state.totalPages); + const totalPages = useReaderPagesStore('totalPages'); const { progressBarType, progressBarSize, progressBarPosition, progressBarPositionAutoVertical } = - useReaderSettingsStore((state) => ({ - progressBarType: state.progressBarType, - progressBarSize: state.progressBarSize, - progressBarPosition: state.progressBarPosition, - progressBarPositionAutoVertical: state.progressBarPositionAutoVertical, - })); - const { isMaximized, isDragging } = useReaderProgressBarStore((state) => ({ - isMaximized: state.isMaximized, - isDragging: state.isDragging, - })); + useReaderSettingsStore( + 'progressBarType', + 'progressBarSize', + 'progressBarPosition', + 'progressBarPositionAutoVertical', + ); + const { isMaximized, isDragging } = useReaderProgressBarStore('isMaximized', 'isDragging'); const [, setRefreshProgressBarPosition] = useState({}); useResizeObserver( diff --git a/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx b/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx index 7e85882f..8a018ac7 100644 --- a/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx +++ b/src/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx @@ -59,20 +59,14 @@ const BaseMobileReaderProgressBar = ({ bottomOffset?: number; }) => { const scrollbar = useReaderScrollbarStore((state) => state); - const isVisible = useReaderOverlayStore((state) => state.isVisible); - const { currentPageIndex, pages } = useReaderPagesStore((state) => ({ - currentPageIndex: state.currentPageIndex, - pages: state.pages, - })); - const { previousChapter, nextChapter } = useReaderChaptersStore((state) => ({ - previousChapter: state.previousChapter, - nextChapter: state.nextChapter, - })); - const { progressBarPosition, progressBarPositionAutoVertical } = useReaderSettingsStore((state) => ({ - progressBarPosition: state.progressBarPosition, - progressBarPositionAutoVertical: state.progressBarPositionAutoVertical, - })); - const isDragging = useReaderProgressBarStore((state) => state.isDragging); + const isVisible = useReaderOverlayStore('isVisible'); + const { currentPageIndex, pages } = useReaderPagesStore('currentPageIndex', 'pages'); + const { previousChapter, nextChapter } = useReaderChaptersStore('previousChapter', 'nextChapter'); + const { progressBarPosition, progressBarPositionAutoVertical } = useReaderSettingsStore( + 'progressBarPosition', + 'progressBarPositionAutoVertical', + ); + const isDragging = useReaderProgressBarStore('isDragging'); const [, setRefreshProgressBarPosition] = useState({}); useResizeObserver( diff --git a/src/features/reader/screens/Reader.tsx b/src/features/reader/screens/Reader.tsx index 8265dc89..4b9a7674 100644 --- a/src/features/reader/screens/Reader.tsx +++ b/src/features/reader/screens/Reader.tsx @@ -49,14 +49,12 @@ const BaseReader = ({ readerNavBarWidth, }: Pick) => { const { t } = useLingui(); - const manga = useReaderStore((state) => state.manga); + const manga = useReaderStore('manga'); const { mangaChapters, initialChapter, chapterForDuplicatesHandling, currentChapter } = useReaderChaptersStore( - (state) => ({ - mangaChapters: state.mangaChapters, - initialChapter: state.initialChapter, - chapterForDuplicatesHandling: state.chapterForDuplicatesHandling, - currentChapter: state.currentChapter, - }), + 'mangaChapters', + 'initialChapter', + 'chapterForDuplicatesHandling', + 'currentChapter', ); const { shouldSkipDupChapters, @@ -67,16 +65,16 @@ const BaseReader = ({ tapZoneInvertMode, shouldShowReadingModePreview, shouldShowTapZoneLayoutPreview, - } = useReaderSettingsStore((state) => ({ - shouldSkipDupChapters: state.shouldSkipDupChapters, - shouldSkipFilteredChapters: state.shouldSkipFilteredChapters, - backgroundColor: state.backgroundColor, - readingMode: state.readingMode, - tapZoneLayout: state.tapZoneLayout, - tapZoneInvertMode: state.tapZoneInvertMode, - shouldShowReadingModePreview: state.shouldShowReadingModePreview, - shouldShowTapZoneLayoutPreview: state.shouldShowTapZoneLayoutPreview, - })); + } = useReaderSettingsStore( + 'shouldSkipDupChapters', + 'shouldSkipFilteredChapters', + 'backgroundColor', + 'readingMode', + 'tapZoneLayout', + 'tapZoneInvertMode', + 'shouldShowReadingModePreview', + 'shouldShowTapZoneLayoutPreview', + ); const scrollElementRef = useRef(null); diff --git a/src/features/reader/services/ReaderService.ts b/src/features/reader/services/ReaderService.ts index 93097c32..48e0a1b3 100644 --- a/src/features/reader/services/ReaderService.ts +++ b/src/features/reader/services/ReaderService.ts @@ -425,7 +425,7 @@ export class ReaderService { static useOverlayMode(): { mode: ReaderOverlayMode; isDesktop: boolean; isMobile: boolean } { const isTouchDevice = MediaQuery.useIsTouchDevice(); - const overlayMode = useReaderSettingsStore((state) => state.overlayMode); + const overlayMode = useReaderSettingsStore('overlayMode'); const isAutoModeSelected = overlayMode === ReaderOverlayMode.AUTO; const isDesktopModeSelected = overlayMode === ReaderOverlayMode.DESKTOP; @@ -442,7 +442,7 @@ export class ReaderService { } static useExit(): () => void { - const exitMode = useReaderSettingsStore((state) => state.exitMode); + const exitMode = useReaderSettingsStore('exitMode'); const handleBack = useBackButton(); const navigate = useNavigate(); diff --git a/src/features/reader/stores/ReaderStore.ts b/src/features/reader/stores/ReaderStore.ts index d8d04e31..9c35f1a8 100644 --- a/src/features/reader/stores/ReaderStore.ts +++ b/src/features/reader/stores/ReaderStore.ts @@ -9,7 +9,6 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; -import { useShallow } from 'zustand/react/shallow'; import type { TMangaReader } from '@/features/manga/Manga.types.ts'; import type { ReaderOverlayStoreSlice } from '@/features/reader/overlay/ReaderOverlayStore.ts'; import { createReaderOverlayStoreSlice } from '@/features/reader/overlay/ReaderOverlayStore.ts'; @@ -156,37 +155,29 @@ const readerStore = create()( }, ), ); -export const useReaderStore = (selector: (state: ReaderStore) => T): T => readerStore(useShallow(selector)); +export const useReaderStore = ZustandUtil.createStoreHook(readerStore); export const getReaderStore = () => readerStore.getState(); -export const useReaderScrollbarStore = (selector: (state: ReaderStore['scrollbar']) => T): T => - readerStore(useShallow((state) => selector(state.scrollbar))); +export const useReaderScrollbarStore = ZustandUtil.createStoreHook(readerStore, 'scrollbar'); export const getReaderScrollbarStore = () => readerStore.getState().scrollbar; -export const useReaderSettingsStore = (selector: (state: ReaderStore['settings']) => T): T => - readerStore(useShallow((state) => selector(state.settings))); +export const useReaderSettingsStore = ZustandUtil.createStoreHook(readerStore, 'settings'); export const getReaderSettingsStore = () => readerStore.getState().settings; -export const useReaderOverlayStore = (selector: (state: ReaderOverlayStoreSlice['overlay']) => T): T => - readerStore(useShallow((state) => selector(state.overlay))); +export const useReaderOverlayStore = ZustandUtil.createStoreHook(readerStore, 'overlay'); export const getReaderOverlayStore = () => readerStore.getState().overlay; -export const useReaderAutoScrollStore = (selector: (state: ReaderAutoScrollStoreSlice['autoScroll']) => T): T => - readerStore(useShallow((state) => selector(state.autoScroll))); +export const useReaderAutoScrollStore = ZustandUtil.createStoreHook(readerStore, 'autoScroll'); export const getReaderAutoScrollStore = () => readerStore.getState().autoScroll; -export const useReaderPagesStore = (selector: (state: ReaderPagesStoreSlice['pages']) => T): T => - readerStore(useShallow((state) => selector(state.pages))); +export const useReaderPagesStore = ZustandUtil.createStoreHook(readerStore, 'pages'); export const getReaderPagesStore = () => readerStore.getState().pages; -export const useReaderChaptersStore = (selector: (state: ReaderChaptersStoreSlice['chapters']) => T): T => - readerStore(useShallow((state) => selector(state.chapters))); +export const useReaderChaptersStore = ZustandUtil.createStoreHook(readerStore, 'chapters'); export const getReaderChaptersStore = () => readerStore.getState().chapters; -export const useReaderProgressBarStore = (selector: (state: ReaderProgressBarStoreSlice['progressBar']) => T): T => - readerStore(useShallow((state) => selector(state.progressBar))); +export const useReaderProgressBarStore = ZustandUtil.createStoreHook(readerStore, 'progressBar'); export const getReaderProgressBarStore = () => readerStore.getState().progressBar; -export const useReaderTapZoneStore = (selector: (state: ReaderTapZoneStoreSlice['tapZone']) => T): T => - readerStore(useShallow((state) => selector(state.tapZone))); +export const useReaderTapZoneStore = ZustandUtil.createStoreHook(readerStore, 'tapZone'); export const getReaderTapZoneStore = () => readerStore.getState().tapZone; diff --git a/src/features/reader/tap-zones/TapZoneLayout.tsx b/src/features/reader/tap-zones/TapZoneLayout.tsx index ba0de556..9b4948de 100644 --- a/src/features/reader/tap-zones/TapZoneLayout.tsx +++ b/src/features/reader/tap-zones/TapZoneLayout.tsx @@ -26,7 +26,7 @@ const BaseTapZoneLayout = ({ readerNavBarWidth }: Pick state.showPreview); + const showPreview = useReaderTapZoneStore('showPreview'); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); diff --git a/src/features/reader/viewer/ReaderViewer.tsx b/src/features/reader/viewer/ReaderViewer.tsx index 4a66c0e5..d71fe6ae 100644 --- a/src/features/reader/viewer/ReaderViewer.tsx +++ b/src/features/reader/viewer/ReaderViewer.tsx @@ -68,24 +68,22 @@ const BaseReaderViewer = ({ ref?: ForwardedRef; }) => { const { direction: themeDirection } = useTheme(); - const isOverlayVisible = useReaderOverlayStore((state) => state.isVisible); + const isOverlayVisible = useReaderOverlayStore('isVisible'); const { currentPageIndex, pageToScrollToIndex, pages, totalPages, transitionPageMode, retryFailedPagesKeyPrefix } = - useReaderPagesStore((state) => ({ - currentPageIndex: state.currentPageIndex, - pageToScrollToIndex: state.pageToScrollToIndex, - pages: state.pages, - totalPages: state.totalPages, - transitionPageMode: state.transitionPageMode, - retryFailedPagesKeyPrefix: state.retryFailedPagesKeyPrefix, - })); + useReaderPagesStore( + 'currentPageIndex', + 'pageToScrollToIndex', + 'pages', + 'totalPages', + 'transitionPageMode', + 'retryFailedPagesKeyPrefix', + ); const { initialChapter, currentChapter, chapters, visibleChapters, isCurrentChapterReady } = useReaderChaptersStore( - (state) => ({ - initialChapter: state.initialChapter, - currentChapter: state.currentChapter, - chapters: state.chapters, - visibleChapters: state.visibleChapters, - isCurrentChapterReady: state.isCurrentChapterReady, - }), + 'initialChapter', + 'currentChapter', + 'chapters', + 'visibleChapters', + 'isCurrentChapterReady', ); const { readingMode, diff --git a/src/features/reader/viewer/components/ReaderTransitionPage.tsx b/src/features/reader/viewer/components/ReaderTransitionPage.tsx index 4db2779e..fb048fd0 100644 --- a/src/features/reader/viewer/components/ReaderTransitionPage.tsx +++ b/src/features/reader/viewer/components/ReaderTransitionPage.tsx @@ -99,9 +99,9 @@ const BaseReaderTransitionPage = ({ handleBack: () => void; }) => { const { t } = useLingui(); - const manga = useReaderStore((state) => state.manga); + const manga = useReaderStore('manga'); const scrollbar = useReaderScrollbarStore((state) => state); - const transitionPageMode = useReaderPagesStore((state) => state.transitionPageMode); + const transitionPageMode = useReaderPagesStore('transitionPageMode'); const { readingMode, backgroundColor, shouldShowTransitionPage } = useReaderSettingsStore((state) => ({ readingMode: state.readingMode.value, backgroundColor: state.backgroundColor, @@ -226,7 +226,7 @@ export const ReaderTransitionPage = withPropsFrom( memo(BaseReaderTransitionPage) as typeof BaseReaderTransitionPage, [ ({ chapterId }: Pick, 'chapterId'>) => { - const chapters = useReaderChaptersStore((state) => state.chapters); + const chapters = useReaderChaptersStore('chapters'); const currentChapterIndex = useMemo( () => chapters.findIndex((chapter) => chapter.id === chapterId), @@ -249,7 +249,7 @@ export const ReaderTransitionPage = withPropsFrom( useNavBarContext, ({ chapterId, type }: Pick, 'chapterId' | 'type'>) => { const handleBack = useBackButton(); - const chapters = useReaderChaptersStore((state) => state.chapters); + const chapters = useReaderChaptersStore('chapters'); const currentChapterIndex = useMemo( () => chapters.findIndex((chapter) => chapter.id === chapterId), diff --git a/src/lib/zustand/ZustandUtil.ts b/src/lib/zustand/ZustandUtil.ts index 35abb236..b317ebdc 100644 --- a/src/lib/zustand/ZustandUtil.ts +++ b/src/lib/zustand/ZustandUtil.ts @@ -6,7 +6,64 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import { useShallow } from 'zustand/react/shallow'; + +type UseBoundStoreAny = (selector: (state: S) => T) => T; + +type StoreHook = { + (selector: (state: State) => T): T; + (key: Key): State[Key]; + (...keys: Key[]): Pick; +}; + export class ZustandUtil { + static createKeySelector(keys: Key[]): (state: State) => Pick { + return (state) => { + const result = {} as Pick; + + for (const key of keys) { + result[key] = state[key]; + } + + return result; + }; + } + + static createStoreHook( + store: UseBoundStoreAny, + sliceKey: SliceKey, + ): StoreHook; + static createStoreHook(store: UseBoundStoreAny): StoreHook; + static createStoreHook( + store: UseBoundStoreAny, + sliceKey?: keyof State, + ): StoreHook { + return ((...args: unknown[]) => { + let selector: (state: State) => unknown; + + if (typeof args[0] === 'function') { + const fn = args[0] as (state: unknown) => unknown; + selector = sliceKey !== undefined ? (state: State) => fn(state[sliceKey]) : fn; + } else if (args.length === 1) { + const key = args[0]; + selector = + sliceKey !== undefined + ? (state: State) => (state[sliceKey] as Record)[key as PropertyKey] + : (state: State) => (state as Record)[key as PropertyKey]; + } else { + const pickSelector = ZustandUtil.createKeySelector, PropertyKey>( + args as PropertyKey[], + ); + selector = + sliceKey !== undefined + ? (state: State) => pickSelector(state[sliceKey] as Record) + : (pickSelector as (state: State) => unknown); + } + + return store(useShallow(selector)); + }) as StoreHook; + } + static createActionName(...names: string[]) { const storeAndSlices = names.slice(0, -1); const action = names.slice(-1)[0];