file-view-pdf.tsx 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { FC, useCallback } from 'react'
  2. import useIsMounted from '@/shared/hooks/use-is-mounted'
  3. import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
  4. const FileViewPdf: FC<{
  5. fileId: string
  6. onLoad: () => void
  7. onError: () => void
  8. }> = ({ fileId, onLoad, onError }) => {
  9. const mountedRef = useIsMounted()
  10. const { previewByPath, pathInFolder } = useFileTreePathContext()
  11. const handleContainer = useCallback(
  12. async (element: HTMLDivElement | null) => {
  13. if (element) {
  14. const { loadPdfDocumentFromUrl } = await import(
  15. '@/features/pdf-preview/util/pdf-js'
  16. )
  17. // bail out if loading PDF.js took too long
  18. if (!mountedRef.current) {
  19. return
  20. }
  21. const path = pathInFolder(fileId)
  22. const preview = path ? previewByPath(path) : null
  23. if (!preview) {
  24. onError()
  25. return
  26. }
  27. const pdf = await loadPdfDocumentFromUrl(preview.url).promise
  28. // bail out if loading the PDF took too long
  29. if (!mountedRef.current) {
  30. return
  31. }
  32. element.textContent = '' // ensure the element is empty
  33. const scale = window.devicePixelRatio || 1
  34. for (let i = 1; i <= pdf.numPages; i++) {
  35. const page = await pdf.getPage(i)
  36. // bail out if the component has been unmounted
  37. if (!mountedRef.current) {
  38. return
  39. }
  40. const viewport = page.getViewport({ scale })
  41. const canvas = document.createElement('canvas')
  42. canvas.classList.add('pdf-page')
  43. canvas.width = viewport.width
  44. canvas.height = viewport.height
  45. canvas.style.width = `${viewport.width / scale}px`
  46. canvas.style.height = `${viewport.height / scale}px`
  47. element.append(canvas)
  48. page.render({
  49. canvasContext: canvas.getContext('2d')!,
  50. viewport,
  51. })
  52. }
  53. onLoad()
  54. }
  55. },
  56. [mountedRef, pathInFolder, fileId, previewByPath, onLoad, onError]
  57. )
  58. return <div className="file-view-pdf" ref={handleContainer} />
  59. }
  60. export default FileViewPdf