Skip to content

Commit cb0fc46

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 ed409dc commit cb0fc46

File tree

2 files changed

+53
-32
lines changed

2 files changed

+53
-32
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: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,42 @@ abstract class _AttachUploadsButton extends StatelessWidget {
323323
}
324324
}
325325

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

@@ -331,39 +367,21 @@ class _AttachFileButton extends _AttachUploadsButton {
331367

332368
@override
333369
Future<Iterable<_File>> getFiles(BuildContext context) async {
334-
FilePickerResult? result;
335-
try {
336-
result = await FilePicker.platform.pickFiles(
337-
allowMultiple: true, withReadStream: true, type: FileType.any);
338-
} catch (e) {
339-
if (e is PlatformException && e.code == 'read_external_storage_denied') {
340-
// Observed on Android. If Android's error message tells us whether the
341-
// user has checked "Don't ask again", it seems the library doesn't pass
342-
// that on to us. So just always prompt to check permissions in settings.
343-
// If the user hasn't checked "Don't ask again", they can always dismiss
344-
// our prompt and retry, and the permissions request will reappear,
345-
// letting them grant permissions and complete the upload.
346-
showSuggestedActionDialog(context: context, // TODO(i18n)
347-
title: 'Permissions needed',
348-
message: 'To upload files, please grant Zulip additional permissions in Settings.',
349-
actionButtonText: 'Open settings',
350-
onActionButtonPress: () {
351-
AppSettings.openAppSettings();
352-
});
353-
} else {
354-
// TODO(i18n)
355-
showErrorDialog(context: context, title: 'Error', message: e.toString());
356-
}
357-
return [];
358-
}
359-
if (result == null) {
360-
return []; // User cancelled; do nothing
361-
}
370+
return _getFilePickerFiles(context, FileType.any);
371+
}
372+
}
362373

363-
return result.files.map((f) {
364-
assert(f.readStream != null); // We passed `withReadStream: true` to pickFiles.
365-
return _File(content: f.readStream!, length: f.size, filename: f.name);
366-
});
374+
class _AttachMediaButton extends _AttachUploadsButton {
375+
const _AttachMediaButton({required super.contentController, required super.contentFocusNode});
376+
377+
@override
378+
IconData get icon => Icons.image;
379+
380+
@override
381+
Future<Iterable<_File>> getFiles(BuildContext context) async {
382+
// TODO: This doesn't give quite the right UI on Android.
383+
// Perhaps try `image_picker`: https://github.com/zulip/zulip-flutter/issues/56#issuecomment-1514001281
384+
return _getFilePickerFiles(context, FileType.media);
367385
}
368386
}
369387

@@ -565,6 +583,7 @@ class _StreamComposeBoxState extends State<StreamComposeBox> {
565583
child: Row(
566584
children: [
567585
_AttachFileButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
586+
_AttachMediaButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
568587
])),
569588
]))));
570589
}

0 commit comments

Comments
 (0)