@@ -76,119 +76,131 @@ This creates an attachment queue which is responsible for tracking, storing and
76
76
import 'package:power_sync_b0w5r9/custom_code/actions/initialize_power_sync.dart'
77
77
show db;
78
78
Future setUpAttachments() async {
79
- // Add your function code here!
80
- await _initializeAttachmentQueue(db);
79
+ // Add your function code here!
80
+ await _initializeAttachmentQueue(db);
81
81
}
82
+
82
83
PhotoAttachmentQueue? attachmentQueue;
83
84
final _remoteStorage = SupabaseStorageAdapter();
85
+
84
86
class SupabaseStorageAdapter implements AbstractRemoteStorageAdapter {
85
- @override
86
- Future<void> uploadFile(String filename, File file,
87
- {String mediaType = 'text/plain'}) async {
87
+ @override
88
+ Future<void> uploadFile(String filename, File file,
89
+ {String mediaType = 'text/plain'}) async {
88
90
_checkSupabaseBucketIsConfigured();
89
91
try {
90
- await Supabase.instance.client.storage
91
- .from(FFAppConstants.supabaseStorageBucket)
92
- .upload(filename, file,
93
- fileOptions: FileOptions(contentType: mediaType));
92
+ await Supabase.instance.client.storage
93
+ .from(FFAppConstants.supabaseStorageBucket)
94
+ .upload(filename, file,
95
+ fileOptions: FileOptions(contentType: mediaType));
94
96
} catch (error) {
95
- throw Exception(error);
97
+ throw Exception(error);
96
98
}
97
- }
98
- @override
99
- Future<Uint8List> downloadFile(String filePath) async {
99
+ }
100
+
101
+ @override
102
+ Future<Uint8List> downloadFile(String filePath) async {
100
103
_checkSupabaseBucketIsConfigured();
101
104
try {
102
- return await Supabase.instance.client.storage
103
- .from(FFAppConstants.supabaseStorageBucket)
104
- .download(filePath);
105
+ return await Supabase.instance.client.storage
106
+ .from(FFAppConstants.supabaseStorageBucket)
107
+ .download(filePath);
105
108
} catch (error) {
106
- throw Exception(error);
109
+ throw Exception(error);
107
110
}
108
- }
109
- @override
110
- Future<void> deleteFile(String filename) async {
111
+ }
112
+
113
+ @override
114
+ Future<void> deleteFile(String filename) async {
111
115
_checkSupabaseBucketIsConfigured();
112
116
try {
113
- await Supabase.instance.client.storage
114
- .from(FFAppConstants.supabaseStorageBucket)
115
- .remove([filename]);
117
+ await Supabase.instance.client.storage
118
+ .from(FFAppConstants.supabaseStorageBucket)
119
+ .remove([filename]);
116
120
} catch (error) {
117
- throw Exception(error);
121
+ throw Exception(error);
118
122
}
119
- }
120
- void _checkSupabaseBucketIsConfigured() {
123
+ }
124
+
125
+ void _checkSupabaseBucketIsConfigured() {
121
126
if (FFAppConstants.supabaseStorageBucket.isEmpty) {
122
- throw Exception(
123
- 'Supabase storage bucket is not configured in App Constants');
127
+ throw Exception(
128
+ 'Supabase storage bucket is not configured in App Constants');
124
129
}
130
+ }
125
131
}
126
- }
132
+
127
133
/// Function to handle errors when downloading attachments
128
134
/// Return false if you want to archive the attachment
129
135
Future<bool> onDownloadError(Attachment attachment, Object exception) async {
130
- if (exception.toString().contains('Object not found')) {
136
+ if (exception.toString().contains('Object not found')) {
131
137
return false;
138
+ }
139
+ return true;
132
140
}
133
- return true;
134
- }
141
+
135
142
class PhotoAttachmentQueue extends AbstractAttachmentQueue {
136
- PhotoAttachmentQueue(db, remoteStorage)
137
- : super(
143
+ PhotoAttachmentQueue(db, remoteStorage)
144
+ : super(
138
145
db: db,
139
146
remoteStorage: remoteStorage,
140
147
onDownloadError: onDownloadError);
141
- @override
142
- init() async {
148
+
149
+ @override
150
+ init() async {
143
151
if (FFAppConstants.supabaseStorageBucket.isEmpty) {
144
- log.info(
145
- 'No Supabase bucket configured, skip setting up PhotoAttachmentQueue watches');
146
- return;
152
+ log.info(
153
+ 'No Supabase bucket configured, skip setting up PhotoAttachmentQueue watches');
154
+ return;
147
155
}
148
156
await super.init();
149
- }
150
- @override
151
- Future<Attachment> saveFile(String fileId, int size,
152
- {mediaType = 'image/jpeg'}) async {
157
+ }
158
+
159
+ @override
160
+ Future<Attachment> saveFile(String fileId, int size,
161
+ {mediaType = 'image/jpeg'}) async {
153
162
String filename = '$fileId.jpg';
154
163
Attachment photoAttachment = Attachment(
155
- id: fileId,
156
- filename: filename,
157
- state: AttachmentState.queuedUpload.index,
158
- mediaType: mediaType,
159
- localUri: getLocalFilePathSuffix(filename),
160
- size: size,
164
+ id: fileId,
165
+ filename: filename,
166
+ state: AttachmentState.queuedUpload.index,
167
+ mediaType: mediaType,
168
+ localUri: getLocalFilePathSuffix(filename),
169
+ size: size,
161
170
);
162
171
return attachmentsService.saveAttachment(photoAttachment);
163
- }
164
- @override
165
- Future<Attachment> deleteFile(String fileId) async {
172
+ }
173
+
174
+ @override
175
+ Future<Attachment> deleteFile(String fileId) async {
166
176
String filename = '$fileId.jpg';
167
177
Attachment photoAttachment = Attachment(
168
178
id: fileId,
169
179
filename: filename,
170
180
state: AttachmentState.queuedDelete.index);
171
181
return attachmentsService.saveAttachment(photoAttachment);
172
- }
173
- @override
174
- StreamSubscription<void> watchIds({String fileExtension = 'jpg'}) {
182
+ }
183
+
184
+ @override
185
+ StreamSubscription<void> watchIds({String fileExtension = 'jpg'}) {
175
186
log.info('Watching photos in lists table...');
176
187
return db.watch('''
177
- SELECT photo_id FROM lists
178
- WHERE photo_id IS NOT NULL
188
+ SELECT photo_id FROM lists
189
+ WHERE photo_id IS NOT NULL
179
190
''').map((results) {
180
- return results.map((row) => row['photo_id'] as String).toList();
191
+ return results.map((row) => row['photo_id'] as String).toList();
181
192
}).listen((ids) async {
182
- List<String> idsInQueue = await attachmentsService.getAttachmentIds();
183
- List<String> relevantIds =
184
- ids.where((element) => !idsInQueue.contains(element)).toList();
185
- syncingService.processIds(relevantIds, fileExtension);
193
+ List<String> idsInQueue = await attachmentsService.getAttachmentIds();
194
+ List<String> relevantIds =
195
+ ids.where((element) => !idsInQueue.contains(element)).toList();
196
+ syncingService.processIds(relevantIds, fileExtension);
186
197
});
198
+ }
187
199
}
188
- }
200
+
189
201
Future<void> _initializeAttachmentQueue(powersync.PowerSyncDatabase db) async {
190
- final queue = attachmentQueue = PhotoAttachmentQueue(db, _remoteStorage);
191
- await queue.init();
202
+ final queue = attachmentQueue = PhotoAttachmentQueue(db, _remoteStorage);
203
+ await queue.init();
192
204
}
193
205
```
194
206
4 . Click "Save Action".
@@ -219,23 +231,24 @@ This action handles downloads by taking an attachment ID and returning an `Uploa
219
231
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
220
232
import 'dart:io';
221
233
import 'set_up_attachments.dart';
234
+
222
235
Future<FFUploadedFile?> resolveItemPicture(String? id) async {
223
- if (id == null) {
236
+ if (id == null) {
224
237
return null;
225
- }
226
- final name = '$id.jpg';
227
- final path = await attachmentQueue?.getLocalUri(name);
228
- if (path == null) {
238
+ }
239
+ final name = '$id.jpg';
240
+ final path = await attachmentQueue?.getLocalUri(name);
241
+ if (path == null) {
229
242
return null;
230
- }
231
- final file = File(path);
232
- if (!await file.exists()) {
243
+ }
244
+ final file = File(path);
245
+ if (!await file.exists()) {
233
246
return null;
234
- }
235
- return FFUploadedFile(
247
+ }
248
+ return FFUploadedFile(
236
249
name: name,
237
250
bytes: await file.readAsBytes(),
238
- );
251
+ );
239
252
}
240
253
```
241
254
3 . Under ** Action Settings -> Define Arguments** on the right, click "Add Arguments".
@@ -258,32 +271,38 @@ This action handles uploads by passing the `UploadedFile` to local storage and t
258
271
show db;
259
272
import 'package:powersync/powersync.dart' as powersync;
260
273
import 'set_up_attachments.dart' show attachmentQueue;
274
+
261
275
Future setItemPicture(
262
- String list ,
263
- FFUploadedFile? picture ,
276
+ FFUploadedFile? picture ,
277
+ Future Function(String? photoId) applyToDatabase ,
264
278
) async {
265
- if (picture == null) {
266
- await db.execute('update lists set photo_id = null where id = ?', [list] );
279
+ if (picture == null) {
280
+ await applyToDatabase( null);
267
281
return;
268
- }
269
- final queue = attachmentQueue;
270
- if (queue == null) {
282
+ }
283
+
284
+ final queue = attachmentQueue;
285
+ if (queue == null) {
271
286
return;
272
- }
273
- String photoId = powersync.uuid.v4();
274
- final storageDirectory = await queue.getStorageDirectory ();
275
- await queue.localStorage
276
- .saveFile('$storageDirectory/$photoId.jpg', picture.bytes!);
277
- queue .saveFile(photoId, picture.bytes!.length );
278
- await db
279
- .execute('update lists set photo_id = ? where id = ?', [ photoId, list] );
287
+ }
288
+
289
+ String photoId = powersync.uuid.v4 ();
290
+ final storageDirectory = await queue.getStorageDirectory();
291
+ await queue.localStorage
292
+ .saveFile('$storageDirectory/$ photoId.jpg' , picture.bytes!);
293
+ queue.saveFile(photoId, picture.bytes!.length);
294
+ await applyToDatabase( photoId);
280
295
}
281
296
```
282
297
3 . Under ** Action Settings -> Define Arguments** on the right, click "Add Arguments".
283
- 1 . Set the "Name" to ` list ` .
284
- 2 . Uncheck "Nullable ".
298
+ 1 . Set the "Name" to ` picture ` .
299
+ 2 . Under "Type" select "UploadedFile ".
285
300
4 . Click "Add Arguments" again.
286
- 1 . Set the "Name" to ` id ` .
301
+ 1 . Set the "Name" to ` applyToDatabase ` .
302
+ 2 . Under "Type" select "Action".
303
+ 3 . Add an Action Parameter.
304
+ 4 . Set the "Name" to ` photoId ` .
305
+ 5 . Set its "Type" to "String".
287
306
5 . Click "Save Action".
288
307
6 . Click "Yes" when prompted about parameters in the settings not matching parameters in the code editor.
289
308
7 . Check the Custom Actions for any errors.
@@ -371,12 +390,26 @@ This action handles uploads by passing the `UploadedFile` to local storage and t
371
390
4 . Add an action, search for "media" and select "Upload/Save Media".
372
391
5 . Under "Upload Type" select "Local Upload (Widget State).
373
392
6 . Chain another action and select the ` setItemPicture ` custom action.
374
- 7 . Under "Set Action Arguments", under the "list" argument, click on the settings icon next to "Value".
375
- 8 . Select the "Component Parameter" -> ` list ` variable.
376
- 9 . Under "Supabase Row Fields" select "id".
377
- 10 . Click "Confirm".
378
- 11 . Under the "picture" argument, set the "Value", to the "Widget State" -> "Uploaded Local File" variable.
379
- 12 . Click "Confirm".
393
+ 7 . Under "Set Action Arguments", under the "picture" argument, set the "Value", to the "Widget State" -> "Uploaded Local File" variable.
394
+ 8 . Click "Confirm".
395
+ 9 . Under the "applyToDatabase" argument, add an action and under "Custom Action" -> "PowerSync", select "powersyncWrite".
396
+ 10 . Under the "Set Action Arguments" -> "sql" section, add the SQL query to update the photo.
397
+ 1 . Paste the following into the "Value" field:
398
+ ``` update lists set photo_id = :photo where id = :id; ```
399
+ 2 . Under the "parameters" section, set the ` photo ` parameter and ` id ` parameters we're using the above query:
400
+ 3 . Click on "UNSET".
401
+ 4 . Select "Create Map (JSON)" as the variable.
402
+ 5 . Under "Add Map Entries", click "Add Key Value Pair".
403
+ 6 . Set the "Key" to ` photo ` .
404
+ 7 . Set the "Value" to "Action Parameter" -> ` photoId ` .
405
+ 8 . Click "Confirm".
406
+ 9 . Add another Key Value Pair.
407
+ 10 . Set the "Key" to ` id ` .
408
+ 11 . Set the "Component Parameters" -> ` list ` .
409
+ 12 . Under "Available Options" select "Get Row Field".
410
+ 13 . Under "Supabase Row Fields" select "id".
411
+ 14 . Click "Confirm".
412
+ 15 . Click "Confirm".
380
413
11 . Chain another action, search for "update com" and select "Update Component State".
381
414
1 . Click on "Add Field".
382
415
2 . Select the "image - Uploaded File" field.
0 commit comments