1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 12:59:21 +00:00

Can send images in conversations

This commit is contained in:
Pierre HUBERT 2019-04-25 20:14:19 +02:00
parent 517b97f12b
commit 18a8ddfbf4
7 changed files with 111 additions and 42 deletions

View File

@ -1,11 +1,10 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:comunic/helpers/account_credentials_helper.dart'; import 'package:comunic/helpers/account_credentials_helper.dart';
import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/api_response.dart'; import 'package:comunic/models/api_response.dart';
import 'package:comunic/models/config.dart'; import 'package:comunic/models/config.dart';
import 'package:http/http.dart' as http; import 'package:dio/dio.dart';
/// API Helper /// API Helper
/// ///
@ -16,7 +15,7 @@ class APIHelper {
/// ///
/// This method should never throw but the response code of the [APIResponse] /// This method should never throw but the response code of the [APIResponse]
/// should be verified before accessing response content /// should be verified before accessing response content
Future<APIResponse> exec(APIRequest request) async { Future<APIResponse> exec(APIRequest request, {bool multipart = false}) async {
try { try {
//Add API tokens //Add API tokens
request.addString("serviceName", config().serviceName); request.addString("serviceName", config().serviceName);
@ -38,20 +37,39 @@ class APIHelper {
else else
url = Uri.https(config().apiServerName, path); url = Uri.https(config().apiServerName, path);
final response = await http.post( final data = FormData.from(request.args);
// Process files (if required)
if (multipart)
request.files.forEach(
(k, v) => data.add(k, UploadFileInfo(v, v.path.split("/").last)));
// Execute the request
final response = await Dio().post(
url.toString(), url.toString(),
body: request.args, data: data,
encoding: utf8 options: Options(
receiveDataWhenStatusError: true,
validateStatus: (s) => true,
responseType: ResponseType.plain,
),
); );
if (response.statusCode != HttpStatus.ok) if (response.statusCode != HttpStatus.ok)
return APIResponse(response.statusCode, null); return APIResponse(response.statusCode, null);
return APIResponse(response.statusCode, utf8.decode(response.bodyBytes)); return APIResponse(response.statusCode, response.data);
} on Exception catch (e) { } on Exception catch (e) {
print(e.toString()); print(e.toString());
print("Could not execute a request!"); print("Could not execute a request!");
return APIResponse(-1, null); return APIResponse(-1, null);
} }
} }
/// Same as exec, but also allows to send files
///
/// Warning ! Currently the response body to such requests is always null !
Future<APIResponse> execWithFiles(APIRequest request) async {
return await exec(request, multipart: true);
}
} }

View File

@ -4,6 +4,7 @@ import 'package:comunic/lists/conversation_messages_list.dart';
import 'package:comunic/lists/conversations_list.dart'; import 'package:comunic/lists/conversations_list.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/api_response.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/new_conversation_message.dart'; import 'package:comunic/models/new_conversation_message.dart';
@ -162,14 +163,25 @@ class ConversationsHelper {
/// Send a new message to the server /// Send a new message to the server
Future<SendMessageResult> sendMessage(NewConversationMessage message) async { Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
final response = await APIRequest( final request = APIRequest(
uri: "conversations/sendMessage", uri: "conversations/sendMessage",
needLogin: true, needLogin: true,
args: { args: {
"conversationID": message.conversationID.toString(), "conversationID": message.conversationID.toString(),
"message": message.message "message": message.hasMessage ? message.message : ""
}, },
).exec(); );
//Check for image
if(message.hasImage)
request.addFile("image", message.image);
//Send the message
APIResponse response;
if(!message.hasImage)
response = await request.exec();
else
response = await request.execWithFiles();
if(response.code == 401) if(response.code == 401)
return SendMessageResult.MESSAGE_REJECTED; return SendMessageResult.MESSAGE_REJECTED;

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:comunic/helpers/api_helper.dart'; import 'package:comunic/helpers/api_helper.dart';
import 'package:comunic/models/api_response.dart'; import 'package:comunic/models/api_response.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
@ -12,6 +14,7 @@ class APIRequest {
final String uri; final String uri;
final bool needLogin; final bool needLogin;
Map<String, String> args; Map<String, String> args;
Map<String, File> files = Map();
APIRequest({@required this.uri, this.needLogin = false, this.args}) APIRequest({@required this.uri, this.needLogin = false, this.args})
: assert(uri != null), : assert(uri != null),
@ -26,6 +29,11 @@ class APIRequest {
void addBool(String name, bool value) => void addBool(String name, bool value) =>
args[name] = value ? "true" : "false"; args[name] = value ? "true" : "false";
void addFile(String name, File file) => files[name] = file;
/// Execute the request /// Execute the request
Future<APIResponse> exec() async => APIHelper().exec(this); Future<APIResponse> exec() async => APIHelper().exec(this);
/// Execute the request with files
Future<APIResponse> execWithFiles() async => APIHelper().execWithFiles(this);
} }

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
/// New conversation message model /// New conversation message model
@ -9,9 +11,15 @@ import 'package:meta/meta.dart';
class NewConversationMessage { class NewConversationMessage {
final int conversationID; final int conversationID;
final String message; final String message;
final File image;
NewConversationMessage({ NewConversationMessage({
@required this.conversationID, @required this.conversationID,
@required this.message, @required this.message,
}) : assert(conversationID != null); this.image
}) : assert(conversationID != null),
assert(image != null || message != null);
bool get hasMessage => message != null;
bool get hasImage => image != null;
} }

View File

@ -86,15 +86,36 @@ class _ConversationScreenState extends State<ConversationScreen> {
/// Pick and send an image /// Pick and send an image
Future<void> _sendImage(BuildContext context) async { Future<void> _sendImage(BuildContext context) async {
final image = await pickImage(context); final image = await pickImage(context);
if (image == null) return null;
_submitMessage(
context,
NewConversationMessage(
conversationID: widget.conversationID,
message: null,
image: image,
),
);
} }
/// Send a new message /// Send a new text message
Future<void> _submitMessage(BuildContext context, String content) async { Future<void> _submitTextMessage(BuildContext context, String content) async {
_submitMessage(
context,
NewConversationMessage(
conversationID: widget.conversationID,
message: content,
),
);
}
/// Submit a new message
Future<void> _submitMessage(
BuildContext context, NewConversationMessage message) async {
//Send the message //Send the message
_setSending(true); _setSending(true);
final result = await _conversationsHelper.sendMessage( final result = await _conversationsHelper.sendMessage(message);
NewConversationMessage(
conversationID: widget.conversationID, message: content));
_setSending(false); _setSending(false);
//Check the result of the operation //Check the result of the operation
@ -148,14 +169,14 @@ class _ConversationScreenState extends State<ConversationScreen> {
children: <Widget>[ children: <Widget>[
// Image area // Image area
new Container( new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0), margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: new IconButton( child: new IconButton(
icon: new Icon( icon: new Icon(
Icons.photo_camera, Icons.photo_camera,
color: Theme.of(context).accentColor, color: Theme.of(context).accentColor,
), ),
onPressed: () => _sendImage(context)), onPressed: () => _sendImage(context)),
), ),
// Message area // Message area
new Flexible( new Flexible(
@ -166,8 +187,9 @@ class _ConversationScreenState extends State<ConversationScreen> {
_isMessageValid = messageText.length > 4; _isMessageValid = messageText.length > 4;
}); });
}, },
onSubmitted: onSubmitted: _isMessageValid
_isMessageValid ? (s) => _submitMessage(context, s) : null, ? (s) => _submitTextMessage(context, s)
: null,
decoration: decoration:
new InputDecoration.collapsed(hintText: tr("Send a message")), new InputDecoration.collapsed(hintText: tr("Send a message")),
), ),
@ -184,7 +206,8 @@ class _ConversationScreenState extends State<ConversationScreen> {
: Theme.of(context).disabledColor, : Theme.of(context).disabledColor,
), ),
onPressed: !_isSendingMessage && _isMessageValid onPressed: !_isSendingMessage && _isMessageValid
? () => _submitMessage(context, _textEditingController.text) ? () =>
_submitTextMessage(context, _textEditingController.text)
: null, : null,
), ),
), ),

View File

@ -29,6 +29,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.11" version: "1.14.11"
cookie_jar:
dependency: transitive
description:
name: cookie_jar
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -36,6 +43,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.2" version: "0.1.2"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -46,20 +60,6 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.0+2"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
image_picker: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -34,7 +34,7 @@ dependencies:
image_picker: ^0.5.4+3 image_picker: ^0.5.4+3
# The HTTP client is used to make requests on the Comunic API # The HTTP client is used to make requests on the Comunic API
http: ^0.12.0+2 dio: ^2.1.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: