import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../di/locator.dart'; // persist class PackCacheManager { late final SharedPreferences _sharedPreferences; PackCacheManager(); Future init() async { _sharedPreferences = await SharedPreferences.getInstance(); } Future loadPackFromCache( String packId, { bool withCardImages = true, }) async { log('Loading from cache ${packId}'); try { final packDir = await _packDirectory(packId); final packFile = File('${packDir.path}/pack.json'); if (!packFile.existsSync()) { log('Pack file not exist'); return throw Exception('Pack file not exist'); } var pack = CardPackDto.fromJson( jsonDecode(packFile.readAsStringSync()), ); if (withCardImages) { Map images = {}; log('Cards ${packId}: ${pack.cards.length}'); if (pack.cards.isNotEmpty) { log('Loading cards ${packId}'); for (final dto in pack.cards) { final file = File('${packDir.path}/images/${dto.id}'); if (file.existsSync()) { try { images[dto.id.toString()] = MemoryImage(await file.readAsBytes()); } catch (e) { throw Exception('Cant load pack $packId $e'); } } else { log('Cant load card ${dto.id} from $packId, skiping'); continue; } } } log('Pack loaded ${packId}'); locator.imagesHolder.setPackImages(images); } return pack; } catch (e, s) { log('Cant load from cache ${packId} ${e} ${s}'); deletePack(packId); rethrow; } } Future> _updateSavedPacks() async { final packsDir = Directory('${(await getApplicationDocumentsDirectory()).path}/packs'); List savedPackIds = []; if (packsDir.existsSync()) { for (final packDir in packsDir.listSync()) { if (File('${packDir.path}/pack.json').existsSync()) { savedPackIds.add(packDir.path.split('/').last); } } } _sharedPreferences.setStringList('packs', savedPackIds); return savedPackIds; } Future> loadAllPacks() async { final ids = await _updateSavedPacks(); final List packs = []; for (final id in ids) { try { final pack = await loadPackFromCache(id, withCardImages: false); packs.add(pack); } catch (e) { log('Pack $id doesnt exist anymore'); } } return packs; } Future deletePack(String packId) async { try { final dir = await _packDirectory(packId); dir.deleteSync(recursive: true); } catch (e) { log('Deletion failed $e'); } _updateSavedPacks(); } Future loadPackFile(String packId, String path) async { final bytes = File('${(await _packDirectory(packId)).path}/$path').readAsBytesSync(); return bytes; } Future clearCache() async { final saved = await _updateSavedPacks(); for (final p in saved) { await deletePack(p); } await _sharedPreferences.remove('packs'); } Future savePack( CardPackDto dto, { Map? images, }) async { log('Saving pack ${dto.id}'); final dir = await _packDirectory(dto.id); final json = jsonEncode(dto); log('Saving dto ${dto.id}'); File('${dir.path}/pack.json') ..createSync(recursive: true) ..writeAsStringSync(json); if (images != null) { log('Saving card images ${dto.id}'); final imageDir = Directory('${dir.path}/images'); if (imageDir.existsSync()) { imageDir.deleteSync(recursive: true); } for (final card in dto.cards) { final bytes = images[card.id.toString()]?.bytes; if (bytes != null) { File('${imageDir.path}/${card.id}') ..createSync(recursive: true) ..writeAsBytesSync(bytes); } } } log('Saved ${dto.id}'); } Future _packDirectory(String id) async => Directory( '${(await getApplicationDocumentsDirectory()).path}/packs/$id', ); }