wip try to encapsulate captureEvent calls in telemetry

This commit is contained in:
lebaudantoine
2026-08-07 16:10:05 +02:00
parent 993efec027
commit 7514bff7c1
9 changed files with 44 additions and 127 deletions
@@ -1,5 +1,21 @@
import { getPosthog } from './utils' import { getPosthog } from './utils'
export const captureEvent = (
event: string,
props?: Record<string, unknown>
) => {
void getPosthog()
.then((ph) => {
ph.capture(event, props)
})
.catch(() => {
/* telemetry must never break the app */
})
if (import.meta.env.DEV) {
console.warn(`[telemetry] ${event}`, props)
}
}
export type LogCode = export type LogCode =
// media // media
| 'join_preview_failure' | 'join_preview_failure'
@@ -16,7 +16,6 @@ import {
notifyRecordingSaveInProgress, notifyRecordingSaveInProgress,
useNotifyParticipants, useNotifyParticipants,
} from '@/features/notifications' } from '@/features/notifications'
import posthog from 'posthog-js'
import { useConfig } from '@/api/useConfig' import { useConfig } from '@/api/useConfig'
import { NoAccessView } from './NoAccessView' import { NoAccessView } from './NoAccessView'
import { ControlsButton } from './ControlsButton' import { ControlsButton } from './ControlsButton'
@@ -29,7 +28,7 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner' import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner'
import { FeatureFlags } from '@/features/analytics/enums' import { FeatureFlags } from '@/features/analytics/enums'
import { LimitDescription } from './LimitDescription' import { LimitDescription } from './LimitDescription'
import { reportError } from '@/features/analytics/telemetry' import { captureEvent, reportError } from '@/features/analytics/telemetry'
export const ScreenRecordingSidePanel = () => { export const ScreenRecordingSidePanel = () => {
const { data } = useConfig() const { data } = useConfig()
@@ -64,7 +63,7 @@ export const ScreenRecordingSidePanel = () => {
await notifyParticipants({ await notifyParticipants({
type: NotificationType.ScreenRecordingRequested, type: NotificationType.ScreenRecordingRequested,
}) })
posthog.capture('screen-recording-requested', {}) captureEvent('screen-recording-requested', {})
} }
const handleScreenRecording = async () => { const handleScreenRecording = async () => {
@@ -101,7 +100,7 @@ export const ScreenRecordingSidePanel = () => {
await notifyParticipants({ await notifyParticipants({
type: NotificationType.ScreenRecordingStarted, type: NotificationType.ScreenRecordingStarted,
}) })
posthog.capture('screen-recording-started', { captureEvent('screen-recording-started', {
includeTranscript: includeTranscript, includeTranscript: includeTranscript,
language: selectedLanguageKey, language: selectedLanguageKey,
}) })
@@ -17,7 +17,6 @@ import {
useNotifyParticipants, useNotifyParticipants,
notifyRecordingSaveInProgress, notifyRecordingSaveInProgress,
} from '@/features/notifications' } from '@/features/notifications'
import posthog from 'posthog-js'
import { useConfig } from '@/api/useConfig' import { useConfig } from '@/api/useConfig'
import { VStack } from '@/styled-system/jsx' import { VStack } from '@/styled-system/jsx'
import { Checkbox } from '@/primitives/Checkbox.tsx' import { Checkbox } from '@/primitives/Checkbox.tsx'
@@ -35,7 +34,7 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner' import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner'
import { LimitDescription } from './LimitDescription' import { LimitDescription } from './LimitDescription'
import { openSettingsDialog } from '@/stores/settings' import { openSettingsDialog } from '@/stores/settings'
import { reportError } from '@/features/analytics/telemetry' import { captureEvent, reportError } from '@/features/analytics/telemetry'
export const TranscriptSidePanel = () => { export const TranscriptSidePanel = () => {
const { data } = useConfig() const { data } = useConfig()
@@ -77,7 +76,7 @@ export const TranscriptSidePanel = () => {
await notifyParticipants({ await notifyParticipants({
type: NotificationType.TranscriptionRequested, type: NotificationType.TranscriptionRequested,
}) })
posthog.capture('transcript-requested', {}) captureEvent('transcript-requested', {})
} }
const handleTranscript = async () => { const handleTranscript = async () => {
@@ -122,7 +121,7 @@ export const TranscriptSidePanel = () => {
await notifyParticipants({ await notifyParticipants({
type: NotificationType.TranscriptionStarted, type: NotificationType.TranscriptionStarted,
}) })
posthog.capture('transcript-started', { captureEvent('transcript-started', {
includeScreenRecording: includeScreenRecording, includeScreenRecording: includeScreenRecording,
language: selectedLanguageKey, language: selectedLanguageKey,
}) })
@@ -26,8 +26,7 @@ import { css } from '@/styled-system/css'
import { BackgroundProcessorFactory } from '../livekit/components/blur' import { BackgroundProcessorFactory } from '../livekit/components/blur'
import { LocalUserChoices } from '@/stores/userChoices' import { LocalUserChoices } from '@/stores/userChoices'
import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert' import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert'
import { reportError } from '@/features/analytics/telemetry' import { captureEvent, reportError } from '@/features/analytics/telemetry'
import { usePostHog } from 'posthog-js/react'
import { useConfig } from '@/api/useConfig' import { useConfig } from '@/api/useConfig'
import { isFireFox } from '@/utils/livekit' import { isFireFox } from '@/utils/livekit'
import { useIsMobile } from '@/utils/useIsMobile' import { useIsMobile } from '@/utils/useIsMobile'
@@ -48,7 +47,6 @@ export const Conference = ({
mode?: 'join' | 'create' mode?: 'join' | 'create'
initialRoomData?: ApiRoom initialRoomData?: ApiRoom
}) => { }) => {
const posthog = usePostHog()
const { data: apiConfig } = useConfig() const { data: apiConfig } = useConfig()
const { userChoices: userConfig } = usePersistentUserChoices() as { const { userChoices: userConfig } = usePersistentUserChoices() as {
@@ -58,8 +56,8 @@ export const Conference = ({
const { username } = useSnapshot(userStore) const { username } = useSnapshot(userStore)
useEffect(() => { useEffect(() => {
posthog.capture('visit-room', { slug: roomId }) captureEvent('visit-room', { slug: roomId })
}, [roomId, posthog]) }, [roomId])
const fetchKey = [keys.room, roomId] const fetchKey = [keys.room, roomId]
const [isConnectionWarmedUp, setIsConnectionWarmedUp] = useState(false) const [isConnectionWarmedUp, setIsConnectionWarmedUp] = useState(false)
@@ -1,13 +1,12 @@
import { Button, H, Input, Text, TextArea } from '@/primitives' import { Button, H, Text, TextArea } from '@/primitives'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import { cva } from '@/styled-system/css' import { cva } from '@/styled-system/css'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { styled, VStack } from '@/styled-system/jsx' import { styled, VStack } from '@/styled-system/jsx'
import { usePostHog } from 'posthog-js/react'
import type { PostHog } from 'posthog-js'
import { Button as RACButton } from 'react-aria-components' import { Button as RACButton } from 'react-aria-components'
import { useIsAnalyticsEnabled } from '@/features/analytics/hooks/useIsAnalyticsEnabled' import { useIsAnalyticsEnabled } from '@/features/analytics/hooks/useIsAnalyticsEnabled'
import type { CandidateInfo } from '@/stores/connectionObserver' import type { CandidateInfo } from '@/stores/connectionObserver'
import { captureEvent } from '@/features/analytics/telemetry'
const Card = styled('div', { const Card = styled('div', {
base: { base: {
@@ -72,11 +71,9 @@ const labelRecipe = cva({
}) })
const OpenFeedback = ({ const OpenFeedback = ({
posthog,
onNext, onNext,
metadata, metadata,
}: { }: {
posthog: PostHog
onNext: () => void onNext: () => void
metadata?: Record<string, unknown> metadata?: Record<string, unknown>
}) => { }) => {
@@ -90,7 +87,7 @@ const OpenFeedback = ({
const onSubmit = () => { const onSubmit = () => {
try { try {
posthog.capture('open-feedback', { captureEvent('open-feedback', {
feedback, feedback,
...metadata, ...metadata,
}) })
@@ -141,12 +138,10 @@ const OpenFeedback = ({
} }
const RateQuality = ({ const RateQuality = ({
posthog,
onNext, onNext,
metadata, metadata,
maxRating = 5, maxRating = 5,
}: { }: {
posthog: PostHog
onNext: () => void onNext: () => void
metadata?: Record<string, unknown> metadata?: Record<string, unknown>
maxRating?: number maxRating?: number
@@ -160,7 +155,7 @@ const RateQuality = ({
const onSubmit = () => { const onSubmit = () => {
try { try {
posthog.capture('quality-rating', { captureEvent('quality-rating', {
rating: selectedRating, rating: selectedRating,
...metadata, ...metadata,
}) })
@@ -243,67 +238,6 @@ const ConfirmationMessage = ({ onNext }: { onNext: () => void }) => {
) )
} }
const AuthenticationMessage = ({
onNext,
posthog,
}: {
onNext: () => void
posthog: PostHog
}) => {
const { t } = useTranslation('rooms', { keyPrefix: 'authenticationMessage' })
const [email, setEmail] = useState('')
const onSubmit = () => {
posthog.people.set({ unsafe_email: email })
onNext()
}
return (
<Card
style={{
maxWidth: '380px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}
>
<H lvl={3}>{t('heading')}</H>
<Input
id="emailInput"
name="email"
placeholder={t('placeholder')}
required
value={email}
onChange={(e) => setEmail(e.target.value)}
style={{
marginBottom: '1rem',
}}
/>
<VStack gap="0.5">
<Button
variant="primary"
size="sm"
fullWidth
isDisabled={!email}
onPress={onSubmit}
>
{t('submit')}
</Button>
<Button
invisible
variant="secondary"
size="sm"
fullWidth
onPress={onNext}
>
{t('ignore')}
</Button>
</VStack>
</Card>
)
}
type RatingMetadata = { type RatingMetadata = {
room_id?: string room_id?: string
pc_publisher?: CandidateInfo pc_publisher?: CandidateInfo
@@ -318,12 +252,6 @@ export const Rating = ({
metadata: RatingMetadata metadata: RatingMetadata
}) => { }) => {
const isAnalyticsEnabled = useIsAnalyticsEnabled() const isAnalyticsEnabled = useIsAnalyticsEnabled()
const posthog = usePostHog()
const isUserAnonymous = useMemo(() => {
return posthog.get_property('$user_state') == 'anonymous'
}, [posthog])
const [step, setStep] = useState(0) const [step, setStep] = useState(0)
const sessionId = useMemo(() => crypto.randomUUID(), []) const sessionId = useMemo(() => crypto.randomUUID(), [])
@@ -339,37 +267,14 @@ export const Rating = ({
if (!isAnalyticsEnabled) return if (!isAnalyticsEnabled) return
if (step == 0) { if (step == 0) {
return ( return <RateQuality onNext={() => setStep(step + 1)} metadata={metadata} />
<RateQuality
posthog={posthog}
onNext={() => setStep(step + 1)}
metadata={metadata}
/>
)
} }
if (step == 1) { if (step == 1) {
return ( return <OpenFeedback onNext={() => setStep(step + 1)} metadata={metadata} />
<OpenFeedback
posthog={posthog}
onNext={() => setStep(step + 1)}
metadata={metadata}
/>
)
} }
if (step == 2) { if (step == 2) {
return isUserAnonymous ? (
<AuthenticationMessage
posthog={posthog}
onNext={() => setStep(step + 1)}
/>
) : (
<ConfirmationMessage onNext={() => setStep(0)} />
)
}
if (step == 3) {
return <ConfirmationMessage onNext={() => setStep(0)} /> return <ConfirmationMessage onNext={() => setStep(0)} />
} }
} }
@@ -11,10 +11,10 @@ import { DisconnectReason, RoomEvent } from 'livekit-client'
import { userPreferencesStore } from '@/stores/userPreferences' import { userPreferencesStore } from '@/stores/userPreferences'
import { connectionObserverStore } from '@/stores/connectionObserver' import { connectionObserverStore } from '@/stores/connectionObserver'
import posthog from 'posthog-js'
import { useFeatureFlagEnabled } from 'posthog-js/react' import { useFeatureFlagEnabled } from 'posthog-js/react'
import { isMobileBrowser } from '@livekit/components-core' import { isMobileBrowser } from '@livekit/components-core'
import { FeatureFlags } from '@/features/analytics/enums' import { FeatureFlags } from '@/features/analytics/enums'
import { captureEvent } from '@/features/analytics/telemetry'
const CANDIDATE_POLL_INTERVAL_MS = 5000 const CANDIDATE_POLL_INTERVAL_MS = 5000
@@ -182,23 +182,23 @@ export const ConnectionObserver = () => {
// total session duration from first connect to final disconnect. // total session duration from first connect to final disconnect.
if (connectionStartTimeRef.current != null) return if (connectionStartTimeRef.current != null) return
connectionStartTimeRef.current = Date.now() connectionStartTimeRef.current = Date.now()
posthog.capture('connection-event') captureEvent('connection-event')
} }
const handleReconnect = () => { const handleReconnect = () => {
posthog.capture('reconnect-event') captureEvent('reconnect-event')
} }
const handleReconnected = () => { const handleReconnected = () => {
posthog.capture('reconnected-event') captureEvent('reconnected-event')
} }
const handleSignalingConnect = () => { const handleSignalingConnect = () => {
posthog.capture('signaling-connect-event') captureEvent('signaling-connect-event')
} }
const handleSignalingReconnect = () => { const handleSignalingReconnect = () => {
posthog.capture('signaling-reconnect-event') captureEvent('signaling-reconnect-event')
} }
const handleDisconnect = ( const handleDisconnect = (
@@ -206,7 +206,7 @@ export const ConnectionObserver = () => {
) => { ) => {
const connectionEndTime = Date.now() const connectionEndTime = Date.now()
posthog.capture('disconnect-event', { captureEvent('disconnect-event', {
// Calculate total session duration from first connection to final disconnect // Calculate total session duration from first connection to final disconnect
// This duration is sensitive to refreshing the page. // This duration is sensitive to refreshing the page.
sessionDuration: connectionStartTimeRef.current sessionDuration: connectionStartTimeRef.current
@@ -1,5 +1,4 @@
import type { ProcessorOptions, Track } from 'livekit-client' import type { ProcessorOptions, Track } from 'livekit-client'
import posthog from 'posthog-js'
import { import {
FilesetResolver, FilesetResolver,
ImageSegmenter, ImageSegmenter,
@@ -18,6 +17,7 @@ import {
type ProcessorType, type ProcessorType,
MEDIAPIPE_PATH_WASM, MEDIAPIPE_PATH_WASM,
} from '.' } from '.'
import { captureEvent } from '@/features/analytics/telemetry.ts'
const PROCESSING_WIDTH = 256 const PROCESSING_WIDTH = 256
const PROCESSING_HEIGHT = 144 const PROCESSING_HEIGHT = 144
@@ -100,7 +100,7 @@ export class BackgroundCustomProcessor implements BackgroundProcessorInterface {
await this.initSegmenter() await this.initSegmenter()
this._initWorker() this._initWorker()
posthog.capture('firefox-blurring-init') captureEvent('firefox-blurring-init', {})
} }
_initVirtualBackgroundImage() { _initVirtualBackgroundImage() {
@@ -1,5 +1,4 @@
import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client' import type { ProcessorOptions, Track, TrackProcessor } from 'livekit-client'
import posthog from 'posthog-js'
import { import {
FilesetResolver, FilesetResolver,
FaceLandmarker, FaceLandmarker,
@@ -16,6 +15,7 @@ import {
ProcessorType, ProcessorType,
MEDIAPIPE_PATH_WASM, MEDIAPIPE_PATH_WASM,
} from '.' } from '.'
import { captureEvent } from '@/features/analytics/telemetry'
const PROCESSING_WIDTH = 256 * 3 const PROCESSING_WIDTH = 256 * 3
const PROCESSING_HEIGHT = 144 * 3 const PROCESSING_HEIGHT = 144 * 3
@@ -101,7 +101,7 @@ export class FaceLandmarksProcessor implements TrackProcessor<Track.Kind> {
await this.initFaceLandmarker() await this.initFaceLandmarker()
this._initWorker() this._initWorker()
posthog.capture('face-landmarks-init') captureEvent('face-landmarks-init', {})
} }
_initWorker() { _initWorker() {
@@ -11,7 +11,6 @@ import { useTranslation } from 'react-i18next'
import { SoundTester } from '@/components/SoundTester' import { SoundTester } from '@/components/SoundTester'
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker' import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
import { useNoiseReductionAvailable } from '@/features/rooms/livekit/hooks/useNoiseReductionAvailable' import { useNoiseReductionAvailable } from '@/features/rooms/livekit/hooks/useNoiseReductionAvailable'
import posthog from 'posthog-js'
import { RowWrapper } from './layout/RowWrapper' import { RowWrapper } from './layout/RowWrapper'
import { useSnapshot } from 'valtio' import { useSnapshot } from 'valtio'
import { import {
@@ -20,6 +19,7 @@ import {
saveNoiseReductionEnabled, saveNoiseReductionEnabled,
userChoicesStore, userChoicesStore,
} from '@/stores/userChoices' } from '@/stores/userChoices'
import { captureEvent } from '@/features/analytics/telemetry'
export type AudioTabProps = Pick<DialogProps, 'onOpenChange'> & export type AudioTabProps = Pick<DialogProps, 'onOpenChange'> &
Pick<TabPanelProps, 'id'> Pick<TabPanelProps, 'id'>
@@ -128,7 +128,7 @@ export const AudioTab = ({ id }: AudioTabProps) => {
isSelected={noiseReductionEnabled} isSelected={noiseReductionEnabled}
onChange={(v) => { onChange={(v) => {
saveNoiseReductionEnabled(v) saveNoiseReductionEnabled(v)
if (v) posthog.capture('noise-reduction-init') if (v) captureEvent('noise-reduction-init')
}} }}
> >
{t('audio.noiseReduction.label')} {t('audio.noiseReduction.label')}