fix: show spinner instead of broken image icon while loading reader pages
All checks were successful
Build, Push & Deploy / deploy (push) Successful in 4m25s
All checks were successful
Build, Push & Deploy / deploy (push) Successful in 4m25s
Only render <img> when blob URL is available from prefetch, preventing
broken image icons from src={undefined} or failed direct URL fallbacks.
Reset error state when blob URL arrives to recover from transient failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -265,9 +265,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
isDoublePage={isDoublePage}
|
||||
shouldShowDoublePage={(page) => shouldShowDoublePage(page, pages.length)}
|
||||
imageBlobUrls={imageBlobUrls}
|
||||
getPageUrl={getPageUrl}
|
||||
isRTL={isRTL}
|
||||
isPageLoading={isPageLoading}
|
||||
/>
|
||||
|
||||
<NavigationBar
|
||||
|
||||
@@ -7,9 +7,7 @@ interface PageDisplayProps {
|
||||
isDoublePage: boolean;
|
||||
shouldShowDoublePage: (page: number) => boolean;
|
||||
imageBlobUrls: Record<number, string>;
|
||||
getPageUrl: (pageNum: number) => string;
|
||||
isRTL: boolean;
|
||||
isPageLoading?: (pageNum: number) => boolean;
|
||||
}
|
||||
|
||||
export function PageDisplay({
|
||||
@@ -18,9 +16,7 @@ export function PageDisplay({
|
||||
isDoublePage,
|
||||
shouldShowDoublePage,
|
||||
imageBlobUrls,
|
||||
getPageUrl,
|
||||
isRTL,
|
||||
isPageLoading,
|
||||
}: PageDisplayProps) {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
@@ -53,6 +49,21 @@ export function PageDisplay({
|
||||
setSecondPageHasError(false);
|
||||
}, [currentPage, isDoublePage]);
|
||||
|
||||
// Reset error state when blob URL becomes available
|
||||
useEffect(() => {
|
||||
if (imageBlobUrls[currentPage] && hasError) {
|
||||
setHasError(false);
|
||||
setIsLoading(true);
|
||||
}
|
||||
}, [imageBlobUrls[currentPage], currentPage, hasError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (imageBlobUrls[currentPage + 1] && secondPageHasError) {
|
||||
setSecondPageHasError(false);
|
||||
setSecondPageLoading(true);
|
||||
}
|
||||
}, [imageBlobUrls[currentPage + 1], currentPage, secondPageHasError]);
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full flex-1 items-center justify-center overflow-hidden">
|
||||
<div className="relative flex h-[calc(100vh-2.5rem)] w-full items-center justify-center px-2 sm:px-4">
|
||||
@@ -99,15 +110,12 @@ export function PageDisplay({
|
||||
</svg>
|
||||
<span className="text-sm opacity-60">Image non disponible</span>
|
||||
</div>
|
||||
) : (
|
||||
) : imageBlobUrls[currentPage] ? (
|
||||
<>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
key={`page-${currentPage}-${imageBlobUrls[currentPage] || ""}`}
|
||||
src={
|
||||
imageBlobUrls[currentPage] ||
|
||||
(isPageLoading && isPageLoading(currentPage) ? undefined : getPageUrl(currentPage))
|
||||
}
|
||||
key={`page-${currentPage}-${imageBlobUrls[currentPage]}`}
|
||||
src={imageBlobUrls[currentPage]}
|
||||
alt={`Page ${currentPage}`}
|
||||
className={cn(
|
||||
"max-h-full max-w-full cursor-pointer object-contain transition-opacity",
|
||||
@@ -124,7 +132,7 @@ export function PageDisplay({
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Page 2 (double page) */}
|
||||
@@ -166,17 +174,12 @@ export function PageDisplay({
|
||||
</svg>
|
||||
<span className="text-sm opacity-60">Image non disponible</span>
|
||||
</div>
|
||||
) : (
|
||||
) : imageBlobUrls[currentPage + 1] ? (
|
||||
<>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
key={`page-${currentPage + 1}-${imageBlobUrls[currentPage + 1] || ""}`}
|
||||
src={
|
||||
imageBlobUrls[currentPage + 1] ||
|
||||
(isPageLoading && isPageLoading(currentPage + 1)
|
||||
? undefined
|
||||
: getPageUrl(currentPage + 1))
|
||||
}
|
||||
key={`page-${currentPage + 1}-${imageBlobUrls[currentPage + 1]}`}
|
||||
src={imageBlobUrls[currentPage + 1]}
|
||||
alt={`Page ${currentPage + 1}`}
|
||||
className={cn(
|
||||
"max-h-full max-w-full cursor-pointer object-contain transition-opacity",
|
||||
@@ -193,7 +196,7 @@ export function PageDisplay({
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user