[botcom] Add event tracking (#4687)

This PR adds basic event tracking to botcom. It slightly improves types
for our SDK's event tracking, too.

### Change type

- [x] `other`
pull/4689/head
Steve Ruiz 2024-10-08 15:30:09 +01:00 zatwierdzone przez GitHub
rodzic 3a3d6c5de8
commit 3fb7c3fce4
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
5 zmienionych plików z 122 dodań i 17 usunięć

Wyświetl plik

@ -9,6 +9,7 @@ import {
} from 'tldraw'
import { useApp } from '../../hooks/useAppState'
import { useRaw } from '../../hooks/useRaw'
import { useTldrawAppUiEvents } from '../../utils/app-ui-events'
import { TlaTabsRoot, TlaTabsTab, TlaTabsTabs } from '../TlaTabs/TlaTabs'
import { TlaShareMenuExportPage } from './TlaFileShareMenuExportPage'
import { TlaShareMenuSharePage } from './TlaFileShareMenuSharePage'
@ -25,6 +26,7 @@ export function TlaFileShareMenu({
}) {
const app = useApp()
const raw = useRaw()
const trackEvent = useTldrawAppUiEvents()
const shareMenuActiveTab = useValue(
'share menu active tab',
@ -33,8 +35,11 @@ export function TlaFileShareMenu({
)
const handleTabChange = useCallback(
(value: TldrawAppSessionState['shareMenuActiveTab']) => app.setShareMenuActiveTab(value),
[app]
(value: TldrawAppSessionState['shareMenuActiveTab']) => {
app.setShareMenuActiveTab(value)
trackEvent('change-share-menu-tab', { tab: value, source: 'file-share-menu' })
},
[app, trackEvent]
)
return (

Wyświetl plik

@ -4,6 +4,7 @@ import { useCallback, useRef, useState } from 'react'
import {
Editor,
FileHelpers,
TLImageExportOptions,
TLShape,
compact,
debounce,
@ -15,6 +16,7 @@ import { globalEditor } from '../../../utils/globalEditor'
import { useApp } from '../../hooks/useAppState'
import { useRaw } from '../../hooks/useRaw'
import { useTldrawUser } from '../../hooks/useUser'
import { useTldrawAppUiEvents } from '../../utils/app-ui-events'
import { getCurrentEditor } from '../../utils/getCurrentEditor'
import { TlaButton } from '../TlaButton/TlaButton'
import { TlaSelect } from '../TlaSelect/TlaSelect'
@ -49,6 +51,7 @@ function ExportBackgroundToggle() {
const app = useApp()
const raw = useRaw()
const user = useTldrawUser()
const trackEvent = useTldrawAppUiEvents()
if (!user) throw Error('should have auth')
const { id: userId } = user
@ -66,8 +69,10 @@ function ExportBackgroundToggle() {
const handleToggleShared = useCallback(() => {
const user = app.getUser(userId)
if (!user) throw Error('no user')
app.setUserExportPadding(userId, !user.exportPadding)
}, [app, userId])
const padding = !user.exportPadding
app.setUserExportPadding(userId, padding)
trackEvent('toggle-export-padding', { padding, source: 'file-share-menu' })
}, [app, userId, trackEvent])
return (
<TlaMenuControl>
@ -81,6 +86,7 @@ function ExportPaddingToggle() {
const app = useApp()
const raw = useRaw()
const user = useTldrawUser()
const trackEvent = useTldrawAppUiEvents()
if (!user) throw Error('should have auth')
const { id: userId } = user
@ -98,8 +104,10 @@ function ExportPaddingToggle() {
const handleToggleShared = useCallback(() => {
const user = app.getUser(userId)
if (!user) throw Error('no user')
app.setUserExportBackground(userId, !user.exportBackground)
}, [app, userId])
const background = !user.exportBackground
app.setUserExportBackground(userId, background)
trackEvent('toggle-export-background', { background, source: 'file-share-menu' })
}, [app, userId, trackEvent])
return (
<TlaMenuControl>
@ -113,6 +121,7 @@ function ExportFormatSelect() {
const app = useApp()
const raw = useRaw()
const user = useTldrawUser()
const trackEvent = useTldrawAppUiEvents()
if (!user) throw Error('should have auth')
const { id: userId } = user
@ -129,8 +138,9 @@ function ExportFormatSelect() {
const handleSelectChange = useCallback(
(value: TldrawAppUser['exportFormat']) => {
app.setUserExportFormat(userId, value)
trackEvent('set-export-format', { format: value, source: 'file-share-menu' })
},
[app, userId]
[app, userId, trackEvent]
)
return (
@ -152,6 +162,7 @@ function ExportThemeSelect() {
const app = useApp()
const raw = useRaw()
const user = useTldrawUser()
const trackEvent = useTldrawAppUiEvents()
if (!user) throw Error('should have auth')
const { id: userId } = user
@ -168,8 +179,9 @@ function ExportThemeSelect() {
const handleSelectChange = useCallback(
(value: TldrawAppUser['exportTheme']) => {
app.setUserExportTheme(userId, value)
trackEvent('set-export-theme', { theme: value, source: 'file-share-menu' })
},
[app, userId]
[app, userId, trackEvent]
)
return (
@ -191,13 +203,12 @@ function ExportThemeSelect() {
function ExportImageButton() {
const app = useApp()
const raw = useRaw()
const trackEvent = useTldrawAppUiEvents()
const [exported, setExported] = useState(false)
const handleExportLinkClick = useCallback(() => {
if (exported) {
return
}
if (exported) return
const editor = getCurrentEditor()
@ -210,15 +221,29 @@ function ExportImageButton() {
const user = app.getUser(auth.userId)
if (!user) throw Error('expected user')
let fullPage = false
let ids = editor.getSelectedShapeIds()
if (ids.length === 0) {
fullPage = true
ids = editor.getSortedChildIdsForParent(editor.getCurrentPageId())
}
exportAs(editor, ids, user.exportFormat, 'file', {
const opts: TLImageExportOptions = {
padding: user.exportPadding ? editor.options.defaultSvgPadding : 0,
background: user.exportBackground,
darkMode: user.exportTheme === 'auto' ? undefined : user.exportTheme === 'dark',
}
exportAs(editor, ids, user.exportFormat, 'file', opts)
trackEvent('export-image', {
source: 'file-share-menu',
fullPage,
padding: user.exportPadding,
background: !!opts.background,
theme: user.exportTheme,
format: user.exportFormat,
})
setExported(true)
@ -227,7 +252,7 @@ function ExportImageButton() {
return () => {
setExported(false)
}
}, [exported, app])
}, [exported, trackEvent, app])
return (
<>

Wyświetl plik

@ -0,0 +1,75 @@
import { TldrawAppSessionState, TldrawAppUser } from '@tldraw/dotcom-shared'
import { ReactNode, createContext, useContext } from 'react'
/** @public */
export type TLAppUiEventSource =
| 'sidebar'
| 'user-preferences'
| 'file-rename-dialog'
| 'file-menu'
| 'file-share-menu'
/** @public */
export interface TLAppUiEventMap {
'create-file': null
'delete-file': null
'rename-file': null
'duplicate-file': null
'drop-tldr-file': null
'import-tldr-file': null
'change-user-name': null
'click-watermark': null
'change-share-menu-tab': { tab: TldrawAppSessionState['shareMenuActiveTab'] }
'copy-share-link': null
'toggle-shared': { shared: boolean }
'set-theme': { theme: 'dark' | 'light' | 'auto' }
'toggle-export-padding': { padding: boolean }
'toggle-export-background': { background: TldrawAppUser['exportBackground'] }
'set-export-format': { format: TldrawAppUser['exportFormat'] }
'set-export-theme': { theme: TldrawAppUser['exportTheme'] }
'export-image': {
fullPage: boolean
theme: TldrawAppUser['exportTheme']
format: TldrawAppUser['exportFormat']
padding: TldrawAppUser['exportPadding']
background: TldrawAppUser['exportBackground']
}
}
/** @public */
export type TLAppUiHandler = <T extends keyof TLAppUiEventMap>(
name: T,
data: { source: TLAppUiEventSource } & (TLAppUiEventMap[T] extends null
? object
: TLAppUiEventMap[T])
) => void
/** @public */
export type TLAppUiContextType = TLAppUiHandler
/** @internal */
const defaultEventHandler: TLAppUiContextType = () => void null
/** @internal */
export const EventsContext = createContext<TLAppUiContextType>(defaultEventHandler)
/** @public */
export interface TldrawAppUiEventsProviderProps {
onEvent?: TLAppUiHandler
children: ReactNode
}
/** @public @react */
export function TldrawAppUiEventsProvider({ onEvent, children }: TldrawAppUiEventsProviderProps) {
return (
<EventsContext.Provider value={onEvent ?? defaultEventHandler}>
{children}
</EventsContext.Provider>
)
}
/** @public */
export function useTldrawAppUiEvents(): TLAppUiContextType {
const eventHandler = useContext(EventsContext)
return eventHandler
}

Wyświetl plik

@ -2572,7 +2572,7 @@ export interface TLUiDropdownMenuTriggerProps {
}
// @public (undocumented)
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>;
export type TLUiEventContextType = TLUiEventHandler;
// @public (undocumented)
export type TLUiEventData<K> = K extends null ? {
@ -2582,7 +2582,7 @@ export type TLUiEventData<K> = K extends null ? {
} & K;
// @public (undocumented)
export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = (name: T, data: TLUiEventData<TLUiEventMap[T]>) => void;
export type TLUiEventHandler = <T extends keyof TLUiEventMap>(name: T, data: TLUiEventData<TLUiEventMap[T]>) => void;
// @public (undocumented)
export interface TLUiEventMap {

Wyświetl plik

@ -119,13 +119,13 @@ export type TLUiEventData<K> = K extends null
: { source: TLUiEventSource } & K
/** @public */
export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = (
export type TLUiEventHandler = <T extends keyof TLUiEventMap>(
name: T,
data: TLUiEventData<TLUiEventMap[T]>
) => void
/** @public */
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>
export type TLUiEventContextType = TLUiEventHandler
/** @internal */
const defaultEventHandler: TLUiEventContextType = () => void null