file-view.jsx 2.2 KB

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