2021-05-28 19:37:26 +04:30
|
|
|
/*
|
|
|
|
|
* 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
|
2023-05-18 13:17:41 +02:00
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
import React, { useState, CSSProperties, useEffect } from 'react';
|
2021-09-09 17:51:22 +04:30
|
|
|
import CircularProgress from '@mui/material/CircularProgress';
|
2023-06-04 21:08:39 +02:00
|
|
|
import Box from '@mui/material/Box';
|
2024-01-15 00:32:39 +01:00
|
|
|
import { Theme, SxProps, Stack, Button } from '@mui/material';
|
|
|
|
|
|
|
|
|
|
import BrokenImageIcon from '@mui/icons-material/BrokenImage';
|
|
|
|
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
2021-05-28 19:37:26 +04:30
|
|
|
|
|
|
|
|
interface IProps {
|
2023-02-06 10:06:33 +01:00
|
|
|
src: string;
|
|
|
|
|
alt: string;
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2023-02-06 10:06:33 +01:00
|
|
|
imgRef?: React.RefObject<HTMLImageElement>;
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2023-02-06 10:06:33 +01:00
|
|
|
spinnerStyle?: SxProps<Theme>;
|
|
|
|
|
imgStyle?: CSSProperties;
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2023-02-06 10:06:33 +01:00
|
|
|
onImageLoad?: () => void;
|
2021-05-28 19:37:26 +04:30
|
|
|
}
|
|
|
|
|
|
2023-10-28 00:32:02 +02:00
|
|
|
export function SpinnerImage(props: IProps) {
|
2023-02-06 10:06:33 +01:00
|
|
|
const { src, alt, onImageLoad, imgRef, spinnerStyle, imgStyle } = props;
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
const [imgLoadRetryKey, setImgLoadRetryKey] = useState(0);
|
|
|
|
|
const [isLoading, setIsLoading] = useState<boolean | undefined>(undefined);
|
|
|
|
|
const [hasError, setHasError] = useState(false);
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
const updateImageState = (loading: boolean, error: boolean = false) => {
|
|
|
|
|
setIsLoading(loading);
|
|
|
|
|
setHasError(error);
|
2021-09-18 00:04:09 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
if (!loading && !error) {
|
|
|
|
|
onImageLoad?.();
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-05-28 19:37:26 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
useEffect(() => {
|
|
|
|
|
// only activate the loading state in case the image has not been cached yet.
|
|
|
|
|
// otherwise, the loading placeholder will always be visible before the actual image is shown, which looks like flickering
|
|
|
|
|
const timeout = setTimeout(() => setIsLoading((prevState) => (prevState === undefined ? true : prevState)), 1);
|
|
|
|
|
return () => clearTimeout(timeout);
|
|
|
|
|
}, []);
|
2021-09-18 00:04:09 +04:30
|
|
|
|
2024-01-15 00:32:39 +01:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{(isLoading || hasError) && (
|
|
|
|
|
<Box sx={spinnerStyle}>
|
|
|
|
|
<Stack height="100%" alignItems="center" justifyContent="center">
|
|
|
|
|
{isLoading && <CircularProgress thickness={5} />}
|
|
|
|
|
{hasError && (
|
|
|
|
|
<>
|
|
|
|
|
<BrokenImageIcon />
|
|
|
|
|
<Button
|
|
|
|
|
startIcon={<RefreshIcon />}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setHasError(false);
|
|
|
|
|
setImgLoadRetryKey((prevState) => (prevState + 1) % 100);
|
|
|
|
|
}}
|
|
|
|
|
size="large"
|
|
|
|
|
>
|
|
|
|
|
Retry
|
|
|
|
|
</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</Stack>
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
<img
|
|
|
|
|
key={`${src}_${imgLoadRetryKey}`}
|
|
|
|
|
style={{ ...imgStyle, display: isLoading || hasError ? 'none' : undefined }}
|
|
|
|
|
ref={imgRef}
|
|
|
|
|
src={src}
|
|
|
|
|
alt={alt}
|
|
|
|
|
onLoad={() => updateImageState(false)}
|
|
|
|
|
onError={() => updateImageState(false, true)}
|
|
|
|
|
draggable={false}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2021-05-28 19:37:26 +04:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SpinnerImage.defaultProps = {
|
2021-10-31 21:02:45 +03:30
|
|
|
spinnerStyle: {},
|
|
|
|
|
imgStyle: {},
|
2021-05-28 19:37:26 +04:30
|
|
|
onImageLoad: () => {},
|
|
|
|
|
imgRef: undefined,
|
|
|
|
|
};
|