Skip to content

Commit 41d1788

Browse files
CubeRomanMagellanMagellan
andauthored
SK-578: share to image (#91)
* create monorepo * add api * remove client old api * add tokens management * rename sama_chat_api to sama_sdk * add image share to support * fix Popup with Send Image after share again --------- Co-authored-by: Magellan <magellan@connectycube.com>
1 parent 26c9fe8 commit 41d1788

7 files changed

Lines changed: 84 additions & 43 deletions

File tree

sama_chat_client/android/app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<activity
1616
android:name=".MainActivity"
1717
android:exported="true"
18-
android:launchMode="singleTop"
18+
android:launchMode="singleTask"
1919
android:theme="@style/LaunchTheme"
2020
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
2121
android:hardwareAccelerated="true"
@@ -37,6 +37,11 @@
3737
<category android:name="android.intent.category.DEFAULT" />
3838
<data android:mimeType="text/*" />
3939
</intent-filter>
40+
<intent-filter>
41+
<action android:name="android.intent.action.SEND" />
42+
<category android:name="android.intent.category.DEFAULT" />
43+
<data android:mimeType="image/*" />
44+
</intent-filter>
4045
</activity>
4146
<!-- Don't delete the meta-data below.
4247
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

sama_chat_client/ios/Share Extension/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
<true/>
1515
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
1616
<integer>1</integer>
17+
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
18+
<integer>100</integer>
1719
</dict>
1820
</dict>
1921
<key>NSExtensionMainStoryboard</key>

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
3636
_onMessageChanged,
3737
);
3838

39-
on<_AddFiles>(
39+
on<AddFiles>(
4040
_onFilesAdded,
4141
);
4242

@@ -55,8 +55,6 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
5555
on<CancelSelection>(
5656
_onCancelSelection,
5757
);
58-
59-
_pickMedia();
6058
}
6159

6260
FutureOr<void> _onPickFiles(
@@ -65,8 +63,7 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
6563
_pickMedia();
6664
}
6765

68-
FutureOr<void> _onFilesAdded(
69-
_AddFiles event, Emitter<MediaSenderState> emit) {
66+
FutureOr<void> _onFilesAdded(AddFiles event, Emitter<MediaSenderState> emit) {
7067
emit(state.copyWith(status: MediaSelectorStatus.mediaSelected));
7168
if (event.error?.isNotEmpty ?? false) {
7269
emit(state.copyWith(error: event.error));
@@ -191,16 +188,16 @@ class MediaSenderBloc extends Bloc<MediaSenderEvent, MediaSenderState> {
191188
.then((result) {
192189
var files = result?.files;
193190
if (files?.isEmpty ?? true) {
194-
add(const _AddFiles([]));
191+
add(const AddFiles([]));
195192
} else {
196193
var files = List<File>.from(result?.files
197194
.map((platformFile) => File(platformFile.path!))
198195
.toList() ??
199196
[]);
200-
add(_AddFiles(files));
197+
add(AddFiles(files));
201198
}
202199
}).catchError((onError) {
203-
add(const _AddFiles([],
200+
add(const AddFiles([],
204201
error: 'Please allow permission access to Gallery'));
205202
});
206203
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ class MediaSenderEvent extends Equatable {
77
List<Object> get props => [];
88
}
99

10-
final class _AddFiles extends MediaSenderEvent {
10+
final class AddFiles extends MediaSenderEvent {
1111
final List<File> selectedFiles;
1212
final String? error;
1313

14-
const _AddFiles(this.selectedFiles, {this.error});
14+
const AddFiles(this.selectedFiles, {this.error});
1515
}
1616

1717
final class PickMoreFiles extends MediaSenderEvent {

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,11 @@ class ConversationPage extends StatelessWidget {
143143
},
144144
child: ConnectionChecker(
145145
child: MessageInput(
146-
sharedText: context
146+
sharedMessage: context
147147
.read<SharingIntentBloc>()
148148
.state
149149
.sharedFiles
150-
.firstOrNull
151-
?.path)),
150+
.firstOrNull)),
152151
)
153152
: const MessageInput()
154153
: const SelectInput())

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,23 @@ import 'package:path/path.dart';
99
import '../../../db/models/conversation_model.dart';
1010
import '../../../db/models/message_model.dart';
1111
import '../../../repository/messages/messages_repository.dart';
12+
import '../../../shared/sharing/bloc/sharing_intent_bloc.dart';
1213
import '../../../shared/ui/colors.dart';
1314
import '../../../shared/utils/date_utils.dart';
1415
import '../../../shared/utils/media_utils.dart';
1516
import '../bloc/media_sender/media_sender_bloc.dart';
1617

1718
class MediaSender extends StatelessWidget {
1819
final MessageModel? replyMessage;
20+
final String? path;
1921

20-
const MediaSender(this.replyMessage, {super.key});
22+
const MediaSender(this.replyMessage, this.path, {super.key});
2123

2224
static Widget create({
2325
Key? key,
2426
required ConversationModel currentConversation,
2527
required MessageModel? replyMessage,
28+
required String? path,
2629
}) {
2730
return BlocProvider<MediaSenderBloc>(
2831
create: (context) => MediaSenderBloc(
@@ -31,18 +34,28 @@ class MediaSender extends StatelessWidget {
3134
RepositoryProvider.of<MessagesRepository>(context)),
3235
child: MediaSender(
3336
replyMessage,
37+
path,
3438
key: key,
3539
),
3640
);
3741
}
3842

3943
@override
4044
Widget build(BuildContext context) {
45+
if (path != null) {
46+
BlocProvider.of<MediaSenderBloc>(context).add(AddFiles([File(path!)]));
47+
} else {
48+
BlocProvider.of<MediaSenderBloc>(context).add(const PickMoreFiles());
49+
}
4150
return BlocListener<MediaSenderBloc, MediaSenderState>(
4251
listener: (context, state) {
4352
if (state.status == MediaSelectorStatus.processingFinished ||
4453
state.status == MediaSelectorStatus.canceled) {
4554
context.pop();
55+
if (context.read<SharingIntentBloc>().state.status ==
56+
SharingIntentStatus.processing) {
57+
context.read<SharingIntentBloc>().add(SharingIntentCompleted());
58+
}
4659
}
4760
},
4861
child: BlocBuilder<MediaSenderBloc, MediaSenderState>(

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

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:go_router/go_router.dart';
4+
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
35

46
import '../../../shared/connection/view/connection_checker.dart';
57
import '../../../shared/ui/colors.dart';
@@ -11,9 +13,9 @@ import '../widgets/header_input_box.dart';
1113
import 'media_sender.dart';
1214

1315
class MessageInput extends StatefulWidget {
14-
final String? sharedText;
16+
final SharedMediaFile? sharedMessage;
1517

16-
const MessageInput({super.key, this.sharedText});
18+
const MessageInput({super.key, this.sharedMessage});
1719

1820
@override
1921
State<StatefulWidget> createState() {
@@ -23,17 +25,33 @@ class MessageInput extends StatefulWidget {
2325

2426
class _MessageInputState extends State<MessageInput> {
2527
late final TextEditingController textEditingController =
26-
TextEditingController(text: widget.sharedText);
28+
TextEditingController(
29+
text: widget.sharedMessage?.type == SharedMediaType.text ||
30+
widget.sharedMessage?.type == SharedMediaType.url
31+
? widget.sharedMessage?.path
32+
: null);
2733

2834
final FocusNode showFocusNode = FocusNode();
35+
BuildContext? dialogContext;
36+
37+
@override
38+
void initState() {
39+
super.initState();
40+
if (widget.sharedMessage?.type == SharedMediaType.image) {
41+
WidgetsBinding.instance.addPostFrameCallback((_) {
42+
showMedia(widget.sharedMessage?.path);
43+
});
44+
}
45+
}
2946

3047
@override
3148
Widget build(BuildContext context) {
3249
var showReply = false;
3350
var showEdit = false;
34-
if (widget.sharedText != null) {
51+
if (widget.sharedMessage?.type == SharedMediaType.text ||
52+
widget.sharedMessage?.type == SharedMediaType.url) {
3553
BlocProvider.of<SendMessageBloc>(context)
36-
.add(TextMessageChanged(widget.sharedText!));
54+
.add(TextMessageChanged(widget.sharedMessage!.path));
3755
}
3856
return MultiBlocListener(
3957
listeners: [
@@ -57,7 +75,7 @@ class _MessageInputState extends State<MessageInput> {
5775
return (previous.draftMessage != current.draftMessage ||
5876
previous.replyMessage != current.replyMessage ||
5977
previous.editMessage != current.editMessage) &&
60-
widget.sharedText == null;
78+
widget.sharedMessage == null;
6179
},
6280
listener: (context, state) {
6381
showReply = state.replyMessage != null;
@@ -121,28 +139,7 @@ class _MessageInputState extends State<MessageInput> {
121139
icon: const Icon(Icons.attach_file_outlined),
122140
color: dullGray,
123141
onPressed: () {
124-
connectionChecker(
125-
context,
126-
() => showDialog(
127-
barrierDismissible: false,
128-
context: context,
129-
builder: (context) {
130-
return AlertDialog(
131-
contentPadding: const EdgeInsets.symmetric(
132-
vertical: 8.0, horizontal: 10.0),
133-
actionsPadding: EdgeInsets.zero,
134-
buttonPadding: EdgeInsets.zero,
135-
content: SizedBox(
136-
width: double.maxFinite,
137-
child: MediaSender.create(
138-
currentConversation: rootContext
139-
.watch<SendMessageBloc>()
140-
.currentConversation,
141-
replyMessage: state.replyMessage),
142-
),
143-
);
144-
},
145-
));
142+
connectionChecker(context, () => showMedia());
146143
},
147144
),
148145
Flexible(
@@ -195,8 +192,36 @@ class _MessageInputState extends State<MessageInput> {
195192
}
196193
}
197194

195+
showMedia([String? path]) {
196+
showDialog(
197+
barrierDismissible: false,
198+
context: context,
199+
builder: (ctx) {
200+
dialogContext = ctx;
201+
return AlertDialog(
202+
contentPadding:
203+
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
204+
actionsPadding: EdgeInsets.zero,
205+
buttonPadding: EdgeInsets.zero,
206+
content: SizedBox(
207+
width: double.maxFinite,
208+
child: MediaSender.create(
209+
currentConversation:
210+
context.watch<SendMessageBloc>().currentConversation,
211+
replyMessage: BlocProvider.of<SendMessageBloc>(context)
212+
.state
213+
.replyMessage,
214+
path: path),
215+
));
216+
},
217+
).then((result) async {
218+
dialogContext = null;
219+
});
220+
}
221+
198222
@override
199223
void dispose() {
224+
if (dialogContext != null && dialogContext!.mounted) dialogContext!.pop();
200225
showFocusNode.dispose();
201226
super.dispose();
202227
}

0 commit comments

Comments
 (0)