Refactor viewer

This commit is contained in:
Reece 2025-07-15 00:48:54 +01:00
parent be251d271d
commit 934cd01268
2 changed files with 33 additions and 57 deletions

View File

@ -184,8 +184,7 @@ const Viewer = ({
}, [previewFile, pdfFile]); }, [previewFile, pdfFile]);
const scrollAreaRef = useRef<HTMLDivElement>(null); const scrollAreaRef = useRef<HTMLDivElement>(null);
const userInitiatedRef = useRef(false); const isManualNavigationRef = useRef(false);
const suppressScrollRef = useRef(false);
const pdfDocRef = useRef<any>(null); const pdfDocRef = useRef<any>(null);
const renderingPagesRef = useRef<Set<number>>(new Set()); const renderingPagesRef = useRef<Set<number>>(new Set());
const currentArrayBufferRef = useRef<ArrayBuffer | null>(null); const currentArrayBufferRef = useRef<ArrayBuffer | null>(null);
@ -264,70 +263,44 @@ const Viewer = ({
preloadingRef.current = false; preloadingRef.current = false;
}; };
// Listen for hash changes and update currentPage // Initialize current page when PDF loads
useEffect(() => { useEffect(() => {
function handleHashChange() { if (numPages > 0 && !currentPage) {
if (window.location.hash.startsWith("#page=")) { setCurrentPage(1);
const page = parseInt(window.location.hash.replace("#page=", ""), 10);
if (!isNaN(page) && page >= 1 && page <= numPages) {
setCurrentPage(page);
}
}
userInitiatedRef.current = false;
} }
window.addEventListener("hashchange", handleHashChange); }, [numPages, currentPage]);
handleHashChange(); // Run on mount
return () => window.removeEventListener("hashchange", handleHashChange);
}, [numPages]);
// Scroll to the current page when it changes // Scroll to the current page when manually navigated
useEffect(() => { useEffect(() => {
if (currentPage && pageRefs.current[currentPage - 1]) { if (currentPage && pageRefs.current[currentPage - 1] && isManualNavigationRef.current) {
suppressScrollRef.current = true;
const el = pageRefs.current[currentPage - 1]; const el = pageRefs.current[currentPage - 1];
el?.scrollIntoView({ behavior: "smooth", block: "center" }); el?.scrollIntoView({ behavior: "smooth", block: "center" });
// Try to use scrollend if supported // Reset manual navigation flag after a delay
const viewport = scrollAreaRef.current; const timeout = setTimeout(() => {
let timeout: NodeJS.Timeout | null = null; isManualNavigationRef.current = false;
let scrollEndHandler: (() => void) | null = null; }, 1000);
if (viewport && "onscrollend" in viewport) { return () => clearTimeout(timeout);
scrollEndHandler = () => {
suppressScrollRef.current = false;
viewport.removeEventListener("scrollend", scrollEndHandler!);
};
viewport.addEventListener("scrollend", scrollEndHandler);
} else {
// Fallback for non-Chromium browsers
timeout = setTimeout(() => {
suppressScrollRef.current = false;
}, 1000);
}
return () => {
if (viewport && scrollEndHandler) {
viewport.removeEventListener("scrollend", scrollEndHandler);
}
if (timeout) clearTimeout(timeout);
};
} }
}, [currentPage, pageImages]); }, [currentPage, pageImages]);
// Detect visible page on scroll and update hash // Detect visible page on scroll (only update display, don't trigger navigation)
const handleScroll = () => { const handleScroll = () => {
if (suppressScrollRef.current) return; if (isManualNavigationRef.current) return;
const scrollArea = scrollAreaRef.current; const scrollArea = scrollAreaRef.current;
if (!scrollArea || !pageRefs.current.length) return; if (!scrollArea || !pageRefs.current.length) return;
const areaRect = scrollArea.getBoundingClientRect(); const areaRect = scrollArea.getBoundingClientRect();
const viewportCenter = areaRect.top + areaRect.height / 2;
let closestIdx = 0; let closestIdx = 0;
let minDist = Infinity; let minDist = Infinity;
pageRefs.current.forEach((img, idx) => { pageRefs.current.forEach((img, idx) => {
if (img) { if (img) {
const imgRect = img.getBoundingClientRect(); const imgRect = img.getBoundingClientRect();
const dist = Math.abs(imgRect.top - areaRect.top); const imgCenter = imgRect.top + imgRect.height / 2;
const dist = Math.abs(imgCenter - viewportCenter);
if (dist < minDist) { if (dist < minDist) {
minDist = dist; minDist = dist;
closestIdx = idx; closestIdx = idx;
@ -335,11 +308,9 @@ const Viewer = ({
} }
}); });
// Only update if the page actually changed and we're not in manual navigation
if (currentPage !== closestIdx + 1) { if (currentPage !== closestIdx + 1) {
setCurrentPage(closestIdx + 1); setCurrentPage(closestIdx + 1);
if (window.location.hash !== `#page=${closestIdx + 1}`) {
window.location.hash = `#page=${closestIdx + 1}`;
}
} }
}; };
@ -417,7 +388,7 @@ const Viewer = ({
}, [pageImages]); }, [pageImages]);
return ( return (
<Box style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}> <Box style={{ position: 'relative', height: '100vh', display: 'flex', flexDirection: 'column' }}>
{/* Close Button - Only show in preview mode */} {/* Close Button - Only show in preview mode */}
{onClose && previewFile && ( {onClose && previewFile && (
<ActionIcon <ActionIcon
@ -467,7 +438,7 @@ const Viewer = ({
</div> </div>
) : ( ) : (
<ScrollArea <ScrollArea
style={{ flex: 1, height: "100vh", position: "relative"}} style={{ flex: 1, position: "relative"}}
viewportRef={scrollAreaRef} viewportRef={scrollAreaRef}
> >
<Stack gap="xl" align="center" > <Stack gap="xl" align="center" >
@ -552,7 +523,8 @@ const Viewer = ({
px={8} px={8}
radius="xl" radius="xl"
onClick={() => { onClick={() => {
window.location.hash = `#page=1`; isManualNavigationRef.current = true;
setCurrentPage(1);
}} }}
disabled={currentPage === 1} disabled={currentPage === 1}
style={{ minWidth: 36 }} style={{ minWidth: 36 }}
@ -566,7 +538,8 @@ const Viewer = ({
px={8} px={8}
radius="xl" radius="xl"
onClick={() => { onClick={() => {
window.location.hash = `#page=${Math.max(1, (currentPage || 1) - 1)}`; isManualNavigationRef.current = true;
setCurrentPage(Math.max(1, (currentPage || 1) - 1));
}} }}
disabled={currentPage === 1} disabled={currentPage === 1}
style={{ minWidth: 36 }} style={{ minWidth: 36 }}
@ -578,7 +551,8 @@ const Viewer = ({
onChange={value => { onChange={value => {
const page = Number(value); const page = Number(value);
if (!isNaN(page) && page >= 1 && page <= numPages) { if (!isNaN(page) && page >= 1 && page <= numPages) {
window.location.hash = `#page=${page}`; isManualNavigationRef.current = true;
setCurrentPage(page);
} }
}} }}
min={1} min={1}
@ -598,7 +572,8 @@ const Viewer = ({
px={8} px={8}
radius="xl" radius="xl"
onClick={() => { onClick={() => {
window.location.hash = `#page=${Math.min(numPages, (currentPage || 1) + 1)}`; isManualNavigationRef.current = true;
setCurrentPage(Math.min(numPages, (currentPage || 1) + 1));
}} }}
disabled={currentPage === numPages} disabled={currentPage === numPages}
style={{ minWidth: 36 }} style={{ minWidth: 36 }}
@ -612,7 +587,8 @@ const Viewer = ({
px={8} px={8}
radius="xl" radius="xl"
onClick={() => { onClick={() => {
window.location.hash = `#page=${numPages}`; isManualNavigationRef.current = true;
setCurrentPage(numPages);
}} }}
disabled={currentPage === numPages} disabled={currentPage === numPages}
style={{ minWidth: 36 }} style={{ minWidth: 36 }}

View File

@ -169,7 +169,7 @@ export default function HomePage() {
/> />
{/* Main content area */} {/* Main content area */}
<Box <Box
className="flex-1 min-h-0 margin-top-200 relative z-10" className="flex-1 min-h-0 relative z-10"
style={{ style={{
transition: 'opacity 0.15s ease-in-out', transition: 'opacity 0.15s ease-in-out',
}} }}