diff --git a/src/App.tsx b/src/App.tsx index f7832a26..f4c16658 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -87,6 +87,8 @@ const ScrollToTop = () => { * and thus, data of existing chapters/mangas in the cache get outdated */ const BackgroundSubscriptions = () => { + // load the full download status once on startup to fill the cache + requestManager.useGetDownloadStatus({ nextFetchPolicy: 'standby' }); requestManager.useDownloadSubscription(); requestManager.useUpdaterSubscription(); requestManager.useWebUIUpdateSubscription(); diff --git a/src/lib/graphql/fragments/DownloadFragments.ts b/src/lib/graphql/fragments/DownloadFragments.ts index 8530430b..3aa8fb5e 100644 --- a/src/lib/graphql/fragments/DownloadFragments.ts +++ b/src/lib/graphql/fragments/DownloadFragments.ts @@ -8,7 +8,7 @@ import gql from 'graphql-tag'; -const DOWNLOAD_TYPE_FIELDS = gql` +export const DOWNLOAD_TYPE_FIELDS = gql` fragment DOWNLOAD_TYPE_FIELDS on DownloadType { chapter { id diff --git a/src/modules/chapter/components/ChapterList.tsx b/src/modules/chapter/components/ChapterList.tsx index f155cec8..033eaddd 100644 --- a/src/modules/chapter/components/ChapterList.tsx +++ b/src/modules/chapter/components/ChapterList.tsx @@ -100,8 +100,7 @@ export const ChapterList = ({ const scrollbarWidth = MediaQuery.useGetScrollbarSize('width'); - const { data: downloaderData } = requestManager.useGetDownloadStatus(); - const queue = downloaderData?.downloadStatus.queue ?? []; + const downloadSubscription = requestManager.useDownloadSubscription(); const [options, dispatch] = useChapterOptions(manga.id); const { @@ -132,15 +131,14 @@ export const ChapterList = ({ const chaptersWithMeta: IChapterWithMeta[] = useMemo( () => visibleChapters.map((chapter) => { - const downloadChapter = queue?.find((cd) => cd.chapter.id === chapter.id); const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null; return { chapter, - downloadChapter, + downloadChapter: Chapters.getDownloadStatusFromCache(chapter.id), selected, }; }), - [queue, selectedItemIds, visibleChapters], + [downloadSubscription.data?.downloadStatusChanged, selectedItemIds, visibleChapters], ); const chapterListFAB = useMemo(() => { diff --git a/src/modules/chapter/components/cards/ChapterCard.tsx b/src/modules/chapter/components/cards/ChapterCard.tsx index 7b035ff1..d15ae6f1 100644 --- a/src/modules/chapter/components/cards/ChapterCard.tsx +++ b/src/modules/chapter/components/cards/ChapterCard.tsx @@ -31,7 +31,6 @@ import { TypographyMaxLines } from '@/modules/core/components/TypographyMaxLines import { ChapterBookmarkInfo, ChapterDownloadInfo, - ChapterDownloadStatus, ChapterIdInfo, ChapterMangaInfo, ChapterNumberInfo, @@ -39,7 +38,7 @@ import { Chapters, ChapterScanlatorInfo, } 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 & ChapterMangaInfo & @@ -54,7 +53,7 @@ interface IProps { mode?: 'manga.page' | 'reader'; chapter: TChapter; allChapters: TChapter[]; - downloadChapter: ChapterDownloadStatus | undefined; + downloadChapter: ChapterWithMetaType['downloadChapter']; showChapterNumber: boolean; onSelect: (selected: boolean, isShiftKey?: boolean) => void; selected: boolean | null; diff --git a/src/modules/chapter/services/Chapters.ts b/src/modules/chapter/services/Chapters.ts index 0b79b4dd..df72981b 100644 --- a/src/modules/chapter/services/Chapters.ts +++ b/src/modules/chapter/services/Chapters.ts @@ -16,6 +16,7 @@ import { ChapterListFieldsFragment, ChapterType, DownloadStatusFieldsFragment, + DownloadTypeFieldsFragment, } from '@/lib/graphql/generated/graphql.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 { AppRoutes } from '@/modules/core/AppRoute.constants.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'; @@ -119,6 +121,23 @@ export class Chapters { }); } + static getDownloadStatusFromCache( + id: number, + fragment: DocumentNode = DOWNLOAD_TYPE_FIELDS, + fragmentName: string = 'DOWNLOAD_TYPE_FIELDS', + ): Unmasked | null { + return requestManager.graphQLClient.client.cache.readFragment({ + id: requestManager.graphQLClient.client.cache.identify({ + __typename: 'DownloadType', + chapter: { + __ref: requestManager.graphQLClient.client.cache.identify({ __typename: 'ChapterType', id }), + }, + }), + fragment, + fragmentName, + }); + } + static getReaderUrl(chapter: Chapter): string { return AppRoutes.reader.path(chapter.mangaId, chapter.sourceOrder); } diff --git a/src/modules/chapter/services/ChaptersWithMeta.ts b/src/modules/chapter/services/ChaptersWithMeta.ts index ca949c3b..9d72f2b6 100644 --- a/src/modules/chapter/services/ChaptersWithMeta.ts +++ b/src/modules/chapter/services/ChaptersWithMeta.ts @@ -21,7 +21,7 @@ export type ChapterWithMetaType< ChapterBookmarkInfo, > = { chapter: Chapter; - downloadChapter: ChapterDownloadStatus | undefined; + downloadChapter: ChapterDownloadStatus | undefined | null; }; export class ChaptersWithMeta { diff --git a/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx b/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx index 399f7c20..14f70040 100644 --- a/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx +++ b/src/modules/reader/components/overlay/navigation/ReaderChapterList.tsx @@ -12,14 +12,14 @@ 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 & Pick, 'style'>) => { - const { data: downloaderData } = requestManager.useGetDownloadStatus(); - const queue = downloaderData?.downloadStatus.queue ?? []; + const downloadSubscription = requestManager.useDownloadSubscription(); const currentChapterIndex = useMemo( () => currentChapter && chapters.findIndex((chapter) => chapter.id === currentChapter.id), @@ -29,7 +29,7 @@ export const ReaderChapterList = ({ const chaptersWithMeta: IChapterWithMeta[] = useMemo( () => chapters.map((chapter) => { - const downloadChapter = queue?.find((cd) => cd.chapter.id === chapter.id); + const downloadChapter = Chapters.getDownloadStatusFromCache(chapter.id); return { chapter, @@ -37,7 +37,7 @@ export const ReaderChapterList = ({ selected: null, }; }), - [queue, chapters], + [downloadSubscription.data?.downloadStatusChanged, chapters], ); return ( diff --git a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx index 46f00534..10979449 100644 --- a/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx +++ b/src/modules/reader/components/overlay/navigation/desktop/ReaderNavBarDesktopActions.tsx @@ -21,18 +21,16 @@ import { actionToTranslationKey, ChapterAction, Chapters } from '@/modules/chapt 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 { DownloadStatusFieldsFragment } from '@/lib/graphql/generated/graphql.ts'; 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> & { - downloadChapter?: DownloadStatusFieldsFragment['queue'][number]; -}) => { +}: Required> & Pick) => { const { t } = useTranslation(); if (currentChapter && Chapters.isDownloaded(currentChapter)) { @@ -76,13 +74,15 @@ const BaseReaderNavBarDesktopActions = memo( const pageRetryKeyPrefix = useRef(0); - const { data: downloaderData } = requestManager.useGetDownloadStatus(); - const queue = downloaderData?.downloadStatus.queue ?? []; + const downloadSubscription = requestManager.useDownloadSubscription(); - const downloadChapter = useMemo( - () => queue.find((queueItem) => queueItem.chapter.id === currentChapter?.id), - [queue, id], - ); + const downloadChapter = useMemo(() => { + if (!currentChapter) { + return null; + } + + return Chapters.getDownloadStatusFromCache(currentChapter?.id); + }, [downloadSubscription.data?.downloadStatusChanged, id]); const haveSomePagesFailedToLoad = useMemo( () => pageLoadStates.some((pageLoadState) => pageLoadState.error), diff --git a/src/modules/updates/screens/Updates.tsx b/src/modules/updates/screens/Updates.tsx index b57719e3..f3954c51 100644 --- a/src/modules/updates/screens/Updates.tsx +++ b/src/modules/updates/screens/Updates.tsx @@ -34,7 +34,7 @@ import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx'; import { dateTimeFormatter, epochToDate, getDateString } from '@/util/DateHelper.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; 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 { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; @@ -74,8 +74,8 @@ export const Updates: React.FC = () => { const updateEntries = chapterUpdateData?.chapters.nodes ?? []; const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]); 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( groupCounts, @@ -107,11 +107,6 @@ export const Updates: React.FC = () => { }; }, [t, lastUpdateTimestamp]); - const downloadForChapter = (chapter: Pick & ChapterMangaInfo) => { - const { sourceOrder, mangaId } = chapter; - return queue.find((q) => sourceOrder === q.chapter.sourceOrder && mangaId === q.manga.id); - }; - const handleRetry = async (chapter: ChapterIdInfo) => { try { await requestManager.addChapterToDownloadQueue(chapter.id).response; @@ -186,7 +181,7 @@ export const Updates: React.FC = () => { itemContent={(index) => { const chapter = updateEntries[index]; const { manga } = chapter; - const download = downloadForChapter(chapter); + const download = Chapters.getDownloadStatusFromCache(chapter.id); return (