import 'dart:async'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:get/get.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'app_database.dart'; enum SyncStatus { idle, syncing, error } class SyncService extends GetxService { static SyncService get to => Get.find(); final syncStatus = SyncStatus.idle.obs; final pendingCount = 0.obs; StreamSubscription? _connectivitySub; final _supabase = Supabase.instance.client; // AppDatabase — nincs külön DB fájl AppDatabase get _db => AppDatabase.instance; @override Future onInit() async { super.onInit(); await _updatePendingCount(); _startConnectivityListener(); if (await _hasConnectivity()) { unawaited(_syncPendingItems()); } } @override Future onClose() async { await _connectivitySub?.cancel(); super.onClose(); } // ── Publikus API ────────────────────────────────────────────────── Future saveMeasuredPoint(Map point) async { final projectId = point['projectId'] as int?; final isLocalOnly = await _isLocalOnlyProject(projectId); final localId = await _db.insertPendingPoint(point); if (isLocalOnly) { await _db.markPointSynced(localId); return; } await _updatePendingCount(); if (await _hasConnectivity()) { await _syncSinglePoint(localId, point); } } // ── Szinkronizálás ──────────────────────────────────────────────── Future _syncSinglePoint(int localId, Map point) async { try { await _supabase.from('TerepiSeged_MeasuredPoints').insert(point); await _supabase .from('TerepiSeged_Receiver') .update({'isMeasured': true}).eq('pointNumber', point['pointNumber']); await _db.markPointSynced(localId); await _updatePendingCount(); } catch (_) { await _db.markPointError(localId); } } Future _syncPendingItems() async { if (syncStatus.value == SyncStatus.syncing) return; final pending = await _db.getPendingPoints(); if (pending.isEmpty) return; syncStatus.value = SyncStatus.syncing; for (final row in pending) { final localId = row['id'] as int; try { final supaPoint = { 'pointNumber': row['point_number'], 'gnssNumber': row['gnss_number'], 'latitude': row['latitude'], 'longitude': row['longitude'], 'altitude': row['altitude'], 'heightOfGeoid': row['height_of_geoid'], 'eovX': row['eov_x'], 'eovY': row['eov_y'], 'poleHeight': row['pole_height'], 'horizontalError': row['horizontal_error'], 'verticalError': row['vertical_error'], 'description': row['description'], 'isDeleted': row['is_deleted'] == 1, 'projectId': row['project_id'], }; await _supabase.from('TerepiSeged_MeasuredPoints').insert(supaPoint); await _supabase.from('TerepiSeged_Receiver').update( {'isMeasured': true}).eq('pointNumber', row['point_number']); await _db.markPointSynced(localId); } catch (_) { // Következő alkalomra halaszt } } await _updatePendingCount(); syncStatus.value = SyncStatus.idle; await _db.purgeSyncedPoints(); } // ── Segédek ─────────────────────────────────────────────────────── Future _updatePendingCount() async { pendingCount.value = await _db.getPendingCount(); } void _startConnectivityListener() { _connectivitySub = Connectivity() .onConnectivityChanged .listen((List results) async { final hasNet = results.any((r) => r != ConnectivityResult.none); if (hasNet && pendingCount.value > 0) { await _syncPendingItems(); } }); } Future _hasConnectivity() async { final results = await Connectivity().checkConnectivity(); return results.any((r) => r != ConnectivityResult.none); } Future _isLocalOnlyProject(int? projectId) async { if (projectId == null) return false; final db = await AppDatabase.instance.database; final rows = await db.query( 'projects', columns: ['is_local_only'], where: 'id = ?', whereArgs: [projectId], limit: 1, ); if (rows.isEmpty) return false; return (rows.first['is_local_only'] as int?) == 1; } }