Просмотр исходного кода

Merge pull request #26100 from overleaf/dp-compile-timeout-paywall

Add compile timeout paywall to new editor

GitOrigin-RevId: 9742ae67b4103c72cc9d87852801ae8751f85d6d
David 1 год назад
Родитель
Сommit
a8a21e05af

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

@@ -287,6 +287,8 @@
   "compile_error_entry_description": "",
   "compile_error_entry_description": "",
   "compile_error_handling": "",
   "compile_error_handling": "",
   "compile_larger_projects": "",
   "compile_larger_projects": "",
+  "compile_limit_reached": "",
+  "compile_limit_upgrade_prompt": "",
   "compile_mode": "",
   "compile_mode": "",
   "compile_terminated_by_user": "",
   "compile_terminated_by_user": "",
   "compiler": "",
   "compiler": "",

+ 85 - 25
services/web/frontend/js/features/ide-redesign/components/pdf-preview/pdf-error-state.tsx

@@ -5,31 +5,37 @@ import { useRailContext } from '../../contexts/rail-context'
 import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider'
 import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider'
 import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
 import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
 import { useIsNewEditorEnabled } from '../../utils/new-editor-utils'
 import { useIsNewEditorEnabled } from '../../utils/new-editor-utils'
+import { upgradePlan } from '@/main/account-upgrade'
+import classNames from 'classnames'
 
 
 function PdfErrorState() {
 function PdfErrorState() {
   const { loadingError } = usePdfPreviewContext()
   const { loadingError } = usePdfPreviewContext()
   // TODO ide-redesign-cleanup: rename showLogs to something else and check usages
   // TODO ide-redesign-cleanup: rename showLogs to something else and check usages
-  const { showLogs } = useCompileContext()
-  const { t } = useTranslation()
-  const { openTab: openRailTab } = useRailContext()
+  const { hasShortCompileTimeout, error, showLogs } = useCompileContext()
   const newEditor = useIsNewEditorEnabled()
   const newEditor = useIsNewEditorEnabled()
 
 
   if (!newEditor || (!loadingError && !showLogs)) {
   if (!newEditor || (!loadingError && !showLogs)) {
     return null
     return null
   }
   }
 
 
+  if (hasShortCompileTimeout && error === 'timedout') {
+    return <CompileTimeoutErrorState />
+  }
+
+  return <GeneralErrorState />
+}
+
+const GeneralErrorState = () => {
+  const { t } = useTranslation()
+  const { openTab: openRailTab } = useRailContext()
+
   return (
   return (
-    <div className="pdf-error-state">
-      <div className="pdf-error-state-top-section">
-        <div className="pdf-error-state-warning-icon">
-          <MaterialIcon type="warning" />
-        </div>
-        <div className="pdf-error-state-text">
-          <p className="pdf-error-state-label">{t('pdf_couldnt_compile')}</p>
-          <p className="pdf-error-state-description">
-            {t('we_are_unable_to_generate_the_pdf_at_this_time')}
-          </p>
-        </div>
+    <ErrorState
+      title={t('pdf_couldnt_compile')}
+      description={t('we_are_unable_to_generate_the_pdf_at_this_time')}
+      iconType="warning"
+      iconClassName="pdf-error-state-warning-icon"
+      actions={
         <OLButton
         <OLButton
           variant="secondary"
           variant="secondary"
           size="sm"
           size="sm"
@@ -39,20 +45,74 @@ function PdfErrorState() {
         >
         >
           {t('check_logs')}
           {t('check_logs')}
         </OLButton>
         </OLButton>
-      </div>
-      <div className="pdf-error-state-info-box">
-        <div className="pdf-error-state-info-box-title">
-          <MaterialIcon type="info" unfilled />
-          {t('why_might_this_happen')}
+      }
+      extraContent={
+        <div className="pdf-error-state-info-box">
+          <div className="pdf-error-state-info-box-title">
+            <MaterialIcon type="info" unfilled />
+            {t('why_might_this_happen')}
+          </div>
+          <ul className="pdf-error-state-info-box-text">
+            <li>{t('there_is_an_unrecoverable_latex_error')}</li>
+            <li>{t('the_document_environment_contains_no_content')}</li>
+            <li>{t('this_project_contains_a_file_called_output')}</li>
+          </ul>
         </div>
         </div>
-        <ul className="pdf-error-state-info-box-text">
-          <li>{t('there_is_an_unrecoverable_latex_error')}</li>
-          <li>{t('the_document_environment_contains_no_content')}</li>
-          <li>{t('this_project_contains_a_file_called_output')}</li>
-        </ul>
+      }
+    />
+  )
+}
+
+const CompileTimeoutErrorState = () => {
+  const { t } = useTranslation()
+
+  return (
+    <ErrorState
+      title={t('compile_limit_reached')}
+      description={t('compile_limit_upgrade_prompt')}
+      iconType="running_with_errors"
+      actions={
+        <OLButton
+          variant="premium"
+          size="sm"
+          onClick={() => upgradePlan('compile-timeout')}
+        >
+          {t('upgrade')}
+        </OLButton>
+      }
+    />
+  )
+}
+
+const ErrorState = ({
+  title,
+  description,
+  iconType,
+  actions,
+  iconClassName,
+  extraContent,
+}: {
+  title: string
+  description: string
+  iconType: string
+  actions: React.ReactNode
+  iconClassName?: string
+  extraContent?: React.ReactNode
+}) => {
+  return (
+    <div className="pdf-error-state">
+      <div className="pdf-error-state-top-section">
+        <div className={classNames('pdf-error-state-icon', iconClassName)}>
+          <MaterialIcon type={iconType} />
+        </div>
+        <div className="pdf-error-state-text">
+          <p className="pdf-error-state-label">{title}</p>
+          <p className="pdf-error-state-description">{description}</p>
+        </div>
+        {actions}
       </div>
       </div>
+      {extraContent}
     </div>
     </div>
   )
   )
 }
 }
-
 export default PdfErrorState
 export default PdfErrorState

+ 10 - 3
services/web/frontend/stylesheets/bootstrap-5/pages/editor/pdf-error-state.scss

@@ -33,9 +33,7 @@
   padding: 0 var(--spacing-09) var(--spacing-09) var(--spacing-09);
   padding: 0 var(--spacing-09) var(--spacing-09) var(--spacing-09);
 }
 }
 
 
-.pdf-error-state-warning-icon {
-  background-color: var(--bg-danger-03);
-  color: var(--content-danger);
+.pdf-error-state-icon {
   width: 80px;
   width: 80px;
   height: 80px;
   height: 80px;
   display: flex;
   display: flex;
@@ -43,6 +41,15 @@
   justify-content: center;
   justify-content: center;
   border-radius: 100%;
   border-radius: 100%;
 
 
+  .material-symbols {
+    font-size: 80px;
+  }
+}
+
+.pdf-error-state-warning-icon {
+  background-color: var(--bg-danger-03);
+  color: var(--content-danger);
+
   .material-symbols {
   .material-symbols {
     font-size: 32px;
     font-size: 32px;
   }
   }

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

@@ -370,6 +370,8 @@
   "compile_error_entry_description": "An error which prevented this project from compiling",
   "compile_error_entry_description": "An error which prevented this project from compiling",
   "compile_error_handling": "Compile error handling",
   "compile_error_handling": "Compile error handling",
   "compile_larger_projects": "Compile larger projects",
   "compile_larger_projects": "Compile larger projects",
+  "compile_limit_reached": "Compile limit reached",
+  "compile_limit_upgrade_prompt": "Your document took longer than the free plan’s compile window. Upgrade to Overleaf Premium for extended compile durations, priority build servers, and uninterrupted LaTeX processing—so you can focus on writing, not waiting.",
   "compile_mode": "Compile mode",
   "compile_mode": "Compile mode",
   "compile_servers": "Compile servers",
   "compile_servers": "Compile servers",
   "compile_servers_info_new": "The servers used to compile your project. Compiles for users on paid plans always run on the fastest available servers.",
   "compile_servers_info_new": "The servers used to compile your project. Compiles for users on paid plans always run on the fastest available servers.",