2019-04-27 06:42:27 +00:00
|
|
|
import 'package:comunic/helpers/database/conversation_messages_database_helper.dart';
|
2019-04-24 12:25:46 +00:00
|
|
|
import 'package:comunic/helpers/database/conversations_database_helper.dart';
|
2019-04-24 15:46:25 +00:00
|
|
|
import 'package:comunic/helpers/users_helper.dart';
|
2019-04-25 06:56:16 +00:00
|
|
|
import 'package:comunic/lists/conversation_messages_list.dart';
|
2019-04-23 15:29:58 +00:00
|
|
|
import 'package:comunic/lists/conversations_list.dart';
|
|
|
|
import 'package:comunic/lists/users_list.dart';
|
2019-04-23 12:35:41 +00:00
|
|
|
import 'package:comunic/models/api_request.dart';
|
2019-04-25 18:14:19 +00:00
|
|
|
import 'package:comunic/models/api_response.dart';
|
2019-04-23 12:35:41 +00:00
|
|
|
import 'package:comunic/models/conversation.dart';
|
2019-04-25 06:56:16 +00:00
|
|
|
import 'package:comunic/models/conversation_message.dart';
|
2020-04-16 13:10:47 +00:00
|
|
|
import 'package:comunic/models/displayed_content.dart';
|
2019-04-25 07:48:52 +00:00
|
|
|
import 'package:comunic/models/new_conversation_message.dart';
|
2019-04-23 16:15:29 +00:00
|
|
|
import 'package:comunic/utils/account_utils.dart';
|
2019-04-27 06:51:58 +00:00
|
|
|
import 'package:meta/meta.dart';
|
2019-04-23 12:35:41 +00:00
|
|
|
|
|
|
|
/// Conversation helper
|
|
|
|
///
|
|
|
|
/// @author Pierre HUBERT
|
|
|
|
|
2019-04-25 07:48:52 +00:00
|
|
|
enum SendMessageResult { SUCCESS, MESSAGE_REJECTED, FAILED }
|
|
|
|
|
2019-04-23 12:35:41 +00:00
|
|
|
class ConversationsHelper {
|
2019-04-24 12:25:46 +00:00
|
|
|
final ConversationsDatabaseHelper _conversationsDatabaseHelper =
|
|
|
|
ConversationsDatabaseHelper();
|
2019-04-27 06:42:27 +00:00
|
|
|
final ConversationMessagesDatabaseHelper _conversationMessagesDatabaseHelper =
|
|
|
|
ConversationMessagesDatabaseHelper();
|
2019-04-24 12:25:46 +00:00
|
|
|
|
2019-04-27 14:23:08 +00:00
|
|
|
/// Create a new conversation
|
|
|
|
///
|
|
|
|
/// Return the ID of the newly created conversation or -1 in case of failure
|
2019-05-01 07:24:50 +00:00
|
|
|
Future<int> createConversation(Conversation settings) async {
|
2019-04-27 16:29:30 +00:00
|
|
|
final response =
|
|
|
|
await APIRequest(uri: "conversations/create", needLogin: true, args: {
|
|
|
|
"name": settings.hasName ? settings.name : "false",
|
|
|
|
"follow": settings.following ? "true" : "false",
|
|
|
|
"users": settings.members.join(",")
|
|
|
|
}).exec();
|
2019-04-27 14:23:08 +00:00
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
if (response.code != 200) return -1;
|
2019-04-27 14:23:08 +00:00
|
|
|
|
|
|
|
return response.getObject()["conversationID"];
|
|
|
|
}
|
|
|
|
|
2019-05-01 07:24:50 +00:00
|
|
|
/// Update an existing conversation
|
|
|
|
///
|
|
|
|
/// Returns a boolean depending of the success of the operation
|
|
|
|
Future<bool> updateConversation(Conversation settings) async {
|
|
|
|
final request =
|
|
|
|
APIRequest(uri: "conversations/updateSettings", needLogin: true, args: {
|
|
|
|
"conversationID": settings.id.toString(),
|
|
|
|
"following": settings.following ? "true" : "false"
|
|
|
|
});
|
|
|
|
|
|
|
|
// Update all conversation settings, if possible
|
2019-05-01 08:52:10 +00:00
|
|
|
if (settings.isOwner) {
|
2019-05-01 07:24:50 +00:00
|
|
|
request.addString("name", settings.hasName ? settings.name : "false");
|
|
|
|
request.addString("members", settings.members.join(","));
|
|
|
|
}
|
|
|
|
|
|
|
|
final response = await request.exec();
|
|
|
|
|
2019-05-02 06:09:40 +00:00
|
|
|
if (response.code != 200) return false;
|
2019-05-01 09:30:55 +00:00
|
|
|
|
|
|
|
//Delete old conversation entry from the database
|
|
|
|
await _conversationsDatabaseHelper.delete(settings.id);
|
|
|
|
|
|
|
|
// Success
|
|
|
|
return true;
|
2019-05-01 07:24:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-01 08:52:10 +00:00
|
|
|
/// Delete a conversation specified by its [id]
|
|
|
|
Future<bool> deleteConversation(int id) async {
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/delete",
|
|
|
|
needLogin: true,
|
|
|
|
args: {
|
|
|
|
"conversationID": id.toString(),
|
|
|
|
},
|
|
|
|
).exec();
|
|
|
|
|
|
|
|
return response.code == 200;
|
|
|
|
}
|
|
|
|
|
2019-04-23 12:35:41 +00:00
|
|
|
/// Download the list of conversations from the server
|
2019-04-23 15:29:58 +00:00
|
|
|
Future<ConversationsList> downloadList() async {
|
2019-04-23 12:35:41 +00:00
|
|
|
final response =
|
|
|
|
await APIRequest(uri: "conversations/getList", needLogin: true).exec();
|
|
|
|
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
|
|
|
|
try {
|
2019-04-23 15:29:58 +00:00
|
|
|
ConversationsList list = ConversationsList();
|
2019-04-24 15:46:25 +00:00
|
|
|
response.getArray().forEach((f) => list.add(_apiToConversation(f)));
|
2019-04-23 12:35:41 +00:00
|
|
|
|
2019-04-24 12:25:46 +00:00
|
|
|
// Update the database
|
|
|
|
await _conversationsDatabaseHelper.clearTable();
|
|
|
|
await _conversationsDatabaseHelper.insertAll(list);
|
|
|
|
|
2019-04-23 12:35:41 +00:00
|
|
|
return list;
|
2019-04-23 15:29:58 +00:00
|
|
|
} on Exception catch (e) {
|
2019-04-23 12:35:41 +00:00
|
|
|
print(e.toString());
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2019-04-23 15:29:58 +00:00
|
|
|
|
2019-04-24 13:03:36 +00:00
|
|
|
/// Get the local list of conversations
|
|
|
|
Future<ConversationsList> getCachedList() async {
|
|
|
|
final list = await _conversationsDatabaseHelper.getAll();
|
|
|
|
list.sort();
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2019-04-24 15:46:25 +00:00
|
|
|
/// Get information about a single conversation specified by its [id]
|
|
|
|
Future<Conversation> _downloadSingle(int id) async {
|
|
|
|
try {
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/getInfoOne",
|
|
|
|
needLogin: true,
|
|
|
|
args: {"conversationID": id.toString()}).exec();
|
|
|
|
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
|
|
|
|
final conversation = _apiToConversation(response.getObject());
|
|
|
|
_conversationsDatabaseHelper.insertOrUpdate(conversation);
|
|
|
|
return conversation;
|
|
|
|
} on Exception catch (e) {
|
|
|
|
print(e.toString());
|
|
|
|
print("Could not get information about a single conversation !");
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get information about a single conversation. If [force] is set to false,
|
|
|
|
/// cached version of the conversation will be used, else it will always get
|
|
|
|
/// the information from the server
|
|
|
|
Future<Conversation> getSingle(int id, {bool force = false}) async {
|
2019-04-25 06:56:16 +00:00
|
|
|
if (force || !await _conversationsDatabaseHelper.has(id))
|
2019-04-24 15:46:25 +00:00
|
|
|
return await _downloadSingle(id);
|
|
|
|
else
|
|
|
|
return _conversationsDatabaseHelper.get(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the name of a [conversation]. This requires information
|
|
|
|
/// about the users of this conversation
|
2019-04-23 16:15:29 +00:00
|
|
|
static String getConversationName(
|
|
|
|
Conversation conversation, UsersList users) {
|
2019-04-25 09:14:05 +00:00
|
|
|
if (conversation.hasName) return conversation.name;
|
2019-04-23 15:29:58 +00:00
|
|
|
|
|
|
|
String name = "";
|
2019-04-23 16:15:29 +00:00
|
|
|
int count = 0;
|
2019-04-23 15:29:58 +00:00
|
|
|
for (int i = 0; i < 3 && i < conversation.members.length; i++)
|
2019-04-23 16:15:29 +00:00
|
|
|
if (conversation.members[i] != userID()) {
|
|
|
|
name += (count > 0 ? ", " : "") +
|
2019-04-24 12:25:46 +00:00
|
|
|
users.getUser(conversation.members[i]).fullName;
|
2019-04-23 16:15:29 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
2019-04-24 12:25:46 +00:00
|
|
|
if (conversation.members.length > 3) name += ", ...";
|
2019-04-23 15:29:58 +00:00
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
2019-04-24 15:46:25 +00:00
|
|
|
|
2019-05-02 06:09:40 +00:00
|
|
|
/// Search and return a private conversation with a given [userID]. If such
|
|
|
|
/// conversation does not exists, it is created if [allowCreate] is set to
|
|
|
|
/// true
|
|
|
|
Future<int> getPrivate(int userID, {bool allowCreate = true}) async {
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/getPrivate",
|
|
|
|
needLogin: true,
|
|
|
|
args: {
|
|
|
|
"otherUser": userID.toString(),
|
|
|
|
"allowCreate": allowCreate.toString()
|
|
|
|
},
|
|
|
|
).exec();
|
|
|
|
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
|
|
|
|
// Get and return conversation ID
|
|
|
|
try {
|
2019-05-02 06:19:51 +00:00
|
|
|
return int.parse(response.getObject()["conversationsID"][0].toString());
|
2019-05-02 06:09:40 +00:00
|
|
|
} catch (e) {
|
|
|
|
e.toString();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-24 15:46:25 +00:00
|
|
|
/// Asynchronously get the name fo the conversation
|
|
|
|
///
|
|
|
|
/// Unlike the synchronous method, this method does not need information
|
|
|
|
/// about the members of the conversation
|
|
|
|
///
|
|
|
|
/// Returns null in case of failure
|
|
|
|
static Future<String> getConversationNameAsync(
|
|
|
|
Conversation conversation) async {
|
2019-04-25 09:14:05 +00:00
|
|
|
if (conversation.hasName) return conversation.name;
|
2019-04-24 15:46:25 +00:00
|
|
|
|
|
|
|
//Get information about the members of the conversation
|
|
|
|
final members = await UsersHelper().getUsersInfo(conversation.members);
|
|
|
|
|
|
|
|
if (members == null) return null;
|
|
|
|
|
|
|
|
return ConversationsHelper.getConversationName(conversation, members);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Turn an API entry into a [Conversation] object
|
2019-04-25 06:56:16 +00:00
|
|
|
Conversation _apiToConversation(Map<String, dynamic> map) {
|
2019-04-24 15:46:25 +00:00
|
|
|
return Conversation(
|
|
|
|
id: map["ID"],
|
|
|
|
ownerID: map["ID_owner"],
|
|
|
|
lastActive: map["last_active"],
|
|
|
|
name: map["name"] == false ? null : map["name"],
|
|
|
|
following: map["following"] == 1,
|
|
|
|
sawLastMessage: map["saw_last_message"] == 1,
|
2020-03-30 20:44:08 +00:00
|
|
|
members: List<int>.from(map["members"]),
|
2019-04-24 15:46:25 +00:00
|
|
|
);
|
|
|
|
}
|
2019-04-25 06:56:16 +00:00
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
/// Parse a list of messages given by the server
|
|
|
|
Future<ConversationMessagesList> _parseConversationMessageFromServer(
|
|
|
|
int conversationID, APIResponse response) async {
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
|
|
|
|
// Parse the response of the server
|
|
|
|
ConversationMessagesList list = ConversationMessagesList();
|
|
|
|
response.getArray().forEach((f) {
|
|
|
|
list.add(
|
|
|
|
_apiToConversationMessage(
|
|
|
|
conversationID: conversationID,
|
|
|
|
map: f,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Save messages in the cache
|
|
|
|
_conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2019-04-25 06:56:16 +00:00
|
|
|
/// Refresh the list of messages of a conversation
|
|
|
|
///
|
|
|
|
/// Set [lastMessageID] to 0 to specify that we do not have any message of the
|
|
|
|
/// conversation yet or another value else
|
2019-04-27 06:51:58 +00:00
|
|
|
Future<ConversationMessagesList> _downloadNewMessagesSingle(
|
|
|
|
int conversationID,
|
2019-04-25 06:56:16 +00:00
|
|
|
{int lastMessageID = 0}) async {
|
|
|
|
// Execute the request on the server
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/refresh_single",
|
|
|
|
needLogin: true,
|
|
|
|
args: {
|
|
|
|
"conversationID": conversationID.toString(),
|
|
|
|
"last_message_id": lastMessageID.toString()
|
|
|
|
}).exec();
|
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
return await _parseConversationMessageFromServer(conversationID, response);
|
|
|
|
}
|
2019-04-25 06:56:16 +00:00
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
/// Get older messages for a given conversation from an online source
|
|
|
|
Future<ConversationMessagesList> getOlderMessages({
|
|
|
|
@required int conversationID,
|
|
|
|
@required int oldestMessagesID,
|
|
|
|
int limit = 15,
|
|
|
|
}) async {
|
|
|
|
// Perform the request online
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/get_older_messages",
|
|
|
|
needLogin: true,
|
|
|
|
args: {
|
|
|
|
"conversationID": conversationID.toString(),
|
|
|
|
"oldest_message_id": oldestMessagesID.toString(),
|
|
|
|
"limit": limit.toString()
|
|
|
|
}).exec();
|
2019-04-27 06:42:27 +00:00
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
return await _parseConversationMessageFromServer(conversationID, response);
|
2019-04-25 06:56:16 +00:00
|
|
|
}
|
2019-04-25 07:48:52 +00:00
|
|
|
|
2019-04-27 06:51:58 +00:00
|
|
|
/// Get new messages for a given conversation
|
|
|
|
///
|
|
|
|
/// If [lastMessageID] is set to 0 then we retrieve the last messages of
|
|
|
|
/// the conversation.
|
|
|
|
/// Otherwise [lastMessageID] contains the ID of the last known message
|
|
|
|
Future<ConversationMessagesList> getNewMessages(
|
2019-04-27 07:24:29 +00:00
|
|
|
{@required int conversationID,
|
|
|
|
int lastMessageID = 0,
|
|
|
|
bool online = true}) async {
|
|
|
|
if (online)
|
|
|
|
return await _downloadNewMessagesSingle(conversationID,
|
|
|
|
lastMessageID: lastMessageID);
|
|
|
|
else
|
|
|
|
return await _conversationMessagesDatabaseHelper
|
|
|
|
.getAllMessagesConversations(conversationID,
|
|
|
|
lastMessageID: lastMessageID);
|
2019-04-27 06:51:58 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 08:24:38 +00:00
|
|
|
/// Get a single conversation message from the local database
|
|
|
|
///
|
|
|
|
/// Returns the message if found or null in case of failure
|
|
|
|
Future<ConversationMessage> getSingleMessageFromCache(int messageID) async {
|
|
|
|
return await _conversationMessagesDatabaseHelper.get(messageID);
|
|
|
|
}
|
|
|
|
|
2019-04-25 07:48:52 +00:00
|
|
|
/// Send a new message to the server
|
|
|
|
Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
|
2019-04-25 18:14:19 +00:00
|
|
|
final request = APIRequest(
|
2019-04-25 07:48:52 +00:00
|
|
|
uri: "conversations/sendMessage",
|
|
|
|
needLogin: true,
|
|
|
|
args: {
|
|
|
|
"conversationID": message.conversationID.toString(),
|
2019-04-25 18:14:19 +00:00
|
|
|
"message": message.hasMessage ? message.message : ""
|
2019-04-25 07:48:52 +00:00
|
|
|
},
|
2019-04-25 18:14:19 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
//Check for image
|
2019-04-27 06:42:27 +00:00
|
|
|
if (message.hasImage) request.addFile("image", message.image);
|
2019-04-25 18:14:19 +00:00
|
|
|
|
|
|
|
//Send the message
|
|
|
|
APIResponse response;
|
2019-04-27 06:42:27 +00:00
|
|
|
if (!message.hasImage)
|
2019-04-25 18:14:19 +00:00
|
|
|
response = await request.exec();
|
|
|
|
else
|
|
|
|
response = await request.execWithFiles();
|
2019-04-25 07:48:52 +00:00
|
|
|
|
2019-04-27 06:42:27 +00:00
|
|
|
if (response.code == 401)
|
2019-04-25 07:48:52 +00:00
|
|
|
return SendMessageResult.MESSAGE_REJECTED;
|
2019-04-27 06:42:27 +00:00
|
|
|
else if (response.code != 200) return SendMessageResult.FAILED;
|
2019-04-25 07:48:52 +00:00
|
|
|
|
|
|
|
return SendMessageResult.SUCCESS;
|
|
|
|
}
|
2019-04-27 16:29:30 +00:00
|
|
|
|
2019-05-04 08:24:38 +00:00
|
|
|
/// Update a message content
|
|
|
|
Future<bool> updateMessage(int id, String newContent) async {
|
|
|
|
final response = await APIRequest(
|
|
|
|
uri: "conversations/updateMessage",
|
|
|
|
needLogin: true,
|
|
|
|
args: {"messageID": id.toString(), "content": newContent}).exec();
|
|
|
|
|
|
|
|
if (response.code != 200) return false;
|
|
|
|
|
|
|
|
// Update the message content locally
|
|
|
|
return await _conversationMessagesDatabaseHelper.updateMessageContent(
|
|
|
|
id: id, newContent: newContent);
|
|
|
|
}
|
|
|
|
|
2019-05-04 06:58:14 +00:00
|
|
|
/// Delete permanently a message specified by its [id]
|
|
|
|
Future<bool> deleteMessage(int id) async {
|
|
|
|
// Delete the message online
|
|
|
|
final response = await APIRequest(
|
2019-05-04 08:24:38 +00:00
|
|
|
uri: "conversations/deleteMessage",
|
|
|
|
needLogin: true,
|
|
|
|
args: {"messageID": id.toString()}).exec();
|
2019-05-04 06:58:14 +00:00
|
|
|
|
2019-05-04 08:24:38 +00:00
|
|
|
if (response.code != 200) return false;
|
2019-05-04 06:58:14 +00:00
|
|
|
|
|
|
|
// Delete the message locally
|
|
|
|
return await _conversationMessagesDatabaseHelper.delete(id);
|
|
|
|
}
|
|
|
|
|
2019-04-27 16:29:30 +00:00
|
|
|
/// Turn an API response into a ConversationMessage object
|
|
|
|
ConversationMessage _apiToConversationMessage({
|
|
|
|
@required int conversationID,
|
|
|
|
@required Map<String, dynamic> map,
|
|
|
|
}) {
|
|
|
|
return ConversationMessage(
|
|
|
|
id: map["ID"],
|
|
|
|
conversationID: conversationID,
|
|
|
|
userID: map["ID_user"],
|
|
|
|
timeInsert: map["time_insert"],
|
2020-04-16 13:10:47 +00:00
|
|
|
message: DisplayedString(map["message"]),
|
2019-04-27 16:29:30 +00:00
|
|
|
imageURL: map["image_path"],
|
|
|
|
);
|
|
|
|
}
|
2019-04-23 12:35:41 +00:00
|
|
|
}
|