Aggiunge overlay server-side burn-in e stabilizza diretta live.
Tabellone, badge e brand sono ricodificati nel flusso _air via OverlayRelay; sync punteggio HTTP, volume condiviso rails/sidekiq, qualità 720p migliorata e badge CONNECTING in ripresa da pausa. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import '../providers/auth_provider.dart';
|
||||
import 'models/invite_preview.dart';
|
||||
import 'models/match.dart';
|
||||
import 'models/recording_item.dart';
|
||||
import 'models/score_state.dart';
|
||||
import 'models/stream_session.dart';
|
||||
import 'models/team.dart';
|
||||
import 'models/user.dart';
|
||||
@@ -210,6 +211,19 @@ class ApiClient {
|
||||
return StreamSession.fromJson(response.data!);
|
||||
}
|
||||
|
||||
/// Persiste il tabellone e restituisce lo stato salvato sul server.
|
||||
Future<ScoreState?> syncScore(String sessionId, Map<String, dynamic> payload) async {
|
||||
final response = await _dio.patch<Map<String, dynamic>>(
|
||||
'/sessions/$sessionId/score',
|
||||
data: payload,
|
||||
);
|
||||
final score = response.data?['score'];
|
||||
if (score is Map<String, dynamic>) {
|
||||
return ScoreState.fromJson(score);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<RegiaLinkResponse> createRegiaLink(String sessionId) async {
|
||||
final response = await _dio.post<Map<String, dynamic>>(
|
||||
'/sessions/$sessionId/regia_link',
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:x_action_cable_v2/x_action_cable.dart';
|
||||
import '../core/config.dart';
|
||||
import '../providers/auth_provider.dart';
|
||||
import '../providers/score_provider.dart';
|
||||
import '../providers/score_sync_provider.dart';
|
||||
import '../providers/session_provider.dart';
|
||||
import 'models/device_state.dart';
|
||||
import 'models/score_state.dart';
|
||||
@@ -88,7 +89,10 @@ class WebsocketService {
|
||||
|
||||
switch (type) {
|
||||
case 'score_update':
|
||||
_ref.read(scoreProvider.notifier).applyRemote(ScoreState.fromJson(data));
|
||||
final remote = ScoreState.fromJson(data);
|
||||
if (_ref.read(scoreSyncProvider).acceptCableScore(remote)) {
|
||||
_ref.read(scoreProvider.notifier).applyRemote(remote);
|
||||
}
|
||||
break;
|
||||
case 'device_state':
|
||||
_ref
|
||||
@@ -109,6 +113,14 @@ class WebsocketService {
|
||||
_ref.read(sessionProvider.notifier).applyCommand(action);
|
||||
}
|
||||
break;
|
||||
case 'stream_event':
|
||||
final event = data['event'] as String?;
|
||||
if (event == 'paused') {
|
||||
_ref.read(sessionProvider.notifier).applyCommand('pause_stream');
|
||||
} else if (event == 'resumed') {
|
||||
_ref.read(sessionProvider.notifier).applyCommand('resume_stream');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app/theme.dart';
|
||||
import '../../features/shared/live_score_actions.dart';
|
||||
import '../../providers/score_provider.dart';
|
||||
import '../websocket_service.dart';
|
||||
import '../../providers/score_sync_provider.dart';
|
||||
import 'score_overlay_bar.dart';
|
||||
|
||||
/// Tabellone interattivo: aggiorna punteggio via WebSocket (senza link regia).
|
||||
@@ -27,8 +29,8 @@ class LiveScoreControls extends ConsumerWidget {
|
||||
final Future<void> Function()? onStopStream;
|
||||
|
||||
void _sync(WidgetRef ref) {
|
||||
if (!ref.read(websocketServiceProvider).isConnected) return;
|
||||
ref.read(websocketServiceProvider).sendScoreUpdate(ref.read(scoreProvider));
|
||||
// HTTP serializzato; il server fa broadcast WebSocket. Pagina live = status.json.
|
||||
unawaited(ref.read(scoreSyncProvider).push());
|
||||
}
|
||||
|
||||
Future<void> _pointHome(BuildContext context, WidgetRef ref) async {
|
||||
|
||||
Reference in New Issue
Block a user