@@ -1768,6 +1768,14 @@ async function stopBroadcast() {
17681768
17691769 isBroadcasting = false ;
17701770
1771+ // Reset UI immediately — before any async calls that might fail
1772+ const startBtn = document . getElementById ( 'startBroadcastBtn' ) ;
1773+ if ( startBtn ) {
1774+ startBtn . textContent = '📡 Broadcast' ;
1775+ startBtn . classList . remove ( 'is-live' ) ;
1776+ startBtn . onclick = startBroadcast ;
1777+ }
1778+
17711779 // Close all peer connections to viewers
17721780 peerConnections . forEach ( pc => pc . close ( ) ) ;
17731781 peerConnections . clear ( ) ;
@@ -1777,14 +1785,8 @@ async function stopBroadcast() {
17771785 broadcastStream = null ;
17781786 }
17791787
1780- await fetch ( '/broadcast/end' , { method : 'POST' } ) ;
1781-
1782- const startBtn = document . getElementById ( 'startBroadcastBtn' ) ;
1783- if ( startBtn ) {
1784- startBtn . textContent = '📡 Broadcast' ;
1785- startBtn . classList . remove ( 'is-live' ) ;
1786- startBtn . onclick = startBroadcast ;
1787- }
1788+ // Best effort — server may already be down
1789+ try { await fetch ( '/broadcast/end' , { method : 'POST' } ) ; } catch ( e ) { }
17881790}
17891791
17901792// Called when broadcaster gets notified a new viewer joined
@@ -1861,21 +1863,27 @@ function joinBroadcast() {
18611863 label . textContent = `${ document . getElementById ( 'broadcastLabel' ) . textContent } ` ;
18621864 panel . style . display = 'flex' ;
18631865
1866+ // Hide join button while viewing
1867+ const joinBtn = document . getElementById ( 'broadcastJoinBtn' ) ;
1868+ if ( joinBtn ) joinBtn . style . display = 'none' ;
1869+
18641870 // Reset position to default top-right on each join
18651871 panel . style . left = '' ;
18661872 panel . style . top = '' ;
18671873 panel . style . right = '20px' ;
18681874
18691875 makeDraggable ( panel ) ;
1870-
1871- // Tell server we joined — get last frame immediately
18721876 socket . emit ( 'broadcast-join' ) ;
18731877}
18741878
18751879function leaveBroadcast ( ) {
18761880 const panel = document . getElementById ( 'viewerPanel' ) ;
18771881 if ( panel ) panel . style . display = 'none' ;
18781882
1883+ // Restore join button
1884+ const joinBtn = document . getElementById ( 'broadcastJoinBtn' ) ;
1885+ if ( joinBtn ) joinBtn . style . display = 'inline-block' ;
1886+
18791887 // Clean up viewer peer connection
18801888 if ( viewerPeerConnection ) {
18811889 viewerPeerConnection . close ( ) ;
@@ -2094,6 +2102,31 @@ socket.on('webrtc-ice', async ({ candidate, fromId }) => {
20942102 }
20952103} ) ;
20962104
2105+ socket . on ( 'disconnect' , ( ) => {
2106+ // Trigger the same cleanup flow as a normal broadcast-ended event
2107+ if ( isBroadcasting || viewerPeerConnection ) {
2108+ isBroadcasting = false ;
2109+
2110+ peerConnections . forEach ( pc => pc . close ( ) ) ;
2111+ peerConnections . clear ( ) ;
2112+
2113+ if ( broadcastStream ) {
2114+ broadcastStream . getTracks ( ) . forEach ( t => t . stop ( ) ) ;
2115+ broadcastStream = null ;
2116+ }
2117+
2118+ const startBtn = document . getElementById ( 'startBroadcastBtn' ) ;
2119+ if ( startBtn ) {
2120+ startBtn . textContent = '📡 Broadcast' ;
2121+ startBtn . classList . remove ( 'is-live' ) ;
2122+ startBtn . onclick = startBroadcast ;
2123+ }
2124+
2125+ hideBroadcastBar ( ) ;
2126+ leaveBroadcast ( ) ;
2127+ }
2128+ } ) ;
2129+
20972130// ─── RAISE HAND ──────────────────────────────────────────────
20982131
20992132socket . on ( 'broadcast-reaction-received' , ( { from } ) => {
0 commit comments