Explorar el Código

Record each completion selection for analytics (#13665)

GitOrigin-RevId: bc8e92ceca51f6365c4311204a35fc85914969b0
Alf Eaton hace 3 años
padre
commit
48e758a5fe

+ 2 - 2
services/web/frontend/js/features/source-editor/components/toolbar/button-menu.tsx

@@ -4,7 +4,7 @@ import Icon from '../../../../shared/components/icon'
 import useDropdown from '../../../../shared/hooks/use-dropdown'
 import Tooltip from '../../../../shared/components/tooltip'
 import { EditorView } from '@codemirror/view'
-import { emitCommandEvent } from '../../extensions/toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import { useCodeMirrorViewContext } from '../codemirror-editor'
 import MaterialIcon from '../../../../shared/components/material-icon'
 
@@ -38,7 +38,7 @@ export const ToolbarButtonMenu: FC<{
       }}
       onClick={event => {
         if (event.altKey && altCommand && open === false) {
-          emitCommandEvent(view, id)
+          emitToolbarEvent(view, id)
           event.preventDefault()
           altCommand(view)
           view.focus()

+ 2 - 2
services/web/frontend/js/features/source-editor/components/toolbar/insert-figure-dropdown.tsx

@@ -4,7 +4,7 @@ import Icon from '../../../../shared/components/icon'
 import { useCallback } from 'react'
 import { FigureModalSource } from '../figure-modal/figure-modal-context'
 import { useTranslation } from 'react-i18next'
-import { emitCommandEvent } from '../../extensions/toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import { useCodeMirrorViewContext } from '../codemirror-editor'
 import { insertFigure } from '../../extensions/toolbar/commands'
 
@@ -13,7 +13,7 @@ export const InsertFigureDropdown = () => {
   const view = useCodeMirrorViewContext()
   const openFigureModal = useCallback(
     (source: FigureModalSource, sourceName: string) => {
-      emitCommandEvent(view, `toolbar-figure-modal-${sourceName}`)
+      emitToolbarEvent(view, `toolbar-figure-modal-${sourceName}`)
       window.dispatchEvent(
         new CustomEvent('figure-modal:open', {
           detail: source,

+ 3 - 3
services/web/frontend/js/features/source-editor/components/toolbar/math-dropdown.tsx

@@ -1,6 +1,6 @@
 import { ListGroupItem } from 'react-bootstrap'
 import { ToolbarButtonMenu } from './button-menu'
-import { emitCommandEvent } from '../../extensions/toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import MaterialIcon from '../../../../shared/components/material-icon'
 import { useTranslation } from 'react-i18next'
 import { useCodeMirrorViewContext } from '../codemirror-editor'
@@ -23,7 +23,7 @@ export function MathDropdown() {
       <ListGroupItem
         aria-label={t('toolbar_insert_inline_math')}
         onClick={event => {
-          emitCommandEvent(view, 'toolbar-inline-math')
+          emitToolbarEvent(view, 'toolbar-inline-math')
           event.preventDefault()
           wrapInInlineMath(view)
           view.focus()
@@ -35,7 +35,7 @@ export function MathDropdown() {
       <ListGroupItem
         aria-label={t('toolbar_insert_display_math')}
         onClick={event => {
-          emitCommandEvent(view, 'toolbar-display-math')
+          emitToolbarEvent(view, 'toolbar-display-math')
           event.preventDefault()
           wrapInDisplayMath(view)
           view.focus()

+ 2 - 2
services/web/frontend/js/features/source-editor/components/toolbar/section-heading-dropdown.tsx

@@ -11,7 +11,7 @@ import { useCallback, useRef } from 'react'
 import { Overlay, Popover } from 'react-bootstrap'
 import useEventListener from '../../../../shared/hooks/use-event-listener'
 import useDropdown from '../../../../shared/hooks/use-dropdown'
-import { emitCommandEvent } from '../../extensions/toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import Icon from '../../../../shared/components/icon'
 import { useTranslation } from 'react-i18next'
 
@@ -90,7 +90,7 @@ export const SectionHeadingDropdown = () => {
                 role="menuitem"
                 key={level}
                 onClick={() => {
-                  emitCommandEvent(view, 'section-level-change')
+                  emitToolbarEvent(view, 'section-level-change')
                   setSectionHeadingLevel(view, level)
                   view.focus()
                   setOverflowOpen(false)

+ 2 - 2
services/web/frontend/js/features/source-editor/components/toolbar/toolbar-button.tsx

@@ -4,7 +4,7 @@ import { useCodeMirrorViewContext } from '../codemirror-editor'
 import { Button } from 'react-bootstrap'
 import classnames from 'classnames'
 import Tooltip from '../../../../shared/components/tooltip'
-import { emitCommandEvent } from '../../extensions/toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import Icon from '../../../../shared/components/icon'
 
 export const ToolbarButton = memo<{
@@ -38,7 +38,7 @@ export const ToolbarButton = memo<{
 
   const handleClick = useCallback(
     event => {
-      emitCommandEvent(view, id)
+      emitToolbarEvent(view, id)
       if (command) {
         event.preventDefault()
         command(view)

+ 28 - 0
services/web/frontend/js/features/source-editor/extensions/completion-logger.ts

@@ -0,0 +1,28 @@
+import { ViewPlugin } from '@codemirror/view'
+import { pickedCompletion } from '@codemirror/autocomplete'
+import { emitCompletionEvent } from './toolbar/utils/analytics'
+
+/**
+ * A custom view plugin that watches for transactions with the `pickedCompletion` annotation.
+ * If the completion label starts with a command, log that command for analytics.
+ */
+export const completionLogger = ViewPlugin.define(view => {
+  return {
+    update(update) {
+      for (const tr of update.transactions) {
+        const completion = tr.annotation(pickedCompletion)
+        if (completion) {
+          const command = completionCommand(completion.label)
+          if (command) {
+            emitCompletionEvent(view, command)
+          }
+        }
+      }
+    },
+  }
+})
+
+const completionCommand = (label: string): string | null => {
+  const matches = label.match(/^(\\\w+)/)
+  return matches ? matches[1] : null
+}

+ 2 - 0
services/web/frontend/js/features/source-editor/extensions/index.ts

@@ -48,6 +48,7 @@ import { highlightSpecialChars } from './highlight-special-chars'
 import { toolbarPanel } from './toolbar/toolbar-panel'
 import { geometryChangeEvent } from './geometry-change-event'
 import { isSplitTestEnabled } from '../../../utils/splitTestUtils'
+import { completionLogger } from './completion-logger'
 
 const moduleExtensions: Array<() => Extension> = importOverleafModules(
   'sourceEditorExtensions'
@@ -128,6 +129,7 @@ export const createExtensions = (options: Record<string, any>): Extension[] => [
   // The built-in extension that highlights the active line in the gutter.
   highlightActiveLineGutter(),
   inlineBackground(options.visual.visual),
+  completionLogger,
   codemirrorDevTools(),
   exceptionLogger(),
   // CodeMirror extensions provided by modules

+ 14 - 2
services/web/frontend/js/features/source-editor/extensions/toolbar/utils/analytics.ts

@@ -2,7 +2,19 @@ import { EditorView } from '@codemirror/view'
 import { sendMB } from '../../../../../infrastructure/event-tracking'
 import { isVisual } from '../../visual/visual'
 
-export function emitCommandEvent(view: EditorView, command: string) {
+export function emitCommandEvent(
+  view: EditorView,
+  key: string,
+  command: string
+) {
   const mode = isVisual(view) ? 'visual' : 'source'
-  sendMB('codemirror-toolbar-event', { command, mode })
+  sendMB(key, { command, mode })
+}
+
+export function emitToolbarEvent(view: EditorView, command: string) {
+  emitCommandEvent(view, 'codemirror-toolbar-event', command)
+}
+
+export function emitCompletionEvent(view: EditorView, command: string) {
+  emitCommandEvent(view, 'codemirror-completion-event', command)
 }

+ 2 - 2
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/editable-graphics.ts

@@ -1,7 +1,7 @@
 import { EditorView } from '@codemirror/view'
 import { GraphicsWidget } from './graphics'
 import { editFigureDataEffect } from '../../figure-modal'
-import { emitCommandEvent } from '../../toolbar/utils/analytics'
+import { emitToolbarEvent } from '../../toolbar/utils/analytics'
 
 export class EditableGraphicsWidget extends GraphicsWidget {
   setEditDispatcher(button: HTMLButtonElement, view: EditorView) {
@@ -12,7 +12,7 @@ export class EditableGraphicsWidget extends GraphicsWidget {
         event.stopImmediatePropagation()
         view.dispatch({ effects: editFigureDataEffect.of(this.figureData) })
         window.dispatchEvent(new CustomEvent('figure-modal:open-modal'))
-        emitCommandEvent(view, 'toolbar-figure-modal-edit')
+        emitToolbarEvent(view, 'toolbar-figure-modal-edit')
         return false
       }
     } else {