Show manga placeholder thumbnail while loading

This commit is contained in:
schroda
2024-09-09 16:01:44 +02:00
parent 9d06503048
commit 0a4124062b
2 changed files with 26 additions and 25 deletions

View File

@@ -9,7 +9,7 @@
import FavoriteIcon from '@mui/icons-material/Favorite'; import FavoriteIcon from '@mui/icons-material/Favorite';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import { styled, useTheme } from '@mui/material/styles'; import { styled, useTheme } from '@mui/material/styles';
import { ComponentProps, useEffect, useMemo, useState } from 'react'; import { ComponentProps, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { t as translate } from 'i18next'; import { t as translate } from 'i18next';
import Link from '@mui/material/Link'; import Link from '@mui/material/Link';
@@ -36,6 +36,7 @@ import { Metadata as BaseMetadata } from '@/components/atoms/Metadata.tsx';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts'; import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts';
import { useLocalStorage } from '@/util/useStorage.tsx'; import { useLocalStorage } from '@/util/useStorage.tsx';
import { useResizeObserver } from '@/util/useResizeObserver.tsx';
const DetailsWrapper = styled('div')(({ theme }) => ({ const DetailsWrapper = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
@@ -103,43 +104,43 @@ const Thumbnail = ({ manga }: { manga: Partial<MangaThumbnailInfo> }) => {
const theme = useTheme(); const theme = useTheme();
const popupState = usePopupState({ variant: 'popover', popupId: 'manga-thumbnail-fullscreen' }); const popupState = usePopupState({ variant: 'popover', popupId: 'manga-thumbnail-fullscreen' });
const [imageHeight, setImageHeight] = useState<number>();
const [imageElement, setImageElement] = useState<HTMLImageElement | null>(null);
useResizeObserver(
imageElement,
useCallback(() => setImageHeight(imageElement?.clientHeight), [imageElement]),
);
const [isImageReady, setIsImageReady] = useState(false); const [isImageReady, setIsImageReady] = useState(false);
const isImageHeightReady = isImageReady && !!imageHeight;
return ( return (
<> <>
<Stack <Stack
sx={{ sx={{
position: 'relative', position: 'relative',
'& img': { borderRadius: 1,
borderRadius: 0.5, overflow: 'hidden',
width: '150px', backgroundColor: 'background.paper',
height: 'auto', width: '150px',
[theme.breakpoints.up('lg')]: { height: `${isImageHeightReady ? imageHeight : 225}px`,
width: '200px', flexShrink: 0,
}, [theme.breakpoints.up('lg')]: {
[theme.breakpoints.up('xl')]: { width: '200px',
width: '300px', height: `${isImageHeightReady ? imageHeight : 300}px`,
}, },
[theme.breakpoints.up('xl')]: {
width: '300px',
height: `${isImageHeightReady ? imageHeight : 450}px`,
}, },
}} }}
> >
<SpinnerImage <SpinnerImage
ref={setImageElement}
src={Mangas.getThumbnailUrl(manga)} src={Mangas.getThumbnailUrl(manga)}
alt="Manga Thumbnail" alt="Manga Thumbnail"
onImageLoad={() => setIsImageReady(true)} onImageLoad={() => setIsImageReady(true)}
spinnerStyle={{
borderRadius: 0.5,
width: '150px',
height: '225px',
[theme.breakpoints.up('lg')]: {
width: '200px',
height: '300px',
},
[theme.breakpoints.up('xl')]: {
width: '300px',
height: '450px',
},
}}
/> />
{isImageReady && ( {isImageReady && (
<Stack <Stack

View File

@@ -103,7 +103,7 @@ export const SpinnerImage = forwardRef((props: IProps, imgRef: ForwardedRef<HTML
return ( return (
<> <>
{(isLoading || hasError) && ( {(isLoading || hasError) && (
<Box sx={spinnerStyle}> <Box sx={{ height: '100%', ...spinnerStyle }}>
<Stack <Stack
sx={{ sx={{
height: '100%', height: '100%',