Reduce requested manga data in queries
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user