dictionary-modal-content.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { useCallback, useState } from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import useAsync from '../../../shared/hooks/use-async'
  4. import { postJSON } from '../../../infrastructure/fetch-json'
  5. import { debugConsole } from '@/utils/debugging'
  6. import {
  7. OLModalBody,
  8. OLModalFooter,
  9. OLModalHeader,
  10. OLModalTitle,
  11. } from '@/shared/components/ol/ol-modal'
  12. import OLTooltip from '@/shared/components/ol/ol-tooltip'
  13. import OLNotification from '@/shared/components/ol/ol-notification'
  14. import OLButton from '@/shared/components/ol/ol-button'
  15. import OLIconButton from '@/shared/components/ol/ol-icon-button'
  16. import { learnedWords as initialLearnedWords } from '@/features/source-editor/extensions/spelling/learned-words'
  17. type DictionaryModalContentProps = {
  18. handleHide: () => void
  19. }
  20. const wordsSortFunction = (a: string, b: string) => a.localeCompare(b)
  21. export default function DictionaryModalContent({
  22. handleHide,
  23. }: DictionaryModalContentProps) {
  24. const { t } = useTranslation()
  25. const [learnedWords, setLearnedWords] = useState<Set<string>>(
  26. initialLearnedWords.global
  27. )
  28. const { isError, runAsync } = useAsync()
  29. const handleRemove = useCallback(
  30. (word: string) => {
  31. runAsync(postJSON('/spelling/unlearn', { body: { word } }))
  32. .then(() => {
  33. setLearnedWords(prevLearnedWords => {
  34. const learnedWords = new Set(prevLearnedWords)
  35. learnedWords.delete(word)
  36. return learnedWords
  37. })
  38. window.dispatchEvent(
  39. new CustomEvent('editor:remove-learned-word', { detail: word })
  40. )
  41. })
  42. .catch(debugConsole.error)
  43. },
  44. [runAsync]
  45. )
  46. return (
  47. <>
  48. <OLModalHeader>
  49. <OLModalTitle>{t('edit_dictionary')}</OLModalTitle>
  50. </OLModalHeader>
  51. <OLModalBody>
  52. {isError ? (
  53. <OLNotification
  54. type="error"
  55. content={t('generic_something_went_wrong')}
  56. />
  57. ) : null}
  58. {learnedWords.size > 0 ? (
  59. <ul className="list-unstyled dictionary-entries-list">
  60. {[...learnedWords].sort(wordsSortFunction).map(learnedWord => (
  61. <li key={learnedWord} className="dictionary-entry">
  62. <span className="dictionary-entry-name" translate="no">
  63. {learnedWord}
  64. </span>
  65. <OLTooltip
  66. id={`tooltip-remove-learned-word-${learnedWord}`}
  67. description={t('edit_dictionary_remove')}
  68. overlayProps={{ delay: 0 }}
  69. >
  70. <OLIconButton
  71. variant="danger"
  72. size="sm"
  73. onClick={() => handleRemove(learnedWord)}
  74. icon="delete"
  75. accessibilityLabel={t('edit_dictionary_remove')}
  76. />
  77. </OLTooltip>
  78. </li>
  79. ))}
  80. </ul>
  81. ) : (
  82. <p className="dictionary-empty-body text-center">
  83. <i>{t('edit_dictionary_empty')}</i>
  84. </p>
  85. )}
  86. </OLModalBody>
  87. <OLModalFooter>
  88. <OLButton variant="secondary" onClick={handleHide}>
  89. {t('close')}
  90. </OLButton>
  91. </OLModalFooter>
  92. </>
  93. )
  94. }