1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2025-07-03 15:13:29 +00:00

Start conversation upgrade

This commit is contained in:
2021-03-10 17:54:41 +01:00
parent b094361f5a
commit dacccf57b5
35 changed files with 818 additions and 520 deletions

View File

@ -1,5 +1,5 @@
import 'package:comunic/helpers/database/conversation_messages_database_helper.dart';
import 'package:comunic/helpers/database/conversations_database_helper.dart';
import 'package:comunic/helpers/serialization/conversation_message_serialization_helper.dart';
import 'package:comunic/helpers/serialization/conversations_serialization_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/helpers/websocket_helper.dart';
import 'package:comunic/lists/conversation_messages_list.dart';
@ -9,9 +9,12 @@ import 'package:comunic/lists/users_list.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_member.dart';
import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/new_conversation.dart';
import 'package:comunic/models/new_conversation_message.dart';
import 'package:comunic/models/new_conversation_settings.dart';
import 'package:comunic/models/unread_conversation.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:meta/meta.dart';
@ -25,20 +28,16 @@ enum SendMessageResult { SUCCESS, MESSAGE_REJECTED, FAILED }
class ConversationsHelper {
static final _registeredConversations = Map<int, int>();
final ConversationsDatabaseHelper _conversationsDatabaseHelper =
ConversationsDatabaseHelper();
final ConversationMessagesDatabaseHelper _conversationMessagesDatabaseHelper =
ConversationMessagesDatabaseHelper();
/// Create a new conversation
///
/// Return the ID of the newly created conversation or -1 in case of failure
Future<int> createConversation(Conversation settings) async {
Future<int> createConversation(NewConversation settings) async {
final response =
await APIRequest(uri: "conversations/create", needLogin: true, args: {
"name": settings.hasName ? settings.name : "false",
"follow": settings.following ? "true" : "false",
"name": settings.name ?? "",
"follow": settings.follow ? "true" : "false",
"users": settings.members.join(","),
"color": settings.color ?? ","
}).addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers).exec();
if (response.code != 200) return -1;
@ -48,72 +47,51 @@ class ConversationsHelper {
/// 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"
});
/// Throws in case of failure
Future<void> updateConversation(NewConversationsSettings settings) async {
final request = APIRequest.withLogin("conversations/updateSettings")
.addInt("conversationID", settings.convID)
.addBool("following", settings.following);
if (settings.isOwner || settings.canEveryoneAddMembers)
request.addString("members", settings.members.join(","));
// Update conversation settings
if (settings.isComplete)
request
.addString("name", settings.name ?? "")
.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers)
.addString("color", settings.color ?? "");
// Update all conversation settings, if possible
if (settings.isOwner) {
request.addString("name", settings.hasName ? settings.name : "false");
request.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers);
}
await request.execWithThrow();
final response = await request.exec();
if (response.code != 200) return false;
//Delete old conversation entry from the database
await _conversationsDatabaseHelper.delete(settings.id);
// Success
return true;
// Delete old conversation entry from the database
await ConversationsSerializationHelper()
.removeElement((t) => t.id == settings.convID);
}
/// 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;
}
Future<void> deleteConversation(int id) async =>
await APIRequest.withLogin("conversations/delete")
.addInt("conversationID", id)
.execWithThrow();
/// Download the list of conversations from the server
///
/// Throws an exception in case of failure
Future<ConversationsList> downloadList() async {
final response =
await APIRequest(uri: "conversations/getList", needLogin: true).exec();
await APIRequest.withLogin("conversations/getList").execWithThrow();
if (response.code != 200) return null;
ConversationsList list = ConversationsList();
response.getArray().forEach((f) => list.add(apiToConversation(f)));
try {
ConversationsList list = ConversationsList();
response.getArray().forEach((f) => list.add(apiToConversation(f)));
// Update the database
await ConversationsSerializationHelper().setList(list);
// Update the database
await _conversationsDatabaseHelper.clearTable();
await _conversationsDatabaseHelper.insertAll(list);
return list;
} on Exception catch (e) {
print(e.toString());
return null;
}
return list;
}
/// Get the local list of conversations
Future<ConversationsList> getCachedList() async {
final list = await _conversationsDatabaseHelper.getAll();
final list = await ConversationsSerializationHelper().getList();
list.sort();
return list;
}
@ -124,41 +102,31 @@ class ConversationsHelper {
final response = await APIRequest(
uri: "conversations/getInfoOne",
needLogin: true,
args: {"conversationID": id.toString()}).exec();
if (response.code != 200) return null;
args: {"conversationID": id.toString()}).execWithThrow();
final conversation = apiToConversation(response.getObject());
_conversationsDatabaseHelper.insertOrUpdate(conversation);
await ConversationsSerializationHelper()
.insertOrReplaceElement((c) => c.id == conversation.id, conversation);
return conversation;
} on Exception catch (e) {
print(e.toString());
print("Could not get information about a single conversation !");
} on Exception catch (e, s) {
print("Could not get information about a single conversation ! $e => $s");
return null;
}
}
/// Get information about a single conversation. If [force] is set to false,
/// Get information about a conversation. If [force] is set to false, a
/// 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 {
if (force || !await _conversationsDatabaseHelper.has(id))
return await _downloadSingle(id);
else
return _conversationsDatabaseHelper.get(id);
}
/// Get information about a conversation. The method throws an [Exception] in
/// the information from the server. The method throws an [Exception] in
/// case of failure
///
/// Return value of this method is never null.
Future<Conversation> getSingleOrThrow(int id, {bool force = false}) async {
final conv = await this.getSingle(id, force: force);
if (conv == null)
throw Exception("Could not get information about the conversation!");
return conv;
Future<Conversation> getSingle(int id, {bool force = false}) async {
if (force ||
!await ConversationsSerializationHelper().any((c) => c.id == id))
return await _downloadSingle(id);
else
return await ConversationsSerializationHelper().get(id);
}
/// Get the name of a [conversation]. This requires information
@ -170,9 +138,9 @@ class ConversationsHelper {
String name = "";
int count = 0;
for (int i = 0; i < 3 && i < conversation.members.length; i++)
if (conversation.members[i] != userID()) {
if (conversation.members[i].userID != userID()) {
name += (count > 0 ? ", " : "") +
users.getUser(conversation.members[i]).fullName;
users.getUser(conversation.members[i].userID).fullName;
count++;
}
@ -184,6 +152,8 @@ class ConversationsHelper {
/// 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
///
/// Throws an exception in case of failure
Future<int> getPrivate(int userID, {bool allowCreate = true}) async {
final response = await APIRequest(
uri: "conversations/getPrivate",
@ -192,17 +162,10 @@ class ConversationsHelper {
"otherUser": userID.toString(),
"allowCreate": allowCreate.toString()
},
).exec();
if (response.code != 200) return null;
).execWithThrow();
// Get and return conversation ID
try {
return int.parse(response.getObject()["conversationsID"][0].toString());
} catch (e) {
e.toString();
return null;
}
return int.parse(response.getObject()["conversationsID"][0].toString());
}
/// Asynchronously get the name of the conversation
@ -210,15 +173,13 @@ class ConversationsHelper {
/// Unlike the synchronous method, this method does not need information
/// about the members of the conversation
///
/// Returns null in case of failure
/// Throws an exception in case of failure
static Future<String> getConversationNameAsync(
Conversation conversation) async {
if (conversation.hasName) return conversation.name;
//Get information about the members of the conversation
final members = await UsersHelper().getUsersInfo(conversation.members);
if (members == null) return null;
final members = await UsersHelper().getList(conversation.membersID);
return ConversationsHelper.getConversationName(conversation, members);
}
@ -226,14 +187,18 @@ class ConversationsHelper {
/// Turn an API entry into a [Conversation] object
static Conversation apiToConversation(Map<String, dynamic> map) {
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,
members: List<int>.from(map["members"]),
canEveryoneAddMembers: map["canEveryoneAddMembers"],
id: map["id"],
lastActivity: map["last_activity"],
name: map["name"],
color: map["color"],
logoURL: map["logo"],
groupID: map["group_id"],
members: map["members"]
.cast<Map<String, dynamic>>()
.map(apiToConversationMember)
.toList()
.cast<ConversationMember>(),
canEveryoneAddMembers: map["can_everyone_add_members"],
callCapabilities: map["can_have_video_call"]
? CallCapabilities.VIDEO
: (map["can_have_call"]
@ -242,10 +207,21 @@ class ConversationsHelper {
isHavingCall: map["has_call_now"]);
}
static ConversationMember apiToConversationMember(Map<String, dynamic> map) =>
ConversationMember(
userID: map["user_id"],
lastMessageSeen: map["last_message_seen"],
lastAccessTime: map["last_access"],
following: map["following"],
isAdmin: map["is_admin"],
);
/// Parse a list of messages given by the server
///
/// Throws an exception in case of failure
Future<ConversationMessagesList> _parseConversationMessageFromServer(
int conversationID, APIResponse response) async {
if (response.code != 200) return null;
response.assertOk();
// Parse the response of the server
ConversationMessagesList list = ConversationMessagesList();
@ -256,7 +232,8 @@ class ConversationsHelper {
});
// Save messages in the cache
_conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
await ConversationsMessagesSerializationHelper(conversationID)
.insertOrReplaceAll(list);
return list;
}
@ -265,6 +242,8 @@ class ConversationsHelper {
///
/// Set [lastMessageID] to 0 to specify that we do not have any message of the
/// conversation yet or another value else
///
/// Throws an exception in case of failure
Future<ConversationMessagesList> _downloadNewMessagesSingle(
int conversationID,
{int lastMessageID = 0}) async {
@ -275,26 +254,26 @@ class ConversationsHelper {
args: {
"conversationID": conversationID.toString(),
"last_message_id": lastMessageID.toString()
}).exec();
}).execWithThrow();
return await _parseConversationMessageFromServer(conversationID, response);
}
/// Get older messages for a given conversation from an online source
///
/// Throws in case of failure
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();
final response =
await APIRequest.withLogin("conversations/get_older_messages", args: {
"conversationID": conversationID.toString(),
"oldest_message_id": oldestMessagesID.toString(),
"limit": limit.toString()
}).execWithThrow();
return await _parseConversationMessageFromServer(conversationID, response);
}
@ -304,6 +283,8 @@ class ConversationsHelper {
/// 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
///
/// Throws in case of failure
Future<ConversationMessagesList> getNewMessages(
{@required int conversationID,
int lastMessageID = 0,
@ -312,16 +293,8 @@ class ConversationsHelper {
return await _downloadNewMessagesSingle(conversationID,
lastMessageID: lastMessageID);
else
return await _conversationMessagesDatabaseHelper
.getAllMessagesConversations(conversationID,
lastMessageID: lastMessageID);
}
/// 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);
return await ConversationsMessagesSerializationHelper(conversationID)
.getList();
}
/// Send a new message to the server
@ -353,14 +326,13 @@ class ConversationsHelper {
}
/// Save / Update a message into the database
Future<void> saveMessage(ConversationMessage msg) async {
await _conversationMessagesDatabaseHelper.insertOrUpdate(msg);
}
Future<void> saveMessage(ConversationMessage msg) async =>
await ConversationsMessagesSerializationHelper(msg.convID)
.insertOrReplace(msg);
/// Remove a message from the database
Future<void> removeMessage(int msgID) async {
await _conversationMessagesDatabaseHelper.delete(msgID);
}
Future<void> removeMessage(ConversationMessage msg) async =>
await ConversationsMessagesSerializationHelper(msg.convID).remove(msg);
/// Update a message content
Future<bool> updateMessage(int id, String newContent) async {
@ -397,11 +369,8 @@ class ConversationsHelper {
return UnreadConversationsList()
..addAll(list.map((f) => UnreadConversation(
id: f["id"],
convName: f["conv_name"],
lastActive: f["last_active"],
userID: f["userID"],
message: f["message"],
conv: apiToConversation(f["conv"]),
message: apiToConversationMessage(f["message"]),
)));
}
@ -432,13 +401,38 @@ class ConversationsHelper {
static ConversationMessage apiToConversationMessage(
Map<String, dynamic> map,
) {
var file;
if (map["file"] != null) {
final fileMap = map["file"];
file = ConversationMessageFile(
url: fileMap["url"],
size: fileMap["size"],
name: fileMap["name"],
thumbnail: fileMap["thumbnail"],
type: fileMap["type"],
);
}
var serverMessage;
if (map["server_message"] != null) {
final srvMessageMap = map["server_message"];
serverMessage = ConversationServerMessage(
type: srvMessageMap["type"],
userID: srvMessageMap["user_id"],
userWhoAdded: srvMessageMap["user_who_added"],
userAdded: srvMessageMap["user_added"],
userWhoRemoved: srvMessageMap["user_who_removed"],
userRemoved: srvMessageMap["user_removed"],
);
}
return ConversationMessage(
id: map["ID"],
conversationID: map["convID"],
userID: map["ID_user"],
timeInsert: map["time_insert"],
message: DisplayedString(map["message"]),
imageURL: map["image_path"],
);
id: map["id"],
convID: map["conv_id"],
userID: map["user_id"],
timeSent: map["time_sent"],
message: DisplayedString(map["message"] ?? ""),
file: file,
serverMessage: serverMessage);
}
}