diff --git a/CHANGELOG.md b/CHANGELOG.md index 1078d410..31e8df91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to - 🐛(frontend) implement hysteresis band for the control bar layout - 🐛(frontend) fix toolbar ResizeObserver loop and alignment drift - 🐛(analytics) filter benign ResizeObserver loop error in Sentry/PostHog +- 🐛(frontend) stop reporting screen-share denials as errors ## [1.26.0] - 2026-08-12 diff --git a/src/frontend/src/features/analytics/telemetry.ts b/src/frontend/src/features/analytics/telemetry.ts index d8d64669..8587c001 100644 --- a/src/frontend/src/features/analytics/telemetry.ts +++ b/src/frontend/src/features/analytics/telemetry.ts @@ -137,6 +137,7 @@ export const captureMediaEvent = async ( | 'media-device-success' | 'device-not-found' | 'permissions-denied' + | 'screen-share-permission-denied' | 'silent-mic-detected' | 'silent-mic-analyser-unavailable' | 'silent-mic-recovered' diff --git a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx index 53f42f67..f470aae7 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/VideoConference.tsx @@ -11,7 +11,9 @@ 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 { captureMediaEvent, reportError } from '@/features/analytics/telemetry' +import { getOS } from '@/utils/os' +import { isFireFox } from '@/utils/livekit' import { MediaStateObserver } from '../components/MediaStateObserver' import { RoomMetadataSynchronizer } from '../components/RoomMetadataSynchronizer' import { useNoiseReduction } from '../hooks/useNoiseReduction' @@ -36,6 +38,20 @@ export interface VideoConferenceProps extends React.HTMLAttributes { + if (error.name === 'NotAllowedError') { + if (/by system/i.test(error.message)) return 'system' + if (/by user/i.test(error.message)) return 'user' + return 'browser' + } + if (error.name === 'NotFoundError' && isFireFox() && getOS() === 'macos') { + return 'system' + } + return null +} + /** * The `VideoConference` ready-made component is your drop-in solution for a classic video conferencing application. * It provides functionality such as focusing on one participant, grid view with pagination to handle large numbers @@ -61,6 +77,34 @@ export function VideoConference({ ...props }: VideoConferenceProps) { const [isShareErrorVisible, setIsShareErrorVisible] = useState(false) + const handleDeviceError = ({ + source, + error, + }: { + source: Track.Source + error: Error + }) => { + if (source === Track.Source.ScreenShare) { + const scope = getScreenSharePermissionDeniedScope(error) + if (scope) { + if (scope === 'system') setIsShareErrorVisible(true) + void captureMediaEvent('screen-share-permission-denied', { + at: 'ControlBar.onDeviceError', + source, + error_name: error.name, + error_message: error.message, + denied_scope: scope, + os: getOS(), + }) + return + } + } + reportError('device_switch_failure', error, { + at: 'ControlBar.onDeviceError', + source, + }) + } + return ( <> @@ -92,21 +136,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) { )} - { - reportError('device_switch_failure', e.error, { - at: 'ControlBar.onDeviceError', - source: e.source, - }) - if ( - e.source == Track.Source.ScreenShare && - e.error.toString() == - 'NotAllowedError: Permission denied by system' - ) { - setIsShareErrorVisible(true) - } - }} - /> + )}