MissawB's picture
Upload folder using huggingface_hub (part 4)
df2e14d verified
Raw
History Blame Contribute Delete
2.63 kB
import { useEffect, useState } from 'react';
import { apiClient } from '../../../utils/apiClient';
import { isChapterDownloaded, getChapterPageBlobs } from './offlineLibrary';
export type PageSource = 'loading' | 'offline' | 'network' | 'unavailable';
interface ReaderPage {
url: string;
index: number;
}
export function useChapterPages(mediaId: string, chapterNumber: string) {
const [pages, setPages] = useState<ReaderPage[]>([]);
const [source, setSource] = useState<PageSource>('loading');
// Reset to the loading state synchronously when the chapter changes by adjusting
// state during render (React's "storing previous value" pattern). Doing this here
// instead of in the effect avoids a cascading-render and guarantees the new
// chapter never briefly shows the previous chapter's pages.
const chapterKey = `${mediaId}:${chapterNumber}`;
const [prevChapterKey, setPrevChapterKey] = useState(chapterKey);
if (prevChapterKey !== chapterKey) {
setPrevChapterKey(chapterKey);
setSource('loading');
setPages([]);
}
useEffect(() => {
let active = true;
// Object URLs created by THIS effect run only, so a Strict-Mode double-invoke
// (or rapid chapter change) never revokes URLs a later run still uses.
const created: string[] = [];
(async () => {
const num = Number(chapterNumber);
if (await isChapterDownloaded(mediaId, num)) {
const blobs = await getChapterPageBlobs(mediaId, num);
if (!active) return;
const offlinePages = blobs.map((blob, index) => {
const url = URL.createObjectURL(blob);
created.push(url);
return { url, index };
});
setPages(offlinePages);
setSource('offline');
return;
}
try {
// skipToast: this hook owns error presentation (the "unavailable" state);
// letting apiClient pop a global error toast would contradict that UI.
const chapter = await apiClient(
`/api/v1/media/Manga/${mediaId}/chapters/${chapterNumber}/`,
{ skipToast: true },
);
if (!active) return;
const networkPages: ReaderPage[] = (chapter?.pages ?? []).map(
(p: { image_url: string; number: number }) => ({ url: p.image_url, index: p.number }),
);
setPages(networkPages);
setSource('network');
} catch {
if (!active) return;
setSource('unavailable');
}
})();
return () => {
active = false;
created.forEach((u) => URL.revokeObjectURL(u));
};
}, [mediaId, chapterNumber]);
return { pages, source };
}