kopia lustrzana https://github.com/Tldraw/Tldraw
[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
rodzic
3a3d6c5de8
commit
3fb7c3fce4
|
@ -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 (
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue