@@ -15,7 +15,6 @@ import { downloadFile } from "../lib/utils/download";
1515import { Button } from "./ui/button" ;
1616import ShareButton from "./ShareButton" ;
1717import ReportButton from "./ReportButton" ;
18- import path from 'path/win32' ;
1918
2019interface ControlProps {
2120 documentId : string ;
@@ -255,7 +254,6 @@ function WheelZoom({ documentId, viewerRef }: WheelZoomProps) {
255254 const rafId = useRef < number | null > ( null ) ;
256255 const curZoom = useRef ( 1 ) ;
257256
258- // Stay in sync with zoom from anywhere (buttons, keyboard, etc.)
259257 useEffect ( ( ) => {
260258 if ( ! zoomProv ) return ;
261259 curZoom . current = zoomProv . getState ( ) . currentZoomLevel ;
@@ -283,7 +281,7 @@ function WheelZoom({ documentId, viewerRef }: WheelZoomProps) {
283281 zoomProv . requestZoom ( target )
284282 curZoom . current = target ;
285283 } else {
286- zoomProv . requestZoomBy ( clamped ) ; // mouse wheel, relative is fine
284+ zoomProv . requestZoomBy ( clamped ) ;
287285 }
288286
289287 accumulatedDelta . current = 0 ;
@@ -306,6 +304,44 @@ function WheelZoom({ documentId, viewerRef }: WheelZoomProps) {
306304 return null ;
307305}
308306
307+ function useLoadingMessage ( messages : string [ ] , interval = 2200 ) {
308+ const [ index , setIndex ] = useState ( 0 ) ;
309+ const [ visible , setVisible ] = useState ( true ) ;
310+
311+ useEffect ( ( ) => {
312+ const timer = setInterval ( ( ) => {
313+ setVisible ( false ) ;
314+ setTimeout ( ( ) => {
315+ setIndex ( i => ( i + 1 ) % messages . length ) ;
316+ setVisible ( true ) ;
317+ } , 400 ) ;
318+ } , interval ) ;
319+ return ( ) => clearInterval ( timer ) ;
320+ } , [ messages , interval ] ) ;
321+
322+ return { message : messages [ index ] , visible } ;
323+ }
324+
325+ const LOADING_MESSAGES = [
326+ "Loading document" ,
327+ "Preparing pages" ,
328+ "Almost there" ,
329+ ] ;
330+
331+ export function Loader ( ) {
332+ const { message, visible } = useLoadingMessage ( LOADING_MESSAGES ) ;
333+ return (
334+ < div className = "flex flex-col items-center justify-center gap-3 h-dvh w-full bg-[#070114]" >
335+ < div className = "w-7 h-7 rounded-full border-2 border-white/10 border-t-white animate-spin" />
336+ < span
337+ className = "text-white/50 text-sm tracking-wide transition-opacity duration-400"
338+ style = { { opacity : visible ? 1 : 0 } }
339+ >
340+ { message }
341+ </ span >
342+ </ div >
343+ ) ;
344+ }
309345export default function PDFViewer ( { url, name } : PdfViewerProps ) {
310346 const { engine, isLoading } = usePdfiumEngine ( ) ;
311347 const { isMobile, isSmall} = useBreakpoint ( ) ;
@@ -351,9 +387,11 @@ export default function PDFViewer({ url, name }: PdfViewerProps) {
351387 } ) ,
352388 ] , [ url , name ] ) ;
353389
354- if ( isLoading || ! engine ) {
355- return < div > Loading PDF Engine...</ div > ;
356- }
390+ if ( isLoading || ! engine ) {
391+ return (
392+ < Loader />
393+ ) ;
394+ }
357395
358396 return (
359397 < div
@@ -371,32 +409,39 @@ export default function PDFViewer({ url, name }: PdfViewerProps) {
371409 toggleFullscreen = { toggleFullscreen }
372410 isFullscreen = { isFullscreen }
373411 onDownload = { handleDownload }
374- forceMobile = { true } // pass the isMobile flag to Controls`
412+ forceMobile = { true }
375413 isMobile = { isMobile }
376414 isSmall = { isSmall }
377415 /> }
378416 < DocumentContent documentId = { activeDocumentId } >
379- { ( { isLoaded } ) =>
380- isLoaded && (
381- < Viewport
382- documentId = { activeDocumentId }
383- style = { { backgroundColor : "#070114" } }
384- >
385- < Scroller
386- documentId = { activeDocumentId }
387- renderPage = { ( { width, height, pageIndex } ) => (
388- < div style = { { width, height } } >
389- < RenderLayer
390- documentId = { activeDocumentId }
391- pageIndex = { pageIndex }
392- />
393- </ div >
394- ) }
395- />
396- </ Viewport >
397- )
398- }
399- </ DocumentContent >
417+ { ( { isLoaded } ) => (
418+ < >
419+ < div
420+ className = "absolute inset-0 z-50 flex items-center justify-center bg-[#070114]"
421+ style = { {
422+ opacity : isLoaded ? 0 : 1 ,
423+ pointerEvents : isLoaded ? "none" : "auto" ,
424+ transition : "opacity 0.3s"
425+ } }
426+ >
427+ < Loader />
428+ </ div >
429+ < Viewport
430+ documentId = { activeDocumentId }
431+ style = { { backgroundColor : "#070114" , visibility : isLoaded ? "visible" : "hidden" } }
432+ >
433+ < Scroller
434+ documentId = { activeDocumentId }
435+ renderPage = { ( { width, height, pageIndex } ) => (
436+ < div style = { { width, height } } >
437+ < RenderLayer documentId = { activeDocumentId } pageIndex = { pageIndex } />
438+ </ div >
439+ ) }
440+ />
441+ </ Viewport >
442+ </ >
443+ ) }
444+ </ DocumentContent >
400445
401446 { ( ! isMobile || isFullscreen ) && (
402447 < Controls
@@ -409,7 +454,6 @@ export default function PDFViewer({ url, name }: PdfViewerProps) {
409454 isSmall = { isSmall }
410455 />
411456 ) }
412-
413457 </ >
414458 )
415459 }
0 commit comments