creek

The AI Image Editor of 2030

lib/services/file_service.dart (5047B)


  1import 'dart:io';
  2import 'dart:convert';
  3import 'package:path_provider/path_provider.dart';
  4import 'package:uuid/uuid.dart';
  5import 'package:image/image.dart' as img;
  6import 'package:creekui/data/repos/file_repo.dart';
  7import 'package:creekui/data/models/file_model.dart';
  8
  9class FileService {
 10  final _repo = FileRepo();
 11
 12  Future<String> saveFile(
 13    File file,
 14    int projectId, {
 15    String? description,
 16    String? name,
 17  }) async {
 18    final dir = await getApplicationDocumentsDirectory();
 19    final folder = Directory("${dir.path}/files");
 20    if (!await folder.exists()) await folder.create(recursive: true);
 21
 22    final id = const Uuid().v4();
 23    String ext = file.path.split('.').last;
 24    final newPath = "${folder.path}/$id.$ext";
 25
 26    await file.copy(newPath);
 27
 28    final projectFile = FileModel(
 29      id: id,
 30      projectId: projectId,
 31      filePath: newPath,
 32      name: name ?? "Untitled File",
 33      description: description,
 34      lastUpdated: DateTime.now(),
 35      createdAt: DateTime.now(),
 36    );
 37
 38    await _repo.addFile(projectFile);
 39    return id;
 40  }
 41
 42  Future<List<FileModel>> getAllFiles() async {
 43    return await _repo.getAllFiles();
 44  }
 45
 46  // Get recent files globally (limit 10 for the UI)
 47  Future<List<FileModel>> getRecentFiles({int limit = 10}) async {
 48    return await _repo.getRecentFiles(limit: limit);
 49  }
 50
 51  @Deprecated('Use getAllFiles() or getRecentFiles() instead')
 52  Future<List<FileModel>> getFiles(int projectId) async {
 53    return await _repo.getFiles(projectId);
 54  }
 55
 56  Future<List<FileModel>> getFilesForProjectAndEvents(int projectId) {
 57    return _repo.getFilesForProjectAndEvents(projectId);
 58  }
 59
 60  Future<void> openFile(String id) async {
 61    await _repo.touchFile(id);
 62  }
 63
 64  Future<void> updateFileDetails(
 65    String id, {
 66    String? name,
 67    String? description,
 68    List<String>? tags,
 69  }) async {
 70    await _repo.updateDetails(
 71      id,
 72      name: name,
 73      description: description,
 74      tags: tags,
 75    );
 76  }
 77
 78  Future<void> deleteFile(String id) async {
 79    final fileData = await _repo.getById(id);
 80    if (fileData != null) {
 81      final file = File(fileData.filePath);
 82      if (await file.exists()) await file.delete();
 83      await _repo.deleteFile(id);
 84    }
 85  }
 86
 87  Future<void> renameFile(String id, String newName) async {
 88    await _repo.updateDetails(id, name: newName);
 89  }
 90
 91  // Saves canvas JSON structure to a file
 92  Future<String> saveCanvasFile({
 93    required int projectId,
 94    required Map<String, dynamic> canvasData,
 95    String? fileId,
 96    String? fileName,
 97  }) async {
 98    final jsonString = jsonEncode(canvasData);
 99
100    if (fileId != null) {
101      // Update existing file
102      final fileData = await _repo.getById(fileId);
103      if (fileData != null) {
104        final file = File(fileData.filePath);
105        await file.writeAsString(jsonString);
106        await _repo.touchFile(fileId); // Update last modified
107      }
108      return fileId;
109    } else {
110      // Create new file via temporary storage
111      final directory = await getTemporaryDirectory();
112      final tempFile = File(
113        '${directory.path}/canvas_${DateTime.now().millisecondsSinceEpoch}.json',
114      );
115      await tempFile.writeAsString(jsonString);
116
117      final newId = await saveFile(
118        tempFile,
119        projectId,
120        name: fileName ?? "Untitled Canvas",
121        description: "Editable Canvas Board",
122      );
123
124      // Cleanup temp file
125      if (await tempFile.exists()) await tempFile.delete();
126      return newId;
127    }
128  }
129
130  // Unified metadata extraction logic
131  Future<FileMetadataInfo> getFileMetadata(String filePath) async {
132    try {
133      final f = File(filePath);
134      if (!await f.exists()) return FileMetadataInfo();
135
136      if (filePath.toLowerCase().endsWith('.json')) {
137        try {
138          final content = await f.readAsString();
139          final data = jsonDecode(content);
140
141          if (data is Map) {
142            final width = (data['width'] as num?)?.toDouble() ?? 0;
143            final height = (data['height'] as num?)?.toDouble() ?? 0;
144            final previewPath = data['preview_path'] as String?;
145
146            return FileMetadataInfo(
147              width: width,
148              height: height,
149              previewPath: previewPath,
150            );
151          }
152        } catch (e) {
153          // ignore: avoid_print
154          print('Error parsing JSON metadata for $filePath: $e');
155        }
156      } else {
157        // Attempt to decode image dimensions directly
158        try {
159          final bytes = await f.readAsBytes();
160          final image = img.decodeImage(bytes);
161          if (image != null) {
162            return FileMetadataInfo(
163              width: image.width.toDouble(),
164              height: image.height.toDouble(),
165              previewPath: filePath,
166            );
167          }
168        } catch (e) {
169          print('Error decoding image metadata for $filePath: $e');
170        }
171      }
172    } catch (e) {
173      print('Error accessing file metadata for $filePath: $e');
174    }
175
176    return FileMetadataInfo();
177  }
178}