156 lines
4.7 KiB
Dart
156 lines
4.7 KiB
Dart
|
|
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;
|
||
|
|
}
|
||
|
|
}
|