compile-timeout-messages.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { memo, useCallback, useEffect, useState, useMemo } from 'react'
  2. import * as eventTracking from '../../../infrastructure/event-tracking'
  3. import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
  4. import usePersistedState from '../../../shared/hooks/use-persisted-state'
  5. import Notification from '../../../shared/components/notification'
  6. import { Trans, useTranslation } from 'react-i18next'
  7. import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
  8. function CompileTimeoutMessages() {
  9. const {
  10. showNewCompileTimeoutUI,
  11. isProjectOwner,
  12. deliveryLatencies,
  13. compiling,
  14. showLogs,
  15. error,
  16. } = useDetachCompileContext()
  17. const { t } = useTranslation()
  18. const [showWarning, setShowWarning] = useState(false)
  19. const [showChangingSoon, setShowChangingSoon] = useState(false)
  20. const [dismissedUntilWarning, setDismissedUntilWarning] = usePersistedState<
  21. Date | undefined
  22. >(`has-dismissed-10s-compile-time-warning-until`)
  23. const segmentation = useMemo(() => {
  24. return {
  25. newCompileTimeout: showNewCompileTimeoutUI,
  26. isProjectOwner,
  27. }
  28. }, [showNewCompileTimeoutUI, isProjectOwner])
  29. const handleNewCompile = useCallback(
  30. compileTime => {
  31. setShowWarning(false)
  32. setShowChangingSoon(false)
  33. if (compileTime > 20000) {
  34. if (showNewCompileTimeoutUI === 'changing') {
  35. setShowChangingSoon(true)
  36. eventTracking.sendMB('compile-time-warning-displayed', {
  37. time: 20,
  38. ...segmentation,
  39. })
  40. }
  41. } else if (compileTime > 10000) {
  42. setShowChangingSoon(false)
  43. if (
  44. (isProjectOwner && showNewCompileTimeoutUI === 'active') ||
  45. showNewCompileTimeoutUI === 'changing'
  46. ) {
  47. if (
  48. !dismissedUntilWarning ||
  49. new Date(dismissedUntilWarning) < new Date()
  50. ) {
  51. setShowWarning(true)
  52. eventTracking.sendMB('compile-time-warning-displayed', {
  53. time: 10,
  54. ...segmentation,
  55. })
  56. }
  57. }
  58. }
  59. },
  60. [
  61. isProjectOwner,
  62. showNewCompileTimeoutUI,
  63. dismissedUntilWarning,
  64. segmentation,
  65. ]
  66. )
  67. const handleDismissWarning = useCallback(() => {
  68. eventTracking.sendMB('compile-time-warning-dismissed', {
  69. time: 10,
  70. ...segmentation,
  71. })
  72. setShowWarning(false)
  73. const until = new Date()
  74. until.setDate(until.getDate() + 1) // 1 day
  75. setDismissedUntilWarning(until)
  76. }, [setDismissedUntilWarning, segmentation])
  77. const handleDismissChangingSoon = useCallback(() => {
  78. eventTracking.sendMB('compile-time-warning-dismissed', {
  79. time: 20,
  80. ...segmentation,
  81. })
  82. }, [segmentation])
  83. useEffect(() => {
  84. if (compiling || error || showLogs) return
  85. handleNewCompile(deliveryLatencies.compileTimeServerE2E)
  86. }, [compiling, error, showLogs, deliveryLatencies, handleNewCompile])
  87. if (!window.ExposedSettings.enableSubscriptions) {
  88. return null
  89. }
  90. if (compiling || error || showLogs) {
  91. return null
  92. }
  93. if (!showWarning && !showChangingSoon) {
  94. return null
  95. }
  96. function sendInfoClickEvent() {
  97. eventTracking.sendMB('paywall-info-click', {
  98. 'paywall-type': 'compile-time-warning',
  99. content: 'blog',
  100. })
  101. }
  102. const compileTimeoutBlogLinks = [
  103. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  104. <a
  105. aria-label={t('read_more_about_free_compile_timeouts_servers')}
  106. href="/blog/changes-to-free-compile-timeouts-and-servers"
  107. key="compileTimeoutBlogLink1"
  108. rel="noopener noreferrer"
  109. target="_blank"
  110. onClick={sendInfoClickEvent}
  111. />,
  112. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  113. <a
  114. aria-label={t('read_more_about_fix_prevent_timeout')}
  115. href="/learn/how-to/Fixing_and_preventing_compile_timeouts"
  116. key="compileTimeoutBlogLink2"
  117. target="_blank"
  118. rel="noopener noreferrer"
  119. />,
  120. ]
  121. // if showWarning is true then the 10s warning is shown
  122. // and if showChangingSoon is true then the 20s-60s should show
  123. return (
  124. <div>
  125. {showWarning && isProjectOwner && (
  126. <Notification
  127. action={
  128. <StartFreeTrialButton
  129. variant="new-10s"
  130. source="compile-time-warning"
  131. buttonProps={{
  132. className: 'btn-secondary-compile-timeout-override',
  133. }}
  134. >
  135. {t('start_free_trial_without_exclamation')}
  136. </StartFreeTrialButton>
  137. }
  138. ariaLive="polite"
  139. content={
  140. <div>
  141. <div>
  142. <span>{t('your_project_near_compile_timeout_limit')}</span>
  143. </div>
  144. {showNewCompileTimeoutUI === 'active' ? (
  145. <>
  146. <strong>{t('upgrade_for_12x_more_compile_time')}</strong>
  147. {'. '}
  148. </>
  149. ) : (
  150. <strong>{t('upgrade_for_plenty_more_compile_time')}</strong>
  151. )}
  152. </div>
  153. }
  154. type="warning"
  155. title={t('took_a_while')}
  156. isActionBelowContent
  157. isDismissible
  158. onDismiss={handleDismissWarning}
  159. />
  160. )}
  161. {showChangingSoon &&
  162. (isProjectOwner ? (
  163. <Notification
  164. action={
  165. <StartFreeTrialButton
  166. variant="new-changing"
  167. source="compile-time-warning"
  168. buttonProps={{
  169. className: 'btn-secondary-compile-timeout-override',
  170. }}
  171. >
  172. {t('start_free_trial_without_exclamation')}
  173. </StartFreeTrialButton>
  174. }
  175. ariaLive="polite"
  176. content={
  177. <div>
  178. <p>
  179. <Trans
  180. i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
  181. components={compileTimeoutBlogLinks}
  182. values={{ date: 'October 27 2023' }}
  183. shouldUnescape
  184. tOptions={{ interpolation: { escapeValue: true } }}
  185. />{' '}
  186. {t('and_you_can_upgrade_for_plenty_more_compile_time')}
  187. </p>
  188. </div>
  189. }
  190. title={t('your_project_compiled_but_soon_might_not')}
  191. type="warning"
  192. isActionBelowContent
  193. isDismissible
  194. onDismiss={handleDismissChangingSoon}
  195. />
  196. ) : (
  197. <Notification
  198. ariaLive="polite"
  199. content={
  200. <div>
  201. <p>
  202. <Trans
  203. i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
  204. components={compileTimeoutBlogLinks}
  205. values={{ date: 'October 27 2023' }}
  206. shouldUnescape
  207. tOptions={{ interpolation: { escapeValue: true } }}
  208. />
  209. </p>
  210. <p className="row-spaced">
  211. {t(
  212. 'tell_the_project_owner_to_upgrade_plan_for_more_compile_time'
  213. )}
  214. </p>
  215. </div>
  216. }
  217. title={t('this_project_compiled_but_soon_might_not')}
  218. type="warning"
  219. isDismissible
  220. onDismiss={handleDismissChangingSoon}
  221. />
  222. ))}
  223. </div>
  224. )
  225. }
  226. export default memo(CompileTimeoutMessages)