From ae696abff70e69446421218913a3cd1fd332d9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Zatorski?= Date: Fri, 4 Sep 2026 14:59:38 +0200 Subject: [PATCH] Kolejka glosu odswieza sie takze bez zdarzenia Kolejka zgloszen i stan glosu polegaly WYLACZNIE na zdarzeniach WebSocket. Zdarzenie potrafi nie dojsc, a polaczenie nie zawsze to sygnalizuje: karta uspiona przez system, chwilowa utrata sieci, przelaczenie z WiFi na komorke. WebSocket nie ma przy tym historii, wiec zgubionego zdarzenia nikt nie powtorzy. Prowadzacy widzial przez to "Nikt nie prosi o glos" przy zgloszeniach czekajacych juz kilka minut i dowiadywal sie o nich dopiero po odswiezeniu strony. Po stronie UCZESTNIKA bylo grozniej: stan pobieral sie RAZ, przy wejsciu na sale. Zgubione zdarzenie o przyznaniu glosu oznaczalo, ze uczestnik nie dowiadywal sie o nim wcale: przycisk zostawal w starym stanie, a mikrofon zamkniety, chociaz uprawnienie po stronie serwera juz bylo. Wyglada to na niedzialajacy mikrofon, a nie na zgubione zdarzenie, wiec szukanie przyczyny idzie w zla strone. Odpytywanie jest DRUGA droga, nie glowna: WebSocket zostaje, bo prowadzacy ma widziec podniesiona reke od razu. Panel odswieza kolejke co 5 sekund, uczestnik swoj stan co 10. Odpowiedz to kilka wierszy albo pojedynczy obiekt, wiec koszt jest znikomy nawet przy duzej sali. Ten sam wzorzec co przy banerach, gdzie front takze nie polega wylacznie na zdarzeniach. Wczesniejsza poprawka (dociagniecie stanu w `onConnect`) lapala tylko jeden przypadek: ponowne polaczenie. Nie pomagala, gdy polaczenie formalnie zyje, a ginie pojedyncze zdarzenie. Co-Authored-By: Claude Opus 5 --- frontend/src/lib/useSpeakRequest.ts | 41 +++++++++++++++---- .../src/pages/webinars/HostConsolePage.tsx | 21 ++++++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/useSpeakRequest.ts b/frontend/src/lib/useSpeakRequest.ts index 42720fe..0bc8584 100644 --- a/frontend/src/lib/useSpeakRequest.ts +++ b/frontend/src/lib/useSpeakRequest.ts @@ -42,18 +42,41 @@ export function useSpeakRequest(token: string | undefined) { if (!token) return let cancelled = false - publicClient - .get<{ request: SpeakRequestView | null }>(`/public/participation/${token}/speak-request`) - .then((response) => { - if (!cancelled) setRequest(response.data.request) - }) - .catch(() => { - // brak stanu nie moze zablokowac wejscia na spotkanie: uczestnik - // zobaczy przycisk w stanie wyjsciowym i zglosi sie ponownie - }) + function load() { + publicClient + .get<{ request: SpeakRequestView | null }>(`/public/participation/${token}/speak-request`) + .then((response) => { + if (!cancelled) setRequest(response.data.request) + }) + .catch(() => { + // brak stanu nie moze zablokowac wejscia na spotkanie: uczestnik + // zobaczy przycisk w stanie wyjsciowym i zglosi sie ponownie + }) + } + + load() + + /* + * ODSWIEZANIE OKRESOWE JAKO SIATKA BEZPIECZENSTWA + * ------------------------------------------------------------------------- + * WebSocket zostaje glowna droga: uczestnik ma zobaczyc przyznany glos od + * razu, a nie po kilkunastu sekundach. + * + * Zdarzenie potrafi jednak nie dojsc, a polaczenie nie zawsze to + * sygnalizuje: karta uspiona przez system, chwilowa utrata sieci, + * przelaczenie z WiFi na komorke. Bez odpytywania uczestnik NIE DOWIE SIE, + * ZE MA GLOS: przycisk zostaje w starym stanie, a mikrofon zamkniety, + * chociaz uprawnienie jest juz przyznane po stronie serwera. Wyglada to na + * niedzialajacy mikrofon, a nie na zgubione zdarzenie. + * + * Odpowiedz to jeden maly obiekt albo null, wiec dziesiec sekund nie kosztuje + * nic zauwazalnego nawet przy duzej sali. + */ + const timer = window.setInterval(load, 10000) return () => { cancelled = true + window.clearInterval(timer) } }, [token]) diff --git a/frontend/src/pages/webinars/HostConsolePage.tsx b/frontend/src/pages/webinars/HostConsolePage.tsx index 73b6f5e..f30e856 100644 --- a/frontend/src/pages/webinars/HostConsolePage.tsx +++ b/frontend/src/pages/webinars/HostConsolePage.tsx @@ -127,8 +127,29 @@ export function HostConsolePage() { } }, [id]) + /* + * ODSWIEZANIE OKRESOWE JAKO SIATKA BEZPIECZENSTWA + * --------------------------------------------------------------------------- + * Kolejka odswieza sie zdarzeniem WebSocket i tak ma zostac: prowadzacy widzi + * podniesiona reke od razu, a nie po kilkunastu sekundach. + * + * Zdarzenie potrafi jednak nie dojsc, a polaczenie nie zawsze to sygnalizuje: + * karta uspiona przez system, chwilowa utrata sieci, przelaczenie WiFi na + * telefonie. Klient wznawia wtedy polaczenie, ale zdarzenia wyslanego + * w miedzyczasie nikt juz nie powtorzy, bo WebSocket nie ma historii. + * Prowadzacy widzial przez to "Nikt nie prosi o glos" przy zgloszeniach + * czekajacych juz kilka minut, i dowiadywal sie o nich dopiero po odswiezeniu + * strony. Zdarzylo sie to na zywo. + * + * Odpytywanie jest wiec DRUGA droga, nie glowna. Piec sekund jest tanie: + * kolejka to kilka wierszy, a odpowiedz przy pustej liscie wazy tyle co nic. + * Ten sam wzorzec co przy banerach na sali, gdzie front takze nie polega + * wylacznie na zdarzeniach. + */ useEffect(() => { void loadSpeakQueue() + const timer = window.setInterval(() => void loadSpeakQueue(), 5000) + return () => window.clearInterval(timer) }, [loadSpeakQueue]) const webinar = useQuery({