feat: add functionality to fetch all chapters from provider and update related hooks
This commit is contained in:
parent
6c8ed19be4
commit
f872d96b80
@ -24,6 +24,10 @@ import type {
|
|||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
|
|
||||||
import { customInstance } from './api';
|
import { customInstance } from './api';
|
||||||
|
export interface UpdateMangaDataCommand {
|
||||||
|
mangaId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DefaultResponseDTOVoid {
|
export interface DefaultResponseDTOVoid {
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
data?: unknown;
|
data?: unknown;
|
||||||
@ -105,18 +109,18 @@ export interface PageMangaListDTO {
|
|||||||
content?: MangaListDTO[];
|
content?: MangaListDTO[];
|
||||||
number?: number;
|
number?: number;
|
||||||
pageable?: PageableObject;
|
pageable?: PageableObject;
|
||||||
|
sort?: SortObject;
|
||||||
first?: boolean;
|
first?: boolean;
|
||||||
last?: boolean;
|
last?: boolean;
|
||||||
sort?: SortObject;
|
|
||||||
numberOfElements?: number;
|
numberOfElements?: number;
|
||||||
empty?: boolean;
|
empty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageableObject {
|
export interface PageableObject {
|
||||||
offset?: number;
|
offset?: number;
|
||||||
paged?: boolean;
|
|
||||||
pageNumber?: number;
|
pageNumber?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
paged?: boolean;
|
||||||
sort?: SortObject;
|
sort?: SortObject;
|
||||||
unpaged?: boolean;
|
unpaged?: boolean;
|
||||||
}
|
}
|
||||||
@ -268,6 +272,65 @@ type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const sendRecord = (
|
||||||
|
updateMangaDataCommand: UpdateMangaDataCommand,
|
||||||
|
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
|
||||||
|
|
||||||
|
return customInstance<string>(
|
||||||
|
{url: `http://mangamochi.badger-pirarucu.ts.net:8080/records`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: updateMangaDataCommand, signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getSendRecordMutationOptions = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
): UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext> => {
|
||||||
|
|
||||||
|
const mutationKey = ['sendRecord'];
|
||||||
|
const {mutation: mutationOptions, request: requestOptions} = options ?
|
||||||
|
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
||||||
|
options
|
||||||
|
: {...options, mutation: {...options.mutation, mutationKey}}
|
||||||
|
: {mutation: { mutationKey, }, request: undefined};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const mutationFn: MutationFunction<Awaited<ReturnType<typeof sendRecord>>, {data: UpdateMangaDataCommand}> = (props) => {
|
||||||
|
const {data} = props ?? {};
|
||||||
|
|
||||||
|
return sendRecord(data,requestOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
|
export type SendRecordMutationResult = NonNullable<Awaited<ReturnType<typeof sendRecord>>>
|
||||||
|
export type SendRecordMutationBody = UpdateMangaDataCommand
|
||||||
|
export type SendRecordMutationError = unknown
|
||||||
|
|
||||||
|
export const useSendRecord = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof sendRecord>>, TError,{data: UpdateMangaDataCommand}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
, queryClient?: QueryClient): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof sendRecord>>,
|
||||||
|
TError,
|
||||||
|
{data: UpdateMangaDataCommand},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
|
||||||
|
const mutationOptions = getSendRecordMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions, queryClient);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch a list of manga chapters for a specific manga/provider combination.
|
* Fetch a list of manga chapters for a specific manga/provider combination.
|
||||||
* @summary Fetch the available chapters for a specific manga/provider combination
|
* @summary Fetch the available chapters for a specific manga/provider combination
|
||||||
@ -332,6 +395,70 @@ export const useFetchMangaChapters = <TError = unknown,
|
|||||||
return useMutation(mutationOptions, queryClient);
|
return useMutation(mutationOptions, queryClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all not yet downloaded chapters from the provider
|
||||||
|
* @summary Fetch all chapters
|
||||||
|
*/
|
||||||
|
export const fetchAllChapters = (
|
||||||
|
mangaProviderId: number,
|
||||||
|
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
|
||||||
|
|
||||||
|
return customInstance<DefaultResponseDTOVoid>(
|
||||||
|
{url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(mangaProviderId))}/fetch-all-chapters`, method: 'POST', signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getFetchAllChaptersMutationOptions = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof fetchAllChapters>>, TError,{mangaProviderId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
): UseMutationOptions<Awaited<ReturnType<typeof fetchAllChapters>>, TError,{mangaProviderId: number}, TContext> => {
|
||||||
|
|
||||||
|
const mutationKey = ['fetchAllChapters'];
|
||||||
|
const {mutation: mutationOptions, request: requestOptions} = options ?
|
||||||
|
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
||||||
|
options
|
||||||
|
: {...options, mutation: {...options.mutation, mutationKey}}
|
||||||
|
: {mutation: { mutationKey, }, request: undefined};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const mutationFn: MutationFunction<Awaited<ReturnType<typeof fetchAllChapters>>, {mangaProviderId: number}> = (props) => {
|
||||||
|
const {mangaProviderId} = props ?? {};
|
||||||
|
|
||||||
|
return fetchAllChapters(mangaProviderId,requestOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
|
export type FetchAllChaptersMutationResult = NonNullable<Awaited<ReturnType<typeof fetchAllChapters>>>
|
||||||
|
|
||||||
|
export type FetchAllChaptersMutationError = unknown
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Fetch all chapters
|
||||||
|
*/
|
||||||
|
export const useFetchAllChapters = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof fetchAllChapters>>, TError,{mangaProviderId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
|
||||||
|
, queryClient?: QueryClient): UseMutationResult<
|
||||||
|
Awaited<ReturnType<typeof fetchAllChapters>>,
|
||||||
|
TError,
|
||||||
|
{mangaProviderId: number},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
|
||||||
|
const mutationOptions = getFetchAllChaptersMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions, queryClient);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a manga from favorites for the logged user.
|
* Remove a manga from favorites for the logged user.
|
||||||
* @summary Unfavorite a manga
|
* @summary Unfavorite a manga
|
||||||
|
|||||||
@ -20,9 +20,10 @@ import {
|
|||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import { ThemeToggle } from "@/components/theme-toggle";
|
import { ThemeToggle } from "@/components/theme-toggle";
|
||||||
import { useFetchMangaChapters, useGetManga } from "@/api/mangamochi";
|
import {useFetchAllChapters, useFetchMangaChapters, useGetManga} from "@/api/mangamochi";
|
||||||
import { MangaChapter } from "@/components/manga-chapter";
|
import { MangaChapter } from "@/components/manga-chapter";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import {toast} from "sonner";
|
||||||
|
|
||||||
export default function MangaDetailPage() {
|
export default function MangaDetailPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -33,12 +34,20 @@ export default function MangaDetailPage() {
|
|||||||
|
|
||||||
const { data: mangaData, queryKey } = useGetManga(mangaId);
|
const { data: mangaData, queryKey } = useGetManga(mangaId);
|
||||||
|
|
||||||
const { mutate, isPending } = useFetchMangaChapters({
|
const { mutate, isPending: fetchPending } = useFetchMangaChapters({
|
||||||
mutation: {
|
mutation: {
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { mutate: fetchAllMutate, isPending: fetchAllPending } = useFetchAllChapters({
|
||||||
|
mutation: {
|
||||||
|
onSuccess: () => toast.success("Chapter import queued successfully.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const isPending = fetchPending || fetchAllPending;
|
||||||
|
|
||||||
const [openProviders, setOpenProviders] = useState<Set<number>>(new Set());
|
const [openProviders, setOpenProviders] = useState<Set<number>>(new Set());
|
||||||
|
|
||||||
if (!mangaData) {
|
if (!mangaData) {
|
||||||
@ -258,7 +267,19 @@ export default function MangaDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{provider.supportsChapterFetch && (
|
{provider.supportsChapterFetch && (
|
||||||
<div className={"pr-4"}>
|
<div className={"flex gap-4 pr-4"}>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={() =>
|
||||||
|
fetchAllMutate({ mangaProviderId: provider.id })
|
||||||
|
}
|
||||||
|
className="gap-2"
|
||||||
|
>
|
||||||
|
<Database className="h-4 w-4" />
|
||||||
|
Fetch all from Provider
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user