From ae9d84c279c1bc8a9e1e6ec5d60831cb64cfbc86 Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:31:23 +0100 Subject: [PATCH] Merge pull request #28392 from overleaf/dp-segment-editor-analytics Add editor-redesign segmentation to a bunch of analytics events GitOrigin-RevId: e8d2091028dab09de06362c38c5a17f32253e7cc --- .../components/download-pdf.tsx | 5 +++-- .../features/event-tracking/search-events.ts | 1 + .../ide-react/context/outline-context.tsx | 15 +++++++------- .../components/toolbar/download-project.tsx | 10 ++++++---- .../components/pdf-hybrid-download-button.tsx | 6 ++++-- .../hooks/use-presentation-mode.ts | 7 ++++--- .../js/shared/hooks/use-editor-analytics.ts | 15 +++++++++++--- .../js/components/full-project-search-ui.tsx | 8 ++++++-- .../frontend/js/util/search-snapshot.ts | 20 +++++++++++++------ 9 files changed, 58 insertions(+), 29 deletions(-) diff --git a/services/web/frontend/js/features/editor-left-menu/components/download-pdf.tsx b/services/web/frontend/js/features/editor-left-menu/components/download-pdf.tsx index ae756190b3..2031ca4a48 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/download-pdf.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/download-pdf.tsx @@ -1,18 +1,19 @@ import { useTranslation } from 'react-i18next' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' import { useProjectContext } from '../../../shared/context/project-context' -import * as eventTracking from '../../../infrastructure/event-tracking' import { isSmallDevice } from '../../../infrastructure/event-tracking' import MaterialIcon from '@/shared/components/material-icon' import OLTooltip from '@/shared/components/ol/ol-tooltip' +import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' export default function DownloadPDF() { const { t } = useTranslation() const { pdfDownloadUrl, pdfUrl } = useCompileContext() const { projectId } = useProjectContext() + const { sendEvent } = useEditorAnalytics() function sendDownloadEvent() { - eventTracking.sendMB('download-pdf-button-click', { + sendEvent('download-pdf-button-click', { projectId, location: 'left-menu', isSmallDevice, diff --git a/services/web/frontend/js/features/event-tracking/search-events.ts b/services/web/frontend/js/features/event-tracking/search-events.ts index 630d07aeaa..61f34e4fc2 100644 --- a/services/web/frontend/js/features/event-tracking/search-events.ts +++ b/services/web/frontend/js/features/event-tracking/search-events.ts @@ -17,6 +17,7 @@ type SearchEventSegmentation = { searchType: 'full-project' totalDocs: number totalResults: number + 'editor-redesign'?: 'enabled' } 'search-result-click': { searchType: 'full-project' diff --git a/services/web/frontend/js/features/ide-react/context/outline-context.tsx b/services/web/frontend/js/features/ide-react/context/outline-context.tsx index f69250a332..cfa0a56ceb 100644 --- a/services/web/frontend/js/features/ide-react/context/outline-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/outline-context.tsx @@ -10,12 +10,12 @@ import { } from 'react' import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter' import useEventListener from '@/shared/hooks/use-event-listener' -import * as eventTracking from '@/infrastructure/event-tracking' import { isValidTeXFile } from '@/main/is-valid-tex-file' import localStorage from '@/infrastructure/local-storage' import { useProjectContext } from '@/shared/context/project-context' import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' import { useFileTreeOpenContext } from './file-tree-open-context' +import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' export type PartialFlatOutline = { level: number @@ -53,6 +53,7 @@ export const OutlineProvider: FC = ({ children }) => { const [ignoreNextCursorUpdate, setIgnoreNextCursorUpdate] = useState(false) const [ignoreNextScroll, setIgnoreNextScroll] = useState(false) + const { sendEvent } = useEditorAnalytics() const goToLineEmitter = useScopeEventEmitter('editor:gotoLine', true) @@ -110,9 +111,9 @@ export const OutlineProvider: FC = ({ children }) => { gotoColumn: 0, syncToPdf, }) - eventTracking.sendMB('outline-jump-to-line') + sendEvent('outline-jump-to-line') }, - [goToLineEmitter] + [goToLineEmitter, sendEvent] ) const highlightedLine = useMemo( @@ -142,18 +143,18 @@ export const OutlineProvider: FC = ({ children }) => { const expandOutline = useCallback(() => { if (canShowOutline) { localStorage.setItem(storageKey, true) - eventTracking.sendMB('outline-expand') + sendEvent('outline-expand') setOutlineExpanded(true) } - }, [canShowOutline, storageKey]) + }, [canShowOutline, storageKey, sendEvent]) const collapseOutline = useCallback(() => { if (canShowOutline) { localStorage.setItem(storageKey, false) - eventTracking.sendMB('outline-collapse') + sendEvent('outline-collapse') setOutlineExpanded(false) } - }, [canShowOutline, storageKey]) + }, [canShowOutline, storageKey, sendEvent]) const toggleOutlineExpanded = useCallback(() => { if (outlineExpanded) { diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx index 4e34e77164..16cb17774e 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/download-project.tsx @@ -6,6 +6,7 @@ import { useDetachCompileContext as useCompileContext } from '@/shared/context/d import { useProjectContext } from '@/shared/context/project-context' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' export const DownloadProjectZip = () => { const { t } = useTranslation() @@ -45,13 +46,14 @@ export const DownloadProjectPDF = () => { const { t } = useTranslation() const { pdfDownloadUrl, pdfUrl } = useCompileContext() const { projectId } = useProjectContext() + const { sendEvent } = useEditorAnalytics() const sendDownloadEvent = useCallback(() => { - sendMB('download-pdf-button-click', { + sendEvent('download-pdf-button-click', { projectId, location: 'project-name-dropdown', isSmallDevice, }) - }, [projectId]) + }, [projectId, sendEvent]) useCommandProvider( () => [ @@ -60,7 +62,7 @@ export const DownloadProjectPDF = () => { disabled: !pdfUrl, href: pdfDownloadUrl || pdfUrl, handler: ({ location }) => { - sendMB('download-pdf-button-click', { + sendEvent('download-pdf-button-click', { projectId, location, isSmallDevice, @@ -69,7 +71,7 @@ export const DownloadProjectPDF = () => { label: t('download_as_pdf'), }, ], - [t, pdfUrl, projectId, pdfDownloadUrl] + [t, pdfUrl, projectId, pdfDownloadUrl, sendEvent] ) const button = ( diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx index 684550e170..8cb476b56b 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx @@ -1,13 +1,15 @@ import { useTranslation } from 'react-i18next' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' import { useProjectContext } from '@/shared/context/project-context' -import { sendMB, isSmallDevice } from '@/infrastructure/event-tracking' +import { isSmallDevice } from '@/infrastructure/event-tracking' import OLTooltip from '@/shared/components/ol/ol-tooltip' import OLButton from '@/shared/components/ol/ol-button' import MaterialIcon from '@/shared/components/material-icon' +import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' function PdfHybridDownloadButton() { const { pdfDownloadUrl } = useCompileContext() + const { sendEvent } = useEditorAnalytics() const { projectId } = useProjectContext() @@ -23,7 +25,7 @@ function PdfHybridDownloadButton() { return } - sendMB('download-pdf-button-click', { + sendEvent('download-pdf-button-click', { projectId, location: 'pdf-preview', isSmallDevice, diff --git a/services/web/frontend/js/features/pdf-preview/hooks/use-presentation-mode.ts b/services/web/frontend/js/features/pdf-preview/hooks/use-presentation-mode.ts index 32fe582435..b15ae09715 100644 --- a/services/web/frontend/js/features/pdf-preview/hooks/use-presentation-mode.ts +++ b/services/web/frontend/js/features/pdf-preview/hooks/use-presentation-mode.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import PDFJSWrapper from '../util/pdf-js-wrapper' -import { sendMB } from '@/infrastructure/event-tracking' import { debugConsole } from '@/utils/debugging' +import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' type StoredPDFState = { scrollMode?: number @@ -17,6 +17,7 @@ export default function usePresentationMode( setScale: (scale: string) => void ): () => void { const storedState = useRef({}) + const { sendEvent } = useEditorAnalytics() const [presentationMode, setPresentationMode] = useState(false) @@ -114,14 +115,14 @@ export default function usePresentationMode( }, [presentationMode, arrowKeyListener, clickListener, mouseWheelListener]) const requestPresentationMode = useCallback(() => { - sendMB('pdf-viewer-enter-presentation-mode') + sendEvent('pdf-viewer-enter-presentation-mode') if (pdfJsWrapper) { pdfJsWrapper.container.parentElement ?.requestFullscreen() .catch(debugConsole.error) } - }, [pdfJsWrapper]) + }, [pdfJsWrapper, sendEvent]) const handleEnterFullscreen = useCallback(() => { if (pdfJsWrapper) { diff --git a/services/web/frontend/js/shared/hooks/use-editor-analytics.ts b/services/web/frontend/js/shared/hooks/use-editor-analytics.ts index 55a0235f76..0116e5b05a 100644 --- a/services/web/frontend/js/shared/hooks/use-editor-analytics.ts +++ b/services/web/frontend/js/shared/hooks/use-editor-analytics.ts @@ -7,14 +7,23 @@ import { } from '@/infrastructure/event-tracking' import { useCallback } from 'react' +export function populateEditorRedesignSegmentation< + SegmentationType extends Segmentation, +>( + segmentation: SegmentationType | undefined = {} as SegmentationType, + editorRedesign: boolean +): SegmentationType & { 'editor-redesign'?: 'enabled' } { + return editorRedesign + ? { ...segmentation, 'editor-redesign': 'enabled' } + : segmentation +} + export const useEditorAnalytics = () => { const editorRedesign = useIsNewEditorEnabled() const populateSegmentation = useCallback( (segmentation: Segmentation | undefined = {}): Segmentation => { - return editorRedesign - ? { ...segmentation, 'editor-redesign': 'enabled' } - : segmentation + return populateEditorRedesignSegmentation(segmentation, editorRedesign) }, [editorRedesign] ) diff --git a/services/web/modules/full-project-search/frontend/js/components/full-project-search-ui.tsx b/services/web/modules/full-project-search/frontend/js/components/full-project-search-ui.tsx index 106386b461..2112793ae9 100644 --- a/services/web/modules/full-project-search/frontend/js/components/full-project-search-ui.tsx +++ b/services/web/modules/full-project-search/frontend/js/components/full-project-search-ui.tsx @@ -111,7 +111,11 @@ const FullProjectSearchUI: FC = () => { await projectSnapshot.refresh() if (!abortControllerRef.current.signal.aborted) { - const results = await searchSnapshot(projectSnapshot, searchQuery) + const results = await searchSnapshot( + projectSnapshot, + searchQuery, + newEditor + ) setMatchedFiles(results) } } catch (error) { @@ -121,7 +125,7 @@ const FullProjectSearchUI: FC = () => { setLoading(false) } }, - [openDocs, projectSnapshot, t] + [openDocs, projectSnapshot, t, newEditor] ) const searchInputRef = useRef(null) diff --git a/services/web/modules/full-project-search/frontend/js/util/search-snapshot.ts b/services/web/modules/full-project-search/frontend/js/util/search-snapshot.ts index 2abb5626b9..daa146bf0a 100644 --- a/services/web/modules/full-project-search/frontend/js/util/search-snapshot.ts +++ b/services/web/modules/full-project-search/frontend/js/util/search-snapshot.ts @@ -3,6 +3,7 @@ import { RegExpCursor, SearchCursor, SearchQuery } from '@codemirror/search' import { ProjectSnapshot } from '@/infrastructure/project-snapshot' import { categorizer, regexpWordTest, stringWordTest } from './search' import { sendSearchEvent } from '@/features/event-tracking/search-events' +import { populateEditorRedesignSegmentation } from '@/shared/hooks/use-editor-analytics' export type Hit = { lineIndex: number @@ -20,7 +21,8 @@ const toLowerCase = (string: string) => string.toLowerCase() export const searchSnapshot = async ( projectSnapshot: ProjectSnapshot, - searchQuery: SearchQuery + searchQuery: SearchQuery, + newEditor: boolean ) => { if (!searchQuery.search.trim().length) { return @@ -82,11 +84,17 @@ export const searchSnapshot = async ( a.path.localeCompare(b.path) ) - sendSearchEvent('search-execute', { - searchType: 'full-project', - totalDocs: docPaths.length, - totalResults: results.flatMap(file => file.hits).length, - }) + sendSearchEvent( + 'search-execute', + populateEditorRedesignSegmentation( + { + searchType: 'full-project', + totalDocs: docPaths.length, + totalResults: results.flatMap(file => file.hits).length, + }, + newEditor + ) + ) return results }