mirror of
https://github.com/Dvorinka/1356.git
synced 2026-06-04 12:02:56 +00:00
37ffb93923
Version: 1.1.0 Major changes: - Implemented complete Flutter app structure with all core features - Added comprehensive UI screens for auth, countdown, goals, profile, settings, and social features - Integrated Supabase backend with authentication and data repositories - Added offline support with Hive caching and local storage - Implemented comprehensive routing with go_router - Added location services with Google Maps integration - Implemented notifications and home widget support - Added voice recording capabilities and AI chat features - Created comprehensive test suite and documentation - Added Android and iOS platform configurations - Implemented achievements system and social features - Added calendar integration and bucket list functionality This represents a complete Phase 1 milestone with 3,775 additions across 31 files.
162 lines
3.9 KiB
Dart
162 lines
3.9 KiB
Dart
// ignore_for_file: depend_on_referenced_packages
|
|
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:crypto/crypto.dart';
|
|
import 'dart:convert';
|
|
|
|
class ImageCacheService {
|
|
static const int _maxCacheSize = 50 * 1024 * 1024; // 50MB
|
|
static const Duration _cacheExpiry = Duration(days: 30);
|
|
static const int _maxConcurrentOperations = 3;
|
|
|
|
late Directory _cacheDir;
|
|
bool _initialized = false;
|
|
int _activeOperations = 0;
|
|
|
|
Future<void> init() async {
|
|
if (_initialized) return;
|
|
|
|
final appDir = await getApplicationDocumentsDirectory();
|
|
_cacheDir = Directory(path.join(appDir.path, 'image_cache'));
|
|
|
|
if (!await _cacheDir.exists()) {
|
|
await _cacheDir.create(recursive: true);
|
|
}
|
|
|
|
_initialized = true;
|
|
await _cleanupExpiredCache();
|
|
}
|
|
|
|
String _generateCacheKey(String url) {
|
|
final bytes = utf8.encode(url);
|
|
final digest = sha256.convert(bytes);
|
|
return digest.toString();
|
|
}
|
|
|
|
Future<File?> getCachedImage(String url) async {
|
|
if (!_initialized) await init();
|
|
|
|
final cacheKey = _generateCacheKey(url);
|
|
final cachedFile = File(path.join(_cacheDir.path, '$cacheKey.jpg'));
|
|
|
|
if (!await cachedFile.exists()) {
|
|
return null;
|
|
}
|
|
|
|
final stat = await cachedFile.stat();
|
|
final age = DateTime.now().difference(stat.modified);
|
|
|
|
if (age > _cacheExpiry) {
|
|
await cachedFile.delete();
|
|
return null;
|
|
}
|
|
|
|
return cachedFile;
|
|
}
|
|
|
|
Future<File> cacheImage(String url, Uint8List imageData) async {
|
|
if (!_initialized) await init();
|
|
|
|
// Limit concurrent operations to avoid overwhelming the system
|
|
while (_activeOperations >= _maxConcurrentOperations) {
|
|
await Future.delayed(const Duration(milliseconds: 10));
|
|
}
|
|
|
|
_activeOperations++;
|
|
|
|
try {
|
|
final cacheKey = _generateCacheKey(url);
|
|
final cachedFile = File(path.join(_cacheDir.path, '$cacheKey.jpg'));
|
|
|
|
await cachedFile.writeAsBytes(imageData);
|
|
|
|
await _enforceCacheSizeLimit();
|
|
|
|
return cachedFile;
|
|
} finally {
|
|
_activeOperations--;
|
|
}
|
|
}
|
|
|
|
Future<void> clearCache() async {
|
|
if (!_initialized) await init();
|
|
|
|
if (await _cacheDir.exists()) {
|
|
await _cacheDir.delete(recursive: true);
|
|
await _cacheDir.create(recursive: true);
|
|
}
|
|
}
|
|
|
|
Future<int> getCacheSize() async {
|
|
if (!_initialized) await init();
|
|
|
|
int totalSize = 0;
|
|
|
|
if (await _cacheDir.exists()) {
|
|
await for (final entity in _cacheDir.list()) {
|
|
if (entity is File) {
|
|
totalSize += await entity.length();
|
|
}
|
|
}
|
|
}
|
|
|
|
return totalSize;
|
|
}
|
|
|
|
Future<void> _cleanupExpiredCache() async {
|
|
if (!await _cacheDir.exists()) return;
|
|
|
|
final now = DateTime.now();
|
|
|
|
await for (final entity in _cacheDir.list()) {
|
|
if (entity is File) {
|
|
final stat = await entity.stat();
|
|
final age = now.difference(stat.modified);
|
|
|
|
if (age > _cacheExpiry) {
|
|
await entity.delete();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _enforceCacheSizeLimit() async {
|
|
final currentSize = await getCacheSize();
|
|
|
|
if (currentSize <= _maxCacheSize) return;
|
|
|
|
final files = <File>[];
|
|
final fileStats = <File, FileStat>{};
|
|
|
|
await for (final entity in _cacheDir.list()) {
|
|
if (entity is File) {
|
|
files.add(entity);
|
|
fileStats[entity] = await entity.stat();
|
|
}
|
|
}
|
|
|
|
files.sort((a, b) {
|
|
final statA = fileStats[a]!;
|
|
final statB = fileStats[b]!;
|
|
return statA.modified.compareTo(statB.modified);
|
|
});
|
|
|
|
int sizeToRemove = currentSize - _maxCacheSize;
|
|
|
|
for (final file in files) {
|
|
if (sizeToRemove <= 0) break;
|
|
|
|
final fileSize = await file.length();
|
|
await file.delete();
|
|
sizeToRemove -= fileSize;
|
|
}
|
|
}
|
|
|
|
Future<void> dispose() async {
|
|
_initialized = false;
|
|
}
|
|
}
|