Skip to content

Commit a3a889a

Browse files
committed
compose: Prototype upload-from-camera UI
Fixes: #61
1 parent 1eca02e commit a3a889a

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
<array>
5454
<string>fetch</string>
5555
</array>
56+
<key>NSCameraUsageDescription</key>
57+
<string>By allowing camera access, you can take photos and send them in Zulip messages.</string>
5658
<key>NSPhotoLibraryUsageDescription</key>
5759
<string>Choose photos from your library and send them in Zulip messages.</string>
5860
</dict>

lib/widgets/compose_box.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:app_settings/app_settings.dart';
22
import 'package:file_picker/file_picker.dart';
33
import 'package:flutter/material.dart';
44
import 'package:flutter/services.dart';
5+
import 'package:image_picker/image_picker.dart';
56
import 'dialog.dart';
67

78
import '../api/route/messages.dart';
@@ -376,6 +377,51 @@ class _AttachMediaButton extends _AttachUploadsButton {
376377
}
377378
}
378379

380+
class _AttachFromCameraButton extends _AttachUploadsButton {
381+
const _AttachFromCameraButton({required super.contentController, required super.contentFocusNode});
382+
383+
@override
384+
IconData get icon => Icons.camera_alt;
385+
386+
@override
387+
Future<Iterable<_File>?> getFiles(BuildContext context) async {
388+
final picker = ImagePicker();
389+
final XFile? result;
390+
try {
391+
// Ideally we'd open a platform interface that lets you choose between
392+
// taking a photo and a video. `image_picker` doesn't yet have that
393+
// option: https://github.com/flutter/flutter/issues/89159
394+
// so just stick with images for now. We could add another button for
395+
// videos, but we don't want too many buttons.
396+
result = await picker.pickImage(source: ImageSource.camera, requestFullMetadata: false);
397+
} catch (e) {
398+
if (e is PlatformException && e.code == 'camera_access_denied') {
399+
// iOS has a quirk where it will only request the native
400+
// permission-request alert once, the first time the app wants to
401+
// use a protected resource. After that, the only way the user can
402+
// grant it is in Settings.
403+
showSuggestedActionDialog(context: context, // TODO(i18n)
404+
title: 'Permissions needed',
405+
message: 'To upload an image, please grant Zulip additional permissions in Settings.',
406+
actionButtonText: 'Open settings',
407+
onActionButtonPress: () {
408+
AppSettings.openAppSettings();
409+
});
410+
} else {
411+
// TODO(i18n)
412+
showErrorDialog(context: context, title: 'Error', message: e.toString());
413+
}
414+
return null;
415+
}
416+
if (result == null) {
417+
return null; // User cancelled; do nothing
418+
}
419+
final length = await result.length();
420+
421+
return [_File(content: result.openRead(), length: length, filename: result.name)];
422+
}
423+
}
424+
379425
/// The send button for StreamComposeBox.
380426
class _StreamSendButton extends StatefulWidget {
381427
const _StreamSendButton({required this.topicController, required this.contentController});
@@ -575,6 +621,7 @@ class _StreamComposeBoxState extends State<StreamComposeBox> {
575621
children: [
576622
_AttachFileButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
577623
_AttachMediaButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
624+
_AttachFromCameraButton(contentController: _contentController, contentFocusNode: _contentFocusNode),
578625
])),
579626
]))));
580627
}

0 commit comments

Comments
 (0)