feat: Add AniList ID support for manga imports, introduce manga data update API, and include chapter language.

This commit is contained in:
Rodrigo Verdiani 2025-12-31 20:01:42 -03:00
parent 8b27e56758
commit 9f0eeab4fb
3 changed files with 123 additions and 25 deletions

View File

@ -12,6 +12,7 @@ export interface DefaultResponseDTOVoid {
export interface ImportRequestDTO {
metadataId?: string;
aniListId?: string;
id: string;
}
@ -114,18 +115,18 @@ export interface PageMangaListDTO {
}
export interface PageableObject {
paged?: boolean;
pageNumber?: number;
pageSize?: number;
paged?: boolean;
unpaged?: boolean;
offset?: number;
sort?: SortObject;
unpaged?: boolean;
}
export interface SortObject {
sorted?: boolean;
empty?: boolean;
unsorted?: boolean;
empty?: boolean;
}
export interface DefaultResponseDTOListMangaChapterDTO {
@ -134,12 +135,21 @@ export interface DefaultResponseDTOListMangaChapterDTO {
message?: string;
}
export interface LanguageDTO {
id: number;
/** @minLength 1 */
code: string;
/** @minLength 1 */
name: string;
}
export interface MangaChapterDTO {
id: number;
/** @minLength 1 */
title: string;
downloaded: boolean;
isRead: boolean;
language?: LanguageDTO;
}
export interface DefaultResponseDTOMangaDTO {

View File

@ -217,6 +217,69 @@ export const useUpdateMangaList = <TError = unknown,
return useMutation(mutationOptions, queryClient);
}
/**
* Triggers the update of the metadata for a manga by its ID
* @summary Trigger manga data update
*/
export const triggerUpdateMangaData = (
mangaId: number,
options?: SecondParameter<typeof customInstance>,signal?: AbortSignal
) => {
return customInstance<DefaultResponseDTOVoid>(
{url: `/management/update-manga-data/${encodeURIComponent(String(mangaId))}`, method: 'POST', signal
},
options);
}
export const getTriggerUpdateMangaDataMutationOptions = <TError = unknown,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof triggerUpdateMangaData>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
): UseMutationOptions<Awaited<ReturnType<typeof triggerUpdateMangaData>>, TError,{mangaId: number}, TContext> => {
const mutationKey = ['triggerUpdateMangaData'];
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 triggerUpdateMangaData>>, {mangaId: number}> = (props) => {
const {mangaId} = props ?? {};
return triggerUpdateMangaData(mangaId,requestOptions)
}
return { mutationFn, ...mutationOptions }}
export type TriggerUpdateMangaDataMutationResult = NonNullable<Awaited<ReturnType<typeof triggerUpdateMangaData>>>
export type TriggerUpdateMangaDataMutationError = unknown
/**
* @summary Trigger manga data update
*/
export const useTriggerUpdateMangaData = <TError = unknown,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof triggerUpdateMangaData>>, TError,{mangaId: number}, TContext>, request?: SecondParameter<typeof customInstance>}
, queryClient?: QueryClient): UseMutationResult<
Awaited<ReturnType<typeof triggerUpdateMangaData>>,
TError,
{mangaId: number},
TContext
> => {
const mutationOptions = getTriggerUpdateMangaDataMutationOptions(options);
return useMutation(mutationOptions, queryClient);
}
/**
* Sends a test notification to all users
* @summary Test notification
*/

View File

@ -45,6 +45,7 @@ export const ProviderImportDialog = ({
value: z.string().min(1, "Please enter an ID or URL."),
providerId: z.string(),
myAnimeListId: z.string().optional(),
anilistId: z.string().optional(),
});
const form = useForm<z.infer<typeof formSchema>>({
@ -53,6 +54,7 @@ export const ProviderImportDialog = ({
value: "",
providerId: "",
myAnimeListId: undefined,
anilistId: undefined,
},
});
@ -74,7 +76,11 @@ export const ProviderImportDialog = ({
(data: z.output<typeof formSchema>) => {
importFromProvider({
providerId: Number(data.providerId),
data: { id: data.value, metadataId: data.myAnimeListId },
data: {
id: data.value,
metadataId: data.myAnimeListId,
aniListId: data.anilistId,
},
});
},
[importFromProvider],
@ -145,27 +151,46 @@ export const ProviderImportDialog = ({
</FormItem>
)}
/>
<FormField
control={form.control}
name="myAnimeListId"
render={({ field }) => (
<FormItem>
<FormLabel>MyAnimeList ID (Optional)</FormLabel>
<FormControl>
<Input
placeholder="e.g., 13 (for One Piece)"
disabled={isPendingImportFromProvider}
{...field}
/>
</FormControl>
<FormDescription>
Optionally link this manga to a MyAnimeList entry for better
precision on metadata fetching.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex w-full gap-4">
<FormField
control={form.control}
name="myAnimeListId"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>MyAnimeList ID (Optional)</FormLabel>
<FormControl>
<Input
placeholder="e.g., 13 (for One Piece)"
disabled={isPendingImportFromProvider}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="anilistId"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>AniList ID (Optional)</FormLabel>
<FormControl>
<Input
placeholder="e.g., 21 (for One Piece)"
disabled={isPendingImportFromProvider}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormDescription>
Optionally link this manga to a MyAnimeList or AniList entry for
better precision on metadata fetching.
</FormDescription>
</form>
</Form>
<DialogFooter>