Reduce requested manga data in queries
This commit is contained in:
@@ -52,7 +52,7 @@ export const MangaCard = (props: MangaCardProps) => {
|
||||
mode === 'source',
|
||||
);
|
||||
|
||||
const mangaLinkTo = getMangaLinkTo(mode, manga.id, manga.source?.id, manga.title);
|
||||
const mangaLinkTo = getMangaLinkTo(mode, manga.id, manga.sourceId, manga.title);
|
||||
|
||||
const nextChapterIndexToRead = firstUnreadChapter?.sourceOrder ?? 1;
|
||||
const isLatestChapterRead = chapters?.totalCount === latestReadChapter?.sourceOrder;
|
||||
|
||||
@@ -17,11 +17,11 @@ import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
|
||||
import { MangaCard } from '@/components/MangaCard';
|
||||
import { GridLayout } from '@/components/context/LibraryOptionsContext';
|
||||
import { useLocalStorage, useSessionStorage } from '@/util/useStorage.tsx';
|
||||
import { TManga, TPartialManga } from '@/typings.ts';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab.tsx';
|
||||
import { AppStorage } from '@/util/AppStorage.ts';
|
||||
import { MangaCardProps } from '@/components/manga/MangaCard.types.tsx';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
const GridContainer = React.forwardRef<HTMLDivElement, GridTypeMap['props']>(({ children, ...props }, ref) => (
|
||||
<Grid {...props} ref={ref} container sx={{ paddingLeft: '5px', paddingRight: '13px' }}>
|
||||
@@ -46,12 +46,14 @@ const GridItemContainerWithDimension = (
|
||||
);
|
||||
};
|
||||
|
||||
type TManga = MangaCardProps['manga'];
|
||||
|
||||
const createMangaCard = (
|
||||
manga: TPartialManga,
|
||||
manga: TManga,
|
||||
gridLayout?: GridLayout,
|
||||
inLibraryIndicator?: boolean,
|
||||
isSelectModeActive: boolean = false,
|
||||
selectedMangaIds?: TManga['id'][],
|
||||
selectedMangaIds?: MangaType['id'][],
|
||||
handleSelection?: DefaultGridProps['handleSelection'],
|
||||
mode?: MangaCardProps['mode'],
|
||||
) => (
|
||||
@@ -68,13 +70,13 @@ const createMangaCard = (
|
||||
|
||||
type DefaultGridProps = Pick<MangaCardProps, 'mode'> & {
|
||||
isLoading: boolean;
|
||||
mangas: TPartialManga[];
|
||||
mangas: TManga[];
|
||||
inLibraryIndicator?: boolean;
|
||||
GridItemContainer: (props: GridTypeMap['props'] & Partial<GridItemProps>) => JSX.Element;
|
||||
gridLayout?: GridLayout;
|
||||
isSelectModeActive?: boolean;
|
||||
selectedMangaIds?: Required<TManga['id']>[];
|
||||
handleSelection?: SelectableCollectionReturnType<TManga['id']>['handleSelection'];
|
||||
selectedMangaIds?: Required<MangaType['id']>[];
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
};
|
||||
|
||||
const HorizontalGrid = forwardRef(
|
||||
|
||||
@@ -19,7 +19,6 @@ import DownloadIcon from '@mui/icons-material/Download';
|
||||
import DoneAllIcon from '@mui/icons-material/DoneAll';
|
||||
import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { ChapterCard } from '@/components/chapter/ChapterCard.tsx';
|
||||
import { ResumeFab } from '@/components/manga/ResumeFAB.tsx';
|
||||
@@ -32,6 +31,7 @@ import {
|
||||
DownloadType,
|
||||
GetChaptersMangaQuery,
|
||||
GetChaptersMangaQueryVariables,
|
||||
MangaScreenFieldsFragment,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { SelectableCollectionSelectAll } from '@/components/collection/SelectableCollectionSelectAll.tsx';
|
||||
@@ -72,7 +72,10 @@ export interface IChapterWithMeta {
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
manga: TManga;
|
||||
manga: Pick<
|
||||
MangaScreenFieldsFragment,
|
||||
'id' | 'firstUnreadChapter' | 'chapters' | 'latestReadChapter' | 'unreadCount' | 'downloadCount'
|
||||
>;
|
||||
isRefreshing: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
const DownloadRange = {
|
||||
NEXT_1: 1,
|
||||
@@ -25,7 +25,7 @@ export const ChaptersDownloadActionMenuItems = ({
|
||||
mangaIds,
|
||||
closeMenu,
|
||||
}: {
|
||||
mangaIds: TManga['id'][];
|
||||
mangaIds: MangaType['id'][];
|
||||
closeMenu: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -9,14 +9,12 @@
|
||||
import React, { useEffect, useLayoutEffect } from 'react';
|
||||
import { StringParam, useQueryParam } from 'use-query-params';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TManga } from '@/typings';
|
||||
import { GridLayout, useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext';
|
||||
import { IMangaGridProps, MangaGrid } from '@/components/MangaGrid';
|
||||
|
||||
interface LibraryMangaGridProps
|
||||
extends Required<Pick<IMangaGridProps, 'isSelectModeActive' | 'selectedMangaIds' | 'handleSelection'>>,
|
||||
extends Required<Pick<IMangaGridProps, 'isSelectModeActive' | 'selectedMangaIds' | 'handleSelection' | 'mangas'>>,
|
||||
Pick<IMangaGridProps, 'retry' | 'message' | 'messageExtra'> {
|
||||
mangas: TManga[];
|
||||
showFilteredOutMessage: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
|
||||
import { StringParam, useQueryParam } from 'use-query-params';
|
||||
import { useMemo } from 'react';
|
||||
import { LibraryOptions, LibrarySortMode, NullAndUndefined, TManga } from '@/typings.ts';
|
||||
import { LibraryOptions, LibrarySortMode, NullAndUndefined } from '@/typings.ts';
|
||||
import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext.tsx';
|
||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
import { Trackers } from '@/lib/data/Trackers.ts';
|
||||
import { ChapterType, MangaType, TrackRecordType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
const triStateFilter = (
|
||||
triState: NullAndUndefined<boolean>,
|
||||
@@ -35,22 +36,26 @@ const triStateFilterNumber = (triState: NullAndUndefined<boolean>, count?: numbe
|
||||
() => count === 0,
|
||||
);
|
||||
|
||||
const queryFilter = (query: NullAndUndefined<string>, { title }: TManga): boolean => {
|
||||
type TMangaQueryFilter = Pick<MangaType, 'title'>;
|
||||
const queryFilter = (query: NullAndUndefined<string>, { title }: TMangaQueryFilter): boolean => {
|
||||
if (!query) return true;
|
||||
return title.toLowerCase().includes(query.toLowerCase());
|
||||
};
|
||||
|
||||
const queryGenreFilter = (query: NullAndUndefined<string>, { genre }: TManga): boolean => {
|
||||
type TMangaQueryGenreFilter = Pick<MangaType, 'genre'>;
|
||||
const queryGenreFilter = (query: NullAndUndefined<string>, { genre }: TMangaQueryGenreFilter): boolean => {
|
||||
if (!query) return true;
|
||||
const queries = query.split(',').map((str) => str.toLowerCase().trim());
|
||||
return queries.every((element) => genre.map((el) => el.toLowerCase()).includes(element));
|
||||
};
|
||||
|
||||
const trackerFilter = (trackFilters: LibraryOptions['tracker'], manga: TManga): boolean =>
|
||||
type TMangaTrackerFilter = { trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] } };
|
||||
const trackerFilter = (trackFilters: LibraryOptions['tracker'], manga: TMangaTrackerFilter): boolean =>
|
||||
Object.entries(trackFilters)
|
||||
.map(([trackFilterId, trackFilterState]) => {
|
||||
const mangaTrackers = Trackers.getTrackers(manga.trackRecords.nodes);
|
||||
const isTrackerBound = mangaTrackers.some((tracker) => tracker.id === Number(trackFilterId));
|
||||
const isTrackerBound = manga.trackRecords.nodes.some(
|
||||
(trackRecord) => trackRecord.trackerId === Number(trackFilterId),
|
||||
);
|
||||
|
||||
return triStateFilter(
|
||||
trackFilterState,
|
||||
@@ -60,15 +65,19 @@ const trackerFilter = (trackFilters: LibraryOptions['tracker'], manga: TManga):
|
||||
})
|
||||
.every((matchesFilter) => matchesFilter);
|
||||
|
||||
const filterManga = (
|
||||
mangas: TManga[],
|
||||
type TMangaFilter = TMangaQueryFilter &
|
||||
TMangaQueryGenreFilter &
|
||||
TMangaTrackerFilter &
|
||||
Pick<MangaType, 'downloadCount' | 'unreadCount' | 'bookmarkCount'>;
|
||||
const filterManga = <Manga extends TMangaFilter>(
|
||||
mangas: Manga[],
|
||||
query: NullAndUndefined<string>,
|
||||
unread: NullAndUndefined<boolean>,
|
||||
downloaded: NullAndUndefined<boolean>,
|
||||
bookmarked: NullAndUndefined<boolean>,
|
||||
tracker: LibraryOptions['tracker'],
|
||||
ignoreFilters: boolean,
|
||||
): TManga[] =>
|
||||
): Manga[] =>
|
||||
mangas.filter((manga) => {
|
||||
const ignoreFiltersWhileSearching = ignoreFilters && query?.length;
|
||||
const matchesSearch = queryFilter(query, manga) || queryGenreFilter(query, manga);
|
||||
@@ -86,11 +95,16 @@ const sortByNumber = (a: number | string = 0, b: number | string = 0) => Number(
|
||||
|
||||
const sortByString = (a: string, b: string): number => a.localeCompare(b);
|
||||
|
||||
const sortManga = (
|
||||
manga: TManga[],
|
||||
type TMangaSort = Pick<MangaType, 'title' | 'inLibraryAt' | 'unreadCount'> & {
|
||||
lastReadChapter?: Pick<ChapterType, 'lastReadAt'> | null;
|
||||
latestUploadedChapter?: Pick<ChapterType, 'uploadDate'> | null;
|
||||
latestFetchedChapter?: Pick<ChapterType, 'fetchedAt'> | null;
|
||||
};
|
||||
const sortManga = <Manga extends TMangaSort>(
|
||||
manga: Manga[],
|
||||
sort: NullAndUndefined<LibrarySortMode>,
|
||||
desc: NullAndUndefined<boolean>,
|
||||
): TManga[] => {
|
||||
): Manga[] => {
|
||||
const result = [...manga];
|
||||
|
||||
switch (sort) {
|
||||
@@ -125,7 +139,12 @@ const sortManga = (
|
||||
return result;
|
||||
};
|
||||
|
||||
export const useGetVisibleLibraryMangas = (mangas: TManga[]) => {
|
||||
export const useGetVisibleLibraryMangas = <Manga extends MangaIdInfo & TMangaFilter & TMangaSort>(
|
||||
mangas: Manga[],
|
||||
): {
|
||||
visibleMangas: Manga[];
|
||||
showFilteredOutMessage: boolean;
|
||||
} => {
|
||||
const [query] = useQueryParam('query', StringParam);
|
||||
const { options } = useLibraryOptionsContext();
|
||||
const { unread, downloaded, bookmarked, tracker } = options;
|
||||
|
||||
@@ -19,8 +19,14 @@ import SyncAltIcon from '@mui/icons-material/SyncAlt';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { actionToTranslationKey, MangaAction, MangaDownloadInfo, Mangas, MangaUnreadInfo } from '@/lib/data/Mangas.ts';
|
||||
import {
|
||||
actionToTranslationKey,
|
||||
MangaAction,
|
||||
MangaDownloadInfo,
|
||||
MangaIdInfo,
|
||||
Mangas,
|
||||
MangaUnreadInfo,
|
||||
} from '@/lib/data/Mangas.ts';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { MenuItem } from '@/components/menu/MenuItem.tsx';
|
||||
import { createGetMenuItemTitle, createIsMenuItemDisabled, createShouldShowMenuItem } from '@/components/menu/util.ts';
|
||||
@@ -29,18 +35,19 @@ import { TrackManga } from '@/components/tracker/TrackManga.tsx';
|
||||
import { useCategorySelect } from '@/components/navbar/action/useCategorySelect.tsx';
|
||||
import { ChaptersDownloadActionMenuItems } from '@/components/chapter/ChaptersDownloadActionMenuItems.tsx';
|
||||
import { NestedMenuItem } from '@/components/menu/NestedMenuItem.tsx';
|
||||
import { MangaChapterStatFieldsFragment, MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
const ACTION_DISABLES_SELECTION_MODE: MangaAction[] = ['remove_from_library'] as const;
|
||||
|
||||
type BaseProps = { onClose: (selectionModeState: boolean) => void; setHideMenu: (hide: boolean) => void };
|
||||
|
||||
export type SingleModeProps = {
|
||||
manga: Pick<TManga, 'id' | 'title' | 'source' | 'trackRecords'> & MangaDownloadInfo & MangaUnreadInfo;
|
||||
handleSelection?: SelectableCollectionReturnType<TManga['id']>['handleSelection'];
|
||||
manga: Pick<MangaType, 'id' | 'title' | 'sourceId'> & MangaDownloadInfo & MangaUnreadInfo;
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
};
|
||||
|
||||
type SelectModeProps = {
|
||||
selectedMangas: TManga[];
|
||||
selectedMangas: MangaChapterStatFieldsFragment[];
|
||||
};
|
||||
|
||||
type Props =
|
||||
@@ -82,7 +89,7 @@ export const MangaActionMenuItems = ({
|
||||
onClose(true);
|
||||
};
|
||||
|
||||
const performAction = (action: MangaAction, mangas: TManga[]) => {
|
||||
const performAction = (action: MangaAction, mangas: MangaIdInfo[]) => {
|
||||
Mangas.performAction(action, manga ? [manga.id] : Mangas.getIds(mangas), {
|
||||
wasManuallyMarkedAsRead: true,
|
||||
}).catch(defaultPromiseErrorHandler(`MangaActionMenuItems:performAction(${action})`));
|
||||
@@ -150,7 +157,7 @@ export const MangaActionMenuItems = ({
|
||||
)}
|
||||
{isSingleMode && (
|
||||
<Link
|
||||
to={`/migrate/source/${manga?.source?.id}/manga/${manga?.id}/search?query=${manga?.title}`}
|
||||
to={`/migrate/source/${manga?.sourceId}/manga/${manga?.id}/search?query=${manga?.title}`}
|
||||
state={{ mangaTitle: manga?.title }}
|
||||
style={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
|
||||
@@ -8,24 +8,37 @@
|
||||
|
||||
import { LongPressPointerHandlers, LongPressResult } from 'use-long-press/lib/use-long-press.types';
|
||||
import { PopupState } from 'material-ui-popup-state/hooks';
|
||||
import { TManga, TPartialManga } from '@/typings.ts';
|
||||
import { GridLayout } from '@/components/context/LibraryOptionsContext.tsx';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { useManageMangaLibraryState } from '@/components/manga/useManageMangaLibraryState.tsx';
|
||||
import { MangaChapterCountInfo, MangaThumbnailInfo } from '@/lib/data/Mangas.ts';
|
||||
import { ChapterType, MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { SingleModeProps } from '@/components/manga/MangaActionMenuItems.tsx';
|
||||
|
||||
export type MangaCardMode = 'default' | 'source' | 'migrate.search' | 'migrate.select' | 'duplicate';
|
||||
|
||||
type MangaCardBaseProps = Pick<MangaType, 'id' | 'title' | 'sourceId'> &
|
||||
SingleModeProps['manga'] &
|
||||
Partial<Pick<MangaType, 'inLibrary' | 'downloadCount' | 'unreadCount'>> &
|
||||
MangaChapterCountInfo & {
|
||||
latestReadChapter?: Pick<ChapterType, 'id' | 'sourceOrder'> | null;
|
||||
firstUnreadChapter?: Pick<ChapterType, 'id' | 'sourceOrder'> | null;
|
||||
};
|
||||
|
||||
type MangaCardSpecificProps = MangaCardBaseProps & MangaThumbnailInfo;
|
||||
|
||||
export interface MangaCardProps {
|
||||
manga: TPartialManga;
|
||||
manga: MangaCardBaseProps;
|
||||
gridLayout?: GridLayout;
|
||||
inLibraryIndicator?: boolean;
|
||||
selected?: boolean | null;
|
||||
handleSelection?: SelectableCollectionReturnType<TManga['id']>['handleSelection'];
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
mode?: MangaCardMode;
|
||||
}
|
||||
|
||||
export type SpecificMangaCardProps = MangaCardProps &
|
||||
export type SpecificMangaCardProps = Omit<MangaCardProps, 'manga'> &
|
||||
Pick<ReturnType<typeof useManageMangaLibraryState>, 'isInLibrary'> & {
|
||||
manga: MangaCardSpecificProps;
|
||||
longPressBind: LongPressResult<LongPressPointerHandlers>;
|
||||
popupState: PopupState;
|
||||
handleClick: (event: React.MouseEvent | React.TouchEvent) => void;
|
||||
|
||||
@@ -10,22 +10,21 @@ import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
||||
import PublicIcon from '@mui/icons-material/Public';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { ComponentProps, useEffect, useMemo } from 'react';
|
||||
import { ComponentProps, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { t as translate } from 'i18next';
|
||||
import Link from '@mui/material/Link';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useLongPress } from 'use-long-press';
|
||||
import { TManga } from '@/typings';
|
||||
import { makeToast } from '@/components/util/Toast';
|
||||
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||
import { Mangas, MangaThumbnailInfo, MangaTrackRecordInfo } from '@/lib/data/Mangas.ts';
|
||||
import { SpinnerImage } from '@/components/util/SpinnerImage.tsx';
|
||||
import { CustomIconButton } from '@/components/atoms/CustomIconButton';
|
||||
import { TrackMangaButton } from '@/components/manga/TrackMangaButton.tsx';
|
||||
import { useManageMangaLibraryState } from '@/components/manga/useManageMangaLibraryState.tsx';
|
||||
import { Metadata as BaseMetadata } from '@/components/atoms/Metadata.tsx';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { SourceType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MangaType, SourceType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
const DetailsWrapper = styled('div')(({ theme }) => ({
|
||||
width: '100%',
|
||||
@@ -156,10 +155,6 @@ const OpenSourceButton = ({ url }: { url?: string | null }) => {
|
||||
return button;
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
manga: TManga;
|
||||
}
|
||||
|
||||
function getSourceName(source?: Pick<SourceType, 'id' | 'displayName'> | null): string {
|
||||
if (!source) {
|
||||
return translate('global.label.unknown');
|
||||
@@ -172,7 +167,18 @@ function getValueOrUnknown(val?: string | null) {
|
||||
return val ?? translate('global.label.unknown');
|
||||
}
|
||||
|
||||
export const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
||||
export const MangaDetails = ({
|
||||
manga,
|
||||
}: {
|
||||
manga: Pick<
|
||||
MangaType,
|
||||
'id' | 'title' | 'author' | 'artist' | 'status' | 'inLibrary' | 'realUrl' | 'description' | 'genre'
|
||||
> &
|
||||
MangaThumbnailInfo &
|
||||
MangaTrackRecordInfo & {
|
||||
source?: Pick<SourceType, 'id' | 'displayName'> | null;
|
||||
};
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -16,8 +16,8 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { PopupState } from 'material-ui-popup-state/hooks';
|
||||
import { bindTrigger } from 'material-ui-popup-state';
|
||||
import { SelectableCollectionReturnType } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { MediaQuery } from '@/lib/ui/MediaQuery.tsx';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
export const MangaOptionButton = forwardRef(
|
||||
(
|
||||
@@ -30,7 +30,7 @@ export const MangaOptionButton = forwardRef(
|
||||
}: {
|
||||
id: number;
|
||||
selected?: boolean | null;
|
||||
handleSelection?: SelectableCollectionReturnType<TManga['id']>['handleSelection'];
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
asCheckbox?: boolean;
|
||||
popupState: PopupState;
|
||||
},
|
||||
|
||||
@@ -21,11 +21,11 @@ import { Link } from 'react-router-dom';
|
||||
import SyncAltIcon from '@mui/icons-material/SyncAlt';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { useCategorySelect } from '@/components/navbar/action/useCategorySelect.tsx';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
|
||||
interface IProps {
|
||||
manga: TManga;
|
||||
manga: Pick<MangaType, 'id' | 'inLibrary' | 'sourceId' | 'title'>;
|
||||
onRefresh: () => any;
|
||||
refreshing: boolean;
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export const MangaToolbarMenu = ({ manga, onRefresh, refreshing }: IProps) => {
|
||||
<>
|
||||
<Tooltip title={t('global.button.migrate')}>
|
||||
<Link
|
||||
to={`/migrate/source/${manga.source?.id}/manga/${manga.id}/search?query=${manga.title}`}
|
||||
to={`/migrate/source/${manga.sourceId}/manga/${manga.id}/search?query=${manga.title}`}
|
||||
state={{ mangaTitle: manga.title }}
|
||||
style={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
@@ -122,7 +122,7 @@ export const MangaToolbarMenu = ({ manga, onRefresh, refreshing }: IProps) => {
|
||||
<MenuItem
|
||||
key="migrate"
|
||||
component={Link}
|
||||
to={`/migrate/source/${manga.source?.id}/manga/${manga.id}/search?query=${manga.title}`}
|
||||
to={`/migrate/source/${manga.sourceId}/manga/${manga.id}/search?query=${manga.title}`}
|
||||
state={{ mangaTitle: manga.title }}
|
||||
style={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
|
||||
@@ -16,20 +16,21 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { TrackManga } from '@/components/tracker/TrackManga.tsx';
|
||||
import { Trackers } from '@/lib/data/Trackers.ts';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { CustomIconButton } from '@/components/atoms/CustomIconButton.tsx';
|
||||
import { GetTrackersSettingsQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GetTrackersSettingsQuery, MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/queries/TrackerQuery.ts';
|
||||
import { MangaTrackRecordInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
export const TrackMangaButton = ({ manga }: { manga: TManga }) => {
|
||||
export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick<MangaType, 'title'> }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const trackerList = requestManager.useGetTrackerList<GetTrackersSettingsQuery>(GET_TRACKERS_SETTINGS);
|
||||
const trackers = trackerList.data?.trackers.nodes ?? [];
|
||||
const mangaTrackers = manga.trackRecords.nodes;
|
||||
|
||||
const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? []);
|
||||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackers));
|
||||
const loggedInTrackers = Trackers.getLoggedIn(trackers);
|
||||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackers, trackers));
|
||||
|
||||
const handleClick = (openPopup: () => void) => {
|
||||
if (trackerList.error) {
|
||||
|
||||
@@ -16,19 +16,18 @@ import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings
|
||||
import { Categories } from '@/lib/data/Categories.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { awaitConfirmation } from '@/lib/ui/AwaitableDialog.tsx';
|
||||
import { GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables, MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_CATEGORIES_BASE } from '@/lib/graphql/queries/CategoryQuery.ts';
|
||||
|
||||
export const useManageMangaLibraryState = (
|
||||
manga: Pick<TManga, 'id' | 'title' | 'inLibrary'>,
|
||||
manga: Pick<MangaType, 'id' | 'title'> & Partial<Pick<MangaType, 'inLibrary'>>,
|
||||
confirmRemoval: boolean = false,
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isInLibrary, setIsInLibrary] = useState(manga.inLibrary);
|
||||
const [isInLibrary, setIsInLibrary] = useState(!!manga.inLibrary);
|
||||
|
||||
const addToLibrary = useCallback(
|
||||
(didSubmit: boolean, addToCategories: number[] = [], removeFromCategories: number[] = []) => {
|
||||
@@ -162,6 +161,6 @@ export const useManageMangaLibraryState = (
|
||||
*
|
||||
* To work around this issue, the currently known in library state gets returned here
|
||||
*/
|
||||
isInLibrary: Mangas.getFromCache<TManga>(manga.id)?.inLibrary ?? isInLibrary,
|
||||
isInLibrary: Mangas.getFromCache(manga.id)?.inLibrary ?? isInLibrary,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -27,12 +27,13 @@ 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, TManga } from '@/typings';
|
||||
import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings } 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';
|
||||
import { MangaChapterCountInfo, MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
const Root = styled('div')({
|
||||
zIndex: 10,
|
||||
@@ -123,7 +124,7 @@ const OpenDrawerButton = styled(IconButton)(({ theme }) => ({
|
||||
interface IProps {
|
||||
settings: IReaderSettings;
|
||||
setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void;
|
||||
manga: TManga;
|
||||
manga: MangaIdInfo & MangaChapterCountInfo;
|
||||
chapter: Pick<ChapterType, 'name' | 'sourceOrder' | 'pageCount'>;
|
||||
chapters: Pick<ChapterType, 'id' | 'sourceOrder' | 'name' | 'chapterNumber' | 'scanlator'>[];
|
||||
curPage: number;
|
||||
|
||||
@@ -25,8 +25,14 @@ import { CheckboxInput } from '@/components/atoms/CheckboxInput.tsx';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { updateMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
import { GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables } from '@/lib/graphql/generated/graphql.ts';
|
||||
import {
|
||||
GetCategoriesBaseQuery,
|
||||
GetCategoriesBaseQueryVariables,
|
||||
GetMangaCategoriesQuery,
|
||||
GetMangaCategoriesQueryVariables,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_CATEGORIES_BASE } from '@/lib/graphql/queries/CategoryQuery.ts';
|
||||
import { GET_MANGA_CATEGORIES } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
|
||||
type BaseProps = {
|
||||
open: boolean;
|
||||
@@ -47,7 +53,11 @@ export type CategorySelectProps =
|
||||
| (BaseProps & PropertiesNever<SingleMangaModeProps> & MultiMangaModeProps);
|
||||
|
||||
const useGetMangaCategoryIds = (mangaId: number | undefined): number[] => {
|
||||
const { data: mangaResult } = requestManager.useGetManga(mangaId ?? -1, { skip: mangaId === undefined });
|
||||
const { data: mangaResult } = requestManager.useGetManga<GetMangaCategoriesQuery, GetMangaCategoriesQueryVariables>(
|
||||
GET_MANGA_CATEGORIES,
|
||||
mangaId ?? -1,
|
||||
{ skip: mangaId === undefined },
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
if (mangaId === undefined || !mangaResult) {
|
||||
@@ -90,6 +100,7 @@ export function CategorySelect(props: CategorySelectProps) {
|
||||
const [doNotShowAddToLibraryDialogAgain, setDoNotShowAddToLibraryDialogAgain] = useState(false);
|
||||
|
||||
const mangaCategoryIds = useGetMangaCategoryIds(mangaId);
|
||||
|
||||
const { data } = requestManager.useGetCategories<GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables>(
|
||||
GET_CATEGORIES_BASE,
|
||||
);
|
||||
|
||||
17
src/components/source/BaseMangaGrid.tsx
Normal file
17
src/components/source/BaseMangaGrid.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 { MangaGrid, IMangaGridProps } from '@/components/MangaGrid';
|
||||
|
||||
type TMangaBaseGrid = Omit<IMangaGridProps['mangas'][number], 'downloadCount' | 'unreadCount' | 'chapters'>;
|
||||
|
||||
export function BaseMangaGrid(props: Omit<IMangaGridProps, 'mangas'> & { mangas: TMangaBaseGrid[] }) {
|
||||
const { mangas } = props;
|
||||
|
||||
return <MangaGrid {...props} mangas={mangas as IMangaGridProps['mangas']} />;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MangaGrid, IMangaGridProps } from '@/components/MangaGrid';
|
||||
import { TPartialManga } from '@/typings.ts';
|
||||
|
||||
function filterManga(mangas: TPartialManga[]): TPartialManga[] {
|
||||
return mangas;
|
||||
}
|
||||
|
||||
export function SourceMangaGrid(props: IMangaGridProps) {
|
||||
const { t } = useTranslation();
|
||||
const { mangas, isLoading, hasNextPage, loadMore, message, messageExtra, gridLayout } = props;
|
||||
|
||||
const filteredManga = filterManga(mangas);
|
||||
const showFilteredOutMessage = filteredManga.length === 0 && mangas.length > 0;
|
||||
|
||||
return (
|
||||
<MangaGrid
|
||||
mangas={filteredManga}
|
||||
isLoading={isLoading}
|
||||
hasNextPage={hasNextPage}
|
||||
loadMore={loadMore}
|
||||
message={showFilteredOutMessage ? t('manga.error.label.no_matches') : message}
|
||||
messageExtra={messageExtra}
|
||||
gridLayout={gridLayout}
|
||||
inLibraryIndicator
|
||||
mode="source"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -16,11 +16,12 @@ import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCe
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||
import { Trackers } from '@/lib/data/Trackers.ts';
|
||||
import { TrackerCard, TrackerMode } from '@/components/tracker/TrackerCard.tsx';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { GetTrackersBindQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GetMangaTrackRecordsQuery, GetTrackersBindQuery, MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_TRACKERS_BIND } from '@/lib/graphql/queries/TrackerQuery.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
import { GET_MANGA_TRACK_RECORDS } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
|
||||
const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracker?: number): TrackerMode => {
|
||||
if (id === searchModeForTracker) {
|
||||
@@ -34,7 +35,7 @@ const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracke
|
||||
return TrackerMode.UNTRACKED;
|
||||
};
|
||||
|
||||
export const TrackManga = ({ manga }: { manga: Pick<TManga, 'id' | 'trackRecords'> }) => {
|
||||
export const TrackManga = ({ manga }: { manga: MangaIdInfo & Pick<MangaType, 'title'> }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -43,26 +44,32 @@ export const TrackManga = ({ manga }: { manga: Pick<TManga, 'id' | 'trackRecords
|
||||
const trackerList = requestManager.useGetTrackerList<GetTrackersBindQuery>(GET_TRACKERS_BIND, {
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
const mangaTrackers = manga.trackRecords.nodes;
|
||||
const trackers = trackerList.data?.trackers.nodes ?? [];
|
||||
|
||||
const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? []);
|
||||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackers));
|
||||
const mangaTrackRecordsList = requestManager.useGetManga<GetMangaTrackRecordsQuery>(
|
||||
GET_MANGA_TRACK_RECORDS,
|
||||
manga.id,
|
||||
);
|
||||
const mangaTrackRecords = mangaTrackRecordsList.data?.manga.trackRecords.nodes ?? [];
|
||||
|
||||
const loggedInTrackers = Trackers.getLoggedIn(trackers);
|
||||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackRecords, trackers));
|
||||
const trackersInUseIds = Trackers.getIds(trackersInUse);
|
||||
|
||||
const isSearchActive = searchModeForTracker !== undefined;
|
||||
const OptionalDialogContent = useMemo(() => (isSearchActive ? Box : DialogContent), [isSearchActive]);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all(manga.trackRecords.nodes.map((trackRecord) => requestManager.fetchTrackBind(trackRecord.id))).catch(
|
||||
() => makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error'),
|
||||
Promise.all(mangaTrackRecords.map((trackRecord) => requestManager.fetchTrackBind(trackRecord.id))).catch(() =>
|
||||
makeToast(t('tracking.error.label.could_not_fetch_track_info'), 'error'),
|
||||
);
|
||||
}, [manga.id]);
|
||||
}, [mangaTrackRecords]);
|
||||
|
||||
const trackerComponents = useMemo(
|
||||
() =>
|
||||
loggedInTrackers.map((tracker) => {
|
||||
const mode = getTrackerMode(tracker.id, trackersInUseIds, searchModeForTracker);
|
||||
const trackRecord = Trackers.getTrackRecordFor(tracker, manga.trackRecords.nodes);
|
||||
const trackRecord = Trackers.getTrackRecordFor(tracker, mangaTrackRecords);
|
||||
|
||||
const isSearchForTracker = mode === TrackerMode.SEARCH;
|
||||
if (isSearchActive && !isSearchForTracker) {
|
||||
@@ -73,27 +80,39 @@ export const TrackManga = ({ manga }: { manga: Pick<TManga, 'id' | 'trackRecords
|
||||
<TrackerCard
|
||||
key={tracker.id}
|
||||
tracker={tracker}
|
||||
mangaId={manga.id}
|
||||
manga={manga}
|
||||
trackRecord={trackRecord}
|
||||
mode={mode}
|
||||
setSearchMode={(id) => setSearchModeForTracker(id)}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
[trackersInUseIds, searchModeForTracker, manga.id],
|
||||
[trackersInUseIds, searchModeForTracker, mangaTrackRecords],
|
||||
);
|
||||
|
||||
if (trackerList.error) {
|
||||
const error = trackerList.error ?? mangaTrackRecordsList.error;
|
||||
if (error) {
|
||||
return (
|
||||
<EmptyViewAbsoluteCentered
|
||||
message={t('global.error.label.failed_to_load_data')}
|
||||
messageExtra={trackerList.error.message}
|
||||
retry={() => trackerList.refetch().catch(defaultPromiseErrorHandler('TrackManga::refetch'))}
|
||||
messageExtra={error.message}
|
||||
retry={() => {
|
||||
if (trackerList.error) {
|
||||
trackerList.refetch().catch(defaultPromiseErrorHandler('TrackManga::refetch: trackerList'));
|
||||
}
|
||||
|
||||
if (mangaTrackRecordsList.error) {
|
||||
mangaTrackRecordsList
|
||||
.refetch()
|
||||
.catch(defaultPromiseErrorHandler('TrackManga::refetch: mangaTrackRecordsList'));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackerList.loading) {
|
||||
const loading = trackerList.loading || mangaTrackRecordsList.loading;
|
||||
if (loading) {
|
||||
return <LoadingPlaceholder />;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import { TrackerUntrackedCard } from '@/components/tracker/TrackerUntrackedCard.
|
||||
import { TrackerSearch } from '@/components/tracker/TrackerSearch.tsx';
|
||||
import { TrackerActiveCard } from '@/components/tracker/TrackerActiveCard.tsx';
|
||||
import { TTrackerBind, TTrackRecordBind } from '@/lib/data/Trackers.ts';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
export enum TrackerMode {
|
||||
UNTRACKED,
|
||||
@@ -19,13 +21,13 @@ export enum TrackerMode {
|
||||
|
||||
export const TrackerCard = ({
|
||||
tracker,
|
||||
mangaId,
|
||||
manga,
|
||||
trackRecord,
|
||||
mode,
|
||||
setSearchMode,
|
||||
}: {
|
||||
tracker: TTrackerBind;
|
||||
mangaId: number;
|
||||
manga: MangaIdInfo & Pick<MangaType, 'title'>;
|
||||
trackRecord?: TTrackRecordBind;
|
||||
mode: TrackerMode;
|
||||
setSearchMode: (id?: number) => void;
|
||||
@@ -37,7 +39,7 @@ export const TrackerCard = ({
|
||||
if (mode === TrackerMode.SEARCH) {
|
||||
return (
|
||||
<TrackerSearch
|
||||
mangaId={mangaId}
|
||||
manga={manga}
|
||||
tracker={tracker}
|
||||
trackedId={trackRecord?.remoteId}
|
||||
closeSearchMode={() => setSearchMode(undefined)}
|
||||
@@ -46,7 +48,7 @@ export const TrackerCard = ({
|
||||
}
|
||||
|
||||
if (mode === TrackerMode.INFO && !trackRecord) {
|
||||
throw new Error(`TrackerCard: unable to find track record for tracker "${tracker.id}" of manga "${mangaId}"}`);
|
||||
throw new Error(`TrackerCard: unable to find track record for tracker "${tracker.id}" of manga "${manga.id}"}`);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,24 +31,23 @@ import { TrackerMangaCard } from '@/components/tracker/TrackerMangaCard.tsx';
|
||||
import { DIALOG_PADDING } from '@/components/tracker/constants.ts';
|
||||
import { getOptionForDirection } from '@/theme.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
export const TrackerSearch = ({
|
||||
mangaId,
|
||||
manga,
|
||||
tracker,
|
||||
closeSearchMode,
|
||||
trackedId,
|
||||
}: {
|
||||
mangaId: number;
|
||||
manga: MangaIdInfo & Pick<MangaType, 'title'>;
|
||||
tracker: Pick<TTrackerBase, 'id'>;
|
||||
closeSearchMode: () => void;
|
||||
trackedId?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// can't be undefined, since this can only be opened from the manga screen
|
||||
const manga = requestManager.useGetManga(mangaId);
|
||||
|
||||
const [searchString, setSearchString] = useState<string>(manga.data!.manga.title);
|
||||
const [searchString, setSearchString] = useState<string>(manga.title);
|
||||
const [tmpSearchString, setTmpSearchString] = useState(searchString);
|
||||
|
||||
const [selectedTrackerRemoteId, setSelectedTrackerRemoteId] = useState<string | undefined>(trackedId);
|
||||
@@ -62,7 +61,7 @@ export const TrackerSearch = ({
|
||||
setSelectedTrackerRemoteId(trackedId);
|
||||
|
||||
return () =>
|
||||
trackerSearch.abortRequest(new Error(`MangaTrackerSearchCard(${tracker.id}, ${mangaId}): search changed`));
|
||||
trackerSearch.abortRequest(new Error(`MangaTrackerSearchCard(${tracker.id}, ${manga.id}): search changed`));
|
||||
}, [searchString]);
|
||||
|
||||
const [bindTracker, bindTrackerMutation] = requestManager.useBindTracker();
|
||||
@@ -82,7 +81,7 @@ export const TrackerSearch = ({
|
||||
return;
|
||||
}
|
||||
|
||||
bindTracker({ variables: { mangaId, remoteId: selectedTrackerRemoteId, trackerId: tracker.id } })
|
||||
bindTracker({ variables: { mangaId: manga.id, remoteId: selectedTrackerRemoteId, trackerId: tracker.id } })
|
||||
.then(() => {
|
||||
makeToast(t('manga.action.track.add.label.success'), 'success');
|
||||
closeSearchMode();
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
import { t as translate } from 'i18next';
|
||||
import gql from 'graphql-tag';
|
||||
import { DocumentNode } from '@apollo/client';
|
||||
import { ChapterOffset, TManga, TranslationKey } from '@/typings.ts';
|
||||
import { ChapterOffset, 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 { ChapterListFieldsFragment, ChapterType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { CHAPTER_LIST_FIELDS } from '@/lib/graphql/fragments/ChapterFragments.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
export type ChapterAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
||||
|
||||
@@ -209,7 +210,7 @@ export class Chapters {
|
||||
static async markAsRead(
|
||||
chapters: (ChapterDownloadInfo & ChapterBookmarkInfo)[],
|
||||
wasManuallyMarkedAsRead: boolean = false,
|
||||
trackProgressMangaId?: TManga['id'],
|
||||
trackProgressMangaId?: MangaIdInfo['id'],
|
||||
): Promise<void> {
|
||||
const { deleteChaptersManuallyMarkedRead, deleteChaptersWithBookmark, updateProgressManualMarkRead } =
|
||||
await getMetadataServerSettings();
|
||||
@@ -279,7 +280,7 @@ export class Chapters {
|
||||
}: Action extends 'mark_as_read'
|
||||
? {
|
||||
wasManuallyMarkedAsRead: boolean;
|
||||
trackProgressMangaId?: TManga['id'];
|
||||
trackProgressMangaId?: MangaIdInfo['id'];
|
||||
chapters: (ChapterDownloadInfo & ChapterBookmarkInfo & ChapterReadInfo)[];
|
||||
}
|
||||
: {
|
||||
|
||||
@@ -8,19 +8,26 @@
|
||||
|
||||
import { t as translate } from 'i18next';
|
||||
import { DocumentNode } from '@apollo/client/core';
|
||||
import { MetadataMigrationSettings, TManga, TranslationKey } from '@/typings.ts';
|
||||
import { MetadataMigrationSettings, TranslationKey } from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import {
|
||||
ChapterConditionInput,
|
||||
GetMangasBaseQuery,
|
||||
GetMangasBaseQueryVariables,
|
||||
GetMangasChapterIdsWithStateQuery,
|
||||
GetMangaToMigrateQuery,
|
||||
GetMangaToMigrateToFetchMutation,
|
||||
MangaBaseFieldsFragment,
|
||||
MangaReaderFieldsFragment,
|
||||
MangaType,
|
||||
TrackRecordType,
|
||||
UpdateMangaCategoriesPatchInput,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { Chapters } from '@/lib/data/Chapters.ts';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { getMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
import { FULL_MANGA_FIELDS } from '@/lib/graphql/Fragments.ts';
|
||||
import { GET_MANGAS_BASE } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
import { MANGA_BASE_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
export type MangaAction =
|
||||
| 'download'
|
||||
@@ -108,10 +115,16 @@ export const actionToTranslationKey: {
|
||||
},
|
||||
};
|
||||
|
||||
export type MangaChapterCountInfo = { chapters: Pick<TManga['chapters'], 'totalCount'> };
|
||||
export type MangaDownloadInfo = Pick<TManga, 'downloadCount'> & MangaChapterCountInfo;
|
||||
export type MangaUnreadInfo = Pick<TManga, 'unreadCount'> & MangaChapterCountInfo;
|
||||
export type MangaThumbnailInfo = Pick<TManga, 'thumbnailUrl' | 'thumbnailUrlLastFetched'>;
|
||||
export type TMangaReader = MangaReaderFieldsFragment;
|
||||
|
||||
export type MangaIdInfo = Pick<MangaType, 'id'>;
|
||||
export type MangaChapterCountInfo = { chapters: Pick<MangaType['chapters'], 'totalCount'> };
|
||||
export type MangaDownloadInfo = Pick<MangaType, 'downloadCount'> & MangaChapterCountInfo;
|
||||
export type MangaUnreadInfo = Pick<MangaType, 'unreadCount'> & MangaChapterCountInfo;
|
||||
export type MangaThumbnailInfo = Pick<MangaType, 'thumbnailUrl' | 'thumbnailUrlLastFetched'>;
|
||||
export type MangaTrackRecordInfo = MangaIdInfo & {
|
||||
trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] };
|
||||
};
|
||||
|
||||
export type MigrateMode = 'copy' | 'migrate';
|
||||
|
||||
@@ -162,14 +175,14 @@ type PerformActionOptions<Action extends MangaAction> = Action extends 'mark_as_
|
||||
: DefaultActionOption;
|
||||
|
||||
export class Mangas {
|
||||
static getIds(mangas: { id: number }[]): number[] {
|
||||
static getIds(mangas: MangaIdInfo[]): number[] {
|
||||
return mangas.map((manga) => manga.id);
|
||||
}
|
||||
|
||||
static getFromCache<T>(
|
||||
id: number,
|
||||
fragment: DocumentNode = FULL_MANGA_FIELDS,
|
||||
fragmentName: string = 'FULL_MANGA_FIELDS',
|
||||
static getFromCache<T = MangaBaseFieldsFragment>(
|
||||
id: MangaIdInfo['id'],
|
||||
fragment: DocumentNode = MANGA_BASE_FIELDS,
|
||||
fragmentName: string = 'MANGA_BASE_FIELDS',
|
||||
): T | null {
|
||||
return requestManager.graphQLClient.client.cache.readFragment<T>({
|
||||
id: requestManager.graphQLClient.client.cache.identify({
|
||||
@@ -236,8 +249,10 @@ export class Mangas {
|
||||
return requestManager.getValidImgUrlFor(thumbnailUrl);
|
||||
}
|
||||
|
||||
static getDuplicateLibraryMangas(title: string): ReturnType<typeof requestManager.getMangas> {
|
||||
return requestManager.getMangas({
|
||||
static getDuplicateLibraryMangas(
|
||||
title: string,
|
||||
): ReturnType<typeof requestManager.getMangas<GetMangasBaseQuery, GetMangasBaseQueryVariables>> {
|
||||
return requestManager.getMangas<GetMangasBaseQuery, GetMangasBaseQueryVariables>(GET_MANGAS_BASE, {
|
||||
condition: { inLibrary: true },
|
||||
filter: { title: { likeInsensitive: title } },
|
||||
});
|
||||
@@ -429,7 +444,7 @@ export class Mangas {
|
||||
}
|
||||
|
||||
static async migrate(
|
||||
mangaId: number,
|
||||
mangaId: MangaIdInfo['id'],
|
||||
mangaIdToMigrateTo: number,
|
||||
{
|
||||
mode,
|
||||
|
||||
@@ -49,12 +49,13 @@ export type TTrackerSearch = TTrackerBase & Pick<TrackerType, 'authUrl'>;
|
||||
|
||||
export type TTrackerBind = TTrackerBase & Pick<TrackerType, 'icon' | 'supportsTrackDeletion' | 'scores' | 'statuses'>;
|
||||
|
||||
type TrackerIdInfo = Pick<TrackerType, 'id'>;
|
||||
type LoggedInInfo = Pick<TrackerType, 'isLoggedIn' | 'isTokenExpired'>;
|
||||
|
||||
type TrackRecordTrackerInfo = { tracker: TTrackerBind };
|
||||
type TrackRecordTrackerInfo = Pick<TrackRecordType, 'trackerId'>;
|
||||
|
||||
export class Trackers {
|
||||
static getIds(trackers: { id: number }[]): number[] {
|
||||
static getIds(trackers: TrackerIdInfo[]): number[] {
|
||||
return trackers.map((tracker) => tracker.id);
|
||||
}
|
||||
|
||||
@@ -74,16 +75,19 @@ export class Trackers {
|
||||
return trackers.filter(this.isLoggedIn);
|
||||
}
|
||||
|
||||
static getTrackers<TrackRecord extends TrackRecordTrackerInfo>(
|
||||
static getTrackers<TrackRecord extends TrackRecordTrackerInfo, Tracker extends TrackerIdInfo>(
|
||||
trackRecords: TrackRecord[],
|
||||
): TrackRecord['tracker'][] {
|
||||
return trackRecords.map((trackRecord) => trackRecord.tracker);
|
||||
trackers: Tracker[],
|
||||
): Tracker[] {
|
||||
return trackRecords
|
||||
.map((trackRecord) => trackers.find((tracker) => tracker.id === trackRecord.trackerId))
|
||||
.filter((tracker) => !!tracker);
|
||||
}
|
||||
|
||||
static getTrackRecordFor<TrackRecord extends TrackRecordTrackerInfo>(
|
||||
tracker: { id: number },
|
||||
tracker: TrackerIdInfo,
|
||||
trackRecords: TrackRecord[],
|
||||
): TrackRecord | undefined {
|
||||
return trackRecords.find((trackRecord) => trackRecord.tracker.id === tracker.id);
|
||||
return trackRecords.find((trackRecord) => trackRecord.trackerId === tracker.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { SOURCE_BASE_FIELDS } from '@/lib/graphql/fragments/SourceFragments.ts';
|
||||
import { TRACKER_BIND_FIELDS } from '@/lib/graphql/fragments/TrackFragments.ts';
|
||||
import { TRACK_RECORD_BIND_FIELDS } from '@/lib/graphql/fragments/TrackRecordFragments.ts';
|
||||
|
||||
export const PAGE_INFO = gql`
|
||||
fragment PAGE_INFO on PageInfo {
|
||||
@@ -27,147 +24,6 @@ export const GLOBAL_METADATA = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const FULL_CATEGORY_FIELDS = gql`
|
||||
fragment FULL_CATEGORY_FIELDS on CategoryType {
|
||||
default
|
||||
id
|
||||
includeInUpdate
|
||||
includeInDownload
|
||||
name
|
||||
order
|
||||
meta {
|
||||
key
|
||||
value
|
||||
}
|
||||
mangas {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const FULL_TRACK_RECORD_FIELDS = gql`
|
||||
${TRACK_RECORD_BIND_FIELDS}
|
||||
${TRACKER_BIND_FIELDS}
|
||||
|
||||
fragment FULL_TRACK_RECORD_FIELDS on TrackRecordType {
|
||||
...TRACK_RECORD_BIND_FIELDS
|
||||
tracker {
|
||||
...TRACKER_BIND_FIELDS
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const BASE_MANGA_FIELDS = gql`
|
||||
${SOURCE_BASE_FIELDS}
|
||||
${FULL_TRACK_RECORD_FIELDS}
|
||||
fragment BASE_MANGA_FIELDS on MangaType {
|
||||
artist
|
||||
author
|
||||
chaptersLastFetchedAt
|
||||
description
|
||||
genre
|
||||
id
|
||||
inLibrary
|
||||
inLibraryAt
|
||||
initialized
|
||||
lastFetchedAt
|
||||
meta {
|
||||
key
|
||||
value
|
||||
}
|
||||
realUrl
|
||||
source {
|
||||
...SOURCE_BASE_FIELDS
|
||||
}
|
||||
status
|
||||
thumbnailUrl
|
||||
thumbnailUrlLastFetched
|
||||
title
|
||||
url
|
||||
trackRecords {
|
||||
totalCount
|
||||
nodes {
|
||||
...FULL_TRACK_RECORD_FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const PARTIAL_MANGA_FIELDS = gql`
|
||||
${BASE_MANGA_FIELDS}
|
||||
${FULL_CATEGORY_FIELDS}
|
||||
fragment PARTIAL_MANGA_FIELDS on MangaType {
|
||||
...BASE_MANGA_FIELDS
|
||||
unreadCount
|
||||
downloadCount
|
||||
bookmarkCount
|
||||
categories {
|
||||
nodes {
|
||||
...FULL_CATEGORY_FIELDS
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
chapters {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const FULL_CHAPTER_FIELDS = gql`
|
||||
fragment FULL_CHAPTER_FIELDS on ChapterType {
|
||||
chapterNumber
|
||||
fetchedAt
|
||||
id
|
||||
isBookmarked
|
||||
isDownloaded
|
||||
isRead
|
||||
lastPageRead
|
||||
lastReadAt
|
||||
mangaId
|
||||
manga {
|
||||
id
|
||||
title
|
||||
inLibrary
|
||||
thumbnailUrl
|
||||
lastFetchedAt
|
||||
}
|
||||
meta {
|
||||
key
|
||||
value
|
||||
}
|
||||
name
|
||||
pageCount
|
||||
realUrl
|
||||
scanlator
|
||||
sourceOrder
|
||||
uploadDate
|
||||
url
|
||||
}
|
||||
`;
|
||||
|
||||
export const FULL_MANGA_FIELDS = gql`
|
||||
${PARTIAL_MANGA_FIELDS}
|
||||
${FULL_CHAPTER_FIELDS}
|
||||
fragment FULL_MANGA_FIELDS on MangaType {
|
||||
...PARTIAL_MANGA_FIELDS
|
||||
lastReadChapter {
|
||||
...FULL_CHAPTER_FIELDS
|
||||
}
|
||||
latestReadChapter {
|
||||
...FULL_CHAPTER_FIELDS
|
||||
}
|
||||
latestFetchedChapter {
|
||||
...FULL_CHAPTER_FIELDS
|
||||
}
|
||||
latestUploadedChapter {
|
||||
...FULL_CHAPTER_FIELDS
|
||||
}
|
||||
firstUnreadChapter {
|
||||
...FULL_CHAPTER_FIELDS
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ABOUT_WEBUI = gql`
|
||||
fragment ABOUT_WEBUI on AboutWebUI {
|
||||
channel
|
||||
|
||||
@@ -7,19 +7,7 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
const MANGA_BASE_FIELDS = gql`
|
||||
fragment MANGA_BASE_FIELDS on MangaType {
|
||||
id
|
||||
title
|
||||
|
||||
thumbnailUrl
|
||||
thumbnailUrlLastFetched
|
||||
|
||||
inLibrary
|
||||
initialized
|
||||
}
|
||||
`;
|
||||
import { MANGA_BASE_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
export const CHAPTER_BASE_FIELDS = gql`
|
||||
fragment CHAPTER_BASE_FIELDS on ChapterType {
|
||||
|
||||
144
src/lib/graphql/fragments/MangaFragments.ts
Normal file
144
src/lib/graphql/fragments/MangaFragments.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
export const MANGA_BASE_FIELDS = gql`
|
||||
fragment MANGA_BASE_FIELDS on MangaType {
|
||||
id
|
||||
title
|
||||
|
||||
thumbnailUrl
|
||||
thumbnailUrlLastFetched
|
||||
inLibrary
|
||||
initialized
|
||||
sourceId
|
||||
}
|
||||
`;
|
||||
|
||||
export const MANGA_CHAPTER_STAT_FIELDS = gql`
|
||||
fragment MANGA_CHAPTER_STAT_FIELDS on MangaType {
|
||||
id
|
||||
unreadCount
|
||||
downloadCount
|
||||
bookmarkCount
|
||||
|
||||
chapters {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MANGA_READER_FIELDS = gql`
|
||||
${MANGA_BASE_FIELDS}
|
||||
|
||||
fragment MANGA_READER_FIELDS on MangaType {
|
||||
...MANGA_BASE_FIELDS
|
||||
|
||||
meta {
|
||||
key
|
||||
value
|
||||
}
|
||||
|
||||
chapters {
|
||||
totalCount
|
||||
}
|
||||
|
||||
trackRecords {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MANGA_LIBRARY_FIELDS = gql`
|
||||
${MANGA_BASE_FIELDS}
|
||||
${MANGA_CHAPTER_STAT_FIELDS}
|
||||
|
||||
fragment MANGA_LIBRARY_FIELDS on MangaType {
|
||||
...MANGA_BASE_FIELDS
|
||||
...MANGA_CHAPTER_STAT_FIELDS
|
||||
|
||||
genre
|
||||
lastFetchedAt
|
||||
inLibraryAt
|
||||
|
||||
trackRecords {
|
||||
totalCount
|
||||
|
||||
nodes {
|
||||
id
|
||||
trackerId
|
||||
}
|
||||
}
|
||||
|
||||
firstUnreadChapter {
|
||||
id
|
||||
sourceOrder
|
||||
}
|
||||
lastReadChapter {
|
||||
id
|
||||
sourceOrder
|
||||
lastReadAt
|
||||
}
|
||||
latestReadChapter {
|
||||
id
|
||||
sourceOrder
|
||||
lastReadAt
|
||||
}
|
||||
latestFetchedChapter {
|
||||
id
|
||||
fetchedAt
|
||||
}
|
||||
latestUploadedChapter {
|
||||
id
|
||||
uploadDate
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MANGA_SCREEN_FIELDS = gql`
|
||||
${MANGA_LIBRARY_FIELDS}
|
||||
|
||||
fragment MANGA_SCREEN_FIELDS on MangaType {
|
||||
...MANGA_LIBRARY_FIELDS
|
||||
|
||||
artist
|
||||
author
|
||||
description
|
||||
|
||||
status
|
||||
realUrl
|
||||
|
||||
sourceId
|
||||
source {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
|
||||
trackRecords {
|
||||
totalCount
|
||||
|
||||
nodes {
|
||||
id
|
||||
trackerId
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MANGA_LIBRARY_DUPLICATE_SCREEN_FIELDS = gql`
|
||||
${MANGA_BASE_FIELDS}
|
||||
${MANGA_CHAPTER_STAT_FIELDS}
|
||||
|
||||
fragment MANGA_LIBRARY_DUPLICATE_SCREEN_FIELDS on MangaType {
|
||||
...MANGA_BASE_FIELDS
|
||||
...MANGA_CHAPTER_STAT_FIELDS
|
||||
|
||||
description
|
||||
}
|
||||
`;
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { FULL_MANGA_FIELDS } from '@/lib/graphql/Fragments.ts';
|
||||
import { MANGA_CHAPTER_STAT_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
const UPDATER_MANGA_FIELDS = gql`
|
||||
fragment UPDATER_MANGA_FIELDS on MangaType {
|
||||
@@ -19,7 +19,7 @@ const UPDATER_MANGA_FIELDS = gql`
|
||||
|
||||
export const UPDATER_SUBSCRIPTION_FIELDS = gql`
|
||||
${UPDATER_MANGA_FIELDS}
|
||||
${FULL_MANGA_FIELDS}
|
||||
${MANGA_CHAPTER_STAT_FIELDS}
|
||||
|
||||
fragment UPDATER_SUBSCRIPTION_FIELDS on UpdateStatus {
|
||||
isRunning
|
||||
@@ -28,7 +28,7 @@ export const UPDATER_SUBSCRIPTION_FIELDS = gql`
|
||||
mangas {
|
||||
totalCount
|
||||
nodes {
|
||||
...FULL_MANGA_FIELDS
|
||||
...MANGA_CHAPTER_STAT_FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
|
||||
import {
|
||||
GetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetGlobalMetadataQueryVariables,
|
||||
GetMangaQueryVariables, GetSourceBrowseQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
|
||||
GetMangaScreenQueryVariables, GetSourceBrowseQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
|
||||
} from "@/lib/graphql/generated/graphql.ts";
|
||||
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";
|
||||
export type AboutServerPayloadKeySpecifier = ('buildTime' | 'buildType' | 'discord' | 'github' | 'name' | 'revision' | 'version' | AboutServerPayloadKeySpecifier)[];
|
||||
@@ -583,7 +583,7 @@ export type QueryFieldPolicy = {
|
||||
extensions?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
getWebUIUpdateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>>,
|
||||
lastUpdateTimestamp?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
manga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>>,
|
||||
manga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaScreenQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaScreenQueryVariables>>,
|
||||
mangas?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
meta?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetGlobalMetadataQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetGlobalMetadataQueryVariables>>,
|
||||
metas?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { FULL_MANGA_FIELDS } from '@/lib/graphql/Fragments';
|
||||
import { MANGA_SCREEN_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
export const DELETE_MANGA_METADATA = gql`
|
||||
mutation DELETE_MANGA_METADATA($input: DeleteMangaMetaInput!) {
|
||||
@@ -37,12 +37,13 @@ export const DELETE_MANGA_METADATA = gql`
|
||||
|
||||
// makes the server fetch and return the manga
|
||||
export const GET_MANGA_FETCH = gql`
|
||||
${FULL_MANGA_FIELDS}
|
||||
${MANGA_SCREEN_FIELDS}
|
||||
|
||||
mutation GET_MANGA_FETCH($input: FetchMangaInput!) {
|
||||
fetchManga(input: $input) {
|
||||
clientMutationId
|
||||
manga {
|
||||
...FULL_MANGA_FIELDS
|
||||
...MANGA_SCREEN_FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,18 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { BASE_MANGA_FIELDS } from '@/lib/graphql/Fragments';
|
||||
import { SOURCE_SETTING_FIELDS } from '@/lib/graphql/fragments/SourceFragments.ts';
|
||||
import { MANGA_BASE_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
export const GET_SOURCE_MANGAS_FETCH = gql`
|
||||
${BASE_MANGA_FIELDS}
|
||||
${MANGA_BASE_FIELDS}
|
||||
|
||||
mutation GET_SOURCE_MANGAS_FETCH($input: FetchSourceMangaInput!) {
|
||||
fetchSourceManga(input: $input) {
|
||||
clientMutationId
|
||||
hasNextPage
|
||||
mangas {
|
||||
...BASE_MANGA_FIELDS
|
||||
...MANGA_BASE_FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +55,13 @@ export const TRACKER_BIND = gql`
|
||||
bindTrack(input: { mangaId: $mangaId, remoteId: $remoteId, trackerId: $trackerId }) {
|
||||
trackRecord {
|
||||
...TRACK_RECORD_BIND_FIELDS
|
||||
tracker {
|
||||
id
|
||||
}
|
||||
manga {
|
||||
id
|
||||
trackRecords {
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
trackerId
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +81,7 @@ export const TRACKER_UNBIND = gql`
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
trackerId
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +103,7 @@ export const TRACKER_UPDATE_BIND = gql`
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
trackerId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { FULL_MANGA_FIELDS, PAGE_INFO } from '@/lib/graphql/Fragments';
|
||||
import { PAGE_INFO } from '@/lib/graphql/Fragments';
|
||||
import {
|
||||
CATEGORY_BASE_FIELDS,
|
||||
CATEGORY_LIBRARY_FIELDS,
|
||||
CATEGORY_SETTING_FIELDS,
|
||||
} from '@/lib/graphql/fragments/CategoryFragments.ts';
|
||||
import { MANGA_LIBRARY_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
export const GET_CATEGORIES_BASE = gql`
|
||||
${CATEGORY_BASE_FIELDS}
|
||||
@@ -126,14 +127,15 @@ export const GET_CATEGORIES_SETTINGS = gql`
|
||||
`;
|
||||
|
||||
export const GET_CATEGORY_MANGAS = gql`
|
||||
${FULL_MANGA_FIELDS}
|
||||
${MANGA_LIBRARY_FIELDS}
|
||||
${PAGE_INFO}
|
||||
|
||||
query GET_CATEGORY_MANGAS($id: Int!) {
|
||||
category(id: $id) {
|
||||
id
|
||||
mangas {
|
||||
nodes {
|
||||
...FULL_MANGA_FIELDS
|
||||
...MANGA_LIBRARY_FIELDS
|
||||
}
|
||||
pageInfo {
|
||||
...PAGE_INFO
|
||||
|
||||
@@ -7,15 +7,68 @@
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
import { FULL_CHAPTER_FIELDS, FULL_MANGA_FIELDS, PAGE_INFO } from '@/lib/graphql/Fragments';
|
||||
import { PAGE_INFO } from '@/lib/graphql/Fragments';
|
||||
import {
|
||||
MANGA_BASE_FIELDS,
|
||||
MANGA_LIBRARY_DUPLICATE_SCREEN_FIELDS,
|
||||
MANGA_LIBRARY_FIELDS,
|
||||
MANGA_READER_FIELDS,
|
||||
MANGA_SCREEN_FIELDS,
|
||||
} from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
import { TRACK_RECORD_BIND_FIELDS } from '@/lib/graphql/fragments/TrackRecordFragments.ts';
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGA = gql`
|
||||
${FULL_MANGA_FIELDS}
|
||||
${FULL_CHAPTER_FIELDS}
|
||||
query GET_MANGA($id: Int!) {
|
||||
export const GET_MANGA_SCREEN = gql`
|
||||
${MANGA_SCREEN_FIELDS}
|
||||
|
||||
query GET_MANGA_SCREEN($id: Int!) {
|
||||
manga(id: $id) {
|
||||
...FULL_MANGA_FIELDS
|
||||
...MANGA_SCREEN_FIELDS
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGA_READER = gql`
|
||||
${MANGA_READER_FIELDS}
|
||||
|
||||
query GET_MANGA_READER($id: Int!) {
|
||||
manga(id: $id) {
|
||||
...MANGA_READER_FIELDS
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGA_TRACK_RECORDS = gql`
|
||||
${TRACK_RECORD_BIND_FIELDS}
|
||||
|
||||
query GET_MANGA_TRACK_RECORDS($id: Int!) {
|
||||
manga(id: $id) {
|
||||
id
|
||||
|
||||
trackRecords {
|
||||
totalCount
|
||||
|
||||
nodes {
|
||||
...TRACK_RECORD_BIND_FIELDS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGA_CATEGORIES = gql`
|
||||
query GET_MANGA_CATEGORIES($id: Int!) {
|
||||
manga(id: $id) {
|
||||
id
|
||||
categories {
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -62,11 +115,11 @@ export const GET_MANGA_TO_MIGRATE = gql`
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGAS = gql`
|
||||
${FULL_MANGA_FIELDS}
|
||||
${FULL_CHAPTER_FIELDS}
|
||||
export const GET_MANGAS_BASE = gql`
|
||||
${MANGA_BASE_FIELDS}
|
||||
${PAGE_INFO}
|
||||
query GET_MANGAS(
|
||||
|
||||
query GET_MANGAS_BASE(
|
||||
$after: Cursor
|
||||
$before: Cursor
|
||||
$condition: MangaConditionInput
|
||||
@@ -89,7 +142,83 @@ export const GET_MANGAS = gql`
|
||||
orderByType: $orderByType
|
||||
) {
|
||||
nodes {
|
||||
...FULL_MANGA_FIELDS
|
||||
...MANGA_BASE_FIELDS
|
||||
}
|
||||
pageInfo {
|
||||
...PAGE_INFO
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGAS_LIBRARY = gql`
|
||||
${MANGA_LIBRARY_FIELDS}
|
||||
${PAGE_INFO}
|
||||
|
||||
query GET_MANGAS_LIBRARY(
|
||||
$after: Cursor
|
||||
$before: Cursor
|
||||
$condition: MangaConditionInput
|
||||
$filter: MangaFilterInput
|
||||
$first: Int
|
||||
$last: Int
|
||||
$offset: Int
|
||||
$orderBy: MangaOrderBy
|
||||
$orderByType: SortOrder
|
||||
) {
|
||||
mangas(
|
||||
after: $after
|
||||
before: $before
|
||||
condition: $condition
|
||||
filter: $filter
|
||||
first: $first
|
||||
last: $last
|
||||
offset: $offset
|
||||
orderBy: $orderBy
|
||||
orderByType: $orderByType
|
||||
) {
|
||||
nodes {
|
||||
...MANGA_LIBRARY_FIELDS
|
||||
}
|
||||
pageInfo {
|
||||
...PAGE_INFO
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// returns the current manga from the database
|
||||
export const GET_MANGAS_DUPLICATES = gql`
|
||||
${MANGA_LIBRARY_DUPLICATE_SCREEN_FIELDS}
|
||||
${PAGE_INFO}
|
||||
|
||||
query GET_MANGAS_DUPLICATES(
|
||||
$after: Cursor
|
||||
$before: Cursor
|
||||
$condition: MangaConditionInput
|
||||
$filter: MangaFilterInput
|
||||
$first: Int
|
||||
$last: Int
|
||||
$offset: Int
|
||||
$orderBy: MangaOrderBy
|
||||
$orderByType: SortOrder
|
||||
) {
|
||||
mangas(
|
||||
after: $after
|
||||
before: $before
|
||||
condition: $condition
|
||||
filter: $filter
|
||||
first: $first
|
||||
last: $last
|
||||
offset: $offset
|
||||
orderBy: $orderBy
|
||||
orderByType: $orderByType
|
||||
) {
|
||||
nodes {
|
||||
...MANGA_LIBRARY_DUPLICATE_SCREEN_FIELDS
|
||||
}
|
||||
pageInfo {
|
||||
...PAGE_INFO
|
||||
@@ -106,9 +235,7 @@ export const GET_MIGRATABLE_SOURCE_MANGAS = gql`
|
||||
id
|
||||
title
|
||||
thumbnailUrl
|
||||
source {
|
||||
id
|
||||
}
|
||||
sourceId
|
||||
categories {
|
||||
nodes {
|
||||
id
|
||||
|
||||
@@ -14,13 +14,13 @@ import {
|
||||
Metadata,
|
||||
MetadataHolder,
|
||||
MetadataKeyValuePair,
|
||||
TManga,
|
||||
} from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { MetaType, SourceType } 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';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
const APP_METADATA_KEY_PREFIX = 'webUI_';
|
||||
|
||||
@@ -378,7 +378,7 @@ export const requestUpdateMetadataValue = async (
|
||||
await requestManager.setGlobalMetadata(metadataKey, value).response;
|
||||
break;
|
||||
case 'manga':
|
||||
await requestManager.setMangaMeta((metadataHolder as TManga).id, metadataKey, value).response;
|
||||
await requestManager.setMangaMeta((metadataHolder as MangaIdInfo).id, metadataKey, value).response;
|
||||
break;
|
||||
case 'source':
|
||||
await requestManager.setSourceMeta((metadataHolder as Pick<SourceType, 'id'>).id, metadataKey, value)
|
||||
@@ -400,7 +400,7 @@ export const requestUpdateServerMetadata = async (keysToValues: MetadataKeyValue
|
||||
requestUpdateMetadata({}, 'global', keysToValues);
|
||||
|
||||
export const requestUpdateMangaMetadata = async (
|
||||
manga: TManga,
|
||||
manga: MangaIdInfo & GqlMetaHolder,
|
||||
keysToValues: MetadataKeyValuePair[],
|
||||
): Promise<void[]> => requestUpdateMetadata(manga, 'manga', keysToValues);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Metadata, IReaderSettings, MetadataKeyValuePair, GqlMetaHolder, TManga } from '@/typings.ts';
|
||||
import { Metadata, IReaderSettings, MetadataKeyValuePair, GqlMetaHolder } from '@/typings.ts';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import {
|
||||
convertFromGqlMeta,
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
requestUpdateServerMetadata,
|
||||
} from '@/lib/metadata/metadata.ts';
|
||||
import { MetaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
type UndefinedReaderSettings = {
|
||||
[setting in keyof IReaderSettings]: IReaderSettings[setting] | undefined;
|
||||
@@ -109,7 +110,7 @@ export const checkAndHandleMissingStoredReaderSettings = async (
|
||||
}
|
||||
|
||||
if (metadataHolderType === 'manga') {
|
||||
await requestUpdateMangaMetadata(metadataHolder as TManga, settingsToUpdate);
|
||||
await requestUpdateMangaMetadata(metadataHolder as MangaIdInfo & GqlMetaHolder, settingsToUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,10 +81,6 @@ import {
|
||||
GetMangaChaptersFetchMutationVariables,
|
||||
GetMangaFetchMutation,
|
||||
GetMangaFetchMutationVariables,
|
||||
GetMangaQuery,
|
||||
GetMangaQueryVariables,
|
||||
GetMangasQuery,
|
||||
GetMangasQueryVariables,
|
||||
GetRestoreStatusQuery,
|
||||
GetRestoreStatusQueryVariables,
|
||||
GetServerSettingsQuery,
|
||||
@@ -197,6 +193,8 @@ import {
|
||||
GetChaptersUpdatesQueryVariables,
|
||||
GetSourcesListQuery,
|
||||
GetSourcesListQueryVariables,
|
||||
GetMangasLibraryQuery,
|
||||
GetMangasLibraryQueryVariables,
|
||||
} 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';
|
||||
@@ -223,9 +221,9 @@ import {
|
||||
UPDATE_MANGAS_CATEGORIES,
|
||||
} from '@/lib/graphql/mutations/MangaMutation.ts';
|
||||
import {
|
||||
GET_MANGA,
|
||||
GET_MANGA_TO_MIGRATE,
|
||||
GET_MANGAS,
|
||||
GET_MANGA_TRACK_RECORDS,
|
||||
GET_MANGAS_LIBRARY,
|
||||
GET_MIGRATABLE_SOURCE_MANGAS,
|
||||
} from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
import {
|
||||
@@ -283,7 +281,7 @@ import { DOWNLOAD_STATUS_SUBSCRIPTION } from '@/lib/graphql/subscriptions/Downlo
|
||||
import { UPDATER_SUBSCRIPTION } from '@/lib/graphql/subscriptions/UpdaterSubscription.ts';
|
||||
import { GET_SERVER_SETTINGS } from '@/lib/graphql/queries/SettingsQuery.ts';
|
||||
import { UPDATE_SERVER_SETTINGS } from '@/lib/graphql/mutations/SettingsMutation.ts';
|
||||
import { BASE_MANGA_FIELDS, GLOBAL_METADATA } from '@/lib/graphql/Fragments.ts';
|
||||
import { GLOBAL_METADATA } from '@/lib/graphql/Fragments.ts';
|
||||
import { CLEAR_SERVER_CACHE } from '@/lib/graphql/mutations/ImageMutation.ts';
|
||||
import { RESET_WEBUI_UPDATE_STATUS, UPDATE_WEBUI } from '@/lib/graphql/mutations/ServerInfoMutation.ts';
|
||||
import { WEBUI_UPDATE_SUBSCRIPTION } from '@/lib/graphql/subscriptions/ServerInfoSubscription.ts';
|
||||
@@ -301,9 +299,11 @@ import {
|
||||
TRACKER_UPDATE_BIND,
|
||||
} from '@/lib/graphql/mutations/TrackerMutation.ts';
|
||||
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
||||
import { MetadataMigrationSettings, TManga } from '@/typings.ts';
|
||||
import { MetadataMigrationSettings } from '@/typings.ts';
|
||||
import { DOWNLOAD_STATUS_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts';
|
||||
import { EXTENSION_LIST_FIELDS } from '@/lib/graphql/fragments/ExtensionFragments.ts';
|
||||
import { MANGA_BASE_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
enum GQLMethod {
|
||||
QUERY = 'QUERY',
|
||||
@@ -1555,8 +1555,8 @@ export class RequestManager {
|
||||
(manga) =>
|
||||
this.graphQLClient.client.cache.readFragment<typeof manga>({
|
||||
id: this.graphQLClient.client.cache.identify(manga),
|
||||
fragment: BASE_MANGA_FIELDS,
|
||||
fragmentName: 'BASE_MANGA_FIELDS',
|
||||
fragment: MANGA_BASE_FIELDS,
|
||||
fragmentName: 'MANGA_BASE_FIELDS',
|
||||
}) ?? manga,
|
||||
),
|
||||
},
|
||||
@@ -1630,18 +1630,20 @@ export class RequestManager {
|
||||
);
|
||||
}
|
||||
|
||||
public useGetManga(
|
||||
public useGetManga<Data, Variables extends OperationVariables = OperationVariables>(
|
||||
document: DocumentNode | TypedDocumentNode<Data, Variables>,
|
||||
mangaId: number | string,
|
||||
options?: QueryHookOptions<GetMangaQuery, GetMangaQueryVariables>,
|
||||
): AbortableApolloUseQueryResponse<GetMangaQuery, GetMangaQueryVariables> {
|
||||
return this.doRequest(GQLMethod.USE_QUERY, GET_MANGA, { id: Number(mangaId) }, options);
|
||||
options?: QueryHookOptions<Data, Variables>,
|
||||
): AbortableApolloUseQueryResponse<Data, Variables> {
|
||||
return this.doRequest(GQLMethod.USE_QUERY, document, { id: Number(mangaId) } as unknown as Variables, options);
|
||||
}
|
||||
|
||||
public getManga(
|
||||
public getManga<Data, Variables extends OperationVariables = OperationVariables>(
|
||||
document: DocumentNode | TypedDocumentNode<Data, Variables>,
|
||||
mangaId: number | string,
|
||||
options?: QueryOptions<GetMangaQueryVariables, GetMangaQuery>,
|
||||
): AbortabaleApolloQueryResponse<GetMangaQuery> {
|
||||
return this.doRequest(GQLMethod.QUERY, GET_MANGA, { id: Number(mangaId) }, options);
|
||||
options?: QueryOptions<Variables, Data>,
|
||||
): AbortabaleApolloQueryResponse<Data> {
|
||||
return this.doRequest(GQLMethod.QUERY, document, { id: Number(mangaId) } as unknown as Variables, options);
|
||||
}
|
||||
|
||||
public getMangaToMigrate(
|
||||
@@ -1712,18 +1714,20 @@ export class RequestManager {
|
||||
);
|
||||
}
|
||||
|
||||
public useGetMangas(
|
||||
variables: GetMangasQueryVariables,
|
||||
options?: QueryHookOptions<GetMangasQuery, GetMangasQueryVariables>,
|
||||
): AbortableApolloUseQueryResponse<GetMangasQuery, GetMangasQueryVariables> {
|
||||
return this.doRequest(GQLMethod.USE_QUERY, GET_MANGAS, variables, options);
|
||||
public useGetMangas<Data, Variables extends OperationVariables = OperationVariables>(
|
||||
document: DocumentNode | TypedDocumentNode<Data, Variables>,
|
||||
variables: Variables,
|
||||
options?: QueryHookOptions<Data, Variables>,
|
||||
): AbortableApolloUseQueryResponse<Data, Variables> {
|
||||
return this.doRequest(GQLMethod.USE_QUERY, document, variables, options);
|
||||
}
|
||||
|
||||
public getMangas(
|
||||
variables: GetMangasQueryVariables,
|
||||
options?: QueryOptions<GetMangasQueryVariables, GetMangasQuery>,
|
||||
): AbortabaleApolloQueryResponse<GetMangasQuery> {
|
||||
return this.doRequest(GQLMethod.QUERY, GET_MANGAS, variables, options);
|
||||
public getMangas<Data, Variables extends OperationVariables = OperationVariables>(
|
||||
document: DocumentNode | TypedDocumentNode<Data, Variables>,
|
||||
variables: Variables,
|
||||
options?: QueryOptions<Variables, Data>,
|
||||
): AbortabaleApolloQueryResponse<Data> {
|
||||
return this.doRequest(GQLMethod.QUERY, document, variables, options);
|
||||
}
|
||||
|
||||
public useGetMigratableSourceMangas(
|
||||
@@ -2014,7 +2018,7 @@ export class RequestManager {
|
||||
|
||||
public updateChapters(
|
||||
ids: number[],
|
||||
patch: UpdateChapterPatchInput & { chapterIdsToDelete?: number[]; trackProgressMangaId?: TManga['id'] },
|
||||
patch: UpdateChapterPatchInput & { chapterIdsToDelete?: number[]; trackProgressMangaId?: MangaIdInfo['id'] },
|
||||
options?: MutationOptions<UpdateChaptersMutation, UpdateChaptersMutationVariables>,
|
||||
): AbortableApolloMutationResponse<UpdateChaptersMutation> {
|
||||
const { chapterIdsToDelete = [], trackProgressMangaId = -1, ...updatePatch } = patch;
|
||||
@@ -2144,8 +2148,8 @@ export class RequestManager {
|
||||
|
||||
public useGetCategoryMangas(
|
||||
id: number,
|
||||
options?: QueryHookOptions<GetMangasQuery, GetMangasQueryVariables>,
|
||||
): AbortableApolloUseQueryResponse<GetMangasQuery, GetMangasQueryVariables> {
|
||||
options?: QueryHookOptions<GetMangasLibraryQuery, GetMangasLibraryQueryVariables>,
|
||||
): AbortableApolloUseQueryResponse<GetMangasLibraryQuery, GetMangasLibraryQueryVariables> {
|
||||
const isDefaultCategory = id === 0;
|
||||
if (isDefaultCategory) {
|
||||
// hacky way of loading the default category mangas - some stuff won't work but since that is not used anyway, it won't be a problem
|
||||
@@ -2165,10 +2169,10 @@ export class RequestManager {
|
||||
__typename: 'Query',
|
||||
}
|
||||
: undefined,
|
||||
} as unknown as AbortableApolloUseQueryResponse<GetMangasQuery, GetMangasQueryVariables>;
|
||||
} as unknown as AbortableApolloUseQueryResponse<GetMangasLibraryQuery, GetMangasLibraryQueryVariables>;
|
||||
}
|
||||
|
||||
return this.useGetMangas({ condition: { inLibrary: true, categoryIds: [id] } }, options);
|
||||
return this.useGetMangas(GET_MANGAS_LIBRARY, { condition: { inLibrary: true, categoryIds: [id] } }, options);
|
||||
}
|
||||
|
||||
public deleteCategory(
|
||||
@@ -2597,7 +2601,7 @@ export class RequestManager {
|
||||
GQLMethod.MUTATION,
|
||||
TRACKER_UNBIND,
|
||||
{ input: { recordId, deleteRemoteTrack } },
|
||||
{ refetchQueries: [GET_MANGA], ...options },
|
||||
{ refetchQueries: [GET_MANGA_TRACK_RECORDS], ...options },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,17 +23,22 @@ import { UpdateChecker } from '@/components/library/UpdateChecker';
|
||||
import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext';
|
||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||
import { useSelectableCollection } from '@/components/collection/useSelectableCollection.ts';
|
||||
import { TManga } from '@/typings.ts';
|
||||
import { SelectableCollectionSelectMode } from '@/components/collection/SelectableCollectionSelectMode.tsx';
|
||||
import { useGetVisibleLibraryMangas } from '@/components/library/useGetVisibleLibraryMangas.ts';
|
||||
import { SelectionFAB } from '@/components/collection/SelectionFAB.tsx';
|
||||
import { PARTIAL_MANGA_FIELDS } from '@/lib/graphql/Fragments.ts';
|
||||
import { MangaActionMenuItems } from '@/components/manga/MangaActionMenuItems.tsx';
|
||||
import { TabsMenu } from '@/components/tabs/TabsMenu.tsx';
|
||||
import { TabsWrapper } from '@/components/tabs/TabsWrapper.tsx';
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { GetCategoriesLibraryQuery, GetCategoriesLibraryQueryVariables } from '@/lib/graphql/generated/graphql.ts';
|
||||
import {
|
||||
GetCategoriesLibraryQuery,
|
||||
GetCategoriesLibraryQueryVariables,
|
||||
MangaChapterStatFieldsFragment,
|
||||
MangaType,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_CATEGORIES_LIBRARY } from '@/lib/graphql/queries/CategoryQuery.ts';
|
||||
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||
import { MANGA_CHAPTER_STAT_FIELDS } from '@/lib/graphql/fragments/MangaFragments.ts';
|
||||
|
||||
const TitleWithSizeTag = styled('span')({
|
||||
display: 'flex',
|
||||
@@ -95,7 +100,7 @@ export function Library() {
|
||||
handleSelectAll,
|
||||
handleSelection,
|
||||
clearSelection,
|
||||
} = useSelectableCollection<TManga['id'], string>(mangas.length, {
|
||||
} = useSelectableCollection<MangaType['id'], string>(mangas.length, {
|
||||
itemIds: mangaIds,
|
||||
currentKey: activeTab?.id.toString(),
|
||||
});
|
||||
@@ -107,14 +112,15 @@ export function Library() {
|
||||
|
||||
const selectedMangas = useMemo(
|
||||
() =>
|
||||
selectedItemIds.map(
|
||||
(id) =>
|
||||
requestManager.graphQLClient.client.cache.readFragment<TManga>({
|
||||
id: requestManager.graphQLClient.client.cache.identify({ __typename: 'MangaType', id }),
|
||||
fragment: PARTIAL_MANGA_FIELDS,
|
||||
fragmentName: 'PARTIAL_MANGA_FIELDS',
|
||||
})!,
|
||||
),
|
||||
selectedItemIds
|
||||
.map((id) =>
|
||||
Mangas.getFromCache<MangaChapterStatFieldsFragment>(
|
||||
id,
|
||||
MANGA_CHAPTER_STAT_FIELDS,
|
||||
'MANGA_CHAPTER_STAT_FIELDS',
|
||||
),
|
||||
)
|
||||
.filter((manga) => !!manga),
|
||||
[selectedItemIds.length, mangas],
|
||||
);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import { MangaDetails } from '@/components/manga/MangaDetails';
|
||||
import { MangaToolbarMenu } from '@/components/manga/MangaToolbarMenu';
|
||||
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
|
||||
import { GetMangaScreenQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_MANGA_SCREEN } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
|
||||
export const Manga: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -32,7 +34,13 @@ export const Manga: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const autofetchedRef = useRef(false);
|
||||
|
||||
const { data, error: mangaError, loading: isLoading, networkStatus, refetch } = requestManager.useGetManga(id);
|
||||
const {
|
||||
data,
|
||||
error: mangaError,
|
||||
loading: isLoading,
|
||||
networkStatus,
|
||||
refetch,
|
||||
} = requestManager.useGetManga<GetMangaScreenQuery>(GET_MANGA_SCREEN, id);
|
||||
const isValidating = isNetworkRequestInFlight(networkStatus);
|
||||
const manga = data?.manga;
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { TMigratableSource } from '@/components/MigrationCard.tsx';
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
|
||||
import { MangaGrid } from '@/components/MangaGrid.tsx';
|
||||
import { TPartialManga } from '@/typings.ts';
|
||||
import { GridLayouts } from '@/components/source/GridLayouts.tsx';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { GridLayout } from '@/components/context/LibraryOptionsContext.tsx';
|
||||
@@ -23,6 +21,7 @@ import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts
|
||||
import { GetSourceMigratableQuery, GetSourceMigratableQueryVariables } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_SOURCE_MIGRATABLE } from '@/lib/graphql/queries/SourceQuery.ts';
|
||||
import { SOURCE_BASE_FIELDS } from '@/lib/graphql/fragments/SourceFragments.ts';
|
||||
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
||||
|
||||
export const Migrate = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -117,11 +116,11 @@ export const Migrate = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<MangaGrid
|
||||
<BaseMangaGrid
|
||||
hasNextPage={false}
|
||||
loadMore={() => {}}
|
||||
isLoading={areMangasLoading}
|
||||
mangas={(migratableSourceMangasData?.mangas.nodes ?? []) as TPartialManga[]}
|
||||
mangas={migratableSourceMangasData?.mangas.nodes ?? []}
|
||||
gridLayout={gridLayout}
|
||||
mode="migrate.search"
|
||||
/>
|
||||
|
||||
@@ -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, TManga } from '@/typings';
|
||||
import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, ReaderType } from '@/typings';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import {
|
||||
checkAndHandleMissingStoredReaderSettings,
|
||||
@@ -31,6 +31,7 @@ import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import {
|
||||
GetChaptersReaderQuery,
|
||||
GetChaptersReaderQueryVariables,
|
||||
GetMangaReaderQuery,
|
||||
UpdateChapterPatchInput,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
@@ -38,6 +39,8 @@ 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';
|
||||
import { GET_MANGA_READER } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
import { TMangaReader } from '@/lib/data/Mangas.ts';
|
||||
|
||||
type TChapter = GetChaptersReaderQuery['chapters']['nodes'][number];
|
||||
|
||||
@@ -94,7 +97,7 @@ export function Reader() {
|
||||
lastReadAt: 0,
|
||||
chapters: { totalCount: 0 },
|
||||
trackRecords: { totalCount: 0 },
|
||||
}) as unknown as TManga,
|
||||
}) as unknown as TMangaReader,
|
||||
[mangaId],
|
||||
);
|
||||
|
||||
@@ -103,7 +106,7 @@ export function Reader() {
|
||||
loading: isMangaLoading,
|
||||
error: mangaError,
|
||||
refetch: refetchManga,
|
||||
} = requestManager.useGetManga(mangaId);
|
||||
} = requestManager.useGetManga<GetMangaReaderQuery>(GET_MANGA_READER, mangaId);
|
||||
const loadedChapter = useRef<TChapter | null>(null);
|
||||
const isChapterLoaded =
|
||||
Number(mangaId) === loadedChapter.current?.mangaId &&
|
||||
|
||||
@@ -19,7 +19,6 @@ import { langSortCmp, sourceDefualtLangs, sourceForcedDefaultLangs } from '@/uti
|
||||
import { translateExtensionLanguage } from '@/screens/util/Extensions';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
import { LangSelect } from '@/components/navbar/action/LangSelect';
|
||||
import { MangaGrid } from '@/components/MangaGrid';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||
import { MangaCardProps } from '@/components/manga/MangaCard.types.tsx';
|
||||
@@ -28,6 +27,7 @@ import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
|
||||
import { SourceType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
||||
|
||||
type SourceLoadingState = { isLoading: boolean; hasResults: boolean; emptySearch: boolean };
|
||||
type SourceToLoadingStateMap = Map<string, SourceLoadingState>;
|
||||
@@ -166,7 +166,7 @@ const SourceSearchPreview = React.memo(
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<MangaGrid
|
||||
<BaseMangaGrid
|
||||
mangas={mangas}
|
||||
isLoading={isLoading}
|
||||
hasNextPage={false}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { styled } from '@mui/material/styles';
|
||||
import FavoriteIcon from '@mui/icons-material/Favorite';
|
||||
import NewReleasesIcon from '@mui/icons-material/NewReleases';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import { IPos, TPartialManga, TranslationKey } from '@/typings';
|
||||
import { IPos, TranslationKey } from '@/typings';
|
||||
import {
|
||||
requestManager,
|
||||
AbortableApolloUseMutationPaginatedResponse,
|
||||
@@ -30,7 +30,7 @@ import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsCon
|
||||
import { SourceGridLayout } from '@/components/source/SourceGridLayout';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
import { SourceOptions } from '@/components/source/SourceOptions';
|
||||
import { SourceMangaGrid } from '@/components/source/SourceMangaGrid';
|
||||
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
||||
import {
|
||||
GetSourceBrowseQuery,
|
||||
GetSourceBrowseQueryVariables,
|
||||
@@ -45,6 +45,7 @@ import { getGridSnapshotKey } from '@/components/MangaGrid.tsx';
|
||||
import { createUpdateSourceMetadata, getSourceMetadata } from '@/lib/metadata/sourceMetadata.ts';
|
||||
import { makeToast } from '@/components/util/Toast.tsx';
|
||||
import { GET_SOURCE_BROWSE } from '@/lib/graphql/queries/SourceQuery.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
|
||||
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@@ -92,9 +93,9 @@ const SOURCE_CONTENT_TYPE_TO_ERROR_MSG_KEY: { [contentType in SourceContentType]
|
||||
[SourceContentType.SEARCH]: 'manga.error.label.no_matches',
|
||||
};
|
||||
|
||||
const getUniqueMangas = (mangas: TPartialManga[]): TPartialManga[] => {
|
||||
const mangaIdToManga: Record<TPartialManga['id'], TPartialManga> = {};
|
||||
const uniqueMangas: TPartialManga[] = [];
|
||||
const getUniqueMangas = <Manga extends MangaIdInfo>(mangas: Manga[]): Manga[] => {
|
||||
const mangaIdToManga: Record<string, Manga> = {};
|
||||
const uniqueMangas: Manga[] = [];
|
||||
|
||||
mangas.forEach((manga) => {
|
||||
const isDuplicate = !!mangaIdToManga[manga.id];
|
||||
@@ -470,7 +471,7 @@ export function SourceMangas() {
|
||||
{t('global.button.filter')}
|
||||
</ContentTypeButton>
|
||||
</ContentTypeMenu>
|
||||
<SourceMangaGrid
|
||||
<BaseMangaGrid
|
||||
key={contentType}
|
||||
mangas={mangas}
|
||||
hasNextPage={hasNextPage}
|
||||
@@ -479,6 +480,8 @@ export function SourceMangas() {
|
||||
messageExtra={messageExtra}
|
||||
isLoading={isLoading}
|
||||
gridLayout={options.SourcegridLayout}
|
||||
mode="source"
|
||||
inLibraryIndicator
|
||||
/>
|
||||
{contentType === SourceContentType.SEARCH && (
|
||||
<SourceOptions
|
||||
|
||||
@@ -15,8 +15,6 @@ import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Box from '@mui/material/Box';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { MangaGrid } from '@/components/MangaGrid.tsx';
|
||||
import { TManga } from '@/typings';
|
||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { GridLayout } from '@/components/context/LibraryOptionsContext.tsx';
|
||||
@@ -28,20 +26,34 @@ import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts
|
||||
import { MangaCard } from '@/components/MangaCard.tsx';
|
||||
import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
||||
import { StyledGroupHeader } from '@/components/virtuoso/StyledGroupHeader.tsx';
|
||||
import {
|
||||
GetMangasDuplicatesQuery,
|
||||
GetMangasDuplicatesQueryVariables,
|
||||
MangaType,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_MANGAS_DUPLICATES } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
||||
import { IMangaGridProps } from '@/components/MangaGrid.tsx';
|
||||
|
||||
const findDuplicatesByTitle = (libraryMangas: TManga[]): Record<string, TManga[]> => {
|
||||
const findDuplicatesByTitle = <Manga extends Pick<MangaType, 'title'>>(
|
||||
libraryMangas: Manga[],
|
||||
): Record<string, Manga[]> => {
|
||||
const titleToMangas = Object.groupBy(libraryMangas, ({ title }) => title.toLowerCase().trim());
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(titleToMangas)
|
||||
.filter((titleToMangaMap): titleToMangaMap is [string, TManga[]] => (titleToMangaMap[1]?.length ?? 0) > 1)
|
||||
.filter((titleToMangaMap): titleToMangaMap is [string, Manga[]] => (titleToMangaMap[1]?.length ?? 0) > 1)
|
||||
.map(([, mangas]) => [mangas[0].title, mangas]),
|
||||
);
|
||||
};
|
||||
|
||||
const findDuplicatesByTitleAndAlternativeTitles = (libraryMangas: TManga[]): Record<string, TManga[]> => {
|
||||
const idToDuplicateStatus: Record<TManga['id'], boolean> = {};
|
||||
const titleToMangas: Record<string, TManga[]> = {};
|
||||
type TMangaDuplicate = Pick<MangaType, 'id' | 'title' | 'description'>;
|
||||
const findDuplicatesByTitleAndAlternativeTitles = <Manga extends TMangaDuplicate>(
|
||||
libraryMangas: Manga[],
|
||||
): Record<string, Manga[]> => {
|
||||
const idToDuplicateStatus: Record<MangaIdInfo['id'], boolean> = {};
|
||||
const titleToMangas: Record<string, Manga[]> = {};
|
||||
|
||||
libraryMangas.forEach((mangaToCheck) =>
|
||||
libraryMangas.forEach((libraryManga) => {
|
||||
@@ -123,10 +135,13 @@ export const LibraryDuplicates = () => {
|
||||
};
|
||||
}, [t, gridLayout, checkAlternativeTitles]);
|
||||
|
||||
const { data, loading, error, refetch } = requestManager.useGetMangas({ condition: { inLibrary: true } });
|
||||
const { data, loading, error, refetch } = requestManager.useGetMangas<
|
||||
GetMangasDuplicatesQuery,
|
||||
GetMangasDuplicatesQueryVariables
|
||||
>(GET_MANGAS_DUPLICATES, { condition: { inLibrary: true } });
|
||||
|
||||
const mangasByTitle = useMemo(() => {
|
||||
const libraryMangas: TManga[] = data?.mangas.nodes ?? [];
|
||||
const libraryMangas: TMangaDuplicate[] = data?.mangas.nodes ?? [];
|
||||
|
||||
if (checkAlternativeTitles) {
|
||||
return findDuplicatesByTitleAndAlternativeTitles(libraryMangas);
|
||||
@@ -172,7 +187,7 @@ export const LibraryDuplicates = () => {
|
||||
itemContent={(index) => (
|
||||
<Box key={duplicatedMangas[index].id} sx={{ px: 1, pb: 1 }}>
|
||||
<MangaCard
|
||||
manga={duplicatedMangas[index]}
|
||||
manga={duplicatedMangas[index] as IMangaGridProps['mangas'][number]}
|
||||
gridLayout={gridLayout}
|
||||
selected={null}
|
||||
mode="duplicate"
|
||||
@@ -188,12 +203,13 @@ export const LibraryDuplicates = () => {
|
||||
<StyledGroupHeader sx={{ pt: index === 0 ? undefined : 0, pb: 0 }} variant="h5" isFirstItem={false}>
|
||||
{title}
|
||||
</StyledGroupHeader>
|
||||
<MangaGrid
|
||||
mangas={mangasByTitle[title]}
|
||||
<BaseMangaGrid
|
||||
mangas={mangasByTitle[title] as IMangaGridProps['mangas']}
|
||||
hasNextPage={false}
|
||||
loadMore={() => {}}
|
||||
isLoading={false}
|
||||
gridLayout={gridLayout}
|
||||
inLibraryIndicator={false}
|
||||
horizontal
|
||||
mode="duplicate"
|
||||
/>
|
||||
|
||||
@@ -29,14 +29,23 @@ import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCe
|
||||
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
|
||||
import { ListItemLink } from '@/components/util/ListItemLink.tsx';
|
||||
import { GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables } from '@/lib/graphql/generated/graphql.ts';
|
||||
import {
|
||||
GetCategoriesSettingsQuery,
|
||||
GetCategoriesSettingsQueryVariables,
|
||||
GetMangasBaseQuery,
|
||||
GetMangasBaseQueryVariables,
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts';
|
||||
import { GET_MANGAS_BASE } from '@/lib/graphql/queries/MangaQuery.ts';
|
||||
|
||||
const removeNonLibraryMangasFromCategories = async (): Promise<void> => {
|
||||
try {
|
||||
const nonLibraryMangas = await requestManager.getMangas({
|
||||
filter: { inLibrary: { equalTo: false }, categoryId: { isNull: false } },
|
||||
}).response;
|
||||
const nonLibraryMangas = await requestManager.getMangas<GetMangasBaseQuery, GetMangasBaseQueryVariables>(
|
||||
GET_MANGAS_BASE,
|
||||
{
|
||||
filter: { inLibrary: { equalTo: false }, categoryId: { isNull: false } },
|
||||
},
|
||||
).response;
|
||||
|
||||
const mangaIdsToRemove = Mangas.getIds(nonLibraryMangas.data.mangas.nodes);
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ import { ParseKeys } from 'i18next';
|
||||
import { Location } from 'react-router-dom';
|
||||
import {
|
||||
GetChaptersReaderQuery,
|
||||
GetMangaQuery,
|
||||
GetServerSettingsQuery,
|
||||
GetSourceBrowseQuery,
|
||||
GetSourceSettingsQuery,
|
||||
MangaReaderFieldsFragment,
|
||||
MetaType,
|
||||
SourcePreferenceChangeInput,
|
||||
TrackerType,
|
||||
@@ -84,13 +84,6 @@ export type AppMetadataKeys = MetadataServerSettingKeys | MangaMetadataKeys | Se
|
||||
|
||||
export type MetadataKeyValuePair = [AppMetadataKeys, AllowedMetadataValueTypes];
|
||||
|
||||
export type TManga = GetMangaQuery['manga'];
|
||||
|
||||
export type TPartialManga = OptionalProperty<
|
||||
TManga,
|
||||
'unreadCount' | 'downloadCount' | 'bookmarkCount' | 'categories' | 'chapters'
|
||||
>;
|
||||
|
||||
export interface INavbarOverride {
|
||||
status: boolean;
|
||||
value: any;
|
||||
@@ -187,7 +180,7 @@ export interface IReaderProps {
|
||||
curPage: number;
|
||||
initialPage: number;
|
||||
settings: IReaderSettings;
|
||||
manga: TManga;
|
||||
manga: MangaReaderFieldsFragment;
|
||||
chapter: GetChaptersReaderQuery['chapters']['nodes'][number];
|
||||
nextChapter: () => void;
|
||||
prevChapter: () => void;
|
||||
|
||||
@@ -42,7 +42,7 @@ const addImports = format(
|
||||
`import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
|
||||
import {
|
||||
\tGetChaptersMangaQuery, GetDownloadStatusQueryVariables, GetGlobalMetadataQueryVariables,
|
||||
\tGetMangaQueryVariables, GetSourceBrowseQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
|
||||
\tGetMangaScreenQueryVariables, GetSourceBrowseQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
|
||||
} from "@/lib/graphql/generated/graphql.ts";
|
||||
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";`,
|
||||
);
|
||||
@@ -93,7 +93,7 @@ const fixTypingOfQueryTypePolicies = format(
|
||||
\textensions?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
\tgetWebUIUpdateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>>,
|
||||
\tlastUpdateTimestamp?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
\tmanga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>>,
|
||||
\tmanga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaScreenQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaScreenQueryVariables>>,
|
||||
\tmangas?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
\tmeta?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetGlobalMetadataQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetGlobalMetadataQueryVariables>>,
|
||||
\tmetas?: FieldPolicy<any> | FieldReadFunction<any>,
|
||||
|
||||
Reference in New Issue
Block a user