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

Merge pull request #34843 from overleaf/bg-add-compile-timer

add timer for compile with checkpoints

GitOrigin-RevId: ec758d34594f242a5cac8192233bfe2db783e42a
Brian Gough 1 месяц назад
Родитель
Сommit
23dca72cd3

+ 1 - 0
services/web/app/src/Features/Project/ProjectController.mjs

@@ -496,6 +496,7 @@ const _ProjectController = {
       'markdown-visual',
       'ai-disabled-collaborators',
       'group-link-sharing',
+      'compile-with-checkpoint',
     ].filter(Boolean)
 
     const getUserValues = async userId =>

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

@@ -1062,6 +1062,7 @@
   "language_suggestions": "",
   "last_active": "",
   "last_active_description": "",
+  "last_compile_duration": "",
   "last_edit": "",
   "last_logged_in": "",
   "last_modified": "",

BIN
services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2


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

@@ -0,0 +1,61 @@
+import { memo, useEffect, useRef, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useDetachCompileContext } from '@/shared/context/detach-compile-context'
+import MaterialIcon from '@/shared/components/material-icon'
+import OLTooltip from '@/shared/components/ol/ol-tooltip'
+
+function PdfCompileTime() {
+  const { t } = useTranslation()
+  const { compiling, deliveryLatencies } = useDetachCompileContext()
+
+  const startRef = useRef<number | null>(null)
+  const [elapsedMs, setElapsedMs] = useState<number | null>(null)
+
+  useEffect(() => {
+    if (!compiling) {
+      startRef.current = null
+      return
+    }
+    startRef.current = performance.now()
+    setElapsedMs(0)
+    const intervalId = window.setInterval(() => {
+      if (startRef.current !== null) {
+        setElapsedMs(performance.now() - startRef.current)
+      }
+    }, 100)
+    return () => {
+      window.clearInterval(intervalId)
+    }
+  }, [compiling])
+
+  const ms = compiling
+    ? (elapsedMs ?? 0)
+    : deliveryLatencies?.compileTimeClientE2E
+  if (ms == null) {
+    return null
+  }
+
+  const seconds = (ms / 1000).toFixed(1)
+
+  return (
+    <OLTooltip
+      id="pdf-compile-time"
+      description={t('last_compile_duration', { seconds })}
+      overlayProps={{ placement: 'bottom' }}
+    >
+      <span
+        className="toolbar-pdf-compile-time"
+        aria-label={t('last_compile_duration', { seconds })}
+      >
+        {compiling ? (
+          <MaterialIcon type="hourglass_top" />
+        ) : (
+          <MaterialIcon type="timer" />
+        )}
+        <span>{seconds} s</span>
+      </span>
+    </OLTooltip>
+  )
+}
+
+export default memo(PdfCompileTime)

+ 5 - 0
services/web/frontend/js/features/pdf-preview/components/pdf-preview-hybrid-toolbar.tsx

@@ -3,6 +3,7 @@ import { memo } from 'react'
 import { useTranslation } from 'react-i18next'
 import OLButtonToolbar from '@/shared/components/ol/ol-button-toolbar'
 import PdfCompileButton from '@/features/pdf-preview/components/pdf-compile-button'
+import PdfCompileTime from '@/features/pdf-preview/components/pdf-compile-time'
 import PdfHybridDownloadButton from '@/features/pdf-preview/components/pdf-hybrid-download-button'
 import { DetachedSynctexControl } from '@/features/pdf-preview/components/detach-synctex-control'
 import SwitchToEditorButton from '@/features/pdf-preview/components/switch-to-editor-button'
@@ -10,6 +11,7 @@ import PdfHybridLogsButton from '@/features/pdf-preview/components/pdf-hybrid-lo
 import PdfPreviewHybridToolbarOrphanRefreshInner from './pdf-preview-hybrid-toolbar-orphan-refresh-inner'
 import PdfPreviewHybridToolbarConnectingInner from './pdf-preview-hybrid-toolbar-connecting-inner'
 import useDetachedOrphanDetection from '../hooks/use-detached-orphan-detection'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
 
 function PdfPreviewHybridToolbar() {
   const { t } = useTranslation()
@@ -36,12 +38,15 @@ function PdfPreviewHybridToolbar() {
 
 function PdfPreviewHybridToolbarInner() {
   const { focusMode } = useLayoutContext()
+  const showCompileTimer = useFeatureFlag('compile-with-checkpoint')
+
   return (
     <>
       <div className="toolbar-pdf-left">
         <PdfCompileButton />
         <PdfHybridLogsButton />
         <PdfHybridDownloadButton />
+        {showCompileTimer && <PdfCompileTime />}
       </div>
       <div className="toolbar-pdf-right">
         <div className="toolbar-pdf-controls" id="toolbar-pdf-controls" />

+ 10 - 0
services/web/frontend/stylesheets/pages/editor/pdf.scss

@@ -376,6 +376,16 @@
   }
 }
 
+.toolbar-pdf-compile-time {
+  color: var(--toolbar-btn-color);
+  font-size: var(--font-size-02);
+  padding: 0 var(--spacing-04);
+  display: inline-flex;
+  align-items: center;
+  gap: var(--spacing-02);
+  white-space: nowrap;
+}
+
 .pdfjs-page-number-input {
   color: var(--toolbar-btn-color);
   font-size: var(--font-size-02);

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

@@ -1370,6 +1370,7 @@
   "larger_discounts_for_groups_of_20_plus": "Larger discounts for groups of 20+",
   "last_active": "Last Active",
   "last_active_description": "Last time a project was opened.",
+  "last_compile_duration": "Last compile = __seconds__ s",
   "last_edit": "Last edit",
   "last_logged_in": "Last logged in",
   "last_modified": "Last modified",

+ 52 - 0
services/web/test/frontend/components/pdf-preview/pdf-compile-time.test.tsx

@@ -0,0 +1,52 @@
+import { render, screen } from '@testing-library/react'
+import { expect } from 'chai'
+import PdfCompileTime from '../../../../frontend/js/features/pdf-preview/components/pdf-compile-time'
+import { DetachCompileContext } from '../../../../frontend/js/shared/context/detach-compile-context'
+
+function renderComponent(compileTimeClientE2E?: number) {
+  return render(
+    <DetachCompileContext.Provider
+      value={
+        {
+          compiling: false,
+          deliveryLatencies: {
+            compileTimeClientE2E,
+          },
+        } as any
+      }
+    >
+      <PdfCompileTime />
+    </DetachCompileContext.Provider>
+  )
+}
+
+describe('<PdfCompileTime />', function () {
+  it('does not render when compile duration is missing', function () {
+    renderComponent(undefined)
+
+    expect(screen.queryByText('1.2 s')).to.not.exist
+  })
+
+  it('renders when compile duration is available', function () {
+    renderComponent(1200)
+
+    screen.getByText('1.2 s')
+  })
+
+  it('renders a running timer while compiling', function () {
+    render(
+      <DetachCompileContext.Provider
+        value={
+          {
+            compiling: true,
+            deliveryLatencies: {},
+          } as any
+        }
+      >
+        <PdfCompileTime />
+      </DetachCompileContext.Provider>
+    )
+
+    screen.getByText('0.0 s')
+  })
+})

+ 12 - 0
services/web/test/unit/src/Project/ProjectController.test.mjs

@@ -909,6 +909,18 @@ describe('ProjectController', function () {
       })
     })
 
+    it('should request compile-with-checkpoint split test assignment', async function (ctx) {
+      await new Promise(resolve => {
+        ctx.res.render = () => {
+          expect(
+            ctx.SplitTestHandler.promises.getAssignment
+          ).to.have.been.calledWith(ctx.req, ctx.res, 'compile-with-checkpoint')
+          resolve()
+        }
+        ctx.ProjectController.loadEditor(ctx.req, ctx.res)
+      })
+    })
+
     it('should redirect to domain capture page', async function (ctx) {
       ctx.Features.hasFeature.withArgs('saas').returns(true)
       ctx.SplitTestHandler.promises.getAssignment