mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-10-31 18:24:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			373 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:comunic/helpers/database/conversation_messages_database_helper.dart';
 | |
| import 'package:comunic/helpers/database/conversations_database_helper.dart';
 | |
| import 'package:comunic/helpers/users_helper.dart';
 | |
| import 'package:comunic/lists/conversation_messages_list.dart';
 | |
| import 'package:comunic/lists/conversations_list.dart';
 | |
| 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_message.dart';
 | |
| import 'package:comunic/models/new_conversation_message.dart';
 | |
| import 'package:comunic/utils/account_utils.dart';
 | |
| import 'package:meta/meta.dart';
 | |
| 
 | |
| /// Conversation helper
 | |
| ///
 | |
| /// @author Pierre HUBERT
 | |
| 
 | |
| enum SendMessageResult { SUCCESS, MESSAGE_REJECTED, FAILED }
 | |
| 
 | |
| class ConversationsHelper {
 | |
|   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 {
 | |
|     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();
 | |
| 
 | |
|     if (response.code != 200) return -1;
 | |
| 
 | |
|     return response.getObject()["conversationID"];
 | |
|   }
 | |
| 
 | |
|   /// 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
 | |
|     if (settings.isOwner) {
 | |
|       request.addString("name", settings.hasName ? settings.name : "false");
 | |
|       request.addString("members", settings.members.join(","));
 | |
|     }
 | |
| 
 | |
|     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 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;
 | |
|   }
 | |
| 
 | |
|   /// Download the list of conversations from the server
 | |
|   Future<ConversationsList> downloadList() async {
 | |
|     final response =
 | |
|         await APIRequest(uri: "conversations/getList", needLogin: true).exec();
 | |
| 
 | |
|     if (response.code != 200) return null;
 | |
| 
 | |
|     try {
 | |
|       ConversationsList list = ConversationsList();
 | |
|       response.getArray().forEach((f) => list.add(_apiToConversation(f)));
 | |
| 
 | |
|       // Update the database
 | |
|       await _conversationsDatabaseHelper.clearTable();
 | |
|       await _conversationsDatabaseHelper.insertAll(list);
 | |
| 
 | |
|       return list;
 | |
|     } on Exception catch (e) {
 | |
|       print(e.toString());
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// Get the local list of conversations
 | |
|   Future<ConversationsList> getCachedList() async {
 | |
|     final list = await _conversationsDatabaseHelper.getAll();
 | |
|     list.sort();
 | |
|     return list;
 | |
|   }
 | |
| 
 | |
|   /// 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 {
 | |
|     if (force || !await _conversationsDatabaseHelper.has(id))
 | |
|       return await _downloadSingle(id);
 | |
|     else
 | |
|       return _conversationsDatabaseHelper.get(id);
 | |
|   }
 | |
| 
 | |
|   /// Get the name of a [conversation]. This requires information
 | |
|   /// about the users of this conversation
 | |
|   static String getConversationName(
 | |
|       Conversation conversation, UsersList users) {
 | |
|     if (conversation.hasName) return conversation.name;
 | |
| 
 | |
|     String name = "";
 | |
|     int count = 0;
 | |
|     for (int i = 0; i < 3 && i < conversation.members.length; i++)
 | |
|       if (conversation.members[i] != userID()) {
 | |
|         name += (count > 0 ? ", " : "") +
 | |
|             users.getUser(conversation.members[i]).fullName;
 | |
|         count++;
 | |
|       }
 | |
| 
 | |
|     if (conversation.members.length > 3) name += ", ...";
 | |
| 
 | |
|     return name;
 | |
|   }
 | |
| 
 | |
|   /// 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 {
 | |
|       return int.parse(response.getObject()["conversationsID"][0].toString());
 | |
|     } catch (e) {
 | |
|       e.toString();
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// 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 {
 | |
|     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;
 | |
| 
 | |
|     return ConversationsHelper.getConversationName(conversation, members);
 | |
|   }
 | |
| 
 | |
|   /// Turn an API entry into a [Conversation] object
 | |
|   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: map["members"].map<int>((f) => int.parse(f)).toList(),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   /// 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;
 | |
|   }
 | |
| 
 | |
|   /// 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
 | |
|   Future<ConversationMessagesList> _downloadNewMessagesSingle(
 | |
|       int conversationID,
 | |
|       {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();
 | |
| 
 | |
|     return await _parseConversationMessageFromServer(conversationID, response);
 | |
|   }
 | |
| 
 | |
|   /// 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();
 | |
| 
 | |
|     return await _parseConversationMessageFromServer(conversationID, response);
 | |
|   }
 | |
| 
 | |
|   /// 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(
 | |
|       {@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);
 | |
|   }
 | |
| 
 | |
|   /// 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);
 | |
|   }
 | |
| 
 | |
|   /// Send a new message to the server
 | |
|   Future<SendMessageResult> sendMessage(NewConversationMessage message) async {
 | |
|     final request = APIRequest(
 | |
|       uri: "conversations/sendMessage",
 | |
|       needLogin: true,
 | |
|       args: {
 | |
|         "conversationID": message.conversationID.toString(),
 | |
|         "message": message.hasMessage ? message.message : ""
 | |
|       },
 | |
|     );
 | |
| 
 | |
|     //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)
 | |
|       return SendMessageResult.MESSAGE_REJECTED;
 | |
|     else if (response.code != 200) return SendMessageResult.FAILED;
 | |
| 
 | |
|     return SendMessageResult.SUCCESS;
 | |
|   }
 | |
| 
 | |
|   /// 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);
 | |
|   }
 | |
| 
 | |
|   /// Delete permanently a message specified by its [id]
 | |
|   Future<bool> deleteMessage(int id) async {
 | |
|     // Delete the message online
 | |
|     final response = await APIRequest(
 | |
|         uri: "conversations/deleteMessage",
 | |
|         needLogin: true,
 | |
|         args: {"messageID": id.toString()}).exec();
 | |
| 
 | |
|     if (response.code != 200) return false;
 | |
| 
 | |
|     // Delete the message locally
 | |
|     return await _conversationMessagesDatabaseHelper.delete(id);
 | |
|   }
 | |
| 
 | |
|   /// 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"],
 | |
|       message: map["message"],
 | |
|       imageURL: map["image_path"],
 | |
|     );
 | |
|   }
 | |
| }
 |