mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-12 19:56:53 +00:00
♻️(frontend) encapsulate error tracking behind a telemetry module
Introduce a telemetry module that exposes a `reportError` helper. Under the hood it forwards errors to PostHog, but the module is the only place that knows about PostHog. Replace `console.error` calls used for error reporting with `reportError`, so the codebase now goes through a single, consistent API for telemetry. This normalizes how errors are reported and makes it straightforward to swap PostHog for another backend later on, without touching every call site.
This commit is contained in:
committed by
aleb_the_flash
parent
d810c9e0de
commit
48c0cb320e
@@ -8,6 +8,10 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- ♻️(frontend) encapsulate error tracking behind a telemetry module
|
||||
|
||||
## [1.25.2] - 2026-08-06
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button } from '@/primitives'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMediaDeviceSelect } from '@livekit/components-react'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const SoundTester = () => {
|
||||
const { t } = useTranslation('settings')
|
||||
@@ -15,7 +16,10 @@ export const SoundTester = () => {
|
||||
try {
|
||||
await audioRef?.current?.setSinkId(deviceId)
|
||||
} catch (error) {
|
||||
console.error(`Error setting sinkId: ${error}`)
|
||||
reportError(
|
||||
'device_switch_failure',
|
||||
new Error(`Error setting sinkId: ${error}`)
|
||||
)
|
||||
}
|
||||
}
|
||||
updateActiveId(activeDeviceId)
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'wouter'
|
||||
import { type PostHog } from 'posthog-js'
|
||||
import { type ApiUser } from '@/features/auth/api/ApiUser'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
|
||||
let posthog: PostHog | null = null
|
||||
|
||||
const getPosthog = async () => {
|
||||
if (!posthog) posthog = (await import('posthog-js')).default
|
||||
return posthog
|
||||
}
|
||||
import { getPosthog } from '../utils'
|
||||
|
||||
export const startAnalyticsSession = (data: ApiUser) => {
|
||||
getPosthog().then((ph) => {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { getPosthog } from './utils'
|
||||
|
||||
export type LogCode =
|
||||
// media
|
||||
| 'join_preview_failure'
|
||||
| 'livekit_room_error'
|
||||
| 'device_switch_failure'
|
||||
| 'permission_poll_failure'
|
||||
// non-media families
|
||||
| 'participant_mute_api_failure'
|
||||
| 'permissions_api_failure'
|
||||
| 'effects_processor_failure'
|
||||
| 'clipboard_failure'
|
||||
| 'fullscreen_failure'
|
||||
| 'publish_sources_failure'
|
||||
| 'disconnect_failure'
|
||||
| 'generic_failure'
|
||||
|
||||
export const reportError = (
|
||||
logCode: LogCode,
|
||||
error: unknown,
|
||||
extraInfo: Record<string, unknown> = {}
|
||||
): void => {
|
||||
const e = error instanceof Error ? error : new Error(String(error))
|
||||
void getPosthog()
|
||||
.then((ph) => {
|
||||
ph.captureException(e, {
|
||||
log_code: logCode,
|
||||
error_name: e.name,
|
||||
error_message: e.message,
|
||||
...extraInfo,
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(`[${logCode}]`, e, extraInfo)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { PostHog } from 'posthog-js'
|
||||
|
||||
let posthog: PostHog | null = null
|
||||
|
||||
export const getPosthog = async () => {
|
||||
if (!posthog) posthog = (await import('posthog-js')).default
|
||||
return posthog
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import { css } from '@/styled-system/css'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
import { LoginButton } from '@/components/LoginButton'
|
||||
import { LoadingScreen } from '@/components/LoadingScreen'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
const Columns = ({ children }: { children?: ReactNode }) => {
|
||||
return (
|
||||
@@ -160,7 +161,9 @@ const Home = () => {
|
||||
window.location.replace(data.external_home_url)
|
||||
} catch (error) {
|
||||
setRedirectFailed(true)
|
||||
console.error('Site is not reachable:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Site is not reachable:',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { NotificationDuration } from './NotificationDuration'
|
||||
import type { Participant } from 'livekit-client'
|
||||
import type { NotificationPayload } from './NotificationPayload'
|
||||
import type { RecordingMode } from '@/features/recording'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const notifyAutoMutedOnJoin = () => {
|
||||
toastQueue.add(
|
||||
@@ -55,7 +56,9 @@ export const decodeNotificationDataReceived = (
|
||||
return parsed as NotificationPayload
|
||||
} catch (error) {
|
||||
// Handle errors appropriately for your application
|
||||
console.error('Failed to decode notification payload:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to decode notification payload:',
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Participant } from 'livekit-client'
|
||||
import { useLowerHandParticipant } from './lowerHandParticipant'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const useLowerHandParticipants = () => {
|
||||
const { lowerHandParticipant } = useLowerHandParticipant()
|
||||
@@ -11,7 +12,9 @@ export const useLowerHandParticipants = () => {
|
||||
)
|
||||
return Promise.all(promises)
|
||||
} catch (error) {
|
||||
console.error('An error occurred while lowering hands :', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'An error occurred while lowering hands :',
|
||||
})
|
||||
throw new Error('An error occurred while lowering hands.', {
|
||||
cause: error,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fetchApi } from '@/api/fetchApi'
|
||||
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
|
||||
import { AssignableParticipantRole } from '@/features/rooms/api/ApiRoom'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const useParticipantRole = () => {
|
||||
const data = useRoomData()
|
||||
@@ -22,8 +23,11 @@ export const useParticipantRole = () => {
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to update participant's role ${identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
reportError(
|
||||
'generic_failure',
|
||||
new Error(
|
||||
`Failed to update participant's role ${identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../../participants/api/listWaitingParticipants'
|
||||
import { decodeNotificationDataReceived } from '@/features/notifications/utils'
|
||||
import { NotificationType } from '@/features/notifications/NotificationType'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const POLL_INTERVAL_MS = 1000
|
||||
|
||||
@@ -87,7 +88,7 @@ export const useWaitingParticipants = () => {
|
||||
|
||||
await refetchWaiting()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
reportError('generic_failure', e)
|
||||
setListEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react'
|
||||
import { flushSync } from 'react-dom'
|
||||
import { documentPictureInPictureStore } from '@/stores/documentPictureInPicture'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const IS_PIP_SUPPORTED =
|
||||
typeof globalThis !== 'undefined' && 'documentPictureInPicture' in globalThis
|
||||
@@ -86,7 +87,9 @@ export const usePictureInPicture = () => {
|
||||
documentPictureInPictureStore.window = ref(pipWindow)
|
||||
} catch (error) {
|
||||
// Avoid unhandled rejections if the user blocks or closes the request.
|
||||
console.error('Failed to open Picture-in-Picture window', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to open Picture-in-Picture window',
|
||||
})
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
|
||||
import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner'
|
||||
import { FeatureFlags } from '@/features/analytics/enums'
|
||||
import { LimitDescription } from './LimitDescription'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const ScreenRecordingSidePanel = () => {
|
||||
const { data } = useConfig()
|
||||
@@ -106,7 +107,9 @@ export const ScreenRecordingSidePanel = () => {
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to handle recording:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to handle recording:',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
|
||||
import { useIsAdminOrOwner } from '@/features/rooms/livekit/hooks/useIsAdminOrOwner'
|
||||
import { LimitDescription } from './LimitDescription'
|
||||
import { openSettingsDialog } from '@/stores/settings'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const TranscriptSidePanel = () => {
|
||||
const { data } = useConfig()
|
||||
@@ -127,7 +128,9 @@ export const TranscriptSidePanel = () => {
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to handle transcript:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to handle transcript:',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useRoomInfo } from '@livekit/components-react'
|
||||
import { useMemo } from 'react'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const useRoomMetadata = () => {
|
||||
const { metadata } = useRoomInfo()
|
||||
@@ -8,7 +9,9 @@ export const useRoomMetadata = () => {
|
||||
try {
|
||||
return JSON.parse(metadata)
|
||||
} catch (error) {
|
||||
console.error('Failed to parse room metadata:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to parse room metadata:',
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { fetchApi } from '@/api/fetchApi'
|
||||
import { useIsAdminOrOwner } from '../livekit/hooks/useIsAdminOrOwner'
|
||||
|
||||
import { useCallback } from 'react'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const useMuteParticipant = () => {
|
||||
const apiRoomData = useRoomData()
|
||||
@@ -31,7 +32,10 @@ export const useMuteParticipant = () => {
|
||||
|
||||
// Guard against undefined token for non-admin users
|
||||
if (!isAdminOrOwner && !apiRoomData.livekit.token) {
|
||||
console.error('Cannot mute participant: missing auth token')
|
||||
reportError(
|
||||
'participant_mute_api_failure',
|
||||
new Error('Cannot mute participant: missing auth token')
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,8 +57,11 @@ export const useMuteParticipant = () => {
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to mute participant ${participant.identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
reportError(
|
||||
'participant_mute_api_failure',
|
||||
new Error(
|
||||
`Failed to mute participant ${participant.identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -65,8 +72,11 @@ export const useMuteParticipant = () => {
|
||||
destinationIdentities: [participant.identity],
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Failed to notify muted participant ${participant.identity}: ${e}`
|
||||
reportError(
|
||||
'participant_mute_api_failure',
|
||||
new Error(
|
||||
`Failed to notify muted participant ${participant.identity}: ${e}`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Participant } from 'livekit-client'
|
||||
import { useMuteParticipant } from './muteParticipant'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const useMuteParticipants = () => {
|
||||
const { muteParticipant } = useMuteParticipant()
|
||||
@@ -11,7 +12,9 @@ export const useMuteParticipants = () => {
|
||||
)
|
||||
return Promise.all(promises)
|
||||
} catch (error) {
|
||||
console.error('An error occurred while muting participants :', error)
|
||||
reportError('participant_mute_api_failure', error, {
|
||||
context: 'An error occurred while muting participants :',
|
||||
})
|
||||
throw new Error('An error occurred while muting participants.', {
|
||||
cause: error,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Participant, Track } from 'livekit-client'
|
||||
import { fetchApi } from '@/api/fetchApi'
|
||||
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
type Source = Track.Source
|
||||
|
||||
export const useParticipantPermissions = () => {
|
||||
@@ -32,8 +33,11 @@ export const useParticipantPermissions = () => {
|
||||
}),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to update participant's permissions ${participant.identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
reportError(
|
||||
'permissions_api_failure',
|
||||
new Error(
|
||||
`Failed to update participant's permissions ${participant.identity}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Participant, Track } from 'livekit-client'
|
||||
import { useParticipantPermissions } from './updateParticipantPermissions'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
type Source = Track.Source
|
||||
|
||||
export const useUpdateParticipantsPermissions = () => {
|
||||
@@ -15,7 +16,9 @@ export const useUpdateParticipantsPermissions = () => {
|
||||
)
|
||||
return Promise.all(promises)
|
||||
} catch (error) {
|
||||
console.error('An error occurred while updating permissions :', error)
|
||||
reportError('permissions_api_failure', error, {
|
||||
context: 'An error occurred while updating permissions :',
|
||||
})
|
||||
throw new Error('An error occurred while updating permissions.', {
|
||||
cause: error,
|
||||
})
|
||||
|
||||
@@ -26,6 +26,7 @@ import { css } from '@/styled-system/css'
|
||||
import { BackgroundProcessorFactory } from '../livekit/components/blur'
|
||||
import { LocalUserChoices } from '@/stores/userChoices'
|
||||
import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
import { usePostHog } from 'posthog-js/react'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
import { isFireFox } from '@/utils/livekit'
|
||||
@@ -37,7 +38,6 @@ import { notifyAutoMutedOnJoin } from '@/features/notifications/utils'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { userPreferencesStore } from '@/stores/userPreferences'
|
||||
import { userStore } from '@/stores/user'
|
||||
import { asError } from '../utils/error'
|
||||
|
||||
export const Conference = ({
|
||||
roomId,
|
||||
@@ -236,7 +236,9 @@ export const Conference = ({
|
||||
backgroundColor: 'primaryDark.50 !important',
|
||||
})}
|
||||
onError={(e) => {
|
||||
posthog.captureException(asError(e))
|
||||
reportError('livekit_room_error', e, {
|
||||
failure: MediaDeviceFailure.getFailure(e) ?? 'not-a-device-error',
|
||||
})
|
||||
}}
|
||||
onConnected={async () => {
|
||||
if (!apiConfig) return
|
||||
|
||||
@@ -36,6 +36,7 @@ import { useLoginHint } from '@/hooks/useLoginHint'
|
||||
import { openPermissionsDialog } from '@/stores/permissions'
|
||||
import { useResolveInitiallyDefaultDeviceId } from '../livekit/hooks/useResolveInitiallyDefaultDeviceId'
|
||||
import { isSafari } from '@/utils/livekit'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
import {
|
||||
type LocalUserChoices,
|
||||
@@ -54,7 +55,8 @@ import { useSnapshot } from 'valtio'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
|
||||
const onError = (e: Error) => console.error('ERROR', e)
|
||||
const onError = (e: Error) =>
|
||||
reportError('join_preview_failure', e, { path: 'join_preview' })
|
||||
|
||||
const Effects = ({
|
||||
videoTrack,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { permissionsStore } from '@/stores/permissions'
|
||||
import { isSafari } from '@/utils/livekit'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
const POLLING_TIME = 500
|
||||
|
||||
@@ -88,7 +89,9 @@ export const useWatchPermissions = () => {
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isCancelled) {
|
||||
console.error('Error polling permissions:', error)
|
||||
reportError('permission_poll_failure', error, {
|
||||
context: 'Error polling permissions:',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, POLLING_TIME)
|
||||
@@ -142,7 +145,9 @@ export const useWatchPermissions = () => {
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isCancelled) {
|
||||
console.error('Error checking permissions:', error)
|
||||
reportError('permission_poll_failure', error, {
|
||||
context: 'Error checking permissions:',
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
if (!isCancelled) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { usePermissionsManager } from '../hooks/usePermissionsManager'
|
||||
import { useEffect } from 'react'
|
||||
import { closeSidePanel } from '@/stores/layout'
|
||||
import { useIsAdminOrOwner } from '../hooks/useIsAdminOrOwner'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const Admin = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'admin' })
|
||||
@@ -205,7 +206,7 @@ export const Admin = () => {
|
||||
patchRoom({
|
||||
roomId,
|
||||
room: { access_level: value as ApiAccessLevel },
|
||||
}).catch((e) => console.error(e))
|
||||
}).catch((e) => reportError('generic_failure', e))
|
||||
}
|
||||
items={[
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { RiCameraSwitchLine } from '@remixicon/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { ButtonProps } from 'react-aria-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
enum FacingMode {
|
||||
USER = 'user',
|
||||
@@ -103,7 +104,11 @@ export const CameraSwitchButton = (props: Partial<ButtonProps>) => {
|
||||
setActiveMediaDevice(device.deviceId)
|
||||
setFacingMode(target)
|
||||
} else {
|
||||
console.error('Cannot get user device with facingMode ' + target)
|
||||
reportError(
|
||||
'device_switch_failure',
|
||||
new Error('Cannot get user device with facingMode ' + target),
|
||||
{ path: 'switch_device', kind: 'videoinput', facing_mode: target }
|
||||
)
|
||||
}
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@/primitives'
|
||||
import { RiPhoneFill } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ConnectionState } from 'livekit-client'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const LeaveButton = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls' })
|
||||
@@ -15,11 +16,11 @@ export const LeaveButton = () => {
|
||||
tooltip={t('leave')}
|
||||
aria-label={t('leave')}
|
||||
onPress={() => {
|
||||
room
|
||||
.disconnect(true)
|
||||
.catch((e) =>
|
||||
console.error('An error occurred while disconnecting:', e)
|
||||
)
|
||||
room.disconnect(true).catch((e) =>
|
||||
reportError('disconnect_failure', e, {
|
||||
context: 'An error occurred while disconnecting:',
|
||||
})
|
||||
)
|
||||
}}
|
||||
data-attr="controls-leave"
|
||||
>
|
||||
|
||||
@@ -33,6 +33,7 @@ import { useConfig } from '@/api/useConfig.ts'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
import { Spinner } from '@/primitives/Spinner.tsx'
|
||||
import { userChoicesStore, saveProcessorConfig } from '@/stores/userChoices'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
enum BlurRadius {
|
||||
NONE = 0,
|
||||
@@ -238,7 +239,9 @@ export const EffectsConfiguration = ({
|
||||
|
||||
updateEffectStatusMessage(config, wasSelectedBeforeToggle)
|
||||
} catch (error) {
|
||||
console.error('Error applying effect:', error)
|
||||
reportError('effects_processor_failure', error, {
|
||||
context: 'Error applying effect:',
|
||||
})
|
||||
} finally {
|
||||
// Without setTimeout the DOM is not refreshing when updating the options.
|
||||
setTimeout(() => setProcessorPending(false))
|
||||
|
||||
@@ -5,6 +5,7 @@ import { RiGlassesLine, RiGoblet2Fill } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaceLandmarksProcessor } from '../blur/FaceLandmarksProcessor'
|
||||
import type { LocalVideoTrack } from 'livekit-client'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export type FunnyEffectsProps = {
|
||||
videoTrack: LocalVideoTrack
|
||||
@@ -55,7 +56,9 @@ export const FunnyEffects = ({
|
||||
await videoTrack.setProcessor(newProcessor)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('could not update processor', e)
|
||||
reportError('effects_processor_failure', e, {
|
||||
context: 'could not update processor',
|
||||
})
|
||||
} finally {
|
||||
onPending(false)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from 'react'
|
||||
import { formatPinCode } from '@/features/rooms/utils/telephony'
|
||||
import type { ApiRoom } from '@/features/rooms/api/ApiRoom'
|
||||
import { getRouteUrl } from '@/navigation/getRouteUrl'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
const COPY_SUCCESS_TIMEOUT = 3000
|
||||
|
||||
@@ -58,7 +59,9 @@ export const useCopyRoomToClipboard = (room: ApiRoom | undefined) => {
|
||||
await navigator.clipboard.writeText(content)
|
||||
setIsCopied(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
reportError('clipboard_failure', error, {
|
||||
context: 'copy_room_content',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +70,9 @@ export const useCopyRoomToClipboard = (room: ApiRoom | undefined) => {
|
||||
await navigator.clipboard.writeText(roomUrl)
|
||||
setIsRoomUrlCopied(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
reportError('clipboard_failure', error, {
|
||||
context: 'copy_room_url',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { useMemo, useState } from 'react'
|
||||
import { type TrackReferenceOrPlaceholder } from '@livekit/components-core'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export function useFullScreen({
|
||||
trackRef,
|
||||
@@ -56,7 +57,9 @@ export function useFullScreen({
|
||||
await docEl.msRequestFullscreen()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error entering fullscreen:', error)
|
||||
reportError('fullscreen_failure', error, {
|
||||
context: 'Error entering fullscreen:',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +73,9 @@ export function useFullScreen({
|
||||
await document.msExitFullscreen()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error exiting fullscreen:', error)
|
||||
reportError('fullscreen_failure', error, {
|
||||
context: 'Error exiting fullscreen:',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { usePatchRoom } from '@/features/rooms/api/patchRoom'
|
||||
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
|
||||
import { useCallback } from 'react'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const usePermissionsManager = () => {
|
||||
const { mutateAsync: patchRoom } = usePatchRoom()
|
||||
@@ -28,7 +29,9 @@ export const usePermissionsManager = () => {
|
||||
|
||||
return { configuration: newConfiguration }
|
||||
} catch (error) {
|
||||
console.error('Failed to update muting permission:', error)
|
||||
reportError('permissions_api_failure', error, {
|
||||
context: 'Failed to update muting permission:',
|
||||
})
|
||||
return { success: false, error }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
NotificationType,
|
||||
useNotifyParticipants,
|
||||
} from '@/features/notifications'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export const updatePublishSources = (
|
||||
currentSources: Source[],
|
||||
@@ -108,7 +109,9 @@ export const usePublishSourcesManager = () => {
|
||||
|
||||
return { configuration: newConfiguration }
|
||||
} catch (error) {
|
||||
console.error(`Failed to update ${sources}:`, error)
|
||||
reportError('publish_sources_failure', error, {
|
||||
context: `Failed to update ${sources}:`,
|
||||
})
|
||||
return { success: false, error }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { isLocal } from '@/utils/livekit'
|
||||
import { useMemo } from 'react'
|
||||
import { useRaiseHand } from '@/features/rooms/api/updateRaiseHand'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
type useRaisedHandProps = {
|
||||
participant: Participant
|
||||
@@ -79,9 +80,9 @@ export function useRaisedHand({ participant }: useRaisedHandProps) {
|
||||
try {
|
||||
await raiseHand(!isHandRaised)
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Failed to toggle hand: ${e instanceof Error ? e.message : 'Unknown error'}`
|
||||
)
|
||||
reportError('generic_failure', e, {
|
||||
context: 'toggle_raised_hand',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SidePanel } from '../components/SidePanel'
|
||||
import { RecordingProvider } from '@/features/recording'
|
||||
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
||||
import { ConnectionObserver } from '../components/ConnectionObserver'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
import { MediaStateObserver } from '../components/MediaStateObserver'
|
||||
import { RoomMetadataSynchronizer } from '../components/RoomMetadataSynchronizer'
|
||||
import { useRoomPageTitle } from '../hooks/useRoomPageTitle'
|
||||
@@ -91,7 +92,10 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
||||
</RoomContentArea>
|
||||
<ControlBar
|
||||
onDeviceError={(e) => {
|
||||
console.error(e)
|
||||
reportError('device_switch_failure', e.error, {
|
||||
at: 'ControlBar.onDeviceError',
|
||||
source: e.source,
|
||||
})
|
||||
if (
|
||||
e.source == Track.Source.ScreenShare &&
|
||||
e.error.toString() ==
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export const asError = (value: unknown): Error => {
|
||||
if (value instanceof Error) return value
|
||||
if (value instanceof Event) {
|
||||
return new Error(
|
||||
`Unhandled event "${value.type}" from ${value.target?.constructor.name ?? 'unknown'}`
|
||||
)
|
||||
}
|
||||
return new Error(String(value))
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { useUser } from '@/features/auth/api/useUser'
|
||||
import { Spinner } from '@/primitives/Spinner'
|
||||
import { CallbackIdHandler } from '../utils/CallbackIdHandler'
|
||||
import { PopupWindow } from '../utils/PopupWindow'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
const callbackIdHandler = new CallbackIdHandler()
|
||||
const popupWindow = new PopupWindow()
|
||||
@@ -52,7 +53,9 @@ const CreatePopup = () => {
|
||||
popupWindow.close()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to create meeting room:', error)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'Failed to create meeting room:',
|
||||
})
|
||||
}
|
||||
}
|
||||
if (isLoggedIn && callbackId) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { usePatchRoom } from '@/features/rooms/api/patchRoom'
|
||||
import { ApiAccessLevel } from '@/features/rooms/api/ApiRoom'
|
||||
import { updatePublishSources } from '@/features/rooms/livekit/hooks/usePublishSourcesManager'
|
||||
import { isSubsetOf } from '@/features/rooms/utils/isSubsetOf'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
type Source = Track.Source
|
||||
|
||||
@@ -108,7 +109,7 @@ const SettingsPopup = () => {
|
||||
patchRoom({
|
||||
roomId: roomSlug,
|
||||
room: { configuration: newConfiguration },
|
||||
}).catch((e) => console.error(e))
|
||||
}).catch((e) => reportError('generic_failure', e))
|
||||
}
|
||||
|
||||
const updateSource = (sources: Source[], enabled: boolean) => {
|
||||
@@ -329,7 +330,7 @@ const SettingsPopup = () => {
|
||||
patchRoom({
|
||||
roomId: roomSlug,
|
||||
room: { access_level: value as ApiAccessLevel },
|
||||
}).catch((e) => console.error(e))
|
||||
}).catch((e) => reportError('generic_failure', e))
|
||||
}
|
||||
items={[
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { authUrl } from '@/features/auth/utils/authUrl'
|
||||
import { PopupMessageType, CallbackCreationRoomData } from './types'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export class PopupWindow {
|
||||
private sendMessageToManager(
|
||||
@@ -9,7 +10,7 @@ export class PopupWindow {
|
||||
callback?: () => void
|
||||
) {
|
||||
if (!window.opener) {
|
||||
console.error('No manager window found')
|
||||
reportError('generic_failure', new Error('No manager window found'))
|
||||
window.close()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useRenameParticipant } from '@/features/rooms/api/renameParticipant'
|
||||
import { saveUsername } from '@/stores/user'
|
||||
import { logout } from '@/features/auth/utils/logout'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export type AccountTabProps = Pick<DialogProps, 'onOpenChange'> &
|
||||
Pick<TabPanelProps, 'id'>
|
||||
@@ -35,9 +36,9 @@ export const AccountTab = ({ id, onOpenChange }: AccountTabProps) => {
|
||||
saveUsername(name)
|
||||
onOpenChange?.(false) // only close on success
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to rename participant: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
reportError('generic_failure', error, {
|
||||
context: 'rename_participant',
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleOnCancel = () => {
|
||||
|
||||
@@ -112,9 +112,6 @@ export const Checkbox = ({
|
||||
const descriptionId = useId()
|
||||
|
||||
if (isInvalid !== undefined) {
|
||||
console.error(
|
||||
'Checkbox: passing isInvalid is not supported, use the validate prop instead'
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { deserializeToProxyMap } from '@/utils/valtio'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
export type UiFont =
|
||||
| 'default'
|
||||
@@ -154,10 +155,9 @@ function getAccessibilityState(): AccessibilityState {
|
||||
|
||||
return DEFAULT_STATE
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[AccessibilityStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
reportError('generic_failure', error, {
|
||||
context: '[AccessibilityStore] Failed to parse stored settings:',
|
||||
})
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { proxyMap } from 'valtio/utils'
|
||||
import { deserializeToProxyMap, serializeProxyMap } from '@/utils/valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { NotificationType } from '@/features/notifications/NotificationType'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
type State = {
|
||||
soundNotifications: Map<NotificationType, boolean>
|
||||
@@ -41,10 +42,9 @@ function getNotificationsState(): State {
|
||||
),
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[NotificationsStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
reportError('generic_failure', error, {
|
||||
context: '[NotificationsStore] Failed to parse stored settings:',
|
||||
})
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
type State = {
|
||||
username: string
|
||||
@@ -18,10 +19,9 @@ function getUserState(): State {
|
||||
...parsed,
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[UserPreferencesStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
reportError('generic_failure', error, {
|
||||
context: '[UserStore] Failed to parse stored settings:',
|
||||
})
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
type State = {
|
||||
is_idle_disconnect_modal_enabled: boolean
|
||||
@@ -21,10 +22,9 @@ function getUserPreferencesState(): State {
|
||||
...parsed,
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[UserPreferencesStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
reportError('generic_failure', error, {
|
||||
context: '[UserPreferencesStore] Failed to parse stored settings:',
|
||||
})
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import clsx from 'clsx'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
/**
|
||||
* Calls all functions in the order they were chained with the same arguments.
|
||||
@@ -25,7 +26,7 @@ export function chain(...callbacks: any[]): (...args: any[]) => void {
|
||||
try {
|
||||
callback(...args)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
reportError('generic_failure', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user