file-view-refresh-button.tsx 3.2 KB

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