import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AuthError } from '@/core/api/types'; import { memoirApi } from './api'; import { shouldShowChapterListLoadError } from './chapter-list-response'; import { toChapterViewModels } from './mappers'; import { memoirKeys } from './query-keys'; import type { ExportPdfRequest, UpdateBookRequest } from './types'; // ─── Book ─── export function useBook() { return useQuery({ queryKey: memoirKeys.book(), queryFn: () => memoirApi.fetchCurrentBook(), }); } export function useHasBook() { const { data } = useBook(); return data !== null && data !== undefined; } export function useUpdateBookTitle() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ bookId, body, }: { bookId: string; body: UpdateBookRequest; }) => memoirApi.updateBookTitle(bookId, body), onSuccess: () => { queryClient.invalidateQueries({ queryKey: memoirKeys.book() }); }, }); } // ─── Chapters ─── export function hasCompletedMemoirChapter( chapters: { isEmpty: boolean }[], ): boolean { return chapters.some((ch) => !ch.isEmpty); } export function useChapters(options?: { enabled?: boolean }) { const enabled = options?.enabled ?? true; const query = useQuery({ queryKey: memoirKeys.chapters(), queryFn: () => memoirApi.fetchChapters(), enabled, retry: (failureCount, error) => { if (error instanceof AuthError) return false; return failureCount < 1; }, }); const viewModels = query.data ? toChapterViewModels(query.data) : []; const hasCompletedChapters = hasCompletedMemoirChapter(viewModels); const isEmptyList = query.isSuccess && viewModels.length === 0 && !hasCompletedChapters; const showLoadError = !query.isLoading && shouldShowChapterListLoadError( query.error, query.isSuccess, viewModels.length, ); return { ...query, viewModels, hasCompletedChapters, isEmptyList, showLoadError, }; } export function useChapterDetail(chapterId: string) { return useQuery({ queryKey: memoirKeys.chapterDetail(chapterId), queryFn: () => memoirApi.fetchChapterDetail(chapterId), enabled: !!chapterId, }); } export function useDeleteChapter() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (chapterId: string) => memoirApi.deleteChapter(chapterId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: memoirKeys.chapters() }); }, }); } export function useCheckCoverGeneration() { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => memoirApi.checkCoverGeneration(), onSuccess: (data) => { if (data.triggered.length > 0) { queryClient.invalidateQueries({ queryKey: memoirKeys.chapters() }); } }, }); } // ─── Memoir state ─── export function useMemoirState(options?: { enabled?: boolean }) { const enabled = options?.enabled ?? true; return useQuery({ queryKey: memoirKeys.state(), queryFn: () => memoirApi.fetchMemoirState(), enabled, }); } export function useNextQuestion() { return useQuery({ queryKey: memoirKeys.nextQuestion(), queryFn: () => memoirApi.fetchNextQuestion(), }); } export function useMarkMemoirRead() { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => memoirApi.markMemoirRead(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: memoirKeys.all }); }, }); } // ─── PDF export ─── export function useExportPdf() { return useMutation({ mutationFn: (body: ExportPdfRequest) => memoirApi.exportPdf(body), }); } // ─── Tasks ─── export function useTasksStatus(enabled = true) { return useQuery({ queryKey: memoirKeys.tasks(), queryFn: () => memoirApi.fetchTasksStatus(), enabled, refetchInterval: (query) => { const data = query.state.data; if (data && !data.all_completed) return 5_000; return false; }, }); } export function useClearTasks() { const queryClient = useQueryClient(); return useMutation({ mutationFn: () => memoirApi.clearTasks(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: memoirKeys.tasks() }); }, }); }