pdf-preview-error.tsx 11 KB

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