Merge pull request #18655 from overleaf/dp-pdf-viewer-typescript

Convert PdfJsViewer component to typescript

GitOrigin-RevId: 1019b947f3e6869a99495d3de7cd14ebd5007587
This commit is contained in:
David 2024-06-03 10:22:27 +01:00 committed by Copybot
parent d652717c5a
commit 656130b2ed
2 changed files with 32 additions and 23 deletions

View file

@ -1,4 +1,3 @@
import PropTypes from 'prop-types'
import { memo, useCallback, useEffect, useRef, useState } from 'react' import { memo, useCallback, useEffect, useRef, useState } from 'react'
import { debounce, throttle } from 'lodash' import { debounce, throttle } from 'lodash'
import PdfViewerControls from './pdf-viewer-controls' import PdfViewerControls from './pdf-viewer-controls'
@ -17,7 +16,12 @@ import { debugConsole } from '@/utils/debugging'
import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider' import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider'
import { useFeatureFlag } from '@/shared/context/split-test-context' import { useFeatureFlag } from '@/shared/context/split-test-context'
function PdfJsViewer({ url, pdfFile }) { type PdfJsViewerProps = {
url: string
pdfFile: Record<string, any>
}
function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
const { setError, firstRenderDone, highlights, position, setPosition } = const { setError, firstRenderDone, highlights, position, setPosition } =
@ -40,7 +44,7 @@ function PdfJsViewer({ url, pdfFile }) {
const [totalPages, setTotalPages] = useState(null) const [totalPages, setTotalPages] = useState(null)
// local state values // local state values
const [pdfJsWrapper, setPdfJsWrapper] = useState() const [pdfJsWrapper, setPdfJsWrapper] = useState<PDFJSWrapper | null>()
const [initialised, setInitialised] = useState(false) const [initialised, setInitialised] = useState(false)
const handlePageChange = useCallback( const handlePageChange = useCallback(
@ -84,8 +88,8 @@ function PdfJsViewer({ url, pdfFile }) {
useEffect(() => { useEffect(() => {
if (!pdfJsWrapper || !firstRenderDone) return if (!pdfJsWrapper || !firstRenderDone) return
let timePDFFetched let timePDFFetched: number
let timePDFRendered let timePDFRendered: number
const submitLatencies = () => { const submitLatencies = () => {
if (!timePDFFetched) { if (!timePDFFetched) {
// The pagerendered event was attached after pagesinit fired. :/ // The pagerendered event was attached after pagesinit fired. :/
@ -164,7 +168,7 @@ function PdfJsViewer({ url, pdfFile }) {
setStartFetch(performance.now()) setStartFetch(performance.now())
const abortController = new AbortController() const abortController = new AbortController()
const handleFetchError = err => { const handleFetchError = (err: Error) => {
if (abortController.signal.aborted) return if (abortController.signal.aborted) return
// The error is already logged at the call-site with additional context. // The error is already logged at the call-site with additional context.
if (err instanceof pdfJsWrapper.PDFJS.MissingPDFException) { if (err instanceof pdfJsWrapper.PDFJS.MissingPDFException) {
@ -192,7 +196,7 @@ function PdfJsViewer({ url, pdfFile }) {
// listen for scroll events // listen for scroll events
useEffect(() => { useEffect(() => {
let storePositionTimer let storePositionTimer: number
if (initialised && pdfJsWrapper) { if (initialised && pdfJsWrapper) {
if (!pdfJsWrapper.isVisible()) { if (!pdfJsWrapper.isVisible()) {
@ -233,7 +237,7 @@ function PdfJsViewer({ url, pdfFile }) {
// listen for double-click events // listen for double-click events
useEffect(() => { useEffect(() => {
if (pdfJsWrapper) { if (pdfJsWrapper) {
const handleTextlayerrendered = textLayer => { const handleTextlayerrendered = (textLayer: any) => {
// handle both versions for backwards-compatibility // handle both versions for backwards-compatibility
const textLayerDiv = const textLayerDiv =
textLayer.source.textLayerDiv ?? textLayer.source.textLayer.div textLayer.source.textLayerDiv ?? textLayer.source.textLayer.div
@ -243,7 +247,7 @@ function PdfJsViewer({ url, pdfFile }) {
if (!pageElement.dataset.listeningForDoubleClick) { if (!pageElement.dataset.listeningForDoubleClick) {
pageElement.dataset.listeningForDoubleClick = true pageElement.dataset.listeningForDoubleClick = true
const doubleClickListener = event => { const doubleClickListener = (event: Event) => {
const clickPosition = pdfJsWrapper.clickPosition( const clickPosition = pdfJsWrapper.clickPosition(
event, event,
pageElement, pageElement,
@ -291,6 +295,10 @@ function PdfJsViewer({ url, pdfFile }) {
return return
} }
if (positionRef.current) { if (positionRef.current) {
// Typescript is incorrectly inferring the type of the scale argument to
// scrollToPosition from its default value. We can remove this ignore once
// pdfJsWrapper is converted to using tyepscript.
// @ts-ignore
pdfJsWrapper.scrollToPosition(positionRef.current, scaleRef.current) pdfJsWrapper.scrollToPosition(positionRef.current, scaleRef.current)
} else { } else {
pdfJsWrapper.viewer.currentScaleValue = scaleRef.current pdfJsWrapper.viewer.currentScaleValue = scaleRef.current
@ -307,8 +315,8 @@ function PdfJsViewer({ url, pdfFile }) {
// when highlights are created, build the highlight elements // when highlights are created, build the highlight elements
useEffect(() => { useEffect(() => {
const timers = [] const timers: number[] = []
let intersectionObserver let intersectionObserver: IntersectionObserver
if (pdfJsWrapper && highlights?.length) { if (pdfJsWrapper && highlights?.length) {
// watch for the highlight elements to scroll into view // watch for the highlight elements to scroll into view
@ -318,12 +326,14 @@ function PdfJsViewer({ url, pdfFile }) {
if (entry.isIntersecting) { if (entry.isIntersecting) {
intersectionObserver.unobserve(entry.target) intersectionObserver.unobserve(entry.target)
const element = entry.target as HTMLElement
// fade the element in and out // fade the element in and out
entry.target.style.opacity = '0.5' element.style.opacity = '0.5'
timers.push( timers.push(
window.setTimeout(() => { window.setTimeout(() => {
entry.target.style.opacity = '0' element.style.opacity = '0'
}, 1000) }, 1000)
) )
} }
@ -334,7 +344,7 @@ function PdfJsViewer({ url, pdfFile }) {
} }
) )
const elements = [] const elements: HTMLDivElement[] = []
for (const highlight of highlights) { for (const highlight of highlights) {
try { try {
@ -384,13 +394,13 @@ function PdfJsViewer({ url, pdfFile }) {
break break
case 'zoom-in': case 'zoom-in':
if (pdfJsWrapper) { if (pdfJsWrapper) {
setScale(pdfJsWrapper.viewer.currentScale * 1.25) setScale(`${pdfJsWrapper.viewer.currentScale * 1.25}`)
} }
break break
case 'zoom-out': case 'zoom-out':
if (pdfJsWrapper) { if (pdfJsWrapper) {
setScale(pdfJsWrapper.viewer.currentScale * 0.75) setScale(`${pdfJsWrapper.viewer.currentScale * 0.75}`)
} }
break break
@ -499,7 +509,7 @@ function PdfJsViewer({ url, pdfFile }) {
> >
<div className="pdfViewer" /> <div className="pdfViewer" />
</div> </div>
<div className="pdfjs-controls" tabIndex="0"> <div className="pdfjs-controls" tabIndex={0}>
{hasNewPdfToolbar ? ( {hasNewPdfToolbar ? (
toolbarInfoLoaded && ( toolbarInfoLoaded && (
<PdfViewerControlsToolbar <PdfViewerControlsToolbar
@ -518,11 +528,6 @@ function PdfJsViewer({ url, pdfFile }) {
) )
} }
PdfJsViewer.propTypes = {
url: PropTypes.string.isRequired,
pdfFile: PropTypes.object,
}
export default withErrorBoundary(memo(PdfJsViewer), () => ( export default withErrorBoundary(memo(PdfJsViewer), () => (
<PdfPreviewErrorBoundaryFallback type="pdf" /> <PdfPreviewErrorBoundaryFallback type="pdf" />
)) ))

View file

@ -79,7 +79,11 @@ export type CompileContext = {
stoppedOnFirstError: boolean stoppedOnFirstError: boolean
uncompiled?: boolean uncompiled?: boolean
validationIssues?: Record<string, any> validationIssues?: Record<string, any>
firstRenderDone: () => void firstRenderDone: (metrics: {
latencyFetch: number
latencyRender: number | undefined
pdfCachingMetrics: { viewerId: string }
}) => void
cleanupCompileResult?: () => void cleanupCompileResult?: () => void
animateCompileDropdownArrow: boolean animateCompileDropdownArrow: boolean
editedSinceCompileStarted: boolean editedSinceCompileStarted: boolean