Make remaining image requests abortable (#645)
This commit is contained in:
@@ -18,6 +18,7 @@ import { PartialExtension, TranslationKey } from '@/typings';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
||||
|
||||
interface IProps {
|
||||
extension: PartialExtension;
|
||||
@@ -170,10 +171,17 @@ export function ExtensionCard(props: IProps) {
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
mr: 2,
|
||||
background: 'transparent',
|
||||
}}
|
||||
alt={name}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
>
|
||||
<SpinnerImage
|
||||
spinnerStyle={{ small: true }}
|
||||
imgStyle={{ objectFit: 'cover', width: '100%', height: '100%' }}
|
||||
alt={name}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
</Avatar>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography variant="h5" component="h2">
|
||||
{name}
|
||||
|
||||
@@ -347,26 +347,26 @@ export const MangaCard = (props: MangaCardProps) => {
|
||||
>
|
||||
<Avatar
|
||||
variant="rounded"
|
||||
sx={
|
||||
inLibraryIndicator && inLibrary
|
||||
? {
|
||||
width: 56,
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
marginRight: 2,
|
||||
imageRendering: 'pixelated',
|
||||
filter: 'brightness(0.4)',
|
||||
}
|
||||
: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
marginRight: 2,
|
||||
imageRendering: 'pixelated',
|
||||
}
|
||||
}
|
||||
src={thumbnailUrl}
|
||||
/>
|
||||
sx={{
|
||||
width: 56,
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
marginRight: 2,
|
||||
}}
|
||||
>
|
||||
<SpinnerImage
|
||||
spinnerStyle={{ small: true }}
|
||||
imgStyle={{
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
imageRendering: 'pixelated',
|
||||
filter: inLibraryIndicator && inLibrary ? 'brightness(0.4)' : undefined,
|
||||
}}
|
||||
alt={manga.title}
|
||||
src={thumbnailUrl}
|
||||
/>
|
||||
</Avatar>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Link } from 'react-router-dom';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { GetMigratableSourcesQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { translateExtensionLanguage } from '@/screens/util/Extensions.ts';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
||||
|
||||
export type TMigratableSource = NonNullable<GetMigratableSourcesQuery['mangas']['nodes'][number]['source']> & {
|
||||
mangaCount: number;
|
||||
@@ -40,10 +41,16 @@ export const MigrationCard = ({ id, name, lang, iconUrl, mangaCount }: TMigratab
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
mr: 2,
|
||||
background: 'transparent',
|
||||
}}
|
||||
alt={name}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
>
|
||||
<SpinnerImage
|
||||
spinnerStyle={{ small: true }}
|
||||
imgStyle={{ objectFit: 'cover', width: '100%', height: '100%' }}
|
||||
alt={name}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
</Avatar>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography variant="h5" component="h2">
|
||||
{name}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { TPartialSource } from '@/typings';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { translateExtensionLanguage } from '@/screens/util/Extensions';
|
||||
import { SourceContentType } from '@/screens/SourceMangas';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
||||
|
||||
const MobileWidthButtons = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@@ -88,9 +89,16 @@ export const SourceCard: React.FC<IProps> = (props: IProps) => {
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
mr: 2,
|
||||
background: 'transparent',
|
||||
}}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
>
|
||||
<SpinnerImage
|
||||
spinnerStyle={{ small: true }}
|
||||
imgStyle={{ objectFit: 'cover', width: '100%', height: '100%' }}
|
||||
alt={name}
|
||||
src={requestManager.getValidImgUrlFor(iconUrl)}
|
||||
/>
|
||||
</Avatar>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { Box, SxProps, Theme } from '@mui/material';
|
||||
import { CSSProperties, forwardRef } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { IReaderSettings } from '@/typings';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage';
|
||||
import { imageStyle } from '@/components/reader/Page';
|
||||
@@ -34,7 +34,7 @@ export const DoublePage = forwardRef((props: IProps, ref: any) => {
|
||||
maxWidth: settings.fitPageToWindow ? `calc(${baseImgStyle.maxWidth} * 0.5)` : baseImgStyle.maxWidth,
|
||||
};
|
||||
|
||||
const spinnerStyle: SxProps<Theme> = {
|
||||
const spinnerStyle: CSSProperties = {
|
||||
...imgStyle,
|
||||
height: '100vh',
|
||||
width: '50%',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import { CSSProperties, forwardRef } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
@@ -16,20 +16,20 @@ import { SpinnerImage } from '@/components/util/SpinnerImage';
|
||||
export const isHorizontalReaderType = (readerType: ReaderType): boolean =>
|
||||
['ContinuesHorizontalLTR', 'ContinuesHorizontalRTL'].includes(readerType);
|
||||
|
||||
export function imageStyle(settings: IReaderSettings): any {
|
||||
export function imageStyle(settings: IReaderSettings): CSSProperties {
|
||||
const isVertical = settings.readerType === 'ContinuesVertical';
|
||||
const isHorizontal = isHorizontalReaderType(settings.readerType);
|
||||
const baseStyling = {
|
||||
const baseStyling: CSSProperties = {
|
||||
margin: 0,
|
||||
width: `${settings.readerWidth}%`,
|
||||
objectFit: 'contain',
|
||||
};
|
||||
|
||||
const continuesVerticalStyling = {
|
||||
const continuesVerticalStyling: CSSProperties = {
|
||||
marginBottom: '15px',
|
||||
};
|
||||
|
||||
const continuesHorizontalStyling = {
|
||||
const continuesHorizontalStyling: CSSProperties = {
|
||||
width: undefined,
|
||||
minHeight: '100vh',
|
||||
maxHeight: '100vh',
|
||||
@@ -37,7 +37,7 @@ export function imageStyle(settings: IReaderSettings): any {
|
||||
marginRight: '7px',
|
||||
};
|
||||
|
||||
const fitToPageStyling = {
|
||||
const fitToPageStyling: CSSProperties = {
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
minWidth: settings.scalePage ? 'calc(100vw - (100vw - 100%))' : undefined,
|
||||
|
||||
@@ -21,14 +21,14 @@ interface IProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
|
||||
spinnerStyle?: SxProps<Theme>;
|
||||
spinnerStyle?: SxProps<Theme> & { small?: boolean };
|
||||
imgStyle?: CSSProperties;
|
||||
|
||||
onImageLoad?: () => void;
|
||||
}
|
||||
|
||||
export const SpinnerImage = forwardRef((props: IProps, imgRef: ForwardedRef<HTMLImageElement | null>) => {
|
||||
const { src, alt, onImageLoad, spinnerStyle, imgStyle } = props;
|
||||
const { src, alt, onImageLoad, spinnerStyle: { small, ...spinnerStyle } = {}, imgStyle } = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -96,15 +96,15 @@ export const SpinnerImage = forwardRef((props: IProps, imgRef: ForwardedRef<HTML
|
||||
<>
|
||||
<BrokenImageIcon />
|
||||
<Button
|
||||
startIcon={<RefreshIcon />}
|
||||
startIcon={!small && <RefreshIcon />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setImgLoadRetryKey((prevState) => (prevState + 1) % 100);
|
||||
}}
|
||||
size="large"
|
||||
size={small ? 'small' : 'large'}
|
||||
>
|
||||
{t('global.button.retry')}
|
||||
{small ? <RefreshIcon /> : t('global.button.retry')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -745,7 +745,7 @@ export class RequestManager {
|
||||
|
||||
public getValidImgUrlFor(imageUrl: string, apiVersion: string = ''): string {
|
||||
// server provided image urls already contain the api version
|
||||
return `${this.getValidUrlFor(imageUrl, apiVersion)}`;
|
||||
return `${this.getValidUrlFor(imageUrl, apiVersion)}`.replace('45670', '45669');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuo
|
||||
import { StyledGroupHeader } from '@/components/virtuoso/StyledGroupHeader.tsx';
|
||||
import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx';
|
||||
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
||||
|
||||
function epochToDate(epoch: number) {
|
||||
const date = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||
@@ -199,10 +200,21 @@ export const Updates: React.FC = () => {
|
||||
height: 56,
|
||||
flex: '0 0 auto',
|
||||
marginRight: 2,
|
||||
imageRendering: 'pixelated',
|
||||
background: 'transparent',
|
||||
}}
|
||||
src={Mangas.getThumbnailUrl(manga)}
|
||||
/>
|
||||
>
|
||||
<SpinnerImage
|
||||
imgStyle={{
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
spinnerStyle={{ small: true }}
|
||||
alt={manga.title}
|
||||
src={Mangas.getThumbnailUrl(manga)}
|
||||
/>
|
||||
</Avatar>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography variant="h5" component="h2">
|
||||
{manga.title}
|
||||
|
||||
Reference in New Issue
Block a user