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>
78 lines
2.3 KiB
Dart
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);
|
|
});
|