Skip to content

Commit 2bd2187

Browse files
committed
compose: Prototype image/video-from-library upload UI
And pull out a helper from _AttachFileButton, since it also uses `file_picker` and it's convenient to reuse that code here. Fixes: #56
1 parent ae6dd5b commit 2bd2187

File tree

2 files changed

+52
-31
lines changed

2 files changed

+52
-31
lines changed

ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,7 @@
5353
<array>
5454
<string>fetch</string>
5555
</array>
56+
<key>NSPhotoLibraryUsageDescription</key>
57+
<string>Choose photos from your library and send them in Zulip messages.</string>
5658
</dict>
5759
</plist>

lib/widgets/compose_box.dart

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,41 @@ abstract class _AttachUploadsButton extends StatelessWidget {
315315
}
316316
}
317317

318+
Future<Iterable<_File>?> _getFilePickerFiles(BuildContext context) async {
319+
FilePickerResult? result;
320+
try {
321+
result = await FilePicker.platform.pickFiles(allowMultiple: true, withReadStream: true);
322+
} catch (e) {
323+
if (e is PlatformException && e.code == 'read_external_storage_denied') {
324+
// Observed on Android. If Android's error message tells us whether the
325+
// user has checked "Don't ask again", it seems the library doesn't pass
326+
// that on to us. So just always prompt to check permissions in settings.
327+
// If the user hasn't checked "Don't ask again", they can always dismiss
328+
// our prompt and retry, and the permissions request will reappear,
329+
// letting them grant permissions and complete the upload.
330+
showSuggestedActionDialog(context: context, // TODO(i18n)
331+
title: 'Permissions needed',
332+
message: 'To upload an image, please grant Zulip additional permissions in Settings.',
333+
actionButtonText: 'Open settings',
334+
onActionButtonPress: () {
335+
AppSettings.openAppSettings();
336+
});
337+
} else {
338+
// TODO(i18n)
339+
showErrorDialog(context: context, title: 'Error', message: e.toString());
340+
}
341+
return null;
342+
}
343+
if (result == null) {
344+
return null; // User cancelled; do nothing
345+
}
346+
347+
return result.files.map((f) {
348+
assert(f.readStream != null); // We passed `withReadStream: true` to pickFiles.
349+
return _File(content: f.readStream!, length: f.size, filename: f.name);
350+
});
351+
}
352+
318353
class _AttachFileButton extends _AttachUploadsButton {
319354
const _AttachFileButton({required super.contentController, required super.contentFocusNode});
320355

@@ -323,38 +358,21 @@ class _AttachFileButton extends _AttachUploadsButton {
323358

324359
@override
325360
Future<Iterable<_File>?> getFiles(BuildContext context) async {
326-
FilePickerResult? result;
327-
try {
328-
result = await FilePicker.platform.pickFiles(allowMultiple: true, withReadStream: true);
329-
} catch (e) {
330-
if (e is PlatformException && e.code == 'read_external_storage_denied') {
331-
// Observed on Android. If Android's error message tells us whether the
332-
// user has checked "Don't ask again", it seems the library doesn't pass
333-
// that on to us. So just always prompt to check permissions in settings.
334-
// If the user hasn't checked "Don't ask again", they can always dismiss
335-
// our prompt and retry, and the permissions request will reappear,
336-
// letting them grant permissions and complete the upload.
337-
showSuggestedActionDialog(context: context, // TODO(i18n)
338-
title: 'Permissions needed',
339-
message: 'To upload an image, please grant Zulip additional permissions in Settings.',
340-
actionButtonText: 'Open settings',
341-
onActionButtonPress: () {
342-
AppSettings.openAppSettings();
343-
});
344-
} else {
345-
// TODO(i18n)
346-
showErrorDialog(context: context, title: 'Error', message: e.toString());
347-
}
348-
return null;
349-
}
350-
if (result == null) {
351-
return null; // User cancelled; do nothing
352-
}
361+
return _getFilePickerFiles(context);
362+
}
363+
}
353364

354-
return result.files.map((f) {
355-
assert(f.readStream != null); // We passed `withReadStream: true` to pickFiles.
356-
return _File(content: f.readStream!, length: f.size, filename: f.name);
357-
});
365+
class _AttachMediaButton extends _AttachUploadsButton {
366+
const _AttachMediaButton({required super.contentController, required super.contentFocusNode});
367+
368+
@override
369+
IconData get icon => Icons.image;
370+
371+
@override
372+
Future<Iterable<_File>?> getFiles(BuildContext context) async {
373+
// TODO: This doesn't give quite the right UI on Android.
374+
// Perhaps try `image_picker`: https://github.com/zulip/zulip-flutter/issues/56#issuecomment-1514001281
375+
return _getFilePickerFiles(context);
358376
}
359377
}
360378

@@ -556,6 +574,7 @@ class _StreamComposeBoxState extends State<StreamComposeBox> {
556574
child: Row(
557575
children: [
558576
_AttachFileButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
577+
_AttachMediaButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
559578
])),
560579
]))));
561580
}

0 commit comments

Comments
 (0)