diff --git a/src/components/manga/hooks.ts b/src/components/manga/hooks.ts index eb1288b0..fe0edd79 100644 --- a/src/components/manga/hooks.ts +++ b/src/components/manga/hooks.ts @@ -8,27 +8,23 @@ import { useCallback, useEffect, useState } from 'react'; import { mutate } from 'swr'; -import requestManager from 'lib/RequestManager'; +import requestManager, { RequestManager } from 'lib/RequestManager'; -// eslint-disable-next-line import/prefer-default-export export const useRefreshManga = (mangaId: string) => { const [fetchingOnline, setFetchingOnline] = useState(false); const handleRefresh = useCallback(async () => { setFetchingOnline(true); await Promise.all([ - requestManager - .getClient() - .get(`/api/v1/manga/${mangaId}/?onlineFetch=true`) - .then((res) => mutate(`/api/v1/manga/${mangaId}`, res.data, { revalidate: false })), - requestManager - .getClient() - .get(`/api/v1/manga/${mangaId}/chapters?onlineFetch=true`) - .then((res) => - mutate(`/api/v1/manga/${mangaId}/chapters`, res.data, { - revalidate: false, - }), - ), + requestManager.getManga(mangaId, true).response.then((res) => { + console.log('manga', res); + mutate(`${RequestManager.API_VERSION}manga/${mangaId}`, res, { revalidate: false }); + }), + requestManager.getMangaChapters(mangaId, true).response.then((res) => + mutate(`${RequestManager.API_VERSION}manga/${mangaId}/chapters`, res, { + revalidate: false, + }), + ), ]).finally(() => setFetchingOnline(false)); }, [mangaId]); diff --git a/src/lib/RequestManager.ts b/src/lib/RequestManager.ts index 8899cce4..e5b9fc2d 100644 --- a/src/lib/RequestManager.ts +++ b/src/lib/RequestManager.ts @@ -69,7 +69,7 @@ export type AbortableSWRInfiniteResponse = SWRInfiniteR // - POST /api/v1/backup/validate - validate backup # "validate backup file" endpoint used instead // - GET /api/v1/backup/export - export backup # no function needed, url gets called via link triggering the download export class RequestManager { - private static readonly API_VERSION = '/api/v1/'; + public static readonly API_VERSION = '/api/v1/'; private readonly restClient: RestClient = new RestClient(); @@ -425,6 +425,11 @@ export class RequestManager { }); } + public getManga(mangaId: number | string, doOnlineFetch?: boolean): AbortableAxiosResponse { + const onlineFetch = doOnlineFetch ? '?onlineFetch=true' : ''; + return this.doRequest(HttpMethod.GET, `manga/${mangaId}${onlineFetch}`); + } + public useGetFullManga( mangaId: number | string, { doOnlineFetch, ...swrOptions }: SWROptions & RequestOption = {}, @@ -463,7 +468,7 @@ export class RequestManager { } public setMangaMeta(mangaId: number, key: string, value: any): AbortableAxiosResponse { - return this.doRequest(HttpMethod.POST, `manga/${mangaId}/meta`, { formData: { key, value } }); + return this.doRequest(HttpMethod.PATCH, `manga/${mangaId}/meta`, { formData: { key, value } }); } public useGetMangaChapters( @@ -476,6 +481,11 @@ export class RequestManager { }); } + public getMangaChapters(mangaId: number | string, doOnlineFetch?: boolean): AbortableAxiosResponse { + const onlineFetch = doOnlineFetch ? '?onlineFetch=true' : ''; + return this.doRequest(HttpMethod.GET, `manga/${mangaId}/chapters${onlineFetch}`); + } + public updateMangaChapters( mangaId: number | string, { @@ -506,6 +516,10 @@ export class RequestManager { }); } + public getChapter(mangaId: number | string, chapterIndex: number | string): AbortableAxiosResponse { + return this.doRequest(HttpMethod.GET, `manga/${mangaId}/chapter/${chapterIndex}`); + } + public deleteDownloadedChapter(mangaId: number | string, chapterIndex: number | string): AbortableAxiosResponse { return this.doRequest(HttpMethod.DELETE, `manga/${mangaId}/chapter/${chapterIndex}`); } @@ -570,7 +584,7 @@ export class RequestManager { } public setCategoryMeta(categoryId: number, key: string, value: any): AbortableAxiosResponse { - return this.doRequest(HttpMethod.PATCH, `category/${categoryId}`, { formData: { key, value } }); + return this.doRequest(HttpMethod.PATCH, `category/${categoryId}/meta`, { formData: { key, value } }); } public restoreBackupFile(file: File): AbortableAxiosResponse { diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index 5fbfb6ff..96a78359 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -29,11 +29,7 @@ import { useTranslation } from 'react-i18next'; import requestManager from 'lib/RequestManager'; const isDupChapter = async (chapterIndex: number, currentChapter: IChapter) => { - const nextChapter = ( - await requestManager - .getClient() - .get(`/api/v1/manga/${currentChapter.mangaId}/chapter/${chapterIndex}`) - ).data; + const nextChapter = await requestManager.getChapter(currentChapter.mangaId, chapterIndex).response; return nextChapter.chapterNumber === currentChapter.chapterNumber; }; diff --git a/src/util/metadata.ts b/src/util/metadata.ts index 93d9278f..bc795f22 100644 --- a/src/util/metadata.ts +++ b/src/util/metadata.ts @@ -14,12 +14,12 @@ import { IManga, IMangaCard, IMangaChapter, + IMetadataMigration, Metadata, MetadataHolder, - IMetadataMigration, MetadataKeyValuePair, } from 'typings'; -import requestManager from 'lib/RequestManager'; +import requestManager, { RequestManager } from 'lib/RequestManager'; const APP_METADATA_KEY_PREFIX = 'webUI_'; @@ -290,72 +290,75 @@ const wrapMetadataWithMetaKey = (wrap: boolean, metadata: Metadata): MetadataHol }; }; +type MetadataHolderType = 'manga' | 'chapter' | 'category' | 'global'; + export const requestUpdateMetadataValue = async ( - endpoint: string, metadataHolder: MetadataHolder, + holderType: MetadataHolderType, key: AppMetadataKeys, value: AllowedMetadataValueTypes, - endpointToMutate: string = endpoint, - wrapWithMetaKey: boolean = true, ): Promise => { - const restApiVersion = '/api/v1'; - const url = `${restApiVersion}${endpoint}/meta`; - const urlToMutate = `${restApiVersion}${endpointToMutate}`; - const metadataKey = getMetadataKey(key); - const valueAsString = `${value}`; - - const formData = new FormData(); - formData.append('key', metadataKey); - formData.append('value', valueAsString); - const mutatedMetadata = { ...metadataHolder.meta, - [metadataKey]: valueAsString, + [metadataKey]: `${value}`, }; - await requestManager.getClient().patch(url, formData); - await mutate( + let endpoint: string; + switch (holderType) { + case 'category': + endpoint = `category/${(metadataHolder as ICategory).id}/meta`; + await requestManager.setCategoryMeta((metadataHolder as ICategory).id, metadataKey, value).response; + break; + case 'chapter': + // eslint-disable-next-line no-case-declarations + const { manga, chapter } = metadataHolder as IMangaChapter; + endpoint = `manga/${manga.id}/chapter/${chapter.index}/meta`; + await requestManager.setChapterMeta(manga.id, chapter.index, metadataKey, value).response; + break; + case 'global': + endpoint = 'meta'; + await requestManager.setGlobalMetadata(metadataKey, value).response; + break; + case 'manga': + endpoint = `manga/${(metadataHolder as IManga).id}/meta`; + await requestManager.setMangaMeta((metadataHolder as IManga).id, metadataKey, value).response; + break; + default: + throw new Error(`requestUpdateMetadataValue: unknown holderType "${holderType}"`); + } + + const urlToMutate = `${RequestManager.API_VERSION}${endpoint}`; + mutate( urlToMutate, - { ...metadataHolder, ...wrapMetadataWithMetaKey(wrapWithMetaKey, mutatedMetadata) }, + { ...metadataHolder, ...wrapMetadataWithMetaKey(holderType !== 'global', mutatedMetadata) }, { revalidate: false }, ); }; export const requestUpdateMetadata = async ( - endpoint: string, metadataHolder: MetadataHolder, + holderType: MetadataHolderType, keysToValues: [AppMetadataKeys, AllowedMetadataValueTypes][], - endpointToMutate?: string, - wrapWithMetaKey?: boolean, ): Promise => - Promise.all( - keysToValues.map(([key, value]) => - requestUpdateMetadataValue(endpoint, metadataHolder, key, value, endpointToMutate, wrapWithMetaKey), - ), - ); + Promise.all(keysToValues.map(([key, value]) => requestUpdateMetadataValue(metadataHolder, holderType, key, value))); export const requestUpdateServerMetadata = async ( serverMetadata: Metadata, keysToValues: MetadataKeyValuePair[], -): Promise => requestUpdateMetadata('', { meta: serverMetadata }, keysToValues, '/meta', false); +): Promise => requestUpdateMetadata({ meta: serverMetadata }, 'global', keysToValues); export const requestUpdateMangaMetadata = async ( manga: IMangaCard | IManga, keysToValues: MetadataKeyValuePair[], -): Promise => requestUpdateMetadata(`/manga/${manga.id}`, manga, keysToValues); +): Promise => requestUpdateMetadata(manga, 'manga', keysToValues); export const requestUpdateChapterMetadata = async ( mangaChapter: IMangaChapter, keysToValues: MetadataKeyValuePair[], -): Promise => - requestUpdateMetadata( - `/manga/${mangaChapter.manga.id}/chapter/${mangaChapter.chapter.index}`, - mangaChapter.chapter, - keysToValues, - ); +): Promise => requestUpdateMetadata(mangaChapter.chapter, 'chapter', keysToValues); export const requestUpdateCategoryMetadata = async ( category: ICategory, keysToValues: MetadataKeyValuePair[], -): Promise => requestUpdateMetadata(`/category/${category.id}`, category, keysToValues); +): Promise => requestUpdateMetadata(category, 'category', keysToValues);