file-view-text.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { useState, useEffect } from 'react'
  2. import { useProjectContext } from '../../../shared/context/project-context'
  3. import { debugConsole } from '@/utils/debugging'
  4. import useAbortController from '../../../shared/hooks/use-abort-controller'
  5. const MAX_FILE_SIZE = 2 * 1024 * 1024
  6. export default function FileViewText({
  7. file,
  8. onLoad,
  9. onError,
  10. }: {
  11. file: { id: string }
  12. onLoad: () => void
  13. onError: () => void
  14. }) {
  15. const { _id: projectId } = useProjectContext()
  16. const [textPreview, setTextPreview] = useState('')
  17. const [shouldShowDots, setShouldShowDots] = useState(false)
  18. const [inFlight, setInFlight] = useState(false)
  19. const fetchContentLengthController = useAbortController()
  20. const fetchDataController = useAbortController()
  21. useEffect(() => {
  22. if (inFlight) {
  23. return
  24. }
  25. let path = `/project/${projectId}/file/${file.id}`
  26. const fetchContentLengthTimeout = setTimeout(
  27. () => fetchContentLengthController.abort(),
  28. 10000
  29. )
  30. let fetchDataTimeout: number | undefined
  31. fetch(path, { method: 'HEAD', signal: fetchContentLengthController.signal })
  32. .then(response => {
  33. if (!response.ok) throw new Error('HTTP Error Code: ' + response.status)
  34. return response.headers.get('Content-Length')
  35. })
  36. .then(fileSize => {
  37. let truncated = false
  38. let maxSize = null
  39. if (fileSize && Number(fileSize) > MAX_FILE_SIZE) {
  40. truncated = true
  41. maxSize = MAX_FILE_SIZE
  42. }
  43. if (maxSize != null) {
  44. path += `?range=0-${maxSize}`
  45. }
  46. fetchDataTimeout = window.setTimeout(
  47. () => fetchDataController.abort(),
  48. 60000
  49. )
  50. return fetch(path, { signal: fetchDataController.signal }).then(
  51. response => {
  52. return response.text().then(text => {
  53. if (truncated) {
  54. text = text.replace(/\n.*$/, '')
  55. }
  56. setTextPreview(text)
  57. onLoad()
  58. setShouldShowDots(truncated)
  59. })
  60. }
  61. )
  62. })
  63. .catch(err => {
  64. debugConsole.error('Error fetching file contents', err)
  65. onError()
  66. })
  67. .finally(() => {
  68. setInFlight(false)
  69. clearTimeout(fetchContentLengthTimeout)
  70. clearTimeout(fetchDataTimeout)
  71. })
  72. }, [
  73. projectId,
  74. file.id,
  75. onError,
  76. onLoad,
  77. inFlight,
  78. fetchContentLengthController,
  79. fetchDataController,
  80. ])
  81. return (
  82. <div>
  83. {textPreview && (
  84. <div className="text-preview">
  85. <div className="scroll-container">
  86. <p>{textPreview}</p>
  87. {shouldShowDots && <p>...</p>}
  88. </div>
  89. </div>
  90. )}
  91. </div>
  92. )
  93. }