diff --git a/src/app/features/call/PrescreenControls.tsx b/src/app/features/call/PrescreenControls.tsx index 1174bbf1c..f313fec7f 100644 --- a/src/app/features/call/PrescreenControls.tsx +++ b/src/app/features/call/PrescreenControls.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Button, Icon, Icons, Spinner, Text } from 'folds'; import { SequenceCard } from '../../components/sequence-card'; import * as css from './styles.css'; @@ -7,6 +7,28 @@ import { useIsDirectRoom, useRoom } from '../../hooks/useRoom'; import { useCallEmbed, useCallJoined, useCallStart } from '../../hooks/useCallEmbed'; import { useCallPreferences } from '../../state/hooks/callPreferences'; +type MediaPermState = 'granted' | 'denied' | 'prompt' | 'unknown'; + +function useMediaPermissions(): MediaPermState { + const [state, setState] = useState('unknown'); + + useEffect(() => { + if (!navigator.permissions) { + setState('unknown'); + return; + } + navigator.permissions + .query({ name: 'microphone' as PermissionName }) + .then((result) => { + setState(result.state as MediaPermState); + result.onchange = () => setState(result.state as MediaPermState); + }) + .catch(() => setState('unknown')); + }, []); + + return state; +} + type PrescreenControlsProps = { canJoin?: boolean; }; @@ -21,7 +43,10 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) { const startCall = useCallStart(direct); const joining = callEmbed?.roomId === room.roomId && !callJoined; - const disabled = inOtherCall || !canJoin; + const micPermission = useMediaPermissions(); + const micDenied = micPermission === 'denied'; + + const disabled = inOtherCall || !canJoin || micDenied; const { microphone, video, sound, toggleMicrophone, toggleVideo, toggleSound } = useCallPreferences(); @@ -45,7 +70,12 @@ export function PrescreenControls({ canJoin }: PrescreenControlsProps) { - + + {micDenied && ( + + Microphone access is blocked. Enable it in your browser settings to join. + + )}