MobilApp/lib/services/sync_service.dart

156 lines
4.7 KiB
Dart
Raw Permalink Normal View History

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<void> onInit() async {
super.onInit();
await _updatePendingCount();
_startConnectivityListener();
if (await _hasConnectivity()) {
unawaited(_syncPendingItems());
}
}
@override
Future<void> onClose() async {
await _connectivitySub?.cancel();
super.onClose();
}
// ── Publikus API ──────────────────────────────────────────────────
Future<void> saveMeasuredPoint(Map<String, dynamic> 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<void> _syncSinglePoint(int localId, Map<String, dynamic> 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<void> _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<void> _updatePendingCount() async {
pendingCount.value = await _db.getPendingCount();
}
void _startConnectivityListener() {
_connectivitySub = Connectivity()
.onConnectivityChanged
.listen((List<ConnectivityResult> results) async {
final hasNet = results.any((r) => r != ConnectivityResult.none);
if (hasNet && pendingCount.value > 0) {
await _syncPendingItems();
}
});
}
Future<bool> _hasConnectivity() async {
final results = await Connectivity().checkConnectivity();
return results.any((r) => r != ConnectivityResult.none);
}
Future<bool> _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;
}
}