Przycisk mikrofonu przy kazdym uczestniku daje i odbiera glos bez jego prosby. Idzie przez kolejke zgloszen (SpeakRequestService.setVoice): powstaje od razu przyznane zgloszenie albo zostaje przyznane oczekujace, wiec kolejka, napis "Masz glos" i automatyczne wlaczenie mikrofonu zgadzaja sie ze stanem. Serwer pokoju zmienia uprawnienia od razu. Uczestnik bez wlasnego zgloszenia rozpoznaje zdarzenie jako swoje po numerze zapisu z sesji, zamiast czekac na odpytanie co 10 sekund. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@ type Participant = {
|
||||
cameraAllowed: boolean
|
||||
/** Indywidualna zgoda prowadzacego na udostepnienie ekranu. */
|
||||
screenAllowed: boolean
|
||||
/** Przyznany glos, z kolejki albo bez zgloszenia. */
|
||||
voiceGranted: boolean
|
||||
}
|
||||
|
||||
type Presence = {
|
||||
@@ -179,6 +181,29 @@ export function RoomParticipants({ webinarId, timezone }: { webinarId: string; t
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Glos bez zgloszenia. Idzie przez kolejke zgloszen, wiec uczestnik widzi
|
||||
* "Masz glos", mikrofon wlacza mu sie sam, a prowadzacy moze go odebrac
|
||||
* takze z karty "Zgloszenia do glosu".
|
||||
*/
|
||||
async function toggleVoice(participant: Participant) {
|
||||
if (participant.registrationId === null) return
|
||||
const next = !participant.voiceGranted
|
||||
setBusy(participant.identity)
|
||||
try {
|
||||
await client.post(
|
||||
`/admin/webinars/${webinarId}/speak-requests/participants/${participant.registrationId}/voice`,
|
||||
{ value: next },
|
||||
)
|
||||
toasts.success(next ? `${participant.name} ma głos` : `Odebrano głos: ${participant.name}`)
|
||||
await refresh()
|
||||
} catch (error) {
|
||||
toasts.error(errorMessage(error, 'Nie udało się zmienić głosu'))
|
||||
} finally {
|
||||
setBusy(null)
|
||||
}
|
||||
}
|
||||
|
||||
async function unban(registrationId: number, name: string) {
|
||||
setBusy(`ban-${registrationId}`)
|
||||
try {
|
||||
@@ -304,6 +329,31 @@ export function RoomParticipants({ webinarId, timezone }: { webinarId: string; t
|
||||
egzekwuje osobno kazde zrodlo. Przy innych dostawcach
|
||||
przycisk obiecywalby cos, czego nikt nie pilnuje.
|
||||
*/}
|
||||
{/*
|
||||
Glos dziala u kazdego dostawcy, bo to ta sama droga co
|
||||
przyznanie z kolejki. Kamera i ekran tylko przy LiveKit.
|
||||
*/}
|
||||
{participant.kind === 'PARTICIPANT' && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={busy === participant.identity}
|
||||
onClick={() => void toggleVoice(participant)}
|
||||
aria-pressed={participant.voiceGranted}
|
||||
aria-label={
|
||||
participant.voiceGranted
|
||||
? `Odbierz głos: ${participant.name}`
|
||||
: `Daj głos bez zgłoszenia: ${participant.name}`
|
||||
}
|
||||
title={participant.voiceGranted ? 'Odbierz głos' : 'Daj głos bez zgłoszenia'}
|
||||
className={`flex h-9 w-9 items-center justify-center rounded-md disabled:opacity-40 ${
|
||||
participant.voiceGranted
|
||||
? 'bg-ok-50 text-ok-600 hover:bg-ok-50'
|
||||
: 'text-slate-400 hover:bg-slate-100 hover:text-slate-700'
|
||||
}`}
|
||||
>
|
||||
<Icon name={participant.voiceGranted ? 'mic' : 'mic-off'} size={16} />
|
||||
</button>
|
||||
)}
|
||||
{participant.kind === 'PARTICIPANT' && data?.source === 'LIVEKIT' && (
|
||||
<>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user