file-view-refresh-button.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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. type FileViewRefreshButtonProps = {
  17. setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
  18. file: BinaryFile
  19. }
  20. const tprFileViewRefreshButton = importOverleafModules(
  21. 'tprFileViewRefreshButton'
  22. ) as {
  23. import: { TPRFileViewRefreshButton: ElementType }
  24. path: string
  25. }[]
  26. export default function FileViewRefreshButton({
  27. setRefreshError,
  28. file,
  29. }: FileViewRefreshButtonProps) {
  30. const { _id: projectId } = useProjectContext()
  31. const { signal } = useAbortController()
  32. const [refreshing, setRefreshing] = useState(false)
  33. const refreshFile = useCallback(
  34. (isTPR: Nullable<boolean>) => {
  35. setRefreshing(true)
  36. // Replacement of the file handled by the file tree
  37. window.expectingLinkedFileRefreshedSocketFor = file.name
  38. const body = {
  39. shouldReindexReferences: isTPR || /\.bib$/.test(file.name),
  40. }
  41. postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
  42. signal,
  43. body,
  44. })
  45. .then(() => {
  46. setRefreshing(false)
  47. })
  48. .catch(err => {
  49. setRefreshing(false)
  50. setRefreshError(err.data?.message || err.message)
  51. })
  52. },
  53. [file, projectId, signal, setRefreshError]
  54. )
  55. if (tprFileViewRefreshButton.length > 0) {
  56. return tprFileViewRefreshButton.map(
  57. ({ import: { TPRFileViewRefreshButton }, path }) => (
  58. <TPRFileViewRefreshButton
  59. key={path}
  60. file={file}
  61. refreshFile={refreshFile}
  62. refreshing={refreshing}
  63. />
  64. )
  65. )[0]
  66. } else {
  67. return (
  68. <FileViewRefreshButtonDefault
  69. refreshFile={refreshFile}
  70. refreshing={refreshing}
  71. />
  72. )
  73. }
  74. }
  75. type FileViewRefreshButtonDefaultProps = {
  76. refreshFile: (isTPR: Nullable<boolean>) => void
  77. refreshing: boolean
  78. }
  79. function FileViewRefreshButtonDefault({
  80. refreshFile,
  81. refreshing,
  82. }: FileViewRefreshButtonDefaultProps) {
  83. const { t } = useTranslation()
  84. return (
  85. <button
  86. className="btn btn-primary"
  87. onClick={() => refreshFile(null)}
  88. disabled={refreshing}
  89. >
  90. <Icon type="refresh" spin={refreshing} fw />
  91. <span>{refreshing ? `${t('refreshing')}…` : t('refresh')}</span>
  92. </button>
  93. )
  94. }