wip encapsulate exception capture

This commit is contained in:
lebaudantoine
2026-08-07 15:02:28 +02:00
parent 46a4955fe0
commit 993efec027
42 changed files with 221 additions and 85 deletions
+5 -1
View File
@@ -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'}`
)
)
}
}
+6 -2
View File
@@ -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
}
+4 -4
View File
@@ -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
}
}
+4 -4
View File
@@ -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
}
}
+4 -4
View File
@@ -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
}
}
+4 -4
View File
@@ -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
}
}
+2 -1
View File
@@ -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)
}
}
}