file-view.jsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { useState, useCallback } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useTranslation } from 'react-i18next'
  4. import FileViewHeader from './file-view-header'
  5. import FileViewImage from './file-view-image'
  6. import FileViewPdf from './file-view-pdf'
  7. import FileViewText from './file-view-text'
  8. import Icon from '../../../shared/components/icon'
  9. const imageExtensions = ['png', 'jpg', 'jpeg', 'gif']
  10. export default function FileView({ file }) {
  11. const [contentLoading, setContentLoading] = useState(true)
  12. const [hasError, setHasError] = useState(false)
  13. const { t } = useTranslation()
  14. const { textExtensions, editableFilenames } = window.ExposedSettings
  15. const extension = file.name.split('.').pop().toLowerCase()
  16. const isEditableTextFile =
  17. textExtensions.includes(extension) ||
  18. editableFilenames.includes(file.name.toLowerCase())
  19. const isImageFile = imageExtensions.includes(extension)
  20. const isPdfFile = extension === 'pdf'
  21. const isUnpreviewableFile = !isEditableTextFile && !isImageFile && !isPdfFile
  22. const handleLoad = useCallback(() => {
  23. setContentLoading(false)
  24. }, [])
  25. const handleError = useCallback(() => {
  26. if (!hasError) {
  27. setContentLoading(false)
  28. setHasError(true)
  29. }
  30. }, [hasError])
  31. const content = (
  32. <>
  33. <FileViewHeader file={file} />
  34. {isImageFile && (
  35. <FileViewImage
  36. fileName={file.name}
  37. fileId={file.id}
  38. onLoad={handleLoad}
  39. onError={handleError}
  40. />
  41. )}
  42. {isEditableTextFile && (
  43. <FileViewText file={file} onLoad={handleLoad} onError={handleError} />
  44. )}
  45. {isPdfFile && (
  46. <FileViewPdf
  47. fileId={file.id}
  48. onLoad={handleLoad}
  49. onError={handleError}
  50. />
  51. )}
  52. </>
  53. )
  54. return (
  55. <div className="file-view full-size">
  56. {!hasError && content}
  57. {!isUnpreviewableFile && contentLoading && <FileViewLoadingIndicator />}
  58. {(isUnpreviewableFile || hasError) && (
  59. <p className="no-preview">{t('no_preview_available')}</p>
  60. )}
  61. </div>
  62. )
  63. }
  64. function FileViewLoadingIndicator() {
  65. const { t } = useTranslation()
  66. return (
  67. <div className="loading-panel loading-panel-file-view">
  68. <span>
  69. <Icon type="refresh" spin />
  70. &nbsp;&nbsp;{t('loading')}…
  71. </span>
  72. </div>
  73. )
  74. }
  75. FileView.propTypes = {
  76. file: PropTypes.shape({
  77. id: PropTypes.string,
  78. name: PropTypes.string,
  79. }).isRequired,
  80. }