image_service.dart (3814B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:path_provider/path_provider.dart'; import 'package:uuid/uuid.dart'; import 'package:creekui/data/repos/image_repo.dart'; import 'package:creekui/data/models/image_model.dart'; import 'analysis_queue_manager.dart'; class ImageService { final ImageRepo _repo = ImageRepo(); final Uuid _uuid = const Uuid(); // Checks if image is a draft (from ShareHandler) and updates it, or saves new Future<String> saveOrUpdateImage( File file, int projectId, { List<String> tags = const [], }) async { final existing = await _repo.getByFilePath(file.path); if (existing != null) { debugPrint( "ImageService: Updating existing draft ${existing.id} -> Project $projectId", ); await _repo.updateProject(existing.id, projectId); await updateTags(existing.id, tags); return existing.id; } else { debugPrint("ImageService: Saving new image"); return await saveImage(file, projectId, tags: tags); } } // --- PUBLIC METHODS --- // Saves a single image and returns its ID Future<String> saveImage( File file, int projectId, { List<String> tags = const [], String? status, }) async { // 1. Prepare Directory final dir = await getApplicationDocumentsDirectory(); final folder = Directory("${dir.path}/images"); if (!await folder.exists()) await folder.create(recursive: true); // 2. Generate ID and Path final String id = _uuid.v4(); String ext = "jpg"; // Default try { if (file.path.contains('.')) { ext = file.path.split('.').last; } } catch (_) {} final String newPath = "${folder.path}/$id.$ext"; await file.copy(newPath); // 3. Create & Save Model final image = ImageModel( id: id, projectId: projectId, filePath: newPath, name: file.path.split('/').last, createdAt: DateTime.now(), tags: tags, status: status ?? 'pending', ); await _repo.addImage(image); // 4. Trigger Analysis Queue AnalysisQueueManager().processQueue(); return id; } // Bulk save method Future<List<String>> saveImages( List<File> files, int projectId, { List<String> tags = const [], }) async { List<String> ids = []; for (var file in files) { String id = await saveImage(file, projectId, tags: tags); ids.add(id); } return ids; } // Updates tags and triggers relevant analysis Future<void> updateTags(String imageId, List<String> newTags) async { // 1. Update Tags in Database await _repo.updateTags(imageId, newTags); // 2. Mark as pending and trigger queue await _repo.updateStatus(imageId, 'pending'); AnalysisQueueManager().processQueue(); } Future<void> renameImage(String id, String newName) async { await _repo.updateName(id, newName); } Future<void> updateAnalysis(String id, Map<String, dynamic> analysis) async { final jsonString = jsonEncode(analysis); await _repo.updateAnalysis(id, jsonString); } Future<ImageModel?> getImage(String id) async { return await _repo.getById(id); } Future<List<String>> getTags(String imageId) async { return await _repo.getTagsForImage(imageId); } Future<void> deleteImage(String id) async { final img = await _repo.getById(id); if (img != null) { final file = File(img.filePath); if (await file.exists()) { try { await file.delete(); } catch (e) { debugPrint("Error deleting file: $e"); } } await _repo.deleteImage(id); } } Future<List<ImageModel>> getImages(int projectId) async { return await _repo.getImages(projectId); } } |