@@ -28,27 +28,34 @@ defmodule CodebattleWeb.Live.Admin.TournamentStreamView do
2828 Codebattle.PubSub . subscribe ( "tournament:#{ tournament . id } " )
2929 Codebattle.PubSub . subscribe ( "tournament:#{ tournament . id } :common" )
3030 Codebattle.PubSub . subscribe ( "tournament:#{ tournament . id } :stream" )
31- # Drive the "round ends in" countdown.
32- Process . send_after ( self ( ) , :tick , 1000 )
3331 end
3432
35- { :ok ,
36- socket
37- |> assign (
38- layout: { CodebattleWeb.LayoutView , :admin } ,
39- tournament: tournament ,
40- current_user: current_user ,
41- active_game_id: TournamentAdminChannel . get_active_game ( tournament . id ) ,
42- autoselect_delay_ms: TournamentAdminChannel . get_autoselect_delay ( tournament . id ) ,
43- widgets: @ widgets ,
44- filter: "current" ,
45- now: NaiveDateTime . utc_now ( :second ) ,
46- # The simulator panel is always available on the admin stream page, so bots
47- # can be started for any tournament — even one created without meta.simulator.
48- simulator_enabled: true
49- )
50- |> assign_matches_and_players ( )
51- |> assign_simulator_state ( ) }
33+ socket =
34+ socket
35+ |> assign (
36+ layout: { CodebattleWeb.LayoutView , :admin } ,
37+ tournament: tournament ,
38+ current_user: current_user ,
39+ active_game_id: TournamentAdminChannel . get_active_game ( tournament . id ) ,
40+ autoselect_delay_ms: TournamentAdminChannel . get_autoselect_delay ( tournament . id ) ,
41+ widgets: @ widgets ,
42+ filter: "current" ,
43+ now: NaiveDateTime . utc_now ( :second ) ,
44+ # Whether the per-second countdown tick loop is currently armed.
45+ ticking: false ,
46+ # The simulator panel is always available on the admin stream page, so bots
47+ # can be started for any tournament — even one created without meta.simulator.
48+ simulator_enabled: true
49+ )
50+ |> assign_matches_and_players ( )
51+ |> assign_simulator_state ( )
52+
53+ # Only arm the "round ends in" countdown once connected, and only while a
54+ # round is actually counting down (see ensure_ticking/1). A finished/waiting
55+ # tournament shows a static label, so it must not push a diff every second.
56+ socket = if connected? ( socket ) , do: ensure_ticking ( socket ) , else: socket
57+
58+ { :ok , socket }
5259 end
5360
5461 defp assign_simulator_state ( socket ) do
@@ -142,19 +149,52 @@ defmodule CodebattleWeb.Live.Admin.TournamentStreamView do
142149 "tournament:updated" ,
143150 "tournament:finished"
144151 ] do
145- { :noreply , socket |> assign_matches_and_players ( ) |> assign_simulator_state ( ) }
152+ { :noreply ,
153+ socket
154+ |> assign_matches_and_players ( )
155+ |> assign_simulator_state ( )
156+ |> ensure_ticking ( ) }
146157 end
147158
148159 def handle_info ( :tick , socket ) do
149- Process . send_after ( self ( ) , :tick , 1000 )
150- { :noreply , assign ( socket , now: NaiveDateTime . utc_now ( :second ) ) }
160+ socket = assign ( socket , now: NaiveDateTime . utc_now ( :second ) )
161+
162+ # Keep the loop alive only while there is a moving countdown to refresh.
163+ # Otherwise stop, so a finished/waiting tournament no longer pushes a diff
164+ # to the client every second; a new round re-arms it via ensure_ticking/1.
165+ socket =
166+ if countdown_active? ( socket . assigns . tournament ) do
167+ Process . send_after ( self ( ) , :tick , 1000 )
168+ socket
169+ else
170+ assign ( socket , ticking: false )
171+ end
172+
173+ { :noreply , socket }
151174 end
152175
153176 def handle_info ( msg , socket ) do
154177 Logger . debug ( "Stream admin LV unexpected: #{ inspect ( msg ) } " )
155178 { :noreply , socket }
156179 end
157180
181+ # Arm the countdown tick loop if it isn't already running and the tournament is
182+ # in a state that needs a per-second countdown. Idempotent: safe to call from
183+ # mount and from any handler that may have changed the tournament state.
184+ defp ensure_ticking ( socket ) do
185+ if not socket . assigns . ticking and countdown_active? ( socket . assigns . tournament ) do
186+ Process . send_after ( self ( ) , :tick , 1000 )
187+ assign ( socket , ticking: true )
188+ else
189+ socket
190+ end
191+ end
192+
193+ # Only an active round (including its break) has a moving "ends in / next round
194+ # in" countdown. Finished/waiting tournaments show a static label.
195+ defp countdown_active? ( % { state: "active" } ) , do: true
196+ defp countdown_active? ( _ ) , do: false
197+
158198 defp assign_matches_and_players ( socket ) do
159199 tournament =
160200 try do
@@ -519,7 +559,11 @@ defmodule CodebattleWeb.Live.Admin.TournamentStreamView do
519559 </ div >
520560 <% end %>
521561
522- < details class = "cb-bg-panel cb-rounded cb-border-color border shadow-sm p-2 mb-3 " >
562+ < details
563+ id = "obs-stream-urls "
564+ phx-update = "ignore "
565+ class = "cb-bg-panel cb-rounded cb-border-color border shadow-sm p-2 mb-3 "
566+ >
523567 < summary class = "text-white " style = "cursor:pointer;font-size:14px;font-weight:600 " >
524568 OBS / stream URLs
525569 < span class = "cb-text ml-1 " style = "font-size:12px;font-weight:400 " > ({ length ( @ widgets ) } )</ span >
0 commit comments