🚸(frontend) add permission hints and modal button to join screen

Add explicit messaging on join screen explaining why users should
allow camera/microphone access, with quick button to open permission
modal dialog.

Targets first-time users who need guidance on permission requirements.
Message persists until permissions are granted to ensure proper user
onboarding and reduce support issues.
This commit is contained in:
lebaudantoine
2025-08-09 23:53:00 +02:00
committed by aleb_the_flash
parent 4fae3c6c47
commit 0e72f61650
5 changed files with 76 additions and 8 deletions

View File

@@ -27,6 +27,8 @@ import { ApiLobbyStatus, ApiRequestEntry } from '../api/requestEntry'
import { Spinner } from '@/primitives/Spinner'
import { ApiAccessLevel } from '../api/ApiRoom'
import { useLoginHint } from '@/hooks/useLoginHint'
import { useSnapshot } from 'valtio'
import { openPermissionsDialog, permissionsStore } from '@/stores/permissions'
const onError = (e: Error) => console.error('ERROR', e)
@@ -220,19 +222,48 @@ export const Join = ({
enterRoom()
}
const permissions = useSnapshot(permissionsStore)
const isCameraDeniedOrPrompted =
permissions.isCameraDenied || permissions.isCameraPrompted
const isMicrophoneDeniedOrPrompted =
permissions.isMicrophoneDenied || permissions.isMicrophonePrompted
const hintMessage = useMemo(() => {
if (isCameraDeniedOrPrompted) {
return isMicrophoneDeniedOrPrompted
? 'cameraAndMicNotGranted'
: 'cameraNotGranted'
}
if (!videoEnabled) {
return 'cameraDisabled'
}
if (!isVideoInitiated.current) {
return 'cameraStarting'
}
if (videoTrack && videoEnabled) {
return ''
}
}, [videoTrack, videoEnabled])
}, [
videoTrack,
videoEnabled,
isCameraDeniedOrPrompted,
isMicrophoneDeniedOrPrompted,
])
const permissionsButtonLabel = useMemo(() => {
if (!isMicrophoneDeniedOrPrompted && !isCameraDeniedOrPrompted) {
return null
}
if (isCameraDeniedOrPrompted && isMicrophoneDeniedOrPrompted) {
return 'cameraAndMicNotGranted'
}
if (isCameraDeniedOrPrompted && !isMicrophoneDeniedOrPrompted) {
return 'cameraNotGranted'
}
return null
}, [isMicrophoneDeniedOrPrompted, isCameraDeniedOrPrompted])
const renderWaitingState = () => {
switch (status) {
@@ -416,7 +447,10 @@ export const Join = ({
width="1280"
height="720"
style={{
display: !videoEnabled ? 'none' : undefined,
display:
!videoEnabled || isCameraDeniedOrPrompted
? 'none'
: undefined,
}}
className={css({
position: 'absolute',
@@ -443,6 +477,7 @@ export const Join = ({
alignItems: 'center',
padding: '0.24rem',
boxSizing: 'border-box',
gap: '1rem',
})}
>
<p
@@ -455,6 +490,15 @@ export const Join = ({
>
{hintMessage && t(hintMessage)}
</p>
{isCameraDeniedOrPrompted && (
<Button
size="sm"
variant="tertiary"
onPress={openPermissionsDialog}
>
{t(`permissionsButton.${permissionsButtonLabel}`)}
</Button>
)}
</div>
</div>
<div

View File

@@ -37,7 +37,13 @@
"usernameEmpty": "Ihr Name darf nicht leer sein"
},
"cameraDisabled": "Kamera ist deaktiviert.",
"cameraStarting": "Kamera wird gestartet.",
"cameraStarting": "Kamera wird gestartet",
"cameraNotGranted": "Möchten Sie, dass andere Sie während der Besprechung sehen können?",
"cameraAndMicNotGranted": "Möchten Sie, dass andere Sie während der Besprechung sehen und hören können?",
"permissionsButton": {
"cameraAndMicNotGranted": "Zugriff auf Kamera und Mikrofon erlauben",
"cameraNotGranted": "Zugriff auf Kamera erlauben"
},
"waiting": {
"title": "Beitrittsanfrage wird gesendet...",
"body": "Sie können diesem Anruf beitreten, sobald jemand Sie autorisiert"

View File

@@ -37,7 +37,13 @@
"usernameEmpty": "Your name cannot be empty"
},
"cameraDisabled": "Camera is disabled.",
"cameraStarting": "Camera is starting.",
"cameraStarting": "Camera is starting",
"cameraNotGranted": "Would you like others to be able to see you during the meeting?",
"cameraAndMicNotGranted": "Would you like others to be able to see and hear you during the meeting?",
"permissionsButton": {
"cameraAndMicNotGranted": "Allow access to camera and microphone",
"cameraNotGranted": "Allow access to camera"
},
"waiting": {
"title": "Requesting to join...",
"body": "You will be able to join this call when someone authorizes you"

View File

@@ -37,7 +37,13 @@
"usernameEmpty": "Votre nom ne peut pas être vide"
},
"cameraDisabled": "La caméra est désactivée.",
"cameraStarting": "La caméra va démarrer.",
"cameraStarting": "La caméra va démarrer",
"cameraNotGranted": "Souhaitez-vous que les autres puissent vous voir pendant la réunion ?",
"cameraAndMicNotGranted": "Souhaitez-vous que les autres puissent vous voir et vous entendre pendant la réunion ?",
"permissionsButton": {
"cameraAndMicNotGranted": "Autorisez l'accès à la caméra et au micro",
"cameraNotGranted": "Autorisez l'accès à la caméra"
},
"waiting": {
"title": "Demande de participation…",
"body": "Vous pourrez participer à cet appel lorsque quelqu'un vous y autorisera"

View File

@@ -37,7 +37,13 @@
"usernameEmpty": "Uw naam kan niet leeg zijn"
},
"cameraDisabled": "Camera is uitgeschakeld.",
"cameraStarting": "Camera wordt ingeschakeld.",
"cameraStarting": "Camera wordt ingeschakeld",
"cameraNotGranted": "Wilt u dat anderen u tijdens de vergadering kunnen zien?",
"cameraAndMicNotGranted": "Wilt u dat anderen u tijdens de vergadering kunnen zien en horen?",
"permissionsButton": {
"cameraAndMicNotGranted": "Toegang tot camera en microfoon toestaan",
"cameraNotGranted": "Toegang tot camera toestaan"
},
"waiting": {
"title": "Verzoek tot deelname...",
"body": "U kunt deelnemen aan dit gesprek wanneer iemand u toestemming geeft"