Versione OK Pour l'onglet événements.
This commit is contained in:
249
unionflow-mobile-apps/lib/core/services/cache_service.dart
Normal file
249
unionflow-mobile-apps/lib/core/services/cache_service.dart
Normal file
@@ -0,0 +1,249 @@
|
||||
import 'dart:convert';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/cotisation_model.dart';
|
||||
import '../models/cotisation_statistics_model.dart';
|
||||
import '../models/payment_model.dart';
|
||||
|
||||
/// Service de gestion du cache local
|
||||
/// Permet de stocker et récupérer des données en mode hors-ligne
|
||||
@LazySingleton()
|
||||
class CacheService {
|
||||
static const String _cotisationsCacheKey = 'cotisations_cache';
|
||||
static const String _cotisationsStatsCacheKey = 'cotisations_stats_cache';
|
||||
static const String _paymentsCacheKey = 'payments_cache';
|
||||
static const String _lastSyncKey = 'last_sync_timestamp';
|
||||
static const Duration _cacheValidityDuration = Duration(minutes: 30);
|
||||
|
||||
final SharedPreferences _prefs;
|
||||
|
||||
CacheService(this._prefs);
|
||||
|
||||
/// Sauvegarde une liste de cotisations dans le cache
|
||||
Future<void> saveCotisations(List<CotisationModel> cotisations, {String? key}) async {
|
||||
final cacheKey = key ?? _cotisationsCacheKey;
|
||||
final jsonList = cotisations.map((c) => c.toJson()).toList();
|
||||
final jsonString = jsonEncode({
|
||||
'data': jsonList,
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await _prefs.setString(cacheKey, jsonString);
|
||||
}
|
||||
|
||||
/// Récupère une liste de cotisations depuis le cache
|
||||
Future<List<CotisationModel>?> getCotisations({String? key}) async {
|
||||
final cacheKey = key ?? _cotisationsCacheKey;
|
||||
final jsonString = _prefs.getString(cacheKey);
|
||||
|
||||
if (jsonString == null) return null;
|
||||
|
||||
try {
|
||||
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
|
||||
|
||||
// Vérifier si le cache est encore valide
|
||||
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
|
||||
await clearCotisations(key: key);
|
||||
return null;
|
||||
}
|
||||
|
||||
final jsonList = jsonData['data'] as List<dynamic>;
|
||||
return jsonList.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>)).toList();
|
||||
} catch (e) {
|
||||
// En cas d'erreur, nettoyer le cache corrompu
|
||||
await clearCotisations(key: key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sauvegarde les statistiques des cotisations
|
||||
Future<void> saveCotisationsStats(CotisationStatisticsModel stats) async {
|
||||
final jsonString = jsonEncode({
|
||||
'data': stats.toJson(),
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await _prefs.setString(_cotisationsStatsCacheKey, jsonString);
|
||||
}
|
||||
|
||||
/// Récupère les statistiques des cotisations depuis le cache
|
||||
Future<CotisationStatisticsModel?> getCotisationsStats() async {
|
||||
final jsonString = _prefs.getString(_cotisationsStatsCacheKey);
|
||||
|
||||
if (jsonString == null) return null;
|
||||
|
||||
try {
|
||||
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
|
||||
|
||||
// Vérifier si le cache est encore valide
|
||||
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
|
||||
await clearCotisationsStats();
|
||||
return null;
|
||||
}
|
||||
|
||||
return CotisationStatisticsModel.fromJson(jsonData['data'] as Map<String, dynamic>);
|
||||
} catch (e) {
|
||||
await clearCotisationsStats();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sauvegarde une liste de paiements dans le cache
|
||||
Future<void> savePayments(List<PaymentModel> payments) async {
|
||||
final jsonList = payments.map((p) => p.toJson()).toList();
|
||||
final jsonString = jsonEncode({
|
||||
'data': jsonList,
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await _prefs.setString(_paymentsCacheKey, jsonString);
|
||||
}
|
||||
|
||||
/// Récupère une liste de paiements depuis le cache
|
||||
Future<List<PaymentModel>?> getPayments() async {
|
||||
final jsonString = _prefs.getString(_paymentsCacheKey);
|
||||
|
||||
if (jsonString == null) return null;
|
||||
|
||||
try {
|
||||
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
|
||||
|
||||
// Vérifier si le cache est encore valide
|
||||
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
|
||||
await clearPayments();
|
||||
return null;
|
||||
}
|
||||
|
||||
final jsonList = jsonData['data'] as List<dynamic>;
|
||||
return jsonList.map((json) => PaymentModel.fromJson(json as Map<String, dynamic>)).toList();
|
||||
} catch (e) {
|
||||
await clearPayments();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sauvegarde une cotisation individuelle dans le cache
|
||||
Future<void> saveCotisation(CotisationModel cotisation) async {
|
||||
final key = 'cotisation_${cotisation.id}';
|
||||
final jsonString = jsonEncode({
|
||||
'data': cotisation.toJson(),
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await _prefs.setString(key, jsonString);
|
||||
}
|
||||
|
||||
/// Récupère une cotisation individuelle depuis le cache
|
||||
Future<CotisationModel?> getCotisation(String id) async {
|
||||
final key = 'cotisation_$id';
|
||||
final jsonString = _prefs.getString(key);
|
||||
|
||||
if (jsonString == null) return null;
|
||||
|
||||
try {
|
||||
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
|
||||
|
||||
// Vérifier si le cache est encore valide
|
||||
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
|
||||
await clearCotisation(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
return CotisationModel.fromJson(jsonData['data'] as Map<String, dynamic>);
|
||||
} catch (e) {
|
||||
await clearCotisation(id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour le timestamp de la dernière synchronisation
|
||||
Future<void> updateLastSyncTimestamp() async {
|
||||
await _prefs.setInt(_lastSyncKey, DateTime.now().millisecondsSinceEpoch);
|
||||
}
|
||||
|
||||
/// Récupère le timestamp de la dernière synchronisation
|
||||
DateTime? getLastSyncTimestamp() {
|
||||
final timestamp = _prefs.getInt(_lastSyncKey);
|
||||
return timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp) : null;
|
||||
}
|
||||
|
||||
/// Vérifie si une synchronisation est nécessaire
|
||||
bool needsSync() {
|
||||
final lastSync = getLastSyncTimestamp();
|
||||
if (lastSync == null) return true;
|
||||
|
||||
return DateTime.now().difference(lastSync) > const Duration(minutes: 15);
|
||||
}
|
||||
|
||||
/// Nettoie le cache des cotisations
|
||||
Future<void> clearCotisations({String? key}) async {
|
||||
final cacheKey = key ?? _cotisationsCacheKey;
|
||||
await _prefs.remove(cacheKey);
|
||||
}
|
||||
|
||||
/// Nettoie le cache des statistiques
|
||||
Future<void> clearCotisationsStats() async {
|
||||
await _prefs.remove(_cotisationsStatsCacheKey);
|
||||
}
|
||||
|
||||
/// Nettoie le cache des paiements
|
||||
Future<void> clearPayments() async {
|
||||
await _prefs.remove(_paymentsCacheKey);
|
||||
}
|
||||
|
||||
/// Nettoie une cotisation individuelle du cache
|
||||
Future<void> clearCotisation(String id) async {
|
||||
final key = 'cotisation_$id';
|
||||
await _prefs.remove(key);
|
||||
}
|
||||
|
||||
/// Nettoie tout le cache des cotisations
|
||||
Future<void> clearAllCotisationsCache() async {
|
||||
final keys = _prefs.getKeys().where((key) =>
|
||||
key.startsWith('cotisation') ||
|
||||
key == _cotisationsStatsCacheKey ||
|
||||
key == _paymentsCacheKey
|
||||
).toList();
|
||||
|
||||
for (final key in keys) {
|
||||
await _prefs.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Retourne la taille du cache en octets (approximation)
|
||||
int getCacheSize() {
|
||||
int totalSize = 0;
|
||||
final keys = _prefs.getKeys().where((key) =>
|
||||
key.startsWith('cotisation') ||
|
||||
key == _cotisationsStatsCacheKey ||
|
||||
key == _paymentsCacheKey
|
||||
);
|
||||
|
||||
for (final key in keys) {
|
||||
final value = _prefs.getString(key);
|
||||
if (value != null) {
|
||||
totalSize += value.length * 2; // Approximation UTF-16
|
||||
}
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
/// Retourne des informations sur le cache
|
||||
Map<String, dynamic> getCacheInfo() {
|
||||
final lastSync = getLastSyncTimestamp();
|
||||
return {
|
||||
'lastSync': lastSync?.toIso8601String(),
|
||||
'needsSync': needsSync(),
|
||||
'cacheSize': getCacheSize(),
|
||||
'cacheSizeFormatted': _formatBytes(getCacheSize()),
|
||||
};
|
||||
}
|
||||
|
||||
/// Formate la taille en octets en format lisible
|
||||
String _formatBytes(int bytes) {
|
||||
if (bytes < 1024) return '$bytes B';
|
||||
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
||||
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user