Improve "DownloadQueue" render performance
This commit is contained in:
@@ -16,7 +16,7 @@ import Stack from '@mui/material/Stack';
|
|||||||
import Box, { BoxProps } from '@mui/material/Box';
|
import Box, { BoxProps } from '@mui/material/Box';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import React, { useContext, useEffect, useLayoutEffect } from 'react';
|
import React, { memo, useCallback, useContext, useEffect, useLayoutEffect } from 'react';
|
||||||
import { DragDropContext, Draggable, DraggableProvided, DropResult } from 'react-beautiful-dnd';
|
import { DragDropContext, Draggable, DraggableProvided, DropResult } from 'react-beautiful-dnd';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@@ -45,79 +45,86 @@ const HeightPreservingItem = ({ children, ...props }: BoxProps) => (
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
const DownloadChapterItem = ({
|
const DownloadChapterItem = memo(
|
||||||
provided,
|
({
|
||||||
item,
|
provided,
|
||||||
handleDelete,
|
item,
|
||||||
handleRetry,
|
handleDelete,
|
||||||
}: {
|
handleRetry,
|
||||||
provided: DraggableProvided;
|
}: {
|
||||||
item: ChapterDownloadStatus;
|
provided: DraggableProvided;
|
||||||
handleDelete: (chapter: ChapterIdInfo) => void;
|
item: ChapterDownloadStatus;
|
||||||
handleRetry: (chapter: ChapterIdInfo) => void;
|
handleDelete: (chapter: ChapterIdInfo) => void;
|
||||||
}) => {
|
handleRetry: (chapter: ChapterIdInfo) => void;
|
||||||
const { t } = useTranslation();
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} sx={{ p: 1, pb: 0 }}>
|
<Box
|
||||||
<Card>
|
{...provided.draggableProps}
|
||||||
<CardActionArea component={Link} to={AppRoutes.manga.path(item.manga.id)}>
|
{...provided.dragHandleProps}
|
||||||
<CardContent
|
ref={provided.innerRef}
|
||||||
sx={{
|
sx={{ p: 1, pb: 0 }}
|
||||||
display: 'flex',
|
>
|
||||||
alignItems: 'center',
|
<Card>
|
||||||
p: 1.5,
|
<CardActionArea component={Link} to={AppRoutes.manga.path(item.manga.id)}>
|
||||||
}}
|
<CardContent
|
||||||
>
|
sx={{
|
||||||
<IconButton sx={{ pointerEvents: 'none' }}>
|
display: 'flex',
|
||||||
<DragHandle />
|
alignItems: 'center',
|
||||||
</IconButton>
|
p: 1.5,
|
||||||
<Stack sx={{ flex: 1, ml: 1 }} direction="column">
|
}}
|
||||||
<Typography variant="h6" component="h3">
|
>
|
||||||
{item.manga.title}
|
<IconButton sx={{ pointerEvents: 'none' }}>
|
||||||
</Typography>
|
<DragHandle />
|
||||||
<Typography
|
</IconButton>
|
||||||
variant="caption"
|
<Stack sx={{ flex: 1, ml: 1 }} direction="column">
|
||||||
sx={{
|
<Typography variant="h6" component="h3">
|
||||||
display: 'block',
|
{item.manga.title}
|
||||||
}}
|
</Typography>
|
||||||
>
|
<Typography
|
||||||
{item.chapter.name}
|
variant="caption"
|
||||||
</Typography>
|
sx={{
|
||||||
</Stack>
|
display: 'block',
|
||||||
<DownloadStateIndicator chapterId={item.chapter.id} />
|
}}
|
||||||
{item.state === DownloadState.Error && (
|
>
|
||||||
<Tooltip title={t('global.button.retry')}>
|
{item.chapter.name}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<DownloadStateIndicator chapterId={item.chapter.id} />
|
||||||
|
{item.state === DownloadState.Error && (
|
||||||
|
<Tooltip title={t('global.button.retry')}>
|
||||||
|
<IconButton
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
handleRetry(item.chapter);
|
||||||
|
}}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<Refresh />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<Tooltip title={t('chapter.action.download.delete.label.action')}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleRetry(item.chapter);
|
handleDelete(item.chapter);
|
||||||
}}
|
}}
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
<Refresh />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
</CardContent>
|
||||||
<Tooltip title={t('chapter.action.download.delete.label.action')}>
|
</CardActionArea>
|
||||||
<IconButton
|
</Card>
|
||||||
onClick={(e) => {
|
</Box>
|
||||||
e.preventDefault();
|
);
|
||||||
e.stopPropagation();
|
},
|
||||||
handleDelete(item.chapter);
|
);
|
||||||
}}
|
|
||||||
size="large"
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</CardContent>
|
|
||||||
</CardActionArea>
|
|
||||||
</Card>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DownloadQueue: React.FC = () => {
|
export const DownloadQueue: React.FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -213,15 +220,15 @@ export const DownloadQueue: React.FC = () => {
|
|||||||
categoryReorder(queue, result.source.index, result.destination.index);
|
categoryReorder(queue, result.source.index, result.destination.index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRetry = async (chapter: ChapterIdInfo) => {
|
const handleRetry = useCallback(async (chapter: ChapterIdInfo) => {
|
||||||
try {
|
try {
|
||||||
await requestManager.addChapterToDownloadQueue(chapter.id).response;
|
await requestManager.addChapterToDownloadQueue(chapter.id).response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
makeToast(t('download.queue.error.label.failed_to_remove'), 'error', getErrorMessage(e));
|
makeToast(t('download.queue.error.label.failed_to_remove'), 'error', getErrorMessage(e));
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleDelete = async (chapter: ChapterIdInfo) => {
|
const handleDelete = useCallback(async (chapter: ChapterIdInfo) => {
|
||||||
const isRunning = status === DownloaderState.Started;
|
const isRunning = status === DownloaderState.Started;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -246,7 +253,7 @@ export const DownloadQueue: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requestManager.startDownloads().response.catch(defaultPromiseErrorHandler('DownloadQueue::startDownloads'));
|
requestManager.startDownloads().response.catch(defaultPromiseErrorHandler('DownloadQueue::startDownloads'));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <LoadingPlaceholder />;
|
return <LoadingPlaceholder />;
|
||||||
@@ -285,17 +292,17 @@ export const DownloadQueue: React.FC = () => {
|
|||||||
<Virtuoso
|
<Virtuoso
|
||||||
useWindowScroll
|
useWindowScroll
|
||||||
overscan={window.innerHeight * 0.5}
|
overscan={window.innerHeight * 0.5}
|
||||||
data={queue}
|
|
||||||
components={{
|
components={{
|
||||||
Item: HeightPreservingItem,
|
Item: HeightPreservingItem,
|
||||||
}}
|
}}
|
||||||
computeItemKey={(_, item) => item.chapter.id}
|
totalCount={queue.length}
|
||||||
itemContent={(index, item) => (
|
computeItemKey={(index) => queue[index].chapter.id}
|
||||||
<Draggable draggableId={`${item.chapter.id}`} index={index}>
|
itemContent={(index) => (
|
||||||
|
<Draggable draggableId={`${queue[index].chapter.id}`} index={index}>
|
||||||
{(draggableProvided) => (
|
{(draggableProvided) => (
|
||||||
<DownloadChapterItem
|
<DownloadChapterItem
|
||||||
provided={draggableProvided}
|
provided={draggableProvided}
|
||||||
item={item}
|
item={queue[index]}
|
||||||
handleDelete={handleDelete}
|
handleDelete={handleDelete}
|
||||||
handleRetry={handleRetry}
|
handleRetry={handleRetry}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user