From 2fa1de578cd1ffc93d44e5762fdc859c14d78727 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:20:28 +0200 Subject: [PATCH] Reduce requested chapter data in queries --- .../chapter/ChapterActionMenuItems.tsx | 15 +-- src/components/chapter/ChapterCard.tsx | 23 +++- src/components/chapter/ChapterList.tsx | 19 ++- src/components/chapter/util.tsx | 27 +++-- src/components/navbar/ReaderNavBar.tsx | 7 +- src/lib/data/Chapters.ts | 29 ++--- src/lib/data/ChaptersWithMeta.ts | 9 +- src/lib/graphql/fragments/ChapterFragments.ts | 84 +++++++++++++ src/lib/graphql/generated/apollo-helpers.ts | 7 +- src/lib/graphql/generated/graphql.ts | 57 +++++++-- src/lib/graphql/mutations/ChapterMutation.ts | 8 +- src/lib/graphql/queries/ChapterQuery.ts | 110 +++++++++++++++--- src/lib/metadata/metadata.ts | 6 +- src/lib/requests/RequestManager.ts | 103 +++++++--------- src/screens/DownloadQueue.tsx | 4 +- src/screens/Reader.tsx | 45 ++++--- src/screens/Updates.tsx | 15 +-- src/typings.ts | 6 +- tools/scripts/codegenFormatter.ts | 7 +- 19 files changed, 400 insertions(+), 181 deletions(-) create mode 100644 src/lib/graphql/fragments/ChapterFragments.ts diff --git a/src/components/chapter/ChapterActionMenuItems.tsx b/src/components/chapter/ChapterActionMenuItems.tsx index 31621252..3f40f9fd 100644 --- a/src/components/chapter/ChapterActionMenuItems.tsx +++ b/src/components/chapter/ChapterActionMenuItems.tsx @@ -29,7 +29,6 @@ import { ChapterRealUrlInfo, Chapters, } from '@/lib/data/Chapters.ts'; -import { TChapter } from '@/typings.ts'; import { MenuItem } from '@/components/menu/MenuItem.tsx'; import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx'; import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts'; @@ -39,13 +38,15 @@ import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings type BaseProps = { onClose: () => void }; +type TChapter = ChapterIdInfo & + ChapterMangaInfo & + ChapterDownloadInfo & + ChapterBookmarkInfo & + ChapterReadInfo & + ChapterRealUrlInfo; + type SingleModeProps = { - chapter: ChapterIdInfo & - ChapterMangaInfo & - ChapterDownloadInfo & - ChapterBookmarkInfo & - ChapterReadInfo & - ChapterRealUrlInfo; + chapter: TChapter; allChapters: TChapter[]; handleSelection?: SelectableCollectionReturnType['handleSelection']; canBeDownloaded: boolean; diff --git a/src/components/chapter/ChapterCard.tsx b/src/components/chapter/ChapterCard.tsx index 3be182ff..5e7c709f 100644 --- a/src/components/chapter/ChapterCard.tsx +++ b/src/components/chapter/ChapterCard.tsx @@ -24,11 +24,28 @@ import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state'; import { useLongPress } from 'use-long-press'; import { getDateString } from '@/util/date.ts'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator.tsx'; -import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; -import { TChapter } from '@/typings.ts'; +import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx'; import { Menu } from '@/components/menu/Menu.tsx'; import { TypographyMaxLines } from '@/components/atoms/TypographyMaxLines.tsx'; +import { + ChapterBookmarkInfo, + ChapterDownloadInfo, + ChapterIdInfo, + ChapterMangaInfo, + ChapterNumberInfo, + ChapterReadInfo, + ChapterScanlatorInfo, +} from '@/lib/data/Chapters.ts'; + +type TChapter = ChapterIdInfo & + ChapterMangaInfo & + ChapterDownloadInfo & + ChapterReadInfo & + ChapterBookmarkInfo & + ChapterNumberInfo & + ChapterScanlatorInfo & + Pick; interface IProps { chapter: TChapter; @@ -86,7 +103,7 @@ export const ChapterCard: React.FC = (props: IProps) => { ({ margin: 8, @@ -61,7 +66,7 @@ const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({ })); export interface IChapterWithMeta { - chapter: TChapter; + chapter: React.ComponentProps['chapter']; downloadChapter: DownloadType | undefined; selected: boolean | null; } @@ -83,7 +88,11 @@ export const ChapterList: React.FC = ({ manga, isRefreshing }) => { loading: isLoading, error, refetch, - } = requestManager.useGetMangaChapters(manga.id, { notifyOnNetworkStatusChange: true }); + } = requestManager.useGetMangaChapters( + GET_CHAPTERS_MANGA, + manga.id, + { notifyOnNetworkStatusChange: true }, + ); const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]); const chapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]); @@ -106,7 +115,7 @@ export const ChapterList: React.FC = ({ manga, isRefreshing }) => { () => visibleChapters.map((chapter) => { const downloadChapter = queue?.find( - (cd) => cd.chapter.sourceOrder === chapter.sourceOrder && cd.chapter.manga.id === chapter.manga.id, + (cd) => cd.chapter.sourceOrder === chapter.sourceOrder && cd.chapter.manga.id === chapter.mangaId, ); const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null; return { diff --git a/src/components/chapter/util.tsx b/src/components/chapter/util.tsx index 34e420fd..142df138 100644 --- a/src/components/chapter/util.tsx +++ b/src/components/chapter/util.tsx @@ -12,10 +12,11 @@ import { ChapterOptionsReducerAction, ChapterSortMode, NullAndUndefined, - TChapter, TranslationKey, } from '@/typings.ts'; import { useReducerLocalStorage } from '@/util/useStorage.tsx'; +import { ChapterBookmarkInfo, ChapterDownloadInfo, ChapterReadInfo } from '@/lib/data/Chapters.ts'; +import { ChapterType } from '@/lib/graphql/generated/graphql.ts'; const defaultChapterOptions: ChapterListOptions = { active: false, @@ -46,7 +47,7 @@ function chapterOptionsReducer(state: ChapterListOptions, actions: ChapterOption } } -export function unreadFilter(unread: NullAndUndefined, { isRead: isChapterRead }: TChapter) { +export function unreadFilter(unread: NullAndUndefined, { isRead: isChapterRead }: ChapterReadInfo) { switch (unread) { case true: return !isChapterRead; @@ -57,7 +58,7 @@ export function unreadFilter(unread: NullAndUndefined, { isRead: isChap } } -function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: chapterDownload }: TChapter) { +function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: chapterDownload }: ChapterDownloadInfo) { switch (downloaded) { case true: return chapterDownload; @@ -68,7 +69,10 @@ function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: c } } -function bookmarkedFilter(bookmarked: NullAndUndefined, { isBookmarked: chapterBookmarked }: TChapter) { +function bookmarkedFilter( + bookmarked: NullAndUndefined, + { isBookmarked: chapterBookmarked }: ChapterBookmarkInfo, +) { switch (bookmarked) { case true: return chapterBookmarked; @@ -79,11 +83,12 @@ function bookmarkedFilter(bookmarked: NullAndUndefined, { isBookmarked: } } -const sortChapters = ( - chapters: TChapter[], +type TChapterSort = Pick; +const sortChapters = ( + chapters: T[], { sortBy, reverse }: Pick, -): TChapter[] => { - const sortedChapters: TChapter[] = [...chapters]; +): T[] => { + const sortedChapters: T[] = [...chapters]; switch (sortBy) { case 'source': @@ -109,7 +114,11 @@ const sortChapters = ( return sortedChapters; }; -export function filterAndSortChapters(chapters: TChapter[], options: ChapterListOptions): TChapter[] { +type TChapterFilter = TChapterSort & ChapterReadInfo & ChapterDownloadInfo & ChapterBookmarkInfo; +export function filterAndSortChapters( + chapters: Chapters[], + options: ChapterListOptions, +): Chapters[] { const filtered = options.active ? chapters.filter( (chp) => diff --git a/src/components/navbar/ReaderNavBar.tsx b/src/components/navbar/ReaderNavBar.tsx index 4e7cd768..5cc7e9be 100644 --- a/src/components/navbar/ReaderNavBar.tsx +++ b/src/components/navbar/ReaderNavBar.tsx @@ -27,11 +27,12 @@ import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import Collapse from '@mui/material/Collapse'; import { useTranslation } from 'react-i18next'; -import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, TChapter, TManga } from '@/typings'; +import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, TManga } from '@/typings'; import { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions'; import { useBackButton } from '@/util/useBackButton.ts'; import { Select } from '@/components/atoms/Select.tsx'; import { getOptionForDirection } from '@/theme.ts'; +import { ChapterType } from '@/lib/graphql/generated/graphql.ts'; const Root = styled('div')({ zIndex: 10, @@ -123,8 +124,8 @@ interface IProps { settings: IReaderSettings; setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void; manga: TManga; - chapter: TChapter; - chapters: TChapter[]; + chapter: Pick; + chapters: Pick[]; curPage: number; scrollToPage: (page: number) => void; openNextChapter: (offset: ChapterOffset) => void; diff --git a/src/lib/data/Chapters.ts b/src/lib/data/Chapters.ts index 8a9941f9..21e1db03 100644 --- a/src/lib/data/Chapters.ts +++ b/src/lib/data/Chapters.ts @@ -9,11 +9,12 @@ import { t as translate } from 'i18next'; import gql from 'graphql-tag'; import { DocumentNode } from '@apollo/client'; -import { ChapterOffset, TChapter, TManga, TranslationKey } from '@/typings.ts'; +import { ChapterOffset, TManga, TranslationKey } from '@/typings.ts'; import { makeToast } from '@/components/util/Toast.tsx'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; -import { FULL_CHAPTER_FIELDS } from '@/lib/graphql/Fragments.ts'; +import { ChapterListFieldsFragment, ChapterType } from '@/lib/graphql/generated/graphql.ts'; +import { CHAPTER_LIST_FIELDS } from '@/lib/graphql/fragments/ChapterFragments.ts'; export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread'; @@ -77,24 +78,24 @@ export const actionToTranslationKey: { }, }; -export type ChapterIdInfo = Pick; -export type ChapterMangaInfo = Pick; -export type ChapterDownloadInfo = ChapterIdInfo & Pick; -export type ChapterBookmarkInfo = ChapterIdInfo & Pick; -export type ChapterReadInfo = ChapterIdInfo & Pick; -export type ChapterNumberInfo = ChapterIdInfo & Pick; -export type ChapterScanlatorInfo = ChapterIdInfo & Pick; -export type ChapterRealUrlInfo = Pick; +export type ChapterIdInfo = Pick; +export type ChapterMangaInfo = Pick; +export type ChapterDownloadInfo = ChapterIdInfo & Pick; +export type ChapterBookmarkInfo = ChapterIdInfo & Pick; +export type ChapterReadInfo = ChapterIdInfo & Pick; +export type ChapterNumberInfo = ChapterIdInfo & Pick; +export type ChapterScanlatorInfo = ChapterIdInfo & Pick; +export type ChapterRealUrlInfo = Pick; export class Chapters { static getIds(chapters: { id: number }[]): number[] { return chapters.map((chapter) => chapter.id); } - static getFromCache( + static getFromCache( id: number, - fragment: DocumentNode = FULL_CHAPTER_FIELDS, - fragmentName: string = 'FULL_CHAPTER_FIELDS', + fragment: DocumentNode = CHAPTER_LIST_FIELDS, + fragmentName: string = 'CHAPTER_LIST_FIELDS', ): T | null { return requestManager.graphQLClient.client.cache.readFragment({ id: requestManager.graphQLClient.client.cache.identify({ @@ -107,7 +108,7 @@ export class Chapters { } static isDownloading(id: number): boolean { - return !!requestManager.graphQLClient.client.cache.readFragment({ + return !!requestManager.graphQLClient.client.cache.readFragment({ id: requestManager.graphQLClient.client.cache.identify({ __typename: 'DownloadType', chapter: { diff --git a/src/lib/data/ChaptersWithMeta.ts b/src/lib/data/ChaptersWithMeta.ts index bc8d541b..0a92fadf 100644 --- a/src/lib/data/ChaptersWithMeta.ts +++ b/src/lib/data/ChaptersWithMeta.ts @@ -6,17 +6,18 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { TChapter } from '@/typings.ts'; import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; -import { Chapters } from '@/lib/data/Chapters.ts'; +import { ChapterBookmarkInfo, ChapterDownloadInfo, ChapterReadInfo, Chapters } from '@/lib/data/Chapters.ts'; export type ChapterWithMetaType = { - chapter: TChapter; + chapter: ChapterDownloadInfo & ChapterReadInfo & ChapterBookmarkInfo; downloadChapter: DownloadType | undefined; }; export class ChaptersWithMeta { - static getChapters(chapters: ChapterWithMetaType[]): TChapter[] { + static getChapters( + chapters: ChaptersWithMeta[], + ): ChaptersWithMeta['chapter'][] { return chapters.map(({ chapter }) => chapter); } diff --git a/src/lib/graphql/fragments/ChapterFragments.ts b/src/lib/graphql/fragments/ChapterFragments.ts new file mode 100644 index 00000000..0ed89fa5 --- /dev/null +++ b/src/lib/graphql/fragments/ChapterFragments.ts @@ -0,0 +1,84 @@ +/* + * 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 gql from 'graphql-tag'; + +const MANGA_BASE_FIELDS = gql` + fragment MANGA_BASE_FIELDS on MangaType { + id + title + + thumbnailUrl + thumbnailUrlLastFetched + + inLibrary + initialized + } +`; + +export const CHAPTER_BASE_FIELDS = gql` + fragment CHAPTER_BASE_FIELDS on ChapterType { + id + name + + mangaId + scanlator + realUrl + + sourceOrder + chapterNumber + } +`; + +export const CHAPTER_STATE_FIELDS = gql` + fragment CHAPTER_STATE_FIELDS on ChapterType { + id + isRead + isDownloaded + isBookmarked + } +`; + +export const CHAPTER_READER_FIELDS = gql` + ${CHAPTER_BASE_FIELDS} + ${CHAPTER_STATE_FIELDS} + + fragment CHAPTER_READER_FIELDS on ChapterType { + ...CHAPTER_BASE_FIELDS + ...CHAPTER_STATE_FIELDS + + lastPageRead + pageCount + } +`; + +export const CHAPTER_LIST_FIELDS = gql` + ${CHAPTER_BASE_FIELDS} + ${CHAPTER_STATE_FIELDS} + + fragment CHAPTER_LIST_FIELDS on ChapterType { + ...CHAPTER_BASE_FIELDS + ...CHAPTER_STATE_FIELDS + + fetchedAt + uploadDate + } +`; + +export const CHAPTER_UPDATE_LIST_FIELDS = gql` + ${CHAPTER_LIST_FIELDS} + ${MANGA_BASE_FIELDS} + + fragment CHAPTER_UPDATE_LIST_FIELDS on ChapterType { + ...CHAPTER_LIST_FIELDS + + manga { + ...MANGA_BASE_FIELDS + } + } +`; diff --git a/src/lib/graphql/generated/apollo-helpers.ts b/src/lib/graphql/generated/apollo-helpers.ts index 70a99a90..335cdf16 100644 --- a/src/lib/graphql/generated/apollo-helpers.ts +++ b/src/lib/graphql/generated/apollo-helpers.ts @@ -1,7 +1,6 @@ import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache'; import { - GetChapterQueryVariables, - GetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables, + GetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables, GetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables, } from "@/lib/graphql/generated/graphql.ts"; import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies"; @@ -575,8 +574,8 @@ export type QueryFieldPolicy = { aboutWebUI?: FieldPolicy | FieldReadFunction, categories?: FieldPolicy | FieldReadFunction, category?: FieldPolicy | FieldReadFunction, - chapter?: FieldPolicy> | FieldReadFunction>, - chapters?: FieldPolicy | FieldReadFunction, + chapter?: FieldPolicy | FieldReadFunction, + chapters?: FieldPolicy | FieldReadFunction, checkForServerUpdates?: FieldPolicy | FieldReadFunction, checkForWebUIUpdate?: FieldPolicy | FieldReadFunction, downloadStatus?: FieldPolicy> | FieldReadFunction>, diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index 97e51b6c..4e81e587 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -2638,6 +2638,18 @@ export type CategoryLibraryFieldsFragment = { __typename?: 'CategoryType', id: n export type CategorySettingFieldsFragment = { __typename?: 'CategoryType', includeInUpdate: IncludeOrExclude, includeInDownload: IncludeOrExclude, id: number, name: string, default: boolean, order: number }; +export type MangaBaseFieldsFragment = { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean }; + +export type ChapterBaseFieldsFragment = { __typename?: 'ChapterType', id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number }; + +export type ChapterStateFieldsFragment = { __typename?: 'ChapterType', id: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }; + +export type ChapterReaderFieldsFragment = { __typename?: 'ChapterType', lastPageRead: number, pageCount: number, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }; + +export type ChapterListFieldsFragment = { __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }; + +export type ChapterUpdateListFieldsFragment = { __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean } }; + export type CreateBackupMutationVariables = Exact<{ input: CreateBackupInput; }>; @@ -2728,7 +2740,7 @@ export type GetMangaChaptersFetchMutationVariables = Exact<{ }>; -export type GetMangaChaptersFetchMutation = { __typename?: 'Mutation', fetchChapters?: { __typename?: 'FetchChaptersPayload', clientMutationId?: string | null, chapters: Array<{ __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> }> } | null }; +export type GetMangaChaptersFetchMutation = { __typename?: 'Mutation', fetchChapters?: { __typename?: 'FetchChaptersPayload', clientMutationId?: string | null, chapters: Array<{ __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number, chapters: { __typename?: 'ChapterNodeList', totalCount: number } } }> } | null }; export type SetChapterMetadataMutationVariables = Exact<{ input: SetChapterMetaInput; @@ -3132,14 +3144,7 @@ export type GetCategoryMangasQueryVariables = Exact<{ export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', unreadCount: number, downloadCount: number, bookmarkCount: number, artist?: string | null, author?: string | null, chaptersLastFetchedAt?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, inLibraryAt: string, initialized: boolean, lastFetchedAt?: string | null, realUrl?: string | null, status: MangaStatus, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, title: string, url: string, lastReadChapter?: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } | null, latestReadChapter?: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } | null, latestFetchedChapter?: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } | null, latestUploadedChapter?: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } | null, firstUnreadChapter?: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } | null, categories: { __typename?: 'CategoryNodeList', totalCount: number, nodes: Array<{ __typename?: 'CategoryType', default: boolean, id: number, includeInUpdate: IncludeOrExclude, includeInDownload: IncludeOrExclude, name: string, order: number, meta: Array<{ __typename?: 'CategoryMetaType', key: string, value: string }>, mangas: { __typename?: 'MangaNodeList', totalCount: number } }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, meta: Array<{ __typename?: 'MangaMetaType', key: string, value: string }>, source?: { __typename?: 'SourceType', displayName: string, iconUrl: string, id: string, isConfigurable: boolean, isNsfw: boolean, lang: string, name: string, supportsLatest: boolean, extension: { __typename?: 'ExtensionType', pkgName: string, repo?: string | null }, meta: Array<{ __typename?: 'SourceMetaType', key: string, value: string }> } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', title: string, remoteUrl: string, remoteId: string, id: number, status: number, lastChapterRead: number, totalChapters: number, score: number, displayScore: string, startDate: string, finishDate: string, tracker: { __typename?: 'TrackerType', id: number, name: string, authUrl?: string | null, icon: string, supportsTrackDeletion?: boolean | null, isLoggedIn: boolean, isTokenExpired: boolean, scores: Array, statuses: Array<{ __typename?: 'TrackStatusType', name: string, value: number }> } }> } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; -export type GetChapterQueryVariables = Exact<{ - id: Scalars['Int']['input']; -}>; - - -export type GetChapterQuery = { __typename?: 'Query', chapter: { __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> } }; - -export type GetChaptersQueryVariables = Exact<{ +export type GetChaptersReaderQueryVariables = Exact<{ after?: InputMaybe; before?: InputMaybe; condition?: InputMaybe; @@ -3152,7 +3157,37 @@ export type GetChaptersQueryVariables = Exact<{ }>; -export type GetChaptersQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', chapterNumber: number, fetchedAt: string, id: number, isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, lastPageRead: number, lastReadAt: string, mangaId: number, name: string, pageCount: number, realUrl?: string | null, scanlator?: string | null, sourceOrder: number, uploadDate: string, url: string, manga: { __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, lastFetchedAt?: string | null }, meta: Array<{ __typename?: 'ChapterMetaType', key: string, value: string }> }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; +export type GetChaptersReaderQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', lastPageRead: number, pageCount: number, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; + +export type GetChaptersMangaQueryVariables = Exact<{ + after?: InputMaybe; + before?: InputMaybe; + condition?: InputMaybe; + filter?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; + orderBy?: InputMaybe; + orderByType?: InputMaybe; +}>; + + +export type GetChaptersMangaQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; + +export type GetChaptersUpdatesQueryVariables = Exact<{ + after?: InputMaybe; + before?: InputMaybe; + condition?: InputMaybe; + filter?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; + orderBy?: InputMaybe; + orderByType?: InputMaybe; +}>; + + +export type GetChaptersUpdatesQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; export type GetMangasChapterIdsWithStateQueryVariables = Exact<{ mangaIds: Array | Scalars['Int']['input']; @@ -3162,7 +3197,7 @@ export type GetMangasChapterIdsWithStateQueryVariables = Exact<{ }>; -export type GetMangasChapterIdsWithStateQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', nodes: Array<{ __typename?: 'ChapterType', id: number, isDownloaded: boolean, isRead: boolean, isBookmarked: boolean, mangaId: number, scanlator?: string | null, chapterNumber: number }> } }; +export type GetMangasChapterIdsWithStateQuery = { __typename?: 'Query', chapters: { __typename?: 'ChapterNodeList', nodes: Array<{ __typename?: 'ChapterType', mangaId: number, scanlator?: string | null, chapterNumber: number, id: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean }> } }; export type GetDownloadStatusQueryVariables = Exact<{ [key: string]: never; }>; diff --git a/src/lib/graphql/mutations/ChapterMutation.ts b/src/lib/graphql/mutations/ChapterMutation.ts index dc0f8f11..f30ec27f 100644 --- a/src/lib/graphql/mutations/ChapterMutation.ts +++ b/src/lib/graphql/mutations/ChapterMutation.ts @@ -7,7 +7,8 @@ */ import gql from 'graphql-tag'; -import { BASE_TRACK_RECORD_FIELDS, FULL_CHAPTER_FIELDS } from '@/lib/graphql/Fragments'; +import { BASE_TRACK_RECORD_FIELDS } from '@/lib/graphql/Fragments'; +import { CHAPTER_LIST_FIELDS } from '@/lib/graphql/fragments/ChapterFragments.ts'; export const DELETE_CHAPTER_METADATA = gql` mutation DELETE_CHAPTER_METADATA($input: DeleteChapterMetaInput!) { @@ -51,12 +52,13 @@ export const GET_CHAPTER_PAGES_FETCH = gql` // makes the server fetch and return the chapters of the manga export const GET_MANGA_CHAPTERS_FETCH = gql` - ${FULL_CHAPTER_FIELDS} + ${CHAPTER_LIST_FIELDS} + mutation GET_MANGA_CHAPTERS_FETCH($input: FetchChaptersInput!) { fetchChapters(input: $input) { clientMutationId chapters { - ...FULL_CHAPTER_FIELDS + ...CHAPTER_LIST_FIELDS manga { id chapters { diff --git a/src/lib/graphql/queries/ChapterQuery.ts b/src/lib/graphql/queries/ChapterQuery.ts index 1cd1cfa2..5815bfcf 100644 --- a/src/lib/graphql/queries/ChapterQuery.ts +++ b/src/lib/graphql/queries/ChapterQuery.ts @@ -7,23 +7,20 @@ */ import gql from 'graphql-tag'; -import { FULL_CHAPTER_FIELDS, PAGE_INFO } from '@/lib/graphql/Fragments'; - -// returns the current chapter from the database -export const GET_CHAPTER = gql` - ${FULL_CHAPTER_FIELDS} - query GET_CHAPTER($id: Int!) { - chapter(id: $id) { - ...FULL_CHAPTER_FIELDS - } - } -`; +import { PAGE_INFO } from '@/lib/graphql/Fragments'; +import { + CHAPTER_LIST_FIELDS, + CHAPTER_READER_FIELDS, + CHAPTER_STATE_FIELDS, + CHAPTER_UPDATE_LIST_FIELDS, +} from '@/lib/graphql/fragments/ChapterFragments.ts'; // returns the current chapters from the database -export const GET_CHAPTERS = gql` - ${FULL_CHAPTER_FIELDS} +export const GET_CHAPTERS_READER = gql` + ${CHAPTER_READER_FIELDS} ${PAGE_INFO} - query GET_CHAPTERS( + + query GET_CHAPTERS_READER( $after: Cursor $before: Cursor $condition: ChapterConditionInput @@ -46,7 +43,83 @@ export const GET_CHAPTERS = gql` orderByType: $orderByType ) { nodes { - ...FULL_CHAPTER_FIELDS + ...CHAPTER_READER_FIELDS + } + pageInfo { + ...PAGE_INFO + } + totalCount + } + } +`; + +// returns the current chapters from the database +export const GET_CHAPTERS_MANGA = gql` + ${CHAPTER_LIST_FIELDS} + ${PAGE_INFO} + + query GET_CHAPTERS_MANGA( + $after: Cursor + $before: Cursor + $condition: ChapterConditionInput + $filter: ChapterFilterInput + $first: Int + $last: Int + $offset: Int + $orderBy: ChapterOrderBy + $orderByType: SortOrder + ) { + chapters( + after: $after + before: $before + condition: $condition + filter: $filter + first: $first + last: $last + offset: $offset + orderBy: $orderBy + orderByType: $orderByType + ) { + nodes { + ...CHAPTER_LIST_FIELDS + } + pageInfo { + ...PAGE_INFO + } + totalCount + } + } +`; + +// returns the current chapters from the database +export const GET_CHAPTERS_UPDATES = gql` + ${CHAPTER_UPDATE_LIST_FIELDS} + ${PAGE_INFO} + + query GET_CHAPTERS_UPDATES( + $after: Cursor + $before: Cursor + $condition: ChapterConditionInput + $filter: ChapterFilterInput + $first: Int + $last: Int + $offset: Int + $orderBy: ChapterOrderBy + $orderByType: SortOrder + ) { + chapters( + after: $after + before: $before + condition: $condition + filter: $filter + first: $first + last: $last + offset: $offset + orderBy: $orderBy + orderByType: $orderByType + ) { + nodes { + ...CHAPTER_UPDATE_LIST_FIELDS } pageInfo { ...PAGE_INFO @@ -57,6 +130,8 @@ export const GET_CHAPTERS = gql` `; export const GET_MANGAS_CHAPTER_IDS_WITH_STATE = gql` + ${CHAPTER_STATE_FIELDS} + query GET_MANGAS_CHAPTER_IDS_WITH_STATE( $mangaIds: [Int!]! $isDownloaded: Boolean = null @@ -69,10 +144,7 @@ export const GET_MANGAS_CHAPTER_IDS_WITH_STATE = gql` orderBy: SOURCE_ORDER ) { nodes { - id - isDownloaded - isRead - isBookmarked + ...CHAPTER_STATE_FIELDS mangaId scanlator chapterNumber diff --git a/src/lib/metadata/metadata.ts b/src/lib/metadata/metadata.ts index 88bd2d6a..5638a27d 100644 --- a/src/lib/metadata/metadata.ts +++ b/src/lib/metadata/metadata.ts @@ -14,7 +14,6 @@ import { Metadata, MetadataHolder, MetadataKeyValuePair, - TChapter, TManga, TPartialSource, } from '@/typings.ts'; @@ -22,6 +21,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts'; import { MetaType } from '@/lib/graphql/generated/graphql.ts'; import { DEFAULT_DEVICE, getActiveDevice } from '@/util/device.ts'; import { CategoryIdInfo } from '@/lib/data/Categories.ts'; +import { ChapterIdInfo } from '@/lib/data/Chapters.ts'; const APP_METADATA_KEY_PREFIX = 'webUI_'; @@ -373,7 +373,7 @@ export const requestUpdateMetadataValue = async ( await requestManager.setCategoryMeta((metadataHolder as CategoryIdInfo).id, metadataKey, value).response; break; case 'chapter': - await requestManager.setChapterMeta((metadataHolder as TChapter).id, metadataKey, value).response; + await requestManager.setChapterMeta((metadataHolder as ChapterIdInfo).id, metadataKey, value).response; break; case 'global': await requestManager.setGlobalMetadata(metadataKey, value).response; @@ -405,7 +405,7 @@ export const requestUpdateMangaMetadata = async ( ): Promise => requestUpdateMetadata(manga, 'manga', keysToValues); export const requestUpdateChapterMetadata = async ( - chapter: TChapter, + chapter: ChapterIdInfo & GqlMetaHolder, keysToValues: MetadataKeyValuePair[], ): Promise => requestUpdateMetadata(chapter, 'chapter', keysToValues); diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 5d5d4d63..ee5f57dd 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -69,8 +69,6 @@ import { GetCategoryMangasQueryVariables, GetChapterPagesFetchMutation, GetChapterPagesFetchMutationVariables, - GetChaptersQuery, - GetChaptersQueryVariables, GetExtensionsFetchMutation, GetExtensionsFetchMutationVariables, GetExtensionsQuery, @@ -199,6 +197,10 @@ import { SetSourceMetadataMutationVariables, GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables, + GetChaptersMangaQuery, + GetChaptersMangaQueryVariables, + GetChaptersUpdatesQuery, + GetChaptersUpdatesQueryVariables, } from '@/lib/graphql/generated/graphql.ts'; import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts'; import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts'; @@ -253,7 +255,11 @@ import { START_DOWNLOADER, STOP_DOWNLOADER, } from '@/lib/graphql/mutations/DownloaderMutation.ts'; -import { GET_CHAPTERS, GET_MANGAS_CHAPTER_IDS_WITH_STATE } from '@/lib/graphql/queries/ChapterQuery.ts'; +import { + GET_CHAPTERS_MANGA, + GET_CHAPTERS_UPDATES, + GET_MANGAS_CHAPTER_IDS_WITH_STATE, +} from '@/lib/graphql/queries/ChapterQuery.ts'; import { GET_CHAPTER_PAGES_FETCH, GET_MANGA_CHAPTERS_FETCH, @@ -1835,30 +1841,34 @@ export class RequestManager { ); } - public useGetChapters( - variables: GetChaptersQueryVariables, - options?: QueryHookOptions, - ): AbortableApolloUseQueryResponse { - return this.doRequest(GQLMethod.USE_QUERY, GET_CHAPTERS, variables, options); + public useGetChapters( + document: DocumentNode | TypedDocumentNode, + variables: Variables, + options?: QueryHookOptions, + ): AbortableApolloUseQueryResponse { + return this.doRequest(GQLMethod.USE_QUERY, document, variables, options); } - public getChapters( - variables: GetChaptersQueryVariables, - options?: QueryOptions, - ): AbortabaleApolloQueryResponse { - return this.doRequest(GQLMethod.QUERY, GET_CHAPTERS, variables, options); + public getChapters( + document: DocumentNode | TypedDocumentNode, + variables: Variables, + options?: QueryOptions, + ): AbortabaleApolloQueryResponse { + return this.doRequest(GQLMethod.QUERY, document, variables, options); } - public useGetMangaChapters( + public useGetMangaChapters( + document: DocumentNode | TypedDocumentNode, mangaId: number | string, - options?: QueryHookOptions, - ): AbortableApolloUseQueryResponse { + options?: QueryHookOptions, + ): AbortableApolloUseQueryResponse { return this.useGetChapters( + document, { condition: { mangaId: Number(mangaId) }, orderBy: ChapterOrderBy.SourceOrder, orderByType: SortOrder.Desc, - }, + } as unknown as Variables, options, ); } @@ -1887,24 +1897,25 @@ export class RequestManager { GQLMethod.MUTATION, GET_MANGA_CHAPTERS_FETCH, { input: { mangaId: Number(mangaId) } }, - { refetchQueries: [GET_CHAPTERS], ...options }, + { refetchQueries: [GET_CHAPTERS_MANGA], ...options }, ); } public useGetMangaChapter( mangaId: number | string, chapterIndex: number | string, - options?: QueryHookOptions, + options?: QueryHookOptions, ): AbortableApolloUseQueryResponse< - Omit & { chapter: GetChaptersQuery['chapters']['nodes'][number] }, - GetChaptersQueryVariables + Omit & { chapter: GetChaptersMangaQuery['chapters']['nodes'][number] }, + GetChaptersMangaQueryVariables > { type Response = AbortableApolloUseQueryResponse< - Omit & { chapter: GetChaptersQuery['chapters']['nodes'][number] }, - GetChaptersQueryVariables + Omit & { chapter: GetChaptersMangaQuery['chapters']['nodes'][number] }, + GetChaptersMangaQueryVariables >; - const chapterResponse = this.useGetChapters( + const chapterResponse = this.useGetChapters( + GET_CHAPTERS_MANGA, { condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) } }, options, ); @@ -1921,43 +1932,6 @@ export class RequestManager { } as unknown as Response; } - public getChapter( - mangaId: number | string, - chapterIndex: number | string, - options?: QueryOptions, - ): AbortabaleApolloQueryResponse< - Omit & { chapter: GetChaptersQuery['chapters']['nodes'][number] } - > { - type ResponseData = Omit & { - chapter: GetChaptersQuery['chapters']['nodes'][number]; - }; - - const chapterRequest = this.doRequest( - GQLMethod.QUERY, - GET_CHAPTERS, - { - condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) }, - }, - options, - ); - - return { - ...chapterRequest, - response: chapterRequest.response.then((chapterResponse) => { - if (!chapterResponse.data) { - return chapterResponse; - } - - return { - ...chapterResponse, - data: { - chapter: chapterResponse.data.chapters.nodes[0], - }, - }; - }) as Promise>, - }; - } - public useGetChapterPagesFetch( chapterId: string | number, options?: MutationHookOptions, @@ -2425,15 +2399,16 @@ export class RequestManager { public useGetRecentlyUpdatedChapters( initialPages: number = 1, - options?: QueryHookOptions, - ): AbortableApolloUseQueryResponse { + options?: QueryHookOptions, + ): AbortableApolloUseQueryResponse { const PAGE_SIZE = 50; const CACHE_KEY = 'useGetRecentlyUpdatedChapters'; const offset = this.cache.getResponseFor(CACHE_KEY, undefined) ?? 0; const [lastOffset] = useState(offset); - const result = this.useGetChapters( + const result = this.useGetChapters( + GET_CHAPTERS_UPDATES, { filter: { inLibrary: { equalTo: true } }, orderBy: ChapterOrderBy.FetchedAt, diff --git a/src/screens/DownloadQueue.tsx b/src/screens/DownloadQueue.tsx index 1d48b89a..2b1c9efd 100644 --- a/src/screens/DownloadQueue.tsx +++ b/src/screens/DownloadQueue.tsx @@ -29,10 +29,10 @@ import { makeToast } from '@/components/util/Toast'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; -import { TChapter } from '@/typings.ts'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; +import { ChapterIdInfo } from '@/lib/data/Chapters.ts'; const HeightPreservingItem = ({ children, ...props }: BoxProps) => ( // the height is necessary to prevent the item container from collapsing, which confuses Virtuoso measurements @@ -192,7 +192,7 @@ export const DownloadQueue: React.FC = () => { categoryReorder(queue, result.source.index, result.destination.index); }; - const handleDelete = async (chapter: TChapter) => { + const handleDelete = async (chapter: ChapterIdInfo) => { const isRunning = status === 'STARTED'; try { diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index 18f41ad4..2b141986 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -11,7 +11,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r import { useLocation, useNavigate, useParams } from 'react-router-dom'; import Box from '@mui/material/Box'; import { useTranslation } from 'react-i18next'; -import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, ReaderType, TChapter, TManga } from '@/typings'; +import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, ReaderType, TManga } from '@/typings'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { checkAndHandleMissingStoredReaderSettings, @@ -28,11 +28,18 @@ import { ReaderNavBar } from '@/components/navbar/ReaderNavBar'; import { makeToast } from '@/components/util/Toast'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { useDebounce } from '@/util/useDebounce.ts'; -import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; +import { + GetChaptersReaderQuery, + GetChaptersReaderQueryVariables, + UpdateChapterPatchInput, +} from '@/lib/graphql/generated/graphql.ts'; import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { Chapters } from '@/lib/data/Chapters.ts'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; +import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts'; + +type TChapter = GetChaptersReaderQuery['chapters']['nodes'][number]; const getReaderComponent = (readerType: ReaderType) => { switch (readerType) { @@ -99,16 +106,25 @@ export function Reader() { } = requestManager.useGetManga(mangaId); const loadedChapter = useRef(null); const isChapterLoaded = - Number(mangaId) === loadedChapter.current?.manga.id && + Number(mangaId) === loadedChapter.current?.mangaId && Number(chapterIndex) === loadedChapter.current?.sourceOrder && loadedChapter.current?.pageCount !== -1; const manga = data?.manga ?? fallbackManga; const { - data: chapterData, + data: chaptersData, loading: isChapterLoading, error: chapterError, refetch: fetchChapter, - } = requestManager.useGetMangaChapter(mangaId, chapterIndex, { notifyOnNetworkStatusChange: true }); + } = requestManager.useGetChapters( + GET_CHAPTERS_READER, + { + condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) }, + }, + { + notifyOnNetworkStatusChange: true, + }, + ); + const chapterData = chaptersData?.chapters.nodes[0]; const arePagesUpdatedRef = useRef(false); const { @@ -121,15 +137,14 @@ export function Reader() { const isSameAsLoadedChapter = isAChapterLoaded && isChapterLoaded; if (isSameAsLoadedChapter) { - const didPageCountChange = - chapterData?.chapter && loadedChapter.current?.pageCount !== chapterData.chapter.pageCount; - return didPageCountChange ? chapterData!.chapter : loadedChapter.current; + const didPageCountChange = chapterData && loadedChapter.current?.pageCount !== chapterData.pageCount; + return didPageCountChange ? chapterData! : loadedChapter.current; } arePagesUpdatedRef.current = false; - if (chapterData?.chapter) { - return chapterData.chapter; + if (chapterData) { + return chapterData; } return null; @@ -142,7 +157,7 @@ export function Reader() { if (initialChapterRef.current === fallbackChapter) { initialChapterRef.current = chapter; } - if (chapter.manga.id !== initialChapterRef.current?.manga.id) { + if (chapter.mangaId !== initialChapterRef.current?.mangaId) { initialChapterRef.current = fallbackChapter; } @@ -179,7 +194,11 @@ export function Reader() { loading: areChaptersLoading, error: chaptersError, refetch: refetchChapters, - } = requestManager.useGetMangaChapters(mangaId, { nextFetchPolicy: 'standby' }); + } = requestManager.useGetMangaChapters( + GET_CHAPTERS_READER, + mangaId, + { nextFetchPolicy: 'standby' }, + ); const mangaChapters = mangaChaptersData?.chapters.nodes; const isLoading = @@ -276,7 +295,7 @@ export function Reader() { const inDownloadRange = (patch.lastPageRead ?? 0) / chapter.pageCount > 0.25; const shouldCheckDownloadAhead = - isDownloadAheadEnabled && chapter.manga.inLibrary && !!currentChapter?.isDownloaded && inDownloadRange; + isDownloadAheadEnabled && manga.inLibrary && !!currentChapter?.isDownloaded && inDownloadRange; if (shouldCheckDownloadAhead) { const nextChapterUpToDate = nextChapter ? Chapters.getFromCache(nextChapter.id) : null; diff --git a/src/screens/Updates.tsx b/src/screens/Updates.tsx index 2101b7d4..207aedf6 100644 --- a/src/screens/Updates.tsx +++ b/src/screens/Updates.tsx @@ -22,8 +22,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator'; -import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; -import { TChapter } from '@/typings.ts'; +import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { UpdateChecker } from '@/components/library/UpdateChecker.tsx'; import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.tsx'; @@ -34,8 +33,9 @@ import { SpinnerImage } from '@/components/util/SpinnerImage.tsx'; import { dateTimeFormatter, epochToDate, getDateString } from '@/util/date.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { TypographyMaxLines } from '@/components/atoms/TypographyMaxLines.tsx'; +import { ChapterIdInfo, ChapterMangaInfo } from '@/lib/data/Chapters.ts'; -const groupByDate = (updates: TChapter[]): [date: string, items: number][] => { +const groupByDate = (updates: Pick[]): [date: string, items: number][] => { if (!updates.length) { return []; } @@ -96,15 +96,12 @@ export const Updates: React.FC = () => { }; }, [t, lastUpdateTimestamp]); - const downloadForChapter = (chapter: TChapter) => { - const { - sourceOrder, - manga: { id: mangaId }, - } = chapter; + const downloadForChapter = (chapter: Pick & ChapterMangaInfo) => { + const { sourceOrder, mangaId } = chapter; return queue.find((q) => sourceOrder === q.chapter.sourceOrder && mangaId === q.chapter.manga.id); }; - const downloadChapter = (chapter: TChapter) => { + const downloadChapter = (chapter: ChapterIdInfo) => { requestManager.addChapterToDownloadQueue(chapter.id); }; diff --git a/src/typings.ts b/src/typings.ts index 2fbce6a6..7dcc9887 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -11,7 +11,7 @@ import { SvgIconTypeMap } from '@mui/material/SvgIcon'; import { ParseKeys } from 'i18next'; import { Location } from 'react-router-dom'; import { - GetChapterQuery, + GetChaptersReaderQuery, GetExtensionQuery, GetMangaQuery, GetServerSettingsQuery, @@ -181,8 +181,6 @@ export interface IMangaChapter { chapter: IChapter; } -export type TChapter = GetChapterQuery['chapter']; - export enum IncludeInGlobalUpdate { EXCLUDE = 0, INCLUDE = 1, @@ -297,7 +295,7 @@ export interface IReaderProps { initialPage: number; settings: IReaderSettings; manga: TManga; - chapter: TChapter; + chapter: GetChaptersReaderQuery['chapters']['nodes'][number]; nextChapter: () => void; prevChapter: () => void; } diff --git a/tools/scripts/codegenFormatter.ts b/tools/scripts/codegenFormatter.ts index 8be77c98..0aae1600 100644 --- a/tools/scripts/codegenFormatter.ts +++ b/tools/scripts/codegenFormatter.ts @@ -41,8 +41,7 @@ const addImports = format( `import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';`, `import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache'; import { -\tGetChapterQueryVariables, -\tGetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables, +\tGetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables, \tGetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables, } from "@/lib/graphql/generated/graphql.ts"; import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";`, @@ -85,8 +84,8 @@ const fixTypingOfQueryTypePolicies = format( \taboutWebUI?: FieldPolicy | FieldReadFunction, \tcategories?: FieldPolicy | FieldReadFunction, \tcategory?: FieldPolicy | FieldReadFunction, -\tchapter?: FieldPolicy> | FieldReadFunction>, -\tchapters?: FieldPolicy | FieldReadFunction, +\tchapter?: FieldPolicy | FieldReadFunction, +\tchapters?: FieldPolicy | FieldReadFunction, \tcheckForServerUpdates?: FieldPolicy | FieldReadFunction, \tcheckForWebUIUpdate?: FieldPolicy | FieldReadFunction, \tdownloadStatus?: FieldPolicy> | FieldReadFunction>,