Reduce requested chapter data in queries

This commit is contained in:
schroda
2024-07-02 16:20:28 +02:00
parent 9b8ace003d
commit 2fa1de578c
19 changed files with 400 additions and 181 deletions

View File

@@ -29,7 +29,6 @@ import {
ChapterRealUrlInfo, ChapterRealUrlInfo,
Chapters, Chapters,
} from '@/lib/data/Chapters.ts'; } from '@/lib/data/Chapters.ts';
import { TChapter } from '@/typings.ts';
import { MenuItem } from '@/components/menu/MenuItem.tsx'; import { MenuItem } from '@/components/menu/MenuItem.tsx';
import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx'; import { IChapterWithMeta } from '@/components/chapter/ChapterList.tsx';
import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts'; import { ChaptersWithMeta } from '@/lib/data/ChaptersWithMeta.ts';
@@ -39,13 +38,15 @@ import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings
type BaseProps = { onClose: () => void }; type BaseProps = { onClose: () => void };
type TChapter = ChapterIdInfo &
ChapterMangaInfo &
ChapterDownloadInfo &
ChapterBookmarkInfo &
ChapterReadInfo &
ChapterRealUrlInfo;
type SingleModeProps = { type SingleModeProps = {
chapter: ChapterIdInfo & chapter: TChapter;
ChapterMangaInfo &
ChapterDownloadInfo &
ChapterBookmarkInfo &
ChapterReadInfo &
ChapterRealUrlInfo;
allChapters: TChapter[]; allChapters: TChapter[];
handleSelection?: SelectableCollectionReturnType<TChapter['id']>['handleSelection']; handleSelection?: SelectableCollectionReturnType<TChapter['id']>['handleSelection'];
canBeDownloaded: boolean; canBeDownloaded: boolean;

View File

@@ -24,11 +24,28 @@ import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
import { useLongPress } from 'use-long-press'; import { useLongPress } from 'use-long-press';
import { getDateString } from '@/util/date.ts'; import { getDateString } from '@/util/date.ts';
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator.tsx'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator.tsx';
import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts';
import { TChapter } from '@/typings.ts';
import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx'; import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuItems.tsx';
import { Menu } from '@/components/menu/Menu.tsx'; import { Menu } from '@/components/menu/Menu.tsx';
import { TypographyMaxLines } from '@/components/atoms/TypographyMaxLines.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<ChapterType, 'name' | 'sourceOrder' | 'uploadDate'>;
interface IProps { interface IProps {
chapter: TChapter; chapter: TChapter;
@@ -86,7 +103,7 @@ export const ChapterCard: React.FC<IProps> = (props: IProps) => {
<Card sx={{ touchCallout: 'none' }}> <Card sx={{ touchCallout: 'none' }}>
<CardActionArea <CardActionArea
component={Link} component={Link}
to={`/manga/${chapter.manga.id}/chapter/${chapter.sourceOrder}`} to={`/manga/${chapter.mangaId}/chapter/${chapter.sourceOrder}`}
style={{ style={{
color: theme.palette.text[chapter.isRead ? 'disabled' : 'primary'], color: theme.palette.text[chapter.isRead ? 'disabled' : 'primary'],
}} }}

View File

@@ -19,7 +19,7 @@ import DownloadIcon from '@mui/icons-material/Download';
import DoneAllIcon from '@mui/icons-material/DoneAll'; import DoneAllIcon from '@mui/icons-material/DoneAll';
import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state'; import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
import Menu from '@mui/material/Menu'; import Menu from '@mui/material/Menu';
import { TChapter, TManga } from '@/typings.ts'; import { TManga } from '@/typings.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { ChapterCard } from '@/components/chapter/ChapterCard.tsx'; import { ChapterCard } from '@/components/chapter/ChapterCard.tsx';
import { ResumeFab } from '@/components/manga/ResumeFAB.tsx'; import { ResumeFab } from '@/components/manga/ResumeFAB.tsx';
@@ -28,7 +28,11 @@ import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCe
import { ChaptersToolbarMenu } from '@/components/chapter/ChaptersToolbarMenu.tsx'; import { ChaptersToolbarMenu } from '@/components/chapter/ChaptersToolbarMenu.tsx';
import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx'; import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx';
import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab.tsx'; import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab.tsx';
import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; import {
DownloadType,
GetChaptersMangaQuery,
GetChaptersMangaQueryVariables,
} from '@/lib/graphql/generated/graphql.ts';
import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts'; import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts';
import { SelectableCollectionSelectAll } from '@/components/collection/SelectableCollectionSelectAll.tsx'; import { SelectableCollectionSelectAll } from '@/components/collection/SelectableCollectionSelectAll.tsx';
import { Chapters } from '@/lib/data/Chapters.ts'; import { Chapters } from '@/lib/data/Chapters.ts';
@@ -37,6 +41,7 @@ import { ChapterActionMenuItems } from '@/components/chapter/ChapterActionMenuIt
import { ChaptersDownloadActionMenuItems } from '@/components/chapter/ChaptersDownloadActionMenuItems.tsx'; import { ChaptersDownloadActionMenuItems } from '@/components/chapter/ChaptersDownloadActionMenuItems.tsx';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
import { GET_CHAPTERS_MANGA } from '@/lib/graphql/queries/ChapterQuery.ts';
const ChapterListHeader = styled(Stack)(({ theme }) => ({ const ChapterListHeader = styled(Stack)(({ theme }) => ({
margin: 8, margin: 8,
@@ -61,7 +66,7 @@ const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
})); }));
export interface IChapterWithMeta { export interface IChapterWithMeta {
chapter: TChapter; chapter: React.ComponentProps<typeof ChapterCard>['chapter'];
downloadChapter: DownloadType | undefined; downloadChapter: DownloadType | undefined;
selected: boolean | null; selected: boolean | null;
} }
@@ -83,7 +88,11 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
loading: isLoading, loading: isLoading,
error, error,
refetch, refetch,
} = requestManager.useGetMangaChapters(manga.id, { notifyOnNetworkStatusChange: true }); } = requestManager.useGetMangaChapters<GetChaptersMangaQuery, GetChaptersMangaQueryVariables>(
GET_CHAPTERS_MANGA,
manga.id,
{ notifyOnNetworkStatusChange: true },
);
const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]); const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]);
const chapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]); const chapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]);
@@ -106,7 +115,7 @@ export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
() => () =>
visibleChapters.map((chapter) => { visibleChapters.map((chapter) => {
const downloadChapter = queue?.find( 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; const selected = !areNoItemsSelected ? selectedItemIds.includes(chapter.id) : null;
return { return {

View File

@@ -12,10 +12,11 @@ import {
ChapterOptionsReducerAction, ChapterOptionsReducerAction,
ChapterSortMode, ChapterSortMode,
NullAndUndefined, NullAndUndefined,
TChapter,
TranslationKey, TranslationKey,
} from '@/typings.ts'; } from '@/typings.ts';
import { useReducerLocalStorage } from '@/util/useStorage.tsx'; 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 = { const defaultChapterOptions: ChapterListOptions = {
active: false, active: false,
@@ -46,7 +47,7 @@ function chapterOptionsReducer(state: ChapterListOptions, actions: ChapterOption
} }
} }
export function unreadFilter(unread: NullAndUndefined<boolean>, { isRead: isChapterRead }: TChapter) { export function unreadFilter(unread: NullAndUndefined<boolean>, { isRead: isChapterRead }: ChapterReadInfo) {
switch (unread) { switch (unread) {
case true: case true:
return !isChapterRead; return !isChapterRead;
@@ -57,7 +58,7 @@ export function unreadFilter(unread: NullAndUndefined<boolean>, { isRead: isChap
} }
} }
function downloadFilter(downloaded: NullAndUndefined<boolean>, { isDownloaded: chapterDownload }: TChapter) { function downloadFilter(downloaded: NullAndUndefined<boolean>, { isDownloaded: chapterDownload }: ChapterDownloadInfo) {
switch (downloaded) { switch (downloaded) {
case true: case true:
return chapterDownload; return chapterDownload;
@@ -68,7 +69,10 @@ function downloadFilter(downloaded: NullAndUndefined<boolean>, { isDownloaded: c
} }
} }
function bookmarkedFilter(bookmarked: NullAndUndefined<boolean>, { isBookmarked: chapterBookmarked }: TChapter) { function bookmarkedFilter(
bookmarked: NullAndUndefined<boolean>,
{ isBookmarked: chapterBookmarked }: ChapterBookmarkInfo,
) {
switch (bookmarked) { switch (bookmarked) {
case true: case true:
return chapterBookmarked; return chapterBookmarked;
@@ -79,11 +83,12 @@ function bookmarkedFilter(bookmarked: NullAndUndefined<boolean>, { isBookmarked:
} }
} }
const sortChapters = ( type TChapterSort = Pick<ChapterType, 'sourceOrder' | 'fetchedAt' | 'chapterNumber' | 'uploadDate'>;
chapters: TChapter[], const sortChapters = <T extends TChapterSort>(
chapters: T[],
{ sortBy, reverse }: Pick<ChapterListOptions, 'sortBy' | 'reverse'>, { sortBy, reverse }: Pick<ChapterListOptions, 'sortBy' | 'reverse'>,
): TChapter[] => { ): T[] => {
const sortedChapters: TChapter[] = [...chapters]; const sortedChapters: T[] = [...chapters];
switch (sortBy) { switch (sortBy) {
case 'source': case 'source':
@@ -109,7 +114,11 @@ const sortChapters = (
return sortedChapters; return sortedChapters;
}; };
export function filterAndSortChapters(chapters: TChapter[], options: ChapterListOptions): TChapter[] { type TChapterFilter = TChapterSort & ChapterReadInfo & ChapterDownloadInfo & ChapterBookmarkInfo;
export function filterAndSortChapters<Chapters extends TChapterFilter>(
chapters: Chapters[],
options: ChapterListOptions,
): Chapters[] {
const filtered = options.active const filtered = options.active
? chapters.filter( ? chapters.filter(
(chp) => (chp) =>

View File

@@ -27,11 +27,12 @@ import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText'; import ListItemText from '@mui/material/ListItemText';
import Collapse from '@mui/material/Collapse'; import Collapse from '@mui/material/Collapse';
import { useTranslation } from 'react-i18next'; 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 { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions';
import { useBackButton } from '@/util/useBackButton.ts'; import { useBackButton } from '@/util/useBackButton.ts';
import { Select } from '@/components/atoms/Select.tsx'; import { Select } from '@/components/atoms/Select.tsx';
import { getOptionForDirection } from '@/theme.ts'; import { getOptionForDirection } from '@/theme.ts';
import { ChapterType } from '@/lib/graphql/generated/graphql.ts';
const Root = styled('div')({ const Root = styled('div')({
zIndex: 10, zIndex: 10,
@@ -123,8 +124,8 @@ interface IProps {
settings: IReaderSettings; settings: IReaderSettings;
setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void; setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void;
manga: TManga; manga: TManga;
chapter: TChapter; chapter: Pick<ChapterType, 'name' | 'sourceOrder' | 'pageCount'>;
chapters: TChapter[]; chapters: Pick<ChapterType, 'id' | 'sourceOrder' | 'name' | 'chapterNumber' | 'scanlator'>[];
curPage: number; curPage: number;
scrollToPage: (page: number) => void; scrollToPage: (page: number) => void;
openNextChapter: (offset: ChapterOffset) => void; openNextChapter: (offset: ChapterOffset) => void;

View File

@@ -9,11 +9,12 @@
import { t as translate } from 'i18next'; import { t as translate } from 'i18next';
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { DocumentNode } from '@apollo/client'; 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 { makeToast } from '@/components/util/Toast.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.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'; export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
@@ -77,24 +78,24 @@ export const actionToTranslationKey: {
}, },
}; };
export type ChapterIdInfo = Pick<TChapter, 'id'>; export type ChapterIdInfo = Pick<ChapterType, 'id'>;
export type ChapterMangaInfo = Pick<TChapter, 'mangaId'>; export type ChapterMangaInfo = Pick<ChapterType, 'mangaId'>;
export type ChapterDownloadInfo = ChapterIdInfo & Pick<TChapter, 'isDownloaded'>; export type ChapterDownloadInfo = ChapterIdInfo & Pick<ChapterType, 'isDownloaded'>;
export type ChapterBookmarkInfo = ChapterIdInfo & Pick<TChapter, 'isBookmarked'>; export type ChapterBookmarkInfo = ChapterIdInfo & Pick<ChapterType, 'isBookmarked'>;
export type ChapterReadInfo = ChapterIdInfo & Pick<TChapter, 'isRead'>; export type ChapterReadInfo = ChapterIdInfo & Pick<ChapterType, 'isRead'>;
export type ChapterNumberInfo = ChapterIdInfo & Pick<TChapter, 'chapterNumber'>; export type ChapterNumberInfo = ChapterIdInfo & Pick<ChapterType, 'chapterNumber'>;
export type ChapterScanlatorInfo = ChapterIdInfo & Pick<TChapter, 'scanlator'>; export type ChapterScanlatorInfo = ChapterIdInfo & Pick<ChapterType, 'scanlator'>;
export type ChapterRealUrlInfo = Pick<TChapter, 'realUrl'>; export type ChapterRealUrlInfo = Pick<ChapterType, 'realUrl'>;
export class Chapters { export class Chapters {
static getIds(chapters: { id: number }[]): number[] { static getIds(chapters: { id: number }[]): number[] {
return chapters.map((chapter) => chapter.id); return chapters.map((chapter) => chapter.id);
} }
static getFromCache<T>( static getFromCache<T = ChapterListFieldsFragment>(
id: number, id: number,
fragment: DocumentNode = FULL_CHAPTER_FIELDS, fragment: DocumentNode = CHAPTER_LIST_FIELDS,
fragmentName: string = 'FULL_CHAPTER_FIELDS', fragmentName: string = 'CHAPTER_LIST_FIELDS',
): T | null { ): T | null {
return requestManager.graphQLClient.client.cache.readFragment<T>({ return requestManager.graphQLClient.client.cache.readFragment<T>({
id: requestManager.graphQLClient.client.cache.identify({ id: requestManager.graphQLClient.client.cache.identify({
@@ -107,7 +108,7 @@ export class Chapters {
} }
static isDownloading(id: number): boolean { static isDownloading(id: number): boolean {
return !!requestManager.graphQLClient.client.cache.readFragment<TChapter>({ return !!requestManager.graphQLClient.client.cache.readFragment<ChapterType>({
id: requestManager.graphQLClient.client.cache.identify({ id: requestManager.graphQLClient.client.cache.identify({
__typename: 'DownloadType', __typename: 'DownloadType',
chapter: { chapter: {

View File

@@ -6,17 +6,18 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * 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 { 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 = { export type ChapterWithMetaType = {
chapter: TChapter; chapter: ChapterDownloadInfo & ChapterReadInfo & ChapterBookmarkInfo;
downloadChapter: DownloadType | undefined; downloadChapter: DownloadType | undefined;
}; };
export class ChaptersWithMeta { export class ChaptersWithMeta {
static getChapters(chapters: ChapterWithMetaType[]): TChapter[] { static getChapters<ChaptersWithMeta extends ChapterWithMetaType>(
chapters: ChaptersWithMeta[],
): ChaptersWithMeta['chapter'][] {
return chapters.map(({ chapter }) => chapter); return chapters.map(({ chapter }) => chapter);
} }

View File

@@ -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
}
}
`;

View File

@@ -1,7 +1,6 @@
import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache'; import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
import { import {
GetChapterQueryVariables, GetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
GetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
GetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables, GetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
} from "@/lib/graphql/generated/graphql.ts"; } from "@/lib/graphql/generated/graphql.ts";
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies"; import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";
@@ -575,8 +574,8 @@ export type QueryFieldPolicy = {
aboutWebUI?: FieldPolicy<any> | FieldReadFunction<any>, aboutWebUI?: FieldPolicy<any> | FieldReadFunction<any>,
categories?: FieldPolicy<any> | FieldReadFunction<any>, categories?: FieldPolicy<any> | FieldReadFunction<any>,
category?: FieldPolicy<any> | FieldReadFunction<any>, category?: FieldPolicy<any> | FieldReadFunction<any>,
chapter?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetChapterQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetChapterQueryVariables>>, chapter?: FieldPolicy<any> | FieldReadFunction<any>,
chapters?: FieldPolicy<GetChaptersQuery['chapters']> | FieldReadFunction<GetChaptersQuery['chapters']>, chapters?: FieldPolicy<GetChaptersMangaQuery['chapters']> | FieldReadFunction<GetChaptersMangaQuery['chapters']>,
checkForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>, checkForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>,
checkForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>, checkForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>,
downloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>, downloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>,

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,8 @@
*/ */
import gql from 'graphql-tag'; 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` export const DELETE_CHAPTER_METADATA = gql`
mutation DELETE_CHAPTER_METADATA($input: DeleteChapterMetaInput!) { 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 // makes the server fetch and return the chapters of the manga
export const GET_MANGA_CHAPTERS_FETCH = gql` export const GET_MANGA_CHAPTERS_FETCH = gql`
${FULL_CHAPTER_FIELDS} ${CHAPTER_LIST_FIELDS}
mutation GET_MANGA_CHAPTERS_FETCH($input: FetchChaptersInput!) { mutation GET_MANGA_CHAPTERS_FETCH($input: FetchChaptersInput!) {
fetchChapters(input: $input) { fetchChapters(input: $input) {
clientMutationId clientMutationId
chapters { chapters {
...FULL_CHAPTER_FIELDS ...CHAPTER_LIST_FIELDS
manga { manga {
id id
chapters { chapters {

View File

@@ -7,23 +7,20 @@
*/ */
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { FULL_CHAPTER_FIELDS, PAGE_INFO } from '@/lib/graphql/Fragments'; import { PAGE_INFO } from '@/lib/graphql/Fragments';
import {
// returns the current chapter from the database CHAPTER_LIST_FIELDS,
export const GET_CHAPTER = gql` CHAPTER_READER_FIELDS,
${FULL_CHAPTER_FIELDS} CHAPTER_STATE_FIELDS,
query GET_CHAPTER($id: Int!) { CHAPTER_UPDATE_LIST_FIELDS,
chapter(id: $id) { } from '@/lib/graphql/fragments/ChapterFragments.ts';
...FULL_CHAPTER_FIELDS
}
}
`;
// returns the current chapters from the database // returns the current chapters from the database
export const GET_CHAPTERS = gql` export const GET_CHAPTERS_READER = gql`
${FULL_CHAPTER_FIELDS} ${CHAPTER_READER_FIELDS}
${PAGE_INFO} ${PAGE_INFO}
query GET_CHAPTERS(
query GET_CHAPTERS_READER(
$after: Cursor $after: Cursor
$before: Cursor $before: Cursor
$condition: ChapterConditionInput $condition: ChapterConditionInput
@@ -46,7 +43,83 @@ export const GET_CHAPTERS = gql`
orderByType: $orderByType orderByType: $orderByType
) { ) {
nodes { 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 { pageInfo {
...PAGE_INFO ...PAGE_INFO
@@ -57,6 +130,8 @@ export const GET_CHAPTERS = gql`
`; `;
export const GET_MANGAS_CHAPTER_IDS_WITH_STATE = gql` export const GET_MANGAS_CHAPTER_IDS_WITH_STATE = gql`
${CHAPTER_STATE_FIELDS}
query GET_MANGAS_CHAPTER_IDS_WITH_STATE( query GET_MANGAS_CHAPTER_IDS_WITH_STATE(
$mangaIds: [Int!]! $mangaIds: [Int!]!
$isDownloaded: Boolean = null $isDownloaded: Boolean = null
@@ -69,10 +144,7 @@ export const GET_MANGAS_CHAPTER_IDS_WITH_STATE = gql`
orderBy: SOURCE_ORDER orderBy: SOURCE_ORDER
) { ) {
nodes { nodes {
id ...CHAPTER_STATE_FIELDS
isDownloaded
isRead
isBookmarked
mangaId mangaId
scanlator scanlator
chapterNumber chapterNumber

View File

@@ -14,7 +14,6 @@ import {
Metadata, Metadata,
MetadataHolder, MetadataHolder,
MetadataKeyValuePair, MetadataKeyValuePair,
TChapter,
TManga, TManga,
TPartialSource, TPartialSource,
} from '@/typings.ts'; } from '@/typings.ts';
@@ -22,6 +21,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
import { MetaType } from '@/lib/graphql/generated/graphql.ts'; import { MetaType } from '@/lib/graphql/generated/graphql.ts';
import { DEFAULT_DEVICE, getActiveDevice } from '@/util/device.ts'; import { DEFAULT_DEVICE, getActiveDevice } from '@/util/device.ts';
import { CategoryIdInfo } from '@/lib/data/Categories.ts'; import { CategoryIdInfo } from '@/lib/data/Categories.ts';
import { ChapterIdInfo } from '@/lib/data/Chapters.ts';
const APP_METADATA_KEY_PREFIX = 'webUI_'; const APP_METADATA_KEY_PREFIX = 'webUI_';
@@ -373,7 +373,7 @@ export const requestUpdateMetadataValue = async (
await requestManager.setCategoryMeta((metadataHolder as CategoryIdInfo).id, metadataKey, value).response; await requestManager.setCategoryMeta((metadataHolder as CategoryIdInfo).id, metadataKey, value).response;
break; break;
case 'chapter': case 'chapter':
await requestManager.setChapterMeta((metadataHolder as TChapter).id, metadataKey, value).response; await requestManager.setChapterMeta((metadataHolder as ChapterIdInfo).id, metadataKey, value).response;
break; break;
case 'global': case 'global':
await requestManager.setGlobalMetadata(metadataKey, value).response; await requestManager.setGlobalMetadata(metadataKey, value).response;
@@ -405,7 +405,7 @@ export const requestUpdateMangaMetadata = async (
): Promise<void[]> => requestUpdateMetadata(manga, 'manga', keysToValues); ): Promise<void[]> => requestUpdateMetadata(manga, 'manga', keysToValues);
export const requestUpdateChapterMetadata = async ( export const requestUpdateChapterMetadata = async (
chapter: TChapter, chapter: ChapterIdInfo & GqlMetaHolder,
keysToValues: MetadataKeyValuePair[], keysToValues: MetadataKeyValuePair[],
): Promise<void[]> => requestUpdateMetadata(chapter, 'chapter', keysToValues); ): Promise<void[]> => requestUpdateMetadata(chapter, 'chapter', keysToValues);

View File

@@ -69,8 +69,6 @@ import {
GetCategoryMangasQueryVariables, GetCategoryMangasQueryVariables,
GetChapterPagesFetchMutation, GetChapterPagesFetchMutation,
GetChapterPagesFetchMutationVariables, GetChapterPagesFetchMutationVariables,
GetChaptersQuery,
GetChaptersQueryVariables,
GetExtensionsFetchMutation, GetExtensionsFetchMutation,
GetExtensionsFetchMutationVariables, GetExtensionsFetchMutationVariables,
GetExtensionsQuery, GetExtensionsQuery,
@@ -199,6 +197,10 @@ import {
SetSourceMetadataMutationVariables, SetSourceMetadataMutationVariables,
GetCategoriesSettingsQuery, GetCategoriesSettingsQuery,
GetCategoriesSettingsQueryVariables, GetCategoriesSettingsQueryVariables,
GetChaptersMangaQuery,
GetChaptersMangaQueryVariables,
GetChaptersUpdatesQuery,
GetChaptersUpdatesQueryVariables,
} from '@/lib/graphql/generated/graphql.ts'; } from '@/lib/graphql/generated/graphql.ts';
import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts'; import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts';
import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts'; import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts';
@@ -253,7 +255,11 @@ import {
START_DOWNLOADER, START_DOWNLOADER,
STOP_DOWNLOADER, STOP_DOWNLOADER,
} from '@/lib/graphql/mutations/DownloaderMutation.ts'; } 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 { import {
GET_CHAPTER_PAGES_FETCH, GET_CHAPTER_PAGES_FETCH,
GET_MANGA_CHAPTERS_FETCH, GET_MANGA_CHAPTERS_FETCH,
@@ -1835,30 +1841,34 @@ export class RequestManager {
); );
} }
public useGetChapters( public useGetChapters<Data, Variables extends OperationVariables>(
variables: GetChaptersQueryVariables, document: DocumentNode | TypedDocumentNode<Data, Variables>,
options?: QueryHookOptions<GetChaptersQuery, GetChaptersQueryVariables>, variables: Variables,
): AbortableApolloUseQueryResponse<GetChaptersQuery, GetChaptersQueryVariables> { options?: QueryHookOptions<Data, Variables>,
return this.doRequest(GQLMethod.USE_QUERY, GET_CHAPTERS, variables, options); ): AbortableApolloUseQueryResponse<Data, Variables> {
return this.doRequest(GQLMethod.USE_QUERY, document, variables, options);
} }
public getChapters( public getChapters<Data, Variables extends OperationVariables>(
variables: GetChaptersQueryVariables, document: DocumentNode | TypedDocumentNode<Data, Variables>,
options?: QueryOptions<GetChaptersQueryVariables, GetChaptersQuery>, variables: Variables,
): AbortabaleApolloQueryResponse<GetChaptersQuery> { options?: QueryOptions<Variables, Data>,
return this.doRequest(GQLMethod.QUERY, GET_CHAPTERS, variables, options); ): AbortabaleApolloQueryResponse<Data> {
return this.doRequest(GQLMethod.QUERY, document, variables, options);
} }
public useGetMangaChapters( public useGetMangaChapters<Data, Variables extends OperationVariables>(
document: DocumentNode | TypedDocumentNode<Data, Variables>,
mangaId: number | string, mangaId: number | string,
options?: QueryHookOptions<GetChaptersQuery, GetChaptersQueryVariables>, options?: QueryHookOptions<Data, Variables>,
): AbortableApolloUseQueryResponse<GetChaptersQuery, GetChaptersQueryVariables> { ): AbortableApolloUseQueryResponse<Data, Variables> {
return this.useGetChapters( return this.useGetChapters(
document,
{ {
condition: { mangaId: Number(mangaId) }, condition: { mangaId: Number(mangaId) },
orderBy: ChapterOrderBy.SourceOrder, orderBy: ChapterOrderBy.SourceOrder,
orderByType: SortOrder.Desc, orderByType: SortOrder.Desc,
}, } as unknown as Variables,
options, options,
); );
} }
@@ -1887,24 +1897,25 @@ export class RequestManager {
GQLMethod.MUTATION, GQLMethod.MUTATION,
GET_MANGA_CHAPTERS_FETCH, GET_MANGA_CHAPTERS_FETCH,
{ input: { mangaId: Number(mangaId) } }, { input: { mangaId: Number(mangaId) } },
{ refetchQueries: [GET_CHAPTERS], ...options }, { refetchQueries: [GET_CHAPTERS_MANGA], ...options },
); );
} }
public useGetMangaChapter( public useGetMangaChapter(
mangaId: number | string, mangaId: number | string,
chapterIndex: number | string, chapterIndex: number | string,
options?: QueryHookOptions<GetChaptersQuery, GetChaptersQueryVariables>, options?: QueryHookOptions<GetChaptersMangaQuery, GetChaptersMangaQueryVariables>,
): AbortableApolloUseQueryResponse< ): AbortableApolloUseQueryResponse<
Omit<GetChaptersQuery, 'chapters'> & { chapter: GetChaptersQuery['chapters']['nodes'][number] }, Omit<GetChaptersMangaQuery, 'chapters'> & { chapter: GetChaptersMangaQuery['chapters']['nodes'][number] },
GetChaptersQueryVariables GetChaptersMangaQueryVariables
> { > {
type Response = AbortableApolloUseQueryResponse< type Response = AbortableApolloUseQueryResponse<
Omit<GetChaptersQuery, 'chapters'> & { chapter: GetChaptersQuery['chapters']['nodes'][number] }, Omit<GetChaptersMangaQuery, 'chapters'> & { chapter: GetChaptersMangaQuery['chapters']['nodes'][number] },
GetChaptersQueryVariables GetChaptersMangaQueryVariables
>; >;
const chapterResponse = this.useGetChapters( const chapterResponse = this.useGetChapters<GetChaptersMangaQuery, GetChaptersMangaQueryVariables>(
GET_CHAPTERS_MANGA,
{ condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) } }, { condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) } },
options, options,
); );
@@ -1921,43 +1932,6 @@ export class RequestManager {
} as unknown as Response; } as unknown as Response;
} }
public getChapter(
mangaId: number | string,
chapterIndex: number | string,
options?: QueryOptions<GetChaptersQueryVariables, GetChaptersQuery>,
): AbortabaleApolloQueryResponse<
Omit<GetChaptersQuery, 'chapters'> & { chapter: GetChaptersQuery['chapters']['nodes'][number] }
> {
type ResponseData = Omit<GetChaptersQuery, 'chapters'> & {
chapter: GetChaptersQuery['chapters']['nodes'][number];
};
const chapterRequest = this.doRequest<GetChaptersQuery, GetChaptersQueryVariables>(
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<ApolloQueryResult<ResponseData>>,
};
}
public useGetChapterPagesFetch( public useGetChapterPagesFetch(
chapterId: string | number, chapterId: string | number,
options?: MutationHookOptions<GetChapterPagesFetchMutation, GetChapterPagesFetchMutationVariables>, options?: MutationHookOptions<GetChapterPagesFetchMutation, GetChapterPagesFetchMutationVariables>,
@@ -2425,15 +2399,16 @@ export class RequestManager {
public useGetRecentlyUpdatedChapters( public useGetRecentlyUpdatedChapters(
initialPages: number = 1, initialPages: number = 1,
options?: QueryHookOptions<GetChaptersQuery, GetChaptersQueryVariables>, options?: QueryHookOptions<GetChaptersUpdatesQuery, GetChaptersUpdatesQueryVariables>,
): AbortableApolloUseQueryResponse<GetChaptersQuery, GetChaptersQueryVariables> { ): AbortableApolloUseQueryResponse<GetChaptersUpdatesQuery, GetChaptersUpdatesQueryVariables> {
const PAGE_SIZE = 50; const PAGE_SIZE = 50;
const CACHE_KEY = 'useGetRecentlyUpdatedChapters'; const CACHE_KEY = 'useGetRecentlyUpdatedChapters';
const offset = this.cache.getResponseFor<number>(CACHE_KEY, undefined) ?? 0; const offset = this.cache.getResponseFor<number>(CACHE_KEY, undefined) ?? 0;
const [lastOffset] = useState(offset); const [lastOffset] = useState(offset);
const result = this.useGetChapters( const result = this.useGetChapters<GetChaptersUpdatesQuery, GetChaptersUpdatesQueryVariables>(
GET_CHAPTERS_UPDATES,
{ {
filter: { inLibrary: { equalTo: true } }, filter: { inLibrary: { equalTo: true } },
orderBy: ChapterOrderBy.FetchedAt, orderBy: ChapterOrderBy.FetchedAt,

View File

@@ -29,10 +29,10 @@ import { makeToast } from '@/components/util/Toast';
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator';
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts';
import { TChapter } from '@/typings.ts';
import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { NavBarContext } from '@/components/context/NavbarContext.tsx';
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { ChapterIdInfo } from '@/lib/data/Chapters.ts';
const HeightPreservingItem = ({ children, ...props }: BoxProps) => ( const HeightPreservingItem = ({ children, ...props }: BoxProps) => (
// the height is necessary to prevent the item container from collapsing, which confuses Virtuoso measurements // 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); categoryReorder(queue, result.source.index, result.destination.index);
}; };
const handleDelete = async (chapter: TChapter) => { const handleDelete = async (chapter: ChapterIdInfo) => {
const isRunning = status === 'STARTED'; const isRunning = status === 'STARTED';
try { try {

View File

@@ -11,7 +11,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r
import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { useLocation, useNavigate, useParams } from 'react-router-dom';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { useTranslation } from 'react-i18next'; 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 { requestManager } from '@/lib/requests/RequestManager.ts';
import { import {
checkAndHandleMissingStoredReaderSettings, checkAndHandleMissingStoredReaderSettings,
@@ -28,11 +28,18 @@ import { ReaderNavBar } from '@/components/navbar/ReaderNavBar';
import { makeToast } from '@/components/util/Toast'; import { makeToast } from '@/components/util/Toast';
import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { NavBarContext } from '@/components/context/NavbarContext.tsx';
import { useDebounce } from '@/util/useDebounce.ts'; 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 { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { Chapters } from '@/lib/data/Chapters.ts'; import { Chapters } from '@/lib/data/Chapters.ts';
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; 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) => { const getReaderComponent = (readerType: ReaderType) => {
switch (readerType) { switch (readerType) {
@@ -99,16 +106,25 @@ export function Reader() {
} = requestManager.useGetManga(mangaId); } = requestManager.useGetManga(mangaId);
const loadedChapter = useRef<TChapter | null>(null); const loadedChapter = useRef<TChapter | null>(null);
const isChapterLoaded = const isChapterLoaded =
Number(mangaId) === loadedChapter.current?.manga.id && Number(mangaId) === loadedChapter.current?.mangaId &&
Number(chapterIndex) === loadedChapter.current?.sourceOrder && Number(chapterIndex) === loadedChapter.current?.sourceOrder &&
loadedChapter.current?.pageCount !== -1; loadedChapter.current?.pageCount !== -1;
const manga = data?.manga ?? fallbackManga; const manga = data?.manga ?? fallbackManga;
const { const {
data: chapterData, data: chaptersData,
loading: isChapterLoading, loading: isChapterLoading,
error: chapterError, error: chapterError,
refetch: fetchChapter, refetch: fetchChapter,
} = requestManager.useGetMangaChapter(mangaId, chapterIndex, { notifyOnNetworkStatusChange: true }); } = requestManager.useGetChapters<GetChaptersReaderQuery, GetChaptersReaderQueryVariables>(
GET_CHAPTERS_READER,
{
condition: { mangaId: Number(mangaId), sourceOrder: Number(chapterIndex) },
},
{
notifyOnNetworkStatusChange: true,
},
);
const chapterData = chaptersData?.chapters.nodes[0];
const arePagesUpdatedRef = useRef(false); const arePagesUpdatedRef = useRef(false);
const { const {
@@ -121,15 +137,14 @@ export function Reader() {
const isSameAsLoadedChapter = isAChapterLoaded && isChapterLoaded; const isSameAsLoadedChapter = isAChapterLoaded && isChapterLoaded;
if (isSameAsLoadedChapter) { if (isSameAsLoadedChapter) {
const didPageCountChange = const didPageCountChange = chapterData && loadedChapter.current?.pageCount !== chapterData.pageCount;
chapterData?.chapter && loadedChapter.current?.pageCount !== chapterData.chapter.pageCount; return didPageCountChange ? chapterData! : loadedChapter.current;
return didPageCountChange ? chapterData!.chapter : loadedChapter.current;
} }
arePagesUpdatedRef.current = false; arePagesUpdatedRef.current = false;
if (chapterData?.chapter) { if (chapterData) {
return chapterData.chapter; return chapterData;
} }
return null; return null;
@@ -142,7 +157,7 @@ export function Reader() {
if (initialChapterRef.current === fallbackChapter) { if (initialChapterRef.current === fallbackChapter) {
initialChapterRef.current = chapter; initialChapterRef.current = chapter;
} }
if (chapter.manga.id !== initialChapterRef.current?.manga.id) { if (chapter.mangaId !== initialChapterRef.current?.mangaId) {
initialChapterRef.current = fallbackChapter; initialChapterRef.current = fallbackChapter;
} }
@@ -179,7 +194,11 @@ export function Reader() {
loading: areChaptersLoading, loading: areChaptersLoading,
error: chaptersError, error: chaptersError,
refetch: refetchChapters, refetch: refetchChapters,
} = requestManager.useGetMangaChapters(mangaId, { nextFetchPolicy: 'standby' }); } = requestManager.useGetMangaChapters<GetChaptersReaderQuery, GetChaptersReaderQueryVariables>(
GET_CHAPTERS_READER,
mangaId,
{ nextFetchPolicy: 'standby' },
);
const mangaChapters = mangaChaptersData?.chapters.nodes; const mangaChapters = mangaChaptersData?.chapters.nodes;
const isLoading = const isLoading =
@@ -276,7 +295,7 @@ export function Reader() {
const inDownloadRange = (patch.lastPageRead ?? 0) / chapter.pageCount > 0.25; const inDownloadRange = (patch.lastPageRead ?? 0) / chapter.pageCount > 0.25;
const shouldCheckDownloadAhead = const shouldCheckDownloadAhead =
isDownloadAheadEnabled && chapter.manga.inLibrary && !!currentChapter?.isDownloaded && inDownloadRange; isDownloadAheadEnabled && manga.inLibrary && !!currentChapter?.isDownloaded && inDownloadRange;
if (shouldCheckDownloadAhead) { if (shouldCheckDownloadAhead) {
const nextChapterUpToDate = nextChapter ? Chapters.getFromCache<TChapter>(nextChapter.id) : null; const nextChapterUpToDate = nextChapter ? Chapters.getFromCache<TChapter>(nextChapter.id) : null;

View File

@@ -22,8 +22,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder'; import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx'; import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator'; import { DownloadStateIndicator } from '@/components/molecules/DownloadStateIndicator';
import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts';
import { TChapter } from '@/typings.ts';
import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { NavBarContext } from '@/components/context/NavbarContext.tsx';
import { UpdateChecker } from '@/components/library/UpdateChecker.tsx'; import { UpdateChecker } from '@/components/library/UpdateChecker.tsx';
import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.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 { dateTimeFormatter, epochToDate, getDateString } from '@/util/date.ts';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { TypographyMaxLines } from '@/components/atoms/TypographyMaxLines.tsx'; 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<ChapterType, 'fetchedAt'>[]): [date: string, items: number][] => {
if (!updates.length) { if (!updates.length) {
return []; return [];
} }
@@ -96,15 +96,12 @@ export const Updates: React.FC = () => {
}; };
}, [t, lastUpdateTimestamp]); }, [t, lastUpdateTimestamp]);
const downloadForChapter = (chapter: TChapter) => { const downloadForChapter = (chapter: Pick<ChapterType, 'sourceOrder'> & ChapterMangaInfo) => {
const { const { sourceOrder, mangaId } = chapter;
sourceOrder,
manga: { id: mangaId },
} = chapter;
return queue.find((q) => sourceOrder === q.chapter.sourceOrder && mangaId === q.chapter.manga.id); 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); requestManager.addChapterToDownloadQueue(chapter.id);
}; };

View File

@@ -11,7 +11,7 @@ import { SvgIconTypeMap } from '@mui/material/SvgIcon';
import { ParseKeys } from 'i18next'; import { ParseKeys } from 'i18next';
import { Location } from 'react-router-dom'; import { Location } from 'react-router-dom';
import { import {
GetChapterQuery, GetChaptersReaderQuery,
GetExtensionQuery, GetExtensionQuery,
GetMangaQuery, GetMangaQuery,
GetServerSettingsQuery, GetServerSettingsQuery,
@@ -181,8 +181,6 @@ export interface IMangaChapter {
chapter: IChapter; chapter: IChapter;
} }
export type TChapter = GetChapterQuery['chapter'];
export enum IncludeInGlobalUpdate { export enum IncludeInGlobalUpdate {
EXCLUDE = 0, EXCLUDE = 0,
INCLUDE = 1, INCLUDE = 1,
@@ -297,7 +295,7 @@ export interface IReaderProps {
initialPage: number; initialPage: number;
settings: IReaderSettings; settings: IReaderSettings;
manga: TManga; manga: TManga;
chapter: TChapter; chapter: GetChaptersReaderQuery['chapters']['nodes'][number];
nextChapter: () => void; nextChapter: () => void;
prevChapter: () => void; prevChapter: () => void;
} }

View File

@@ -41,8 +41,7 @@ const addImports = format(
`import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';`, `import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';`,
`import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache'; `import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
import { import {
\tGetChapterQueryVariables, \tGetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
\tGetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
\tGetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables, \tGetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
} from "@/lib/graphql/generated/graphql.ts"; } from "@/lib/graphql/generated/graphql.ts";
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";`, import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";`,
@@ -85,8 +84,8 @@ const fixTypingOfQueryTypePolicies = format(
\taboutWebUI?: FieldPolicy<any> | FieldReadFunction<any>, \taboutWebUI?: FieldPolicy<any> | FieldReadFunction<any>,
\tcategories?: FieldPolicy<any> | FieldReadFunction<any>, \tcategories?: FieldPolicy<any> | FieldReadFunction<any>,
\tcategory?: FieldPolicy<any> | FieldReadFunction<any>, \tcategory?: FieldPolicy<any> | FieldReadFunction<any>,
\tchapter?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetChapterQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetChapterQueryVariables>>, \tchapter?: FieldPolicy<any> | FieldReadFunction<any>,
\tchapters?: FieldPolicy<GetChaptersQuery['chapters']> | FieldReadFunction<GetChaptersQuery['chapters']>, \tchapters?: FieldPolicy<GetChaptersMangaQuery['chapters']> | FieldReadFunction<GetChaptersMangaQuery['chapters']>,
\tcheckForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>, \tcheckForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>,
\tcheckForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>, \tcheckForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>,
\tdownloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>, \tdownloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>,