diff --git a/server/src/index.ts b/server/src/index.ts index dc9d281..df33d9d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -975,9 +975,13 @@ app.get('/api/events', (req: Request, res: Response) => { // Snapshot senden try { res.write(`data: ${JSON.stringify({ type: 'snapshot', party: Array.from(partyActive) })}\n\n`); } catch {} + // Ping, damit Proxies die Verbindung offen halten + const ping = setInterval(() => { try { res.write(':\n\n'); } catch {} }, 15_000); + sseClients.add(res); req.on('close', () => { sseClients.delete(res); + clearInterval(ping); try { res.end(); } catch {} }); }); diff --git a/web/src/App.tsx b/web/src/App.tsx index 9ea23cc..a7a2f90 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -45,6 +45,7 @@ export default function App() { return `https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/${codePoints}.svg`; } const [showBroccoli, setShowBroccoli] = useState(false); + const [partyActiveGuilds, setPartyActiveGuilds] = useState([]); const selectedCount = useMemo(() => Object.values(selectedSet).filter(Boolean).length, [selectedSet]); const [clock, setClock] = useState(() => new Intl.DateTimeFormat('de-DE', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Berlin' }).format(new Date())); const [totalPlays, setTotalPlays] = useState(0); @@ -74,22 +75,31 @@ export default function App() { const h = await fetch('/api/health').then(r => r.json()).catch(() => null); if (h && typeof h.totalPlays === 'number') setTotalPlays(h.totalPlays); } catch {} - // SSE: Partymode Status global synchronisieren - const unsub = subscribeEvents((msg)=>{ - if (msg?.type === 'party') { - const [gid] = (selected||'').split(':'); - if (gid && msg.guildId === gid) { - setChaosMode(!!msg.active); - } - } else if (msg?.type === 'snapshot') { - const [gid] = (selected||'').split(':'); - if (gid) setChaosMode(msg.party?.includes(gid)); - } - }); - return () => { try { unsub(); } catch {} }; })(); }, []); + // SSE: Partymode-Status global synchronisieren (sauberes Cleanup) + useEffect(() => { + const unsub = subscribeEvents((msg) => { + if (msg?.type === 'party') { + setPartyActiveGuilds((prev) => { + const s = new Set(prev); + if (msg.active) s.add(msg.guildId); else s.delete(msg.guildId); + return Array.from(s); + }); + } else if (msg?.type === 'snapshot') { + setPartyActiveGuilds(Array.isArray(msg.party) ? msg.party : []); + } + }); + return () => { try { unsub(); } catch {} }; + }, []); + + // Aus aktivem Guild-Status die lokale Anzeige setzen + useEffect(() => { + const gid = selected ? selected.split(':')[0] : ''; + setChaosMode(gid ? partyActiveGuilds.includes(gid) : false); + }, [selected, partyActiveGuilds]); + // Uhrzeit (Berlin) aktualisieren useEffect(() => { const fmt = new Intl.DateTimeFormat('de-DE', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Berlin' });