import 'package:flutter/material.dart'; import 'package:puzzleweave/models/api_helper.dart'; import 'package:puzzleweave/models/cached_request.dart'; import 'package:puzzleweave/models/items.dart'; import 'package:puzzleweave/persistence/persistence.dart'; import 'package:logging/logging.dart'; import 'package:puzzleweave/utils/utils.dart'; final Logger _log = Logger('data.dart'); class Data { final Persistence _persistence; ValueNotifier> completedWorks = ValueNotifier([]); // 已完成的图 ValueNotifier> completedCollections = ValueNotifier([]); // 已完成的收藏图 Data({required Persistence persistence}) : _persistence = persistence; Future loadDataFromPersistence() async { // for test 为了测试合集完成动画 // var works = _persistence.completedWorks; // works = works.sublist(0, works.length - 1); // _persistence.completedWorks = works; // _persistence.completedCollections = []; // 1. 先初始化内置索引(独立、确定、不依赖缓存判断) await _initBuiltinRegistry(); completedWorks.value = _persistence.completedWorks; completedCollections.value = _persistence.completedCollections; } /// 独立初始化内置图索引,确保无论网络/磁盘缓存如何,Asset 里的图都能被识别 Future _initBuiltinRegistry() async { try { _log.info('Initializing BuiltinRegistry from Asset...'); // 直接读取资源文件,不走 CachedRequest 逻辑 final latestData = await loadJSONFromAsset('assets/builtin/latest.json'); if (latestData['data'] != null) { BuiltinRegistry.init(latestData['data']); _log.info('BuiltinRegistry initialized with ${latestData['total']} items.'); } // 如果 collection.json 也有内置 ID 需要保护,也可以在这里 init // final collectionData = await loadJSONFromAsset('assets/builtin/collection.json'); // BuiltinRegistry.init(collectionData['data']); } catch (e) { _log.severe('Failed to initialize BuiltinRegistry: $e'); } } // 完成某个作品调用此接口记录到存储中 // !!! 改造点:接受 ListItem 和可选的耗时 void workDone(ListItem item, {Duration? timeSpent}) { final newWork = Work.fromListItem(item, timeSpent: timeSpent); // 1. 记录作品完成 final updatedWorks = [...completedWorks.value, newWork]; completedWorks.value = updatedWorks; _persistence.completedWorks = updatedWorks; // 存储更新后的列表 // 2. 核心新增:清除已完成作品的缓存 _clearCompletedItemCache(item); } /// 清除已完成作品的缓存文件。 void _clearCompletedItemCache(ListItem item) async { // 删除进度状态json try { final file = await localFile(item.jsonPath); if (await file.exists()) { await file.delete(); _log.info('Successfully cleared json cache file for ${item.id}'); } else { _log.warning('json cache file not found for ${item.id} at path: ${item.jsonPath}'); } } catch (e) { _log.severe('Failed to clear json cache for item ${item.id}, error: $e'); } // 只有 RemoteItem 才会有需要清理的下载缓存 if (item is RemoteItem) { _log.info('Work done for remote item ${item.id}. Attempting to clear cache at path: ${item.cachePath}'); try { final file = await localFile(item.cachePath); if (await file.exists()) { await file.delete(); _log.info('Successfully cleared cache file for ${item.id}'); } else { _log.warning('Cache file not found for ${item.id} at path: ${item.cachePath}'); } final tmpfile = await localFile('${item.cachePath}.tmp'); if (await tmpfile.exists()) { await tmpfile.delete(); _log.info('Successfully cleared cache tmp file for ${item.id}'); } } catch (e) { _log.severe('Failed to clear cache for item ${item.id}, error: $e'); } } else { _log.info('Work done for asset item ${item.id}. No network cache to clear.'); } } // 获取当前的关卡index (基于已完成作品的数量) int get currentLevel => completedWorks.value.length; // 完成某个collection void collectionDone(ListItem item) { final newCollection = Work.fromListItem(item); final updatedCollections = [...completedCollections.value, newCollection]; completedCollections.value = updatedCollections; _persistence.completedCollections = updatedCollections; // 存储更新后的列表 } // 获取当前的合集index (基于已完成收藏的数量) int get currentCollectionIndex => completedCollections.value.length; CachedRequest? _latest; CachedRequest get latest { _latest ??= CachedRequest.fromUrl( ApiHelper.latestUri, transformFunction: (json) async { late List list; if (json['asset'] != null) { // from asset list = List.from((json['data'] as Iterable).map((e) => AssetItem.fromJSON(e))); } else { // from remote list = List.from((json['data'] as Iterable).map((e) => RemoteItem.fromJSON(e))); } return list; }, ); return _latest!; } CachedRequest? _collection; CachedRequest get collection { _collection ??= CachedRequest.fromUrl( ApiHelper.collectionUri, transformFunction: (json) async { late List list; if (json['asset'] != null) { // from asset list = List.from((json['data'] as Iterable).map((e) => AssetItem.fromJSON(e))); } else { // from remote list = List.from((json['data'] as Iterable).map((e) => RemoteItem.fromJSON(e))); } return list; }, ); return _collection!; } } // 内置图索引 class BuiltinRegistry { static final Set _builtinIds = {}; // 在 App 启动(或 CachedRequest 加载内置数据时)调用 static void init(List data) { _builtinIds.clear(); for (var item in data) { if (item['_id'] != null) { _builtinIds.add(item['_id']); } } } static bool contains(String id) => _builtinIds.contains(id); }