file-view-refresh-button.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {
  2. type Dispatch,
  3. type SetStateAction,
  4. type ElementType,
  5. useCallback,
  6. useState,
  7. } from 'react'
  8. import { useTranslation } from 'react-i18next'
  9. import Icon from '@/shared/components/icon'
  10. import { postJSON } from '@/infrastructure/fetch-json'
  11. import { useProjectContext } from '@/shared/context/project-context'
  12. import useAbortController from '@/shared/hooks/use-abort-controller'
  13. import type { BinaryFile } from '../types/binary-file'
  14. import { Nullable } from '../../../../../types/utils'
  15. import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
  16. import OLButton from '@/features/ui/components/ol/ol-button'
  17. type FileViewRefreshButtonProps = {
  18. setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
  19. file: BinaryFile
  20. }
  21. const tprFileViewRefreshButton = importOverleafModules(
  22. 'tprFileViewRefreshButton'
  23. ) as {
  24. import: { TPRFileViewRefreshButton: ElementType }
  25. path: string
  26. }[]
  27. export default function FileViewRefreshButton({
  28. setRefreshError,
  29. file,
  30. }: FileViewRefreshButtonProps) {
  31. const { _id: projectId } = useProjectContext()
  32. const { signal } = useAbortController()
  33. const [refreshing, setRefreshing] = useState(false)
  34. const refreshFile = useCallback(
  35. (isTPR: Nullable<boolean>) => {
  36. setRefreshing(true)
  37. // Replacement of the file handled by the file tree
  38. window.expectingLinkedFileRefreshedSocketFor = file.name
  39. const body = {
  40. shouldReindexReferences: isTPR || /\.bib$/.test(file.name),
  41. }
  42. postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
  43. signal,
  44. body,
  45. })
  46. .then(() => {
  47. setRefreshing(false)
  48. })
  49. .catch(err => {
  50. setRefreshing(false)
  51. setRefreshError(err.data?.message || err.message)
  52. })
  53. },
  54. [file, projectId, signal, setRefreshError]
  55. )
  56. if (tprFileViewRefreshButton.length > 0) {
  57. return tprFileViewRefreshButton.map(
  58. ({ import: { TPRFileViewRefreshButton }, path }) => (
  59. <TPRFileViewRefreshButton
  60. key={path}
  61. file={file}
  62. refreshFile={refreshFile}
  63. refreshing={refreshing}
  64. />
  65. )
  66. )[0]
  67. } else {
  68. return (
  69. <FileViewRefreshButtonDefault
  70. refreshFile={refreshFile}
  71. refreshing={refreshing}
  72. />
  73. )
  74. }
  75. }
  76. type FileViewRefreshButtonDefaultProps = {
  77. refreshFile: (isTPR: Nullable<boolean>) => void
  78. refreshing: boolean
  79. }
  80. function FileViewRefreshButtonDefault({
  81. refreshFile,
  82. refreshing,
  83. }: FileViewRefreshButtonDefaultProps) {
  84. const { t } = useTranslation()
  85. return (
  86. <OLButton
  87. variant="primary"
  88. onClick={() => refreshFile(null)}
  89. disabled={refreshing}
  90. isLoading={refreshing}
  91. bs3Props={{
  92. loading: (
  93. <>
  94. <Icon type="refresh" spin={refreshing} fw />{' '}
  95. <span>{refreshing ? `${t('refreshing')}…` : t('refresh')}</span>
  96. </>
  97. ),
  98. }}
  99. >
  100. {t('refresh')}
  101. </OLButton>
  102. )
  103. }