pdf-preview-error.tsx 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import { useTranslation, Trans } from 'react-i18next'
  2. import { memo, useCallback } from 'react'
  3. import OLButton from '@/features/ui/components/ol/ol-button'
  4. import PdfLogEntry from './pdf-log-entry'
  5. import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
  6. import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
  7. import getMeta from '../../../utils/meta'
  8. function PdfPreviewError({
  9. error,
  10. includeWarnings = true,
  11. includeErrors = true,
  12. }: {
  13. error: string
  14. includeWarnings?: boolean
  15. includeErrors?: boolean
  16. }) {
  17. const { t } = useTranslation()
  18. const { startCompile } = useCompileContext()
  19. switch (error) {
  20. case 'rendering-error-expected':
  21. return (
  22. includeWarnings && (
  23. <PdfLogEntry
  24. headerTitle={t('pdf_rendering_error')}
  25. formattedContent={
  26. <>
  27. <Trans
  28. i18nKey="something_went_wrong_rendering_pdf_expected"
  29. components={[
  30. // eslint-disable-next-line react/jsx-key
  31. <OLButton
  32. variant="primary"
  33. size="sm"
  34. onClick={() => startCompile()}
  35. />,
  36. ]}
  37. />
  38. <br />
  39. <br />
  40. <Trans
  41. i18nKey="last_resort_trouble_shooting_guide"
  42. components={[
  43. // eslint-disable-next-line jsx-a11y/anchor-has-content
  44. <a
  45. href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
  46. target="_blank"
  47. key="troubleshooting-link"
  48. />,
  49. ]}
  50. />
  51. </>
  52. }
  53. level="warning"
  54. />
  55. )
  56. )
  57. case 'rendering-error':
  58. return (
  59. includeErrors && (
  60. <ErrorLogEntry title={t('pdf_rendering_error')}>
  61. {t('something_went_wrong_rendering_pdf')}
  62. &nbsp;
  63. <Trans
  64. i18nKey="try_recompile_project_or_troubleshoot"
  65. components={[
  66. // eslint-disable-next-line jsx-a11y/anchor-has-content
  67. <a
  68. href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
  69. target="_blank"
  70. key="troubleshooting-link"
  71. />,
  72. ]}
  73. />
  74. {getMeta('ol-compilesUserContentDomain') && (
  75. <>
  76. <br />
  77. <br />
  78. <Trans
  79. i18nKey="new_compile_domain_notice"
  80. values={{
  81. compilesUserContentDomain: new URL(
  82. getMeta('ol-compilesUserContentDomain')
  83. ).hostname,
  84. }}
  85. shouldUnescape
  86. tOptions={{ interpolation: { escapeValue: true } }}
  87. components={[
  88. <code key="domain" />,
  89. /* eslint-disable-next-line jsx-a11y/anchor-has-content */
  90. <a
  91. href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
  92. target="_blank"
  93. key="troubleshooting-link"
  94. />,
  95. ]}
  96. />
  97. </>
  98. )}
  99. </ErrorLogEntry>
  100. )
  101. )
  102. case 'clsi-maintenance':
  103. return (
  104. includeErrors && (
  105. <ErrorLogEntry title={t('server_error')}>
  106. {t('clsi_maintenance')}
  107. </ErrorLogEntry>
  108. )
  109. )
  110. case 'clsi-unavailable':
  111. return (
  112. includeErrors && (
  113. <ErrorLogEntry title={t('server_error')}>
  114. {t('clsi_unavailable')}
  115. </ErrorLogEntry>
  116. )
  117. )
  118. case 'too-recently-compiled':
  119. return (
  120. includeErrors && (
  121. <ErrorLogEntry title={t('server_error')}>
  122. {t('too_recently_compiled')}
  123. </ErrorLogEntry>
  124. )
  125. )
  126. case 'terminated':
  127. return (
  128. includeErrors && (
  129. <ErrorLogEntry title={t('terminated')}>
  130. {t('compile_terminated_by_user')}
  131. </ErrorLogEntry>
  132. )
  133. )
  134. case 'rate-limited':
  135. return (
  136. includeErrors && (
  137. <ErrorLogEntry title={t('pdf_compile_rate_limit_hit')}>
  138. {t('project_flagged_too_many_compiles')}
  139. </ErrorLogEntry>
  140. )
  141. )
  142. case 'compile-in-progress':
  143. return (
  144. includeErrors && (
  145. <ErrorLogEntry title={t('pdf_compile_in_progress_error')}>
  146. {t('pdf_compile_try_again')}
  147. </ErrorLogEntry>
  148. )
  149. )
  150. case 'autocompile-disabled':
  151. return (
  152. includeErrors && (
  153. <ErrorLogEntry title={t('autocompile_disabled')}>
  154. {t('autocompile_disabled_reason')}
  155. </ErrorLogEntry>
  156. )
  157. )
  158. case 'project-too-large':
  159. return (
  160. includeErrors && (
  161. <ErrorLogEntry title={t('project_too_large')}>
  162. {t('project_too_much_editable_text')}
  163. </ErrorLogEntry>
  164. )
  165. )
  166. case 'timedout':
  167. return includeErrors && <TimedOutLogEntry />
  168. case 'failure':
  169. return (
  170. includeErrors && (
  171. <ErrorLogEntry title={t('no_pdf_error_title')}>
  172. {t('no_pdf_error_explanation')}
  173. <ul className="my-1 ps-3">
  174. <li>{t('no_pdf_error_reason_unrecoverable_error')}</li>
  175. <li>
  176. <Trans
  177. i18nKey="no_pdf_error_reason_no_content"
  178. components={{ code: <code /> }}
  179. />
  180. </li>
  181. <li>
  182. <Trans
  183. i18nKey="no_pdf_error_reason_output_pdf_already_exists"
  184. components={{ code: <code /> }}
  185. />
  186. </li>
  187. </ul>
  188. </ErrorLogEntry>
  189. )
  190. )
  191. case 'clear-cache':
  192. return (
  193. includeErrors && (
  194. <ErrorLogEntry title={t('server_error')}>
  195. {t('somthing_went_wrong_compiling')}
  196. </ErrorLogEntry>
  197. )
  198. )
  199. case 'pdf-viewer-loading-error':
  200. return (
  201. includeErrors && (
  202. <ErrorLogEntry title={t('pdf_rendering_error')}>
  203. <Trans
  204. i18nKey="something_went_wrong_loading_pdf_viewer"
  205. components={[
  206. <strong key="strong-" />,
  207. // eslint-disable-next-line jsx-a11y/anchor-has-content
  208. <a
  209. href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
  210. target="_blank"
  211. key="troubleshooting-link"
  212. />,
  213. // eslint-disable-next-line jsx-a11y/anchor-has-content
  214. <a key="contact-link" target="_blank" href="/contact" />,
  215. ]}
  216. />
  217. </ErrorLogEntry>
  218. )
  219. )
  220. case 'validation-problems':
  221. return null // handled elsewhere
  222. case 'error':
  223. default:
  224. return (
  225. includeErrors && (
  226. <ErrorLogEntry title={t('server_error')}>
  227. {t('somthing_went_wrong_compiling')}
  228. </ErrorLogEntry>
  229. )
  230. )
  231. }
  232. }
  233. export default memo(PdfPreviewError)
  234. function ErrorLogEntry({
  235. title,
  236. headerIcon,
  237. children,
  238. }: {
  239. title: string
  240. headerIcon?: React.ReactElement
  241. children: React.ReactNode
  242. }) {
  243. const { t } = useTranslation()
  244. return (
  245. <PdfLogEntry
  246. headerTitle={title}
  247. headerIcon={headerIcon}
  248. formattedContent={children}
  249. entryAriaLabel={t('compile_error_entry_description')}
  250. level="error"
  251. />
  252. )
  253. }
  254. function TimedOutLogEntry() {
  255. const { t } = useTranslation()
  256. const { enableStopOnFirstError } = useStopOnFirstError({
  257. eventSource: 'timeout',
  258. })
  259. const { startCompile, lastCompileOptions, setAnimateCompileDropdownArrow } =
  260. useCompileContext()
  261. const handleEnableStopOnFirstErrorClick = useCallback(() => {
  262. enableStopOnFirstError()
  263. startCompile({ stopOnFirstError: true })
  264. setAnimateCompileDropdownArrow(true)
  265. }, [enableStopOnFirstError, startCompile, setAnimateCompileDropdownArrow])
  266. return (
  267. <ErrorLogEntry title={t('timedout')}>
  268. <p>{t('project_timed_out_intro')}</p>
  269. <ul>
  270. <li>
  271. <Trans
  272. i18nKey="project_timed_out_optimize_images"
  273. components={[
  274. // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
  275. <a href="https://www.overleaf.com/learn/how-to/Optimising_very_large_image_files" />,
  276. ]}
  277. />
  278. </li>
  279. <li>
  280. <Trans
  281. i18nKey="project_timed_out_fatal_error"
  282. components={[
  283. // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
  284. <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F#Fatal_compile_errors_blocking_the_compilation" />,
  285. ]}
  286. />
  287. {!lastCompileOptions.stopOnFirstError && (
  288. <>
  289. {' '}
  290. <Trans
  291. i18nKey="project_timed_out_enable_stop_on_first_error"
  292. components={[
  293. // eslint-disable-next-line react/jsx-key
  294. <OLButton
  295. variant="primary"
  296. size="sm"
  297. onClick={handleEnableStopOnFirstErrorClick}
  298. />,
  299. ]}
  300. />{' '}
  301. </>
  302. )}
  303. </li>
  304. </ul>
  305. <p>
  306. <Trans
  307. i18nKey="project_timed_out_learn_more"
  308. components={[
  309. // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
  310. <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F" />,
  311. ]}
  312. />
  313. </p>
  314. </ErrorLogEntry>
  315. )
  316. }