feat: add alot

This commit is contained in:
2026-10-08 04:12:36 +02:00
parent ccf5165417
commit fc607cb7bd
99 changed files with 4981 additions and 3655 deletions
+43 -107
View File
@@ -1,13 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import '../database/app_database.dart';
import '../models/remote_command.dart';
import '../services/remote_sync_service.dart';
import '../utils/app_config.dart';
class RemoteSyncViewModel extends ChangeNotifier {
final RemoteSyncService syncService;
@@ -16,11 +13,10 @@ class RemoteSyncViewModel extends ChangeNotifier {
_eventSubscription = syncService.events.listen(_onEvent);
}
StreamSubscription<RemoteSyncEvent>? _eventSubscription;
late final StreamSubscription<RemoteSyncEvent> _eventSubscription;
List<RemoteSyncEvent> _recentEvents = [];
List<RemoteSyncEvent> get recentEvents => _recentEvents;
bool _hasLoaded = false;
bool get hasLoaded => _hasLoaded;
bool _isSyncing = false;
bool get isSyncing => _isSyncing;
@@ -40,114 +36,56 @@ class RemoteSyncViewModel extends ChangeNotifier {
List<ProcessedRemoteCommandRow> _activity = [];
List<ProcessedRemoteCommandRow> get activity => _activity;
bool _isRegistering = false;
bool get isRegistering => _isRegistering;
String? _registrationError;
String? get registrationError => _registrationError;
Stream<RemoteSyncEvent> get tabsClosedRemotely => syncService.events.where(
(event) => event.type == RemoteSyncEventType.tabClosedRemotely,
);
Future<void> init() async {
_isConfigured = await syncService.isConfigured;
_venueId = await syncService.venueId;
await _refreshStatus();
_lastSyncAt = await syncService.lastSuccessfulSyncAt;
await loadActivity();
if (_isConfigured) {
syncService.startPeriodicSync();
}
await _loadActivity();
if (_isConfigured) syncService.startPeriodicSync();
_hasLoaded = true;
notifyListeners();
}
Future<void> refreshStatus() async {
/// Called once registration stored new credentials.
Future<void> activate() async {
await _refreshStatus();
notifyListeners();
if (!_isConfigured) return;
syncService.startPeriodicSync();
await syncNow();
}
Future<void> _refreshStatus() async {
_isConfigured = await syncService.isConfigured;
_venueId = await syncService.venueId;
notifyListeners();
}
Future<void> syncNow() async {
_isSyncing = true;
_lastError = null;
notifyListeners();
await syncService.sync();
_isSyncing = syncService.isSyncing;
_lastSyncAt = await syncService.lastSuccessfulSyncAt;
await loadActivity();
_isSyncing = false;
await _loadActivity();
notifyListeners();
}
Future<void> loadActivity() async {
Future<void> _loadActivity() async {
try {
_activity = await syncService.getRecentActivity();
} catch (e) {
} on Exception catch (e) {
debugPrint('RemoteSyncViewModel: loadActivity error: $e');
}
}
Future<bool> registerDevice({
required String venueId,
required String deviceName,
required String registrationCode,
}) async {
_isRegistering = true;
_registrationError = null;
notifyListeners();
try {
final response = await http.post(
Uri.parse('$kRemoteServerUrl/devices/register'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'venueId': venueId,
'name': deviceName,
'registrationCode': registrationCode,
}),
).timeout(const Duration(seconds: 15));
if (response.statusCode == 201) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
final deviceId = data['deviceId'] as String;
final token = data['token'] as String;
await syncService.setCredentials(
deviceId: deviceId,
token: token,
venueId: venueId,
serverUrl: kRemoteServerUrl,
);
_isConfigured = true;
_venueId = venueId;
_isRegistering = false;
notifyListeners();
syncService.startPeriodicSync();
await syncNow();
return true;
} else {
final body = response.body;
String message;
try {
final errorData = jsonDecode(body) as Map<String, dynamic>;
message = errorData['message'] as String? ?? 'Registration failed';
} catch (_) {
message = 'Registration failed (HTTP ${response.statusCode})';
}
_registrationError = message;
_isRegistering = false;
notifyListeners();
return false;
}
} catch (e) {
debugPrint('RemoteSyncViewModel: registration error: $e');
_registrationError = e.toString();
_isRegistering = false;
notifyListeners();
return false;
}
}
Future<void> unlink() async {
syncService.stopPeriodicSync();
await syncService.clearCredentials();
@@ -155,24 +93,23 @@ class RemoteSyncViewModel extends ChangeNotifier {
_venueId = null;
_lastSyncAt = null;
_lastError = null;
_recentEvents = [];
_activity = [];
notifyListeners();
}
void startSync() {
syncService.startPeriodicSync();
syncNow();
}
void _onEvent(RemoteSyncEvent event) {
_recentEvents = [event, ..._recentEvents.take(49)];
_lastSyncAt = DateTime.now();
if (event.type == RemoteSyncEventType.syncFailed) {
_lastError = event.message;
} else if (event.type == RemoteSyncEventType.syncCompleted) {
_lastError = null;
switch (event.type) {
case RemoteSyncEventType.syncCompleted:
_lastSyncAt = DateTime.now();
_lastError = null;
case RemoteSyncEventType.syncFailed:
_lastError = event.message;
case RemoteSyncEventType.unauthorized:
// The router sends the device back to registration.
_isConfigured = false;
case RemoteSyncEventType.tabClosedRemotely:
case RemoteSyncEventType.conflict:
break;
}
notifyListeners();
@@ -184,13 +121,12 @@ class RemoteSyncViewModel extends ChangeNotifier {
void resume() {
syncService.resume();
syncNow();
unawaited(syncNow());
}
@override
void dispose() {
_eventSubscription?.cancel();
syncService.dispose();
_eventSubscription.cancel();
super.dispose();
}
}
}