/* * 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 { useTheme } from '@mui/material/styles'; import { memo, useCallback, useState } from 'react'; import { ReaderProgressBar } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.tsx'; import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ProgressBarType } from '@/features/reader/Reader.types.ts'; import { applyStyles } from '@/base/utils/ApplyStyles.ts'; import { getProgressBarPositionInfo } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx'; import { ReaderProgressBarDirectionWrapper } from '@/features/reader/overlay/progress-bar/components/ReaderProgressBarDirectionWrapper.tsx'; import { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx'; import { ReaderProgressBarSlotDesktop } from '@/features/reader/overlay/progress-bar/desktop/components/ReaderProgressBarSlotDesktop.tsx'; import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx'; import { getProgressBarPosition } from '@/features/reader/settings/ReaderSettings.utils.tsx'; import { useReaderStore } from '@/features/reader/stores/ReaderStore.ts'; const BaseStandardReaderProgressBar = ({ readerNavBarWidth, readerDirection, }: Pick & { readerDirection: ReturnType; }) => { const theme = useTheme(); const scrollbar = useReaderStore((state) => state.scrollbar); const totalPages = useReaderStore((state) => state.pages.totalPages); const { progressBarType, progressBarSize, progressBarPosition, progressBarPositionAutoVertical } = useReaderStore( (state) => ({ progressBarType: state.settings.progressBarType, progressBarSize: state.settings.progressBarSize, progressBarPosition: state.settings.progressBarPosition, progressBarPositionAutoVertical: state.settings.progressBarPositionAutoVertical, }), ); const { isMaximized, setIsMaximized, isDragging } = useReaderStore((state) => ({ isMaximized: state.progressBar.isMaximized, setIsMaximized: state.progressBar.setIsMaximized, isDragging: state.progressBar.isDragging, })); const [, setRefreshProgressBarPosition] = useState({}); useResizeObserver( window.document.documentElement, useCallback(() => setRefreshProgressBarPosition({}), []), ); const finalProgressBarPosition = getProgressBarPosition( progressBarPosition, progressBarPositionAutoVertical, scrollbar.ySize, readerNavBarWidth + scrollbar.xSize, ); const { isBottom, isLeft, isRight, isVertical, isHorizontal } = getProgressBarPositionInfo(finalProgressBarPosition); const arePagesLoaded = !!totalPages; const isHidden = progressBarType === ProgressBarType.HIDDEN; const isMinimized = !isMaximized && !isDragging; // the progress bar uses the reading direction to set the themes direction, thus, stuff has to be adjusted to still be correctly positioned // depending on which combination of theme direction and reading direction is currently active return ( ( ), [finalProgressBarPosition], )} slotProps={{ container: { sx: { overflow: 'hidden', transition: `all 0.${theme.transitions.duration.shortest}s`, ...applyStyles(isHorizontal, { minHeight: '100px', ...applyStyles(theme.direction === 'ltr', { left: readerDirection === 'ltr' ? readerNavBarWidth : scrollbar.ySize, right: readerDirection === 'rtl' ? readerNavBarWidth : scrollbar.ySize, }), ...applyStyles(theme.direction === 'rtl', { left: readerDirection === 'rtl' ? readerNavBarWidth : scrollbar.ySize, right: readerDirection === 'ltr' ? readerNavBarWidth : scrollbar.ySize, }), }), ...applyStyles(isVertical, { minWidth: '100px', bottom: `${scrollbar.xSize}px`, }), ...applyStyles(isBottom, { bottom: `${scrollbar.xSize}px`, }), ...applyStyles(isLeft, { ...applyStyles(theme.direction === 'ltr', { left: readerDirection === 'ltr' ? readerNavBarWidth : 'unset', right: readerDirection === 'rtl' ? readerNavBarWidth : 'unset', }), ...applyStyles(theme.direction === 'rtl', { right: readerDirection === 'rtl' ? scrollbar.ySize : 'unset', left: readerDirection === 'ltr' ? scrollbar.ySize : 'unset', }), }), ...applyStyles(isRight, { ...applyStyles(theme.direction === 'ltr', { right: readerDirection === 'ltr' ? scrollbar.ySize : 'unset', left: readerDirection === 'rtl' ? scrollbar.ySize : 'unset', }), ...applyStyles(theme.direction === 'rtl', { left: readerDirection === 'rtl' ? readerNavBarWidth : 'unset', right: readerDirection === 'ltr' ? readerNavBarWidth : 'unset', }), }), ...applyStyles(isMinimized, { opacity: !isHidden ? 0.85 : 0, }), ...applyStyles(!arePagesLoaded, { pointerEvents: 'none', }), }, onMouseEnter: () => setIsMaximized(true), onMouseLeave: () => setIsMaximized(false), }, progressBarRoot: { sx: { gap: 0, transition: 'all 0.1s ease-in-out', backgroundColor: 'background.paper', ...applyStyles(isVertical, { flexDirection: 'column', height: '100%', }), ...applyStyles(isMinimized, { backgroundColor: 'unset', pointerEvents: 'none', ...applyStyles(isHorizontal, { px: 2, }), ...applyStyles(isVertical, { py: 2, }), ...applyStyles(isBottom, { pb: 0.25, }), ...applyStyles(isLeft, { ...applyStyles(theme.direction === 'ltr', { pl: readerDirection === 'ltr' ? 0.25 : 0, pr: readerDirection === 'rtl' ? 0.25 : 0, }), ...applyStyles(theme.direction === 'rtl', { pr: readerDirection === 'rtl' ? 0.25 : 0, pl: readerDirection === 'ltr' ? 0.25 : 0, }), }), ...applyStyles(isRight, { ...applyStyles(theme.direction === 'ltr', { pr: readerDirection === 'ltr' ? 0.25 : 0, pl: readerDirection === 'rtl' ? 0.25 : 0, }), ...applyStyles(theme.direction === 'rtl', { pl: readerDirection === 'rtl' ? 0.25 : 0, pr: readerDirection === 'ltr' ? 0.25 : 0, }), }), }), }, }, progressBarSlotsContainer: { sx: { gap: 0.25, borderRadius: 2, transition: 'height 0.1s ease-in-out', ...applyStyles(isHorizontal, { height: '20px', }), ...applyStyles(isVertical, { flexDirection: 'column', width: '20px', }), ...applyStyles(isMinimized, { ...applyStyles(isHorizontal, { height: `${progressBarSize}px`, }), ...applyStyles(isVertical, { width: `${progressBarSize}px`, }), }), }, }, progressBarReadPages: { sx: { display: 'none' } }, progressBarCurrentPageSlot: { sx: { display: 'none' } }, progressBarPageTexts: { base: { sx: { px: 1, py: 1.5, transition: 'all 0.1s ease-in-out', ...applyStyles(isMinimized, { transform: 'scale(0)', p: 0, minWidth: 0, maxWidth: 0, minHeight: 0, maxHeight: 0, }), }, }, }, }} /> ); }; export const StandardReaderProgressBar = withPropsFrom( memo(BaseStandardReaderProgressBar), [useNavBarContext, () => ({ readerDirection: ReaderService.useGetThemeDirection() })], ['readerNavBarWidth', 'readerDirection'], );