Move download status subscription to "ChapterCard"
Only re-render chapters whose download status has changed instead of the whole list everytime a download status change was received
This commit is contained in:
@@ -28,6 +28,7 @@ import { ChaptersToolbarMenu } from '@/modules/chapter/components/ChaptersToolba
|
||||
import { SelectionFAB } from '@/modules/collection/components/SelectionFAB.tsx';
|
||||
import { DEFAULT_FULL_FAB_HEIGHT } from '@/modules/core/components/buttons/StyledFab.tsx';
|
||||
import {
|
||||
ChapterListFieldsFragment,
|
||||
GetChaptersMangaQuery,
|
||||
GetChaptersMangaQueryVariables,
|
||||
MangaScreenFieldsFragment,
|
||||
@@ -35,7 +36,6 @@ import {
|
||||
import { useSelectableCollection } from '@/modules/collection/hooks/useSelectableCollection.ts';
|
||||
import { SelectableCollectionSelectAll } from '@/modules/collection/components/SelectableCollectionSelectAll.tsx';
|
||||
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||
import { ChaptersWithMeta, ChapterWithMetaType } from '@/modules/chapter/services/ChaptersWithMeta.ts';
|
||||
import { ChapterActionMenuItems } from '@/modules/chapter/components/actions/ChapterActionMenuItems.tsx';
|
||||
import { ChaptersDownloadActionMenuItems } from '@/modules/chapter/components/actions/ChaptersDownloadActionMenuItems.tsx';
|
||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||
@@ -75,9 +75,27 @@ const StyledVirtuoso = styled(Virtuoso, {
|
||||
},
|
||||
}));
|
||||
|
||||
export interface IChapterWithMeta extends ChapterWithMetaType<ComponentProps<typeof ChapterCard>['chapter']> {
|
||||
selected: boolean | null;
|
||||
}
|
||||
const ChapterListFAB = ({
|
||||
selectedChapters,
|
||||
firstUnreadChapter,
|
||||
}: {
|
||||
selectedChapters: ChapterListFieldsFragment[];
|
||||
firstUnreadChapter: ComponentProps<typeof ResumeFab>['chapter'] | null | undefined;
|
||||
}) => {
|
||||
if (selectedChapters.length) {
|
||||
return (
|
||||
<SelectionFAB selectedItemsCount={selectedChapters.length} title="chapter.title_one">
|
||||
{(handleClose) => <ChapterActionMenuItems selectedChapters={selectedChapters} onClose={handleClose} />}
|
||||
</SelectionFAB>
|
||||
);
|
||||
}
|
||||
|
||||
if (firstUnreadChapter) {
|
||||
return <ResumeFab chapter={firstUnreadChapter} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const ChapterList = ({
|
||||
manga,
|
||||
@@ -100,8 +118,6 @@ export const ChapterList = ({
|
||||
|
||||
const scrollbarWidth = MediaQuery.useGetScrollbarSize('width');
|
||||
|
||||
const downloadSubscription = requestManager.useDownloadSubscription();
|
||||
|
||||
const [options, dispatch] = useChapterOptions(manga.id);
|
||||
const {
|
||||
data: chaptersData,
|
||||
@@ -128,39 +144,6 @@ export const ChapterList = ({
|
||||
const noChaptersFound = chapters.length === 0;
|
||||
const noChaptersMatchingFilter = !noChaptersFound && visibleChapters.length === 0;
|
||||
|
||||
const chaptersWithMeta: IChapterWithMeta[] = useMemo(
|
||||
() =>
|
||||
visibleChapters.map((chapter) => {
|
||||
const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null;
|
||||
return {
|
||||
chapter,
|
||||
downloadChapter: Chapters.getDownloadStatusFromCache(chapter.id),
|
||||
selected,
|
||||
};
|
||||
}),
|
||||
[downloadSubscription.data?.downloadStatusChanged, selectedItemIds, visibleChapters],
|
||||
);
|
||||
|
||||
const chapterListFAB = useMemo(() => {
|
||||
const selectedChapters = chaptersWithMeta.filter((chapter) => chapter.selected);
|
||||
|
||||
if (selectedChapters.length) {
|
||||
return (
|
||||
<SelectionFAB selectedItemsCount={selectedChapters.length} title="chapter.title_one">
|
||||
{(handleClose) => (
|
||||
<ChapterActionMenuItems selectedChapters={selectedChapters} onClose={handleClose} />
|
||||
)}
|
||||
</SelectionFAB>
|
||||
);
|
||||
}
|
||||
|
||||
if (manga.firstUnreadChapter) {
|
||||
return <ResumeFab chapter={manga.firstUnreadChapter} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [chaptersWithMeta]);
|
||||
|
||||
if (isLoading || (noChaptersFound && isRefreshing)) {
|
||||
return (
|
||||
<Stack sx={{ justifyContent: 'center', alignItems: 'center', position: 'relative', flexGrow: 1 }}>
|
||||
@@ -201,13 +184,7 @@ export const ChapterList = ({
|
||||
<Tooltip title={t('chapter.action.mark_as_read.add.label.action.current')}>
|
||||
<IconButton
|
||||
disabled={areAllChaptersRead}
|
||||
onClick={() =>
|
||||
Chapters.markAsRead(
|
||||
ChaptersWithMeta.getChapters(ChaptersWithMeta.getNonRead(chaptersWithMeta)),
|
||||
true,
|
||||
manga.id,
|
||||
)
|
||||
}
|
||||
onClick={() => Chapters.markAsRead(Chapters.getNonRead(chapters), true, manga.id)}
|
||||
>
|
||||
<DoneAllIcon />
|
||||
</IconButton>
|
||||
@@ -259,11 +236,12 @@ export const ChapterList = ({
|
||||
computeItemKey={(index) => visibleChapters[index].id}
|
||||
itemContent={(index: number) => (
|
||||
<ChapterCard
|
||||
{...chaptersWithMeta[index]}
|
||||
chapter={chapters[index]}
|
||||
selected={!areNoItemsSelected ? selectedItemIds.includes(chapters[index].id) : null}
|
||||
allChapters={chapters}
|
||||
showChapterNumber={options.showChapterNumber}
|
||||
onSelect={(selected, selectRange) =>
|
||||
handleSelection(chaptersWithMeta[index].chapter.id, selected, { selectRange })
|
||||
handleSelection(chapters[index].id, selected, { selectRange })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
@@ -271,7 +249,12 @@ export const ChapterList = ({
|
||||
overscan={window.innerHeight * 0.5}
|
||||
/>
|
||||
</Stack>
|
||||
{chapterListFAB}
|
||||
<ChapterListFAB
|
||||
selectedChapters={selectedItemIds
|
||||
.map((id) => chapters.find((chapter) => chapter.id === id))
|
||||
.filter((chapter) => chapter != null)}
|
||||
firstUnreadChapter={manga.firstUnreadChapter}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import BookmarkRemove from '@mui/icons-material/BookmarkRemove';
|
||||
import BookmarkAdd from '@mui/icons-material/BookmarkAdd';
|
||||
import DoneAll from '@mui/icons-material/DoneAll';
|
||||
import { useMemo } from 'react';
|
||||
import { ComponentProps, useMemo } from 'react';
|
||||
import LaunchIcon from '@mui/icons-material/Launch';
|
||||
import { SelectableCollectionReturnType } from '@/modules/collection/hooks/useSelectableCollection.ts';
|
||||
import {
|
||||
@@ -30,8 +30,6 @@ import {
|
||||
Chapters,
|
||||
} from '@/modules/chapter/services/Chapters.ts';
|
||||
import { MenuItem } from '@/modules/core/components/menu/MenuItem.tsx';
|
||||
import { IChapterWithMeta } from '@/modules/chapter/components/ChapterList.tsx';
|
||||
import { ChaptersWithMeta } from '@/modules/chapter/services/ChaptersWithMeta.ts';
|
||||
import {
|
||||
createGetMenuItemTitle,
|
||||
createIsMenuItemDisabled,
|
||||
@@ -39,6 +37,7 @@ import {
|
||||
} from '@/modules/core/components/menu/Menu.utils.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||
import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts';
|
||||
import { ChapterCard } from '@/modules/chapter/components/cards/ChapterCard.tsx';
|
||||
|
||||
type BaseProps = { onClose: () => void; selectable?: boolean };
|
||||
|
||||
@@ -57,7 +56,7 @@ type SingleModeProps = {
|
||||
};
|
||||
|
||||
type SelectModeProps = {
|
||||
selectedChapters: IChapterWithMeta[];
|
||||
selectedChapters: ComponentProps<typeof ChapterCard>['chapter'][];
|
||||
};
|
||||
|
||||
type Props =
|
||||
@@ -95,12 +94,12 @@ export const ChapterActionMenuItems = ({
|
||||
readChapters,
|
||||
} = useMemo(
|
||||
() => ({
|
||||
downloadableChapters: ChaptersWithMeta.getDownloadable(selectedChapters),
|
||||
downloadedChapters: ChaptersWithMeta.getDownloaded(selectedChapters),
|
||||
unbookmarkedChapters: ChaptersWithMeta.getNonBookmarked(selectedChapters),
|
||||
bookmarkedChapters: ChaptersWithMeta.getBookmarked(selectedChapters),
|
||||
unreadChapters: ChaptersWithMeta.getNonRead(selectedChapters),
|
||||
readChapters: ChaptersWithMeta.getRead(selectedChapters),
|
||||
downloadableChapters: Chapters.getDownloadable(selectedChapters),
|
||||
downloadedChapters: Chapters.getDownloaded(selectedChapters),
|
||||
unbookmarkedChapters: Chapters.getNonBookmarked(selectedChapters),
|
||||
bookmarkedChapters: Chapters.getBookmarked(selectedChapters),
|
||||
unreadChapters: Chapters.getNonRead(selectedChapters),
|
||||
readChapters: Chapters.getRead(selectedChapters),
|
||||
}),
|
||||
[selectedChapters],
|
||||
);
|
||||
@@ -110,7 +109,7 @@ export const ChapterActionMenuItems = ({
|
||||
onClose();
|
||||
};
|
||||
|
||||
const performAction = (action: ChapterAction | 'mark_prev_as_read', chaptersWithMeta: IChapterWithMeta[]) => {
|
||||
const performAction = (action: ChapterAction | 'mark_prev_as_read', chapters: TChapter[]) => {
|
||||
const isMarkPrevAsRead = action === 'mark_prev_as_read';
|
||||
const actualAction: ChapterAction = isMarkPrevAsRead ? 'mark_as_read' : action;
|
||||
|
||||
@@ -125,7 +124,7 @@ export const ChapterActionMenuItems = ({
|
||||
const getChapters = (): SingleModeProps['chapter'][] => {
|
||||
// select mode
|
||||
if (!chapter) {
|
||||
return ChaptersWithMeta.getChapters(chaptersWithMeta);
|
||||
return chapters;
|
||||
}
|
||||
|
||||
if (!isMarkPrevAsRead) {
|
||||
@@ -142,17 +141,17 @@ export const ChapterActionMenuItems = ({
|
||||
return allChapters.slice(index + 1);
|
||||
};
|
||||
|
||||
const chapters = getChapters();
|
||||
const chaptersToUpdate = getChapters();
|
||||
|
||||
if (!chapters.length) {
|
||||
if (!chaptersToUpdate.length) {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
Chapters.performAction(actualAction, Chapters.getIds(chapters), {
|
||||
chapters,
|
||||
Chapters.performAction(actualAction, Chapters.getIds(chaptersToUpdate), {
|
||||
chapters: chaptersToUpdate,
|
||||
wasManuallyMarkedAsRead: true,
|
||||
trackProgressMangaId: chapters[0]?.mangaId,
|
||||
trackProgressMangaId: chaptersToUpdate[0]?.mangaId,
|
||||
}).catch(defaultPromiseErrorHandler('ChapterActionMenuItems::performAction'));
|
||||
onClose();
|
||||
};
|
||||
@@ -186,10 +185,7 @@ export const ChapterActionMenuItems = ({
|
||||
Icon={Delete}
|
||||
disabled={isMenuItemDisabled(!downloadedChapters.length)}
|
||||
onClick={() =>
|
||||
performAction(
|
||||
'delete',
|
||||
ChaptersWithMeta.getDeletable(downloadedChapters, deleteChaptersWithBookmark),
|
||||
)
|
||||
performAction('delete', Chapters.getDeletable(downloadedChapters, deleteChaptersWithBookmark))
|
||||
}
|
||||
title={getMenuItemTitle('delete', downloadedChapters.length)}
|
||||
/>
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
Chapters,
|
||||
ChapterScanlatorInfo,
|
||||
} from '@/modules/chapter/services/Chapters.ts';
|
||||
import { ChaptersWithMeta, ChapterWithMetaType } from '@/modules/chapter/services/ChaptersWithMeta.ts';
|
||||
|
||||
type TChapter = ChapterIdInfo &
|
||||
ChapterMangaInfo &
|
||||
@@ -53,7 +52,6 @@ interface IProps {
|
||||
mode?: 'manga.page' | 'reader';
|
||||
chapter: TChapter;
|
||||
allChapters: TChapter[];
|
||||
downloadChapter: ChapterWithMetaType['downloadChapter'];
|
||||
showChapterNumber: boolean;
|
||||
onSelect: (selected: boolean, isShiftKey?: boolean) => void;
|
||||
selected: boolean | null;
|
||||
@@ -71,7 +69,6 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
mode = 'manga.page',
|
||||
chapter,
|
||||
allChapters,
|
||||
downloadChapter: dc,
|
||||
showChapterNumber,
|
||||
onSelect,
|
||||
selected,
|
||||
@@ -175,7 +172,7 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
{dc && <DownloadStateIndicator download={dc} />}
|
||||
<DownloadStateIndicator chapterId={chapter.id} />
|
||||
|
||||
<Stack sx={{ minHeight: '48px' }}>
|
||||
{selected === null ? (
|
||||
@@ -209,10 +206,7 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
|
||||
chapter={chapter}
|
||||
allChapters={allChapters}
|
||||
handleSelection={() => onSelect(true)}
|
||||
canBeDownloaded={ChaptersWithMeta.isDownloadable({
|
||||
chapter,
|
||||
downloadChapter: dc,
|
||||
})}
|
||||
canBeDownloaded={Chapters.isDownloadable(chapter)}
|
||||
selectable={selectable}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
|
||||
import { t as translate } from 'i18next';
|
||||
import gql from 'graphql-tag';
|
||||
import { DocumentNode, Unmasked } from '@apollo/client';
|
||||
import { DocumentNode, MaybeMasked, Unmasked, useFragment } from '@apollo/client';
|
||||
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { getMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts';
|
||||
import {
|
||||
ChapterListFieldsFragment,
|
||||
ChapterType,
|
||||
DownloadState,
|
||||
DownloadStatusFieldsFragment,
|
||||
DownloadTypeFieldsFragment,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
@@ -138,6 +139,30 @@ export class Chapters {
|
||||
});
|
||||
}
|
||||
|
||||
static useDownloadStatusFromCache<T = DownloadTypeFieldsFragment>(
|
||||
id: number,
|
||||
fragment: DocumentNode = DOWNLOAD_TYPE_FIELDS,
|
||||
fragmentName: string = 'DOWNLOAD_TYPE_FIELDS',
|
||||
): MaybeMasked<T> | null {
|
||||
const downloadStatus = useFragment<T>({
|
||||
from: {
|
||||
__typename: 'DownloadType',
|
||||
chapter: {
|
||||
__ref: requestManager.graphQLClient.client.cache.identify({ __typename: 'ChapterType', id }),
|
||||
},
|
||||
},
|
||||
fragment,
|
||||
fragmentName,
|
||||
client: requestManager.graphQLClient.client,
|
||||
});
|
||||
|
||||
if (!downloadStatus.complete || !Object.keys(downloadStatus.data ?? {}).length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return downloadStatus.data;
|
||||
}
|
||||
|
||||
static getReaderUrl<Chapter extends ChapterMangaInfo & ChapterSourceOrderInfo>(chapter: Chapter): string {
|
||||
return AppRoutes.reader.path(chapter.mangaId, chapter.sourceOrder);
|
||||
}
|
||||
@@ -170,6 +195,15 @@ export class Chapters {
|
||||
return chapters.filter(Chapters.isDownloaded);
|
||||
}
|
||||
|
||||
static isDownloadable<Chapter extends ChapterDownloadInfo>(chapter: Chapter): boolean {
|
||||
const downloadStatus = Chapters.getDownloadStatusFromCache(chapter.id);
|
||||
return !Chapters.isDownloaded(chapter) && (!downloadStatus || downloadStatus.state === DownloadState.Error);
|
||||
}
|
||||
|
||||
static getDownloadable<Chapter extends ChapterDownloadInfo>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(this.isDownloadable);
|
||||
}
|
||||
|
||||
static isDeletable(
|
||||
{ isBookmarked, ...chapter }: ChapterDownloadInfo & ChapterBookmarkInfo,
|
||||
canDeleteBookmarked: boolean = false,
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import {
|
||||
ChapterBookmarkInfo,
|
||||
ChapterDownloadInfo,
|
||||
ChapterDownloadStatus,
|
||||
ChapterReadInfo,
|
||||
Chapters,
|
||||
} from '@/modules/chapter/services/Chapters.ts';
|
||||
import { DownloadState } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
export type ChapterWithMetaType<
|
||||
Chapter extends ChapterDownloadInfo & ChapterReadInfo & ChapterBookmarkInfo = ChapterDownloadInfo &
|
||||
ChapterReadInfo &
|
||||
ChapterBookmarkInfo,
|
||||
> = {
|
||||
chapter: Chapter;
|
||||
downloadChapter: ChapterDownloadStatus | undefined | null;
|
||||
};
|
||||
|
||||
export class ChaptersWithMeta {
|
||||
static getChapters<ChaptersWithMeta extends ChapterWithMetaType>(
|
||||
chapters: ChaptersWithMeta[],
|
||||
): ChaptersWithMeta['chapter'][] {
|
||||
return chapters.map(({ chapter }) => chapter);
|
||||
}
|
||||
|
||||
static getIds(chapters: ChapterWithMetaType[]): number[] {
|
||||
return Chapters.getIds(ChaptersWithMeta.getChapters(chapters));
|
||||
}
|
||||
|
||||
static getDownloaded<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => Chapters.isDownloaded(chapter));
|
||||
}
|
||||
|
||||
static getDeletable<Chapter extends ChapterWithMetaType>(
|
||||
chapters: Chapter[],
|
||||
canDeleteBookmarked?: boolean,
|
||||
): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => Chapters.isDeletable(chapter, canDeleteBookmarked));
|
||||
}
|
||||
|
||||
static getNonDownloaded<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => !Chapters.isDownloaded(chapter));
|
||||
}
|
||||
|
||||
static isDownloadable<Chapter extends ChapterWithMetaType>({ chapter, downloadChapter }: Chapter): boolean {
|
||||
return !Chapters.isDownloaded(chapter) && (!downloadChapter || downloadChapter?.state === DownloadState.Error);
|
||||
}
|
||||
|
||||
static getDownloadable<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(this.isDownloadable);
|
||||
}
|
||||
|
||||
static getBookmarked<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => Chapters.isBookmarked(chapter));
|
||||
}
|
||||
|
||||
static getNonBookmarked<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => !Chapters.isBookmarked(chapter));
|
||||
}
|
||||
|
||||
static getRead<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => Chapters.isRead(chapter));
|
||||
}
|
||||
|
||||
static getNonRead<Chapter extends ChapterWithMetaType>(chapters: Chapter[]): Chapter[] {
|
||||
return chapters.filter(({ chapter }) => !Chapters.isRead(chapter));
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DownloadState } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { ChapterDownloadStatus } from '@/modules/chapter/services/Chapters.ts';
|
||||
import { ChapterIdInfo, Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||
import { TranslationKey } from '@/Base.types.ts';
|
||||
|
||||
const DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP: { [state in DownloadState]: TranslationKey } = {
|
||||
@@ -21,9 +21,15 @@ const DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP: { [state in DownloadState]: Transla
|
||||
QUEUED: 'download.state.label.queued',
|
||||
} as const;
|
||||
|
||||
export const DownloadStateIndicator = ({ download }: { download: ChapterDownloadStatus }) => {
|
||||
export const DownloadStateIndicator = ({ chapterId }: { chapterId: ChapterIdInfo['id'] }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const download = Chapters.useDownloadStatusFromCache(chapterId);
|
||||
|
||||
if (!download) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isDownloading = download.state === DownloadState.Downloading;
|
||||
const isPartiallyDownloaded = download.progress !== 0;
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ const DownloadChapterItem = ({
|
||||
{item.chapter.name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<DownloadStateIndicator download={item} />
|
||||
<DownloadStateIndicator chapterId={item.chapter.id} />
|
||||
{item.state === DownloadState.Error && (
|
||||
<Tooltip title={t('global.button.retry')}>
|
||||
<IconButton
|
||||
|
||||
@@ -8,53 +8,33 @@
|
||||
|
||||
import { Virtuoso, VirtuosoProps } from 'react-virtuoso';
|
||||
import { useMemo } from 'react';
|
||||
import { IChapterWithMeta } from '@/modules/chapter/components/ChapterList.tsx';
|
||||
import { ChapterCard } from '@/modules/chapter/components/cards/ChapterCard.tsx';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { Chapters } from '@/modules/chapter/services/Chapters';
|
||||
|
||||
export const ReaderChapterList = ({
|
||||
currentChapter,
|
||||
chapters,
|
||||
style,
|
||||
}: Pick<ReaderStateChapters, 'chapters' | 'currentChapter'> & Pick<VirtuosoProps<any, any>, 'style'>) => {
|
||||
const downloadSubscription = requestManager.useDownloadSubscription();
|
||||
|
||||
const currentChapterIndex = useMemo(
|
||||
() => currentChapter && chapters.findIndex((chapter) => chapter.id === currentChapter.id),
|
||||
[currentChapter, chapters],
|
||||
);
|
||||
|
||||
const chaptersWithMeta: IChapterWithMeta[] = useMemo(
|
||||
() =>
|
||||
chapters.map((chapter) => {
|
||||
const downloadChapter = Chapters.getDownloadStatusFromCache(chapter.id);
|
||||
|
||||
return {
|
||||
chapter,
|
||||
downloadChapter,
|
||||
selected: null,
|
||||
};
|
||||
}),
|
||||
[downloadSubscription.data?.downloadStatusChanged, chapters],
|
||||
);
|
||||
|
||||
return (
|
||||
<Virtuoso
|
||||
style={{
|
||||
height: `calc(${chaptersWithMeta.length} * 100px)`,
|
||||
height: `calc(${chapters.length} * 100px)`,
|
||||
...style,
|
||||
}}
|
||||
initialTopMostItemIndex={currentChapterIndex ?? 0}
|
||||
totalCount={chaptersWithMeta.length}
|
||||
computeItemKey={(index) => chaptersWithMeta[index].chapter.id}
|
||||
totalCount={chapters.length}
|
||||
computeItemKey={(index) => chapters[index].id}
|
||||
itemContent={(index) => (
|
||||
<ChapterCard
|
||||
key={chaptersWithMeta[index].chapter.id}
|
||||
key={chapters[index].id}
|
||||
mode="reader"
|
||||
chapter={chaptersWithMeta[index].chapter}
|
||||
downloadChapter={chaptersWithMeta[index].downloadChapter}
|
||||
chapter={chapters[index]}
|
||||
allChapters={chapters}
|
||||
showChapterNumber={false}
|
||||
selected={null}
|
||||
|
||||
@@ -19,20 +19,17 @@ import { memo, useMemo, useRef } from 'react';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import { actionToTranslationKey, ChapterAction, Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||
import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { DownloadStateIndicator } from '@/modules/core/components/DownloadStateIndicator.tsx';
|
||||
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
||||
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
|
||||
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
||||
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
|
||||
import { ChapterWithMetaType } from '@/modules/chapter/services/ChaptersWithMeta.ts';
|
||||
|
||||
const DownloadButton = ({
|
||||
currentChapter,
|
||||
downloadChapter,
|
||||
}: Required<Pick<ReaderStateChapters, 'currentChapter'>> & Pick<ChapterWithMetaType, 'downloadChapter'>) => {
|
||||
const DownloadButton = ({ currentChapter }: Required<Pick<ReaderStateChapters, 'currentChapter'>>) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const downloadChapter = Chapters.getDownloadStatusFromCache(currentChapter?.id ?? -1);
|
||||
|
||||
if (currentChapter && Chapters.isDownloaded(currentChapter)) {
|
||||
return (
|
||||
<Tooltip title={t(actionToTranslationKey.delete.action.single)}>
|
||||
@@ -44,7 +41,7 @@ const DownloadButton = ({
|
||||
}
|
||||
|
||||
if (downloadChapter) {
|
||||
return <DownloadStateIndicator download={downloadChapter} />;
|
||||
return <DownloadStateIndicator chapterId={downloadChapter.chapter.id} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -74,16 +71,6 @@ const BaseReaderNavBarDesktopActions = memo(
|
||||
|
||||
const pageRetryKeyPrefix = useRef<number>(0);
|
||||
|
||||
const downloadSubscription = requestManager.useDownloadSubscription();
|
||||
|
||||
const downloadChapter = useMemo(() => {
|
||||
if (!currentChapter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Chapters.getDownloadStatusFromCache(currentChapter?.id);
|
||||
}, [downloadSubscription.data?.downloadStatusChanged, id]);
|
||||
|
||||
const haveSomePagesFailedToLoad = useMemo(
|
||||
() => pageLoadStates.some((pageLoadState) => pageLoadState.error),
|
||||
[pageLoadStates],
|
||||
@@ -118,7 +105,7 @@ const BaseReaderNavBarDesktopActions = memo(
|
||||
<ReplayIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<DownloadButton currentChapter={currentChapter} downloadChapter={downloadChapter} />
|
||||
<DownloadButton currentChapter={currentChapter} />
|
||||
<Tooltip title={t('chapter.action.label.open_on_source')}>
|
||||
<IconButton
|
||||
disabled={!realUrl}
|
||||
|
||||
@@ -30,7 +30,7 @@ import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||
|
||||
export const ChapterUpdateCard = ({ chapter }: { chapter: ChapterUpdateListFieldsFragment }) => {
|
||||
const { manga } = chapter;
|
||||
const download = Chapters.getDownloadStatusFromCache(chapter.id);
|
||||
const download = Chapters.useDownloadStatusFromCache(chapter.id);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const location = useLocation();
|
||||
@@ -112,7 +112,7 @@ export const ChapterUpdateCard = ({ chapter }: { chapter: ChapterUpdateListField
|
||||
</TypographyMaxLines>
|
||||
</Box>
|
||||
</Box>
|
||||
{download && <DownloadStateIndicator download={download} />}
|
||||
<DownloadStateIndicator chapterId={chapter.id} />
|
||||
{download?.state === DownloadState.Error && (
|
||||
<Tooltip title={t('global.button.retry')}>
|
||||
<IconButton
|
||||
|
||||
@@ -58,8 +58,6 @@ export const Updates: React.FC = () => {
|
||||
const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]);
|
||||
const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]);
|
||||
|
||||
requestManager.useDownloadSubscription();
|
||||
|
||||
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
|
||||
groupCounts,
|
||||
useCallback((index) => groupedUpdates[index][0], [groupedUpdates]),
|
||||
|
||||
Reference in New Issue
Block a user