Skip to content

Commit 65e9683

Browse files
authored
Flutterflow attachments polish (#146)
* Improve formatting * Updated setItemPicture so it's reusable * Update integration-guides/flutterflow-+-powersync/handling-attachments.mdx
1 parent e7b6af6 commit 65e9683

File tree

1 file changed

+133
-100
lines changed

1 file changed

+133
-100
lines changed

integration-guides/flutterflow-+-powersync/handling-attachments.mdx

Lines changed: 133 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -76,119 +76,131 @@ This creates an attachment queue which is responsible for tracking, storing and
7676
import 'package:power_sync_b0w5r9/custom_code/actions/initialize_power_sync.dart'
7777
show db;
7878
Future setUpAttachments() async {
79-
// Add your function code here!
80-
await _initializeAttachmentQueue(db);
79+
// Add your function code here!
80+
await _initializeAttachmentQueue(db);
8181
}
82+
8283
PhotoAttachmentQueue? attachmentQueue;
8384
final _remoteStorage = SupabaseStorageAdapter();
85+
8486
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 {
8890
_checkSupabaseBucketIsConfigured();
8991
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));
9496
} catch (error) {
95-
throw Exception(error);
97+
throw Exception(error);
9698
}
97-
}
98-
@override
99-
Future<Uint8List> downloadFile(String filePath) async {
99+
}
100+
101+
@override
102+
Future<Uint8List> downloadFile(String filePath) async {
100103
_checkSupabaseBucketIsConfigured();
101104
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);
105108
} catch (error) {
106-
throw Exception(error);
109+
throw Exception(error);
107110
}
108-
}
109-
@override
110-
Future<void> deleteFile(String filename) async {
111+
}
112+
113+
@override
114+
Future<void> deleteFile(String filename) async {
111115
_checkSupabaseBucketIsConfigured();
112116
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]);
116120
} catch (error) {
117-
throw Exception(error);
121+
throw Exception(error);
118122
}
119-
}
120-
void _checkSupabaseBucketIsConfigured() {
123+
}
124+
125+
void _checkSupabaseBucketIsConfigured() {
121126
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');
124129
}
130+
}
125131
}
126-
}
132+
127133
/// Function to handle errors when downloading attachments
128134
/// Return false if you want to archive the attachment
129135
Future<bool> onDownloadError(Attachment attachment, Object exception) async {
130-
if (exception.toString().contains('Object not found')) {
136+
if (exception.toString().contains('Object not found')) {
131137
return false;
138+
}
139+
return true;
132140
}
133-
return true;
134-
}
141+
135142
class PhotoAttachmentQueue extends AbstractAttachmentQueue {
136-
PhotoAttachmentQueue(db, remoteStorage)
137-
: super(
143+
PhotoAttachmentQueue(db, remoteStorage)
144+
: super(
138145
db: db,
139146
remoteStorage: remoteStorage,
140147
onDownloadError: onDownloadError);
141-
@override
142-
init() async {
148+
149+
@override
150+
init() async {
143151
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;
147155
}
148156
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 {
153162
String filename = '$fileId.jpg';
154163
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,
161170
);
162171
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 {
166176
String filename = '$fileId.jpg';
167177
Attachment photoAttachment = Attachment(
168178
id: fileId,
169179
filename: filename,
170180
state: AttachmentState.queuedDelete.index);
171181
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'}) {
175186
log.info('Watching photos in lists table...');
176187
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
179190
''').map((results) {
180-
return results.map((row) => row['photo_id'] as String).toList();
191+
return results.map((row) => row['photo_id'] as String).toList();
181192
}).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);
186197
});
198+
}
187199
}
188-
}
200+
189201
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();
192204
}
193205
```
194206
4. Click "Save Action".
@@ -219,23 +231,24 @@ This action handles downloads by taking an attachment ID and returning an `Uploa
219231
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
220232
import 'dart:io';
221233
import 'set_up_attachments.dart';
234+
222235
Future<FFUploadedFile?> resolveItemPicture(String? id) async {
223-
if (id == null) {
236+
if (id == null) {
224237
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) {
229242
return null;
230-
}
231-
final file = File(path);
232-
if (!await file.exists()) {
243+
}
244+
final file = File(path);
245+
if (!await file.exists()) {
233246
return null;
234-
}
235-
return FFUploadedFile(
247+
}
248+
return FFUploadedFile(
236249
name: name,
237250
bytes: await file.readAsBytes(),
238-
);
251+
);
239252
}
240253
```
241254
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
258271
show db;
259272
import 'package:powersync/powersync.dart' as powersync;
260273
import 'set_up_attachments.dart' show attachmentQueue;
274+
261275
Future setItemPicture(
262-
String list,
263-
FFUploadedFile? picture,
276+
FFUploadedFile? picture,
277+
Future Function(String? photoId) applyToDatabase,
264278
) 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);
267281
return;
268-
}
269-
final queue = attachmentQueue;
270-
if (queue == null) {
282+
}
283+
284+
final queue = attachmentQueue;
285+
if (queue == null) {
271286
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);
280295
}
281296
```
282297
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".
285300
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".
287306
5. Click "Save Action".
288307
6. Click "Yes" when prompted about parameters in the settings not matching parameters in the code editor.
289308
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
371390
4. Add an action, search for "media" and select "Upload/Save Media".
372391
5. Under "Upload Type" select "Local Upload (Widget State).
373392
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".
380413
11. Chain another action, search for "update com" and select "Update Component State".
381414
1. Click on "Add Field".
382415
2. Select the "image - Uploaded File" field.

0 commit comments

Comments
 (0)