Files
unionflow-mobile-apps/lib/core/network/offline_manager.dart
dahoud d094d6db9c Initial commit: unionflow-mobile-apps
Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
2026-03-15 16:30:08 +00:00

170 lines
5.1 KiB
Dart

/// Offline-first manager for connectivity monitoring and operation queueing
library offline_manager;
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
import '../storage/pending_operations_store.dart';
import '../utils/logger.dart' show AppLogger;
/// Status of network connectivity
enum ConnectivityStatus {
online,
offline,
unknown,
}
/// Offline manager that monitors connectivity and manages offline operations
@singleton
class OfflineManager {
final Connectivity _connectivity;
final PendingOperationsStore _operationsStore;
ConnectivityStatus _currentStatus = ConnectivityStatus.unknown;
final _statusController = StreamController<ConnectivityStatus>.broadcast();
StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;
OfflineManager(
this._connectivity,
this._operationsStore,
) {
_initConnectivityMonitoring();
}
/// Current connectivity status
ConnectivityStatus get currentStatus => _currentStatus;
/// Stream of connectivity status changes
Stream<ConnectivityStatus> get statusStream => _statusController.stream;
/// Check if device is currently online
Future<bool> get isOnline async {
final result = await _connectivity.checkConnectivity();
return result.any((r) => r != ConnectivityResult.none);
}
/// Initialize connectivity monitoring
void _initConnectivityMonitoring() {
// Check initial connectivity
_connectivity.checkConnectivity().then((result) {
_updateStatus(result);
});
// Listen for connectivity changes
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(
_updateStatus,
onError: (error) {
AppLogger.error('Connectivity monitoring error', error: error);
_updateStatus([ConnectivityResult.none]);
},
);
}
/// Update connectivity status
void _updateStatus(List<ConnectivityResult> results) {
final isConnected = results.any((r) => r != ConnectivityResult.none);
final newStatus = isConnected
? ConnectivityStatus.online
: ConnectivityStatus.offline;
if (newStatus != _currentStatus) {
final previousStatus = _currentStatus;
_currentStatus = newStatus;
_statusController.add(newStatus);
AppLogger.info('Connectivity changed: $previousStatus$newStatus');
// When back online, process pending operations
if (newStatus == ConnectivityStatus.online &&
previousStatus == ConnectivityStatus.offline) {
_processPendingOperations();
}
}
}
/// Queue an operation for later retry when offline
Future<void> queueOperation({
required String operationType,
required String endpoint,
required Map<String, dynamic> data,
Map<String, String>? headers,
}) async {
try {
await _operationsStore.addPendingOperation(
operationType: operationType,
endpoint: endpoint,
data: data,
headers: headers,
);
AppLogger.info('Operation queued: $operationType on $endpoint');
} catch (e) {
AppLogger.error('Failed to queue operation', error: e);
}
}
/// Process all pending operations when back online
Future<void> _processPendingOperations() async {
AppLogger.info('Processing pending operations...');
try {
final operations = await _operationsStore.getPendingOperations();
if (operations.isEmpty) {
AppLogger.info('No pending operations to process');
return;
}
AppLogger.info('Found ${operations.length} pending operations');
// Process operations one by one
for (final operation in operations) {
try {
// Note: Actual retry logic is delegated to the calling code
// This manager only provides the queuing mechanism
AppLogger.info('Pending operation ready for retry: ${operation['operationType']}');
} catch (e) {
AppLogger.error('Error processing pending operation', error: e);
}
}
} catch (e) {
AppLogger.error('Failed to process pending operations', error: e);
}
}
/// Manually trigger processing of pending operations
Future<void> retryPendingOperations() async {
if (_currentStatus == ConnectivityStatus.online) {
await _processPendingOperations();
} else {
AppLogger.warning('Cannot retry pending operations while offline');
}
}
/// Clear all pending operations
Future<void> clearPendingOperations() async {
try {
await _operationsStore.clearAll();
AppLogger.info('Pending operations cleared');
} catch (e) {
AppLogger.error('Failed to clear pending operations', error: e);
}
}
/// Get count of pending operations
Future<int> getPendingOperationsCount() async {
try {
final operations = await _operationsStore.getPendingOperations();
return operations.length;
} catch (e) {
AppLogger.error('Failed to get pending operations count', error: e);
return 0;
}
}
/// Dispose resources
void dispose() {
_connectivitySubscription?.cancel();
_statusController.close();
}
}