mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #15843 from overleaf/jpa-download-events
[web] add additional event tracking GitOrigin-RevId: 877f92db41efff017db370ec75b8d1f861eed4f2
This commit is contained in:
parent
f76563787b
commit
6b2daed903
18 changed files with 72 additions and 5 deletions
|
@ -4,6 +4,7 @@ import { useProjectContext } from '../../../shared/context/project-context'
|
|||
import Icon from '../../../shared/components/icon'
|
||||
import Tooltip from '../../../shared/components/tooltip'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { isMobileDevice } from '../../../infrastructure/event-tracking'
|
||||
|
||||
export default function DownloadPDF() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -14,6 +15,7 @@ export default function DownloadPDF() {
|
|||
eventTracking.sendMB('download-pdf-button-click', {
|
||||
projectId,
|
||||
location: 'left-menu',
|
||||
isMobileDevice,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import { useProjectContext } from '../../../shared/context/project-context'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { isMobileDevice } from '../../../infrastructure/event-tracking'
|
||||
|
||||
export default function DownloadSource() {
|
||||
const { t } = useTranslation()
|
||||
|
@ -11,6 +12,7 @@ export default function DownloadSource() {
|
|||
eventTracking.sendMB('download-zip-button-click', {
|
||||
projectId,
|
||||
location: 'left-menu',
|
||||
isMobileDevice,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { isMobileDevice, sendMB } from '@/infrastructure/event-tracking'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
// record once per page load
|
||||
let recorded = false
|
||||
|
||||
export function recordDocumentFirstChangeEvent() {
|
||||
if (recorded) return
|
||||
recorded = true
|
||||
const projectId = getMeta('ol-project_id')
|
||||
sendMB('document-first-change', { projectId, isMobileDevice })
|
||||
}
|
|
@ -16,6 +16,7 @@ import {
|
|||
TrackChangesIdSeeds,
|
||||
} from '@/features/ide-react/editor/types/document'
|
||||
import { EditorFacade } from '@/features/source-editor/extensions/realtime'
|
||||
import { recordDocumentFirstChangeEvent } from '@/features/event-tracking/document-first-change-event'
|
||||
|
||||
// All times below are in milliseconds
|
||||
const SINGLE_USER_FLUSH_DELAY = 2000
|
||||
|
@ -421,6 +422,7 @@ export class ShareJsDoc extends EventEmitter {
|
|||
private bindToDocChanges(doc: Doc) {
|
||||
const { submitOp } = doc
|
||||
doc.submitOp = (op: ShareJsOperation, callback?: () => void) => {
|
||||
recordDocumentFirstChangeEvent()
|
||||
this.trigger('op:sent', op)
|
||||
doc.pendingCallbacks.push(() => {
|
||||
return this.trigger('op:acknowledged', op)
|
||||
|
|
|
@ -5,6 +5,7 @@ import Icon from '../../../shared/components/icon'
|
|||
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 { isMobileDevice } from '../../../infrastructure/event-tracking'
|
||||
|
||||
function PdfHybridDownloadButton() {
|
||||
const { pdfDownloadUrl } = useCompileContext()
|
||||
|
@ -20,6 +21,7 @@ function PdfHybridDownloadButton() {
|
|||
eventTracking.sendMB('download-pdf-button-click', {
|
||||
projectId,
|
||||
location: 'pdf-preview',
|
||||
isMobileDevice,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import AccessibleModal from '../../../../shared/components/accessible-modal'
|
|||
import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
|
||||
import useIsMounted from '../../../../shared/hooks/use-is-mounted'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import { isMobileDevice } from '../../../../infrastructure/event-tracking'
|
||||
|
||||
type ProjectsActionModalProps = {
|
||||
title?: string
|
||||
|
@ -57,7 +58,10 @@ function ProjectsActionModal({
|
|||
|
||||
useEffect(() => {
|
||||
if (showModal) {
|
||||
eventTracking.sendMB('project-list-page-interaction', { action })
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action,
|
||||
isMobileDevice,
|
||||
})
|
||||
}
|
||||
}, [action, showModal])
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import useAsync from '../../../../shared/hooks/use-async'
|
|||
import { useProjectListContext } from '../../context/project-list-context'
|
||||
import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { isMobileDevice } from '../../../../infrastructure/event-tracking'
|
||||
|
||||
type RenameProjectModalProps = {
|
||||
handleCloseModal: () => void
|
||||
|
@ -37,9 +38,11 @@ function RenameProjectModal({
|
|||
if (showModal) {
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'rename',
|
||||
projectId: project.id,
|
||||
isMobileDevice,
|
||||
})
|
||||
}
|
||||
}, [showModal])
|
||||
}, [showModal, project.id])
|
||||
|
||||
const isValid = useMemo(
|
||||
() => newProjectName !== project.name && newProjectName.trim().length > 0,
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as eventTracking from '../../../infrastructure/event-tracking'
|
|||
import classnames from 'classnames'
|
||||
import { Tag } from '../../../../../app/src/Features/Tags/types'
|
||||
import { Filter } from '../context/project-list-context'
|
||||
import { isMobileDevice } from '../../../infrastructure/event-tracking'
|
||||
|
||||
type SearchFormOwnProps = {
|
||||
inputValue: string
|
||||
|
@ -64,7 +65,10 @@ function SearchForm({
|
|||
HTMLInputElement & Omit<FormControl, keyof HTMLInputElement>
|
||||
>
|
||||
) => {
|
||||
eventTracking.sendMB('project-list-page-interaction', { action: 'search' })
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'search',
|
||||
isMobileDevice,
|
||||
})
|
||||
setInputValue(e.target.value)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
Project,
|
||||
} from '../../../../../../../../types/project/dashboard/api'
|
||||
import { useProjectTags } from '@/features/project-list/hooks/use-project-tags'
|
||||
import { isMobileDevice } from '../../../../../../infrastructure/event-tracking'
|
||||
|
||||
type CopyButtonProps = {
|
||||
project: Project
|
||||
|
@ -41,7 +42,11 @@ function CopyProjectButton({ project, children }: CopyButtonProps) {
|
|||
|
||||
const handleAfterCloned = useCallback(
|
||||
(clonedProject: ClonedProject, tags: { _id: string }[]) => {
|
||||
eventTracking.sendMB('project-list-page-interaction', { action: 'clone' })
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'clone',
|
||||
projectId: project.id,
|
||||
isMobileDevice,
|
||||
})
|
||||
addClonedProjectToViewData(clonedProject)
|
||||
for (const tag of tags) {
|
||||
addProjectToTagInView(tag._id, clonedProject.project_id)
|
||||
|
|
|
@ -5,6 +5,7 @@ import Icon from '../../../../../../shared/components/icon'
|
|||
import Tooltip from '../../../../../../shared/components/tooltip'
|
||||
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
|
||||
import { useLocation } from '../../../../../../shared/hooks/use-location'
|
||||
import { isMobileDevice } from '../../../../../../infrastructure/event-tracking'
|
||||
|
||||
type DownloadProjectButtonProps = {
|
||||
project: Project
|
||||
|
@ -22,6 +23,8 @@ function DownloadProjectButton({
|
|||
const downloadProject = useCallback(() => {
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'downloadZip',
|
||||
projectId: project.id,
|
||||
isMobileDevice,
|
||||
})
|
||||
location.assign(`/project/${project.id}/download/zip`)
|
||||
}, [project, location])
|
||||
|
|
|
@ -5,6 +5,7 @@ import Tooltip from '../../../../../../shared/components/tooltip'
|
|||
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
|
||||
import { useProjectListContext } from '../../../../context/project-list-context'
|
||||
import { useLocation } from '../../../../../../shared/hooks/use-location'
|
||||
import { isMobileDevice } from '../../../../../../infrastructure/event-tracking'
|
||||
|
||||
function DownloadProjectsButton() {
|
||||
const { selectedProjects, selectOrUnselectAllProjects } =
|
||||
|
@ -18,6 +19,7 @@ function DownloadProjectsButton() {
|
|||
const handleDownloadProjects = useCallback(() => {
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'downloadZips',
|
||||
isMobileDevice,
|
||||
})
|
||||
|
||||
location.assign(`/project/download/zip?project_ids=${projectIds.join(',')}`)
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useProjectListContext } from '../../../../context/project-list-context'
|
|||
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
|
||||
import { ClonedProject } from '../../../../../../../../types/project/dashboard/api'
|
||||
import { useProjectTags } from '@/features/project-list/hooks/use-project-tags'
|
||||
import { isMobileDevice } from '../../../../../../infrastructure/event-tracking'
|
||||
|
||||
function CopyProjectMenuItem() {
|
||||
const {
|
||||
|
@ -33,7 +34,11 @@ function CopyProjectMenuItem() {
|
|||
const handleAfterCloned = useCallback(
|
||||
(clonedProject: ClonedProject, tags: { _id: string }[]) => {
|
||||
const project = selectedProjects[0]
|
||||
eventTracking.sendMB('project-list-page-interaction', { action: 'clone' })
|
||||
eventTracking.sendMB('project-list-page-interaction', {
|
||||
action: 'clone',
|
||||
projectId: project.id,
|
||||
isMobileDevice,
|
||||
})
|
||||
addClonedProjectToViewData(clonedProject)
|
||||
for (const tag of tags) {
|
||||
addProjectToTagInView(tag._id, clonedProject.project_id)
|
||||
|
|
|
@ -20,6 +20,7 @@ import EventEmitter from '../../utils/EventEmitter'
|
|||
import ShareJs from '../../vendor/libs/sharejs'
|
||||
import EditorWatchdogManager from '../connection/EditorWatchdogManager'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { recordDocumentFirstChangeEvent } from '@/features/event-tracking/document-first-change-event'
|
||||
|
||||
let ShareJsDoc
|
||||
const SINGLE_USER_FLUSH_DELAY = 2000 // ms
|
||||
|
@ -457,6 +458,7 @@ export default ShareJsDoc = (function () {
|
|||
_bindToDocChanges(doc) {
|
||||
const { submitOp } = doc
|
||||
doc.submitOp = (...args) => {
|
||||
recordDocumentFirstChangeEvent()
|
||||
this.trigger('op:sent', ...Array.from(args))
|
||||
doc.pendingCallbacks.push(() => {
|
||||
return this.trigger('op:acknowledged', ...Array.from(args))
|
||||
|
|
|
@ -51,6 +51,12 @@ export function sendMBSampled(key, body = {}, rate = 0.01) {
|
|||
}
|
||||
}
|
||||
|
||||
// Use breakpoint @screen-xs-max from less:
|
||||
// @screen-xs-max: (@screen-sm-min - 1);
|
||||
// @screen-sm-min: @screen-sm;
|
||||
// @screen-sm: 768px;
|
||||
export const isMobileDevice = window.matchMedia('(max-width: 767px)').matches
|
||||
|
||||
function sendBeacon(key, data) {
|
||||
if (!navigator || !navigator.sendBeacon) return
|
||||
|
||||
|
|
3
services/web/test/frontend/bootstrap.js
vendored
3
services/web/test/frontend/bootstrap.js
vendored
|
@ -120,6 +120,9 @@ globalThis.WebSocket = class WebSocket {
|
|||
static CLOSED = 3
|
||||
}
|
||||
|
||||
// add stub for window.matchMedia
|
||||
window.matchMedia = () => ({ matches: false })
|
||||
|
||||
// node-fetch doesn't accept relative URL's: https://github.com/node-fetch/node-fetch/blob/master/docs/v2-LIMITS.md#known-differences
|
||||
const fetch = require('node-fetch')
|
||||
globalThis.fetch =
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
owner,
|
||||
archivedProjects,
|
||||
makeLongProjectList,
|
||||
archiveableProject,
|
||||
copyableProject,
|
||||
} from '../fixtures/projects-data'
|
||||
import * as useLocationModule from '../../../../../frontend/js/shared/hooks/use-location'
|
||||
|
||||
|
@ -818,6 +820,8 @@ describe('<ProjectListRoot />', function () {
|
|||
{
|
||||
action: 'rename',
|
||||
page: '/',
|
||||
projectId: copyableProject.id,
|
||||
isMobileDevice: false,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -949,6 +953,8 @@ describe('<ProjectListRoot />', function () {
|
|||
{
|
||||
action: 'clone',
|
||||
page: '/',
|
||||
projectId: archiveableProject.id,
|
||||
isMobileDevice: false,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1106,6 +1112,8 @@ describe('<ProjectListRoot />', function () {
|
|||
{
|
||||
action: 'clone',
|
||||
page: '/',
|
||||
projectId: archiveableProject.id,
|
||||
isMobileDevice: false,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ describe('Project list search form', function () {
|
|||
expect(sendMBSpy).to.have.been.calledWith('project-list-page-interaction', {
|
||||
action: 'search',
|
||||
page: '/',
|
||||
isMobileDevice: false,
|
||||
})
|
||||
expect(setInputValueMock).to.be.calledWith(value)
|
||||
})
|
||||
|
|
|
@ -91,6 +91,7 @@ describe('<ProjectsActionModal />', function () {
|
|||
expect(sendMBSpy).to.have.been.calledWith('project-list-page-interaction', {
|
||||
action: 'archive',
|
||||
page: '/',
|
||||
isMobileDevice: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue