From af9d49e5e99fa52a6534307f46ec5a630619c9d7 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Wed, 6 Sep 2023 21:47:13 +0200 Subject: [PATCH] Use gql for "mangas" II - category mangas --- src/components/MangaCard.tsx | 7 ++--- src/components/MangaGrid.tsx | 8 +++--- src/components/library/LibraryMangaGrid.tsx | 30 +++++++++++---------- src/lib/requests/RequestManager.ts | 10 ++++--- src/screens/Library.tsx | 25 ++++++++++------- 5 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/components/MangaCard.tsx b/src/components/MangaCard.tsx index 892b1751b9..c6e076263a 100644 --- a/src/components/MangaCard.tsx +++ b/src/components/MangaCard.tsx @@ -12,10 +12,10 @@ import Typography from '@mui/material/Typography'; import { Link } from 'react-router-dom'; import { Avatar, Box, CardContent, styled } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { IMangaCard } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; import { GridLayout, useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext'; import SpinnerImage from '@/components/util/SpinnerImage'; +import { MangaType } from '@/lib/graphql/generated/graphql.ts'; const BottomGradient = styled('div')({ position: 'absolute', @@ -66,7 +66,7 @@ const BadgeContainer = styled('div')({ }); interface IProps { - manga: IMangaCard; + manga: MangaType; gridLayout?: GridLayout; inLibraryIndicator?: boolean; } @@ -75,10 +75,11 @@ const MangaCard = (props: IProps) => { const { t } = useTranslation(); const { - manga: { id, title, thumbnailUrl, downloadCount, unreadCount: unread, inLibrary }, + manga: { id, title, thumbnailUrl: tmpThumbnailUrl, downloadCount, unreadCount: unread, inLibrary }, gridLayout, inLibraryIndicator, } = props; + const thumbnailUrl = tmpThumbnailUrl ?? 'nonExistingMangaUrl'; const { options: { showUnreadBadge, showDownloadBadge }, } = useLibraryOptionsContext(); diff --git a/src/components/MangaGrid.tsx b/src/components/MangaGrid.tsx index 1cabeb338a..6051ff4fe7 100644 --- a/src/components/MangaGrid.tsx +++ b/src/components/MangaGrid.tsx @@ -11,12 +11,12 @@ import Grid, { GridTypeMap } from '@mui/material/Grid'; import { Box, Typography } from '@mui/material'; import { GridItemProps, VirtuosoGrid, VirtuosoGridHandle } from 'react-virtuoso'; import { useNavigate, useLocation } from 'react-router-dom'; -import { IMangaCard } from '@/typings'; import EmptyView from '@/components/util/EmptyView'; import LoadingPlaceholder from '@/components/util/LoadingPlaceholder'; import MangaCard from '@/components/MangaCard'; import { GridLayout } from '@/components/context/LibraryOptionsContext'; import useLocalStorage from '@/util/useLocalStorage'; +import { MangaType } from '@/lib/graphql/generated/graphql.ts'; const GridContainer = React.forwardRef(({ children, ...props }, ref) => ( @@ -40,13 +40,13 @@ const GridItemContainerWithDimension = ( ); }; -const createMangaCard = (manga: IMangaCard, gridLayout?: GridLayout, inLibraryIndicator?: boolean) => ( +const createMangaCard = (manga: MangaType, gridLayout?: GridLayout, inLibraryIndicator?: boolean) => ( ); type DefaultGridProps = { isLoading: boolean; - mangas: IMangaCard[]; + mangas: MangaType[]; inLibraryIndicator?: boolean; GridItemContainer: (props: GridTypeMap['props'] & Partial) => JSX.Element; gridLayout?: GridLayout; @@ -150,7 +150,7 @@ const VerticalGrid = ({ }; export interface IMangaGridProps { - mangas: IMangaCard[]; + mangas: MangaType[]; isLoading: boolean; message?: string; messageExtra?: JSX.Element; diff --git a/src/components/library/LibraryMangaGrid.tsx b/src/components/library/LibraryMangaGrid.tsx index c3250e057a..321f0ec6a7 100644 --- a/src/components/library/LibraryMangaGrid.tsx +++ b/src/components/library/LibraryMangaGrid.tsx @@ -9,12 +9,13 @@ import React, { useEffect, useMemo } from 'react'; import { StringParam, useQueryParam } from 'use-query-params'; import { useTranslation } from 'react-i18next'; -import { IMangaCard, LibrarySortMode, NullAndUndefined } from '@/typings'; +import { LibrarySortMode, NullAndUndefined } from '@/typings'; import { useSearchSettings } from '@/util/searchSettings'; import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext'; import MangaGrid from '@/components/MangaGrid'; +import { MangaType } from '@/lib/graphql/generated/graphql.ts'; -const unreadFilter = (unread: NullAndUndefined, { unreadCount }: IMangaCard): boolean => { +const unreadFilter = (unread: NullAndUndefined, { unreadCount }: MangaType): boolean => { switch (unread) { case true: return !!unreadCount && unreadCount >= 1; @@ -25,7 +26,7 @@ const unreadFilter = (unread: NullAndUndefined, { unreadCount }: IManga } }; -const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount }: IMangaCard): boolean => { +const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount }: MangaType): boolean => { switch (downloaded) { case true: return !!downloadCount && downloadCount >= 1; @@ -36,24 +37,24 @@ const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount } }; -const queryFilter = (query: NullAndUndefined, { title }: IMangaCard): boolean => { +const queryFilter = (query: NullAndUndefined, { title }: MangaType): boolean => { if (!query) return true; return title.toLowerCase().includes(query.toLowerCase()); }; -const queryGenreFilter = (query: NullAndUndefined, { genre }: IMangaCard): boolean => { +const queryGenreFilter = (query: NullAndUndefined, { genre }: MangaType): 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 filterManga = ( - mangas: IMangaCard[], + mangas: MangaType[], query: NullAndUndefined, unread: NullAndUndefined, downloaded: NullAndUndefined, ignoreFilters: boolean, -): IMangaCard[] => +): MangaType[] => mangas.filter((manga) => { const ignoreFiltersWhileSearching = ignoreFilters && query?.length; const matchesSearch = queryFilter(query, manga) || queryGenreFilter(query, manga); @@ -63,19 +64,20 @@ const filterManga = ( return matchesSearch && matchesFilters; }); -const sortByUnread = (a: IMangaCard, b: IMangaCard): number => (a.unreadCount ?? 0) - (b.unreadCount ?? 0); +const sortByUnread = (a: MangaType, b: MangaType): number => (a.unreadCount ?? 0) - (b.unreadCount ?? 0); -const sortByTitle = (a: IMangaCard, b: IMangaCard): number => a.title.localeCompare(b.title); +const sortByTitle = (a: MangaType, b: MangaType): number => a.title.localeCompare(b.title); -const sortByDateAdded = (a: IMangaCard, b: IMangaCard): number => a.inLibraryAt - b.inLibraryAt; +const sortByDateAdded = (a: MangaType, b: MangaType): number => Number(a.inLibraryAt) - Number(b.inLibraryAt); -const sortByLastRead = (a: IMangaCard, b: IMangaCard): number => b.lastReadAt - a.lastReadAt; +const sortByLastRead = (a: MangaType, b: MangaType): number => + Number(b.lastReadChapter?.lastReadAt ?? 0) - Number(a.lastReadChapter?.lastReadAt ?? 0); const sortManga = ( - manga: IMangaCard[], + manga: MangaType[], sort: NullAndUndefined, desc: NullAndUndefined, -): IMangaCard[] => { +): MangaType[] => { const result = [...manga]; switch (sort) { @@ -103,7 +105,7 @@ const sortManga = ( }; interface LibraryMangaGridProps { - mangas: IMangaCard[]; + mangas: MangaType[]; isLoading: boolean; message?: string; } diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 118b76a618..4bbdc4031f 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -25,7 +25,6 @@ import { BackupValidationResult, ICategory, IChapter, - IManga, IMangaChapter, ISourceFilters, PaginatedList, @@ -65,6 +64,8 @@ import { GetExtensionsFetchMutationVariables, GetCategoriesQuery, GetCategoriesQueryVariables, + GetCategoryMangasQuery, + GetCategoryMangasQueryVariables, GetExtensionsQuery, GetExtensionsQueryVariables, GetGlobalMetadatasQuery, @@ -918,8 +919,11 @@ export class RequestManager { ); } - public useGetCategoryMangas(categoryId: number, swrOptions?: SWROptions): AbortableSWRResponse { - return this.doRequest(HttpMethod.SWR_GET, `category/${categoryId}`, { swrOptions }); + public useGetCategoryMangas( + id: number, + options?: QueryHookOptions, + ): AbortableApolloUseQueryResponse { + return this.doRequestNew(GQLMethod.USE_QUERY, GET_CATEGORY_MANGAS, { id }, options); } public deleteCategory(categoryId: number): AbortableApolloMutationResponse { diff --git a/src/screens/Library.tsx b/src/screens/Library.tsx index ecaf427912..dd11d669b8 100644 --- a/src/screens/Library.tsx +++ b/src/screens/Library.tsx @@ -20,6 +20,7 @@ import LibraryMangaGrid from '@/components/library/LibraryMangaGrid'; import AppbarSearch from '@/components/util/AppbarSearch'; import UpdateChecker from '@/components/library/UpdateChecker'; import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext'; +import { MangaType } from '@/lib/graphql/generated/graphql.ts'; const StyledGridWrapper = styled(Box)(({ theme }) => ({ // TabsMenu height + TabsMenu bottom padding - grid item top padding @@ -63,8 +64,14 @@ export default function Library() { const { options } = useLibraryOptionsContext(); const [lastLibraryUpdate, setLastLibraryUpdate] = useState(Date.now()); - const { data, error: tabsError, loading: areCategoriesLoading } = requestManager.useGetCategories(); - const tabsData = data?.categories.nodes.filter((category) => category.id !== 0); + const { + data: categoriesResponse, + error: tabsError, + loading: areCategoriesLoading, + } = requestManager.useGetCategories(); + const tabsData = categoriesResponse?.categories.nodes.filter( + (category) => category.id !== 0 || (category.id === 0 && category.mangas.totalCount), + ); const tabs = tabsData ?? []; const librarySize = useMemo( () => tabs.map((tab) => tab.mangas.totalCount).reduce((prev, curr) => prev + curr, 0), @@ -75,11 +82,11 @@ export default function Library() { const activeTab = tabs.find((tab) => tab.order === tabSearchParam) ?? tabs[0]; const { - data: mangaData, + data: categoryMangaResponse, error: mangaError, - isLoading: mangaLoading, - } = requestManager.useGetCategoryMangas(activeTab?.id, { skipRequest: !activeTab }); - const mangas = mangaData ?? []; + loading: mangaLoading, + } = requestManager.useGetCategoryMangas(activeTab?.id, { skip: !activeTab }); + const mangas = (categoryMangaResponse?.category.mangas.nodes as unknown as MangaType[]) ?? []; const { setTitle, setAction } = useContext(NavbarContext); useEffect(() => { @@ -105,14 +112,14 @@ export default function Library() { }, [t, librarySize, areCategoriesLoading, options]); const handleTabChange = (newTab: number) => { - setTabSearchParam(newTab === 0 ? undefined : newTab); + setTabSearchParam(newTab); }; if (tabsError != null) { return ( ); } @@ -171,7 +178,7 @@ export default function Library() { (mangaError ? ( ) : (