compile-timeout-messages.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 || 'control',
  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. } else {
  58. eventTracking.sendMB('compile-time-warning-would-display', {
  59. time: 10,
  60. ...segmentation,
  61. })
  62. }
  63. }
  64. },
  65. [
  66. isProjectOwner,
  67. showNewCompileTimeoutUI,
  68. dismissedUntilWarning,
  69. segmentation,
  70. ]
  71. )
  72. const handleDismissWarning = useCallback(() => {
  73. eventTracking.sendMB('compile-time-warning-dismissed', {
  74. time: 10,
  75. ...segmentation,
  76. })
  77. setShowWarning(false)
  78. const until = new Date()
  79. until.setDate(until.getDate() + 1) // 1 day
  80. setDismissedUntilWarning(until)
  81. }, [setDismissedUntilWarning, segmentation])
  82. const handleDismissChangingSoon = useCallback(() => {
  83. eventTracking.sendMB('compile-time-warning-dismissed', {
  84. time: 20,
  85. ...segmentation,
  86. })
  87. }, [segmentation])
  88. useEffect(() => {
  89. if (compiling || error || showLogs) return
  90. window.sl_console.log(
  91. `[compileTimeout] compiledTimeServerE2E ${
  92. deliveryLatencies.compileTimeServerE2E / 1000
  93. }s`
  94. )
  95. handleNewCompile(deliveryLatencies.compileTimeServerE2E)
  96. }, [compiling, error, showLogs, deliveryLatencies, handleNewCompile])
  97. if (!window.ExposedSettings.enableSubscriptions) {
  98. return null
  99. }
  100. if (compiling || error || showLogs) {
  101. return null
  102. }
  103. if (!showWarning && !showChangingSoon) {
  104. return null
  105. }
  106. function sendInfoClickEvent() {
  107. eventTracking.sendMB('paywall-info-click', {
  108. 'paywall-type': 'compile-time-warning',
  109. content: 'blog',
  110. })
  111. }
  112. const compileTimeoutBlogLinks = [
  113. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  114. <a
  115. aria-label={t('read_more_about_free_compile_timeouts_servers')}
  116. href="/blog/changes-to-free-compile-timeouts-and-servers"
  117. key="compileTimeoutBlogLink1"
  118. rel="noopener noreferrer"
  119. target="_blank"
  120. onClick={sendInfoClickEvent}
  121. />,
  122. /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
  123. <a
  124. aria-label={t('read_more_about_fix_prevent_timeout')}
  125. href="/learn/how-to/Fixing_and_preventing_compile_timeouts"
  126. key="compileTimeoutBlogLink2"
  127. target="_blank"
  128. rel="noopener noreferrer"
  129. />,
  130. ]
  131. // if showWarning is true then the 10s warning is shown
  132. // and if showChangingSoon is true then the 20s-60s should show
  133. return (
  134. <div>
  135. {showWarning && isProjectOwner && (
  136. <Notification
  137. action={
  138. <StartFreeTrialButton
  139. source="compile-time-warning-new-10s"
  140. buttonProps={{
  141. className: 'btn-secondary-compile-timeout-override',
  142. }}
  143. >
  144. {t('start_free_trial_without_exclamation')}
  145. </StartFreeTrialButton>
  146. }
  147. ariaLive="polite"
  148. content={
  149. <div>
  150. <div>
  151. <span>
  152. <Trans i18nKey="your_project_near_compile_timeout_limit" />
  153. </span>
  154. </div>
  155. {showNewCompileTimeoutUI === 'active' ? (
  156. <>
  157. <strong>
  158. <Trans i18nKey="upgrade_for_12x_more_compile_time" />
  159. </strong>
  160. {'. '}
  161. </>
  162. ) : (
  163. <strong>
  164. <Trans i18nKey="upgrade_for_plenty_more_compile_time" />
  165. </strong>
  166. )}
  167. </div>
  168. }
  169. type="warning"
  170. title={t('took_a_while')}
  171. isActionBelowContent
  172. isDismissible
  173. onDismiss={handleDismissWarning}
  174. />
  175. )}
  176. {showChangingSoon &&
  177. (isProjectOwner ? (
  178. <Notification
  179. action={
  180. <StartFreeTrialButton
  181. source="compile-time-warning-new-changing-soon"
  182. buttonProps={{
  183. className: 'btn-secondary-compile-timeout-override',
  184. }}
  185. >
  186. {t('start_free_trial_without_exclamation')}
  187. </StartFreeTrialButton>
  188. }
  189. ariaLive="polite"
  190. content={
  191. <div>
  192. <p>
  193. <Trans
  194. i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
  195. components={compileTimeoutBlogLinks}
  196. values={{ date: 'October 6 2023' }}
  197. />{' '}
  198. <Trans i18nKey="and_you_can_upgrade_for_plenty_more_compile_time" />
  199. </p>
  200. </div>
  201. }
  202. title={t('your_project_compiled_but_soon_might_not')}
  203. type="warning"
  204. isActionBelowContent
  205. isDismissible
  206. onDismiss={handleDismissChangingSoon}
  207. />
  208. ) : (
  209. <Notification
  210. ariaLive="polite"
  211. content={
  212. <div>
  213. <p>
  214. <Trans
  215. i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
  216. components={compileTimeoutBlogLinks}
  217. values={{ date: 'October 6 2023' }}
  218. />
  219. </p>
  220. <p className="row-spaced">
  221. <Trans i18nKey="tell_the_project_owner_to_upgrade_plan_for_more_compile_time" />
  222. </p>
  223. </div>
  224. }
  225. title={t('this_project_compiled_but_soon_might_not')}
  226. type="warning"
  227. isDismissible
  228. onDismiss={handleDismissChangingSoon}
  229. />
  230. ))}
  231. </div>
  232. )
  233. }
  234. export default memo(CompileTimeoutMessages)