Przeglądaj źródła

Merge pull request #8361 from overleaf/em-halt-on-error-events

Analytics events for "Stop on first error"

GitOrigin-RevId: cbdb054cdf031f0364390c36cc1c1865169f2cd4
Eric Mc Sween 4 lat temu
rodzic
commit
3d0f464637

+ 5 - 3
services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.js

@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
 import { memo } from 'react'
 import classnames from 'classnames'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
+import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 import PdfCompileButtonInner from './pdf-compile-button-inner'
 import getMeta from '../../../utils/meta'
 
@@ -16,7 +17,6 @@ function PdfCompileButton() {
     hasChanges,
     setAutoCompile,
     setDraft,
-    setStopOnFirstError,
     setStopOnValidationError,
     stopOnFirstError,
     stopOnValidationError,
@@ -24,6 +24,8 @@ function PdfCompileButton() {
     stopCompile,
     recompileFromScratch,
   } = useCompileContext()
+  const { enableStopOnFirstError, disableStopOnFirstError } =
+    useStopOnFirstError({ eventSource: 'dropdown' })
 
   const { t } = useTranslation()
   const showStopOnFirstError = getMeta('ol-showStopOnFirstError')
@@ -90,14 +92,14 @@ function PdfCompileButton() {
         )}
 
         {showStopOnFirstError && (
-          <MenuItem onSelect={() => setStopOnFirstError(true)}>
+          <MenuItem onSelect={enableStopOnFirstError}>
             <Icon type={stopOnFirstError ? 'check' : ''} fw />
             {t('stop_on_first_error')}
           </MenuItem>
         )}
 
         {showStopOnFirstError && (
-          <MenuItem onSelect={() => setStopOnFirstError(false)}>
+          <MenuItem onSelect={disableStopOnFirstError}>
             <Icon type={!stopOnFirstError ? 'check' : ''} fw />
             {t('try_to_compile_despite_errors')}
           </MenuItem>

+ 90 - 80
services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js

@@ -1,15 +1,14 @@
 import PropTypes from 'prop-types'
 import { useTranslation, Trans } from 'react-i18next'
-import { memo } from 'react'
+import { memo, useCallback } from 'react'
 import { Button } from 'react-bootstrap'
 import PdfLogEntry from './pdf-log-entry'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
+import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 import getMeta from '../../../utils/meta'
 
 function PdfPreviewError({ error }) {
   const { t } = useTranslation()
-  const { lastCompileOptions, setStopOnFirstError, startCompile } =
-    useCompileContext()
 
   switch (error) {
     case 'rendering-error':
@@ -75,82 +74,8 @@ function PdfPreviewError({ error }) {
         </ErrorLogEntry>
       )
 
-    case 'timedout': {
-      const showStopOnFirstError = getMeta('ol-showStopOnFirstError')
-      if (showStopOnFirstError) {
-        return (
-          <ErrorLogEntry title={t('timedout')}>
-            <p>{t('project_timed_out_intro')}</p>
-            <ul>
-              <li>
-                <Trans
-                  i18nKey="project_timed_out_optimize_images"
-                  components={[
-                    // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
-                    <a href="https://www.overleaf.com/learn/how-to/Optimising_very_large_image_files" />,
-                  ]}
-                />
-              </li>
-              <li>
-                <Trans
-                  i18nKey="project_timed_out_fatal_error"
-                  components={[
-                    // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
-                    <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F#Fatal_compile_errors_blocking_the_compilation" />,
-                  ]}
-                />
-                {!lastCompileOptions.stopOnFirstError && (
-                  <>
-                    {' '}
-                    <Trans
-                      i18nKey="project_timed_out_enable_stop_on_first_error"
-                      components={[
-                        // eslint-disable-next-line react/jsx-key
-                        <Button
-                          bsSize="xs"
-                          bsStyle="info"
-                          onClick={() => {
-                            startCompile({ stopOnFirstError: true })
-                            setStopOnFirstError(true)
-                          }}
-                        />,
-                      ]}
-                    />
-                  </>
-                )}
-              </li>
-            </ul>
-            <p>
-              <Trans
-                i18nKey="project_timed_out_learn_more"
-                components={{
-                  link: (
-                    // eslint-disable-next-line jsx-a11y/anchor-has-content
-                    <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F" />
-                  ),
-                }}
-              />
-            </p>
-          </ErrorLogEntry>
-        )
-      } else {
-        return (
-          <ErrorLogEntry title={t('timedout')}>
-            {t('proj_timed_out_reason')}
-
-            <div>
-              <a
-                href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F"
-                target="_blank"
-                rel="noopener"
-              >
-                {t('learn_how_to_make_documents_compile_quickly')}
-              </a>
-            </div>
-          </ErrorLogEntry>
-        )
-      }
-    }
+    case 'timedout':
+      return <TimedOutLogEntry />
 
     case 'failure':
       return (
@@ -233,8 +158,93 @@ function ErrorLogEntry({ title, children }) {
     />
   )
 }
-
 ErrorLogEntry.propTypes = {
   title: PropTypes.string.isRequired,
   children: PropTypes.any.isRequired,
 }
+
+function TimedOutLogEntry() {
+  const { t } = useTranslation()
+  const { enableStopOnFirstError } = useStopOnFirstError({
+    eventSource: 'timeout',
+  })
+  const { startCompile, lastCompileOptions } = useCompileContext()
+  const showStopOnFirstError = getMeta('ol-showStopOnFirstError')
+
+  const handleEnableStopOnFirstErrorClick = useCallback(() => {
+    enableStopOnFirstError()
+    startCompile({ stopOnFirstError: true })
+  }, [enableStopOnFirstError, startCompile])
+
+  if (showStopOnFirstError) {
+    return (
+      <ErrorLogEntry title={t('timedout')}>
+        <p>{t('project_timed_out_intro')}</p>
+        <ul>
+          <li>
+            <Trans
+              i18nKey="project_timed_out_optimize_images"
+              components={[
+                // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
+                <a href="https://www.overleaf.com/learn/how-to/Optimising_very_large_image_files" />,
+              ]}
+            />
+          </li>
+          <li>
+            <Trans
+              i18nKey="project_timed_out_fatal_error"
+              components={[
+                // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
+                <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F#Fatal_compile_errors_blocking_the_compilation" />,
+              ]}
+            />
+            {!lastCompileOptions.stopOnFirstError && (
+              <>
+                {' '}
+                <Trans
+                  i18nKey="project_timed_out_enable_stop_on_first_error"
+                  components={[
+                    // eslint-disable-next-line react/jsx-key
+                    <Button
+                      bsSize="xs"
+                      bsStyle="info"
+                      onClick={handleEnableStopOnFirstErrorClick}
+                    />,
+                  ]}
+                />
+              </>
+            )}
+          </li>
+        </ul>
+        <p>
+          <Trans
+            i18nKey="project_timed_out_learn_more"
+            components={{
+              link: (
+                // eslint-disable-next-line jsx-a11y/anchor-has-content
+                <a href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F" />
+              ),
+            }}
+          />
+        </p>
+      </ErrorLogEntry>
+    )
+  } else {
+    return (
+      <ErrorLogEntry title={t('timedout')}>
+        {t('proj_timed_out_reason')}
+
+        <div>
+          <a
+            href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F"
+            target="_blank"
+            rel="noopener"
+          >
+            {t('learn_how_to_make_documents_compile_quickly')}
+          </a>
+        </div>
+      </ErrorLogEntry>
+    )
+  }
+}
+TimedOutLogEntry.propTypes = {}

+ 13 - 9
services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js

@@ -1,11 +1,22 @@
+import { useCallback } from 'react'
 import { useTranslation, Trans } from 'react-i18next'
 import { Button } from 'react-bootstrap'
 import PdfLogEntry from './pdf-log-entry'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
+import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 
 export default function StopOnFirstErrorPrompt() {
   const { t } = useTranslation()
-  const { setStopOnFirstError, startCompile } = useCompileContext()
+  const { startCompile } = useCompileContext()
+  const { disableStopOnFirstError } = useStopOnFirstError({
+    eventSource: 'logs-pane',
+  })
+
+  const handleDisableButtonClick = useCallback(() => {
+    disableStopOnFirstError()
+    startCompile({ stopOnFirstError: false })
+  }, [disableStopOnFirstError, startCompile])
+
   return (
     <PdfLogEntry
       headerTitle={t('stop_on_first_error_enabled_title')}
@@ -16,14 +27,7 @@ export default function StopOnFirstErrorPrompt() {
             // eslint-disable-next-line react/jsx-key
             components={[<strong />]}
           />{' '}
-          <Button
-            bsSize="xs"
-            bsStyle="info"
-            onClick={() => {
-              startCompile({ stopOnFirstError: false })
-              setStopOnFirstError(false)
-            }}
-          >
+          <Button bsSize="xs" bsStyle="info" onClick={handleDisableButtonClick}>
             {t('disable_stop_on_first_error')}
           </Button>
         </>

+ 12 - 6
services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js

@@ -1,3 +1,4 @@
+import { useCallback } from 'react'
 import PropTypes from 'prop-types'
 import { Trans, useTranslation } from 'react-i18next'
 import { Button } from 'react-bootstrap'
@@ -5,18 +6,26 @@ import PreviewLogEntryHeader from './preview-log-entry-header'
 import Icon from '../../../shared/components/icon'
 import getMeta from '../../../utils/meta'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
+import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
 
 function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
   const { t } = useTranslation()
   const showStopOnFirstError = getMeta('ol-showStopOnFirstError')
-  const { startCompile, setStopOnFirstError, stoppedOnFirstError } =
-    useCompileContext()
+  const { startCompile, stoppedOnFirstError } = useCompileContext()
+  const { enableStopOnFirstError } = useStopOnFirstError({
+    eventSource: 'too-many-logs',
+  })
 
   const title = t('log_entry_maximum_entries_title', {
     total: totalEntries,
     displayed: entriesShown,
   })
 
+  const handleEnableStopOnFirstErrorClick = useCallback(() => {
+    enableStopOnFirstError()
+    startCompile({ stopOnFirstError: true })
+  }, [enableStopOnFirstError, startCompile])
+
   return (
     <div className="log-entry" aria-label={t('log_entry_maximum_entries')}>
       <PreviewLogEntryHeader level="raw" headerTitle={title} />
@@ -35,10 +44,7 @@ function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
                       <Button
                         bsSize="xs"
                         bsStyle="info"
-                        onClick={() => {
-                          startCompile({ stopOnFirstError: true })
-                          setStopOnFirstError(true)
-                        }}
+                        onClick={handleEnableStopOnFirstErrorClick}
                       />
                     ),
                     'learn-more-link': (

+ 5 - 1
services/web/frontend/js/shared/context/local-compile-context.js

@@ -293,7 +293,10 @@ export function LocalCompileProvider({ children }) {
             )
 
             // sample compile stats for real users
-            if (!window.user.alphaProgram && data.status === 'success') {
+            if (
+              !window.user.alphaProgram &&
+              ['success', 'stopped-on-first-error'].includes(data.status)
+            ) {
               sendMBSampled(
                 'compile-result',
                 {
@@ -301,6 +304,7 @@ export function LocalCompileProvider({ children }) {
                   warnings: result.logEntries.warnings.length,
                   typesetting: result.logEntries.typesetting.length,
                   newPdfPreview: true, // TODO: is this useful?
+                  stopOnFirstError: data.options.stopOnFirstError,
                 },
                 0.01
               )

+ 34 - 0
services/web/frontend/js/shared/hooks/use-stop-on-first-error.js

@@ -0,0 +1,34 @@
+import { useCallback } from 'react'
+import { useDetachCompileContext as useCompileContext } from '../context/detach-compile-context'
+import { useProjectContext } from '../context/project-context'
+import * as eventTracking from '../../infrastructure/event-tracking'
+
+export function useStopOnFirstError(opts = {}) {
+  const { eventSource } = opts
+  const { stopOnFirstError, setStopOnFirstError } = useCompileContext()
+  const { _id: projectId } = useProjectContext()
+
+  const enableStopOnFirstError = useCallback(() => {
+    if (!stopOnFirstError) {
+      const opts = { projectId }
+      if (eventSource) {
+        opts.source = eventSource
+      }
+      eventTracking.sendMB('stop-on-first-error-enabled', opts)
+    }
+    setStopOnFirstError(true)
+  }, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
+
+  const disableStopOnFirstError = useCallback(() => {
+    const opts = { projectId }
+    if (eventSource) {
+      opts.source = eventSource
+    }
+    if (stopOnFirstError) {
+      eventTracking.sendMB('stop-on-first-error-disabled', opts)
+    }
+    setStopOnFirstError(false)
+  }, [eventSource, projectId, stopOnFirstError, setStopOnFirstError])
+
+  return { enableStopOnFirstError, disableStopOnFirstError }
+}