faster-compiles-feedback.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { memo, useEffect, useRef, useState } from 'react'
  2. import { Button, Alert } from 'react-bootstrap'
  3. import { useTranslation } from 'react-i18next'
  4. import Icon from '../../../shared/components/icon'
  5. import { sendMB } from '../../../infrastructure/event-tracking'
  6. import usePersistedState from '../../../shared/hooks/use-persisted-state'
  7. import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
  8. import { useProjectContext } from '../../../shared/context/project-context'
  9. const SAY_THANKS_TIMEOUT = 10 * 1000
  10. function FasterCompilesFeedbackContent() {
  11. const { clsiServerId, deliveryLatencies, pdfFile, pdfUrl } =
  12. useCompileContext()
  13. const { _id: projectId } = useProjectContext()
  14. const [incrementalCompiles, setIncrementalCompiles] = useState(0)
  15. const [hasRatedProject, setHasRatedProject] = usePersistedState(
  16. `faster-compiles-feedback:${projectId}`,
  17. false,
  18. true
  19. )
  20. const [dismiss, setDismiss] = usePersistedState(
  21. 'faster-compiles-feedback:dismiss',
  22. false,
  23. true
  24. )
  25. const [sayThanks, setSayThanks] = useState(false)
  26. const lastClsiServerId = useRef<string | undefined>(undefined)
  27. const lastPdfUrl = useRef<string | undefined>(undefined)
  28. useEffect(() => {
  29. if (
  30. !pdfUrl ||
  31. !lastPdfUrl.current ||
  32. clsiServerId !== lastClsiServerId.current
  33. ) {
  34. // Reset history after
  35. // - clearing cache / server error (both reset pdfUrl)
  36. // - initial compile after reset of pdfUrl
  37. // - switching the clsi server, aka we get a _slow_ full compile.
  38. setIncrementalCompiles(0)
  39. lastClsiServerId.current = clsiServerId
  40. } else {
  41. setIncrementalCompiles(n => n + 1)
  42. }
  43. lastPdfUrl.current = pdfUrl
  44. }, [clsiServerId, lastPdfUrl, pdfUrl, setIncrementalCompiles])
  45. function submitFeedback(feedback = '') {
  46. sendMB('faster-compiles-feedback', {
  47. projectId,
  48. server: clsiServerId?.includes('-c2d-') ? 'faster' : 'normal',
  49. feedback,
  50. pdfSize: pdfFile?.size,
  51. ...deliveryLatencies,
  52. })
  53. setHasRatedProject(true)
  54. setSayThanks(true)
  55. window.setTimeout(() => {
  56. setSayThanks(false)
  57. }, SAY_THANKS_TIMEOUT)
  58. }
  59. function dismissFeedback() {
  60. sendMB('faster-compiles-feedback-dismiss')
  61. setDismiss(true)
  62. }
  63. const { t } = useTranslation()
  64. // Hide the feedback prompt in all these cases:
  65. // - the initial compile (0), its always perceived as _slow_.
  66. // - the first incremental compile (1), its always _faster_ than ^.
  67. // - the user has dismissed the prompt
  68. // - the user has rated compile speed already (say thanks if needed)
  69. switch (true) {
  70. case sayThanks:
  71. return (
  72. <Alert
  73. bsStyle="info"
  74. className="faster-compiles-feedback"
  75. onClick={() => setSayThanks(false)}
  76. >
  77. {t('faster_compiles_feedback_thanks')}
  78. </Alert>
  79. )
  80. case dismiss || hasRatedProject:
  81. return null
  82. case incrementalCompiles > 1:
  83. return (
  84. <Alert bsStyle="info" className="faster-compiles-feedback">
  85. <button
  86. type="button"
  87. aria-label={t('dismiss')}
  88. className="btn-inline-link faster-compiles-feedback-dismiss"
  89. onClick={dismissFeedback}
  90. >
  91. <Icon type="close" fw />
  92. </button>
  93. {t('faster_compiles_feedback_question')}
  94. <div className="faster-compiles-feedback-options">
  95. {['slower', 'same', 'faster'].map(feedback => (
  96. <Button
  97. bsStyle="default"
  98. bsSize="xs"
  99. className="faster-compiles-feedback-option"
  100. onClick={() => submitFeedback(feedback)}
  101. key={feedback}
  102. >
  103. {feedback === 'faster'
  104. ? t('faster_compiles_feedback_seems_faster')
  105. : feedback === 'same'
  106. ? t('faster_compiles_feedback_seems_same')
  107. : t('faster_compiles_feedback_seems_slower')}
  108. </Button>
  109. ))}
  110. </div>
  111. </Alert>
  112. )
  113. default:
  114. return null
  115. }
  116. }
  117. function FasterCompilesFeedback() {
  118. const { showFasterCompilesFeedbackUI } = useCompileContext()
  119. if (!showFasterCompilesFeedbackUI) {
  120. return null
  121. }
  122. return <FasterCompilesFeedbackContent />
  123. }
  124. export default memo(FasterCompilesFeedback)