export-document-toasts.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { GlobalToastGeneratorEntry } from '@/features/ide-react/components/global-toasts'
  2. import { Trans, useTranslation } from 'react-i18next'
  3. const PreparingExportToast = () => {
  4. const { t } = useTranslation()
  5. return <span>{t('preparing_for_export')}</span>
  6. }
  7. const ExportDocumentErrorToast = ({ data }: { data?: any }) => {
  8. const { t } = useTranslation()
  9. const errorMessage =
  10. typeof data?.errorMessage === 'string' ? data.errorMessage : null
  11. return (
  12. <>
  13. <p>
  14. <b>{t('we_couldnt_export_this_document')}</b>
  15. </p>
  16. <Trans
  17. i18nKey="the_document_contains_formatting_we_werent_able_to_convert"
  18. components={[
  19. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  20. <a
  21. href="https://docs.overleaf.com/managing-projects-and-files/importing-and-exporting-files#common-issues-and-how-to-address-them"
  22. target="_BLANK"
  23. rel="noopener noreferrer"
  24. />,
  25. ]}
  26. />
  27. {errorMessage && (
  28. <details>
  29. <summary>{t('conversion_error_details')}</summary>
  30. <pre
  31. style={{ maxWidth: '800px', maxHeight: '300px', overflow: 'auto' }}
  32. >
  33. <code>{errorMessage}</code>
  34. </pre>
  35. </details>
  36. )}
  37. </>
  38. )
  39. }
  40. const ExportDocumentSuccessToast = ({ data }: { data?: any }) => {
  41. const type = data?.type
  42. if (type === 'docx') {
  43. return (
  44. <Trans
  45. i18nKey="docx_export_feedback_message"
  46. components={[
  47. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  48. <a
  49. href="https://forms.gle/Fg4BUXV2yv61hStX8"
  50. target="_BLANK"
  51. rel="noopener noreferrer"
  52. />,
  53. ]}
  54. />
  55. )
  56. } else if (type === 'markdown') {
  57. return (
  58. <Trans
  59. i18nKey="markdown_export_feedback_message"
  60. components={[
  61. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  62. <a
  63. href="https://forms.gle/wc43zEukeqpec9mAA"
  64. target="_BLANK"
  65. rel="noopener noreferrer"
  66. />,
  67. ]}
  68. />
  69. )
  70. } else if (type === 'html') {
  71. return (
  72. <Trans
  73. i18nKey="html_export_feedback_message"
  74. components={[
  75. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  76. <a
  77. href="https://forms.gle/nBUVGqPYwLcWHSmt5"
  78. target="_BLANK"
  79. rel="noopener noreferrer"
  80. />,
  81. ]}
  82. />
  83. )
  84. } else {
  85. return (
  86. <Trans
  87. i18nKey="generic_export_feedback_message"
  88. components={[
  89. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  90. <a href="/contact" target="_BLANK" rel="noopener noreferrer" />,
  91. ]}
  92. />
  93. )
  94. }
  95. }
  96. const generators: GlobalToastGeneratorEntry[] = [
  97. {
  98. key: 'export-document:error',
  99. generator: (data: any) => ({
  100. content: <ExportDocumentErrorToast data={data} />,
  101. type: 'error',
  102. // Only auto-hide if we have no extra details
  103. autoHide: !data?.errorMessage,
  104. delay: 5000,
  105. isDismissible: true,
  106. }),
  107. },
  108. {
  109. key: 'export-document:preparing',
  110. generator: () => ({
  111. content: <PreparingExportToast />,
  112. type: 'info',
  113. autoHide: false,
  114. isDismissible: true,
  115. }),
  116. },
  117. {
  118. key: 'export-document:success',
  119. generator: (data: any) => ({
  120. content: <ExportDocumentSuccessToast data={data} />,
  121. type: 'success',
  122. autoHide: true,
  123. delay: 45000,
  124. isDismissible: true,
  125. }),
  126. },
  127. ]
  128. export default generators
  129. // We only ever care about the latest error toast, so use a static handle.
  130. const EXPORT_DOCUMENT_ERROR_HANDLE = 'export-document-error'
  131. export const showExportDocumentError = (errorMessage?: string) => {
  132. window.dispatchEvent(
  133. new CustomEvent('ide:show-toast', {
  134. detail: {
  135. key: 'export-document:error',
  136. handle: EXPORT_DOCUMENT_ERROR_HANDLE,
  137. errorMessage,
  138. },
  139. })
  140. )
  141. }
  142. export const hideExportDocumentError = () => {
  143. window.dispatchEvent(
  144. new CustomEvent('ide:dismiss-toast', {
  145. detail: { handle: EXPORT_DOCUMENT_ERROR_HANDLE },
  146. })
  147. )
  148. }
  149. export const showPreparingExportToast = () => {
  150. const handle = `export-document-preparing-${Date.now()}`
  151. window.dispatchEvent(
  152. new CustomEvent('ide:show-toast', {
  153. detail: { key: 'export-document:preparing', handle },
  154. })
  155. )
  156. return handle
  157. }
  158. export const hidePreparingExportToast = (handle: string) => {
  159. window.dispatchEvent(
  160. new CustomEvent('ide:dismiss-toast', {
  161. detail: { key: 'export-document:preparing', handle },
  162. })
  163. )
  164. }
  165. export const showExportDocumentSuccess = (
  166. type: 'docx' | 'markdown' | 'html'
  167. ) => {
  168. window.dispatchEvent(
  169. new CustomEvent('ide:show-toast', {
  170. detail: { key: 'export-document:success', type },
  171. })
  172. )
  173. }