Skip to content

Commit 3e176b2

Browse files
CubeRomanMagellanMagellan
andauthored
SK-589: implement ability to take a photo (#92)
* create monorepo * add api * remove client old api * add tokens management * rename sama_chat_api to sama_sdk * add image share to support * add image_picker * add camera picker * update agpVersion --------- Co-authored-by: Magellan <magellan@connectycube.com>
1 parent 586bf9d commit 3e176b2

10 files changed

Lines changed: 105 additions & 77 deletions

File tree

sama_chat_client/android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx4G
22
android.useAndroidX=true
33
android.enableJetifier=true
44
kotlinVersion=2.2.0
5-
agpVersion=8.9.0
5+
agpVersion=8.9.1

sama_chat_client/ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
<true/>
4949
<key>NSPhotoLibraryUsageDescription</key>
5050
<string>To upload photos, please allow permission to access your photo library.</string>
51+
<key>NSCameraUsageDescription</key>
52+
<string>To upload photos from camera</string>
5153
<key>UIApplicationSupportsIndirectInputEvents</key>
5254
<true/>
5355
<key>UIBackgroundModes</key>

sama_chat_client/lib/src/features/conversation/bloc/media_sender/media_sender_bloc.dart

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'dart:async';
22
import 'dart:io';
33

44
import 'package:equatable/equatable.dart';
5-
import 'package:file_picker/file_picker.dart';
65
import 'package:flutter_bloc/flutter_bloc.dart';
6+
import 'package:image_picker/image_picker.dart';
77
import 'package:path/path.dart';
88
import 'package:sama_sdk/api/api.dart';
99

@@ -23,13 +23,18 @@ const maxAttachmentsCount = 10;
2323
class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
2424
final ConversationModel currentConversation;
2525
final MessagesRepository messagesRepository;
26+
final ImagePicker picker = ImagePicker();
2627

2728
MediaSenderBloc({
2829
required this.currentConversation,
2930
required this.messagesRepository,
3031
}) : super(MediaSenderState()) {
31-
on<PickMoreFiles>(
32-
_onPickFiles,
32+
on<PickMedia>(
33+
_onPickMedia,
34+
);
35+
36+
on<PickCamera>(
37+
_onPickCamera,
3338
);
3439

3540
on<ChangeMessage>(
@@ -57,12 +62,17 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
5762
);
5863
}
5964

60-
FutureOr<void> _onPickFiles(
61-
PickMoreFiles event, Emitter<MediaSenderState> emit) {
65+
FutureOr<void> _onPickMedia(PickMedia event, Emitter<MediaSenderState> emit) {
6266
emit(state.copyWith(status: MediaSelectorStatus.picking));
6367
_pickMedia();
6468
}
6569

70+
FutureOr<void> _onPickCamera(
71+
PickCamera event, Emitter<MediaSenderState> emit) {
72+
emit(state.copyWith(status: MediaSelectorStatus.picking));
73+
_pickCamera();
74+
}
75+
6676
FutureOr<void> _onFilesAdded(AddFiles event, Emitter<MediaSenderState> emit) {
6777
emit(state.copyWith(status: MediaSelectorStatus.mediaSelected));
6878
if (event.error?.isNotEmpty ?? false) {
@@ -175,25 +185,12 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
175185
}
176186

177187
void _pickMedia() {
178-
FilePicker.platform
179-
.pickFiles(
180-
type: FileType.media,
181-
//TODO RP remove this if it's all ok on both android and iOS sides
182-
// allowedExtensions: [
183-
// ...supportedImageAttachmentExtentions,
184-
// ...supportedVideoAttachmentExtentions
185-
// ],
186-
allowMultiple: true,
187-
compressionQuality: 0)
188-
.then((result) {
189-
var files = result?.files;
190-
if (files?.isEmpty ?? true) {
188+
picker.pickMultipleMedia().then((result) {
189+
if (result.isEmpty) {
191190
add(const AddFiles([]));
192191
} else {
193-
var files = List<File>.from(result?.files
194-
.map((platformFile) => File(platformFile.path!))
195-
.toList() ??
196-
[]);
192+
var files = List<File>.from(
193+
result.map((platformFile) => File(platformFile.path)).toList());
197194
add(AddFiles(files));
198195
}
199196
}).catchError((onError) {
@@ -202,6 +199,19 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
202199
});
203200
}
204201

202+
void _pickCamera() {
203+
picker.pickImage(source: ImageSource.camera).then((result) {
204+
if (result?.path.isEmpty ?? true) {
205+
add(const AddFiles([]));
206+
} else {
207+
add(AddFiles([File(result!.path)]));
208+
}
209+
}).catchError((onError) {
210+
add(const AddFiles([],
211+
error: 'Please allow permission access to Gallery'));
212+
});
213+
}
214+
205215
FutureOr<void> _onMessageChanged(
206216
ChangeMessage event, Emitter<MediaSenderState> emit) {
207217
emit(state.copyWith(message: event.message));

sama_chat_client/lib/src/features/conversation/bloc/media_sender/media_sender_event.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ final class AddFiles extends MediaSenderEvent {
1414
const AddFiles(this.selectedFiles, {this.error});
1515
}
1616

17-
final class PickMoreFiles extends MediaSenderEvent {
18-
const PickMoreFiles();
17+
final class PickCamera extends MediaSenderEvent {
18+
const PickCamera();
19+
}
20+
21+
final class PickMedia extends MediaSenderEvent {
22+
const PickMedia();
1923
}
2024

2125
final class ChangeMessage extends MediaSenderEvent {

sama_chat_client/lib/src/features/conversation/view/media_sender.dart

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ class MediaSender extends StatelessWidget {
4444
Widget build(BuildContext context) {
4545
if (path != null) {
4646
BlocProvider.of<MediaSenderBloc>(context).add(AddFiles([File(path!)]));
47-
} else {
48-
BlocProvider.of<MediaSenderBloc>(context).add(const PickMoreFiles());
4947
}
5048
return BlocListener<MediaSenderBloc, MediaSenderState>(
5149
listener: (context, state) {
@@ -74,18 +72,7 @@ class MediaSender extends StatelessWidget {
7472
const SizedBox(
7573
height: 8,
7674
),
77-
if (state.selectedFiles.isEmpty &&
78-
state.status == MediaSelectorStatus.initial)
79-
Container(
80-
padding: const EdgeInsets.all(8),
81-
width: 48,
82-
height: 48,
83-
child: const CircularProgressIndicator(
84-
strokeWidth: 3.0,
85-
),
86-
),
87-
if (state.selectedFiles.isEmpty &&
88-
state.status != MediaSelectorStatus.initial)
75+
if (state.selectedFiles.isEmpty)
8976
const Center(
9077
child: Text(
9178
'Select files',
@@ -131,25 +118,55 @@ class MediaSender extends StatelessWidget {
131118
),
132119
Row(
133120
children: [
134-
TextButton(
135-
onPressed: () {
136-
if (state.selectedFiles.length < 10 &&
137-
state.status !=
138-
MediaSelectorStatus.processing) {
139-
BlocProvider.of<MediaSenderBloc>(context)
140-
.add(const PickMoreFiles());
141-
}
142-
},
143-
child: Text(
144-
'Add',
145-
style: TextStyle(
146-
color: state.selectedFiles.length < 10 &&
147-
state.status !=
148-
MediaSelectorStatus.processing
149-
? slateBlue
150-
: whiteAluminum),
151-
),
152-
),
121+
Column(
122+
mainAxisSize: MainAxisSize.min,
123+
crossAxisAlignment: CrossAxisAlignment.start,
124+
children: [
125+
TextButton(
126+
onPressed: () {
127+
if (state.selectedFiles.length < 10 &&
128+
state.status !=
129+
MediaSelectorStatus.processing) {
130+
BlocProvider.of<MediaSenderBloc>(context)
131+
.add(const PickCamera());
132+
}
133+
},
134+
style: TextButton.styleFrom(
135+
tapTargetSize:
136+
MaterialTapTargetSize.shrinkWrap),
137+
child: Text(
138+
'Camera',
139+
style: TextStyle(
140+
color: state.selectedFiles.length < 10 &&
141+
state.status !=
142+
MediaSelectorStatus.processing
143+
? slateBlue
144+
: whiteAluminum),
145+
),
146+
),
147+
TextButton(
148+
onPressed: () {
149+
if (state.selectedFiles.length < 10 &&
150+
state.status !=
151+
MediaSelectorStatus.processing) {
152+
BlocProvider.of<MediaSenderBloc>(context)
153+
.add(const PickMedia());
154+
}
155+
},
156+
style: TextButton.styleFrom(
157+
tapTargetSize:
158+
MaterialTapTargetSize.shrinkWrap),
159+
child: Text(
160+
'Media',
161+
style: TextStyle(
162+
color: state.selectedFiles.length < 10 &&
163+
state.status !=
164+
MediaSelectorStatus.processing
165+
? slateBlue
166+
: whiteAluminum),
167+
),
168+
),
169+
]),
153170
const Expanded(child: SizedBox.shrink()),
154171
TextButton(
155172
onPressed: () {

sama_chat_client/lib/src/features/conversation/view/message_input.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ class _MessageInputState extends State<MessageInput> {
196196
showDialog(
197197
barrierDismissible: false,
198198
context: context,
199-
builder: (ctx) {
200-
dialogContext = ctx;
199+
builder: (_) {
201200
return AlertDialog(
202201
contentPadding:
203202
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
@@ -214,9 +213,7 @@ class _MessageInputState extends State<MessageInput> {
214213
path: path),
215214
));
216215
},
217-
).then((result) async {
218-
dialogContext = null;
219-
});
216+
);
220217
}
221218

222219
@override

sama_chat_client/lib/src/features/conversation_group_create/bloc/group_bloc.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'dart:io';
22

33
import 'package:bloc/bloc.dart';
44
import 'package:equatable/equatable.dart';
5-
import 'package:file_picker/file_picker.dart';
65
import 'package:formz/formz.dart';
6+
import 'package:image_picker/image_picker.dart';
77

88
import '../../../db/models/user_model.dart';
99
import '../models/avatar.dart';
@@ -41,11 +41,9 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
4141
GroupAvatarPicked event,
4242
Emitter<GroupState> emit,
4343
) async {
44-
await FilePicker.platform
45-
.pickFiles(type: FileType.image, compressionQuality: 0)
46-
.then((result) {
44+
await ImagePicker().pickImage(source: ImageSource.gallery).then((result) {
4745
if (result != null) {
48-
File file = File(result.files.single.path!);
46+
File file = File(result.path);
4947
final avatarUrl = GroupAvatar.dirty(file);
5048
emit(
5149
state.copyWith(

sama_chat_client/lib/src/features/group_info/bloc/group_info_bloc.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'dart:io';
22

33
import 'package:bloc/bloc.dart';
44
import 'package:equatable/equatable.dart';
5-
import 'package:file_picker/file_picker.dart';
65
import 'package:formz/formz.dart';
6+
import 'package:image_picker/image_picker.dart';
77
import 'package:sama_sdk/api/api.dart';
88

99
import '../../../db/models/models.dart';
@@ -60,11 +60,11 @@ class GroupInfoBloc extends Bloc<GroupInfoEvent, GroupInfoState> {
6060
GroupAvatarPicked event,
6161
Emitter<GroupInfoState> emit,
6262
) async {
63-
await FilePicker.platform
64-
.pickFiles(type: FileType.image, compressionQuality: 0)
63+
await ImagePicker()
64+
.pickImage(source: ImageSource.gallery)
6565
.then((result) async {
6666
if (result != null) {
67-
File file = File(result.files.single.path!);
67+
File file = File(result.path);
6868
try {
6969
final chat = await _conversationRepository.updateConversation(
7070
id: state.conversation.id, avatarUrl: file);

sama_chat_client/lib/src/features/profile/bloc/profile_bloc.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'dart:io';
22

33
import 'package:bloc/bloc.dart';
44
import 'package:equatable/equatable.dart';
5-
import 'package:file_picker/file_picker.dart';
65
import 'package:formz/formz.dart';
6+
import 'package:image_picker/image_picker.dart';
77

88
import '../../../db/models/models.dart';
99
import '../../../repository/user/user_repository.dart';
@@ -56,11 +56,11 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
5656
ProfileAvatarPicked event,
5757
Emitter<ProfileState> emit,
5858
) async {
59-
await FilePicker.platform
60-
.pickFiles(type: FileType.image, compressionQuality: 0)
59+
await ImagePicker()
60+
.pickImage(source: ImageSource.gallery)
6161
.then((result) async {
6262
if (result != null) {
63-
File file = File(result.files.single.path!);
63+
File file = File(result.path);
6464
try {
6565
final user = await _userRepository.updateAvatar(file);
6666
emit(state.copyWith(

sama_chat_client/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ dependencies:
1818
connectivity_plus: ^6.0.3
1919
cupertino_icons: ^1.0.6
2020
equatable: ^2.0.5
21-
file_picker: ^8.0.6
2221
firebase_core: ^3.6.0
2322
firebase_messaging: ^15.1.3
2423
flutter:
@@ -35,6 +34,7 @@ dependencies:
3534
go_router: ^14.8.1
3635
http: ^1.2.2
3736
image: ^4.2.0
37+
image_picker: ^1.2.0
3838
intl: ^0.20.0
3939
mime: ^1.0.5
4040
objectbox: ^4.0.3

0 commit comments

Comments
 (0)