Remove "forwardRef" usage

This commit is contained in:
schroda
2025-09-24 00:51:13 +02:00
parent bcb5e391f0
commit a2d7091712
19 changed files with 1055 additions and 1101 deletions

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { useState, useEffect, forwardRef, ForwardedRef, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef, Ref } from 'react';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
@@ -41,10 +41,11 @@ export interface SpinnerImageProps {
priority?: Priority; priority?: Priority;
retryKeyPrefix?: string; retryKeyPrefix?: string;
ref?: Ref<HTMLImageElement | HTMLDivElement | null>;
} }
export const SpinnerImage = forwardRef( export const SpinnerImage = ({ ref, ...props }: SpinnerImageProps) => {
(props: SpinnerImageProps, imgRef: ForwardedRef<HTMLImageElement | HTMLDivElement | null>) => {
const { const {
shouldLoad = true, shouldLoad = true,
shouldDecode, shouldDecode,
@@ -148,7 +149,7 @@ export const SpinnerImage = forwardRef(
<> <>
{showMissingImageIcon ? ( {showMissingImageIcon ? (
<Stack <Stack
ref={imgRef} ref={ref}
sx={{ sx={{
height: '100%', height: '100%',
alignItems: 'center', alignItems: 'center',
@@ -172,7 +173,7 @@ export const SpinnerImage = forwardRef(
}), }),
}), }),
]} ]}
ref={imgRef} ref={ref}
crossOrigin={disableCors ? undefined : 'anonymous'} crossOrigin={disableCors ? undefined : 'anonymous'}
src={imageSourceUrl} src={imageSourceUrl}
alt={alt} alt={alt}
@@ -221,5 +222,4 @@ export const SpinnerImage = forwardRef(
)} )}
</> </>
); );
}, };
);

View File

@@ -8,14 +8,12 @@
import Button, { ButtonProps } from '@mui/material/Button'; import Button, { ButtonProps } from '@mui/material/Button';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { ForwardedRef, forwardRef } from 'react';
export const CustomButton = forwardRef( export const CustomButton = <C extends React.ElementType>({
<C extends React.ElementType>( children,
{ children, ...props }: ButtonProps<C, { component?: C }>, ...props
ref: ForwardedRef<HTMLButtonElement | null>, }: ButtonProps<C, { component?: C }>) => (
) => ( <Button {...props}>
<Button ref={ref} {...props}>
<Stack <Stack
direction="row" direction="row"
sx={{ sx={{
@@ -28,5 +26,4 @@ export const CustomButton = forwardRef(
{children} {children}
</Stack> </Stack>
</Button> </Button>
),
); );

View File

@@ -7,15 +7,12 @@
*/ */
import Button, { ButtonProps } from '@mui/material/Button'; import Button, { ButtonProps } from '@mui/material/Button';
import { ForwardedRef, forwardRef } from 'react';
export const CustomButtonIcon = forwardRef( export const CustomButtonIcon = <C extends React.ElementType>({
<C extends React.ElementType>( children,
{ children, ...props }: ButtonProps<C, { component?: C }>, ...props
ref: ForwardedRef<HTMLButtonElement | null>, }: ButtonProps<C, { component?: C }>) => (
) => (
<Button <Button
ref={ref}
{...props} {...props}
sx={{ sx={{
minWidth: 'unset', minWidth: 'unset',
@@ -25,5 +22,4 @@ export const CustomButtonIcon = forwardRef(
> >
{children} {children}
</Button> </Button>
),
); );

View File

@@ -7,7 +7,7 @@
*/ */
import { closeSnackbar, CustomContentProps, SnackbarContent, VariantType } from 'notistack'; import { closeSnackbar, CustomContentProps, SnackbarContent, VariantType } from 'notistack';
import { ForwardedRef, forwardRef, Fragment, memo } from 'react'; import { ForwardedRef, Fragment, memo } from 'react';
import Alert from '@mui/material/Alert'; import Alert from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle'; import AlertTitle from '@mui/material/AlertTitle';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
@@ -30,20 +30,17 @@ const SNACKBAR_VARIANT_TO_TRANSLATION_KEY: Record<VariantType, TranslationKey> =
}; };
export const SnackbarWithDescription = memo( export const SnackbarWithDescription = memo(
forwardRef( ({
(
{
id, id,
message, message,
description, description,
variant, variant,
action, action,
ref,
}: CustomContentProps & { }: CustomContentProps & {
// eslint-disable-next-line react/no-unused-prop-types
description?: string; description?: string;
}, ref?: ForwardedRef<HTMLDivElement>;
ref: ForwardedRef<HTMLDivElement>, }) => {
) => {
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
@@ -113,5 +110,4 @@ export const SnackbarWithDescription = memo(
</SnackbarContent> </SnackbarContent>
); );
}, },
),
); );

View File

@@ -13,7 +13,7 @@
import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemIcon from '@mui/material/ListItemIcon';
import MenuItem, { MenuItemProps as MuiMenuItemProps } from '@mui/material/MenuItem'; import MenuItem, { MenuItemProps as MuiMenuItemProps } from '@mui/material/MenuItem';
import { SxProps, Theme } from '@mui/material/styles'; import { SxProps, Theme } from '@mui/material/styles';
import React, { forwardRef, RefObject } from 'react'; import React, { Ref } from 'react';
import { OverridableComponent } from '@mui/material/OverridableComponent'; import { OverridableComponent } from '@mui/material/OverridableComponent';
import { SvgIconTypeMap } from '@mui/material/SvgIcon'; import { SvgIconTypeMap } from '@mui/material/SvgIcon';
@@ -27,14 +27,21 @@ type IconMenuItemProps = {
renderLabel?: () => React.ReactNode; renderLabel?: () => React.ReactNode;
LeftIcon?: OverridableComponent<SvgIconTypeMap> & { muiName: string }; LeftIcon?: OverridableComponent<SvgIconTypeMap> & { muiName: string };
onClick?: (event: React.MouseEvent<HTMLElement>) => void; onClick?: (event: React.MouseEvent<HTMLElement>) => void;
ref?: RefObject<HTMLLIElement | null>; ref?: Ref<HTMLLIElement | null>;
RightIcon?: OverridableComponent<SvgIconTypeMap> & { muiName: string }; RightIcon?: OverridableComponent<SvgIconTypeMap> & { muiName: string };
sx?: SxProps<Theme>; sx?: SxProps<Theme>;
}; };
export const IconMenuItem = forwardRef<HTMLLIElement, IconMenuItemProps>( export const IconMenuItem = ({
({ MenuItemProps, className, label, LeftIcon, renderLabel, RightIcon, ...props }, ref) => ( MenuItemProps,
<MenuItem {...MenuItemProps} ref={ref} className={className} {...props}> className,
label,
LeftIcon,
renderLabel,
RightIcon,
...props
}: IconMenuItemProps) => (
<MenuItem {...MenuItemProps} className={className} {...props}>
{LeftIcon && ( {LeftIcon && (
<ListItemIcon> <ListItemIcon>
<LeftIcon fontSize="small" /> <LeftIcon fontSize="small" />
@@ -47,5 +54,4 @@ export const IconMenuItem = forwardRef<HTMLLIElement, IconMenuItemProps>(
</ListItemIcon> </ListItemIcon>
)} )}
</MenuItem> </MenuItem>
),
); );

View File

@@ -16,7 +16,6 @@ import Menu, { MenuProps as MuiMenuProps } from '@mui/material/Menu';
import { MenuItemProps as MuiMenuItemProps } from '@mui/material/MenuItem'; import { MenuItemProps as MuiMenuItemProps } from '@mui/material/MenuItem';
import { import {
ElementType, ElementType,
forwardRef,
HTMLAttributes, HTMLAttributes,
KeyboardEvent, KeyboardEvent,
FocusEvent, FocusEvent,
@@ -25,6 +24,7 @@ import {
RefAttributes, RefAttributes,
useRef, useRef,
useState, useState,
Ref,
} from 'react'; } from 'react';
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
@@ -51,9 +51,10 @@ export type NestedMenuItemProps = Omit<MuiMenuItemProps, 'button'> & {
ContainerProps?: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>; ContainerProps?: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>;
MenuProps?: Partial<Omit<MuiMenuProps, 'children'>>; MenuProps?: Partial<Omit<MuiMenuProps, 'children'>>;
button?: true | undefined; button?: true | undefined;
ref?: Ref<HTMLLIElement | null>;
}; };
const NestedMenuItem = forwardRef<HTMLLIElement | null, NestedMenuItemProps>((props, ref) => { export const NestedMenuItem = ({ ref, ...props }: NestedMenuItemProps) => {
const { const {
parentMenuOpen, parentMenuOpen,
label, label,
@@ -229,7 +230,4 @@ const NestedMenuItem = forwardRef<HTMLLIElement | null, NestedMenuItemProps>((pr
</Menu> </Menu>
</Box> </Box>
); );
}); };
NestedMenuItem.displayName = 'NestedMenuItem';
export { NestedMenuItem };

View File

@@ -8,7 +8,6 @@
import Tabs, { TabsProps } from '@mui/material/Tabs'; import Tabs, { TabsProps } from '@mui/material/Tabs';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import { ForwardedRef, forwardRef } from 'react';
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx'; import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
const StyledTabsMenu = styled(Tabs)(({ theme }) => ({ const StyledTabsMenu = styled(Tabs)(({ theme }) => ({
@@ -24,14 +23,12 @@ const StyledTabsMenu = styled(Tabs)(({ theme }) => ({
borderColor: theme.palette.divider, borderColor: theme.palette.divider,
})); }));
export const TabsMenu = forwardRef( export const TabsMenu = ({ children, sx, ...props }: TabsProps) => {
({ children, sx, ...props }: TabsProps, ref: ForwardedRef<HTMLDivElement | null>) => {
const { appBarHeight } = useNavBarContext(); const { appBarHeight } = useNavBarContext();
return ( return (
<StyledTabsMenu <StyledTabsMenu
sx={{ ...sx, top: appBarHeight }} sx={{ ...sx, top: appBarHeight }}
ref={ref}
indicatorColor="primary" indicatorColor="primary"
textColor="primary" textColor="primary"
variant="scrollable" variant="scrollable"
@@ -42,5 +39,4 @@ export const TabsMenu = forwardRef(
{children} {children}
</StyledTabsMenu> </StyledTabsMenu>
); );
}, };
);

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { ComponentType, memo, forwardRef } from 'react'; import { ComponentType, memo, Ref } from 'react';
type PropsSourceCreator<T, Props extends Record<string, any>> = (props: Props) => T; type PropsSourceCreator<T, Props extends Record<string, any>> = (props: Props) => T;
@@ -21,10 +21,12 @@ export const withPropsFrom = <
}, },
sourcePropKeys: SourcePropKeys[], sourcePropKeys: SourcePropKeys[],
) => ) =>
memo( memo(({ ref, ...props }: Omit<ComponentProps, SourcePropKeys> & { ref?: Ref<HTMLElement> }) => {
forwardRef<HTMLElement, Omit<ComponentProps, SourcePropKeys>>((props, ref) => {
const sourceProps = propsSources.reduce( const sourceProps = propsSources.reduce(
(acc, propsSource) => ({ ...acc, ...propsSource(props as Omit<ComponentProps, SourcePropKeys>) }), (acc, propsSource) => ({
...acc,
...propsSource(props as unknown as Omit<ComponentProps, SourcePropKeys>),
}),
{}, {},
); );
@@ -35,5 +37,4 @@ export const withPropsFrom = <
const combinedProps = { ...props, ...selectedProps } as unknown as ComponentProps; const combinedProps = { ...props, ...selectedProps } as unknown as ComponentProps;
return <Component {...combinedProps} ref={ref} />; return <Component {...combinedProps} ref={ref} />;
}), });
);

View File

@@ -6,16 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import React, { import React, { ForwardedRef, Ref, useCallback, useLayoutEffect, useMemo, useRef, useState, type JSX } from 'react';
ForwardedRef,
forwardRef,
useCallback,
useLayoutEffect,
useMemo,
useRef,
useState,
type JSX,
} from 'react';
import Grid, { GridTypeMap } from '@mui/material/Grid'; import Grid, { GridTypeMap } from '@mui/material/Grid';
import Box, { BoxProps } from '@mui/material/Box'; import Box, { BoxProps } from '@mui/material/Box';
import { GridItemProps } from 'react-virtuoso'; import { GridItemProps } from 'react-virtuoso';
@@ -33,11 +24,11 @@ import { GridLayout } from '@/base/Base.types.ts';
import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts';
import { VirtuosoGridPersisted } from '@/lib/virtuoso/Component/VirtuosoGridPersisted.tsx'; import { VirtuosoGridPersisted } from '@/lib/virtuoso/Component/VirtuosoGridPersisted.tsx';
const GridContainer = React.forwardRef<HTMLDivElement, GridTypeMap['props']>(({ children, ...props }, ref) => ( const GridContainer = ({ children, ref, ...props }: GridTypeMap['props'] & { ref?: Ref<HTMLDivElement> }) => (
<Grid {...props} ref={ref} container spacing={1}> <Grid {...props} ref={ref} container spacing={1}>
{children} {children}
</Grid> </Grid>
)); );
const GridItemContainerWithDimension = ( const GridItemContainerWithDimension = (
dimensions: number, dimensions: number,
@@ -86,11 +77,10 @@ type DefaultGridProps = Pick<MangaCardProps, 'mode'> & {
isSelectModeActive?: boolean; isSelectModeActive?: boolean;
selectedMangaIds?: Required<MangaType['id']>[]; selectedMangaIds?: Required<MangaType['id']>[];
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection']; handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
ref?: ForwardedRef<HTMLDivElement | null>;
}; };
const HorizontalGrid = forwardRef( const HorizontalGrid = ({
(
{
isLoading, isLoading,
mangas, mangas,
inLibraryIndicator, inLibraryIndicator,
@@ -100,9 +90,8 @@ const HorizontalGrid = forwardRef(
selectedMangaIds, selectedMangaIds,
handleSelection, handleSelection,
mode, mode,
}: DefaultGridProps, ref,
ref: ForwardedRef<HTMLDivElement | null>, }: DefaultGridProps) => (
) => (
<Grid <Grid
ref={ref} ref={ref}
container container
@@ -132,14 +121,11 @@ const HorizontalGrid = forwardRef(
)) ))
)} )}
</Grid> </Grid>
),
); );
export const MANGA_GRID_SNAPSHOT_KEY = 'MangaGrid-snapshot-location'; export const MANGA_GRID_SNAPSHOT_KEY = 'MangaGrid-snapshot-location';
const VerticalGrid = forwardRef( const VerticalGrid = ({
(
{
isLoading, isLoading,
mangas, mangas,
inLibraryIndicator, inLibraryIndicator,
@@ -151,12 +137,11 @@ const VerticalGrid = forwardRef(
selectedMangaIds, selectedMangaIds,
handleSelection, handleSelection,
mode, mode,
}: DefaultGridProps & { ref,
}: DefaultGridProps & {
hasNextPage: boolean; hasNextPage: boolean;
loadMore: () => void; loadMore: () => void;
}, }) => (
ref: ForwardedRef<HTMLDivElement | null>,
) => (
<> <>
<Box ref={ref}> <Box ref={ref}>
<VirtuosoGridPersisted <VirtuosoGridPersisted
@@ -194,7 +179,6 @@ const VerticalGrid = forwardRef(
<div style={{ height: '75px' }} /> <div style={{ height: '75px' }} />
) : null} ) : null}
</> </>
),
); );
export interface IMangaGridProps export interface IMangaGridProps

View File

@@ -7,7 +7,7 @@
*/ */
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { BaseSyntheticEvent, ChangeEvent, useMemo, forwardRef, ForwardedRef } from 'react'; import { BaseSyntheticEvent, ChangeEvent, useMemo, ForwardedRef } from 'react';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import Checkbox from '@mui/material/Checkbox'; import Checkbox from '@mui/material/Checkbox';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
@@ -19,23 +19,21 @@ import { SelectableCollectionReturnType } from '@/features/collection/hooks/useS
import { MangaType } from '@/lib/graphql/generated/graphql.ts'; import { MangaType } from '@/lib/graphql/generated/graphql.ts';
import { MUIUtil } from '@/lib/mui/MUI.util.ts'; import { MUIUtil } from '@/lib/mui/MUI.util.ts';
export const MangaOptionButton = forwardRef( export const MangaOptionButton = ({
(
{
id, id,
selected, selected,
handleSelection, handleSelection,
asCheckbox = false, asCheckbox = false,
popupState, popupState,
}: { ref,
}: {
id: number; id: number;
selected?: boolean | null; selected?: boolean | null;
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection']; handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
asCheckbox?: boolean; asCheckbox?: boolean;
popupState: PopupState; popupState: PopupState;
}, ref?: ForwardedRef<HTMLButtonElement | null>;
ref: ForwardedRef<HTMLButtonElement | null>, }) => {
) => {
const { t } = useTranslation(); const { t } = useTranslation();
const bindTriggerProps = useMemo(() => bindTrigger(popupState), [popupState]); const bindTriggerProps = useMemo(() => bindTrigger(popupState), [popupState]);
@@ -108,5 +106,4 @@ export const MangaOptionButton = forwardRef(
</Button> </Button>
</CustomTooltip> </CustomTooltip>
); );
}, };
);

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { MutableRefObject } from 'react'; import { MutableRefObject, Ref } from 'react';
import { TapZoneInvertMode, TapZoneLayouts } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { TapZoneInvertMode, TapZoneLayouts } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { TChapterReader } from '@/features/chapter/Chapter.types.ts'; import { TChapterReader } from '@/features/chapter/Chapter.types.ts';
import { ReaderService } from '@/features/reader/services/ReaderService.ts'; import { ReaderService } from '@/features/reader/services/ReaderService.ts';
@@ -349,6 +349,7 @@ export interface ReaderPagerProps
isPreloadMode: boolean; isPreloadMode: boolean;
resumeMode: ReaderResumeMode; resumeMode: ReaderResumeMode;
handleAsInitialRender: boolean; handleAsInitialRender: boolean;
ref?: Ref<HTMLDivElement>;
} }
export enum PageInViewportType { export enum PageInViewportType {

View File

@@ -17,7 +17,7 @@ import Link from '@mui/material/Link';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
import { alpha } from '@mui/material/styles'; import { alpha } from '@mui/material/styles';
import Slide from '@mui/material/Slide'; import Slide from '@mui/material/Slide';
import { forwardRef, memo } from 'react'; import { memo, Ref } from 'react';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { TypographyMaxLines } from '@/base/components/texts/TypographyMaxLines.tsx'; import { TypographyMaxLines } from '@/base/components/texts/TypographyMaxLines.tsx';
import { makeToast } from '@/base/utils/Toast.ts'; import { makeToast } from '@/base/utils/Toast.ts';
@@ -38,7 +38,7 @@ import {
const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' }; const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' };
const BaseReaderOverlayHeaderMobile = forwardRef<HTMLDivElement, MobileHeaderProps>(({ isVisible }, ref) => { const BaseReaderOverlayHeaderMobile = ({ isVisible, ref }: MobileHeaderProps & { ref?: Ref<HTMLDivElement> }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' }); const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' });
const currentChapter = useReaderChaptersStore((state) => state.chapters.currentChapter); const currentChapter = useReaderChaptersStore((state) => state.chapters.currentChapter);
@@ -126,6 +126,6 @@ const BaseReaderOverlayHeaderMobile = forwardRef<HTMLDivElement, MobileHeaderPro
</Stack> </Stack>
</Slide> </Slide>
); );
}); };
export const ReaderOverlayHeaderMobile = memo(BaseReaderOverlayHeaderMobile); export const ReaderOverlayHeaderMobile = memo(BaseReaderOverlayHeaderMobile);

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { forwardRef, useMemo } from 'react'; import { useMemo } from 'react';
import { CacheProvider } from '@emotion/react'; import { CacheProvider } from '@emotion/react';
import { ThemeProvider } from '@mui/material/styles'; import { ThemeProvider } from '@mui/material/styles';
import Box, { BoxProps } from '@mui/material/Box'; import Box, { BoxProps } from '@mui/material/Box';
@@ -17,12 +17,12 @@ import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { DIRECTION_TO_CACHE } from '@/features/theme/ThemeDirectionCache.ts'; import { DIRECTION_TO_CACHE } from '@/features/theme/ThemeDirectionCache.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
const BaseReaderProgressBarDirectionWrapper = forwardRef< const BaseReaderProgressBarDirectionWrapper = ({
HTMLElement, direction,
BoxProps & { ...boxProps
}: BoxProps & {
direction: ReturnType<typeof ReaderService.useGetThemeDirection>; direction: ReturnType<typeof ReaderService.useGetThemeDirection>;
} }) => {
>(({ direction, ...boxProps }, ref) => {
const { const {
settings: { customThemes, appTheme, themeMode, shouldUsePureBlackMode }, settings: { customThemes, appTheme, themeMode, shouldUsePureBlackMode },
} = useMetadataServerSettings(); } = useMetadataServerSettings();
@@ -35,11 +35,11 @@ const BaseReaderProgressBarDirectionWrapper = forwardRef<
return ( return (
<CacheProvider value={DIRECTION_TO_CACHE[direction]}> <CacheProvider value={DIRECTION_TO_CACHE[direction]}>
<ThemeProvider theme={readerTheme}> <ThemeProvider theme={readerTheme}>
<Box {...boxProps} ref={ref} dir={direction} /> <Box {...boxProps} dir={direction} />
</ThemeProvider> </ThemeProvider>
</CacheProvider> </CacheProvider>
); );
}); };
export const ReaderProgressBarDirectionWrapper = withPropsFrom( export const ReaderProgressBarDirectionWrapper = withPropsFrom(
BaseReaderProgressBarDirectionWrapper, BaseReaderProgressBarDirectionWrapper,

View File

@@ -6,17 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { import { ForwardedRef, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
ForwardedRef,
forwardRef,
memo,
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@@ -74,17 +64,14 @@ const READING_MODE_TO_IN_VIEWPORT_TYPE: Record<ReadingMode, PageInViewportType>
[ReadingMode.WEBTOON]: PageInViewportType.Y, [ReadingMode.WEBTOON]: PageInViewportType.Y,
}; };
const BaseReaderViewer = forwardRef( const BaseReaderViewer = ({
(
{
readerNavBarWidth, readerNavBarWidth,
updateCurrentPageIndex, updateCurrentPageIndex,
}: Pick<NavbarContextType, 'readerNavBarWidth'> & { ref,
}: Pick<NavbarContextType, 'readerNavBarWidth'> & {
updateCurrentPageIndex: ReturnType<typeof ReaderControls.useUpdateCurrentPageIndex>; updateCurrentPageIndex: ReturnType<typeof ReaderControls.useUpdateCurrentPageIndex>;
}, ref?: ForwardedRef<HTMLDivElement | null>;
}) => {
ref: ForwardedRef<HTMLDivElement | null>,
) => {
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
const isOverlayVisible = useReaderOverlayStore((state) => state.overlay.isVisible); const isOverlayVisible = useReaderOverlayStore((state) => state.overlay.isVisible);
const { const {
@@ -114,15 +101,16 @@ const BaseReaderViewer = forwardRef(
retryFailedPagesKeyPrefix: state.pages.retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix: state.pages.retryFailedPagesKeyPrefix,
setTransitionPageMode: state.pages.setTransitionPageMode, setTransitionPageMode: state.pages.setTransitionPageMode,
})); }));
const { initialChapter, currentChapter, chapters, visibleChapters, isCurrentChapterReady } = const { initialChapter, currentChapter, chapters, visibleChapters, isCurrentChapterReady } = useReaderChaptersStore(
useReaderChaptersStore((state) => ({ (state) => ({
initialChapter: state.chapters.initialChapter, initialChapter: state.chapters.initialChapter,
currentChapter: state.chapters.currentChapter, currentChapter: state.chapters.currentChapter,
chapters: state.chapters.chapters, chapters: state.chapters.chapters,
visibleChapters: state.chapters.visibleChapters, visibleChapters: state.chapters.visibleChapters,
setReaderStateChapters: state.chapters.setReaderStateChapters, setReaderStateChapters: state.chapters.setReaderStateChapters,
isCurrentChapterReady: state.chapters.isCurrentChapterReady, isCurrentChapterReady: state.chapters.isCurrentChapterReady,
})); }),
);
const { const {
readingMode, readingMode,
readingDirection, readingDirection,
@@ -337,11 +325,9 @@ const BaseReaderViewer = forwardRef(
const chapter = chaptersToRender[chapterIndex]; const chapter = chaptersToRender[chapterIndex];
const previousChapter = const previousChapter =
chaptersToRender[chapterIndex + 1] ?? chaptersToRender[chapterIndex + 1] ?? chapters[initialChapterIndex + visibleChapters.leading + 1];
chapters[initialChapterIndex + visibleChapters.leading + 1];
const nextChapter = const nextChapter =
chaptersToRender[chapterIndex - 1] ?? chaptersToRender[chapterIndex - 1] ?? chapters[initialChapterIndex - visibleChapters.trailing - 1];
chapters[initialChapterIndex - visibleChapters.trailing - 1];
const isInitialChapter = chapter.id === initialChapter.id; const isInitialChapter = chapter.id === initialChapter.id;
const isCurrentChapter = chapter.id === currentChapter.id; const isCurrentChapter = chapter.id === currentChapter.id;
@@ -350,8 +336,7 @@ const BaseReaderViewer = forwardRef(
const isLeadingChapter = initialChapter.sourceOrder > chapter.sourceOrder; const isLeadingChapter = initialChapter.sourceOrder > chapter.sourceOrder;
const isTrailingChapter = initialChapter.sourceOrder < chapter.sourceOrder; const isTrailingChapter = initialChapter.sourceOrder < chapter.sourceOrder;
const isLastLeadingChapter = visibleChapters.lastLeadingChapterSourceOrder === chapter.sourceOrder; const isLastLeadingChapter = visibleChapters.lastLeadingChapterSourceOrder === chapter.sourceOrder;
const isLastTrailingChapter = const isLastTrailingChapter = visibleChapters.lastTrailingChapterSourceOrder === chapter.sourceOrder;
visibleChapters.lastTrailingChapterSourceOrder === chapter.sourceOrder;
const isPreloadMode = const isPreloadMode =
(isLastLeadingChapter && visibleChapters.isLeadingChapterPreloadMode) || (isLastLeadingChapter && visibleChapters.isLeadingChapterPreloadMode) ||
(isLastTrailingChapter && visibleChapters.isTrailingChapterPreloadMode); (isLastTrailingChapter && visibleChapters.isTrailingChapterPreloadMode);
@@ -428,8 +413,7 @@ const BaseReaderViewer = forwardRef(
})} })}
</Stack> </Stack>
); );
}, };
);
export const ReaderViewer = withPropsFrom( export const ReaderViewer = withPropsFrom(
memo(BaseReaderViewer), memo(BaseReaderViewer),

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { forwardRef, memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'; import { memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
import Box, { BoxProps } from '@mui/material/Box'; import Box, { BoxProps } from '@mui/material/Box';
import { getPageIndexesToLoad, isATransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { getPageIndexesToLoad, isATransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
import { import {
@@ -22,28 +22,7 @@ import { isContinuousReadingMode } from '@/features/reader/settings/ReaderSettin
const getPreviousCurrentPageIndex = (resumeMode: ReaderResumeMode): number => const getPreviousCurrentPageIndex = (resumeMode: ReaderResumeMode): number =>
resumeMode === ReaderResumeMode.END ? Number.MAX_SAFE_INTEGER : -1; resumeMode === ReaderResumeMode.END ? Number.MAX_SAFE_INTEGER : -1;
const BaseBasePager = forwardRef< const BaseBasePager = ({
HTMLDivElement,
Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix' | 'isPreloadMode'> &
Pick<IReaderSettings, 'readingMode' | 'imagePreLoadAmount'> & {
createPage: (
page: ReaderStatePages['pages'][number],
pagesIndex: number,
shouldLoad: boolean,
shouldDisplay: boolean,
setRef: (pagesIndex: number, element: HTMLElement | null) => void,
readingMode: ReaderPagerProps['readingMode'],
customFilter: ReaderPagerProps['customFilter'],
pageScaleMode: ReaderPagerProps['pageScaleMode'],
shouldStretchPage: ReaderPagerProps['shouldStretchPage'],
readerWidth: ReaderPagerProps['readerWidth'],
readerNavBarWidth: ReaderPagerProps['readerNavBarWidth'],
) => ReactNode;
slots?: { boxProps?: BoxProps };
}
>(
(
{
currentPageIndex, currentPageIndex,
pages, pages,
transitionPageMode, transitionPageMode,
@@ -62,9 +41,24 @@ const BaseBasePager = forwardRef<
readerNavBarWidth, readerNavBarWidth,
resumeMode, resumeMode,
handleAsInitialRender, handleAsInitialRender,
},
ref, ref,
) => { }: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix' | 'isPreloadMode'> &
Pick<IReaderSettings, 'readingMode' | 'imagePreLoadAmount'> & {
createPage: (
page: ReaderStatePages['pages'][number],
pagesIndex: number,
shouldLoad: boolean,
shouldDisplay: boolean,
setRef: (pagesIndex: number, element: HTMLElement | null) => void,
readingMode: ReaderPagerProps['readingMode'],
customFilter: ReaderPagerProps['customFilter'],
pageScaleMode: ReaderPagerProps['pageScaleMode'],
shouldStretchPage: ReaderPagerProps['shouldStretchPage'],
readerWidth: ReaderPagerProps['readerWidth'],
readerNavBarWidth: ReaderPagerProps['readerNavBarWidth'],
) => ReactNode;
slots?: { boxProps?: BoxProps };
}) => {
const previousCurrentPageIndex = useRef(getPreviousCurrentPageIndex(resumeMode)); const previousCurrentPageIndex = useRef(getPreviousCurrentPageIndex(resumeMode));
if (handleAsInitialRender) { if (handleAsInitialRender) {
@@ -83,15 +77,7 @@ const BaseBasePager = forwardRef<
isPreviousChapter, isPreviousChapter,
isNextChapter, isNextChapter,
), ),
[ [currentPageIndex, pages, imagePreLoadAmount, readingMode, isCurrentChapter, isPreviousChapter, isNextChapter],
currentPageIndex,
pages,
imagePreLoadAmount,
readingMode,
isCurrentChapter,
isPreviousChapter,
isNextChapter,
],
); );
useEffect(() => { useEffect(() => {
if (isCurrentChapter) { if (isCurrentChapter) {
@@ -119,8 +105,7 @@ const BaseBasePager = forwardRef<
...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]), ...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]),
// hide pager, without actually unmounting it to prevent re-renders, while a chapter transition page is taking up the full screen // hide pager, without actually unmounting it to prevent re-renders, while a chapter transition page is taking up the full screen
applyStyles( applyStyles(
!isContinuousReadingMode(readingMode) && !isContinuousReadingMode(readingMode) && isATransitionPageVisible(transitionPageMode, readingMode),
isATransitionPageVisible(transitionPageMode, readingMode),
{ {
visibility: 'hidden', visibility: 'hidden',
width: 0, width: 0,
@@ -148,7 +133,6 @@ const BaseBasePager = forwardRef<
)} )}
</Box> </Box>
); );
}, };
);
export const BasePager = memo(BaseBasePager); export const BasePager = memo(BaseBasePager);

View File

@@ -7,7 +7,7 @@
*/ */
import { Direction, useTheme } from '@mui/material/styles'; import { Direction, useTheme } from '@mui/material/styles';
import { forwardRef, Fragment, memo, useMemo } from 'react'; import { Fragment, memo, useMemo } from 'react';
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx'; import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
import { import {
IReaderSettings, IReaderSettings,
@@ -41,10 +41,14 @@ const getPagePosition = (
return isLtrReadingDirection ? 'right' : 'left'; return isLtrReadingDirection ? 'right' : 'left';
}; };
const BaseReaderDoublePagedPager = forwardRef< const BaseReaderDoublePagedPager = ({
HTMLDivElement, onLoad,
ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'> onError,
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => { pageLoadStates,
retryFailedPagesKeyPrefix,
isPreloadMode,
...props
}: ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'>) => {
const { currentPageIndex, pages, totalPages, readingDirection, pageScaleMode } = props; const { currentPageIndex, pages, totalPages, readingDirection, pageScaleMode } = props;
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
@@ -54,7 +58,6 @@ const BaseReaderDoublePagedPager = forwardRef<
return ( return (
<BasePager <BasePager
ref={ref}
{...props} {...props}
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) => { createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) => {
const { primary, secondary } = page; const { primary, secondary } = page;
@@ -129,6 +132,6 @@ const BaseReaderDoublePagedPager = forwardRef<
}} }}
/> />
); );
}); };
export const ReaderDoublePagedPager = memo(BaseReaderDoublePagedPager); export const ReaderDoublePagedPager = memo(BaseReaderDoublePagedPager);

View File

@@ -7,16 +7,20 @@
*/ */
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import { forwardRef, memo } from 'react'; import { memo } from 'react';
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx'; import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
import { applyStyles } from '@/base/utils/ApplyStyles.ts'; import { applyStyles } from '@/base/utils/ApplyStyles.ts';
import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/features/reader/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/features/reader/Reader.types.ts';
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
const BaseReaderHorizontalPager = forwardRef< const BaseReaderHorizontalPager = ({
HTMLDivElement, onLoad,
ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'> onError,
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => { pageLoadStates,
retryFailedPagesKeyPrefix,
isPreloadMode,
...props
}: ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'>) => {
const { currentPageIndex, totalPages, pageGap, readingDirection } = props; const { currentPageIndex, totalPages, pageGap, readingDirection } = props;
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
@@ -25,7 +29,6 @@ const BaseReaderHorizontalPager = forwardRef<
return ( return (
<BasePager <BasePager
ref={ref}
{...props} {...props}
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) => createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
createReaderPage( createReaderPage(
@@ -69,6 +72,6 @@ const BaseReaderHorizontalPager = forwardRef<
}} }}
/> />
); );
}); };
export const ReaderHorizontalPager = memo(BaseReaderHorizontalPager); export const ReaderHorizontalPager = memo(BaseReaderHorizontalPager);

View File

@@ -6,18 +6,23 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { forwardRef, memo } from 'react'; import { memo } from 'react';
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx'; import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
import { ReaderPagerProps } from '@/features/reader/Reader.types.ts'; import { ReaderPagerProps } from '@/features/reader/Reader.types.ts';
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
const BaseReaderPagedPager = forwardRef<HTMLDivElement, ReaderPagerProps>( const BaseReaderPagedPager = ({
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => { onLoad,
onError,
pageLoadStates,
retryFailedPagesKeyPrefix,
isPreloadMode,
...props
}: ReaderPagerProps) => {
const { currentPageIndex, totalPages } = props; const { currentPageIndex, totalPages } = props;
return ( return (
<BasePager <BasePager
ref={ref}
{...props} {...props}
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) => createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) =>
createReaderPage( createReaderPage(
@@ -45,7 +50,6 @@ const BaseReaderPagedPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
}} }}
/> />
); );
}, };
);
export const ReaderPagedPager = memo(BaseReaderPagedPager); export const ReaderPagedPager = memo(BaseReaderPagedPager);

View File

@@ -6,13 +6,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { forwardRef, memo } from 'react'; import { memo } from 'react';
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx'; import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
import { ReaderPagerProps, ReadingMode } from '@/features/reader/Reader.types.ts'; import { ReaderPagerProps, ReadingMode } from '@/features/reader/Reader.types.ts';
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx'; import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
const BaseReaderVerticalPager = forwardRef<HTMLDivElement, ReaderPagerProps>( const BaseReaderVerticalPager = ({
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => { onLoad,
onError,
pageLoadStates,
retryFailedPagesKeyPrefix,
isPreloadMode,
...props
}: ReaderPagerProps) => {
const { currentPageIndex, totalPages, readingMode, pageGap } = props; const { currentPageIndex, totalPages, readingMode, pageGap } = props;
const isWebtoonMode = readingMode === ReadingMode.WEBTOON; const isWebtoonMode = readingMode === ReadingMode.WEBTOON;
@@ -20,7 +26,6 @@ const BaseReaderVerticalPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
return ( return (
<BasePager <BasePager
ref={ref}
{...props} {...props}
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) => createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
createReaderPage( createReaderPage(
@@ -46,7 +51,6 @@ const BaseReaderVerticalPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
slots={{ boxProps: { sx: { margin: 'auto' } } }} slots={{ boxProps: { sx: { margin: 'auto' } } }}
/> />
); );
}, };
);
export const ReaderVerticalPager = memo(BaseReaderVerticalPager); export const ReaderVerticalPager = memo(BaseReaderVerticalPager);