Fix regia basket e stabilità diretta iOS.

La regia remota non va più in 500 su basket/timed; iOS gestisce chiusura set, riconnessione RTMP e avanzamento quarti in modo affidabile.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Emiliano Frascaro
2026-06-20 12:30:34 +02:00
co-authored by Cursor
parent 731b43ea88
commit 270bf680a4
16 changed files with 665 additions and 441 deletions
@@ -117,7 +117,10 @@ struct BroadcastScreen: View {
}
}
.alert("Errore", isPresented: Binding(get: { error != nil }, set: { if !$0 { error = nil } })) {
Button("OK") { onFinished() }
Button("Riprova") {
Task { await retryBroadcast() }
}
Button("Esci", role: .destructive) { onFinished() }
} message: {
Text(error ?? "")
}
@@ -240,7 +243,12 @@ struct BroadcastScreen: View {
onCloseSet: usesSetScoring ? {
Task { await liveScoreActions(for: match).requestCloseSet() }
} : nil,
onAdvancePeriod: usesActionScoring ? { scoreController.applyAction("advance_period") } : nil,
onAdvancePeriod: usesActionScoring ? {
Task {
guard await scoreController.applyBoardAction("advance_period") else { return }
updateOverlay()
}
} : nil,
onPauseOrResume: {
if isPaused {
Task { await resumeStream() }
@@ -285,6 +293,7 @@ struct BroadcastScreen: View {
if isPaused { return MatchColors.accentYellow }
switch metrics.phase {
case .live: return MatchColors.successGreen
case .reconnecting: return MatchColors.accentYellow
case .error: return MatchColors.primaryRed
default: return MatchColors.accentYellow
}
@@ -500,6 +509,18 @@ struct BroadcastScreen: View {
)
}
private func retryBroadcast() async {
error = nil
guard let session, let url = session.rtmpIngestUrl, !url.isEmpty else { return }
let config = broadcastConfig(for: session, rtmpUrl: url)
do {
try await container.broadcastCoordinator.resumeBroadcast(config: config)
snackbarMessage = "Riconnessione avviata"
} catch {
self.error = UserFacingError.message(for: error) ?? "Riconnessione non riuscita"
}
}
private func stopStream() async {
_ = try? await container.sessionRepository.stopSession(id: sessionId)
await teardown()
@@ -31,9 +31,10 @@ final class LiveScoreDialogHost: ObservableObject {
}
func resolve(_ value: Bool) {
guard let continuation = waiter else { return }
pending = nil
waiter?.resume(returning: value)
waiter = nil
continuation.resume(returning: value)
}
}
@@ -131,10 +132,9 @@ struct ScoreDialogRouter: View {
private var dialogBinding: Binding<ScoreDialogState?> {
Binding(
get: { host.pending },
set: { newValue in
if newValue == nil, host.pending != nil {
host.resolve(false)
}
set: { _ in
// Non risolvere qui: su iPad la dismiss dell'alert può azzerare l'item
// dopo il tap su «Chiudi set» e sovrascrivere resolve(true) con false.
}
)
}