mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-13 12:17:24 +00:00
wip encapsulate exception capture
This commit is contained in:
@@ -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,7 @@ export const useCopyRoomToClipboard = (room: ApiRoom | undefined) => {
|
||||
await navigator.clipboard.writeText(content)
|
||||
setIsCopied(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
reportError('clipboard_failure', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ export const useCopyRoomToClipboard = (room: ApiRoom | undefined) => {
|
||||
await navigator.clipboard.writeText(roomUrl)
|
||||
setIsRoomUrlCopied(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
reportError('clipboard_failure', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 +80,11 @@ 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',
|
||||
new Error(
|
||||
`Failed to toggle hand: ${e instanceof Error ? e.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +36,11 @@ 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',
|
||||
new Error(
|
||||
`Failed to rename participant: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { type StyledVariantProps } from '@/styled-system/types'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { FieldErrors } from './FieldErrors'
|
||||
import { FieldDescription } from './FieldDescription'
|
||||
import { reportError } from '@/features/analytics/telemetry'
|
||||
|
||||
// styled taken from example at https://react-spectrum.adobe.com/react-aria/Checkbox.html
|
||||
const StyledCheckbox = styled(RACCheckbox, {
|
||||
@@ -112,8 +113,11 @@ export const Checkbox = ({
|
||||
const descriptionId = useId()
|
||||
|
||||
if (isInvalid !== undefined) {
|
||||
console.error(
|
||||
'Checkbox: passing isInvalid is not supported, use the validate prop instead'
|
||||
reportError(
|
||||
'generic_failure',
|
||||
new 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: '[UserPreferencesStore] 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