Files
MatchLiveTv/mobile/lib/providers/score_sync_provider.dart
Emiliano Frascaro ab9cb02083 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>
2026-06-03 23:55:17 +02:00

78 lines
2.3 KiB
Dart

import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../shared/api_client.dart';
import '../shared/models/score_state.dart';
import 'score_provider.dart';
import 'session_provider.dart';
/// Coda sync HTTP: evita richieste fuori ordine e allinea l'app al DB (pagina live).
class ScoreSyncService {
ScoreSyncService(this._ref);
final Ref _ref;
bool _busy = false;
bool _dirty = false;
DateTime? _suppressStaleRemoteUntil;
/// Ignora broadcast WebSocket obsoleti subito dopo un push HTTP.
bool acceptCableScore(ScoreState remote) {
final until = _suppressStaleRemoteUntil;
if (until == null || DateTime.now().isAfter(until)) return true;
final local = _ref.read(scoreProvider);
return !_isClearlyBehind(remote, local);
}
bool _isClearlyBehind(ScoreState remote, ScoreState local) {
final r = remote.homeSets * 1000 +
remote.awaySets * 1000 +
remote.homePoints +
remote.awayPoints;
final l = local.homeSets * 1000 +
local.awaySets * 1000 +
local.homePoints +
local.awayPoints;
return r < l;
}
Future<void> push() async {
_dirty = true;
if (_busy) return;
_busy = true;
while (_dirty) {
_dirty = false;
final sessionId = _ref.read(sessionProvider).session?.id;
if (sessionId == null) continue;
final payload = _ref.read(scoreProvider).toCablePayload();
try {
final remote = await _ref.read(apiClientProvider).syncScore(sessionId, payload);
_suppressStaleRemoteUntil = DateTime.now().add(const Duration(milliseconds: 500));
if (remote != null) {
_ref.read(scoreProvider.notifier).applyRemote(remote);
}
} catch (_) {
// Mantieni stato locale; il prossimo pull periodico riallinea se possibile.
}
}
_busy = false;
}
Future<void> pullFromServer() async {
if (_busy) return;
final sessionId = _ref.read(sessionProvider).session?.id;
if (sessionId == null) return;
try {
final session = await _ref.read(apiClientProvider).fetchSession(sessionId);
if (session.score != null) {
_ref.read(scoreProvider.notifier).applyRemote(session.score!);
}
} catch (_) {}
}
}
final scoreSyncProvider = Provider<ScoreSyncService>((ref) {
return ScoreSyncService(ref);
});