Read chapter download status from cache

Instead of getting the whole download status, which contains all queued/active downloads, the download status should be read from the cache for only the required chapters
This commit is contained in:
schroda
2025-01-12 14:09:10 +01:00
parent abc3908b1e
commit 57f73636a0
9 changed files with 46 additions and 33 deletions

View File

@@ -87,6 +87,8 @@ const ScrollToTop = () => {
* and thus, data of existing chapters/mangas in the cache get outdated * and thus, data of existing chapters/mangas in the cache get outdated
*/ */
const BackgroundSubscriptions = () => { const BackgroundSubscriptions = () => {
// load the full download status once on startup to fill the cache
requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' });
requestManager.useDownloadSubscription(); requestManager.useDownloadSubscription();
requestManager.useUpdaterSubscription(); requestManager.useUpdaterSubscription();
requestManager.useWebUIUpdateSubscription(); requestManager.useWebUIUpdateSubscription();

View File

@@ -8,7 +8,7 @@
import gql from 'graphql-tag'; import gql from 'graphql-tag';
const DOWNLOAD_TYPE_FIELDS = gql` export const DOWNLOAD_TYPE_FIELDS = gql`
fragment DOWNLOAD_TYPE_FIELDS on DownloadType { fragment DOWNLOAD_TYPE_FIELDS on DownloadType {
chapter { chapter {
id id

View File

@@ -100,8 +100,7 @@ export const ChapterList = ({
const scrollbarWidth = MediaQuery.useGetScrollbarSize('width'); const scrollbarWidth = MediaQuery.useGetScrollbarSize('width');
const { data: downloaderData } = requestManager.useGetDownloadStatus(); const downloadSubscription = requestManager.useDownloadSubscription();
const queue = downloaderData?.downloadStatus.queue ?? [];
const [options, dispatch] = useChapterOptions(manga.id); const [options, dispatch] = useChapterOptions(manga.id);
const { const {
@@ -132,15 +131,14 @@ export const ChapterList = ({
const chaptersWithMeta: IChapterWithMeta[] = useMemo( const chaptersWithMeta: IChapterWithMeta[] = useMemo(
() => () =>
visibleChapters.map((chapter) => { visibleChapters.map((chapter) => {
const downloadChapter = queue?.find((cd) => cd.chapter.id === chapter.id);
const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null; const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null;
return { return {
chapter, chapter,
downloadChapter, downloadChapter: Chapters.getDownloadStatusFromCache(chapter.id),
selected, selected,
}; };
}), }),
[queue, selectedItemIds, visibleChapters], [downloadSubscription.data?.downloadStatusChanged, selectedItemIds, visibleChapters],
); );
const chapterListFAB = useMemo(() => { const chapterListFAB = useMemo(() => {

View File

@@ -31,7 +31,6 @@ import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines
import { import {
ChapterBookmarkInfo, ChapterBookmarkInfo,
ChapterDownloadInfo, ChapterDownloadInfo,
ChapterDownloadStatus,
ChapterIdInfo, ChapterIdInfo,
ChapterMangaInfo, ChapterMangaInfo,
ChapterNumberInfo, ChapterNumberInfo,
@@ -39,7 +38,7 @@ import {
Chapters, Chapters,
ChapterScanlatorInfo, ChapterScanlatorInfo,
} from '@/modules/chapter/services/Chapters.ts'; } from '@/modules/chapter/services/Chapters.ts';
import { ChaptersWithMeta } from '@/modules/chapter/services/ChaptersWithMeta.ts'; import { ChaptersWithMeta, ChapterWithMetaType } from '@/modules/chapter/services/ChaptersWithMeta.ts';
type TChapter = ChapterIdInfo & type TChapter = ChapterIdInfo &
ChapterMangaInfo & ChapterMangaInfo &
@@ -54,7 +53,7 @@ interface IProps {
mode?: 'manga.page' | 'reader'; mode?: 'manga.page' | 'reader';
chapter: TChapter; chapter: TChapter;
allChapters: TChapter[]; allChapters: TChapter[];
downloadChapter: ChapterDownloadStatus | undefined; downloadChapter: ChapterWithMetaType['downloadChapter'];
showChapterNumber: boolean; showChapterNumber: boolean;
onSelect: (selected: boolean, isShiftKey?: boolean) => void; onSelect: (selected: boolean, isShiftKey?: boolean) => void;
selected: boolean | null; selected: boolean | null;

View File

@@ -16,6 +16,7 @@ import {
ChapterListFieldsFragment, ChapterListFieldsFragment,
ChapterType, ChapterType,
DownloadStatusFieldsFragment, DownloadStatusFieldsFragment,
DownloadTypeFieldsFragment,
} from '@/lib/graphql/generated/graphql.ts'; } from '@/lib/graphql/generated/graphql.ts';
import { CHAPTER_LIST_FIELDS } from '@/lib/graphql/fragments/ChapterFragments.ts'; import { CHAPTER_LIST_FIELDS } from '@/lib/graphql/fragments/ChapterFragments.ts';
@@ -24,6 +25,7 @@ import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
import { ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts'; import { ReaderResumeMode } from '@/modules/reader/types/Reader.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { DOWNLOAD_TYPE_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts';
export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread'; export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
@@ -119,6 +121,23 @@ export class Chapters {
}); });
} }
static getDownloadStatusFromCache<T = DownloadTypeFieldsFragment>(
id: number,
fragment: DocumentNode = DOWNLOAD_TYPE_FIELDS,
fragmentName: string = 'DOWNLOAD_TYPE_FIELDS',
): Unmasked<T> | null {
return requestManager.graphQLClient.client.cache.readFragment<T>({
id: requestManager.graphQLClient.client.cache.identify({
__typename: 'DownloadType',
chapter: {
__ref: requestManager.graphQLClient.client.cache.identify({ __typename: 'ChapterType', id }),
},
}),
fragment,
fragmentName,
});
}
static getReaderUrl<Chapter extends ChapterMangaInfo & ChapterSourceOrderInfo>(chapter: Chapter): string { static getReaderUrl<Chapter extends ChapterMangaInfo & ChapterSourceOrderInfo>(chapter: Chapter): string {
return AppRoutes.reader.path(chapter.mangaId, chapter.sourceOrder); return AppRoutes.reader.path(chapter.mangaId, chapter.sourceOrder);
} }

View File

@@ -21,7 +21,7 @@ export type ChapterWithMetaType<
ChapterBookmarkInfo, ChapterBookmarkInfo,
> = { > = {
chapter: Chapter; chapter: Chapter;
downloadChapter: ChapterDownloadStatus | undefined; downloadChapter: ChapterDownloadStatus | undefined | null;
}; };
export class ChaptersWithMeta { export class ChaptersWithMeta {

View File

@@ -12,14 +12,14 @@ import { IChapterWithMeta } from '@/modules/chapter/components/ChapterList.tsx';
import { ChapterCard } from '@/modules/chapter/components/cards/ChapterCard.tsx'; import { ChapterCard } from '@/modules/chapter/components/cards/ChapterCard.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts'; import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
import { Chapters } from '@/modules/chapter/services/Chapters';
export const ReaderChapterList = ({ export const ReaderChapterList = ({
currentChapter, currentChapter,
chapters, chapters,
style, style,
}: Pick<ReaderStateChapters, 'chapters' | 'currentChapter'> & Pick<VirtuosoProps<any, any>, 'style'>) => { }: Pick<ReaderStateChapters, 'chapters' | 'currentChapter'> & Pick<VirtuosoProps<any, any>, 'style'>) => {
const { data: downloaderData } = requestManager.useGetDownloadStatus(); const downloadSubscription = requestManager.useDownloadSubscription();
const queue = downloaderData?.downloadStatus.queue ?? [];
const currentChapterIndex = useMemo( const currentChapterIndex = useMemo(
() => currentChapter && chapters.findIndex((chapter) => chapter.id === currentChapter.id), () => currentChapter && chapters.findIndex((chapter) => chapter.id === currentChapter.id),
@@ -29,7 +29,7 @@ export const ReaderChapterList = ({
const chaptersWithMeta: IChapterWithMeta[] = useMemo( const chaptersWithMeta: IChapterWithMeta[] = useMemo(
() => () =>
chapters.map((chapter) => { chapters.map((chapter) => {
const downloadChapter = queue?.find((cd) => cd.chapter.id === chapter.id); const downloadChapter = Chapters.getDownloadStatusFromCache(chapter.id);
return { return {
chapter, chapter,
@@ -37,7 +37,7 @@ export const ReaderChapterList = ({
selected: null, selected: null,
}; };
}), }),
[queue, chapters], [downloadSubscription.data?.downloadStatusChanged, chapters],
); );
return ( return (

View File

@@ -21,18 +21,16 @@ import { actionToTranslationKey, ChapterAction, Chapters } from '@/modules/chapt
import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts'; import { ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { DownloadStateIndicator } from '@/modules/core/components/DownloadStateIndicator.tsx'; import { DownloadStateIndicator } from '@/modules/core/components/DownloadStateIndicator.tsx';
import { DownloadStatusFieldsFragment } from '@/lib/graphql/generated/graphql.ts';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts'; import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx'; import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx'; import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
import { ChapterWithMetaType } from '@/modules/chapter/services/ChaptersWithMeta.ts';
const DownloadButton = ({ const DownloadButton = ({
currentChapter, currentChapter,
downloadChapter, downloadChapter,
}: Required<Pick<ReaderStateChapters, 'currentChapter'>> & { }: Required<Pick<ReaderStateChapters, 'currentChapter'>> & Pick<ChapterWithMetaType, 'downloadChapter'>) => {
downloadChapter?: DownloadStatusFieldsFragment['queue'][number];
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
if (currentChapter && Chapters.isDownloaded(currentChapter)) { if (currentChapter && Chapters.isDownloaded(currentChapter)) {
@@ -76,13 +74,15 @@ const BaseReaderNavBarDesktopActions = memo(
const pageRetryKeyPrefix = useRef<number>(0); const pageRetryKeyPrefix = useRef<number>(0);
const { data: downloaderData } = requestManager.useGetDownloadStatus(); const downloadSubscription = requestManager.useDownloadSubscription();
const queue = downloaderData?.downloadStatus.queue ?? [];
const downloadChapter = useMemo( const downloadChapter = useMemo(() => {
() => queue.find((queueItem) => queueItem.chapter.id === currentChapter?.id), if (!currentChapter) {
[queue, id], return null;
); }
return Chapters.getDownloadStatusFromCache(currentChapter?.id);
}, [downloadSubscription.data?.downloadStatusChanged, id]);
const haveSomePagesFailedToLoad = useMemo( const haveSomePagesFailedToLoad = useMemo(
() => pageLoadStates.some((pageLoadState) => pageLoadState.error), () => pageLoadStates.some((pageLoadState) => pageLoadState.error),

View File

@@ -34,7 +34,7 @@ import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx';
import { dateTimeFormatter, epochToDate, getDateString } from '@/util/DateHelper.ts'; import { dateTimeFormatter, epochToDate, getDateString } from '@/util/DateHelper.ts';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines.tsx'; import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines.tsx';
import { ChapterIdInfo, ChapterMangaInfo } from '@/modules/chapter/services/Chapters.ts'; import { ChapterIdInfo, Chapters } from '@/modules/chapter/services/Chapters.ts';
import { makeToast } from '@/modules/core/utils/Toast.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts';
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx'; import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
@@ -74,8 +74,8 @@ export const Updates: React.FC = () => {
const updateEntries = chapterUpdateData?.chapters.nodes ?? []; const updateEntries = chapterUpdateData?.chapters.nodes ?? [];
const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]); const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]);
const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]); const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]);
const { data: downloaderData } = requestManager.useGetDownloadStatus();
const queue = downloaderData?.downloadStatus.queue ?? []; requestManager.useDownloadSubscription();
const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey( const computeItemKey = VirtuosoUtil.useCreateGroupedComputeItemKey(
groupCounts, groupCounts,
@@ -107,11 +107,6 @@ export const Updates: React.FC = () => {
}; };
}, [t, lastUpdateTimestamp]); }, [t, lastUpdateTimestamp]);
const downloadForChapter = (chapter: Pick<ChapterType, 'sourceOrder'> & ChapterMangaInfo) => {
const { sourceOrder, mangaId } = chapter;
return queue.find((q) => sourceOrder === q.chapter.sourceOrder && mangaId === q.manga.id);
};
const handleRetry = async (chapter: ChapterIdInfo) => { const handleRetry = async (chapter: ChapterIdInfo) => {
try { try {
await requestManager.addChapterToDownloadQueue(chapter.id).response; await requestManager.addChapterToDownloadQueue(chapter.id).response;
@@ -186,7 +181,7 @@ export const Updates: React.FC = () => {
itemContent={(index) => { itemContent={(index) => {
const chapter = updateEntries[index]; const chapter = updateEntries[index];
const { manga } = chapter; const { manga } = chapter;
const download = downloadForChapter(chapter); const download = Chapters.getDownloadStatusFromCache(chapter.id);
return ( return (
<StyledGroupItemWrapper> <StyledGroupItemWrapper>