bora para o relatorio

This commit is contained in:
2026-05-21 10:51:35 +01:00
parent 332361c296
commit 7d2f3c4679
3 changed files with 435 additions and 293 deletions

View File

@@ -35,6 +35,7 @@
StreamSubscription? _syncSubscription;
bool _isApplyingRemoteSync = false;
final Set<String> _appliedSyncEventIds = {};
final Map<String, DateTime> _lastAppliedActionAt = {};
@override
void initState() {
@@ -169,7 +170,7 @@
_sharedWithName = await _resolveUserName(sharedWith);
}
_setupSyncListener();
await _setupSyncListener();
setState(() {});
}
@@ -189,10 +190,11 @@
}
}
void _setupSyncListener() {
Future<void> _setupSyncListener() async {
if (_sessionId == null) return;
_syncSubscription?.cancel();
_appliedSyncEventIds.clear();
await _seedAppliedSyncEventIds();
_syncSubscription = _sharingController
.listenToGameSyncOthers(_sessionId!)
.listen(
@@ -233,17 +235,39 @@
});
for (final record in rows) {
final recordId = record['id']?.toString();
if (recordId == null || _appliedSyncEventIds.contains(recordId)) {
final recordId = record['id']?.toString();
if (recordId == null || _appliedSyncEventIds.contains(recordId)) {
continue;
}
// Skip if this event is older or equal to the last applied event of the same type
final actionType = record['action_type']?.toString();
DateTime? recordTime;
try {
final created = record['created_at']?.toString();
if (created != null) recordTime = DateTime.parse(created);
} catch (_) {
recordTime = null;
}
if (actionType != null && recordTime != null) {
final last = _lastAppliedActionAt[actionType];
if (last != null && !recordTime.isAfter(last)) {
_appliedSyncEventIds.add(recordId);
continue;
}
_appliedSyncEventIds.add(recordId);
print(
"🔄 Evento remoto recebido: ${record['action_type']} - ${record['action_data']}",
);
_applyRemoteSyncEvent(record);
}
_appliedSyncEventIds.add(recordId);
if (actionType != null && recordTime != null) {
_lastAppliedActionAt[actionType] = recordTime;
}
print(
"🔄 Evento remoto recebido: ${record['action_type']} - ${record['action_data']}",
);
_applyRemoteSyncEvent(record);
}
},
onError: (error) {
print("⚠️ Erro no stream de sync: $error");
@@ -251,6 +275,29 @@
);
}
Future<void> _seedAppliedSyncEventIds() async {
if (_sessionId == null) return;
try {
final response = await Supabase.instance.client
.from('game_sync_events')
.select('id')
.eq('session_id', _sessionId!)
.order('created_at', ascending: true);
if (response is List) {
for (final item in response) {
final id = item['id']?.toString();
if (id != null) {
_appliedSyncEventIds.add(id);
}
}
}
} catch (e) {
print('⚠️ Erro ao semear eventos históricos de sync: $e');
}
}
void _handleSyncRecords(Map<String, dynamic> record) {
// Mantido apenas como fallback, mas a escuta principal usa listenToGameSyncOthers.
_applyRemoteSyncEvent(record);
@@ -317,7 +364,6 @@
if (remoteIsRunning != _controller.isRunning) {
_isApplyingRemoteSync = true;
_controller.applyRemoteTimerState(remoteIsRunning);
_controller.notifyListeners();
_isApplyingRemoteSync = false;
}
@@ -362,6 +408,13 @@
_isApplyingRemoteSync = true;
_controller.useTimeout(isOpponent);
_isApplyingRemoteSync = false;
} else if (actionType == 'add_shot') {
final shot = actionData['shot'] as Map<String, dynamic>?;
if (shot != null) {
_isApplyingRemoteSync = true;
_controller.applyRemoteAddShot(Map<String, dynamic>.from(shot));
_isApplyingRemoteSync = false;
}
}
}
@@ -383,7 +436,7 @@
if (result != null) {
_sessionId = result['session_id']?.toString();
_shareCode = result['share_code']?.toString();
_setupSyncListener();
await _setupSyncListener();
setState(() {});
}
}
@@ -398,7 +451,7 @@
_sessionId = result['session_id']?.toString();
_shareCode = result['share_code']?.toString();
_sharedWithName = result['creator_name']?.toString() ?? '';
_setupSyncListener();
await _setupSyncListener();
setState(() {});
}
}