Skip to content

Commit f99a3b3

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

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';
@@ -385,6 +386,51 @@ class _AttachMediaButton extends _AttachUploadsButton {
385386
}
386387
}
387388

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

0 commit comments

Comments
 (0)