Napis o mikrofonie idzie za uprawnieniem, nie za zgloszeniem
Deploy PROD / deploy (push) Successful in 24s

Interfejs sali mial DWA niezalezne zrodla prawdy o tym, czy uczestnik moze
mowic: napis "Masz glos, mikrofon jest wlaczony" bral sie ze stanu ZGLOSZENIA
(`speak.granted`), a faktyczny mikrofon z uprawnienia w TOKENIE
(`canPublishAudio`). Te dwie rzeczy nie sa tym samym.

Prawo do audio daje takze rola ACTIVE_PARTICIPANT, ktora uczestnik dostaje przy
wlaczonym trybie "wideo dla wszystkich". Odebranie glosu nie zmienia wtedy
uprawnien, ale zmienia stan zgloszenia: napis znikal, a mikrofon zostawal
otwarty. Po kilku cyklach nadania i odebrania obraz w panelu rozjezdzal sie
z tym, co uczestnik faktycznie moze. Zgloszone z produkcji.

Pasek akcji dostaje teraz `canPublishAudio` wprost z tokenu i to ono
rozstrzyga o napisie. Gdy glos jest juz przyznany, a token jeszcze nie dotarl,
pasek mowi "trwa wlaczanie mikrofonu" zamiast klamac w ktorakolwiek strone.
Przycisk "Oddaj glos" zostaje przy zgloszeniu, bo dotyczy wlasnie jego.

SLAD DO DIAGNOZY
Rozjazd miedzy interfejsem a serwerem jest trudny do zbadania po fakcie:
uczestnik zglasza "mikrofon nie dziala", a log serwera pokazuje poprawnie
wydany token. Konsola sali wypisuje teraz po kazdej zmianie stan tokenu obok
stanu zgloszenia i obu trybow, wiec przy nastepnym zgloszeniu widac, ktora
strona jest nieaktualna. To nie jest debugowanie do usuniecia: nigdzie indziej
nie widac obu tych rzeczy naraz.

Hipoteza o wygasajacym tokenie sprawdzona i ODRZUCONA: TTL to szesc godzin,
czyli dluzej niz najdluzszy webinar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 14:59:58 +02:00
parent ae696abff7
commit 64d5bc391d
2 changed files with 50 additions and 3 deletions
+28 -3
View File
@@ -32,6 +32,19 @@ export function MeetingActionBar({
isSpeaker,
/** Tryb otwartego mikrofonu: kazdy mowi bez proszenia o glos. */
openMic,
/**
* Faktyczne uprawnienie do mikrofonu, prosto z tokenu wydanego przez backend.
*
* JEDYNE ZRODLO PRAWDY O TYM, CZY UCZESTNIK MOZE MOWIC
* ---------------------------------------------------------------------
* Wczesniej pasek skladal te odpowiedz sam, ze stanu zgloszenia
* (`speak.granted`) i trybu otwartego mikrofonu. Rozjezdzalo sie to
* z rzeczywistoscia, bo prawo do audio daje takze rola ACTIVE_PARTICIPANT
* przy wlaczonym "wideo dla wszystkich": odebranie glosu nie zmienialo wtedy
* uprawnien, ale napis "Masz glos" znikal. Uczestnik widzial zamkniety
* mikrofon przy dzialajacym i odwrotnie.
*/
canPublishAudio,
/**
* Temat wybrany klikiem w baner.
*
@@ -46,6 +59,7 @@ export function MeetingActionBar({
speak: ReturnType<typeof useSpeakRequest>
isSpeaker: boolean
openMic: boolean
canPublishAudio: boolean
offerTopicFromBanner?: string | null
onOfferHandled?: () => void
}) {
@@ -96,7 +110,7 @@ export function MeetingActionBar({
Możesz mówić bez zgłaszania się
</span>
) : (
<SpeakButton speak={speak} />
<SpeakButton speak={speak} canPublishAudio={canPublishAudio} />
))}
{/*
@@ -193,13 +207,24 @@ export function MeetingActionBar({
* oczekiwania, czyli dokladnie ten, w ktorym uczestnik zastanawia sie,
* czy zgloszenie w ogole doszlo.
*/
function SpeakButton({ speak }: { speak: ReturnType<typeof useSpeakRequest> }) {
function SpeakButton({
speak,
canPublishAudio,
}: {
speak: ReturnType<typeof useSpeakRequest>
canPublishAudio: boolean
}) {
/*
* Napis o mikrofonie idzie za UPRAWNIENIEM, a nie za stanem zgloszenia:
* mowi o tym, co uczestnik faktycznie moze, a nie o tym, o co prosil.
* Przycisk "Oddaj glos" zostaje przy zgloszeniu, bo dotyczy wlasnie jego.
*/
if (speak.granted) {
return (
<div className="flex flex-wrap items-center gap-2">
<span className="inline-flex items-center gap-1.5 rounded-md bg-ok-50 px-3 py-1.5 text-sm font-semibold text-ok-600">
<Icon name="check-circle" size={16} />
Masz głos, mikrofon jest włączony
{canPublishAudio ? 'Masz głos, mikrofon jest włączony' : 'Masz głos, trwa włączanie mikrofonu...'}
</span>
<button
type="button"
+22
View File
@@ -240,6 +240,27 @@ function MeetingRoom({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [speak.granted, openMic, openVideo])
/*
* Slad w konsoli po kazdej zmianie uprawnien.
*
* Rozjazd miedzy tym, co pokazuje interfejs, a tym, co faktycznie pozwala
* serwer, jest trudny do zdiagnozowania po fakcie: uczestnik zglasza
* "mikrofon nie dziala", a log serwera pokazuje poprawnie wydany token.
* Ta linia pozwala porownac obie strony bez zgadywania - wystarczy otworzyc
* konsole na sali. Nie jest to debugowanie do usuniecia: to jedyne miejsce,
* w ktorym widac stan tokenu razem ze stanem zgloszenia.
*/
useEffect(() => {
console.info('[sala] uprawnienia', {
audio: join.canPublishAudio,
video: join.canPublishVideo,
rola: join.role,
zgloszenieGlosu: speak.granted,
otwartyMikrofon: openMic,
wideoDlaWszystkich: openVideo,
})
}, [join.canPublishAudio, join.canPublishVideo, join.role, speak.granted, openMic, openVideo])
const src = useMemo(() => {
if (!join.embedded) return null
const url = new URL(join.url)
@@ -337,6 +358,7 @@ function MeetingRoom({
speak={speak}
isSpeaker={isSpeaker}
openMic={openMic}
canPublishAudio={join.canPublishAudio}
offerTopicFromBanner={offerTopicFromBanner}
onOfferHandled={() => setOfferTopicFromBanner(null)}
/>