creek
The AI Image Editor of 2030git clone https://github.com/maydayv7/creek
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}