mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 04:49:21 +00:00
Can send images in conversations
This commit is contained in:
parent
517b97f12b
commit
18a8ddfbf4
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
28
pubspec.lock
28
pubspec.lock
@ -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:
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user