Răsfoiți Sursa

Merge pull request #17909 from overleaf/ac-tear-down-compile-timeout-tests-2

[web]  Remove split-tests `compile-backend-class*` and `compile-timeout-20s*` (attempt 2)

GitOrigin-RevId: 5658f2977d3e7089eec5bbe7a33eee81c153e41d
Antoine Clausse 2 ani în urmă
părinte
comite
d35204033f
27 a modificat fișierele cu 394 adăugiri și 1052 ștergeri
  1. 0 9
      services/web/app/src/Features/Compile/CompileController.js
  2. 15 120
      services/web/app/src/Features/Compile/CompileManager.js
  3. 0 58
      services/web/app/src/Features/Editor/EditorHttpController.js
  4. 0 1
      services/web/config/settings.defaults.js
  5. 0 21
      services/web/frontend/extracted-translations.json
  6. 4 11
      services/web/frontend/js/features/pdf-preview/components/compile-time-warning-upgrade-prompt-inner.tsx
  7. 78 0
      services/web/frontend/js/features/pdf-preview/components/compile-time-warning-upgrade-prompt.tsx
  8. 0 116
      services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx
  9. 0 122
      services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx
  10. 0 126
      services/web/frontend/js/features/pdf-preview/components/compile-timeout-messages.tsx
  11. 0 135
      services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx
  12. 3 7
      services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.jsx
  13. 3 10
      services/web/frontend/js/features/pdf-preview/components/pdf-preview-pane.jsx
  14. 61 100
      services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt-new.tsx
  15. 0 62
      services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt.jsx
  16. 0 8
      services/web/frontend/js/features/pdf-preview/util/compiler.js
  17. 10 19
      services/web/frontend/js/shared/context/detach-compile-context.tsx
  18. 12 21
      services/web/frontend/js/shared/context/local-compile-context.tsx
  19. 0 17
      services/web/frontend/js/shared/context/project-context.tsx
  20. 2 2
      services/web/frontend/stories/pdf-preview-messages.stories.jsx
  21. 2 37
      services/web/frontend/stories/pdf-preview-messages.stories.tsx
  22. 0 21
      services/web/locales/en.json
  23. 57 0
      services/web/scripts/migration_compile_timeout_60s_to_20s.js
  24. 134 0
      services/web/test/acceptance/src/MigrateUserFeatureTimeoutTests.js
  25. 0 1
      services/web/test/unit/src/Compile/ClsiManagerTests.js
  26. 0 1
      services/web/test/unit/src/Compile/CompileControllerTests.js
  27. 13 27
      services/web/test/unit/src/Compile/CompileManagerTests.js

+ 0 - 9
services/web/app/src/Features/Compile/CompileController.js

@@ -105,15 +105,6 @@ module.exports = CompileController = {
       stopOnFirstError,
     }
 
-    // temporary override to force the new compile timeout
-    const forceNewCompileTimeout = req.query.force_new_compile_timeout
-    if (
-      forceNewCompileTimeout === 'active' ||
-      forceNewCompileTimeout === 'changing'
-    ) {
-      options.forceNewCompileTimeout = forceNewCompileTimeout
-    }
-
     if (req.body.rootDoc_id) {
       options.rootDoc_id = req.body.rootDoc_id
     } else if (

+ 15 - 120
services/web/app/src/Features/Compile/CompileManager.js

@@ -8,18 +8,9 @@ const UserGetter = require('../User/UserGetter')
 const ClsiManager = require('./ClsiManager')
 const Metrics = require('@overleaf/metrics')
 const { RateLimiter } = require('../../infrastructure/RateLimiter')
-const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache')
 
-const NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF = new Date('2023-09-18T11:00:00.000Z')
-const NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE = new Date(
-  '2023-10-10T11:00:00.000Z'
-)
-
 module.exports = CompileManager = {
-  NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF,
-  NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE,
-
   compile(projectId, userId, options = {}, _callback) {
     const timer = new Metrics.Timer('editor.compile')
     const callback = function (...args) {
@@ -62,16 +53,6 @@ module.exports = CompileManager = {
                       const value = limits[key]
                       options[key] = value
                     }
-                    if (options.timeout !== 20) {
-                      // temporary override to force the new compile timeout
-                      if (options.forceNewCompileTimeout === 'active') {
-                        options.timeout = 20
-                      } else if (
-                        options.forceNewCompileTimeout === 'changing'
-                      ) {
-                        options.timeout = 60
-                      }
-                    }
                     // Put a lower limit on autocompiles for free users, based on compileGroup
                     CompileManager._checkCompileGroupAutoCompileLimit(
                       options.isAutoCompile,
@@ -184,78 +165,25 @@ module.exports = CompileManager = {
                 if (err) {
                   return callback(err)
                 }
+                const compileGroup =
+                  ownerFeatures.compileGroup ||
+                  Settings.defaultFeatures.compileGroup
+                const compileTimeout =
+                  ownerFeatures.compileTimeout ||
+                  Settings.defaultFeatures.compileTimeout
+
                 const limits = {
                   timeout:
-                    ownerFeatures.compileTimeout ||
-                    Settings.defaultFeatures.compileTimeout,
-                  compileGroup:
-                    ownerFeatures.compileGroup ||
-                    Settings.defaultFeatures.compileGroup,
+                    // temporary override until users' compileTimeout is migrated
+                    compileGroup === 'standard' && compileTimeout <= 60
+                      ? 20
+                      : compileTimeout,
+                  compileGroup,
+                  compileBackendClass:
+                    compileGroup === 'standard' ? 'n2d' : 'c2d',
                   ownerAnalyticsId: analyticsId,
                 }
-                CompileManager._getCompileBackendClassDetails(
-                  owner,
-                  limits.compileGroup,
-                  (
-                    err,
-                    { compileBackendClass, showFasterCompilesFeedbackUI }
-                  ) => {
-                    if (err) return callback(err)
-                    limits.compileBackendClass = compileBackendClass
-                    limits.showFasterCompilesFeedbackUI =
-                      showFasterCompilesFeedbackUI
-                    if (compileBackendClass === 'n2d' && limits.timeout <= 60) {
-                      // project owners with faster compiles but with <= 60 compile timeout (default)
-                      // will have a 20s compile timeout
-                      // The compile-timeout-20s split test exists to enable a gradual rollout
-                      SplitTestHandler.getAssignmentForMongoUser(
-                        owner,
-                        'compile-timeout-20s',
-                        (err, assignment) => {
-                          if (err) return callback(err)
-                          // users who were on the 'default' servers at time of original rollout
-                          // will have a later cutoff date for the 20s timeout in the next phase
-                          // we check the backend class at version 8 (baseline)
-                          const backendClassHistory =
-                            owner.splitTests?.['compile-backend-class-n2d'] ||
-                            []
-                          const backendClassBaselineVariant =
-                            backendClassHistory.find(version => {
-                              return version.versionNumber === 8
-                            })?.variantName
-                          const timeoutEnforcedCutoff =
-                            backendClassBaselineVariant === 'default'
-                              ? NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE
-                              : NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF
-                          if (assignment?.variant === '20s') {
-                            if (owner.signUpDate > timeoutEnforcedCutoff) {
-                              limits.timeout = 20
-                              callback(null, limits)
-                            } else {
-                              SplitTestHandler.getAssignmentForMongoUser(
-                                owner,
-                                'compile-timeout-20s-existing-users',
-                                (err, assignmentExistingUsers) => {
-                                  if (err) return callback(err)
-                                  if (
-                                    assignmentExistingUsers?.variant === '20s'
-                                  ) {
-                                    limits.timeout = 20
-                                  }
-                                  callback(null, limits)
-                                }
-                              )
-                            }
-                          } else {
-                            callback(null, limits)
-                          }
-                        }
-                      )
-                    } else {
-                      callback(null, limits)
-                    }
-                  }
-                )
+                callback(null, limits)
               }
             )
           }
@@ -322,39 +250,6 @@ module.exports = CompileManager = {
       })
   },
 
-  _getCompileBackendClassDetails(owner, compileGroup, callback) {
-    const { defaultBackendClass } = Settings.apis.clsi
-    if (compileGroup === 'standard') {
-      return SplitTestHandler.getAssignmentForMongoUser(
-        owner,
-        'compile-backend-class-n2d',
-        (err, assignment) => {
-          if (err) return callback(err, {})
-          const { variant } = assignment
-          callback(null, {
-            compileBackendClass:
-              variant === 'default' ? defaultBackendClass : variant,
-            showFasterCompilesFeedbackUI: false,
-          })
-        }
-      )
-    }
-    SplitTestHandler.getAssignmentForMongoUser(
-      owner,
-      'compile-backend-class',
-      (err, assignment) => {
-        if (err) return callback(err, {})
-        const { analytics, variant } = assignment
-        const activeForUser = analytics?.segmentation?.splitTest != null
-        callback(null, {
-          compileBackendClass:
-            variant === 'default' ? defaultBackendClass : variant,
-          showFasterCompilesFeedbackUI: activeForUser,
-        })
-      }
-    )
-  },
-
   wordCount(projectId, userId, file, clsiserverid, callback) {
     CompileManager.getProjectCompileLimits(projectId, function (error, limits) {
       if (error) {

+ 0 - 58
services/web/app/src/Features/Editor/EditorHttpController.js

@@ -13,12 +13,6 @@ const Errors = require('../Errors/Errors')
 const DocstoreManager = require('../Docstore/DocstoreManager')
 const logger = require('@overleaf/logger')
 const { expressify } = require('@overleaf/promise-utils')
-const SplitTestHandler = require('../SplitTests/SplitTestHandler')
-const {
-  NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF,
-  NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE,
-} = require('../Compile/CompileManager')
-const UserGetter = require('../User/UserGetter')
 
 module.exports = {
   joinProject: expressify(joinProject),
@@ -72,58 +66,6 @@ async function joinProject(req, res, next) {
   if (!project) {
     return res.sendStatus(403)
   }
-  // Compile timeout 20s test
-  if (project.features?.compileTimeout <= 60) {
-    const compileAssignment =
-      await SplitTestHandler.promises.getAssignmentForUser(
-        project.owner._id,
-        'compile-backend-class-n2d'
-      )
-    if (compileAssignment?.variant === 'n2d') {
-      const timeoutAssignment =
-        await SplitTestHandler.promises.getAssignmentForUser(
-          project.owner._id,
-          'compile-timeout-20s'
-        )
-      if (timeoutAssignment?.variant === '20s') {
-        // users who were on the 'default' servers at time of original rollout
-        // will have a later cutoff date for the 20s timeout in the next phase
-        // we check the backend class at version 8 (baseline)
-        const owner = await UserGetter.promises.getUser(project.owner._id, {
-          _id: 1,
-          'splitTests.compile-backend-class-n2d': 1,
-        })
-        const backendClassHistory =
-          owner.splitTests?.['compile-backend-class-n2d'] || []
-        const backendClassBaselineVariant = backendClassHistory.find(
-          version => {
-            return version.versionNumber === 8
-          }
-        )?.variantName
-        const timeoutEnforcedCutoff =
-          backendClassBaselineVariant === 'default'
-            ? NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE
-            : NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF
-        if (project.owner.signUpDate > timeoutEnforcedCutoff) {
-          // New users will see a 10s warning and compile fail at 20s
-          project.showNewCompileTimeoutUI = 'active'
-        } else {
-          const existingUserTimeoutAssignment =
-            await SplitTestHandler.promises.getAssignmentForUser(
-              project.owner._id,
-              'compile-timeout-20s-existing-users'
-            )
-          if (existingUserTimeoutAssignment?.variant === '20s') {
-            // Older users in treatment see 10s warning and compile fail at 20s
-            project.showNewCompileTimeoutUI = 'active'
-          } else {
-            // Older users in control aren't limited to 20s, but will see a notice of upcoming changes if compile >20s
-            project.showNewCompileTimeoutUI = 'changing'
-          }
-        }
-      }
-    }
-  }
   // Hide sensitive data if the user is restricted
   if (isRestrictedUser) {
     project.owner = { _id: project.owner._id }

+ 0 - 1
services/web/config/settings.defaults.js

@@ -272,7 +272,6 @@ module.exports = {
       url: `http://${process.env.CLSI_HOST || 'localhost'}:3013`,
       // url: "http://#{process.env['CLSI_LB_HOST']}:3014"
       backendGroupName: undefined,
-      defaultBackendClass: process.env.CLSI_DEFAULT_BACKEND_CLASS || 'e2',
       submissionBackendClass:
         process.env.CLSI_SUBMISSION_BACKEND_CLASS || 'n2d',
     },

+ 0 - 21
services/web/frontend/extracted-translations.json

@@ -70,11 +70,9 @@
   "also": "",
   "an_email_has_already_been_sent_to": "",
   "an_error_occurred_when_verifying_the_coupon_code": "",
-  "and_you_can_upgrade_for_plenty_more_compile_time": "",
   "anonymous": "",
   "anyone_with_link_can_edit": "",
   "anyone_with_link_can_view": "",
-  "approaching_compile_timeout_limit_upgrade_for_more_compile_time": "",
   "archive": "",
   "archive_projects": "",
   "archived": "",
@@ -86,7 +84,6 @@
   "are_you_sure": "",
   "ask_proj_owner_to_unlink_from_current_github": "",
   "ask_proj_owner_to_upgrade_for_full_history": "",
-  "ask_proj_owner_to_upgrade_for_longer_compiles": "",
   "ask_proj_owner_to_upgrade_for_references_search": "",
   "ask_repo_owner_to_reconnect": "",
   "ask_repo_owner_to_renew_overleaf_subscription": "",
@@ -192,7 +189,6 @@
   "compile_mode": "",
   "compile_terminated_by_user": "",
   "compiler": "",
-  "compiles_on_our_free_plan_are_now_on_faster_servers": "",
   "compiling": "",
   "configure_sso": "",
   "confirm": "",
@@ -285,7 +281,6 @@
   "disabling": "",
   "disconnected": "",
   "discount_of": "",
-  "dismiss": "",
   "dismiss_error_popup": "",
   "display_deleted_user": "",
   "do_you_want_to_change_your_primary_email_address_to": "",
@@ -386,11 +381,6 @@
   "failed_to_send_managed_user_invite_to_email": "",
   "failed_to_send_sso_link_invite_to_email": "",
   "fast": "",
-  "faster_compiles_feedback_question": "",
-  "faster_compiles_feedback_seems_faster": "",
-  "faster_compiles_feedback_seems_same": "",
-  "faster_compiles_feedback_seems_slower": "",
-  "faster_compiles_feedback_thanks": "",
   "file_action_created": "",
   "file_action_deleted": "",
   "file_action_edited": "",
@@ -426,7 +416,6 @@
   "found_matching_deleted_users": "",
   "free_7_day_trial_billed_annually": "",
   "free_7_day_trial_billed_monthly": "",
-  "free_accounts_have_timeout_upgrade_to_increase": "",
   "free_plan_label": "",
   "free_plan_tooltip": "",
   "from_another_project": "",
@@ -918,7 +907,6 @@
   "please_wait": "",
   "plus_additional_collaborators_document_history_track_changes_and_more": "",
   "plus_more": "",
-  "plus_upgraded_accounts_receive": "",
   "postal_code": "",
   "premium_feature": "",
   "premium_plan_label": "",
@@ -980,7 +968,6 @@
   "react_history_tutorial_content": "",
   "react_history_tutorial_title": "",
   "reactivate_subscription": "",
-  "read_more_about_fix_prevent_timeout": "",
   "read_more_about_free_compile_timeouts_servers": "",
   "read_only": "",
   "read_only_token": "",
@@ -1292,7 +1279,6 @@
   "tc_switch_guests_tip": "",
   "tc_switch_user_tip": "",
   "tell_the_project_owner_and_ask_them_to_upgrade": "",
-  "tell_the_project_owner_to_upgrade_plan_for_more_compile_time": "",
   "template_approved_by_publisher": "",
   "template_description": "",
   "template_title_taken_from_project_title": "",
@@ -1322,7 +1308,6 @@
   "this_could_be_because_we_cant_support_some_elements_of_the_table": "",
   "this_field_is_required": "",
   "this_grants_access_to_features_2": "",
-  "this_project_compiled_but_soon_might_not": "",
   "this_project_exceeded_compile_timeout_limit_on_free_plan": "",
   "this_project_is_public": "",
   "this_project_is_public_read_only": "",
@@ -1464,8 +1449,6 @@
   "upgrade": "",
   "upgrade_cc_btn": "",
   "upgrade_for_12x_more_compile_time": "",
-  "upgrade_for_longer_compiles": "",
-  "upgrade_for_plenty_more_compile_time": "",
   "upgrade_now": "",
   "upgrade_to_get_feature": "",
   "upgrade_to_track_changes": "",
@@ -1519,8 +1502,6 @@
   "we_sent_new_code": "",
   "wed_love_you_to_stay": "",
   "welcome_to_sl": "",
-  "were_in_the_process_of_reducing_compile_timeout_which_may_affect_this_project": "",
-  "were_in_the_process_of_reducing_compile_timeout_which_may_affect_your_project": "",
   "were_performing_maintenance": "",
   "weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project": "",
   "weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project": "",
@@ -1571,7 +1552,6 @@
   "you_have_been_invited_to_transfer_management_of_your_account": "",
   "you_have_been_invited_to_transfer_management_of_your_account_to": "",
   "you_have_been_removed_from_this_project_and_will_be_redirected_to_project_dashboard": "",
-  "you_may_be_able_to_fix_issues_to_speed_up_the_compile": "",
   "you_need_to_configure_your_sso_settings": "",
   "you_will_be_able_to_reassign_subscription": "",
   "youll_get_best_results_in_visual_but_can_be_used_in_source": "",
@@ -1591,7 +1571,6 @@
   "your_new_plan": "",
   "your_plan": "",
   "your_plan_is_changing_at_term_end": "",
-  "your_project_compiled_but_soon_might_not": "",
   "your_project_exceeded_compile_timeout_limit_on_free_plan": "",
   "your_project_near_compile_timeout_limit": "",
   "your_projects": "",

+ 4 - 11
services/web/frontend/js/features/pdf-preview/components/compile-timeout-warning.tsx → services/web/frontend/js/features/pdf-preview/components/compile-time-warning-upgrade-prompt-inner.tsx

@@ -4,10 +4,9 @@ import { useTranslation } from 'react-i18next'
 import { FC } from 'react'
 import { useSplitTestContext } from '@/shared/context/split-test-context'
 
-export const CompileTimeoutWarning: FC<{
+export const CompileTimeWarningUpgradePromptInner: FC<{
   handleDismissWarning: () => void
-  showNewCompileTimeoutUI?: string
-}> = ({ handleDismissWarning, showNewCompileTimeoutUI }) => {
+}> = ({ handleDismissWarning }) => {
   const { t } = useTranslation()
 
   const { splitTestVariants } = useSplitTestContext()
@@ -34,14 +33,8 @@ export const CompileTimeoutWarning: FC<{
           <div>
             <span>{t('your_project_near_compile_timeout_limit')}</span>
           </div>
-          {showNewCompileTimeoutUI === 'active' ? (
-            <>
-              <strong>{t('upgrade_for_12x_more_compile_time')}</strong>
-              {'. '}
-            </>
-          ) : (
-            <strong>{t('upgrade_for_plenty_more_compile_time')}</strong>
-          )}
+          <strong>{t('upgrade_for_12x_more_compile_time')}</strong>
+          {'. '}
         </div>
       }
       type="warning"

+ 78 - 0
services/web/frontend/js/features/pdf-preview/components/compile-time-warning-upgrade-prompt.tsx

@@ -0,0 +1,78 @@
+import { memo, useCallback, useEffect, useState } from 'react'
+import * as eventTracking from '@/infrastructure/event-tracking'
+import { useDetachCompileContext } from '@/shared/context/detach-compile-context'
+import usePersistedState from '@/shared/hooks/use-persisted-state'
+import { CompileTimeWarningUpgradePromptInner } from '@/features/pdf-preview/components/compile-time-warning-upgrade-prompt-inner'
+
+function CompileTimeWarningUpgradePrompt() {
+  const { isProjectOwner, deliveryLatencies, compiling, showLogs, error } =
+    useDetachCompileContext()
+
+  const [showWarning, setShowWarning] = useState(false)
+  const [dismissedUntilWarning, setDismissedUntilWarning] = usePersistedState<
+    Date | undefined
+  >(`has-dismissed-10s-compile-time-warning-until`)
+
+  const handleNewCompile = useCallback(
+    compileTime => {
+      setShowWarning(false)
+      if (compileTime > 10000) {
+        if (isProjectOwner) {
+          if (
+            !dismissedUntilWarning ||
+            new Date(dismissedUntilWarning) < new Date()
+          ) {
+            setShowWarning(true)
+            eventTracking.sendMB('compile-time-warning-displayed', {
+              time: 10,
+              isProjectOwner,
+            })
+          }
+        }
+      }
+    },
+    [isProjectOwner, dismissedUntilWarning]
+  )
+
+  const handleDismissWarning = useCallback(() => {
+    eventTracking.sendMB('compile-time-warning-dismissed', {
+      time: 10,
+      isProjectOwner,
+    })
+    setShowWarning(false)
+    const until = new Date()
+    until.setDate(until.getDate() + 1) // 1 day
+    setDismissedUntilWarning(until)
+  }, [isProjectOwner, setDismissedUntilWarning])
+
+  useEffect(() => {
+    if (compiling || error || showLogs) return
+    handleNewCompile(deliveryLatencies.compileTimeServerE2E)
+  }, [compiling, error, showLogs, deliveryLatencies, handleNewCompile])
+
+  if (!window.ExposedSettings.enableSubscriptions) {
+    return null
+  }
+
+  if (compiling || error || showLogs) {
+    return null
+  }
+
+  if (!showWarning) {
+    return null
+  }
+
+  // if showWarning is true then the 10s warning is shown
+
+  return (
+    <div>
+      {showWarning && isProjectOwner && (
+        <CompileTimeWarningUpgradePromptInner
+          handleDismissWarning={handleDismissWarning}
+        />
+      )}
+    </div>
+  )
+}
+
+export default memo(CompileTimeWarningUpgradePrompt)

+ 0 - 116
services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx

@@ -1,116 +0,0 @@
-import { memo, useCallback, useEffect } from 'react'
-import { Button } from 'react-bootstrap'
-import { Trans, useTranslation } from 'react-i18next'
-import * as eventTracking from '../../../infrastructure/event-tracking'
-import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
-import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
-import usePersistedState from '../../../shared/hooks/use-persisted-state'
-import { useSplitTestContext } from '@/shared/context/split-test-context'
-
-const TWENTY_FOUR_DAYS = 24 * 60 * 60 * 24 * 1000
-
-function CompileTimeWarning() {
-  const { t } = useTranslation()
-
-  const [displayStatus, setDisplayStatus] = usePersistedState(
-    'compile-time-warning-display-status',
-    { lastDisplayTime: 0, dismissed: false },
-    true
-  )
-
-  const {
-    showCompileTimeWarning,
-    setShowCompileTimeWarning,
-    deliveryLatencies,
-    isProjectOwner,
-  } = useDetachCompileContext()
-
-  const { splitTestVariants } = useSplitTestContext()
-  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
-
-  useEffect(() => {
-    if (deliveryLatencies && deliveryLatencies.compileTimeServerE2E) {
-      // compile-timeout-20s test
-      if (deliveryLatencies.compileTimeServerE2E > 10000) {
-        eventTracking.sendMB('compile-time-warning-would-display', {
-          time: 10,
-          newCompileTimeout: 'control',
-          isProjectOwner,
-        })
-      }
-    }
-  }, [deliveryLatencies, isProjectOwner])
-
-  useEffect(() => {
-    if (showCompileTimeWarning) {
-      if (
-        displayStatus &&
-        Date.now() - displayStatus.lastDisplayTime < TWENTY_FOUR_DAYS
-      ) {
-        return
-      }
-      setDisplayStatus({ lastDisplayTime: Date.now(), dismissed: false })
-      eventTracking.sendMB('compile-time-warning-displayed', { time: 30 })
-    }
-  }, [showCompileTimeWarning, displayStatus, setDisplayStatus])
-
-  const getTimeSinceDisplayed = useCallback(() => {
-    return (Date.now() - displayStatus.lastDisplayTime) / 1000
-  }, [displayStatus])
-
-  const closeWarning = useCallback(() => {
-    eventTracking.sendMB('compile-time-warning-dismissed', {
-      'time-since-displayed': getTimeSinceDisplayed(),
-      time: 30,
-    })
-    setShowCompileTimeWarning(false)
-    setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
-  }, [getTimeSinceDisplayed, setShowCompileTimeWarning, setDisplayStatus])
-
-  const handleUpgradeClick = useCallback(() => {
-    eventTracking.sendMB('compile-time-warning-upgrade-click', {
-      'time-since-displayed': getTimeSinceDisplayed(),
-      time: 30,
-    })
-    setShowCompileTimeWarning(false)
-    setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
-  }, [getTimeSinceDisplayed, setShowCompileTimeWarning, setDisplayStatus])
-
-  if (!showCompileTimeWarning || displayStatus.dismissed) {
-    return null
-  }
-
-  return (
-    <div className="alert alert-success compile-time-warning" role="alert">
-      <Button
-        className="close"
-        data-dismiss="alert"
-        aria-label="Close"
-        onClick={closeWarning}
-      >
-        <span aria-hidden="true">&times;</span>
-      </Button>
-      <div className="warning-content">
-        <div className="warning-text">
-          <Trans
-            i18nKey="approaching_compile_timeout_limit_upgrade_for_more_compile_time"
-            components={{
-              strong: <strong style={{ display: 'inline-block' }} />,
-            }}
-          />
-        </div>
-        <div className="upgrade-prompt">
-          <StartFreeTrialButton
-            buttonProps={{ bsStyle: 'primary', bsSize: 'sm' }}
-            handleClick={handleUpgradeClick}
-            source="compile-time-warning"
-          >
-            {hasNewPaywallCta ? t('get_more_compile_time') : t('upgrade')}
-          </StartFreeTrialButton>
-        </div>
-      </div>
-    </div>
-  )
-}
-
-export default memo(CompileTimeWarning)

+ 0 - 122
services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx

@@ -1,122 +0,0 @@
-import Notification from '@/shared/components/notification'
-import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
-import { Trans, useTranslation } from 'react-i18next'
-import * as eventTracking from '@/infrastructure/event-tracking'
-import { FC } from 'react'
-import { useSplitTestContext } from '@/shared/context/split-test-context'
-
-const sendInfoClickEvent = () => {
-  eventTracking.sendMB('paywall-info-click', {
-    'paywall-type': 'compile-time-warning',
-    content: 'blog',
-  })
-}
-
-export const CompileTimeoutChangingSoon: FC<{
-  isProjectOwner?: boolean
-  handleDismissChangingSoon: () => void
-}> = ({ isProjectOwner = false, handleDismissChangingSoon }) => {
-  const { t } = useTranslation()
-
-  const { splitTestVariants } = useSplitTestContext()
-  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
-
-  const compileTimeoutChangesBlogLink = (
-    /* eslint-disable-next-line jsx-a11y/anchor-has-content */
-    <a
-      aria-label={t('read_more_about_free_compile_timeouts_servers')}
-      href="/blog/changes-to-free-compile-timeouts-and-servers"
-      key="compileTimeoutBlogLink1"
-      rel="noopener noreferrer"
-      target="_blank"
-      onClick={sendInfoClickEvent}
-    />
-  )
-
-  const fixingCompileTimeoutsLearnLink = (
-    /* eslint-disable-next-line jsx-a11y/anchor-has-content */
-    <a
-      aria-label={t('read_more_about_fix_prevent_timeout')}
-      href="/learn/how-to/Fixing_and_preventing_compile_timeouts"
-      key="compileTimeoutBlogLink2"
-      target="_blank"
-      rel="noopener noreferrer"
-    />
-  )
-
-  if (isProjectOwner) {
-    return (
-      <Notification
-        action={
-          <StartFreeTrialButton
-            variant="new-changing"
-            source="compile-time-warning"
-            buttonProps={{
-              className: 'btn-secondary-compile-timeout-override',
-            }}
-          >
-            {hasNewPaywallCta
-              ? t('get_more_compile_time')
-              : t('start_free_trial_without_exclamation')}
-          </StartFreeTrialButton>
-        }
-        ariaLive="polite"
-        content={
-          <div>
-            <p>
-              <Trans
-                i18nKey="compiles_on_our_free_plan_are_now_on_faster_servers"
-                components={[compileTimeoutChangesBlogLink]}
-              />
-            </p>
-            <p className="row-spaced">
-              <Trans
-                i18nKey="you_may_be_able_to_fix_issues_to_speed_up_the_compile"
-                components={[fixingCompileTimeoutsLearnLink]}
-              />{' '}
-              <Trans
-                i18nKey="and_you_can_upgrade_for_plenty_more_compile_time"
-                components={{ strong: <strong /> }}
-              />
-            </p>
-          </div>
-        }
-        title={t('your_project_compiled_but_soon_might_not')}
-        type="warning"
-        isActionBelowContent
-        isDismissible
-        onDismiss={handleDismissChangingSoon}
-      />
-    )
-  }
-
-  return (
-    <Notification
-      ariaLive="polite"
-      content={
-        <div>
-          <p>
-            <Trans
-              i18nKey="compiles_on_our_free_plan_are_now_on_faster_servers"
-              components={[compileTimeoutChangesBlogLink]}
-            />{' '}
-            <Trans
-              i18nKey="you_may_be_able_to_fix_issues_to_speed_up_the_compile"
-              components={[fixingCompileTimeoutsLearnLink]}
-            />
-          </p>
-          <p className="row-spaced">
-            <Trans
-              i18nKey="tell_the_project_owner_to_upgrade_plan_for_more_compile_time"
-              components={{ strong: <strong /> }}
-            />
-          </p>
-        </div>
-      }
-      title={t('this_project_compiled_but_soon_might_not')}
-      type="warning"
-      isDismissible
-      onDismiss={handleDismissChangingSoon}
-    />
-  )
-}

+ 0 - 126
services/web/frontend/js/features/pdf-preview/components/compile-timeout-messages.tsx

@@ -1,126 +0,0 @@
-import { memo, useCallback, useEffect, useMemo, useState } from 'react'
-import * as eventTracking from '@/infrastructure/event-tracking'
-import { useDetachCompileContext } from '@/shared/context/detach-compile-context'
-import usePersistedState from '@/shared/hooks/use-persisted-state'
-import { CompileTimeoutWarning } from '@/features/pdf-preview/components/compile-timeout-warning'
-import { CompileTimeoutChangingSoon } from '@/features/pdf-preview/components/compile-timeout-changing-soon'
-
-function CompileTimeoutMessages() {
-  const {
-    showNewCompileTimeoutUI,
-    isProjectOwner,
-    deliveryLatencies,
-    compiling,
-    showLogs,
-    error,
-  } = useDetachCompileContext()
-
-  const [showWarning, setShowWarning] = useState(false)
-  const [showChangingSoon, setShowChangingSoon] = useState(false)
-  const [dismissedUntilWarning, setDismissedUntilWarning] = usePersistedState<
-    Date | undefined
-  >(`has-dismissed-10s-compile-time-warning-until`)
-
-  const segmentation = useMemo(() => {
-    return {
-      newCompileTimeout: showNewCompileTimeoutUI,
-      isProjectOwner,
-    }
-  }, [showNewCompileTimeoutUI, isProjectOwner])
-
-  const handleNewCompile = useCallback(
-    compileTime => {
-      setShowWarning(false)
-      setShowChangingSoon(false)
-      if (compileTime > 20000) {
-        if (showNewCompileTimeoutUI === 'changing') {
-          setShowChangingSoon(true)
-          eventTracking.sendMB('compile-time-warning-displayed', {
-            time: 20,
-            ...segmentation,
-          })
-        }
-      } else if (compileTime > 10000) {
-        setShowChangingSoon(false)
-        if (
-          (isProjectOwner && showNewCompileTimeoutUI === 'active') ||
-          showNewCompileTimeoutUI === 'changing'
-        ) {
-          if (
-            !dismissedUntilWarning ||
-            new Date(dismissedUntilWarning) < new Date()
-          ) {
-            setShowWarning(true)
-            eventTracking.sendMB('compile-time-warning-displayed', {
-              time: 10,
-              ...segmentation,
-            })
-          }
-        }
-      }
-    },
-    [
-      isProjectOwner,
-      showNewCompileTimeoutUI,
-      dismissedUntilWarning,
-      segmentation,
-    ]
-  )
-
-  const handleDismissWarning = useCallback(() => {
-    eventTracking.sendMB('compile-time-warning-dismissed', {
-      time: 10,
-      ...segmentation,
-    })
-    setShowWarning(false)
-    const until = new Date()
-    until.setDate(until.getDate() + 1) // 1 day
-    setDismissedUntilWarning(until)
-  }, [setDismissedUntilWarning, segmentation])
-
-  const handleDismissChangingSoon = useCallback(() => {
-    eventTracking.sendMB('compile-time-warning-dismissed', {
-      time: 20,
-      ...segmentation,
-    })
-  }, [segmentation])
-
-  useEffect(() => {
-    if (compiling || error || showLogs) return
-    handleNewCompile(deliveryLatencies.compileTimeServerE2E)
-  }, [compiling, error, showLogs, deliveryLatencies, handleNewCompile])
-
-  if (!window.ExposedSettings.enableSubscriptions) {
-    return null
-  }
-
-  if (compiling || error || showLogs) {
-    return null
-  }
-
-  if (!showWarning && !showChangingSoon) {
-    return null
-  }
-
-  // if showWarning is true then the 10s warning is shown
-  // and if showChangingSoon is true then the 20s-60s should show
-
-  return (
-    <div>
-      {showWarning && isProjectOwner && (
-        <CompileTimeoutWarning
-          showNewCompileTimeoutUI={showNewCompileTimeoutUI}
-          handleDismissWarning={handleDismissWarning}
-        />
-      )}
-      {showChangingSoon && (
-        <CompileTimeoutChangingSoon
-          isProjectOwner={isProjectOwner}
-          handleDismissChangingSoon={handleDismissChangingSoon}
-        />
-      )}
-    </div>
-  )
-}
-
-export default memo(CompileTimeoutMessages)

+ 0 - 135
services/web/frontend/js/features/pdf-preview/components/faster-compiles-feedback.tsx

@@ -1,135 +0,0 @@
-import { memo, useEffect, useRef, useState } from 'react'
-import { Button, Alert } from 'react-bootstrap'
-import { useTranslation } from 'react-i18next'
-import Icon from '../../../shared/components/icon'
-import { sendMB } from '../../../infrastructure/event-tracking'
-import usePersistedState from '../../../shared/hooks/use-persisted-state'
-import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
-import { useProjectContext } from '../../../shared/context/project-context'
-
-const SAY_THANKS_TIMEOUT = 10 * 1000
-
-function FasterCompilesFeedbackContent() {
-  const { clsiServerId, deliveryLatencies, pdfFile, pdfUrl } =
-    useCompileContext()
-  const { _id: projectId } = useProjectContext()
-
-  const [incrementalCompiles, setIncrementalCompiles] = useState(0)
-  const [hasRatedProject, setHasRatedProject] = usePersistedState(
-    `faster-compiles-feedback:${projectId}`,
-    false,
-    true
-  )
-  const [dismiss, setDismiss] = usePersistedState(
-    'faster-compiles-feedback:dismiss',
-    false,
-    true
-  )
-  const [sayThanks, setSayThanks] = useState(false)
-  const lastClsiServerId = useRef<string | undefined>(undefined)
-  const lastPdfUrl = useRef<string | undefined>(undefined)
-
-  useEffect(() => {
-    if (
-      !pdfUrl ||
-      !lastPdfUrl.current ||
-      clsiServerId !== lastClsiServerId.current
-    ) {
-      // Reset history after
-      // - clearing cache / server error (both reset pdfUrl)
-      // - initial compile after reset of pdfUrl
-      // - switching the clsi server, aka we get a _slow_ full compile.
-      setIncrementalCompiles(0)
-      lastClsiServerId.current = clsiServerId
-    } else {
-      setIncrementalCompiles(n => n + 1)
-    }
-    lastPdfUrl.current = pdfUrl
-  }, [clsiServerId, lastPdfUrl, pdfUrl, setIncrementalCompiles])
-
-  function submitFeedback(feedback = '') {
-    sendMB('faster-compiles-feedback', {
-      projectId,
-      server: clsiServerId?.includes('-c2d-') ? 'faster' : 'normal',
-      feedback,
-      pdfSize: pdfFile?.size,
-      ...deliveryLatencies,
-    })
-    setHasRatedProject(true)
-    setSayThanks(true)
-    window.setTimeout(() => {
-      setSayThanks(false)
-    }, SAY_THANKS_TIMEOUT)
-  }
-
-  function dismissFeedback() {
-    sendMB('faster-compiles-feedback-dismiss')
-    setDismiss(true)
-  }
-
-  const { t } = useTranslation()
-
-  // Hide the feedback prompt in all these cases:
-  // - the initial compile (0), its always perceived as _slow_.
-  // - the first incremental compile (1), its always _faster_ than ^.
-  // - the user has dismissed the prompt
-  // - the user has rated compile speed already (say thanks if needed)
-  switch (true) {
-    case sayThanks:
-      return (
-        <Alert
-          bsStyle="info"
-          className="faster-compiles-feedback"
-          onClick={() => setSayThanks(false)}
-        >
-          {t('faster_compiles_feedback_thanks')}
-        </Alert>
-      )
-    case dismiss || hasRatedProject:
-      return null
-    case incrementalCompiles > 1:
-      return (
-        <Alert bsStyle="info" className="faster-compiles-feedback">
-          <button
-            type="button"
-            aria-label={t('dismiss')}
-            className="btn-inline-link faster-compiles-feedback-dismiss"
-            onClick={dismissFeedback}
-          >
-            <Icon type="close" fw />
-          </button>
-          {t('faster_compiles_feedback_question')}
-          <div className="faster-compiles-feedback-options">
-            {['slower', 'same', 'faster'].map(feedback => (
-              <Button
-                bsStyle="default"
-                bsSize="xs"
-                className="faster-compiles-feedback-option"
-                onClick={() => submitFeedback(feedback)}
-                key={feedback}
-              >
-                {feedback === 'faster'
-                  ? t('faster_compiles_feedback_seems_faster')
-                  : feedback === 'same'
-                    ? t('faster_compiles_feedback_seems_same')
-                    : t('faster_compiles_feedback_seems_slower')}
-              </Button>
-            ))}
-          </div>
-        </Alert>
-      )
-    default:
-      return null
-  }
-}
-
-function FasterCompilesFeedback() {
-  const { showFasterCompilesFeedbackUI } = useCompileContext()
-
-  if (!showFasterCompilesFeedbackUI) {
-    return null
-  }
-  return <FasterCompilesFeedbackContent />
-}
-
-export default memo(FasterCompilesFeedback)

+ 3 - 7
services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.jsx

@@ -3,7 +3,6 @@ import { memo } from 'react'
 import classnames from 'classnames'
 import PdfValidationIssue from './pdf-validation-issue'
 import StopOnFirstErrorPrompt from './stop-on-first-error-prompt'
-import TimeoutUpgradePrompt from './timeout-upgrade-prompt'
 import TimeoutUpgradePromptNew from './timeout-upgrade-prompt-new'
 import PdfPreviewError from './pdf-preview-error'
 import PdfClearCacheButton from './pdf-clear-cache-button'
@@ -20,12 +19,12 @@ function PdfLogsViewer() {
   const {
     codeCheckFailed,
     error,
+    hasShortCompileTimeout,
     logEntries,
     rawLog,
     validationIssues,
     showLogs,
     stoppedOnFirstError,
-    showNewCompileTimeoutUI,
   } = useCompileContext()
 
   const { loadingError } = usePdfPreviewContext()
@@ -45,13 +44,10 @@ function PdfLogsViewer() {
 
         {loadingError && <PdfPreviewError error="pdf-viewer-loading-error" />}
 
-        {showNewCompileTimeoutUI && error === 'timedout' ? (
+        {hasShortCompileTimeout && error === 'timedout' ? (
           <TimeoutUpgradePromptNew />
         ) : (
-          <>
-            {error && <PdfPreviewError error={error} />}
-            {error === 'timedout' && <TimeoutUpgradePrompt />}
-          </>
+          <>{error && <PdfPreviewError error={error} />}</>
         )}
 
         {validationIssues &&

+ 3 - 10
services/web/frontend/js/features/pdf-preview/components/pdf-preview-pane.jsx

@@ -5,14 +5,12 @@ import PdfViewer from './pdf-viewer'
 import { FullSizeLoadingSpinner } from '../../../shared/components/loading-spinner'
 import PdfHybridPreviewToolbar from './pdf-preview-hybrid-toolbar'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
-import FasterCompilesFeedback from './faster-compiles-feedback'
 import { PdfPreviewMessages } from './pdf-preview-messages'
-import CompileTimeWarning from './compile-time-warning'
-import CompileTimeoutMessages from './compile-timeout-messages'
+import CompileTimeWarningUpgradePrompt from './compile-time-warning-upgrade-prompt'
 import { PdfPreviewProvider } from './pdf-preview-provider'
 
 function PdfPreviewPane() {
-  const { pdfUrl, showNewCompileTimeoutUI } = useCompileContext()
+  const { pdfUrl, hasShortCompileTimeout } = useCompileContext()
   const classes = classNames('pdf', 'full-size', {
     'pdf-empty': !pdfUrl,
   })
@@ -21,16 +19,11 @@ function PdfPreviewPane() {
       <PdfPreviewProvider>
         <PdfHybridPreviewToolbar />
         <PdfPreviewMessages>
-          {showNewCompileTimeoutUI ? (
-            <CompileTimeoutMessages />
-          ) : (
-            <CompileTimeWarning />
-          )}
+          {hasShortCompileTimeout && <CompileTimeWarningUpgradePrompt />}
         </PdfPreviewMessages>
         <Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
           <div className="pdf-viewer">
             <PdfViewer />
-            <FasterCompilesFeedback />
           </div>
         </Suspense>
         <PdfLogsViewer />

+ 61 - 100
services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt-new.tsx

@@ -13,7 +13,6 @@ function TimeoutUpgradePromptNew() {
     startCompile,
     lastCompileOptions,
     setAnimateCompileDropdownArrow,
-    showNewCompileTimeoutUI,
     isProjectOwner,
   } = useDetachCompileContext()
 
@@ -27,35 +26,25 @@ function TimeoutUpgradePromptNew() {
     setAnimateCompileDropdownArrow(true)
   }, [enableStopOnFirstError, startCompile, setAnimateCompileDropdownArrow])
 
-  if (!window.ExposedSettings.enableSubscriptions) {
-    return null
-  }
-
-  const compileTimeChanging = showNewCompileTimeoutUI === 'changing'
-
   return (
     <>
-      <CompileTimeout
-        compileTimeChanging={compileTimeChanging}
-        isProjectOwner={isProjectOwner}
-      />
-      <PreventTimeoutHelpMessage
-        compileTimeChanging={compileTimeChanging}
-        handleEnableStopOnFirstErrorClick={handleEnableStopOnFirstErrorClick}
-        lastCompileOptions={lastCompileOptions}
-        isProjectOwner={isProjectOwner}
-      />
+      <CompileTimeout isProjectOwner={isProjectOwner} />
+      {window.ExposedSettings.enableSubscriptions && (
+        <PreventTimeoutHelpMessage
+          handleEnableStopOnFirstErrorClick={handleEnableStopOnFirstErrorClick}
+          lastCompileOptions={lastCompileOptions}
+          isProjectOwner={isProjectOwner}
+        />
+      )}
     </>
   )
 }
 
 type CompileTimeoutProps = {
-  compileTimeChanging?: boolean
   isProjectOwner: boolean
 }
 
 const CompileTimeout = memo(function CompileTimeout({
-  compileTimeChanging,
   isProjectOwner,
 }: CompileTimeoutProps) {
   const { t } = useTranslation()
@@ -67,58 +56,48 @@ const CompileTimeout = memo(function CompileTimeout({
     <PdfLogEntry
       headerTitle={t('your_compile_timed_out')}
       formattedContent={
-        <>
-          <p>
-            {isProjectOwner
-              ? t('your_project_exceeded_compile_timeout_limit_on_free_plan')
-              : t('this_project_exceeded_compile_timeout_limit_on_free_plan')}
-          </p>
-          {isProjectOwner ? (
+        window.ExposedSettings.enableSubscriptions && (
+          <>
             <p>
-              {compileTimeChanging ? (
-                <>
-                  <strong>{t('upgrade_for_plenty_more_compile_time')}</strong>{' '}
-                  {t(
-                    'plus_additional_collaborators_document_history_track_changes_and_more'
-                  )}
-                </>
-              ) : (
-                <>
-                  <strong>{t('upgrade_for_12x_more_compile_time')}</strong>{' '}
-                  {t(
-                    'plus_additional_collaborators_document_history_track_changes_and_more'
-                  )}
-                </>
-              )}
+              {isProjectOwner
+                ? t('your_project_exceeded_compile_timeout_limit_on_free_plan')
+                : t('this_project_exceeded_compile_timeout_limit_on_free_plan')}
             </p>
-          ) : (
-            <Trans
-              i18nKey="tell_the_project_owner_and_ask_them_to_upgrade"
-              components={[
-                // eslint-disable-next-line react/jsx-key
-                <strong />,
-              ]}
-            />
-          )}
-
-          {isProjectOwner && (
-            <p className="text-center">
-              <StartFreeTrialButton
-                variant={compileTimeChanging ? 'new-changing' : 'new-20s'}
-                source="compile-timeout"
-                buttonProps={{
-                  bsStyle: 'success',
-                  className: 'row-spaced-small',
-                  block: true,
-                }}
-              >
-                {hasNewPaywallCta
-                  ? t('get_more_compile_time')
-                  : t('start_a_free_trial')}
-              </StartFreeTrialButton>
-            </p>
-          )}
-        </>
+            {isProjectOwner ? (
+              <p>
+                <strong>{t('upgrade_for_12x_more_compile_time')}</strong>{' '}
+                {t(
+                  'plus_additional_collaborators_document_history_track_changes_and_more'
+                )}
+              </p>
+            ) : (
+              <Trans
+                i18nKey="tell_the_project_owner_and_ask_them_to_upgrade"
+                components={[
+                  // eslint-disable-next-line react/jsx-key
+                  <strong />,
+                ]}
+              />
+            )}
+
+            {isProjectOwner && (
+              <p className="text-center">
+                <StartFreeTrialButton
+                  source="compile-timeout"
+                  buttonProps={{
+                    bsStyle: 'success',
+                    className: 'row-spaced-small',
+                    block: true,
+                  }}
+                >
+                  {hasNewPaywallCta
+                    ? t('get_more_compile_time')
+                    : t('start_a_free_trial')}
+                </StartFreeTrialButton>
+              </p>
+            )}
+          </>
+        )
       }
       // @ts-ignore
       entryAriaLabel={t('your_compile_timed_out')}
@@ -128,14 +107,12 @@ const CompileTimeout = memo(function CompileTimeout({
 })
 
 type PreventTimeoutHelpMessageProps = {
-  compileTimeChanging?: boolean
   lastCompileOptions: any
   handleEnableStopOnFirstErrorClick: () => void
   isProjectOwner: boolean
 }
 
 const PreventTimeoutHelpMessage = memo(function PreventTimeoutHelpMessage({
-  compileTimeChanging,
   lastCompileOptions,
   handleEnableStopOnFirstErrorClick,
   isProjectOwner,
@@ -227,35 +204,19 @@ const PreventTimeoutHelpMessage = memo(function PreventTimeoutHelpMessage({
           </p>
           <p>
             <em>
-              {compileTimeChanging ? (
-                <>
-                  {isProjectOwner ? (
-                    <Trans
-                      i18nKey="were_in_the_process_of_reducing_compile_timeout_which_may_affect_your_project"
-                      components={[compileTimeoutChangesBlogLink]}
-                    />
-                  ) : (
-                    <Trans
-                      i18nKey="were_in_the_process_of_reducing_compile_timeout_which_may_affect_this_project"
-                      components={[compileTimeoutChangesBlogLink]}
-                    />
-                  )}
-                </>
-              ) : (
-                <>
-                  {isProjectOwner ? (
-                    <Trans
-                      i18nKey="weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project"
-                      components={[compileTimeoutChangesBlogLink]}
-                    />
-                  ) : (
-                    <Trans
-                      i18nKey="weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project"
-                      components={[compileTimeoutChangesBlogLink]}
-                    />
-                  )}
-                </>
-              )}
+              <>
+                {isProjectOwner ? (
+                  <Trans
+                    i18nKey="weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project"
+                    components={[compileTimeoutChangesBlogLink]}
+                  />
+                ) : (
+                  <Trans
+                    i18nKey="weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project"
+                    components={[compileTimeoutChangesBlogLink]}
+                  />
+                )}
+              </>
             </em>
           </p>
         </>

+ 0 - 62
services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt.jsx

@@ -1,62 +0,0 @@
-import { useTranslation } from 'react-i18next'
-import { useEditorContext } from '../../../shared/context/editor-context'
-import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
-import { memo } from 'react'
-import PdfLogEntry from './pdf-log-entry'
-import UpgradeBenefits from '../../../shared/components/upgrade-benefits'
-import { useSplitTestContext } from '@/shared/context/split-test-context'
-
-function TimeoutUpgradePrompt() {
-  const { t } = useTranslation()
-
-  const { hasPremiumCompile, isProjectOwner } = useEditorContext()
-
-  const { splitTestVariants } = useSplitTestContext()
-  const hasNewPaywallCta = splitTestVariants['paywall-cta'] === 'enabled'
-
-  if (!window.ExposedSettings.enableSubscriptions || hasPremiumCompile) {
-    return null
-  }
-
-  return (
-    <PdfLogEntry
-      headerTitle={
-        isProjectOwner
-          ? t('upgrade_for_longer_compiles')
-          : t('ask_proj_owner_to_upgrade_for_longer_compiles')
-      }
-      formattedContent={
-        <>
-          <p>{t('free_accounts_have_timeout_upgrade_to_increase')}</p>
-          <p>{t('plus_upgraded_accounts_receive')}:</p>
-          <div>
-            <UpgradeBenefits />
-          </div>
-          {isProjectOwner && (
-            <p className="text-center">
-              <StartFreeTrialButton
-                source="compile-timeout"
-                buttonProps={{
-                  bsStyle: 'success',
-                  className: 'row-spaced-small',
-                }}
-              >
-                {hasNewPaywallCta
-                  ? t('get_more_compile_time')
-                  : t('start_free_trial')}
-              </StartFreeTrialButton>
-            </p>
-          )}
-        </>
-      }
-      entryAriaLabel={
-        isProjectOwner
-          ? t('upgrade_for_longer_compiles')
-          : t('ask_proj_owner_to_upgrade_for_longer_compiles')
-      }
-      level="success"
-    />
-  )
-}
-
-export default memo(TimeoutUpgradePrompt)

+ 0 - 8
services/web/frontend/js/features/pdf-preview/util/compiler.js

@@ -221,14 +221,6 @@ export default class DocumentCompiler {
       params.file_line_errors = 'true'
     }
 
-    // temporary override to force the new compile timeout
-    const newCompileTimeoutOverride = new URLSearchParams(
-      window.location.search
-    ).get('force_new_compile_timeout')
-    if (newCompileTimeoutOverride) {
-      params.set('force_new_compile_timeout', newCompileTimeoutOverride)
-    }
-
     return params
   }
 

+ 10 - 19
services/web/frontend/js/shared/context/detach-compile-context.tsx

@@ -29,6 +29,7 @@ export const DetachCompileProvider: FC = ({ children }) => {
     error: _error,
     fileList: _fileList,
     hasChanges: _hasChanges,
+    hasShortCompileTimeout: _hasShortCompileTimeout,
     highlights: _highlights,
     isProjectOwner: _isProjectOwner,
     lastCompileOptions: _lastCompileOptions,
@@ -52,8 +53,6 @@ export const DetachCompileProvider: FC = ({ children }) => {
     setStopOnValidationError: _setStopOnValidationError,
     showLogs: _showLogs,
     showCompileTimeWarning: _showCompileTimeWarning,
-    showNewCompileTimeoutUI: _showNewCompileTimeoutUI,
-    showFasterCompilesFeedbackUI: _showFasterCompilesFeedbackUI,
     stopOnFirstError: _stopOnFirstError,
     stopOnValidationError: _stopOnValidationError,
     stoppedOnFirstError: _stoppedOnFirstError,
@@ -126,6 +125,12 @@ export const DetachCompileProvider: FC = ({ children }) => {
     'detacher',
     'detached'
   )
+  const [hasShortCompileTimeout] = useDetachStateWatcher(
+    'hasShortCompileTimeout',
+    _hasShortCompileTimeout,
+    'detacher',
+    'detached'
+  )
   const [highlights] = useDetachStateWatcher(
     'highlights',
     _highlights,
@@ -192,18 +197,6 @@ export const DetachCompileProvider: FC = ({ children }) => {
     'detacher',
     'detached'
   )
-  const [showNewCompileTimeoutUI] = useDetachStateWatcher(
-    'showNewCompileTimeoutUI',
-    _showNewCompileTimeoutUI,
-    'detacher',
-    'detached'
-  )
-  const [showFasterCompilesFeedbackUI] = useDetachStateWatcher(
-    'showFasterCompilesFeedbackUI',
-    _showFasterCompilesFeedbackUI,
-    'detacher',
-    'detached'
-  )
   const [stopOnFirstError] = useDetachStateWatcher(
     'stopOnFirstError',
     _stopOnFirstError,
@@ -386,6 +379,7 @@ export const DetachCompileProvider: FC = ({ children }) => {
       error,
       fileList,
       hasChanges,
+      hasShortCompileTimeout,
       highlights,
       isProjectOwner,
       lastCompileOptions,
@@ -413,8 +407,6 @@ export const DetachCompileProvider: FC = ({ children }) => {
       setStopOnValidationError,
       showLogs,
       showCompileTimeWarning,
-      showNewCompileTimeoutUI,
-      showFasterCompilesFeedbackUI,
       startCompile,
       stopCompile,
       stopOnFirstError,
@@ -437,10 +429,11 @@ export const DetachCompileProvider: FC = ({ children }) => {
       compiling,
       deliveryLatencies,
       draft,
-      error,
       editedSinceCompileStarted,
+      error,
       fileList,
       hasChanges,
+      hasShortCompileTimeout,
       highlights,
       isProjectOwner,
       lastCompileOptions,
@@ -466,8 +459,6 @@ export const DetachCompileProvider: FC = ({ children }) => {
       setStopOnValidationError,
       showCompileTimeWarning,
       showLogs,
-      showNewCompileTimeoutUI,
-      showFasterCompilesFeedbackUI,
       startCompile,
       stopCompile,
       stopOnFirstError,

+ 12 - 21
services/web/frontend/js/shared/context/local-compile-context.tsx

@@ -49,6 +49,7 @@ export type CompileContext = {
   error?: string
   fileList?: Record<string, any>
   hasChanges: boolean
+  hasShortCompileTimeout: boolean
   highlights?: Record<string, any>[]
   isProjectOwner: boolean
   logEntries?: Record<string, any>
@@ -72,8 +73,6 @@ export type CompileContext = {
   setStopOnValidationError: (value: boolean) => void
   showCompileTimeWarning: boolean
   showLogs: boolean
-  showNewCompileTimeoutUI?: string
-  showFasterCompilesFeedbackUI: boolean
   stopOnFirstError: boolean
   stopOnValidationError: boolean
   stoppedOnFirstError: boolean
@@ -103,11 +102,7 @@ export const LocalCompileProvider: FC = ({ children }) => {
 
   const { hasPremiumCompile, isProjectOwner } = useEditorContext()
 
-  const {
-    _id: projectId,
-    rootDocId,
-    showNewCompileTimeoutUI,
-  } = useProjectContext()
+  const { _id: projectId, rootDocId } = useProjectContext()
 
   const { pdfPreviewOpen } = useLayoutContext()
 
@@ -122,6 +117,8 @@ export const LocalCompileProvider: FC = ({ children }) => {
   // whether to show the compile time warning
   const [showCompileTimeWarning, setShowCompileTimeWarning] = useState(false)
 
+  const [hasShortCompileTimeout, setHasShortCompileTimeout] = useState(false)
+
   // the log entries parsed from the compile output log
   const [logEntries, setLogEntries] = useScopeValueSetterOnly('pdf.logEntries')
 
@@ -182,10 +179,6 @@ export const LocalCompileProvider: FC = ({ children }) => {
   // whether the logs should be visible
   const [showLogs, setShowLogs] = useState(false)
 
-  // whether the faster compiles feedback UI should be displayed
-  const [showFasterCompilesFeedbackUI, setShowFasterCompilesFeedbackUI] =
-    useState(false)
-
   // whether the compile dropdown arrow should be animated
   const [animateCompileDropdownArrow, setAnimateCompileDropdownArrow] =
     useState(false)
@@ -332,10 +325,13 @@ export const LocalCompileProvider: FC = ({ children }) => {
   }, [compiledOnce, currentDoc, compiler])
 
   useEffect(() => {
-    const compileTimeWarningEnabled =
+    setHasShortCompileTimeout(
       features?.compileTimeout !== undefined && features.compileTimeout <= 60
+    )
+  }, [features])
 
-    if (compileTimeWarningEnabled && compiling && isProjectOwner) {
+  useEffect(() => {
+    if (hasShortCompileTimeout && compiling && isProjectOwner) {
       const timeout = window.setTimeout(() => {
         setShowCompileTimeWarning(true)
       }, 30000)
@@ -344,7 +340,7 @@ export const LocalCompileProvider: FC = ({ children }) => {
         window.clearTimeout(timeout)
       }
     }
-  }, [compiling, isProjectOwner, features])
+  }, [compiling, isProjectOwner, hasShortCompileTimeout])
 
   const { splitTestVariants } = useSplitTestContext()
 
@@ -358,9 +354,6 @@ export const LocalCompileProvider: FC = ({ children }) => {
       if (data.clsiServerId) {
         setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
       }
-      setShowFasterCompilesFeedbackUI(
-        Boolean(data.showFasterCompilesFeedbackUI)
-      )
 
       if (data.outputFiles) {
         const outputFiles = new Map()
@@ -606,6 +599,7 @@ export const LocalCompileProvider: FC = ({ children }) => {
       error,
       fileList,
       hasChanges,
+      hasShortCompileTimeout,
       highlights,
       isProjectOwner,
       lastCompileOptions,
@@ -633,8 +627,6 @@ export const LocalCompileProvider: FC = ({ children }) => {
       setStopOnFirstError,
       setStopOnValidationError,
       showLogs,
-      showNewCompileTimeoutUI,
-      showFasterCompilesFeedbackUI,
       startCompile,
       stopCompile,
       stopOnFirstError,
@@ -661,6 +653,7 @@ export const LocalCompileProvider: FC = ({ children }) => {
       error,
       fileList,
       hasChanges,
+      hasShortCompileTimeout,
       highlights,
       isProjectOwner,
       lastCompileOptions,
@@ -683,8 +676,6 @@ export const LocalCompileProvider: FC = ({ children }) => {
       setStopOnValidationError,
       showCompileTimeWarning,
       showLogs,
-      showNewCompileTimeoutUI,
-      showFasterCompilesFeedbackUI,
       startCompile,
       stopCompile,
       stopOnFirstError,

+ 0 - 17
services/web/frontend/js/shared/context/project-context.tsx

@@ -30,7 +30,6 @@ const ProjectContext = createContext<
         _id: UserId
         email: string
       }
-      showNewCompileTimeoutUI?: string
       tags: {
         _id: string
         name: string
@@ -74,7 +73,6 @@ export const ProjectProvider: FC = ({ children }) => {
     features,
     publicAccesLevel: publicAccessLevel,
     owner,
-    showNewCompileTimeoutUI,
     trackChangesState,
   } = project || projectFallback
 
@@ -86,17 +84,6 @@ export const ProjectProvider: FC = ({ children }) => {
     []
   )
 
-  // temporary override for new compile timeout
-  const forceNewCompileTimeout = new URLSearchParams(
-    window.location.search
-  ).get('force_new_compile_timeout')
-  const newCompileTimeoutOverride =
-    forceNewCompileTimeout === 'active'
-      ? 'active'
-      : forceNewCompileTimeout === 'changing'
-        ? 'changing'
-        : undefined
-
   const value = useMemo(() => {
     return {
       _id,
@@ -107,8 +94,6 @@ export const ProjectProvider: FC = ({ children }) => {
       features,
       publicAccessLevel,
       owner,
-      showNewCompileTimeoutUI:
-        newCompileTimeoutOverride || showNewCompileTimeoutUI,
       tags,
       trackChangesState,
     }
@@ -121,8 +106,6 @@ export const ProjectProvider: FC = ({ children }) => {
     features,
     publicAccessLevel,
     owner,
-    showNewCompileTimeoutUI,
-    newCompileTimeoutOverride,
     tags,
     trackChangesState,
   ])

+ 2 - 2
services/web/frontend/stories/pdf-preview-messages.stories.jsx

@@ -2,7 +2,7 @@ import { ScopeDecorator } from './decorators/scope'
 import { useLocalCompileContext } from '../js/shared/context/local-compile-context'
 import { useEffect } from 'react'
 import { PdfPreviewMessages } from '../js/features/pdf-preview/components/pdf-preview-messages'
-import CompileTimeWarning from '../js/features/pdf-preview/components/compile-time-warning'
+import CompileTimeWarningUpgradePrompt from '@/features/pdf-preview/components/compile-time-warning-upgrade-prompt'
 
 export default {
   title: 'Editor / PDF Preview / Messages',
@@ -20,7 +20,7 @@ export const Dismissible = () => {
   return (
     <div style={{ width: 800, position: 'relative' }}>
       <PdfPreviewMessages>
-        <CompileTimeWarning />
+        <CompileTimeWarningUpgradePrompt />
       </PdfPreviewMessages>
     </div>
   )

+ 2 - 37
services/web/frontend/stories/pdf-preview-messages.stories.tsx

@@ -1,10 +1,6 @@
-import { useEffect } from 'react'
 import { ScopeDecorator } from './decorators/scope'
-import { useLocalCompileContext } from '@/shared/context/local-compile-context'
 import { PdfPreviewMessages } from '@/features/pdf-preview/components/pdf-preview-messages'
-import CompileTimeWarning from '@/features/pdf-preview/components/compile-time-warning'
-import { CompileTimeoutChangingSoon } from '@/features/pdf-preview/components/compile-timeout-changing-soon'
-import { CompileTimeoutWarning } from '@/features/pdf-preview/components/compile-timeout-warning'
+import { CompileTimeWarningUpgradePromptInner } from '@/features/pdf-preview/components/compile-time-warning-upgrade-prompt-inner'
 
 export default {
   title: 'Editor / PDF Preview / Messages',
@@ -21,40 +17,9 @@ export default {
   ],
 }
 
-export const CompileTime = () => {
-  const { setShowCompileTimeWarning } = useLocalCompileContext()
-
-  useEffect(() => {
-    setShowCompileTimeWarning(true)
-  }, [setShowCompileTimeWarning])
-
-  return <CompileTimeWarning />
-}
-
-export const CompileTimeoutChangingSoonNotProjectOwner = (args: any) => {
-  return <CompileTimeoutChangingSoon {...args} />
-}
-CompileTimeoutChangingSoonNotProjectOwner.argTypes = {
-  handleDismissChangingSoon: { action: 'dismiss changing soon' },
-}
-
-export const CompileTimeoutChangingSoonProjectOwner = (args: any) => {
-  return <CompileTimeoutChangingSoon {...args} isProjectOwner />
-}
-CompileTimeoutChangingSoonProjectOwner.argTypes = {
-  handleDismissChangingSoon: { action: 'dismiss changing soon' },
-}
-
 export const CompileTimeoutWarningActive = (args: any) => {
-  return <CompileTimeoutWarning {...args} showNewCompileTimeoutUI="active" />
+  return <CompileTimeWarningUpgradePromptInner {...args} />
 }
 CompileTimeoutWarningActive.argTypes = {
   handleDismissWarning: { action: 'dismiss warning' },
 }
-
-export const CompileTimeoutWarningChanging = (args: any) => {
-  return <CompileTimeoutWarning {...args} showNewCompileTimeoutUI="changing" />
-}
-CompileTimeoutWarningChanging.argTypes = {
-  handleDismissWarning: { action: 'dismiss warning' },
-}

+ 0 - 21
services/web/locales/en.json

@@ -106,7 +106,6 @@
   "an_email_has_already_been_sent_to": "An email has already been sent to <0>__email__</0>. Please wait and try again later.",
   "an_error_occurred_when_verifying_the_coupon_code": "An error occurred when verifying the coupon code",
   "and": "and",
-  "and_you_can_upgrade_for_plenty_more_compile_time": "And you can <strong>upgrade to get plenty more compile time.</strong>",
   "annual": "Annual",
   "annual_billing_enabled": "Annual billing enabled",
   "anonymous": "Anonymous",
@@ -115,7 +114,6 @@
   "app_on_x": "__appName__ on __social__",
   "apply_educational_discount": "Apply educational discount",
   "apply_educational_discount_info": "Overleaf offers a 40% educational discount for groups of 10 or more. Applies to students or faculty using Overleaf for teaching.",
-  "approaching_compile_timeout_limit_upgrade_for_more_compile_time": "You are approaching your compile timeout limit. Upgrade to <strong>Overleaf Premium</strong> for <0>4x more</0> compile time.",
   "april": "April",
   "archive": "Archive",
   "archive_projects": "Archive Projects",
@@ -132,7 +130,6 @@
   "ascending": "Ascending",
   "ask_proj_owner_to_unlink_from_current_github": "Ask the owner of the project (<0>__projectOwnerEmail__</0>) to unlink the project from the current GitHub repository and create a connection to a different repository.",
   "ask_proj_owner_to_upgrade_for_full_history": "Please ask the project owner to upgrade to access this project’s full history.",
-  "ask_proj_owner_to_upgrade_for_longer_compiles": "Please ask the project owner to upgrade to increase the timeout limit.",
   "ask_proj_owner_to_upgrade_for_references_search": "Please ask the project owner to upgrade to use the References Search feature.",
   "ask_repo_owner_to_reconnect": "Ask the GitHub repository owner (<0>__repoOwnerEmail__</0>) to reconnect the project.",
   "ask_repo_owner_to_renew_overleaf_subscription": "Ask the GitHub repository owner (<0>__repoOwnerEmail__</0>) to renew their __appName__ subscription and reconnect the project.",
@@ -293,7 +290,6 @@
   "compile_timeout_short": "Compile timeout",
   "compile_timeout_short_info_basic": "This is how much time you get to compile your project on the Overleaf servers. You may need additional time for longer or more complex projects.",
   "compiler": "Compiler",
-  "compiles_on_our_free_plan_are_now_on_faster_servers": "Compiles on our free plan are now on faster servers, and we’re gradually <0>introducing a shorter compile timeout limit</0>. This project currently exceeds the new limit, so it might not compile in future.",
   "compiling": "Compiling",
   "complete": "Complete",
   "compliance": "Compliance",
@@ -420,7 +416,6 @@
   "disabling": "Disabling",
   "disconnected": "Disconnected",
   "discount_of": "Discount of __amount__",
-  "dismiss": "Dismiss",
   "dismiss_error_popup": "Dismiss first error alert",
   "display_deleted_user": "Display deleted users",
   "do_not_have_acct_or_do_not_want_to_link": "If you don’t have an <b>__appName__</b> account, or if you don’t want to link to your <b>__institutionName__</b> account, please click <b>__clickText__</b>.",
@@ -579,11 +574,6 @@
   "faq_what_is_the_difference_between_users_and_collaborators_answer_second_paragraph": "In other words, collaborators are just other Overleaf users that you are working with on one of your projects.",
   "faq_what_is_the_difference_between_users_and_collaborators_question": "What’s the difference between users and collaborators?",
   "fast": "Fast",
-  "faster_compiles_feedback_question": "Was this compile different than usual?",
-  "faster_compiles_feedback_seems_faster": "Faster",
-  "faster_compiles_feedback_seems_same": "Same",
-  "faster_compiles_feedback_seems_slower": "Slower",
-  "faster_compiles_feedback_thanks": "Thanks for the feedback!",
   "fastest": "Fastest",
   "feature_included": "Feature included",
   "feature_not_included": "Feature not included",
@@ -650,7 +640,6 @@
   "free": "Free",
   "free_7_day_trial_billed_annually": "Free 7-day trial, then billed annually",
   "free_7_day_trial_billed_monthly": "Free 7-day trial, then billed monthly",
-  "free_accounts_have_timeout_upgrade_to_increase": "Free accounts have a one minute timeout, whereas upgraded accounts have a timeout of four minutes.",
   "free_dropbox_and_history": "Free Dropbox and History",
   "free_plan_label": "You’re on the <b>free plan</b>",
   "free_plan_tooltip": "Click to find out how you could benefit from Overleaf premium features.",
@@ -1371,7 +1360,6 @@
   "please_wait": "Please wait",
   "plus_additional_collaborators_document_history_track_changes_and_more": "(plus additional collaborators, document history, track changes, and more).",
   "plus_more": "plus more",
-  "plus_upgraded_accounts_receive": "Plus with an upgraded account you get",
   "popular_tags": "Popular Tags",
   "portal_add_affiliation_to_join": "It looks like you are already logged in to __appName__. If you have a __portalTitle__ email you can add it now.",
   "position": "Position",
@@ -1454,7 +1442,6 @@
   "react_history_tutorial_content": "To compare a range of versions, use the  <0></0> on the versions you want at the start and end of the range. To add a label or to download a version use the options in the three-dot menu. <1>Learn more about using Overleaf History.</1>",
   "react_history_tutorial_title": "History actions have a new home",
   "reactivate_subscription": "Reactivate your subscription",
-  "read_more_about_fix_prevent_timeout": "Read more about how to fix and prevent timeouts",
   "read_more_about_free_compile_timeouts_servers": "Read more about changes to free compile timeouts and servers",
   "read_only": "Read Only",
   "read_only_token": "Read-Only Token",
@@ -1864,7 +1851,6 @@
   "tc_switch_guests_tip": "Toggle track-changes for all link-sharing guests",
   "tc_switch_user_tip": "Toggle track-changes for this user",
   "tell_the_project_owner_and_ask_them_to_upgrade": "<0>Tell the project owner</0> and ask them to upgrade their Overleaf plan if you need more compile time.",
-  "tell_the_project_owner_to_upgrade_plan_for_more_compile_time": "<strong>Tell the project owner</strong> and ask them to upgrade their Overleaf plan if you need more compile time.",
   "template": "Template",
   "template_approved_by_publisher": "This template has been approved by the publisher",
   "template_description": "Template Description",
@@ -1918,7 +1904,6 @@
   "this_field_is_required": "This field is required",
   "this_grants_access_to_features_2": "This grants you access to <0>__appName__</0> <0>__featureType__</0> features.",
   "this_is_your_template": "This is your template from your project",
-  "this_project_compiled_but_soon_might_not": "This project compiled, but soon it might not",
   "this_project_exceeded_compile_timeout_limit_on_free_plan": "This project exceeded the compile timeout limit on our free plan.",
   "this_project_is_public": "This project is public and can be edited by anyone with the URL.",
   "this_project_is_public_read_only": "This project is public and can be viewed but not edited by anyone with the URL",
@@ -2086,8 +2071,6 @@
   "upgrade": "Upgrade",
   "upgrade_cc_btn": "Upgrade now, pay after 7 days",
   "upgrade_for_12x_more_compile_time": "Upgrade to get 12x more compile time",
-  "upgrade_for_longer_compiles": "Upgrade to increase your timeout limit.",
-  "upgrade_for_plenty_more_compile_time": "Upgrade to get plenty more compile time.",
   "upgrade_now": "Upgrade Now",
   "upgrade_to_get_feature": "Upgrade to get __feature__, plus:",
   "upgrade_to_track_changes": "Upgrade to track changes",
@@ -2155,8 +2138,6 @@
   "website_status": "Website status",
   "wed_love_you_to_stay": "We’d love you to stay",
   "welcome_to_sl": "Welcome to __appName__",
-  "were_in_the_process_of_reducing_compile_timeout_which_may_affect_this_project": "We’re in the process of <0>reducing the compile timeout limit</0> on our free plan, which may affect this project in future.",
-  "were_in_the_process_of_reducing_compile_timeout_which_may_affect_your_project": "We’re in the process of <0>reducing the compile timeout limit</0> on our free plan, which may affect your project in future.",
   "were_performing_maintenance": "We’re performing maintenance on Overleaf and you need to wait a moment. Sorry for any inconvenience. The editor will refresh automatically in __seconds__ seconds.",
   "weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_this_project": "We’ve recently <0>reduced the compile timeout limit</0> on our free plan, which may have affected this project.",
   "weve_recently_reduced_the_compile_timeout_limit_which_may_have_affected_your_project": "We’ve recently <0>reduced the compile timeout limit</0> on our free plan, which may have affected your project.",
@@ -2227,7 +2208,6 @@
   "you_have_been_removed_from_this_project_and_will_be_redirected_to_project_dashboard": "You have been removed from this project, and will no longer have access to it. You will be redirected to your project dashboard momentarily.",
   "you_introed_high_number": " You’ve introduced <0>__numberOfPeople__</0> people to __appName__. Good job!",
   "you_introed_small_number": " You’ve introduced <0>__numberOfPeople__</0> person to __appName__. Good job, but can you get some more?",
-  "you_may_be_able_to_fix_issues_to_speed_up_the_compile": "You may be able to fix issues to <0>speed up the compile</0>.",
   "you_need_to_configure_your_sso_settings": "You need to configure and test your SSO settings before enabling SSO",
   "you_not_introed_anyone_to_sl": "You’ve not introduced anyone to __appName__ yet. Get sharing!",
   "you_plus_1": "You + 1",
@@ -2255,7 +2235,6 @@
   "your_password_has_been_successfully_changed": "Your password has been successfully changed",
   "your_plan": "Your plan",
   "your_plan_is_changing_at_term_end": "Your plan is changing to <0>__pendingPlanName__</0> at the end of the current billing period.",
-  "your_project_compiled_but_soon_might_not": "Your project compiled, but soon it might not",
   "your_project_exceeded_compile_timeout_limit_on_free_plan": "Your project exceeded the compile timeout limit on our free plan.",
   "your_project_near_compile_timeout_limit": "Your project is near the compile timeout limit for our free plan.",
   "your_projects": "Your Projects",

+ 57 - 0
services/web/scripts/migration_compile_timeout_60s_to_20s.js

@@ -0,0 +1,57 @@
+#!/usr/bin/env node
+
+const minimist = require('minimist')
+const {
+  db,
+  READ_PREFERENCE_SECONDARY,
+  waitForDb,
+} = require('../app/src/infrastructure/mongodb')
+const { batchedUpdate } = require('./helpers/batchedUpdate')
+
+async function logCount() {
+  const count60s = await db.users.countDocuments(
+    { 'features.compileTimeout': { $lte: 60, $ne: 20 } },
+    { readPreference: READ_PREFERENCE_SECONDARY }
+  )
+  const count20s = await db.users.countDocuments(
+    { 'features.compileTimeout': 20 },
+    { readPreference: READ_PREFERENCE_SECONDARY }
+  )
+  console.log(`Found ${count60s} users with compileTimeout <= 60s && != 20s`)
+  console.log(`Found ${count20s} users with compileTimeout == 20s`)
+}
+
+const main = async ({ COMMIT }) => {
+  console.time('Script Duration')
+
+  await waitForDb()
+
+  await logCount()
+
+  if (COMMIT) {
+    const nModified = await batchedUpdate(
+      'users',
+      { 'features.compileTimeout': { $lte: 60, $ne: 20 } },
+      { $set: { 'features.compileTimeout': 20 } }
+    )
+    console.log(`Updated ${nModified} records`)
+  }
+
+  console.timeEnd('Script Duration')
+}
+
+const setup = () => {
+  const argv = minimist(process.argv.slice(2))
+  const COMMIT = argv.commit !== undefined
+  if (!COMMIT) {
+    console.warn('Doing dry run. Add --commit to commit changes')
+  }
+  return { COMMIT }
+}
+
+main(setup())
+  .catch(err => {
+    console.error(err)
+    process.exit(1)
+  })
+  .then(() => process.exit(0))

+ 134 - 0
services/web/test/acceptance/src/MigrateUserFeatureTimeoutTests.js

@@ -0,0 +1,134 @@
+const { expect } = require('chai')
+const { promisify } = require('node:util')
+const { exec } = require('node:child_process')
+const logger = require('@overleaf/logger')
+const { db } = require('../../../app/src/infrastructure/mongodb')
+
+async function runScript(args = []) {
+  try {
+    return await promisify(exec)(
+      ['node', 'scripts/migration_compile_timeout_60s_to_20s.js', ...args].join(
+        ' '
+      )
+    )
+  } catch (error) {
+    logger.error({ error }, 'script failed')
+    throw error
+  }
+}
+
+describe('MigrateUserFeatureTimeoutTests', function () {
+  const usersInput = {
+    noFeatures: {},
+    noFeatureTimeout: { features: {} },
+    timeout10s: { features: { compileTimeout: 10, other: 'val1' }, bar: '1' },
+    timeout20s: { features: { compileTimeout: 20, other: 'val2' }, bar: '2' },
+    timeout30s: { features: { compileTimeout: 30, other: 'val3' }, bar: '3' },
+    timeout60s: { features: { compileTimeout: 60, other: 'val4' }, bar: '4' },
+    timeout120s: { features: { compileTimeout: 120, other: 'val5' }, bar: '5' },
+    timeout180s: { features: { compileTimeout: 180, other: 'val6' }, bar: '6' },
+  }
+
+  const usersKeys = Object.keys(usersInput)
+  const userIds = {}
+
+  beforeEach('insert users', async function () {
+    const usersInsertedValues = await db.users.insertMany(
+      usersKeys.map(key => ({
+        ...usersInput[key],
+        email: `${key}@example.com`,
+      }))
+    )
+    usersKeys.forEach(
+      (key, index) => (userIds[key] = usersInsertedValues.insertedIds[index])
+    )
+  })
+
+  it('gives correct counts in dry mode', async function () {
+    const users = await db.users.find().toArray()
+    expect(users).to.have.lengthOf(usersKeys.length)
+
+    const result = await runScript([])
+
+    expect(result.stderr).to.contain(
+      'Doing dry run. Add --commit to commit changes'
+    )
+    expect(result.stdout).to.contain(
+      'Found 3 users with compileTimeout <= 60s && != 20s'
+    )
+    expect(result.stdout).to.contain('Found 1 users with compileTimeout == 20s')
+    expect(result.stdout).not.to.contain('Updated')
+
+    const usersAfter = await db.users.find().toArray()
+
+    expect(usersAfter).to.deep.equal(users)
+  })
+
+  it("updates users compileTimeout when '--commit' is set", async function () {
+    const users = await db.users.find().toArray()
+    expect(users).to.have.lengthOf(usersKeys.length)
+    const result = await runScript(['--commit'])
+
+    expect(result.stdout).to.contain(
+      'Found 3 users with compileTimeout <= 60s && != 20s'
+    )
+    expect(result.stdout).to.contain('Found 1 users with compileTimeout == 20s')
+    expect(result.stdout).to.contain('Updated 3 records')
+
+    const usersAfter = await db.users.find().toArray()
+
+    expect(usersAfter).to.deep.equal([
+      { _id: userIds.noFeatures, email: 'noFeatures@example.com' },
+      {
+        _id: userIds.noFeatureTimeout,
+        email: 'noFeatureTimeout@example.com',
+        features: {},
+      },
+      {
+        _id: userIds.timeout10s,
+        email: 'timeout10s@example.com',
+        features: { compileTimeout: 20, other: 'val1' },
+        bar: '1',
+      },
+      {
+        _id: userIds.timeout20s,
+        email: 'timeout20s@example.com',
+        features: { compileTimeout: 20, other: 'val2' },
+        bar: '2',
+      },
+      {
+        _id: userIds.timeout30s,
+        email: 'timeout30s@example.com',
+        features: { compileTimeout: 20, other: 'val3' },
+        bar: '3',
+      },
+      {
+        _id: userIds.timeout60s,
+        email: 'timeout60s@example.com',
+        features: { compileTimeout: 20, other: 'val4' },
+        bar: '4',
+      },
+      {
+        _id: userIds.timeout120s,
+        email: 'timeout120s@example.com',
+        features: { compileTimeout: 120, other: 'val5' },
+        bar: '5',
+      },
+      {
+        _id: userIds.timeout180s,
+        email: 'timeout180s@example.com',
+        features: { compileTimeout: 180, other: 'val6' },
+        bar: '6',
+      },
+    ])
+
+    const result2 = await runScript([])
+
+    expect(result2.stdout).to.contain(
+      'Found 0 users with compileTimeout <= 60s && != 20s'
+    )
+    expect(result2.stdout).to.contain(
+      'Found 4 users with compileTimeout == 20s'
+    )
+  })
+})

+ 0 - 1
services/web/test/unit/src/Compile/ClsiManagerTests.js

@@ -120,7 +120,6 @@ describe('ClsiManager', function () {
         },
         clsi: {
           url: `http://${CLSI_HOST}`,
-          defaultBackendClass: 'e2',
           submissionBackendClass: 'n2d',
         },
         clsi_priority: {

+ 0 - 1
services/web/test/unit/src/Compile/CompileControllerTests.js

@@ -32,7 +32,6 @@ describe('CompileController', function () {
       apis: {
         clsi: {
           url: 'http://clsi.example.com',
-          defaultBackendClass: 'e2',
           submissionBackendClass: 'n2d',
         },
         clsi_priority: {

+ 13 - 27
services/web/test/unit/src/Compile/CompileManagerTests.js

@@ -23,7 +23,7 @@ describe('CompileManager', function () {
       requires: {
         '@overleaf/settings': (this.settings = {
           apis: {
-            clsi: { defaultBackendClass: 'e2', submissionBackendClass: 'n2d' },
+            clsi: { submissionBackendClass: 'n2d' },
           },
           redis: { web: { host: 'localhost', port: 42 } },
           rateLimit: { autoCompile: {} },
@@ -235,9 +235,8 @@ describe('CompileManager', function () {
         .calledWith(null, {
           timeout: this.timeout,
           compileGroup: this.group,
-          compileBackendClass: 'e2',
+          compileBackendClass: 'c2d',
           ownerAnalyticsId: 'abc',
-          showFasterCompilesFeedbackUI: false,
         })
         .should.equal(true)
     })
@@ -303,7 +302,7 @@ describe('CompileManager', function () {
             this.callback
           )
           this.callback
-            .calledWith(null, sinon.match({ timeout: 60 }))
+            .calledWith(null, sinon.match({ timeout: 20 }))
             .should.equal(true)
         })
         describe('user is in the compile-timeout-20s-existing-users treatment', function () {
@@ -388,20 +387,12 @@ describe('CompileManager', function () {
               this.callback
             )
             this.callback
-              .calledWith(null, sinon.match({ timeout: 60 }))
+              .calledWith(null, sinon.match({ timeout: 20 }))
               .should.equal(true)
           })
         })
 
         describe('user signed up after the second phase rollout', function () {
-          beforeEach(function () {
-            const signUpDate = new Date(
-              this.CompileManager.NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF_DEFAULT_BASELINE
-            )
-            signUpDate.setDate(signUpDate.getDate() + 1)
-            this.user.signUpDate = signUpDate
-          })
-
           it('should reduce compile timeout to 20s', function () {
             this.CompileManager.getProjectCompileLimits(
               this.project_id,
@@ -441,13 +432,12 @@ describe('CompileManager', function () {
             variant: 'default',
           })
         })
-        it('should return the e2 class and disable the ui', function (done) {
+        it('should return the n2d class and disable the ui', function (done) {
           this.CompileManager.getProjectCompileLimits(
             this.project_id,
-            (err, { compileBackendClass, showFasterCompilesFeedbackUI }) => {
+            (err, { compileBackendClass }) => {
               if (err) return done(err)
-              expect(compileBackendClass).to.equal('e2')
-              expect(showFasterCompilesFeedbackUI).to.equal(false)
+              expect(compileBackendClass).to.equal('n2d')
               done()
             }
           )
@@ -463,10 +453,9 @@ describe('CompileManager', function () {
         it('should return the n2d class and disable the ui', function (done) {
           this.CompileManager.getProjectCompileLimits(
             this.project_id,
-            (err, { compileBackendClass, showFasterCompilesFeedbackUI }) => {
+            (err, { compileBackendClass }) => {
               if (err) return done(err)
               expect(compileBackendClass).to.equal('n2d')
-              expect(showFasterCompilesFeedbackUI).to.equal(false)
               done()
             }
           )
@@ -489,10 +478,9 @@ describe('CompileManager', function () {
         it('should return the default class and disable ui', function (done) {
           this.CompileManager.getProjectCompileLimits(
             this.project_id,
-            (err, { compileBackendClass, showFasterCompilesFeedbackUI }) => {
+            (err, { compileBackendClass }) => {
               if (err) return done(err)
-              expect(compileBackendClass).to.equal('e2')
-              expect(showFasterCompilesFeedbackUI).to.equal(false)
+              expect(compileBackendClass).to.equal('c2d')
               done()
             }
           )
@@ -510,10 +498,9 @@ describe('CompileManager', function () {
           it('should return the default class and enable ui', function (done) {
             this.CompileManager.getProjectCompileLimits(
               this.project_id,
-              (err, { compileBackendClass, showFasterCompilesFeedbackUI }) => {
+              (err, { compileBackendClass }) => {
                 if (err) return done(err)
-                expect(compileBackendClass).to.equal('e2')
-                expect(showFasterCompilesFeedbackUI).to.equal(true)
+                expect(compileBackendClass).to.equal('c2d')
                 done()
               }
             )
@@ -530,10 +517,9 @@ describe('CompileManager', function () {
           it('should return the c2d class and enable ui', function (done) {
             this.CompileManager.getProjectCompileLimits(
               this.project_id,
-              (err, { compileBackendClass, showFasterCompilesFeedbackUI }) => {
+              (err, { compileBackendClass }) => {
                 if (err) return done(err)
                 expect(compileBackendClass).to.equal('c2d')
-                expect(showFasterCompilesFeedbackUI).to.equal(true)
                 done()
               }
             )