import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; import 'package:image/image.dart'; import 'package:injectable/injectable.dart'; import 'package:isar/isar.dart'; import 'package:mnemo_cards_backend/database/database.dart'; import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart' hide VoiceModel; import 'package:archive/archive.dart'; import 'package:mnemo_cards_backend/packs/card_dto_extension.dart'; import 'package:mnemo_cards_backend/packs/card_pack_model_extension.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import '../main.dart' as backend_main; import 'pack_dto_converter.dart'; import 'pack_manager_extensions.dart'; @lazySingleton class PackManager { final AppDatabase _db; final PackDtoConverter packDtoConverter; PackManager(this._db, this.packDtoConverter); Future> listPacksPreviews( UserModel? userModel, Map? params, ) async { // TODO: Full migration - replace CardPackModel with Drift CardPack // Need to update PackDtoConverter to work with Drift models // For now using isar temporarily until full migration final models = await backend_main.database.transaction(() async { // Temporary: using isar through backend_main until conversion complete return []; }); // TODO: Use _db.packDao.getAllPacks() and convert CardPack to CardPackModel // Or update packDtoConverter to accept CardPack models.sort((p, n) => p.order.compareTo(n.order)); return (await Future.wait(models.map( (model) async { final dto = await packDtoConverter.toCardPackPreviewDto(model, userModel); if (!model.enabled) { return dto.copyWith(subtitle: 'DISABLED ${dto.subtitle}'); } return dto; }, ))) .toList(); } Future> _getPacks() async { return await backend_main.isar.cardPackModels .filter() .enabledEqualTo(true) .findAll(); } Future getPack(Id id) async { return await backend_main.isar.cardPackModels.get(id); } Future> getCards(Id packId) async { return await backend_main.isar.gameCardModels .filter() .packIdEqualTo(packId) .findAll(); } Future getCard(Id id) async { return await backend_main.isar.gameCardModels.get(id); } Future getVoice(Id id) async { return await backend_main.isar.voiceModels.get(id); } Future> getVoices(Id cardId) async { return await backend_main.isar.voiceModels .filter() .cardIdEqualTo(cardId) .findAll(); } Future getPackDto(Id id, UserModel? userModel) async { final pack = await getPack(id); if (pack == null) { throw StateError('Pack not found'); } final cards = await getCards(id); final voices = []; for (final card in cards) { voices.addAll(await getVoices(card.id!)); } return await packDtoConverter.toCardPackDto(pack, cards, voices, userModel); } Future> getPackPreviewImages(Id packId) async { final pack = await getPack(packId); if (pack == null) { throw StateError('Pack not found'); } final cards = await getCards(packId); final previewCardIds = pack.previewCardsOrder.take(6); final previewCards = cards .where((card) => previewCardIds.contains(card.id)) .take(6) .toList(); final images = []; for (final card in previewCards) { if (card.image.isNotEmpty) { try { final image = await card.image.base64Image; images.add(image); } catch (e, s) { log('error while reading image', error: e, stackTrace: s); } } } // If we don't have enough preview images, fill with empty strings while (images.length < 6) { images.add(''); } return images; } Future> getPackImages(Id packId) async { final cards = await getCards(packId); final empty = {}; return cards.fold(empty, (Map map, card) { if (card.image.isNotEmpty) { try { return map ..addAll({ card.id.toString(): File('${PackManagerUtils.assetsDirectory.path}/cards/${card.image}') .readAsBytesSync(), }); } catch (e, s) { log('error while reading image', error: e, stackTrace: s); return map; } } return map; }); } } // Static utility functions for PackManager enum _ImageSize { big, medium, small, extraSmall, } class PackManagerUtils { static Directory get assetsDirectory { String mainPath = Platform.resolvedExecutable; if ((Platform.isMacOS || Platform.isLinux) && !Platform.script.toString().contains('StudioProjects')) { mainPath = mainPath.substring(0, mainPath.lastIndexOf("/")); var dir = Directory("$mainPath/../data"); if (dir.existsSync()) { return dir; } dir = Directory("$mainPath/data"); if (dir.existsSync()) { return dir; } throw Exception('No asset dir! $mainPath'); } if (Platform.isMacOS) { mainPath = mainPath.substring(0, mainPath.lastIndexOf("/")); return Directory( '/Users/dmitry/StudioProjects/mnemo_cards/mnemo_cards_backend/data'); } else if (Platform.isWindows) { mainPath = mainPath.substring(0, mainPath.lastIndexOf("\\")); return Directory("$mainPath/data/flutter_assets/data"); } else { return Directory(''); } } static File getResizedFile(String id, String type, _ImageSize size) => File('${assetsDirectory.path}/$type/${size.name}/$id'); }