mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Merge pull request #24979 from overleaf/mj-editor-event-hook
[web] Introduce React hook wrapper around sendMB and friends GitOrigin-RevId: 3c693ae609c6d4e5ba280c45096692aca47975ca
This commit is contained in:
committed by
Copybot
parent
0b9cb185fa
commit
e98addf33a
@@ -33,6 +33,7 @@ import DictionarySettingsModal from './settings/editor-settings/dictionary-setti
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import OLIconButton from '@/features/ui/components/ol/ol-icon-button'
|
||||
import { useChatContext } from '@/features/chat/context/chat-context'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
|
||||
type RailElement = {
|
||||
icon: AvailableUnfilledIcon
|
||||
@@ -76,6 +77,7 @@ const RAIL_MODALS: {
|
||||
]
|
||||
|
||||
export const RailLayout = () => {
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
activeModal,
|
||||
@@ -147,16 +149,20 @@ export const RailLayout = () => {
|
||||
key: 'settings',
|
||||
icon: 'settings',
|
||||
title: t('settings'),
|
||||
action: () => setLeftMenuShown(true),
|
||||
action: () => {
|
||||
sendEvent('rail-click', { tab: 'settings' })
|
||||
setLeftMenuShown(true)
|
||||
},
|
||||
},
|
||||
],
|
||||
[setLeftMenuShown, t]
|
||||
[setLeftMenuShown, t, sendEvent]
|
||||
)
|
||||
|
||||
const onTabSelect = useCallback(
|
||||
(key: string | null) => {
|
||||
if (key === selectedTab) {
|
||||
togglePane()
|
||||
sendEvent('rail-click', { tab: key, type: 'toggle' })
|
||||
} else {
|
||||
// HACK: Apparently the onSelect event is triggered with href attributes
|
||||
// from DropdownItems
|
||||
@@ -164,15 +170,17 @@ export const RailLayout = () => {
|
||||
// Attempting to open a non-existent tab
|
||||
return
|
||||
}
|
||||
const keyOrDefault = key ?? 'file-tree'
|
||||
// Change the selected tab and make sure it's open
|
||||
openTab((key ?? 'file-tree') as RailTabKey)
|
||||
openTab(keyOrDefault as RailTabKey)
|
||||
sendEvent('rail-click', { tab: keyOrDefault })
|
||||
|
||||
if (key === 'chat') {
|
||||
markMessagesAsRead()
|
||||
}
|
||||
}
|
||||
},
|
||||
[openTab, togglePane, selectedTab, railTabs, markMessagesAsRead]
|
||||
[openTab, togglePane, selectedTab, railTabs, sendEvent, markMessagesAsRead]
|
||||
)
|
||||
|
||||
const isReviewPanelOpen = selectedTab === 'review-panel'
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { useSwitchEnableNewEditorState } from '../../hooks/use-switch-enable-new-editor-state'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
|
||||
export const IdeRedesignSwitcherModal = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -66,13 +67,18 @@ const SwitcherModalContentEnabled: FC<ModalContentProps> = ({
|
||||
loading,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const disable = useCallback(() => {
|
||||
sendEvent('editor-redesign-toggle', {
|
||||
action: 'disable',
|
||||
location: 'modal',
|
||||
})
|
||||
setEditorRedesignStatus(false)
|
||||
.then(hide)
|
||||
.catch(() => {
|
||||
// do nothing, we're already showing the error
|
||||
})
|
||||
}, [setEditorRedesignStatus, hide])
|
||||
}, [setEditorRedesignStatus, hide, sendEvent])
|
||||
return (
|
||||
<>
|
||||
<OLModalBody>
|
||||
@@ -116,13 +122,18 @@ const SwitcherModalContentDisabled: FC<ModalContentProps> = ({
|
||||
loading,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const enable = useCallback(() => {
|
||||
sendEvent('editor-redesign-toggle', {
|
||||
action: 'enable',
|
||||
location: 'modal',
|
||||
})
|
||||
setEditorRedesignStatus(true)
|
||||
.then(hide)
|
||||
.catch(() => {
|
||||
// do nothing, we're already showing the error
|
||||
})
|
||||
}, [setEditorRedesignStatus, hide])
|
||||
}, [setEditorRedesignStatus, hide, sendEvent])
|
||||
return (
|
||||
<>
|
||||
<OLModalBody>
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
} from '@/shared/context/layout-context'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { DetachRole } from '@/shared/context/detach-context'
|
||||
import { Spinner } from 'react-bootstrap'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
|
||||
type LayoutOption = 'sideBySide' | 'editorOnly' | 'pdfOnly' | 'detachedPdf'
|
||||
|
||||
@@ -87,6 +87,7 @@ const LayoutDropdownItem = ({
|
||||
}
|
||||
|
||||
export default function ChangeLayoutOptions() {
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const {
|
||||
reattach,
|
||||
detach,
|
||||
@@ -99,16 +100,16 @@ export default function ChangeLayoutOptions() {
|
||||
|
||||
const handleDetach = useCallback(() => {
|
||||
detach()
|
||||
eventTracking.sendMB('project-layout-detach')
|
||||
}, [detach])
|
||||
sendEvent('project-layout-detach')
|
||||
}, [detach, sendEvent])
|
||||
|
||||
const handleReattach = useCallback(() => {
|
||||
if (detachRole !== 'detacher') {
|
||||
return
|
||||
}
|
||||
reattach()
|
||||
eventTracking.sendMB('project-layout-reattach')
|
||||
}, [detachRole, reattach])
|
||||
sendEvent('project-layout-reattach')
|
||||
}, [detachRole, reattach, sendEvent])
|
||||
|
||||
// reattach when the PDF pane opens
|
||||
useEventListener('ui:pdf-open', handleReattach)
|
||||
@@ -117,12 +118,12 @@ export default function ChangeLayoutOptions() {
|
||||
(newLayout: IdeLayout, newView?: IdeView) => {
|
||||
handleReattach()
|
||||
changeLayout(newLayout, newView)
|
||||
eventTracking.sendMB('project-layout-change', {
|
||||
sendEvent('project-layout-change', {
|
||||
layout: newLayout,
|
||||
view: newView,
|
||||
})
|
||||
},
|
||||
[changeLayout, handleReattach]
|
||||
[changeLayout, handleReattach, sendEvent]
|
||||
)
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -101,6 +101,7 @@ const CommandDropdownChild = ({ item }: { item: Entry<TaggedCommand> }) => {
|
||||
if (isTaggedCommand(item)) {
|
||||
return (
|
||||
<MenuBarOption
|
||||
eventKey={item.id}
|
||||
key={item.id}
|
||||
title={item.label}
|
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
|
||||
@@ -24,6 +24,7 @@ import { useRailContext } from '../../contexts/rail-context'
|
||||
import WordCountModal from '@/features/word-count-modal/components/word-count-modal'
|
||||
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
|
||||
import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
|
||||
export const ToolbarMenuBar = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -210,6 +211,7 @@ export const ToolbarMenuBar = () => {
|
||||
<ChangeLayoutOptions />
|
||||
<DropdownHeader>Editor settings</DropdownHeader>
|
||||
<MenuBarOption
|
||||
eventKey="show_equation_preview"
|
||||
title={t('show_equation_preview')}
|
||||
trailingIcon={mathPreview ? 'check' : undefined}
|
||||
onClick={toggleMathPreview}
|
||||
@@ -227,18 +229,25 @@ export const ToolbarMenuBar = () => {
|
||||
className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued"
|
||||
>
|
||||
<MenuBarOption
|
||||
eventKey="keyboard_shortcuts"
|
||||
title={t('keyboard_shortcuts')}
|
||||
onClick={openKeyboardShortcutsModal}
|
||||
/>
|
||||
<MenuBarOption
|
||||
title={t('documentation')}
|
||||
eventKey="documentation"
|
||||
href="/learn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
<DropdownDivider />
|
||||
<MenuBarOption title={t('contact_us')} onClick={openContactUsModal} />
|
||||
<MenuBarOption
|
||||
eventKey="contact_us"
|
||||
title={t('contact_us')}
|
||||
onClick={openContactUsModal}
|
||||
/>
|
||||
<MenuBarOption
|
||||
eventKey="give_feedback"
|
||||
title={t('give_feedback')}
|
||||
href="https://forms.gle/soyVStc5qDx9na1Z6"
|
||||
target="_blank"
|
||||
@@ -247,6 +256,7 @@ export const ToolbarMenuBar = () => {
|
||||
<DropdownDivider />
|
||||
<SwitchToOldEditorMenuBarOption />
|
||||
<MenuBarOption
|
||||
eventKey="whats_new"
|
||||
title="What's new?"
|
||||
onClick={openEditorRedesignSwitcherModal}
|
||||
/>
|
||||
@@ -263,14 +273,19 @@ export const ToolbarMenuBar = () => {
|
||||
const SwitchToOldEditorMenuBarOption = () => {
|
||||
const { loading, error, setEditorRedesignStatus } =
|
||||
useSwitchEnableNewEditorState()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
|
||||
const disable: MouseEventHandler = useCallback(
|
||||
event => {
|
||||
// Don't close the dropdown
|
||||
event.stopPropagation()
|
||||
sendEvent('editor-redesign-toggle', {
|
||||
action: 'disable',
|
||||
location: 'menu-bar',
|
||||
})
|
||||
setEditorRedesignStatus(false)
|
||||
},
|
||||
[setEditorRedesignStatus]
|
||||
[setEditorRedesignStatus, sendEvent]
|
||||
)
|
||||
let icon = null
|
||||
if (loading) {
|
||||
@@ -280,6 +295,7 @@ const SwitchToOldEditorMenuBarOption = () => {
|
||||
}
|
||||
return (
|
||||
<MenuBarOption
|
||||
eventKey="switch_to_old_editor"
|
||||
title="Switch to old editor"
|
||||
onClick={disable}
|
||||
disabled={loading}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import OLIconButton from '@/features/ui/components/ol/ol-icon-button'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
import { useCallback } from 'react'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import OLIconButton from '@/features/ui/components/ol/ol-icon-button'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
|
||||
export default function ShowHistoryButton() {
|
||||
const { t } = useTranslation()
|
||||
const { view, setView } = useLayoutContext()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
|
||||
const toggleHistoryOpen = useCallback(() => {
|
||||
const action = view === 'history' ? 'close' : 'open'
|
||||
eventTracking.sendMB('navigation-clicked-history', { action })
|
||||
sendEvent('navigation-clicked-history', { action })
|
||||
|
||||
setView(view === 'history' ? 'editor' : 'history')
|
||||
}, [view, setView])
|
||||
}, [view, setView, sendEvent])
|
||||
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sessionStorage from './session-storage'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type Segmentation = Record<
|
||||
export type Segmentation = Record<
|
||||
string,
|
||||
string | number | boolean | undefined | unknown | any // TODO: RecurlyError
|
||||
>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
|
||||
import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
import { useNestableDropdown } from '@/shared/hooks/use-nestable-dropdown'
|
||||
import { MouseEventHandler, ReactNode } from 'react'
|
||||
import { MouseEventHandler, ReactNode, useCallback } from 'react'
|
||||
|
||||
type MenuBarOptionProps = {
|
||||
title: string
|
||||
@@ -11,18 +12,30 @@ type MenuBarOptionProps = {
|
||||
href?: string
|
||||
target?: string
|
||||
rel?: string
|
||||
eventKey?: string
|
||||
}
|
||||
|
||||
export const MenuBarOption = ({
|
||||
title,
|
||||
onClick,
|
||||
onClick: clickHandler,
|
||||
href,
|
||||
disabled,
|
||||
trailingIcon,
|
||||
target,
|
||||
rel,
|
||||
eventKey,
|
||||
}: MenuBarOptionProps) => {
|
||||
const { setSelected } = useNestableDropdown()
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const onClick: MouseEventHandler = useCallback(
|
||||
e => {
|
||||
if (eventKey) {
|
||||
sendEvent('menu-bar-option-click', { key: eventKey })
|
||||
}
|
||||
return clickHandler?.(e)
|
||||
},
|
||||
[clickHandler, eventKey, sendEvent]
|
||||
)
|
||||
return (
|
||||
<DropdownListItem>
|
||||
<DropdownItem
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import {
|
||||
Segmentation,
|
||||
sendMB,
|
||||
sendMBOnce,
|
||||
sendMBSampled,
|
||||
} from '@/infrastructure/event-tracking'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export const useEditorAnalytics = () => {
|
||||
const editorRedesign = useIsNewEditorEnabled()
|
||||
|
||||
const populateSegmentation = useCallback(
|
||||
(segmentation: Segmentation | undefined = {}): Segmentation => {
|
||||
return editorRedesign
|
||||
? { ...segmentation, 'editor-redesign': 'enabled' }
|
||||
: segmentation
|
||||
},
|
||||
[editorRedesign]
|
||||
)
|
||||
|
||||
const sendEvent: typeof sendMB = useCallback(
|
||||
(key, segmentation) => {
|
||||
sendMB(key, populateSegmentation(segmentation))
|
||||
},
|
||||
[populateSegmentation]
|
||||
)
|
||||
|
||||
const sendEventOnce: typeof sendMBOnce = useCallback(
|
||||
(key, segmentation) => {
|
||||
sendMBOnce(key, populateSegmentation(segmentation))
|
||||
},
|
||||
[populateSegmentation]
|
||||
)
|
||||
|
||||
const sendEventSampled: typeof sendMBSampled = useCallback(
|
||||
(key, segmentation, rate) => {
|
||||
sendMBSampled(key, populateSegmentation(segmentation), rate)
|
||||
},
|
||||
[populateSegmentation]
|
||||
)
|
||||
|
||||
return { sendEvent, sendEventOnce, sendEventSampled }
|
||||
}
|
||||
Reference in New Issue
Block a user