word-count-client.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { FC, useEffect, useMemo, useState } from 'react'
  2. import { WordCountData } from '@/features/word-count-modal/components/word-count-data'
  3. import { WordCountError } from '@/features/word-count-modal/components/word-count-error'
  4. import { useProjectContext } from '@/shared/context/project-context'
  5. import useAbortController from '@/shared/hooks/use-abort-controller'
  6. import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
  7. import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
  8. import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
  9. import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
  10. import { debugConsole } from '@/utils/debugging'
  11. import { signalWithTimeout } from '@/utils/abort-signal'
  12. import { isMainFile } from '@/features/pdf-preview/util/editor-files'
  13. import { countWordsInFile } from '@/features/word-count-modal/utils/count-words-in-file'
  14. import { createSegmenters } from '@/features/word-count-modal/utils/segmenters'
  15. import { WordCountsClient } from './word-counts-client'
  16. import LoadingSpinner from '@/shared/components/loading-spinner'
  17. export const WordCountClient: FC = () => {
  18. const [loading, setLoading] = useState(true)
  19. const [error, setError] = useState(false)
  20. const [data, setData] = useState<WordCountData | null>(null)
  21. const { projectSnapshot, project } = useProjectContext()
  22. const rootDocId = project?.rootDocId
  23. const { spellCheckLanguage } = useProjectSettingsContext()
  24. const { openDocs } = useEditorManagerContext()
  25. const { currentDocument } = useEditorOpenDocContext()
  26. const { pathInFolder } = useFileTreePathContext()
  27. const { signal } = useAbortController()
  28. const segmenters = useMemo(() => {
  29. return createSegmenters(spellCheckLanguage?.replace(/_/, '-'))
  30. }, [spellCheckLanguage])
  31. useEffect(() => {
  32. if (currentDocument && segmenters) {
  33. const countWords = async () => {
  34. await openDocs.awaitBufferedOps(signalWithTimeout(signal, 5000))
  35. await projectSnapshot.refresh()
  36. if (signal.aborted) return null
  37. const currentDocSnapshot = currentDocument.getSnapshot()
  38. const currentRootDocId = isMainFile(currentDocSnapshot)
  39. ? currentDocument.doc_id
  40. : rootDocId
  41. if (!currentRootDocId) return null
  42. const currentRootDocPath = pathInFolder(currentRootDocId)
  43. if (!currentRootDocPath) return null
  44. const data: WordCountData = {
  45. encode: 'ascii',
  46. textWords: 0,
  47. textCharacters: 0,
  48. headWords: 0,
  49. headCharacters: 0,
  50. abstractWords: 0,
  51. abstractCharacters: 0,
  52. captionWords: 0,
  53. captionCharacters: 0,
  54. footnoteWords: 0,
  55. footnoteCharacters: 0,
  56. outside: 0,
  57. otherWords: 0,
  58. otherCharacters: 0,
  59. headers: 0,
  60. elements: 0,
  61. mathInline: 0,
  62. mathDisplay: 0,
  63. errors: 0,
  64. messages: '',
  65. }
  66. countWordsInFile(
  67. data,
  68. projectSnapshot,
  69. currentRootDocPath,
  70. '/',
  71. segmenters
  72. )
  73. return data
  74. }
  75. countWords()
  76. .then(data => {
  77. setData(data)
  78. })
  79. .catch(error => {
  80. debugConsole.error(error)
  81. setError(true)
  82. })
  83. .finally(() => {
  84. setLoading(false)
  85. })
  86. }
  87. }, [
  88. signal,
  89. openDocs,
  90. projectSnapshot,
  91. segmenters,
  92. currentDocument,
  93. rootDocId,
  94. pathInFolder,
  95. ])
  96. return (
  97. <>
  98. {loading && !error && <LoadingSpinner />}
  99. {error && <WordCountError />}
  100. {data && <WordCountsClient data={data} />}
  101. </>
  102. )
  103. }