diff --git a/src/components/ExtensionCard.tsx b/src/components/ExtensionCard.tsx index 2825f30b..cc917968 100644 --- a/src/components/ExtensionCard.tsx +++ b/src/components/ExtensionCard.tsx @@ -14,11 +14,11 @@ import Avatar from '@mui/material/Avatar'; import Typography from '@mui/material/Typography'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { Extension, TranslationKey } from '@/typings'; +import { PartialExtension, TranslationKey } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; interface IProps { - extension: Extension; + extension: PartialExtension; } enum ExtensionAction { diff --git a/src/components/MangaCard.tsx b/src/components/MangaCard.tsx index c6e07626..bba7181f 100644 --- a/src/components/MangaCard.tsx +++ b/src/components/MangaCard.tsx @@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next'; 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'; +import { TPartialManga } from '@/typings.ts'; const BottomGradient = styled('div')({ position: 'absolute', @@ -66,7 +66,7 @@ const BadgeContainer = styled('div')({ }); interface IProps { - manga: MangaType; + manga: TPartialManga; gridLayout?: GridLayout; inLibraryIndicator?: boolean; } @@ -120,10 +120,10 @@ const MangaCard = (props: IProps) => { {t('manga.button.in_library')} )} - {showUnreadBadge && unread! > 0 && ( + {showUnreadBadge && (unread ?? 0) > 0 && ( {unread} )} - {showDownloadBadge && downloadCount! > 0 && ( + {showDownloadBadge && (downloadCount ?? 0) > 0 && ( (({ children, ...props }, ref) => ( @@ -40,13 +40,13 @@ const GridItemContainerWithDimension = ( ); }; -const createMangaCard = (manga: MangaType, gridLayout?: GridLayout, inLibraryIndicator?: boolean) => ( +const createMangaCard = (manga: TPartialManga, gridLayout?: GridLayout, inLibraryIndicator?: boolean) => ( ); type DefaultGridProps = { isLoading: boolean; - mangas: MangaType[]; + mangas: TPartialManga[]; inLibraryIndicator?: boolean; GridItemContainer: (props: GridTypeMap['props'] & Partial) => JSX.Element; gridLayout?: GridLayout; @@ -150,7 +150,7 @@ const VerticalGrid = ({ }; export interface IMangaGridProps { - mangas: MangaType[]; + mangas: TPartialManga[]; isLoading: boolean; message?: string; messageExtra?: JSX.Element; diff --git a/src/components/library/LibraryMangaGrid.tsx b/src/components/library/LibraryMangaGrid.tsx index c2534b2c..93b67d99 100644 --- a/src/components/library/LibraryMangaGrid.tsx +++ b/src/components/library/LibraryMangaGrid.tsx @@ -9,13 +9,12 @@ import React, { useEffect, useMemo } from 'react'; import { StringParam, useQueryParam } from 'use-query-params'; import { useTranslation } from 'react-i18next'; -import { LibrarySortMode, NullAndUndefined } from '@/typings'; +import { LibrarySortMode, NullAndUndefined, TManga } 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 }: MangaType): boolean => { +const unreadFilter = (unread: NullAndUndefined, { unreadCount }: TManga): boolean => { switch (unread) { case true: return !!unreadCount && unreadCount >= 1; @@ -26,7 +25,7 @@ const unreadFilter = (unread: NullAndUndefined, { unreadCount }: MangaT } }; -const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount }: MangaType): boolean => { +const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount }: TManga): boolean => { switch (downloaded) { case true: return !!downloadCount && downloadCount >= 1; @@ -37,24 +36,24 @@ const downloadedFilter = (downloaded: NullAndUndefined, { downloadCount } }; -const queryFilter = (query: NullAndUndefined, { title }: MangaType): boolean => { +const queryFilter = (query: NullAndUndefined, { title }: TManga): boolean => { if (!query) return true; return title.toLowerCase().includes(query.toLowerCase()); }; -const queryGenreFilter = (query: NullAndUndefined, { genre }: MangaType): boolean => { +const queryGenreFilter = (query: NullAndUndefined, { genre }: TManga): 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: MangaType[], + mangas: TManga[], query: NullAndUndefined, unread: NullAndUndefined, downloaded: NullAndUndefined, ignoreFilters: boolean, -): MangaType[] => +): TManga[] => mangas.filter((manga) => { const ignoreFiltersWhileSearching = ignoreFilters && query?.length; const matchesSearch = queryFilter(query, manga) || queryGenreFilter(query, manga); @@ -64,20 +63,20 @@ const filterManga = ( return matchesSearch && matchesFilters; }); -const sortByUnread = (a: MangaType, b: MangaType): number => (a.unreadCount ?? 0) - (b.unreadCount ?? 0); +const sortByUnread = (a: TManga, b: TManga): number => (a.unreadCount ?? 0) - (b.unreadCount ?? 0); -const sortByTitle = (a: MangaType, b: MangaType): number => a.title.localeCompare(b.title); +const sortByTitle = (a: TManga, b: TManga): number => a.title.localeCompare(b.title); -const sortByDateAdded = (a: MangaType, b: MangaType): number => Number(a.inLibraryAt) - Number(b.inLibraryAt); +const sortByDateAdded = (a: TManga, b: TManga): number => Number(a.inLibraryAt) - Number(b.inLibraryAt); -const sortByLastRead = (a: MangaType, b: MangaType): number => +const sortByLastRead = (a: TManga, b: TManga): number => Number(b.lastReadChapter?.lastReadAt ?? 0) - Number(a.lastReadChapter?.lastReadAt ?? 0); const sortManga = ( - manga: MangaType[], + manga: TManga[], sort: NullAndUndefined, desc: NullAndUndefined, -): MangaType[] => { +): TManga[] => { const result = [...manga]; switch (sort) { @@ -105,7 +104,7 @@ const sortManga = ( }; interface LibraryMangaGridProps { - mangas: MangaType[]; + mangas: TManga[]; isLoading: boolean; message?: string; } diff --git a/src/components/manga/ChapterCard.tsx b/src/components/manga/ChapterCard.tsx index b415db85..a39db05a 100644 --- a/src/components/manga/ChapterCard.tsx +++ b/src/components/manga/ChapterCard.tsx @@ -30,10 +30,11 @@ import { useTranslation } from 'react-i18next'; import requestManager from '@/lib/requests/RequestManager.ts'; import { getUploadDateString } from '@/util/date'; import DownloadStateIndicator from '@/components/molecules/DownloadStateIndicator'; -import { ChapterType, DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; +import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; +import { TChapter } from '@/typings.ts'; interface IProps { - chapter: ChapterType; + chapter: TChapter; chapterIds: number[]; downloadChapter: DownloadType | undefined; showChapterNumber: boolean; diff --git a/src/components/manga/ChapterList.tsx b/src/components/manga/ChapterList.tsx index bb53fb22..b53e5732 100644 --- a/src/components/manga/ChapterList.tsx +++ b/src/components/manga/ChapterList.tsx @@ -11,7 +11,7 @@ import Typography from '@mui/material/Typography'; import React, { ComponentProps, useEffect, useMemo, useRef, useState } from 'react'; import { Virtuoso } from 'react-virtuoso'; import { useTranslation } from 'react-i18next'; -import { TranslationKey } from '@/typings'; +import { TChapter, TManga, TranslationKey } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; import ChapterCard from '@/components/manga/ChapterCard'; import ResumeFab from '@/components/manga/ResumeFAB'; @@ -21,7 +21,7 @@ import makeToast from '@/components/util/Toast'; import ChaptersToolbarMenu from '@/components/manga/ChaptersToolbarMenu'; import SelectionFAB from '@/components/manga/SelectionFAB'; import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab'; -import { ChapterType, DownloadType, MangaType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; +import { DownloadType, UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts'; const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({ listStyle: 'none', @@ -68,13 +68,13 @@ const actionsStrings: { }; export interface IChapterWithMeta { - chapter: ChapterType; + chapter: TChapter; downloadChapter: DownloadType | undefined; selected: boolean | null; } interface IProps { - manga: MangaType; + manga: TManga; isRefreshing: boolean; } @@ -88,10 +88,7 @@ const ChapterList: React.FC = ({ manga, isRefreshing }) => { const [options, dispatch] = useChapterOptions(manga.id); const { data: chaptersData, loading: isLoading, refetch } = requestManager.useGetMangaChapters(manga.id); - const chapters = useMemo( - () => (chaptersData?.chapters.nodes as ChapterType[]) ?? [], - [chaptersData?.chapters.nodes], - ); + const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]); const mangaChapterIds = useMemo(() => chapters.map((chapter) => chapter.id), [chapters]); useEffect(() => { diff --git a/src/components/manga/MangaDetails.tsx b/src/components/manga/MangaDetails.tsx index 71a67232..19cbc91d 100644 --- a/src/components/manga/MangaDetails.tsx +++ b/src/components/manga/MangaDetails.tsx @@ -14,10 +14,9 @@ import React, { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { t as translate } from 'i18next'; import Button from '@mui/material/Button'; -import { ISource } from '@/typings'; +import { ISource, TManga } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; import makeToast from '@/components/util/Toast'; -import { MangaType } from '@/lib/graphql/generated/graphql.ts'; const DetailsWrapper = styled('div')(({ theme }) => ({ width: '100%', @@ -151,7 +150,7 @@ const OpenSourceButton = ({ url }: { url?: string | null }) => { }; interface IProps { - manga: MangaType; + manga: TManga; } function getSourceName(source?: ISource | null) { diff --git a/src/components/manga/MangaToolbarMenu.tsx b/src/components/manga/MangaToolbarMenu.tsx index 4e035d04..5a2f886b 100644 --- a/src/components/manga/MangaToolbarMenu.tsx +++ b/src/components/manga/MangaToolbarMenu.tsx @@ -22,10 +22,10 @@ import { import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import CategorySelect from '@/components/navbar/action/CategorySelect'; -import { MangaType } from '@/lib/graphql/generated/graphql.ts'; +import { TManga } from '@/typings.ts'; interface IProps { - manga: MangaType; + manga: TManga; onRefresh: () => any; refreshing: boolean; } diff --git a/src/components/manga/util.tsx b/src/components/manga/util.tsx index edfb25c0..ec63ee08 100644 --- a/src/components/manga/util.tsx +++ b/src/components/manga/util.tsx @@ -12,10 +12,10 @@ import { ChapterOptionsReducerAction, ChapterSortMode, NullAndUndefined, + TChapter, TranslationKey, } from '@/typings'; import { useReducerLocalStorage } from '@/util/useLocalStorage'; -import { ChapterType } from '@/lib/graphql/generated/graphql.ts'; const defaultChapterOptions: ChapterListOptions = { active: false, @@ -46,7 +46,7 @@ function chapterOptionsReducer(state: ChapterListOptions, actions: ChapterOption } } -export function unreadFilter(unread: NullAndUndefined, { isRead: isChapterRead }: ChapterType) { +export function unreadFilter(unread: NullAndUndefined, { isRead: isChapterRead }: TChapter) { switch (unread) { case true: return !isChapterRead; @@ -57,7 +57,7 @@ export function unreadFilter(unread: NullAndUndefined, { isRead: isChap } } -function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: chapterDownload }: ChapterType) { +function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: chapterDownload }: TChapter) { switch (downloaded) { case true: return chapterDownload; @@ -68,7 +68,7 @@ function downloadFilter(downloaded: NullAndUndefined, { isDownloaded: c } } -function bookmarkedFilter(bookmarked: NullAndUndefined, { isBookmarked: chapterBookmarked }: ChapterType) { +function bookmarkedFilter(bookmarked: NullAndUndefined, { isBookmarked: chapterBookmarked }: TChapter) { switch (bookmarked) { case true: return chapterBookmarked; @@ -79,7 +79,7 @@ function bookmarkedFilter(bookmarked: NullAndUndefined, { isBookmarked: } } -export function filterAndSortChapters(chapters: ChapterType[], options: ChapterListOptions): ChapterType[] { +export function filterAndSortChapters(chapters: TChapter[], options: ChapterListOptions): TChapter[] { const filtered = options.active ? chapters.filter( (chp) => diff --git a/src/components/navbar/ReaderNavBar.tsx b/src/components/navbar/ReaderNavBar.tsx index 236e6474..3ad637d1 100644 --- a/src/components/navbar/ReaderNavBar.tsx +++ b/src/components/navbar/ReaderNavBar.tsx @@ -24,9 +24,8 @@ import ListItemText from '@mui/material/ListItemText'; import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; import Collapse from '@mui/material/Collapse'; import { useTranslation } from 'react-i18next'; -import { ChapterOffset, IReaderSettings } from '@/typings'; +import { ChapterOffset, IReaderSettings, TChapter, TManga } from '@/typings'; import ReaderSettingsOptions from '@/components/reader/ReaderSettingsOptions'; -import { ChapterType, MangaType } from '@/lib/graphql/generated/graphql.ts'; const Root = styled('div')(({ theme }) => ({ top: 0, @@ -115,8 +114,8 @@ const OpenDrawerButton = styled(IconButton)(({ theme }) => ({ interface IProps { settings: IReaderSettings; setSettingValue: (key: keyof IReaderSettings, value: string | boolean) => void; - manga: MangaType; - chapter: ChapterType; + manga: TManga; + chapter: TChapter; curPage: number; scrollToPage: (page: number) => void; openNextChapter: (offset: ChapterOffset, setHistory: (nextChapterIndex: number) => void) => Promise; diff --git a/src/components/source/SourceMangaGrid.tsx b/src/components/source/SourceMangaGrid.tsx index 9b6cb7a8..8fcb14ed 100644 --- a/src/components/source/SourceMangaGrid.tsx +++ b/src/components/source/SourceMangaGrid.tsx @@ -8,9 +8,9 @@ import { useTranslation } from 'react-i18next'; import MangaGrid, { IMangaGridProps } from '@/components/MangaGrid'; -import { MangaType } from '@/lib/graphql/generated/graphql.ts'; +import { TPartialManga } from '@/typings.ts'; -function filterManga(mangas: MangaType[]): MangaType[] { +function filterManga(mangas: TPartialManga[]): TPartialManga[] { return mangas; } diff --git a/src/screens/DownloadQueue.tsx b/src/screens/DownloadQueue.tsx index a913c58b..325ecc14 100644 --- a/src/screens/DownloadQueue.tsx +++ b/src/screens/DownloadQueue.tsx @@ -25,7 +25,8 @@ import { NavbarToolbar } from '@/components/navbar/DefaultNavBar'; import DownloadStateIndicator from '@/components/molecules/DownloadStateIndicator'; import EmptyView from '@/components/util/EmptyView'; import NavbarContext from '@/components/context/NavbarContext'; -import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; +import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; +import { TChapter } from '@/typings.ts'; const DownloadQueue: React.FC = () => { const { t } = useTranslation(); @@ -55,7 +56,7 @@ const DownloadQueue: React.FC = () => { return ; } - const handleDelete = async (chapter: ChapterType) => { + const handleDelete = async (chapter: TChapter) => { const isRunning = status === 'STARTED'; try { diff --git a/src/screens/Extensions.tsx b/src/screens/Extensions.tsx index 805a60f2..aeb5172e 100644 --- a/src/screens/Extensions.tsx +++ b/src/screens/Extensions.tsx @@ -30,12 +30,12 @@ import { makeToaster } from '@/components/util/Toast'; import LangSelect from '@/components/navbar/action/LangSelect'; import NavbarContext from '@/components/context/NavbarContext'; import ExtensionCard from '@/components/ExtensionCard'; -import { Extension } from '@/typings.ts'; +import { PartialExtension } from '@/typings.ts'; const LANGUAGE = 0; const EXTENSIONS = 1; -function getExtensionsInfo(extensions: Extension[]): { +function getExtensionsInfo(extensions: PartialExtension[]): { allLangs: string[]; groupedExtensions: GroupedExtensionsResult; } { @@ -132,7 +132,7 @@ export default function MangaExtensions() { [shownLangs, groupedExtensions], ); - const flatRenderItems: (Extension | string)[] = filteredGroupedExtensions.flat(2); + const flatRenderItems: (PartialExtension | string)[] = filteredGroupedExtensions.flat(2); const [toasts, makeToast] = makeToaster(useState([])); @@ -229,7 +229,7 @@ export default function MangaExtensions() { ); } - const item = flatRenderItems[index] as Extension; + const item = flatRenderItems[index] as PartialExtension; return ; }} diff --git a/src/screens/Library.tsx b/src/screens/Library.tsx index 4b708fb2..20150cca 100644 --- a/src/screens/Library.tsx +++ b/src/screens/Library.tsx @@ -7,7 +7,7 @@ */ import { Chip, Tab, Tabs, styled, Box } from '@mui/material'; -import React, { useContext, useEffect, useMemo, useRef } from 'react'; +import React, { useContext, useEffect, useMemo } from 'react'; import { useQueryParam, NumberParam } from 'use-query-params'; import { useTranslation } from 'react-i18next'; import requestManager from '@/lib/requests/RequestManager.ts'; @@ -20,7 +20,6 @@ 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 @@ -85,7 +84,7 @@ export default function Library() { error: mangaError, loading: mangaLoading, } = requestManager.useGetCategoryMangas(activeTab?.id, { skip: !activeTab, nextFetchPolicy: 'cache-only' }); - const mangas = (categoryMangaResponse?.category.mangas.nodes as unknown as MangaType[]) ?? []; + const mangas = categoryMangaResponse?.category.mangas.nodes ?? []; const { setTitle, setAction } = useContext(NavbarContext); useEffect(() => { diff --git a/src/screens/Manga.tsx b/src/screens/Manga.tsx index 48958c04..7768d0a1 100644 --- a/src/screens/Manga.tsx +++ b/src/screens/Manga.tsx @@ -20,7 +20,6 @@ import MangaDetails from '@/components/manga/MangaDetails'; import MangaToolbarMenu from '@/components/manga/MangaToolbarMenu'; import EmptyView from '@/components/util/EmptyView'; import LoadingPlaceholder from '@/components/util/LoadingPlaceholder'; -import { MangaType } from '@/lib/graphql/generated/graphql.ts'; const AUTOFETCH_AGE = 1000 * 60 * 60 * 24; // 24 hours @@ -33,7 +32,7 @@ const Manga: React.FC = () => { const { data, error, loading: isLoading, networkStatus, refetch } = requestManager.useGetManga(id); const isValidating = isNetworkRequestInFlight(networkStatus); - const manga = data?.manga as MangaType | undefined; + const manga = data?.manga; const [refresh, { loading: refreshing }] = useRefreshManga(id); useSetDefaultBackTo('library'); diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index 5fc012bf..e6baa514 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -11,7 +11,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'r import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { ChapterOffset, IReaderSettings, ReaderType, TranslationKey } from '@/typings'; +import { ChapterOffset, IReaderSettings, ReaderType, TChapter, TManga, TranslationKey } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; import { checkAndHandleMissingStoredReaderSettings, @@ -27,9 +27,8 @@ import VerticalPager from '@/components/reader/pager/VerticalPager'; import ReaderNavBar from '@/components/navbar/ReaderNavBar'; import NavbarContext from '@/components/context/NavbarContext'; import makeToast from '@/components/util/Toast'; -import { ChapterType, MangaType } from '@/lib/graphql/generated/graphql.ts'; -const isDupChapter = async (chapterIndex: number, currentChapter: ChapterType) => { +const isDupChapter = async (chapterIndex: number, currentChapter: TChapter) => { const nextChapter = await requestManager.getChapter(currentChapter.manga.id, chapterIndex).response; return nextChapter.data.chapter.chapterNumber === currentChapter.chapterNumber; @@ -42,7 +41,7 @@ const isDupChapter = async (chapterIndex: number, currentChapter: ChapterType) = */ const getOffsetChapter = async ( chapterIndex: number, - currentChapter: ChapterType, + currentChapter: TChapter, skipDupChapters: boolean, offset: ChapterOffset, ): Promise => { @@ -86,7 +85,7 @@ const initialChapter = { chapterCount: 0, lastPageRead: 0, name: 'Loading...', -} as unknown as ChapterType; +} as unknown as TChapter; export default function Reader() { const { t } = useTranslation(); @@ -105,17 +104,17 @@ export default function Reader() { inLibraryAt: 0, lastReadAt: 0, chapters: { totalCount: 0 }, - }) as unknown as MangaType, + }) as unknown as TManga, [mangaId], ); const { data, loading: isMangaLoading } = requestManager.useGetManga(mangaId); - const loadedChapter = useRef(null); + const loadedChapter = useRef(null); const isChapterLoaded = Number(mangaId) === loadedChapter.current?.manga.id && Number(chapterIndex) === loadedChapter.current?.sourceOrder && loadedChapter.current?.pageCount !== -1; - const manga = (data?.manga as MangaType) ?? initialManga; + const manga = data?.manga ?? initialManga; const { data: chapterData, loading: isChapterLoading } = requestManager.useGetMangaChapter(mangaId, chapterIndex, { skip: isChapterLoaded, }); @@ -129,7 +128,7 @@ export default function Reader() { } if (chapterData?.chapter) { - return chapterData.chapter as ChapterType; + return chapterData.chapter; } return null; diff --git a/src/screens/SearchAll.tsx b/src/screens/SearchAll.tsx index 2afc5336..903956d3 100644 --- a/src/screens/SearchAll.tsx +++ b/src/screens/SearchAll.tsx @@ -21,7 +21,6 @@ import LangSelect from '@/components/navbar/action/LangSelect'; import MangaGrid from '@/components/MangaGrid'; import NavbarContext from '@/components/context/NavbarContext'; import { useDebounce } from '@/components/manga/hooks'; -import { MangaType } from '@/lib/graphql/generated/graphql.ts'; type SourceLoadingState = { isLoading: boolean; hasResults: boolean; emptySearch: boolean }; type SourceToLoadingStateMap = Map; @@ -103,7 +102,7 @@ const SourceSearchPreview = React.memo( skipRequest: !searchString, }); const { data: searchResult, isLoading, error, abortRequest } = results[0]!; - const mangas = (searchResult?.fetchSourceManga.mangas as MangaType[]) ?? []; + const mangas = searchResult?.fetchSourceManga.mangas ?? []; const noMangasFound = !isLoading && !mangas.length; useEffect(() => { diff --git a/src/screens/SourceMangas.tsx b/src/screens/SourceMangas.tsx index 3db87ace..442a6012 100644 --- a/src/screens/SourceMangas.tsx +++ b/src/screens/SourceMangas.tsx @@ -17,7 +17,7 @@ import { Box, Button, styled, useTheme, useMediaQuery } from '@mui/material'; import FavoriteIcon from '@mui/icons-material/Favorite'; import NewReleasesIcon from '@mui/icons-material/NewReleases'; import FilterListIcon from '@mui/icons-material/FilterList'; -import { TranslationKey } from '@/typings'; +import { TPartialManga, TranslationKey } from '@/typings'; import requestManager, { AbortableApolloUseMutationPaginatedResponse } from '@/lib/requests/RequestManager.ts'; import { useDebounce } from '@/components/manga/hooks'; import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext'; @@ -29,7 +29,6 @@ import SourceMangaGrid from '@/components/source/SourceMangaGrid'; import { GetSourceMangasFetchMutation, GetSourceMangasFetchMutationVariables, - MangaType, } from '@/lib/graphql/generated/graphql.ts'; const ContentTypeMenu = styled('div')(({ theme }) => ({ @@ -87,8 +86,8 @@ const SOURCE_CONTENT_TYPE_TO_ERROR_MSG_KEY: { [contentType in SourceContentType] [SourceContentType.SEARCH]: 'manga.error.label.no_mangas_found', }; -const getUniqueMangas = (mangas: MangaType[]): MangaType[] => { - const uniqueMangas: MangaType[] = []; +const getUniqueMangas = (mangas: TPartialManga[]): TPartialManga[] => { + const uniqueMangas: TPartialManga[] = []; mangas.forEach((manga) => { const isDuplicate = uniqueMangas.some((uniqueManga) => uniqueManga.id === manga.id); @@ -166,7 +165,7 @@ const useSourceManga = ( .map((page) => page.data?.fetchSourceManga.mangas ?? []) .reduce((prevList, list) => [...prevList, ...list], []), [pages], - ) as MangaType[]; + ); const uniqueItems = useMemo(() => getUniqueMangas(items), [items]); if (!uniqueItems.length) { @@ -224,7 +223,7 @@ export default function SourceMangas() { filtersToApply, isLargeScreen ? 2 : 1, ); - const mangas = (data?.fetchSourceManga.mangas as MangaType[]) ?? []; + const mangas = data?.fetchSourceManga.mangas ?? []; const hasNextPage = data?.fetchSourceManga.hasNextPage ?? false; const { data: sourceData } = requestManager.useGetSource(sourceId); diff --git a/src/screens/Updates.tsx b/src/screens/Updates.tsx index 12c98818..2776e252 100644 --- a/src/screens/Updates.tsx +++ b/src/screens/Updates.tsx @@ -23,7 +23,8 @@ import LoadingPlaceholder from '@/components/util/LoadingPlaceholder'; import EmptyView from '@/components/util/EmptyView'; import DownloadStateIndicator from '@/components/molecules/DownloadStateIndicator'; import NavbarContext from '@/components/context/NavbarContext'; -import { ChapterType, DownloadType } from '@/lib/graphql/generated/graphql.ts'; +import { DownloadType } from '@/lib/graphql/generated/graphql.ts'; +import { TChapter } from '@/typings.ts'; const StyledGroupedVirtuoso = styled(GroupedVirtuoso)(({ theme }) => ({ // 64px header @@ -81,7 +82,7 @@ function getDateString(date: Date) { return date.toLocaleDateString(); } -const groupByDate = (updates: ChapterType[]): [date: string, items: number][] => { +const groupByDate = (updates: TChapter[]): [date: string, items: number][] => { if (!updates.length) { return []; } @@ -111,7 +112,7 @@ const Updates: React.FC = () => { }); const hasNextPage = !!chapterUpdateData?.chapters.pageInfo.hasNextPage; const endCursor = chapterUpdateData?.chapters.pageInfo.endCursor; - const updateEntries = (chapterUpdateData?.chapters.nodes as ChapterType[]) ?? []; + const updateEntries = chapterUpdateData?.chapters.nodes ?? []; const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]); const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]); const { data: downloaderData } = requestManager.useDownloadSubscription(); @@ -123,7 +124,7 @@ const Updates: React.FC = () => { setAction(null); }, [t]); - const downloadForChapter = (chapter: ChapterType) => { + const downloadForChapter = (chapter: TChapter) => { const { sourceOrder, manga: { id: mangaId }, @@ -131,7 +132,7 @@ const Updates: React.FC = () => { return queue.find((q) => sourceOrder === q.chapter.sourceOrder && mangaId === q.chapter.manga.id); }; - const downloadChapter = (chapter: ChapterType) => { + const downloadChapter = (chapter: TChapter) => { requestManager.addChapterToDownloadQueue(chapter.id); }; diff --git a/src/screens/settings/Categories.tsx b/src/screens/settings/Categories.tsx index 001e4691..212d54aa 100644 --- a/src/screens/settings/Categories.tsx +++ b/src/screens/settings/Categories.tsx @@ -28,7 +28,7 @@ import requestManager from '@/lib/requests/RequestManager.ts'; import StrictModeDroppable from '@/lib/StrictModeDroppable'; import { DEFAULT_FULL_FAB_HEIGHT } from '@/components/util/StyledFab'; import NavbarContext, { useSetDefaultBackTo } from '@/components/context/NavbarContext'; -import { CategoryType } from '@/lib/graphql/generated/graphql.ts'; +import { TCategory } from '@/typings.ts'; const getItemStyle = ( isDragging: boolean, @@ -58,7 +58,7 @@ export default function Categories() { if (res.length > 0 && res[0].name === 'Default') { res.shift(); } - return res as CategoryType[]; + return res; }, [data]); const [categoryToEdit, setCategoryToEdit] = useState(-1); // -1 means new category @@ -69,7 +69,7 @@ export default function Categories() { useSetDefaultBackTo('settings'); - const categoryReorder = (list: CategoryType[], from: number, to: number) => { + const categoryReorder = (list: TCategory[], from: number, to: number) => { const reorderedCategory = list[from]; const newData = [...list]; const [removed] = newData.splice(from, 1); diff --git a/src/screens/settings/LibrarySettings.tsx b/src/screens/settings/LibrarySettings.tsx index fa601885..99d0ebcb 100644 --- a/src/screens/settings/LibrarySettings.tsx +++ b/src/screens/settings/LibrarySettings.tsx @@ -25,7 +25,8 @@ import makeToast from '@/components/util/Toast'; import ThreeStateCheckboxInput from '@/components/atoms/ThreeStateCheckboxInput'; import NavbarContext, { useSetDefaultBackTo } from '@/components/context/NavbarContext'; import SearchSettings from '@/screens/settings/SearchSettings'; -import { CategoryType, IncludeInUpdate } from '@/lib/graphql/generated/graphql.ts'; +import { IncludeInUpdate } from '@/lib/graphql/generated/graphql.ts'; +import { TCategory } from '@/typings.ts'; const CategoriesDiv = styled('div')({ display: 'flex', @@ -62,7 +63,7 @@ const includeInUpdateStatusToBoolean = (status: IncludeInUpdate): boolean | null }; const getCategoryUpdateInfo = ( - categories: CategoryType[], + categories: TCategory[], areIncluded: boolean, unsetCategories: number, allCategories: number, @@ -100,19 +101,19 @@ export default function LibrarySettings() { useSetDefaultBackTo('settings'); const { data, error: requestError } = requestManager.useGetCategories(); - const categories = (data?.categories.nodes ?? []) as CategoryType[]; - const [dialogCategories, setDialogCategories] = useState(categories); + const categories = data?.categories.nodes ?? []; + const [dialogCategories, setDialogCategories] = useState(categories); const [isDialogOpen, setIsDialogOpen] = useState(false); useEffect(() => { setDialogCategories(categories); }, [categories]); - const unsetCategories: CategoryType[] = + const unsetCategories: TCategory[] = categories?.filter((category) => category.includeInUpdate === IncludeInUpdate.Unset) ?? []; - const excludedCategories: CategoryType[] = + const excludedCategories: TCategory[] = categories?.filter((category) => category.includeInUpdate === IncludeInUpdate.Exclude) ?? []; - const includedCategories: CategoryType[] = + const includedCategories: TCategory[] = categories?.filter((category) => category.includeInUpdate === IncludeInUpdate.Include) ?? []; const excludedCategoriesText = getCategoryUpdateInfo( excludedCategories, @@ -129,7 +130,7 @@ export default function LibrarySettings() { requestError, ); - const updateCategory = (category: CategoryType) => + const updateCategory = (category: TCategory) => requestManager.updateCategory(category.id, { includeInUpdate: category.includeInUpdate }).response; const updateCategories = async () => { @@ -218,7 +219,7 @@ export default function LibrarySettings() { const categoryIndex = dialogCategories.findIndex( (category_) => category_ === category, ); - const updatedDialogCategories: CategoryType[] = [ + const updatedDialogCategories: TCategory[] = [ ...dialogCategories.slice(0, categoryIndex), { ...category, diff --git a/src/screens/util/Extensions.ts b/src/screens/util/Extensions.ts index 09e5d4de..5d83890c 100644 --- a/src/screens/util/Extensions.ts +++ b/src/screens/util/Extensions.ts @@ -7,7 +7,7 @@ */ import { t } from 'i18next'; -import { Extension, TranslationKey } from '@/typings'; +import { PartialExtension, TranslationKey } from '@/typings'; import { DefaultLanguage, langCodeToName } from '@/util/language'; export enum ExtensionState { @@ -16,16 +16,16 @@ export enum ExtensionState { OBSOLETE = 'OBSOLETE', } -export type GroupedExtensionsResult = [KEY, Extension[]][]; +export type GroupedExtensionsResult = [KEY, PartialExtension[]][]; export type GroupedByExtensionState = { - [state in ExtensionState]: Extension[]; + [state in ExtensionState]: PartialExtension[]; }; export type GroupedByLanguage = { - [language in DefaultLanguage]: Extension[]; + [language in DefaultLanguage]: PartialExtension[]; } & { - [language: string]: Extension[]; + [language: string]: PartialExtension[]; }; export type GroupedExtensions = GroupedByExtensionState & GroupedByLanguage; diff --git a/src/typings.ts b/src/typings.ts index 9bae87a2..589a4b50 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -11,10 +11,11 @@ import { SvgIconTypeMap } from '@mui/material/SvgIcon/SvgIcon'; import { ParseKeys } from 'i18next'; import { Location } from 'react-router-dom'; import { - ChapterType, - ExtensionType, + GetCategoryQuery, + GetChapterQuery, + GetExtensionQuery, + GetMangaQuery, GetSourceQuery, - MangaType, MetaType, SourcePreferenceChangeInput, } from '@/lib/graphql/generated/graphql.ts'; @@ -33,6 +34,8 @@ export type RecursivePartial = { : T[P]; }; +export type OptionalProperty = Omit & Partial>; + type GenericLocation = Omit & { state?: State }; declare module 'react-router-dom' { @@ -43,7 +46,7 @@ declare module 'react-router-dom' { export type TranslationKey = ParseKeys; -export type Extension = Omit; +export type PartialExtension = GetExtensionQuery['extension']; export interface ISource { id: string; @@ -105,6 +108,10 @@ export interface IMangaCard { lastReadAt: number; } +export type TManga = GetMangaQuery['manga']; + +export type TPartialManga = OptionalProperty; + export interface IManga { id: number; sourceId: string; @@ -166,12 +173,16 @@ export interface IMangaChapter { chapter: IChapter; } +export type TChapter = GetChapterQuery['chapter']; + export enum IncludeInGlobalUpdate { EXCLUDE = 0, INCLUDE = 1, UNSET = -1, } +export type TCategory = GetCategoryQuery['category']; + export interface ICategory { id: number; order: number; @@ -230,8 +241,8 @@ export interface IReaderProps { curPage: number; initialPage: number; settings: IReaderSettings; - manga: MangaType; - chapter: ChapterType; + manga: TManga; + chapter: TChapter; nextChapter: () => void; prevChapter: () => void; } diff --git a/src/util/metadata.ts b/src/util/metadata.ts index 8ef3464d..d16bd040 100644 --- a/src/util/metadata.ts +++ b/src/util/metadata.ts @@ -14,9 +14,12 @@ import { Metadata, MetadataHolder, MetadataKeyValuePair, + TCategory, + TChapter, + TManga, } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; -import { CategoryType, ChapterType, MangaType, MetaType } from '@/lib/graphql/generated/graphql.ts'; +import { MetaType } from '@/lib/graphql/generated/graphql.ts'; const APP_METADATA_KEY_PREFIX = 'webUI_'; @@ -322,16 +325,16 @@ export const requestUpdateMetadataValue = async ( switch (holderType) { case 'category': - await requestManager.setCategoryMeta((metadataHolder as CategoryType).id, metadataKey, value).response; + await requestManager.setCategoryMeta((metadataHolder as TCategory).id, metadataKey, value).response; break; case 'chapter': - await requestManager.setChapterMeta((metadataHolder as ChapterType).id, metadataKey, value).response; + await requestManager.setChapterMeta((metadataHolder as TChapter).id, metadataKey, value).response; break; case 'global': await requestManager.setGlobalMetadata(metadataKey, value).response; break; case 'manga': - await requestManager.setMangaMeta((metadataHolder as MangaType).id, metadataKey, value).response; + await requestManager.setMangaMeta((metadataHolder as TManga).id, metadataKey, value).response; break; default: throw new Error(`requestUpdateMetadataValue: unknown holderType "${holderType}"`); @@ -351,16 +354,16 @@ export const requestUpdateServerMetadata = async ( ): Promise => requestUpdateMetadata({ meta: serverMetadata }, 'global', keysToValues); export const requestUpdateMangaMetadata = async ( - manga: MangaType, + manga: TManga, keysToValues: MetadataKeyValuePair[], ): Promise => requestUpdateMetadata(manga, 'manga', keysToValues); export const requestUpdateChapterMetadata = async ( - chapter: ChapterType, + chapter: TChapter, keysToValues: MetadataKeyValuePair[], ): Promise => requestUpdateMetadata(chapter, 'chapter', keysToValues); export const requestUpdateCategoryMetadata = async ( - category: CategoryType, + category: TCategory, keysToValues: MetadataKeyValuePair[], ): Promise => requestUpdateMetadata(category, 'category', keysToValues); diff --git a/src/util/readerSettings.ts b/src/util/readerSettings.ts index d3f05cc8..bfd61325 100644 --- a/src/util/readerSettings.ts +++ b/src/util/readerSettings.ts @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { Metadata, IReaderSettings, MetadataKeyValuePair, GqlMetaHolder } from '@/typings'; +import { Metadata, IReaderSettings, MetadataKeyValuePair, GqlMetaHolder, TManga } from '@/typings'; import requestManager from '@/lib/requests/RequestManager.ts'; import { convertFromGqlMeta, @@ -14,7 +14,7 @@ import { requestUpdateMangaMetadata, requestUpdateServerMetadata, } from '@/util/metadata'; -import { MangaType, MetaType } from '@/lib/graphql/generated/graphql.ts'; +import { MetaType } from '@/lib/graphql/generated/graphql.ts'; type UndefinedReaderSettings = { [setting in keyof IReaderSettings]: IReaderSettings[setting] | undefined; @@ -103,7 +103,7 @@ export const checkAndHandleMissingStoredReaderSettings = async ( } if (metadataHolderType === 'manga') { - await requestUpdateMangaMetadata(metadataHolder as MangaType, settingsToUpdate); + await requestUpdateMangaMetadata(metadataHolder as TManga, settingsToUpdate); return; }