Просмотр исходного кода

Merge pull request #14999 from overleaf/mf-file-view-header-typescript

[web] Convert `file-view-header.js` to typescript

GitOrigin-RevId: 1f05b086e3acb692f3745481c4022a920c4f7599
M Fahru 2 лет назад
Родитель
Сommit
496bb4d4e7

+ 36 - 37
services/web/frontend/js/features/file-view/components/file-view-header.js → services/web/frontend/js/features/file-view/components/file-view-header.tsx

@@ -1,4 +1,4 @@
-import { useState, useCallback } from 'react'
+import { useState, useCallback, type ElementType } from 'react'
 import PropTypes from 'prop-types'
 import PropTypes from 'prop-types'
 import { Trans, useTranslation } from 'react-i18next'
 import { Trans, useTranslation } from 'react-i18next'
 
 
@@ -11,18 +11,27 @@ import { useProjectContext } from '../../../shared/context/project-context'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
 import useAbortController from '../../../shared/hooks/use-abort-controller'
 import useAbortController from '../../../shared/hooks/use-abort-controller'
 import { LinkedFileIcon } from './file-view-icons'
 import { LinkedFileIcon } from './file-view-icons'
+import { BinaryFile, hasProvider, LinkedFile } from '../types/binary-file'
 import { debugConsole } from '@/utils/debugging'
 import { debugConsole } from '@/utils/debugging'
-const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo')
+
+const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo') as {
+  import: { LinkedFileInfo: ElementType }
+  path: string
+}[]
+
 const tprLinkedFileRefreshError = importOverleafModules(
 const tprLinkedFileRefreshError = importOverleafModules(
   'tprLinkedFileRefreshError'
   'tprLinkedFileRefreshError'
-)
+) as {
+  import: { LinkedFileRefreshError: ElementType }
+  path: string
+}[]
 
 
 const MAX_URL_LENGTH = 60
 const MAX_URL_LENGTH = 60
 const FRONT_OF_URL_LENGTH = 35
 const FRONT_OF_URL_LENGTH = 35
 const FILLER = '...'
 const FILLER = '...'
 const TAIL_OF_URL_LENGTH = MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length
 const TAIL_OF_URL_LENGTH = MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length
 
 
-function shortenedUrl(url) {
+function shortenedUrl(url: string) {
   if (!url) {
   if (!url) {
     return
     return
   }
   }
@@ -34,7 +43,15 @@ function shortenedUrl(url) {
   return url
   return url
 }
 }
 
 
-export default function FileViewHeader({ file, storeReferencesKeys }) {
+type FileViewHeaderProps = {
+  file: BinaryFile
+  storeReferencesKeys: (keys: string[]) => void
+}
+
+export default function FileViewHeader({
+  file,
+  storeReferencesKeys,
+}: FileViewHeaderProps) {
   const { _id: projectId } = useProjectContext({
   const { _id: projectId } = useProjectContext({
     _id: PropTypes.string.isRequired,
     _id: PropTypes.string.isRequired,
   })
   })
@@ -50,19 +67,19 @@ export default function FileViewHeader({ file, storeReferencesKeys }) {
 
 
   let fileInfo
   let fileInfo
   if (file.linkedFileData) {
   if (file.linkedFileData) {
-    if (file.linkedFileData.provider === 'url') {
+    if (hasProvider(file, 'url')) {
       fileInfo = (
       fileInfo = (
         <div>
         <div>
           <UrlProvider file={file} />
           <UrlProvider file={file} />
         </div>
         </div>
       )
       )
-    } else if (file.linkedFileData.provider === 'project_file') {
+    } else if (hasProvider(file, 'project_file')) {
       fileInfo = (
       fileInfo = (
         <div>
         <div>
           <ProjectFilePathProvider file={file} />
           <ProjectFilePathProvider file={file} />
         </div>
         </div>
       )
       )
-    } else if (file.linkedFileData.provider === 'project_output_file') {
+    } else if (hasProvider(file, 'project_output_file')) {
       fileInfo = (
       fileInfo = (
         <div>
         <div>
           <ProjectOutputFileProvider file={file} />
           <ProjectOutputFileProvider file={file} />
@@ -85,8 +102,8 @@ export default function FileViewHeader({ file, storeReferencesKeys }) {
       })
       })
       .finally(() => {
       .finally(() => {
         if (
         if (
-          file.linkedFileData.provider === 'mendeley' ||
-          file.linkedFileData.provider === 'zotero' ||
+          hasProvider(file, 'mendeley') ||
+          hasProvider(file, 'zotero') ||
           file.name.match(/^.*\.bib$/)
           file.name.match(/^.*\.bib$/)
         ) {
         ) {
           reindexReferences()
           reindexReferences()
@@ -151,16 +168,11 @@ export default function FileViewHeader({ file, storeReferencesKeys }) {
   )
   )
 }
 }
 
 
-FileViewHeader.propTypes = {
-  file: PropTypes.shape({
-    id: PropTypes.string,
-    name: PropTypes.string,
-    linkedFileData: PropTypes.object,
-  }).isRequired,
-  storeReferencesKeys: PropTypes.func.isRequired,
+type UrlProviderProps = {
+  file: LinkedFile<'url'>
 }
 }
 
 
-function UrlProvider({ file }) {
+function UrlProvider({ file }: UrlProviderProps) {
   return (
   return (
     <p>
     <p>
       <LinkedFileIcon />
       <LinkedFileIcon />
@@ -181,14 +193,11 @@ function UrlProvider({ file }) {
   )
   )
 }
 }
 
 
-UrlProvider.propTypes = {
-  file: PropTypes.shape({
-    linkedFileData: PropTypes.object,
-    created: PropTypes.string,
-  }).isRequired,
+type ProjectFilePathProviderProps = {
+  file: LinkedFile<'project_file'>
 }
 }
 
 
-function ProjectFilePathProvider({ file }) {
+function ProjectFilePathProvider({ file }: ProjectFilePathProviderProps) {
   /* eslint-disable jsx-a11y/anchor-has-content, react/jsx-key */
   /* eslint-disable jsx-a11y/anchor-has-content, react/jsx-key */
   return (
   return (
     <p>
     <p>
@@ -218,14 +227,11 @@ function ProjectFilePathProvider({ file }) {
   )
   )
 }
 }
 
 
-ProjectFilePathProvider.propTypes = {
-  file: PropTypes.shape({
-    linkedFileData: PropTypes.object,
-    created: PropTypes.string,
-  }).isRequired,
+type ProjectOutputFileProviderProps = {
+  file: LinkedFile<'project_output_file'>
 }
 }
 
 
-function ProjectOutputFileProvider({ file }) {
+function ProjectOutputFileProvider({ file }: ProjectOutputFileProviderProps) {
   return (
   return (
     <p>
     <p>
       <LinkedFileIcon />
       <LinkedFileIcon />
@@ -252,10 +258,3 @@ function ProjectOutputFileProvider({ file }) {
     </p>
     </p>
   )
   )
 }
 }
-
-ProjectOutputFileProvider.propTypes = {
-  file: PropTypes.shape({
-    linkedFileData: PropTypes.object,
-    created: PropTypes.string,
-  }).isRequired,
-}

+ 44 - 0
services/web/frontend/js/features/file-view/types/binary-file.ts

@@ -0,0 +1,44 @@
+type LinkedFileData = {
+  url: {
+    provider: 'url'
+    url: string
+  }
+  zotero: {
+    provider: 'zotero'
+    importer_id: string
+  }
+  mendeley: {
+    provider: 'mendeley'
+    importer_id: string
+  }
+  project_file: {
+    provider: 'project_file'
+    v1_source_doc_id?: string
+    source_project_id: string
+    source_entity_path: string
+  }
+  project_output_file: {
+    provider: 'project_output_file'
+    v1_source_doc_id?: string
+    source_project_id: string
+    source_output_file_path: string
+  }
+}
+
+export type BinaryFile<T extends keyof LinkedFileData = keyof LinkedFileData> =
+  {
+    _id: string
+    name: string
+    created: Date
+    id: string
+    type: string
+    selected: boolean
+    linkedFileData?: LinkedFileData[T]
+  }
+
+export type LinkedFile<T extends keyof LinkedFileData> = Required<BinaryFile<T>>
+
+export const hasProvider = <T extends keyof LinkedFileData>(
+  file: BinaryFile,
+  provider: T
+): file is LinkedFile<T> => file.linkedFileData?.provider === provider

+ 1 - 1
services/web/test/frontend/features/file-view/components/file-view-header.test.js

@@ -8,7 +8,7 @@ import fetchMock from 'fetch-mock'
 import sinon from 'sinon'
 import sinon from 'sinon'
 
 
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
-import FileViewHeader from '../../../../../frontend/js/features/file-view/components/file-view-header.js'
+import FileViewHeader from '../../../../../frontend/js/features/file-view/components/file-view-header'
 
 
 describe('<FileViewHeader/>', function () {
 describe('<FileViewHeader/>', function () {
   const urlFile = {
   const urlFile = {

+ 1 - 0
services/web/types/window.ts

@@ -39,5 +39,6 @@ declare global {
       enterprise?: boolean
       enterprise?: boolean
       useRecaptchaNet?: boolean
       useRecaptchaNet?: boolean
     }
     }
+    expectingLinkedFileRefreshedSocketFor?: string | null
   }
   }
 }
 }