mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-03 11:44:18 +00:00 
			
		
		
		
	Start conversation upgrade
This commit is contained in:
		
							
								
								
									
										6
									
								
								lib/constants.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								lib/constants.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					/// Comunic mobile constants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Data serialisation directory
 | 
				
			||||||
 | 
					const SERIALIZATION_DIRECTORY = "serialization";
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import 'package:comunic/helpers/database/conversation_messages_database_helper.dart';
 | 
					import 'package:comunic/helpers/serialization/conversation_message_serialization_helper.dart';
 | 
				
			||||||
import 'package:comunic/helpers/database/conversations_database_helper.dart';
 | 
					import 'package:comunic/helpers/serialization/conversations_serialization_helper.dart';
 | 
				
			||||||
import 'package:comunic/helpers/users_helper.dart';
 | 
					import 'package:comunic/helpers/users_helper.dart';
 | 
				
			||||||
import 'package:comunic/helpers/websocket_helper.dart';
 | 
					import 'package:comunic/helpers/websocket_helper.dart';
 | 
				
			||||||
import 'package:comunic/lists/conversation_messages_list.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_request.dart';
 | 
				
			||||||
import 'package:comunic/models/api_response.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_member.dart';
 | 
				
			||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
import 'package:comunic/models/displayed_content.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_message.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/new_conversation_settings.dart';
 | 
				
			||||||
import 'package:comunic/models/unread_conversation.dart';
 | 
					import 'package:comunic/models/unread_conversation.dart';
 | 
				
			||||||
import 'package:comunic/utils/account_utils.dart';
 | 
					import 'package:comunic/utils/account_utils.dart';
 | 
				
			||||||
import 'package:meta/meta.dart';
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
@@ -25,20 +28,16 @@ enum SendMessageResult { SUCCESS, MESSAGE_REJECTED, FAILED }
 | 
				
			|||||||
class ConversationsHelper {
 | 
					class ConversationsHelper {
 | 
				
			||||||
  static final _registeredConversations = Map<int, int>();
 | 
					  static final _registeredConversations = Map<int, int>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final ConversationsDatabaseHelper _conversationsDatabaseHelper =
 | 
					 | 
				
			||||||
      ConversationsDatabaseHelper();
 | 
					 | 
				
			||||||
  final ConversationMessagesDatabaseHelper _conversationMessagesDatabaseHelper =
 | 
					 | 
				
			||||||
      ConversationMessagesDatabaseHelper();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Create a new conversation
 | 
					  /// Create a new conversation
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Return the ID of the newly created conversation or -1 in case of failure
 | 
					  /// 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 =
 | 
					    final response =
 | 
				
			||||||
        await APIRequest(uri: "conversations/create", needLogin: true, args: {
 | 
					        await APIRequest(uri: "conversations/create", needLogin: true, args: {
 | 
				
			||||||
      "name": settings.hasName ? settings.name : "false",
 | 
					      "name": settings.name ?? "",
 | 
				
			||||||
      "follow": settings.following ? "true" : "false",
 | 
					      "follow": settings.follow ? "true" : "false",
 | 
				
			||||||
      "users": settings.members.join(","),
 | 
					      "users": settings.members.join(","),
 | 
				
			||||||
 | 
					      "color": settings.color ?? ","
 | 
				
			||||||
    }).addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers).exec();
 | 
					    }).addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers).exec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.code != 200) return -1;
 | 
					    if (response.code != 200) return -1;
 | 
				
			||||||
@@ -48,72 +47,51 @@ class ConversationsHelper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Update an existing conversation
 | 
					  /// Update an existing conversation
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Returns a boolean depending of the success of the operation
 | 
					  /// Throws in case of failure
 | 
				
			||||||
  Future<bool> updateConversation(Conversation settings) async {
 | 
					  Future<void> updateConversation(NewConversationsSettings settings) async {
 | 
				
			||||||
    final request =
 | 
					    final request = APIRequest.withLogin("conversations/updateSettings")
 | 
				
			||||||
        APIRequest(uri: "conversations/updateSettings", needLogin: true, args: {
 | 
					        .addInt("conversationID", settings.convID)
 | 
				
			||||||
      "conversationID": settings.id.toString(),
 | 
					        .addBool("following", settings.following);
 | 
				
			||||||
      "following": settings.following ? "true" : "false"
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (settings.isOwner || settings.canEveryoneAddMembers)
 | 
					    // Update conversation settings
 | 
				
			||||||
      request.addString("members", settings.members.join(","));
 | 
					    if (settings.isComplete)
 | 
				
			||||||
 | 
					      request
 | 
				
			||||||
 | 
					          .addString("name", settings.name ?? "")
 | 
				
			||||||
 | 
					          .addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers)
 | 
				
			||||||
 | 
					          .addString("color", settings.color ?? "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Update all conversation settings, if possible
 | 
					    await request.execWithThrow();
 | 
				
			||||||
    if (settings.isOwner) {
 | 
					 | 
				
			||||||
      request.addString("name", settings.hasName ? settings.name : "false");
 | 
					 | 
				
			||||||
      request.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final response = await request.exec();
 | 
					    // Delete old conversation entry from the database
 | 
				
			||||||
 | 
					    await ConversationsSerializationHelper()
 | 
				
			||||||
    if (response.code != 200) return false;
 | 
					        .removeElement((t) => t.id == settings.convID);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    //Delete old conversation entry from the database
 | 
					 | 
				
			||||||
    await _conversationsDatabaseHelper.delete(settings.id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Success
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Delete a conversation specified by its [id]
 | 
					  /// Delete a conversation specified by its [id]
 | 
				
			||||||
  Future<bool> deleteConversation(int id) async {
 | 
					  Future<void> deleteConversation(int id) async =>
 | 
				
			||||||
    final response = await APIRequest(
 | 
					      await APIRequest.withLogin("conversations/delete")
 | 
				
			||||||
      uri: "conversations/delete",
 | 
					          .addInt("conversationID", id)
 | 
				
			||||||
      needLogin: true,
 | 
					          .execWithThrow();
 | 
				
			||||||
      args: {
 | 
					 | 
				
			||||||
        "conversationID": id.toString(),
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ).exec();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return response.code == 200;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Download the list of conversations from the server
 | 
					  /// Download the list of conversations from the server
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws an exception in case of failure
 | 
				
			||||||
  Future<ConversationsList> downloadList() async {
 | 
					  Future<ConversationsList> downloadList() async {
 | 
				
			||||||
    final response =
 | 
					    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 {
 | 
					    // Update the database
 | 
				
			||||||
      ConversationsList list = ConversationsList();
 | 
					    await ConversationsSerializationHelper().setList(list);
 | 
				
			||||||
      response.getArray().forEach((f) => list.add(apiToConversation(f)));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Update the database
 | 
					    return list;
 | 
				
			||||||
      await _conversationsDatabaseHelper.clearTable();
 | 
					 | 
				
			||||||
      await _conversationsDatabaseHelper.insertAll(list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return list;
 | 
					 | 
				
			||||||
    } on Exception catch (e) {
 | 
					 | 
				
			||||||
      print(e.toString());
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get the local list of conversations
 | 
					  /// Get the local list of conversations
 | 
				
			||||||
  Future<ConversationsList> getCachedList() async {
 | 
					  Future<ConversationsList> getCachedList() async {
 | 
				
			||||||
    final list = await _conversationsDatabaseHelper.getAll();
 | 
					    final list = await ConversationsSerializationHelper().getList();
 | 
				
			||||||
    list.sort();
 | 
					    list.sort();
 | 
				
			||||||
    return list;
 | 
					    return list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -124,41 +102,31 @@ class ConversationsHelper {
 | 
				
			|||||||
      final response = await APIRequest(
 | 
					      final response = await APIRequest(
 | 
				
			||||||
          uri: "conversations/getInfoOne",
 | 
					          uri: "conversations/getInfoOne",
 | 
				
			||||||
          needLogin: true,
 | 
					          needLogin: true,
 | 
				
			||||||
          args: {"conversationID": id.toString()}).exec();
 | 
					          args: {"conversationID": id.toString()}).execWithThrow();
 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (response.code != 200) return null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final conversation = apiToConversation(response.getObject());
 | 
					      final conversation = apiToConversation(response.getObject());
 | 
				
			||||||
      _conversationsDatabaseHelper.insertOrUpdate(conversation);
 | 
					
 | 
				
			||||||
 | 
					      await ConversationsSerializationHelper()
 | 
				
			||||||
 | 
					          .insertOrReplaceElement((c) => c.id == conversation.id, conversation);
 | 
				
			||||||
      return conversation;
 | 
					      return conversation;
 | 
				
			||||||
    } on Exception catch (e) {
 | 
					    } on Exception catch (e, s) {
 | 
				
			||||||
      print(e.toString());
 | 
					      print("Could not get information about a single conversation ! $e => $s");
 | 
				
			||||||
      print("Could not get information about a single conversation !");
 | 
					 | 
				
			||||||
      return null;
 | 
					      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
 | 
					  /// cached version of the conversation will be used, else it will always get
 | 
				
			||||||
  /// the information from the server
 | 
					  /// the information from the server. The method throws an [Exception] in
 | 
				
			||||||
  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
 | 
					 | 
				
			||||||
  /// case of failure
 | 
					  /// case of failure
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Return value of this method is never null.
 | 
					  /// Return value of this method is never null.
 | 
				
			||||||
  Future<Conversation> getSingleOrThrow(int id, {bool force = false}) async {
 | 
					  Future<Conversation> getSingle(int id, {bool force = false}) async {
 | 
				
			||||||
    final conv = await this.getSingle(id, force: force);
 | 
					    if (force ||
 | 
				
			||||||
 | 
					        !await ConversationsSerializationHelper().any((c) => c.id == id))
 | 
				
			||||||
    if (conv == null)
 | 
					      return await _downloadSingle(id);
 | 
				
			||||||
      throw Exception("Could not get information about the conversation!");
 | 
					    else
 | 
				
			||||||
 | 
					      return await ConversationsSerializationHelper().get(id);
 | 
				
			||||||
    return conv;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get the name of a [conversation]. This requires information
 | 
					  /// Get the name of a [conversation]. This requires information
 | 
				
			||||||
@@ -170,9 +138,9 @@ class ConversationsHelper {
 | 
				
			|||||||
    String name = "";
 | 
					    String name = "";
 | 
				
			||||||
    int count = 0;
 | 
					    int count = 0;
 | 
				
			||||||
    for (int i = 0; i < 3 && i < conversation.members.length; i++)
 | 
					    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 ? ", " : "") +
 | 
					        name += (count > 0 ? ", " : "") +
 | 
				
			||||||
            users.getUser(conversation.members[i]).fullName;
 | 
					            users.getUser(conversation.members[i].userID).fullName;
 | 
				
			||||||
        count++;
 | 
					        count++;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,6 +152,8 @@ class ConversationsHelper {
 | 
				
			|||||||
  /// Search and return a private conversation with a given [userID]. If such
 | 
					  /// Search and return a private conversation with a given [userID]. If such
 | 
				
			||||||
  /// conversation does not exists, it is created if [allowCreate] is set to
 | 
					  /// conversation does not exists, it is created if [allowCreate] is set to
 | 
				
			||||||
  /// true
 | 
					  /// true
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws an exception in case of failure
 | 
				
			||||||
  Future<int> getPrivate(int userID, {bool allowCreate = true}) async {
 | 
					  Future<int> getPrivate(int userID, {bool allowCreate = true}) async {
 | 
				
			||||||
    final response = await APIRequest(
 | 
					    final response = await APIRequest(
 | 
				
			||||||
      uri: "conversations/getPrivate",
 | 
					      uri: "conversations/getPrivate",
 | 
				
			||||||
@@ -192,17 +162,10 @@ class ConversationsHelper {
 | 
				
			|||||||
        "otherUser": userID.toString(),
 | 
					        "otherUser": userID.toString(),
 | 
				
			||||||
        "allowCreate": allowCreate.toString()
 | 
					        "allowCreate": allowCreate.toString()
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ).exec();
 | 
					    ).execWithThrow();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (response.code != 200) return null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get and return conversation ID
 | 
					    // Get and return conversation ID
 | 
				
			||||||
    try {
 | 
					    return int.parse(response.getObject()["conversationsID"][0].toString());
 | 
				
			||||||
      return int.parse(response.getObject()["conversationsID"][0].toString());
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      e.toString();
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Asynchronously get the name of the conversation
 | 
					  /// Asynchronously get the name of the conversation
 | 
				
			||||||
@@ -210,15 +173,13 @@ class ConversationsHelper {
 | 
				
			|||||||
  /// Unlike the synchronous method, this method does not need information
 | 
					  /// Unlike the synchronous method, this method does not need information
 | 
				
			||||||
  /// about the members of the conversation
 | 
					  /// about the members of the conversation
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Returns null in case of failure
 | 
					  /// Throws an exception in case of failure
 | 
				
			||||||
  static Future<String> getConversationNameAsync(
 | 
					  static Future<String> getConversationNameAsync(
 | 
				
			||||||
      Conversation conversation) async {
 | 
					      Conversation conversation) async {
 | 
				
			||||||
    if (conversation.hasName) return conversation.name;
 | 
					    if (conversation.hasName) return conversation.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Get information about the members of the conversation
 | 
					    //Get information about the members of the conversation
 | 
				
			||||||
    final members = await UsersHelper().getUsersInfo(conversation.members);
 | 
					    final members = await UsersHelper().getList(conversation.membersID);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (members == null) return null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ConversationsHelper.getConversationName(conversation, members);
 | 
					    return ConversationsHelper.getConversationName(conversation, members);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -226,14 +187,18 @@ class ConversationsHelper {
 | 
				
			|||||||
  /// Turn an API entry into a [Conversation] object
 | 
					  /// Turn an API entry into a [Conversation] object
 | 
				
			||||||
  static Conversation apiToConversation(Map<String, dynamic> map) {
 | 
					  static Conversation apiToConversation(Map<String, dynamic> map) {
 | 
				
			||||||
    return Conversation(
 | 
					    return Conversation(
 | 
				
			||||||
        id: map["ID"],
 | 
					        id: map["id"],
 | 
				
			||||||
        ownerID: map["ID_owner"],
 | 
					        lastActivity: map["last_activity"],
 | 
				
			||||||
        lastActive: map["last_active"],
 | 
					        name: map["name"],
 | 
				
			||||||
        name: map["name"] == false ? null : map["name"],
 | 
					        color: map["color"],
 | 
				
			||||||
        following: map["following"] == 1,
 | 
					        logoURL: map["logo"],
 | 
				
			||||||
        sawLastMessage: map["saw_last_message"] == 1,
 | 
					        groupID: map["group_id"],
 | 
				
			||||||
        members: List<int>.from(map["members"]),
 | 
					        members: map["members"]
 | 
				
			||||||
        canEveryoneAddMembers: map["canEveryoneAddMembers"],
 | 
					            .cast<Map<String, dynamic>>()
 | 
				
			||||||
 | 
					            .map(apiToConversationMember)
 | 
				
			||||||
 | 
					            .toList()
 | 
				
			||||||
 | 
					            .cast<ConversationMember>(),
 | 
				
			||||||
 | 
					        canEveryoneAddMembers: map["can_everyone_add_members"],
 | 
				
			||||||
        callCapabilities: map["can_have_video_call"]
 | 
					        callCapabilities: map["can_have_video_call"]
 | 
				
			||||||
            ? CallCapabilities.VIDEO
 | 
					            ? CallCapabilities.VIDEO
 | 
				
			||||||
            : (map["can_have_call"]
 | 
					            : (map["can_have_call"]
 | 
				
			||||||
@@ -242,10 +207,21 @@ class ConversationsHelper {
 | 
				
			|||||||
        isHavingCall: map["has_call_now"]);
 | 
					        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
 | 
					  /// Parse a list of messages given by the server
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws an exception in case of failure
 | 
				
			||||||
  Future<ConversationMessagesList> _parseConversationMessageFromServer(
 | 
					  Future<ConversationMessagesList> _parseConversationMessageFromServer(
 | 
				
			||||||
      int conversationID, APIResponse response) async {
 | 
					      int conversationID, APIResponse response) async {
 | 
				
			||||||
    if (response.code != 200) return null;
 | 
					    response.assertOk();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Parse the response of the server
 | 
					    // Parse the response of the server
 | 
				
			||||||
    ConversationMessagesList list = ConversationMessagesList();
 | 
					    ConversationMessagesList list = ConversationMessagesList();
 | 
				
			||||||
@@ -256,7 +232,8 @@ class ConversationsHelper {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Save messages in the cache
 | 
					    // Save messages in the cache
 | 
				
			||||||
    _conversationMessagesDatabaseHelper.insertOrUpdateAll(list);
 | 
					    await ConversationsMessagesSerializationHelper(conversationID)
 | 
				
			||||||
 | 
					        .insertOrReplaceAll(list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return list;
 | 
					    return list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -265,6 +242,8 @@ class ConversationsHelper {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Set [lastMessageID] to 0 to specify that we do not have any message of the
 | 
					  /// Set [lastMessageID] to 0 to specify that we do not have any message of the
 | 
				
			||||||
  /// conversation yet or another value else
 | 
					  /// conversation yet or another value else
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws an exception in case of failure
 | 
				
			||||||
  Future<ConversationMessagesList> _downloadNewMessagesSingle(
 | 
					  Future<ConversationMessagesList> _downloadNewMessagesSingle(
 | 
				
			||||||
      int conversationID,
 | 
					      int conversationID,
 | 
				
			||||||
      {int lastMessageID = 0}) async {
 | 
					      {int lastMessageID = 0}) async {
 | 
				
			||||||
@@ -275,26 +254,26 @@ class ConversationsHelper {
 | 
				
			|||||||
        args: {
 | 
					        args: {
 | 
				
			||||||
          "conversationID": conversationID.toString(),
 | 
					          "conversationID": conversationID.toString(),
 | 
				
			||||||
          "last_message_id": lastMessageID.toString()
 | 
					          "last_message_id": lastMessageID.toString()
 | 
				
			||||||
        }).exec();
 | 
					        }).execWithThrow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await _parseConversationMessageFromServer(conversationID, response);
 | 
					    return await _parseConversationMessageFromServer(conversationID, response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get older messages for a given conversation from an online source
 | 
					  /// Get older messages for a given conversation from an online source
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws in case of failure
 | 
				
			||||||
  Future<ConversationMessagesList> getOlderMessages({
 | 
					  Future<ConversationMessagesList> getOlderMessages({
 | 
				
			||||||
    @required int conversationID,
 | 
					    @required int conversationID,
 | 
				
			||||||
    @required int oldestMessagesID,
 | 
					    @required int oldestMessagesID,
 | 
				
			||||||
    int limit = 15,
 | 
					    int limit = 15,
 | 
				
			||||||
  }) async {
 | 
					  }) async {
 | 
				
			||||||
    // Perform the request online
 | 
					    // Perform the request online
 | 
				
			||||||
    final response = await APIRequest(
 | 
					    final response =
 | 
				
			||||||
        uri: "conversations/get_older_messages",
 | 
					        await APIRequest.withLogin("conversations/get_older_messages", args: {
 | 
				
			||||||
        needLogin: true,
 | 
					      "conversationID": conversationID.toString(),
 | 
				
			||||||
        args: {
 | 
					      "oldest_message_id": oldestMessagesID.toString(),
 | 
				
			||||||
          "conversationID": conversationID.toString(),
 | 
					      "limit": limit.toString()
 | 
				
			||||||
          "oldest_message_id": oldestMessagesID.toString(),
 | 
					    }).execWithThrow();
 | 
				
			||||||
          "limit": limit.toString()
 | 
					 | 
				
			||||||
        }).exec();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await _parseConversationMessageFromServer(conversationID, response);
 | 
					    return await _parseConversationMessageFromServer(conversationID, response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -304,6 +283,8 @@ class ConversationsHelper {
 | 
				
			|||||||
  /// If [lastMessageID] is set to 0 then we retrieve the last messages of
 | 
					  /// If [lastMessageID] is set to 0 then we retrieve the last messages of
 | 
				
			||||||
  /// the conversation.
 | 
					  /// the conversation.
 | 
				
			||||||
  /// Otherwise [lastMessageID] contains the ID of the last known message
 | 
					  /// Otherwise [lastMessageID] contains the ID of the last known message
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws in case of failure
 | 
				
			||||||
  Future<ConversationMessagesList> getNewMessages(
 | 
					  Future<ConversationMessagesList> getNewMessages(
 | 
				
			||||||
      {@required int conversationID,
 | 
					      {@required int conversationID,
 | 
				
			||||||
      int lastMessageID = 0,
 | 
					      int lastMessageID = 0,
 | 
				
			||||||
@@ -312,16 +293,8 @@ class ConversationsHelper {
 | 
				
			|||||||
      return await _downloadNewMessagesSingle(conversationID,
 | 
					      return await _downloadNewMessagesSingle(conversationID,
 | 
				
			||||||
          lastMessageID: lastMessageID);
 | 
					          lastMessageID: lastMessageID);
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      return await _conversationMessagesDatabaseHelper
 | 
					      return await ConversationsMessagesSerializationHelper(conversationID)
 | 
				
			||||||
          .getAllMessagesConversations(conversationID,
 | 
					          .getList();
 | 
				
			||||||
              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
 | 
					  /// Send a new message to the server
 | 
				
			||||||
@@ -353,14 +326,13 @@ class ConversationsHelper {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Save / Update a  message into the database
 | 
					  /// Save / Update a  message into the database
 | 
				
			||||||
  Future<void> saveMessage(ConversationMessage msg) async {
 | 
					  Future<void> saveMessage(ConversationMessage msg) async =>
 | 
				
			||||||
    await _conversationMessagesDatabaseHelper.insertOrUpdate(msg);
 | 
					      await ConversationsMessagesSerializationHelper(msg.convID)
 | 
				
			||||||
  }
 | 
					          .insertOrReplace(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Remove a message from the database
 | 
					  /// Remove a message from the database
 | 
				
			||||||
  Future<void> removeMessage(int msgID) async {
 | 
					  Future<void> removeMessage(ConversationMessage msg) async =>
 | 
				
			||||||
    await _conversationMessagesDatabaseHelper.delete(msgID);
 | 
					      await ConversationsMessagesSerializationHelper(msg.convID).remove(msg);
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Update a message content
 | 
					  /// Update a message content
 | 
				
			||||||
  Future<bool> updateMessage(int id, String newContent) async {
 | 
					  Future<bool> updateMessage(int id, String newContent) async {
 | 
				
			||||||
@@ -397,11 +369,8 @@ class ConversationsHelper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return UnreadConversationsList()
 | 
					    return UnreadConversationsList()
 | 
				
			||||||
      ..addAll(list.map((f) => UnreadConversation(
 | 
					      ..addAll(list.map((f) => UnreadConversation(
 | 
				
			||||||
            id: f["id"],
 | 
					            conv: apiToConversation(f["conv"]),
 | 
				
			||||||
            convName: f["conv_name"],
 | 
					            message: apiToConversationMessage(f["message"]),
 | 
				
			||||||
            lastActive: f["last_active"],
 | 
					 | 
				
			||||||
            userID: f["userID"],
 | 
					 | 
				
			||||||
            message: f["message"],
 | 
					 | 
				
			||||||
          )));
 | 
					          )));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -432,13 +401,38 @@ class ConversationsHelper {
 | 
				
			|||||||
  static ConversationMessage apiToConversationMessage(
 | 
					  static ConversationMessage apiToConversationMessage(
 | 
				
			||||||
    Map<String, dynamic> map,
 | 
					    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(
 | 
					    return ConversationMessage(
 | 
				
			||||||
      id: map["ID"],
 | 
					        id: map["id"],
 | 
				
			||||||
      conversationID: map["convID"],
 | 
					        convID: map["conv_id"],
 | 
				
			||||||
      userID: map["ID_user"],
 | 
					        userID: map["user_id"],
 | 
				
			||||||
      timeInsert: map["time_insert"],
 | 
					        timeSent: map["time_sent"],
 | 
				
			||||||
      message: DisplayedString(map["message"]),
 | 
					        message: DisplayedString(map["message"] ?? ""),
 | 
				
			||||||
      imageURL: map["image_path"],
 | 
					        file: file,
 | 
				
			||||||
    );
 | 
					        serverMessage: serverMessage);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,37 +0,0 @@
 | 
				
			|||||||
import 'package:comunic/helpers/database/database_contract.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/helpers/database/model_database_helper.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/lists/conversation_messages_list.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Conversation messages database helper
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ConversationMessagesDatabaseHelper
 | 
					 | 
				
			||||||
    extends ModelDatabaseHelper<ConversationMessage> {
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  ConversationMessage initializeFromMap(Map<String, dynamic> map) {
 | 
					 | 
				
			||||||
    return ConversationMessage.fromMap(map);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  String tableName() {
 | 
					 | 
				
			||||||
    return ConversationsMessagesTableContract.TABLE_NAME;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Get all the message cached for a given conversation
 | 
					 | 
				
			||||||
  Future<ConversationMessagesList> getAllMessagesConversations(
 | 
					 | 
				
			||||||
      int conversationID,
 | 
					 | 
				
			||||||
      {int lastMessageID = 0}) async {
 | 
					 | 
				
			||||||
    final list = await getMultiple(
 | 
					 | 
				
			||||||
      where: "${ConversationsMessagesTableContract.C_CONVERSATION_ID} = ? "
 | 
					 | 
				
			||||||
          "AND ${BaseTableContract.C_ID} > ?",
 | 
					 | 
				
			||||||
      whereArgs: [conversationID, lastMessageID],
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Turn the list into a conversation messages list
 | 
					 | 
				
			||||||
    ConversationMessagesList finalList = ConversationMessagesList();
 | 
					 | 
				
			||||||
    finalList.addAll(list);
 | 
					 | 
				
			||||||
    return finalList;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
import 'package:comunic/helpers/database/database_contract.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/helpers/database/model_database_helper.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/lists/conversations_list.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/models/conversation.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Conversations database helper
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ConversationsDatabaseHelper extends ModelDatabaseHelper<Conversation> {
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  Conversation initializeFromMap(Map<String, dynamic> map) {
 | 
					 | 
				
			||||||
    return Conversation.fromMap(map);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  String tableName() {
 | 
					 | 
				
			||||||
    return ConversationTableContract.TABLE_NAME;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  Future<ConversationsList> getAll() async {
 | 
					 | 
				
			||||||
    ConversationsList list = ConversationsList();
 | 
					 | 
				
			||||||
    list.addAll(await super.getAll());
 | 
					 | 
				
			||||||
    return list;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -25,30 +25,6 @@ abstract class UserTableContract {
 | 
				
			|||||||
  static const C_CUSTOM_EMOJIES = "custom_emojies";
 | 
					  static const C_CUSTOM_EMOJIES = "custom_emojies";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Conversations table contract
 | 
					 | 
				
			||||||
abstract class ConversationTableContract {
 | 
					 | 
				
			||||||
  static const TABLE_NAME = "conversations";
 | 
					 | 
				
			||||||
  static const C_ID = BaseTableContract.C_ID;
 | 
					 | 
				
			||||||
  static const C_OWNER_ID = "owner_id";
 | 
					 | 
				
			||||||
  static const C_LAST_ACTIVE = "last_active";
 | 
					 | 
				
			||||||
  static const C_NAME = "name";
 | 
					 | 
				
			||||||
  static const C_FOLLOWING = "following";
 | 
					 | 
				
			||||||
  static const C_SAW_LAST_MESSAGE = "saw_last_message";
 | 
					 | 
				
			||||||
  static const C_MEMBERS = "members";
 | 
					 | 
				
			||||||
  static const C_CAN_EVERYONE_ADD_MEMBERS = "can_everyone_add_members";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Conversations messages table contract
 | 
					 | 
				
			||||||
abstract class ConversationsMessagesTableContract {
 | 
					 | 
				
			||||||
  static const TABLE_NAME = "conversations_messages";
 | 
					 | 
				
			||||||
  static const C_ID = BaseTableContract.C_ID;
 | 
					 | 
				
			||||||
  static const C_CONVERSATION_ID = "conversation_id";
 | 
					 | 
				
			||||||
  static const C_USER_ID = "user_id";
 | 
					 | 
				
			||||||
  static const C_TIME_INSERT = "time_insert";
 | 
					 | 
				
			||||||
  static const C_MESSAGE = "message";
 | 
					 | 
				
			||||||
  static const C_IMAGE_URL = "image_url";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Friends table contract
 | 
					/// Friends table contract
 | 
				
			||||||
abstract class FriendsListTableContract {
 | 
					abstract class FriendsListTableContract {
 | 
				
			||||||
  static const TABLE_NAME = "friends";
 | 
					  static const TABLE_NAME = "friends";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,14 +45,6 @@ abstract class DatabaseHelper {
 | 
				
			|||||||
    // Drop users table
 | 
					    // Drop users table
 | 
				
			||||||
    await db.execute("DROP TABLE IF EXISTS ${UserTableContract.TABLE_NAME}");
 | 
					    await db.execute("DROP TABLE IF EXISTS ${UserTableContract.TABLE_NAME}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Drop conversations table
 | 
					 | 
				
			||||||
    await db.execute(
 | 
					 | 
				
			||||||
        "DROP TABLE IF EXISTS ${ConversationTableContract.TABLE_NAME}");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Drop conversations messages table
 | 
					 | 
				
			||||||
    await db.execute(
 | 
					 | 
				
			||||||
        "DROP TABLE IF EXISTS ${ConversationsMessagesTableContract.TABLE_NAME}");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Drop friends list table
 | 
					    // Drop friends list table
 | 
				
			||||||
    await db
 | 
					    await db
 | 
				
			||||||
        .execute("DROP TABLE IF EXISTS ${FriendsListTableContract.TABLE_NAME}");
 | 
					        .execute("DROP TABLE IF EXISTS ${FriendsListTableContract.TABLE_NAME}");
 | 
				
			||||||
@@ -74,29 +66,6 @@ abstract class DatabaseHelper {
 | 
				
			|||||||
        "${UserTableContract.C_CUSTOM_EMOJIES} TEXT"
 | 
					        "${UserTableContract.C_CUSTOM_EMOJIES} TEXT"
 | 
				
			||||||
        ")");
 | 
					        ")");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create conversations table
 | 
					 | 
				
			||||||
    await db.execute("CREATE TABLE ${ConversationTableContract.TABLE_NAME} ("
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_ID} INTEGER PRIMARY KEY, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_OWNER_ID} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_LAST_ACTIVE} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_NAME} TEXT, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_FOLLOWING} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_SAW_LAST_MESSAGE} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_MEMBERS} TEXT, "
 | 
					 | 
				
			||||||
        "${ConversationTableContract.C_CAN_EVERYONE_ADD_MEMBERS} INTEGER"
 | 
					 | 
				
			||||||
        ")");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Create conversation messages table
 | 
					 | 
				
			||||||
    await db.execute(
 | 
					 | 
				
			||||||
        "CREATE TABLE ${ConversationsMessagesTableContract.TABLE_NAME} ("
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_ID} INTEGER PRIMARY KEY, "
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_CONVERSATION_ID} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_USER_ID} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_TIME_INSERT} INTEGER, "
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_MESSAGE} TEXT, "
 | 
					 | 
				
			||||||
        "${ConversationsMessagesTableContract.C_IMAGE_URL} TEXT"
 | 
					 | 
				
			||||||
        ")");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Friends list table
 | 
					    // Friends list table
 | 
				
			||||||
    await db.execute("CREATE TABLE ${FriendsListTableContract.TABLE_NAME} ("
 | 
					    await db.execute("CREATE TABLE ${FriendsListTableContract.TABLE_NAME} ("
 | 
				
			||||||
        "${FriendsListTableContract.C_ID} INTEGER PRIMARY KEY, "
 | 
					        "${FriendsListTableContract.C_ID} INTEGER PRIMARY KEY, "
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										121
									
								
								lib/helpers/serialization/base_serialization_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								lib/helpers/serialization/base_serialization_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:comunic/constants.dart';
 | 
				
			||||||
 | 
					import 'package:path/path.dart' as path;
 | 
				
			||||||
 | 
					import 'package:path_provider/path_provider.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Base serialization helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class SerializableElement<T> extends Comparable<T> {
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class BaseSerializationHelper<T extends SerializableElement> {
 | 
				
			||||||
 | 
					  /// List cache
 | 
				
			||||||
 | 
					  List<T> _cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The name of the type of data to serialise
 | 
				
			||||||
 | 
					  String get type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Parse an json entry into a [T] object
 | 
				
			||||||
 | 
					  T parse(Map<String, dynamic> m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get the file where data should be stored
 | 
				
			||||||
 | 
					  Future<File> _getFilePath() async {
 | 
				
			||||||
 | 
					    final dir = await getApplicationDocumentsDirectory();
 | 
				
			||||||
 | 
					    final targetDir =
 | 
				
			||||||
 | 
					        Directory(path.join(dir.absolute.path, SERIALIZATION_DIRECTORY));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    targetDir.create(recursive: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return File(path.join(targetDir.absolute.path, type));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Load the cache
 | 
				
			||||||
 | 
					  Future<void> _loadCache() async {
 | 
				
			||||||
 | 
					    if (_cache != null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      final file = await _getFilePath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!await file.exists()) return _cache = List();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final List<dynamic> json = jsonDecode(await file.readAsString());
 | 
				
			||||||
 | 
					      _cache = json.cast<Map<String, dynamic>>().map(parse).toList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      _cache.sort();
 | 
				
			||||||
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
					      print("Failed to read serialized data! $e => $s");
 | 
				
			||||||
 | 
					      _cache = List();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Save the cache to the persistent memory
 | 
				
			||||||
 | 
					  Future<void> _saveCache() async {
 | 
				
			||||||
 | 
					    final file = await _getFilePath();
 | 
				
			||||||
 | 
					    await file.writeAsString(jsonEncode(
 | 
				
			||||||
 | 
					        _cache.map((e) => e.toJson()).toList().cast<Map<String, dynamic>>()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get the current list of elements
 | 
				
			||||||
 | 
					  Future<List<T>> getList() async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    return List.from(_cache);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Set a new list of conversations
 | 
				
			||||||
 | 
					  Future<void> setList(List<T> list) async {
 | 
				
			||||||
 | 
					    _cache = List.from(list);
 | 
				
			||||||
 | 
					    await _saveCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Insert new element
 | 
				
			||||||
 | 
					  Future<void> insert(T el) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    _cache.add(el);
 | 
				
			||||||
 | 
					    _cache.sort();
 | 
				
			||||||
 | 
					    await _saveCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Insert new element
 | 
				
			||||||
 | 
					  Future<void> insertMany(List<T> els) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    _cache.addAll(els);
 | 
				
			||||||
 | 
					    _cache.sort();
 | 
				
			||||||
 | 
					    await _saveCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Check if any entry in the last match the predicate
 | 
				
			||||||
 | 
					  Future<bool> any(bool isContained(T t)) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    return _cache.any((element) => isContained(element));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Check if any entry in the last match the predicate
 | 
				
			||||||
 | 
					  Future<T> first(bool filter(T t)) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    return _cache.firstWhere((element) => filter(element));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Replace an element with another one
 | 
				
			||||||
 | 
					  Future<void> insertOrReplaceElement(bool isToReplace(T t), T newEl) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Insert or replace the element
 | 
				
			||||||
 | 
					    _cache.where((element) => !isToReplace(element)).toList();
 | 
				
			||||||
 | 
					    _cache.add(newEl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _cache.sort();
 | 
				
			||||||
 | 
					    await _saveCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Remove elements
 | 
				
			||||||
 | 
					  Future<void> removeElement(bool isToRemove(T t)) async {
 | 
				
			||||||
 | 
					    await _loadCache();
 | 
				
			||||||
 | 
					    _cache.removeWhere((element) => isToRemove(element));
 | 
				
			||||||
 | 
					    await _saveCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					import 'dart:collection';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/lists/conversation_messages_list.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Conversations serialization helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final HashMap<int, ConversationsMessagesSerializationHelper> _instances =
 | 
				
			||||||
 | 
					    HashSet() as HashMap<int, ConversationsMessagesSerializationHelper>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConversationsMessagesSerializationHelper
 | 
				
			||||||
 | 
					    extends BaseSerializationHelper<ConversationMessage> {
 | 
				
			||||||
 | 
					  final int convID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationsMessagesSerializationHelper._(int convID)
 | 
				
			||||||
 | 
					      : convID = convID,
 | 
				
			||||||
 | 
					        assert(convID != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  factory ConversationsMessagesSerializationHelper(int convID) {
 | 
				
			||||||
 | 
					    if (!_instances.containsKey(convID))
 | 
				
			||||||
 | 
					      _instances[convID] = ConversationsMessagesSerializationHelper._(convID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _instances[convID];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  ConversationMessage parse(Map<String, dynamic> m) =>
 | 
				
			||||||
 | 
					      ConversationMessage.fromJson(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String get type => "conv-messages-$convID";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<ConversationMessagesList> getList() async =>
 | 
				
			||||||
 | 
					      ConversationMessagesList()..addAll(await super.getList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> insertOrReplace(ConversationMessage msg) async =>
 | 
				
			||||||
 | 
					      await insertOrReplaceElement((t) => t.id == msg.id, msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> remove(ConversationMessage msg) async =>
 | 
				
			||||||
 | 
					      await removeElement((t) => t.id == msg.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Insert or replace a list of messages
 | 
				
			||||||
 | 
					  Future<void> insertOrReplaceAll(List<ConversationMessage> list) async {
 | 
				
			||||||
 | 
					    for (var message in list)
 | 
				
			||||||
 | 
					      await insertOrReplaceElement((t) => t.id == message.id, message);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/lists/conversations_list.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/conversation.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Conversations serialization helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConversationsSerializationHelper
 | 
				
			||||||
 | 
					    extends BaseSerializationHelper<Conversation> {
 | 
				
			||||||
 | 
					  /// Singleton
 | 
				
			||||||
 | 
					  factory ConversationsSerializationHelper() {
 | 
				
			||||||
 | 
					    if (_cache == null) _cache = ConversationsSerializationHelper._();
 | 
				
			||||||
 | 
					    return _cache;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationsSerializationHelper._();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Conversation parse(Map<String, dynamic> m) => Conversation.fromJson(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String get type => "conversations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<ConversationsList> getList() async =>
 | 
				
			||||||
 | 
					      ConversationsList()..addAll(await super.getList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get a conversation
 | 
				
			||||||
 | 
					  Future<Conversation> get(int id) => first((t) => t.id == id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -91,9 +91,16 @@ class UsersHelper {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get users information from a given [Set]
 | 
					  /// Get users information from a given [Set]
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws in case of failure
 | 
				
			||||||
  Future<UsersList> getList(Set<int> users,
 | 
					  Future<UsersList> getList(Set<int> users,
 | 
				
			||||||
      {bool forceDownload = false}) async {
 | 
					      {bool forceDownload = false}) async {
 | 
				
			||||||
    return await getUsersInfo(users.toList());
 | 
					    final list = await getUsersInfo(users.toList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (list == null)
 | 
				
			||||||
 | 
					      throw Exception("Failed to get the list of users!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get users information
 | 
					  /// Get users information
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,11 +8,11 @@ import 'package:comunic/models/conversation.dart';
 | 
				
			|||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConversationsList extends ListBase<Conversation> {
 | 
					class ConversationsList extends ListBase<Conversation> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  final List<Conversation> _list = List();
 | 
					  final List<Conversation> _list = List();
 | 
				
			||||||
  UsersList users;
 | 
					  UsersList users;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  set length(l) => _list.length = l;
 | 
					  set length(l) => _list.length = l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int get length => _list.length;
 | 
					  int get length => _list.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -22,12 +22,9 @@ class ConversationsList extends ListBase<Conversation> {
 | 
				
			|||||||
  void operator []=(int index, Conversation value) => _list[index] = value;
 | 
					  void operator []=(int index, Conversation value) => _list[index] = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get the entire lists of users ID in this list
 | 
					  /// Get the entire lists of users ID in this list
 | 
				
			||||||
  List<int> get allUsersID {
 | 
					  Set<int> get allUsersID {
 | 
				
			||||||
    final List<int> list = List();
 | 
					    final Set<int> list = Set();
 | 
				
			||||||
    forEach((c) => c.members.forEach((id){
 | 
					    forEach((c) => c.members.forEach((member) => list.add(member.userID)));
 | 
				
			||||||
      if(!list.contains(id))
 | 
					 | 
				
			||||||
        list.add(id);
 | 
					 | 
				
			||||||
    }));
 | 
					 | 
				
			||||||
    return list;
 | 
					    return list;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ class MembershipList extends AbstractList<Membership> {
 | 
				
			|||||||
        case MembershipType.GROUP:
 | 
					        case MembershipType.GROUP:
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        case MembershipType.CONVERSATION:
 | 
					        case MembershipType.CONVERSATION:
 | 
				
			||||||
          s.addAll(m.conversation.members);
 | 
					          s.addAll(m.conversation.membersID);
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,5 +7,9 @@ import 'package:comunic/models/unread_conversation.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class UnreadConversationsList extends AbstractList<UnreadConversation> {
 | 
					class UnreadConversationsList extends AbstractList<UnreadConversation> {
 | 
				
			||||||
  /// Get the ID of the users included in this list
 | 
					  /// Get the ID of the users included in this list
 | 
				
			||||||
  Set<int> get usersID => new Set<int>()..addAll(map((f) => f.userID));
 | 
					  Set<int> get usersID {
 | 
				
			||||||
 | 
					    final set = Set();
 | 
				
			||||||
 | 
					    forEach((element) => set.addAll(element.message.usersID));
 | 
				
			||||||
 | 
					    return set;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
import 'package:comunic/helpers/database/database_contract.dart';
 | 
					import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
 | 
				
			||||||
import 'package:comunic/models/cache_model.dart';
 | 
					import 'package:comunic/models/conversation_member.dart';
 | 
				
			||||||
import 'package:comunic/utils/account_utils.dart';
 | 
					import 'package:comunic/utils/account_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/list_utils.dart';
 | 
					 | 
				
			||||||
import 'package:meta/meta.dart';
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Conversation model
 | 
					/// Conversation model
 | 
				
			||||||
@@ -10,79 +9,86 @@ import 'package:meta/meta.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
enum CallCapabilities { NONE, AUDIO, VIDEO }
 | 
					enum CallCapabilities { NONE, AUDIO, VIDEO }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Conversation extends CacheModel implements Comparable {
 | 
					class Conversation extends SerializableElement<Conversation> {
 | 
				
			||||||
  final int ownerID;
 | 
					  final int id;
 | 
				
			||||||
  final int lastActive;
 | 
					  final int lastActivity;
 | 
				
			||||||
  final String name;
 | 
					  final String name;
 | 
				
			||||||
  final bool following;
 | 
					  final String color;
 | 
				
			||||||
  final bool sawLastMessage;
 | 
					  final String logoURL;
 | 
				
			||||||
  final List<int> members;
 | 
					  final int groupID;
 | 
				
			||||||
 | 
					  final List<ConversationMember> members;
 | 
				
			||||||
  final bool canEveryoneAddMembers;
 | 
					  final bool canEveryoneAddMembers;
 | 
				
			||||||
  final CallCapabilities callCapabilities;
 | 
					  final CallCapabilities callCapabilities;
 | 
				
			||||||
  final bool isHavingCall;
 | 
					  final bool isHavingCall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const Conversation({
 | 
					  Conversation({
 | 
				
			||||||
    @required int id,
 | 
					    @required this.id,
 | 
				
			||||||
    @required this.ownerID,
 | 
					    @required this.lastActivity,
 | 
				
			||||||
    @required this.lastActive,
 | 
					 | 
				
			||||||
    @required this.name,
 | 
					    @required this.name,
 | 
				
			||||||
    @required this.following,
 | 
					    @required this.color,
 | 
				
			||||||
    @required this.sawLastMessage,
 | 
					    @required this.logoURL,
 | 
				
			||||||
 | 
					    @required this.groupID,
 | 
				
			||||||
    @required this.members,
 | 
					    @required this.members,
 | 
				
			||||||
    @required this.canEveryoneAddMembers,
 | 
					    @required this.canEveryoneAddMembers,
 | 
				
			||||||
    this.callCapabilities = CallCapabilities.NONE,
 | 
					    this.callCapabilities = CallCapabilities.NONE,
 | 
				
			||||||
    this.isHavingCall = false,
 | 
					    this.isHavingCall = false,
 | 
				
			||||||
  })  : assert(id != null),
 | 
					  })  : assert(id != null),
 | 
				
			||||||
        assert(ownerID != null),
 | 
					        assert(lastActivity != null),
 | 
				
			||||||
        assert(lastActive != null),
 | 
					 | 
				
			||||||
        assert(following != null),
 | 
					 | 
				
			||||||
        assert(sawLastMessage != null),
 | 
					 | 
				
			||||||
        assert(members != null),
 | 
					        assert(members != null),
 | 
				
			||||||
        assert(canEveryoneAddMembers != null),
 | 
					        assert(canEveryoneAddMembers != null),
 | 
				
			||||||
        assert(callCapabilities != null),
 | 
					        assert(callCapabilities != null),
 | 
				
			||||||
        assert(isHavingCall != null),
 | 
					        assert(isHavingCall != null);
 | 
				
			||||||
        super(id: id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Check out whether a conversation has a fixed name or not
 | 
					  /// Check out whether a conversation has a fixed name or not
 | 
				
			||||||
  bool get hasName => this.name != null;
 | 
					  bool get hasName => this.name != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Check out whether current user of the application is the owner of it or
 | 
					  /// Get current user membership
 | 
				
			||||||
  /// not
 | 
					  ConversationMember get membership =>
 | 
				
			||||||
  bool get isOwner => this.ownerID == userID();
 | 
					      members.firstWhere((m) => m.userID == userID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Conversation.fromMap(Map<String, dynamic> map)
 | 
					  /// Check out whether current user of the application is an admin
 | 
				
			||||||
      : ownerID = map[ConversationTableContract.C_OWNER_ID],
 | 
					  bool get isAdmin => membership.isAdmin;
 | 
				
			||||||
        lastActive = map[ConversationTableContract.C_LAST_ACTIVE],
 | 
					
 | 
				
			||||||
        name = map[ConversationTableContract.C_NAME],
 | 
					  /// Check it current user is following the conversation or not
 | 
				
			||||||
        following = map[ConversationTableContract.C_FOLLOWING] == 1,
 | 
					  bool get following => membership.following;
 | 
				
			||||||
        sawLastMessage = map[ConversationTableContract.C_SAW_LAST_MESSAGE] == 1,
 | 
					
 | 
				
			||||||
        members =
 | 
					  /// Get the list of members in the conversation
 | 
				
			||||||
            listToIntList(map[ConversationTableContract.C_MEMBERS].split(",")),
 | 
					  Set<int> get membersID => members.map((e) => e.userID).toSet();
 | 
				
			||||||
        canEveryoneAddMembers =
 | 
					
 | 
				
			||||||
            map[ConversationTableContract.C_CAN_EVERYONE_ADD_MEMBERS] == 1,
 | 
					  /// Check if the last message has been seen or not
 | 
				
			||||||
 | 
					  bool get sawLastMessage => lastActivity <= membership.lastAccessTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Conversation.fromJson(Map<String, dynamic> map)
 | 
				
			||||||
 | 
					      : id = map["id"],
 | 
				
			||||||
 | 
					        name = map["name"],
 | 
				
			||||||
 | 
					        color = map["color"],
 | 
				
			||||||
 | 
					        logoURL = map["logoURL"],
 | 
				
			||||||
 | 
					        groupID = map["groupID"],
 | 
				
			||||||
 | 
					        lastActivity = map["lastActivity"],
 | 
				
			||||||
 | 
					        members = map["members"]
 | 
				
			||||||
 | 
					            .map((el) => ConversationMember.fromJSON(el))
 | 
				
			||||||
 | 
					            .toList(),
 | 
				
			||||||
 | 
					        canEveryoneAddMembers = map["canEveryoneAddMembers"],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // By default, we can not do any call
 | 
					        // By default, we can not do any call
 | 
				
			||||||
        callCapabilities = CallCapabilities.NONE,
 | 
					        callCapabilities = CallCapabilities.NONE,
 | 
				
			||||||
        isHavingCall = false,
 | 
					        isHavingCall = false;
 | 
				
			||||||
        super.fromMap(map);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
  Map<String, dynamic> toMap() {
 | 
					 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      ConversationTableContract.C_ID: id,
 | 
					      "id": id,
 | 
				
			||||||
      ConversationTableContract.C_OWNER_ID: ownerID,
 | 
					      "name": name,
 | 
				
			||||||
      ConversationTableContract.C_LAST_ACTIVE: lastActive,
 | 
					      "color": color,
 | 
				
			||||||
      ConversationTableContract.C_NAME: name,
 | 
					      "logoURL": logoURL,
 | 
				
			||||||
      ConversationTableContract.C_FOLLOWING: following ? 1 : 0,
 | 
					      "groupID": groupID,
 | 
				
			||||||
      ConversationTableContract.C_SAW_LAST_MESSAGE: sawLastMessage ? 1 : 0,
 | 
					      "lastActivity": lastActivity,
 | 
				
			||||||
      ConversationTableContract.C_MEMBERS: members.join(","),
 | 
					      "members": members.map((e) => e.toJson()).toList(),
 | 
				
			||||||
      ConversationTableContract.C_CAN_EVERYONE_ADD_MEMBERS:
 | 
					      "canEveryoneAddMembers": canEveryoneAddMembers,
 | 
				
			||||||
          canEveryoneAddMembers ? 1 : 0
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int compareTo(other) {
 | 
					  int compareTo(Conversation other) {
 | 
				
			||||||
    return other.lastActive.compareTo(this.lastActive);
 | 
					    return other.lastActivity.compareTo(this.lastActivity);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								lib/models/conversation_member.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/models/conversation_member.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/widgets.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Conversation member
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConversationMember {
 | 
				
			||||||
 | 
					  final int userID;
 | 
				
			||||||
 | 
					  final int lastMessageSeen;
 | 
				
			||||||
 | 
					  final int lastAccessTime;
 | 
				
			||||||
 | 
					  final bool following;
 | 
				
			||||||
 | 
					  final bool isAdmin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ConversationMember({
 | 
				
			||||||
 | 
					    @required this.userID,
 | 
				
			||||||
 | 
					    @required this.lastMessageSeen,
 | 
				
			||||||
 | 
					    @required this.lastAccessTime,
 | 
				
			||||||
 | 
					    @required this.following,
 | 
				
			||||||
 | 
					    @required this.isAdmin,
 | 
				
			||||||
 | 
					  })  : assert(userID != null),
 | 
				
			||||||
 | 
					        assert(lastMessageSeen != null),
 | 
				
			||||||
 | 
					        assert(lastAccessTime != null),
 | 
				
			||||||
 | 
					        assert(following != null),
 | 
				
			||||||
 | 
					        assert(isAdmin != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() => {
 | 
				
			||||||
 | 
					        'userID': userID,
 | 
				
			||||||
 | 
					        'lastMessageSeen': lastMessageSeen,
 | 
				
			||||||
 | 
					        'lastAccessTime': lastAccessTime,
 | 
				
			||||||
 | 
					        'following': following,
 | 
				
			||||||
 | 
					        'isAdmin': isAdmin,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationMember.fromJSON(Map<String, dynamic> json)
 | 
				
			||||||
 | 
					      : userID = json["userID"],
 | 
				
			||||||
 | 
					        lastMessageSeen = json["lastMessageSeen"],
 | 
				
			||||||
 | 
					        lastAccessTime = json["lastAccessTime"],
 | 
				
			||||||
 | 
					        following = json["following"],
 | 
				
			||||||
 | 
					        isAdmin = json["isAdmin"];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
import 'package:comunic/helpers/database/database_contract.dart';
 | 
					import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
 | 
				
			||||||
import 'package:comunic/models/cache_model.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/models/displayed_content.dart';
 | 
					import 'package:comunic/models/displayed_content.dart';
 | 
				
			||||||
import 'package:comunic/utils/account_utils.dart' as account;
 | 
					import 'package:comunic/utils/account_utils.dart' as account;
 | 
				
			||||||
import 'package:meta/meta.dart';
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
@@ -8,59 +7,171 @@ import 'package:meta/meta.dart';
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConversationMessage extends CacheModel implements Comparable {
 | 
					class ConversationMessageFile {
 | 
				
			||||||
  final int id;
 | 
					  final String url;
 | 
				
			||||||
  final int conversationID;
 | 
					  final int size;
 | 
				
			||||||
 | 
					  final String name;
 | 
				
			||||||
 | 
					  final String thumbnail;
 | 
				
			||||||
 | 
					  final String type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ConversationMessageFile({
 | 
				
			||||||
 | 
					    @required this.url,
 | 
				
			||||||
 | 
					    @required this.size,
 | 
				
			||||||
 | 
					    @required this.name,
 | 
				
			||||||
 | 
					    @required this.thumbnail,
 | 
				
			||||||
 | 
					    @required this.type,
 | 
				
			||||||
 | 
					  })  : assert(url != null),
 | 
				
			||||||
 | 
					        assert(size != null),
 | 
				
			||||||
 | 
					        assert(name != null),
 | 
				
			||||||
 | 
					        assert(type != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() => {
 | 
				
			||||||
 | 
					        "url": url,
 | 
				
			||||||
 | 
					        "size": size,
 | 
				
			||||||
 | 
					        "name": name,
 | 
				
			||||||
 | 
					        "thumbnail": thumbnail,
 | 
				
			||||||
 | 
					        "type": type
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationMessageFile.fromJson(Map<String, dynamic> json)
 | 
				
			||||||
 | 
					      : url = json["url"],
 | 
				
			||||||
 | 
					        size = json["size"],
 | 
				
			||||||
 | 
					        name = json["name"],
 | 
				
			||||||
 | 
					        thumbnail = json["thumbnail"],
 | 
				
			||||||
 | 
					        type = json["type"];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ConversationServerMessageType {
 | 
				
			||||||
 | 
					  USER_CREATED_CONVERSATION,
 | 
				
			||||||
 | 
					  USER_ADDED_ANOTHER_USER,
 | 
				
			||||||
 | 
					  USER_LEFT_CONV,
 | 
				
			||||||
 | 
					  USER_REMOVED_ANOTHER_USER
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConversationServerMessage {
 | 
				
			||||||
 | 
					  final ConversationServerMessageType type;
 | 
				
			||||||
  final int userID;
 | 
					  final int userID;
 | 
				
			||||||
  final int timeInsert;
 | 
					  final int userWhoAdded;
 | 
				
			||||||
  final DisplayedString message;
 | 
					  final int userAdded;
 | 
				
			||||||
  final String imageURL;
 | 
					  final int userWhoRemoved;
 | 
				
			||||||
 | 
					  final int userRemoved;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const ConversationMessage({
 | 
					  const ConversationServerMessage({
 | 
				
			||||||
    @required this.id,
 | 
					    @required this.type,
 | 
				
			||||||
    @required this.conversationID,
 | 
					 | 
				
			||||||
    @required this.userID,
 | 
					    @required this.userID,
 | 
				
			||||||
    @required this.timeInsert,
 | 
					    @required this.userWhoAdded,
 | 
				
			||||||
    @required this.message,
 | 
					    @required this.userAdded,
 | 
				
			||||||
    @required this.imageURL,
 | 
					    @required this.userWhoRemoved,
 | 
				
			||||||
  })  : assert(id != null),
 | 
					    @required this.userRemoved,
 | 
				
			||||||
        assert(userID != null),
 | 
					  })  : assert(type != null),
 | 
				
			||||||
        assert(timeInsert != null),
 | 
					        assert(userID != null ||
 | 
				
			||||||
        assert(message != null),
 | 
					            (type != ConversationServerMessageType.USER_CREATED_CONVERSATION &&
 | 
				
			||||||
        super(id: id);
 | 
					                type != ConversationServerMessageType.USER_LEFT_CONV)),
 | 
				
			||||||
 | 
					        assert((userWhoAdded != null && userAdded != null) ||
 | 
				
			||||||
 | 
					            type != ConversationServerMessageType.USER_ADDED_ANOTHER_USER),
 | 
				
			||||||
 | 
					        assert((userWhoRemoved != null && userRemoved != null) ||
 | 
				
			||||||
 | 
					            type != ConversationServerMessageType.USER_REMOVED_ANOTHER_USER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DateTime get date => DateTime.fromMillisecondsSinceEpoch(timeInsert * 1000);
 | 
					  Set<int> get usersID {
 | 
				
			||||||
 | 
					    switch (type) {
 | 
				
			||||||
 | 
					      case ConversationServerMessageType.USER_CREATED_CONVERSATION:
 | 
				
			||||||
 | 
					      case ConversationServerMessageType.USER_LEFT_CONV:
 | 
				
			||||||
 | 
					        return Set()..add(userID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case ConversationServerMessageType.USER_ADDED_ANOTHER_USER:
 | 
				
			||||||
 | 
					        return Set()..add(userWhoAdded)..add(userAdded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case ConversationServerMessageType.USER_REMOVED_ANOTHER_USER:
 | 
				
			||||||
 | 
					        return Set()..add(userWhoRemoved)..add(userRemoved);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throw Exception("Unsupported server message type!");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() => {
 | 
				
			||||||
 | 
					        "type": type.toString(),
 | 
				
			||||||
 | 
					        "userID": userID,
 | 
				
			||||||
 | 
					        "userWhoAdded": userWhoAdded,
 | 
				
			||||||
 | 
					        "userAdded": userAdded,
 | 
				
			||||||
 | 
					        "userWhoRemoved": userWhoRemoved,
 | 
				
			||||||
 | 
					        "userRemoved": userRemoved,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationServerMessage.fromJson(Map<String, dynamic> json)
 | 
				
			||||||
 | 
					      : type = ConversationServerMessageType.values
 | 
				
			||||||
 | 
					            .firstWhere((el) => el.toString() == json["type"]),
 | 
				
			||||||
 | 
					        userID = json["userID"],
 | 
				
			||||||
 | 
					        userWhoAdded = json["userWhoAdded"],
 | 
				
			||||||
 | 
					        userAdded = json["userAdded"],
 | 
				
			||||||
 | 
					        userWhoRemoved = json["userWhoRemoved"],
 | 
				
			||||||
 | 
					        userRemoved = json["userRemoved"];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConversationMessage extends SerializableElement<ConversationMessage> {
 | 
				
			||||||
 | 
					  final int id;
 | 
				
			||||||
 | 
					  final int convID;
 | 
				
			||||||
 | 
					  final int userID;
 | 
				
			||||||
 | 
					  final int timeSent;
 | 
				
			||||||
 | 
					  final DisplayedString message;
 | 
				
			||||||
 | 
					  final ConversationMessageFile file;
 | 
				
			||||||
 | 
					  final ConversationServerMessage serverMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ConversationMessage({
 | 
				
			||||||
 | 
					    @required this.id,
 | 
				
			||||||
 | 
					    @required this.convID,
 | 
				
			||||||
 | 
					    @required this.userID,
 | 
				
			||||||
 | 
					    @required this.timeSent,
 | 
				
			||||||
 | 
					    @required this.message,
 | 
				
			||||||
 | 
					    @required this.file,
 | 
				
			||||||
 | 
					    @required this.serverMessage,
 | 
				
			||||||
 | 
					  })  : assert(id != null),
 | 
				
			||||||
 | 
					        assert(convID != null),
 | 
				
			||||||
 | 
					        assert(userID != null),
 | 
				
			||||||
 | 
					        assert(timeSent != null),
 | 
				
			||||||
 | 
					        assert(message != null || file != null || serverMessage != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DateTime get date => DateTime.fromMillisecondsSinceEpoch(timeSent * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get hasMessage => !message.isNull && message.length > 0;
 | 
					  bool get hasMessage => !message.isNull && message.length > 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get hasImage => imageURL != null && imageURL != "null";
 | 
					  bool get hasFile => file != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get hasThumbnail => hasFile && file.thumbnail != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get hasImage => hasFile && file.type.startsWith("image/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get isOwner => account.userID() == userID;
 | 
					  bool get isOwner => account.userID() == userID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get the list of the ID of the users implied in this message
 | 
				
			||||||
 | 
					  Set<int> get usersID {
 | 
				
			||||||
 | 
					    if (userID != null) return Set()..add(userID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return serverMessage.usersID;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int compareTo(other) {
 | 
					  int compareTo(ConversationMessage other) {
 | 
				
			||||||
    return id.compareTo(other.id);
 | 
					    return id.compareTo(other.id);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
  Map<String, dynamic> toMap() {
 | 
					 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      ConversationsMessagesTableContract.C_ID: id,
 | 
					      "id": id,
 | 
				
			||||||
      ConversationsMessagesTableContract.C_CONVERSATION_ID: conversationID,
 | 
					      "convID": convID,
 | 
				
			||||||
      ConversationsMessagesTableContract.C_USER_ID: userID,
 | 
					      "userID": userID,
 | 
				
			||||||
      ConversationsMessagesTableContract.C_TIME_INSERT: timeInsert,
 | 
					      "timeSent": timeSent,
 | 
				
			||||||
      ConversationsMessagesTableContract.C_MESSAGE: message.content,
 | 
					      "message": message,
 | 
				
			||||||
      ConversationsMessagesTableContract.C_IMAGE_URL: imageURL
 | 
					      "file": file?.toJson(),
 | 
				
			||||||
 | 
					      "serverMessage": serverMessage?.toJson(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ConversationMessage.fromMap(Map<String, dynamic> map)
 | 
					  ConversationMessage.fromJson(Map<String, dynamic> map)
 | 
				
			||||||
      : id = map[ConversationsMessagesTableContract.C_ID],
 | 
					      : id = map["id"],
 | 
				
			||||||
        conversationID =
 | 
					        convID = map["convID"],
 | 
				
			||||||
            map[ConversationsMessagesTableContract.C_CONVERSATION_ID],
 | 
					        userID = map["userID"],
 | 
				
			||||||
        userID = map[ConversationsMessagesTableContract.C_USER_ID],
 | 
					        timeSent = map["timeSent"],
 | 
				
			||||||
        timeInsert = map[ConversationsMessagesTableContract.C_TIME_INSERT],
 | 
					        message = DisplayedString(map["message"]),
 | 
				
			||||||
        message = DisplayedString(map[ConversationsMessagesTableContract.C_MESSAGE]),
 | 
					        file = map["file"],
 | 
				
			||||||
        imageURL = map[ConversationsMessagesTableContract.C_IMAGE_URL],
 | 
					        serverMessage = map["serverMessage"];
 | 
				
			||||||
        super.fromMap(map);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@ class Membership {
 | 
				
			|||||||
      case MembershipType.GROUP:
 | 
					      case MembershipType.GROUP:
 | 
				
			||||||
        return groupLastActive;
 | 
					        return groupLastActive;
 | 
				
			||||||
      case MembershipType.CONVERSATION:
 | 
					      case MembershipType.CONVERSATION:
 | 
				
			||||||
        return conversation.lastActive;
 | 
					        return conversation.lastActivity;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        throw Exception("Unreachable statment!");
 | 
					        throw Exception("Unreachable statment!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								lib/models/new_conversation.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/models/new_conversation.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/cupertino.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// New conversation information
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewConversation {
 | 
				
			||||||
 | 
					  final String name;
 | 
				
			||||||
 | 
					  final List<int> members;
 | 
				
			||||||
 | 
					  final bool follow;
 | 
				
			||||||
 | 
					  final bool canEveryoneAddMembers;
 | 
				
			||||||
 | 
					  final String color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const NewConversation({
 | 
				
			||||||
 | 
					    @required this.name,
 | 
				
			||||||
 | 
					    @required this.members,
 | 
				
			||||||
 | 
					    @required this.follow,
 | 
				
			||||||
 | 
					    @required this.canEveryoneAddMembers,
 | 
				
			||||||
 | 
					    @required this.color,
 | 
				
			||||||
 | 
					  })  : assert(members != null),
 | 
				
			||||||
 | 
					        assert(members.length > 0),
 | 
				
			||||||
 | 
					        assert(follow != null),
 | 
				
			||||||
 | 
					        assert(canEveryoneAddMembers != null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								lib/models/new_conversation_settings.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/models/new_conversation_settings.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/widgets.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Conversation settings update
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewConversationsSettings {
 | 
				
			||||||
 | 
					  final int convID;
 | 
				
			||||||
 | 
					  final bool following;
 | 
				
			||||||
 | 
					  final bool isComplete;
 | 
				
			||||||
 | 
					  final String name;
 | 
				
			||||||
 | 
					  final bool canEveryoneAddMembers;
 | 
				
			||||||
 | 
					  final String color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const NewConversationsSettings({
 | 
				
			||||||
 | 
					    @required this.convID,
 | 
				
			||||||
 | 
					    @required this.following,
 | 
				
			||||||
 | 
					    @required this.isComplete,
 | 
				
			||||||
 | 
					    @required this.name,
 | 
				
			||||||
 | 
					    @required this.canEveryoneAddMembers,
 | 
				
			||||||
 | 
					    @required this.color,
 | 
				
			||||||
 | 
					  })  : assert(convID != null),
 | 
				
			||||||
 | 
					        assert(convID > 0),
 | 
				
			||||||
 | 
					        assert(following != null),
 | 
				
			||||||
 | 
					        assert(isComplete != null),
 | 
				
			||||||
 | 
					        assert(!isComplete && canEveryoneAddMembers != null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/models/conversation.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Unread conversation information
 | 
					/// Unread conversation information
 | 
				
			||||||
@@ -5,21 +7,12 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
/// @author Pierre Hubert
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UnreadConversation {
 | 
					class UnreadConversation {
 | 
				
			||||||
  final int id;
 | 
					  final Conversation conv;
 | 
				
			||||||
  final String convName;
 | 
					  final ConversationMessage message;
 | 
				
			||||||
  final int lastActive;
 | 
					 | 
				
			||||||
  final int userID;
 | 
					 | 
				
			||||||
  final String message;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const UnreadConversation({
 | 
					  const UnreadConversation({
 | 
				
			||||||
    @required this.id,
 | 
					    @required this.conv,
 | 
				
			||||||
    @required this.convName,
 | 
					 | 
				
			||||||
    @required this.lastActive,
 | 
					 | 
				
			||||||
    @required this.userID,
 | 
					 | 
				
			||||||
    @required this.message,
 | 
					    @required this.message,
 | 
				
			||||||
  })  : assert(id != null),
 | 
					  })  : assert(conv != null),
 | 
				
			||||||
        assert(convName != null),
 | 
					 | 
				
			||||||
        assert(lastActive != null),
 | 
					 | 
				
			||||||
        assert(userID != null),
 | 
					 | 
				
			||||||
        assert(message != null);
 | 
					        assert(message != null);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import 'package:comunic/ui/routes/main_route/main_route.dart';
 | 
				
			|||||||
import 'package:comunic/ui/routes/update_conversation_route.dart';
 | 
					import 'package:comunic/ui/routes/update_conversation_route.dart';
 | 
				
			||||||
import 'package:comunic/ui/screens/conversation_screen.dart';
 | 
					import 'package:comunic/ui/screens/conversation_screen.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/comunic_back_button_widget.dart';
 | 
					import 'package:comunic/ui/widgets/comunic_back_button_widget.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/widgets/safe_state.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/ui_utils.dart';
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
@@ -25,7 +26,7 @@ class ConversationRoute extends StatefulWidget {
 | 
				
			|||||||
  State<StatefulWidget> createState() => _ConversationRouteState();
 | 
					  State<StatefulWidget> createState() => _ConversationRouteState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _ConversationRouteState extends State<ConversationRoute> {
 | 
					class _ConversationRouteState extends SafeState<ConversationRoute> {
 | 
				
			||||||
  final ConversationsHelper _conversationsHelper = ConversationsHelper();
 | 
					  final ConversationsHelper _conversationsHelper = ConversationsHelper();
 | 
				
			||||||
  Conversation _conversation;
 | 
					  Conversation _conversation;
 | 
				
			||||||
  String _conversationName;
 | 
					  String _conversationName;
 | 
				
			||||||
@@ -42,21 +43,22 @@ class _ConversationRouteState extends State<ConversationRoute> {
 | 
				
			|||||||
  Future<void> _loadConversation() async {
 | 
					  Future<void> _loadConversation() async {
 | 
				
			||||||
    setError(false);
 | 
					    setError(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _conversation = await _conversationsHelper.getSingle(widget.conversationID,
 | 
					    try {
 | 
				
			||||||
        force: true);
 | 
					      _conversation = await _conversationsHelper
 | 
				
			||||||
 | 
					          .getSingle(widget.conversationID, force: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (_conversation == null) return setError(true);
 | 
					      if (_conversation == null) return setError(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final conversationName =
 | 
					      final conversationName =
 | 
				
			||||||
        await ConversationsHelper.getConversationNameAsync(_conversation);
 | 
					          await ConversationsHelper.getConversationNameAsync(_conversation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!this.mounted) return null;
 | 
					      if (!this.mounted) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (conversationName == null) return setError(true);
 | 
					      setState(() => _conversationName = conversationName);
 | 
				
			||||||
 | 
					    } catch (e, s) {
 | 
				
			||||||
    setState(() {
 | 
					      print("Failed to get conversation name! $e => $s");
 | 
				
			||||||
      _conversationName = conversationName;
 | 
					      setError(true);
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _openSettings() {
 | 
					  void _openSettings() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,19 +42,18 @@ class _UpdateConversationRoute extends State<UpdateConversationRoute> {
 | 
				
			|||||||
  Future<void> _loadConversation() async {
 | 
					  Future<void> _loadConversation() async {
 | 
				
			||||||
    setError(false);
 | 
					    setError(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final conversation = await ConversationsHelper()
 | 
					    try {
 | 
				
			||||||
        .getSingle(widget.conversationID, force: true);
 | 
					      final conversation = await ConversationsHelper()
 | 
				
			||||||
 | 
					          .getSingle(widget.conversationID, force: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (conversation == null) return setError(true);
 | 
					      //Load information about the members of the conversation
 | 
				
			||||||
 | 
					      _membersInfo = await UsersHelper().getList(conversation.membersID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Load information about the members of the conversation
 | 
					      setState(() => _conversation = conversation);
 | 
				
			||||||
    _membersInfo = await UsersHelper().getUsersInfo(conversation.members);
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
					      print("Failed to load conversation information! $e=>$s");
 | 
				
			||||||
    if (_membersInfo == null) return setError(true);
 | 
					      setError(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    setState(() {
 | 
					 | 
				
			||||||
      _conversation = conversation;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Build the body of this widget
 | 
					  /// Build the body of this widget
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,7 +110,7 @@ class _CallScreenState extends SafeState<CallScreen> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      // First, load information about the conversation
 | 
					      // First, load information about the conversation
 | 
				
			||||||
      _conversation =
 | 
					      _conversation =
 | 
				
			||||||
          await ConversationsHelper().getSingleOrThrow(convID, force: true);
 | 
					          await ConversationsHelper().getSingle(convID, force: true);
 | 
				
			||||||
      _convName =
 | 
					      _convName =
 | 
				
			||||||
          await ConversationsHelper.getConversationNameAsync(_conversation);
 | 
					          await ConversationsHelper.getConversationNameAsync(_conversation);
 | 
				
			||||||
      assert(_convName != null);
 | 
					      assert(_convName != null);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,8 +32,7 @@ class _ConversationMembersScreenState extends State<ConversationMembersScreen> {
 | 
				
			|||||||
  Future<void> _refresh() async {
 | 
					  Future<void> _refresh() async {
 | 
				
			||||||
    _conversation =
 | 
					    _conversation =
 | 
				
			||||||
        await ConversationsHelper().getSingle(widget.convID, force: true);
 | 
					        await ConversationsHelper().getSingle(widget.convID, force: true);
 | 
				
			||||||
    _members =
 | 
					    _members = await UsersHelper().getListWithThrow(_conversation.membersID);
 | 
				
			||||||
        await UsersHelper().getListWithThrow(_conversation.members.toSet());
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -55,12 +54,12 @@ class _ConversationMembersScreenState extends State<ConversationMembersScreen> {
 | 
				
			|||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _buildItem(BuildContext context, int index) {
 | 
					  Widget _buildItem(BuildContext context, int index) {
 | 
				
			||||||
    final user = _members.getUser(_conversation.members[index]);
 | 
					    final member = _conversation.members[index];
 | 
				
			||||||
 | 
					    final user = _members.getUser(member.userID);
 | 
				
			||||||
    return ListTile(
 | 
					    return ListTile(
 | 
				
			||||||
      leading: AccountImageWidget(user: user),
 | 
					      leading: AccountImageWidget(user: user),
 | 
				
			||||||
      title: Text(user.displayName),
 | 
					      title: Text(user.displayName),
 | 
				
			||||||
      subtitle:
 | 
					      subtitle: Text(member.isAdmin ? tr("Admin") : tr("Member")),
 | 
				
			||||||
          Text(_conversation.ownerID == user.id ? tr("Owner") : tr("Member")),
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,9 +70,8 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Method called when an error occurred while loading messages
 | 
					  /// Method called when an error occurred while loading messages
 | 
				
			||||||
  void _errorLoading() {
 | 
					  void _errorLoading() =>
 | 
				
			||||||
    _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR);
 | 
					      _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR);
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Load the first conversations
 | 
					  /// Load the first conversations
 | 
				
			||||||
  Future<void> _init() async {
 | 
					  Future<void> _init() async {
 | 
				
			||||||
@@ -86,22 +85,27 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
 | 
				
			|||||||
        .registerConversationEvents(widget.conversationID);
 | 
					        .registerConversationEvents(widget.conversationID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.listen<NewConversationMessageEvent>((ev) async {
 | 
					    this.listen<NewConversationMessageEvent>((ev) async {
 | 
				
			||||||
      if (ev.msg.conversationID == widget.conversationID) {
 | 
					      if (ev.msg.convID == widget.conversationID) {
 | 
				
			||||||
        await _conversationsHelper.saveMessage(ev.msg);
 | 
					        try {
 | 
				
			||||||
        await _applyNewMessages(ConversationMessagesList()..add(ev.msg));
 | 
					          await _conversationsHelper.saveMessage(ev.msg);
 | 
				
			||||||
 | 
					          await _applyNewMessages(ConversationMessagesList()..add(ev.msg));
 | 
				
			||||||
 | 
					        } catch (e, s) {
 | 
				
			||||||
 | 
					          print("Failed to show new message! $e => $s");
 | 
				
			||||||
 | 
					          _errorLoading();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.listen<UpdatedConversationMessageEvent>((ev) async {
 | 
					    this.listen<UpdatedConversationMessageEvent>((ev) async {
 | 
				
			||||||
      if (ev.msg.conversationID == widget.conversationID) {
 | 
					      if (ev.msg.convID == widget.conversationID) {
 | 
				
			||||||
        await _conversationsHelper.saveMessage(ev.msg);
 | 
					        await _conversationsHelper.saveMessage(ev.msg);
 | 
				
			||||||
        setState(() => _messages.replace(ev.msg));
 | 
					        setState(() => _messages.replace(ev.msg));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.listen<DeletedConversationMessageEvent>((ev) async {
 | 
					    this.listen<DeletedConversationMessageEvent>((ev) async {
 | 
				
			||||||
      if (ev.msg.conversationID == widget.conversationID) {
 | 
					      if (ev.msg.convID == widget.conversationID) {
 | 
				
			||||||
        await _conversationsHelper.removeMessage(ev.msg.id);
 | 
					        await _conversationsHelper.removeMessage(ev.msg);
 | 
				
			||||||
        setState(() => _messages.removeMsg(ev.msg.id));
 | 
					        setState(() => _messages.removeMsg(ev.msg.id));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -116,19 +120,23 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
 | 
				
			|||||||
  Future<void> _loadMessages(bool online) async {
 | 
					  Future<void> _loadMessages(bool online) async {
 | 
				
			||||||
    if (!mounted) return;
 | 
					    if (!mounted) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //First, get the messages
 | 
					    try {
 | 
				
			||||||
    final messages = await _conversationsHelper.getNewMessages(
 | 
					      //First, get the messages
 | 
				
			||||||
 | 
					      final messages = await _conversationsHelper.getNewMessages(
 | 
				
			||||||
        conversationID: widget.conversationID,
 | 
					        conversationID: widget.conversationID,
 | 
				
			||||||
        lastMessageID: _messages == null ? 0 : _messages.lastMessageID,
 | 
					        lastMessageID: _messages == null ? 0 : _messages.lastMessageID,
 | 
				
			||||||
        online: online);
 | 
					        online: online,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (messages == null) return _errorLoading();
 | 
					      // In case we are offline and we did not get any message we do not do
 | 
				
			||||||
 | 
					      // anything (we wait for the online request)
 | 
				
			||||||
 | 
					      if (messages.length == 0 && !online) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // In case we are offline and we did not get any message we do not do
 | 
					      await _applyNewMessages(messages);
 | 
				
			||||||
    // anything (we wait for the online request)
 | 
					    } catch (e, s) {
 | 
				
			||||||
    if (messages.length == 0 && !online) return;
 | 
					      print("Failed to load messages! $e => $s");
 | 
				
			||||||
 | 
					      _errorLoading();
 | 
				
			||||||
    await _applyNewMessages(messages);
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Get older messages
 | 
					  /// Get older messages
 | 
				
			||||||
@@ -136,45 +144,43 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
 | 
				
			|||||||
    if (_loadingOlderMessages != _OlderMessagesLevel.NONE ||
 | 
					    if (_loadingOlderMessages != _OlderMessagesLevel.NONE ||
 | 
				
			||||||
        _messages == null ||
 | 
					        _messages == null ||
 | 
				
			||||||
        _messages.length == 0) return;
 | 
					        _messages.length == 0) return;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // Let's start to load older messages
 | 
				
			||||||
 | 
					      _setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Let's start to load older messages
 | 
					      final messages = await _conversationsHelper.getOlderMessages(
 | 
				
			||||||
    _setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
 | 
					          conversationID: widget.conversationID,
 | 
				
			||||||
 | 
					          oldestMessagesID: _messages.firstMessageID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final messages = await _conversationsHelper.getOlderMessages(
 | 
					      // Mark as not loading anymore
 | 
				
			||||||
        conversationID: widget.conversationID,
 | 
					      _setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
 | 
				
			||||||
        oldestMessagesID: _messages.firstMessageID);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Mark as not loading anymore
 | 
					      // Check if there is no more unread messages
 | 
				
			||||||
    _setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
 | 
					      if (messages.length == 0) {
 | 
				
			||||||
 | 
					        _setLoadingOlderMessagesState(_OlderMessagesLevel.NO_MORE_AVAILABLE);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check for errors
 | 
					      // Apply the messages
 | 
				
			||||||
    if (messages == null) {
 | 
					      _applyNewMessages(messages);
 | 
				
			||||||
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
					      print("Failed to load older messages! $e => $s");
 | 
				
			||||||
      _errorLoading();
 | 
					      _errorLoading();
 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Check if there is no more unread messages
 | 
					 | 
				
			||||||
    if (messages.length == 0) {
 | 
					 | 
				
			||||||
      _setLoadingOlderMessagesState(_OlderMessagesLevel.NO_MORE_AVAILABLE);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Apply the messages
 | 
					 | 
				
			||||||
    _applyNewMessages(messages);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Apply new messages [messages] must not be null
 | 
					  /// Apply new messages [messages] must not be null
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Throws in case of failure
 | 
				
			||||||
  Future<void> _applyNewMessages(ConversationMessagesList messages) async {
 | 
					  Future<void> _applyNewMessages(ConversationMessagesList messages) async {
 | 
				
			||||||
    // We ignore new messages once the area is no longer visible
 | 
					    // We ignore new messages once the area is no longer visible
 | 
				
			||||||
    if (!this.mounted) return;
 | 
					    if (!this.mounted) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Then get information about users
 | 
					    //Then get information about users
 | 
				
			||||||
    final usersToGet =
 | 
					    final usersToGet =
 | 
				
			||||||
        findMissingFromList(_usersInfo.usersID, messages.getUsersID());
 | 
					        findMissingFromList(_usersInfo.usersID, messages.getUsersID()).toSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final users = await _usersHelper.getUsersInfo(usersToGet);
 | 
					    final users = await _usersHelper.getList(usersToGet);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (users == null) _errorLoading();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Save the new list of messages
 | 
					    // Save the new list of messages
 | 
				
			||||||
    setState(() {
 | 
					    setState(() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,32 +62,24 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
 | 
				
			|||||||
    await _loadConversationsList(false);
 | 
					    await _loadConversationsList(false);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _gotLoadingError() {
 | 
					 | 
				
			||||||
    setError(_list == null ? LoadErrorLevel.MAJOR : LoadErrorLevel.MINOR);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Load the list of conversations
 | 
					  /// Load the list of conversations
 | 
				
			||||||
  Future<void> _loadConversationsList(bool cached) async {
 | 
					  Future<void> _loadConversationsList(bool cached) async {
 | 
				
			||||||
    setError(LoadErrorLevel.NONE);
 | 
					    setError(LoadErrorLevel.NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Get the list of conversations
 | 
					    try {
 | 
				
			||||||
    var list;
 | 
					      ConversationsList list = cached
 | 
				
			||||||
    if (cached)
 | 
					          ? await _conversationsHelper.getCachedList()
 | 
				
			||||||
      list = await _conversationsHelper.getCachedList();
 | 
					          : await _conversationsHelper.downloadList();
 | 
				
			||||||
    else
 | 
					      assert(list != null);
 | 
				
			||||||
      list = await _conversationsHelper.downloadList();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (list == null) return _gotLoadingError();
 | 
					      //Get information about the members of the conversations
 | 
				
			||||||
 | 
					      list.users = await _usersHelper.getList(list.allUsersID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //Get information about the members of the conversations
 | 
					      setState(() => _list = list);
 | 
				
			||||||
    list.users = await _usersHelper.getUsersInfo(list.allUsersID);
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
					      debugPrint("Failed to get conversations list! $e => $s", wrapWidth: 1024);
 | 
				
			||||||
    if (list.users == null) return _gotLoadingError();
 | 
					      setError(_list == null ? LoadErrorLevel.MAJOR : LoadErrorLevel.MINOR);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    //Save list
 | 
					 | 
				
			||||||
    setState(() {
 | 
					 | 
				
			||||||
      _list = list;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Build an error card
 | 
					  /// Build an error card
 | 
				
			||||||
@@ -159,9 +151,13 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
 | 
				
			|||||||
    if (result == null || !result) return;
 | 
					    if (result == null || !result) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Request the conversation to be deleted now
 | 
					    // Request the conversation to be deleted now
 | 
				
			||||||
    if (!await _conversationsHelper.deleteConversation(conversation.id))
 | 
					    try {
 | 
				
			||||||
 | 
					      await _conversationsHelper.deleteConversation(conversation.id);
 | 
				
			||||||
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
					      print("Failed to delete conversation! $e => $s");
 | 
				
			||||||
      Scaffold.of(context).showSnackBar(
 | 
					      Scaffold.of(context).showSnackBar(
 | 
				
			||||||
          SnackBar(content: Text(tr("Could not delete the conversation!"))));
 | 
					          SnackBar(content: Text(tr("Could not delete the conversation!"))));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Reload the list of conversations
 | 
					    // Reload the list of conversations
 | 
				
			||||||
    _loadConversationsList(false);
 | 
					    _loadConversationsList(false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,11 +3,8 @@ import 'package:comunic/helpers/events_helper.dart';
 | 
				
			|||||||
import 'package:comunic/helpers/users_helper.dart';
 | 
					import 'package:comunic/helpers/users_helper.dart';
 | 
				
			||||||
import 'package:comunic/lists/unread_conversations_list.dart';
 | 
					import 'package:comunic/lists/unread_conversations_list.dart';
 | 
				
			||||||
import 'package:comunic/lists/users_list.dart';
 | 
					import 'package:comunic/lists/users_list.dart';
 | 
				
			||||||
import 'package:comunic/ui/routes/main_route/main_route.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/ui/widgets/account_image_widget.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/ui/widgets/async_screen_widget.dart';
 | 
					import 'package:comunic/ui/widgets/async_screen_widget.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/safe_state.dart';
 | 
					import 'package:comunic/ui/widgets/safe_state.dart';
 | 
				
			||||||
import 'package:comunic/utils/date_utils.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,7 +67,7 @@ class _UnreadConversationsScreenState
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _tileBuilder(BuildContext context, int index) {
 | 
					  Widget _tileBuilder(BuildContext context, int index) {
 | 
				
			||||||
    final conv = _list[index];
 | 
					    /*final conv = _list[index];
 | 
				
			||||||
    final user = _users.getUser(conv.userID);
 | 
					    final user = _users.getUser(conv.userID);
 | 
				
			||||||
    return ListTile(
 | 
					    return ListTile(
 | 
				
			||||||
      leading: AccountImageWidget(user: user),
 | 
					      leading: AccountImageWidget(user: user),
 | 
				
			||||||
@@ -83,9 +80,12 @@ class _UnreadConversationsScreenState
 | 
				
			|||||||
            style: TextStyle(fontStyle: FontStyle.italic),
 | 
					            style: TextStyle(fontStyle: FontStyle.italic),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ]),
 | 
					        ]),
 | 
				
			||||||
      ),
 | 
					      )
 | 
				
			||||||
      trailing: Text(diffTimeFromNowToStr(conv.lastActive)),
 | 
					      trailing: Text(diffTimeFromNowToStr(conv.lastActive)),
 | 
				
			||||||
      onTap: () => MainController.of(context).openConversation(conv.id),
 | 
					      onTap: () => MainController.of(context).openConversation(conv.id),
 | 
				
			||||||
    );
 | 
					    );*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO : reimplement
 | 
				
			||||||
 | 
					    throw new Exception("unimplemented");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,6 @@
 | 
				
			|||||||
import 'package:comunic/helpers/conversations_helper.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/lists/users_list.dart';
 | 
					import 'package:comunic/lists/users_list.dart';
 | 
				
			||||||
import 'package:comunic/models/conversation.dart';
 | 
					import 'package:comunic/models/conversation.dart';
 | 
				
			||||||
import 'package:comunic/models/user.dart';
 | 
					import 'package:comunic/models/user.dart';
 | 
				
			||||||
import 'package:comunic/ui/routes/main_route/main_route.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/ui/tiles/simple_user_tile.dart';
 | 
					import 'package:comunic/ui/tiles/simple_user_tile.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/pick_user_widget.dart';
 | 
					import 'package:comunic/ui/widgets/pick_user_widget.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
@@ -36,11 +34,11 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  get isUpdating => widget.initialSettings != null;
 | 
					  get isUpdating => widget.initialSettings != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get isOwner => !isUpdating || widget.initialSettings.isOwner;
 | 
					  get isAdmin => !isUpdating || widget.initialSettings.isAdmin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Conversation get _initialSettings => widget.initialSettings;
 | 
					  Conversation get _initialSettings => widget.initialSettings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get _canAddMembers => isOwner || _initialSettings.canEveryoneAddMembers;
 | 
					  bool get _canAddMembers => isAdmin || _initialSettings.canEveryoneAddMembers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
@@ -68,7 +66,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
              decoration: InputDecoration(
 | 
					              decoration: InputDecoration(
 | 
				
			||||||
                labelText: tr("Conversation name (optionnal)"),
 | 
					                labelText: tr("Conversation name (optionnal)"),
 | 
				
			||||||
                alignLabelWithHint: true,
 | 
					                alignLabelWithHint: true,
 | 
				
			||||||
                enabled: isOwner,
 | 
					                enabled: isAdmin,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -90,7 +88,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
              children: <Widget>[
 | 
					              children: <Widget>[
 | 
				
			||||||
                Switch.adaptive(
 | 
					                Switch.adaptive(
 | 
				
			||||||
                  value: _canEveryoneAddMembers,
 | 
					                  value: _canEveryoneAddMembers,
 | 
				
			||||||
                  onChanged: isOwner
 | 
					                  onChanged: isAdmin
 | 
				
			||||||
                      ? (b) => setState(() {
 | 
					                      ? (b) => setState(() {
 | 
				
			||||||
                            _canEveryoneAddMembers = b;
 | 
					                            _canEveryoneAddMembers = b;
 | 
				
			||||||
                          })
 | 
					                          })
 | 
				
			||||||
@@ -126,7 +124,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
                                  PopupMenuItem(
 | 
					                                  PopupMenuItem(
 | 
				
			||||||
                                    child: Text(tr("Remove")),
 | 
					                                    child: Text(tr("Remove")),
 | 
				
			||||||
                                    value: _MembersMenuChoices.REMOVE,
 | 
					                                    value: _MembersMenuChoices.REMOVE,
 | 
				
			||||||
                                    enabled: isOwner ||
 | 
					                                    enabled: isAdmin ||
 | 
				
			||||||
                                        (_canEveryoneAddMembers &&
 | 
					                                        (_canEveryoneAddMembers &&
 | 
				
			||||||
                                            !_initialSettings.members
 | 
					                                            !_initialSettings.members
 | 
				
			||||||
                                                .contains(f.id)),
 | 
					                                                .contains(f.id)),
 | 
				
			||||||
@@ -163,7 +161,8 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Submit the conversation
 | 
					  /// Submit the conversation
 | 
				
			||||||
  Future<void> _submitForm() async {
 | 
					  Future<void> _submitForm() async {
 | 
				
			||||||
    final settings = Conversation(
 | 
					    // TODO : reimplement
 | 
				
			||||||
 | 
					    /* final settings = Conversation(
 | 
				
			||||||
        id: isUpdating ? widget.initialSettings.id : 0,
 | 
					        id: isUpdating ? widget.initialSettings.id : 0,
 | 
				
			||||||
        ownerID: isUpdating ? widget.initialSettings.ownerID : 0,
 | 
					        ownerID: isUpdating ? widget.initialSettings.ownerID : 0,
 | 
				
			||||||
        name: _nameController.text,
 | 
					        name: _nameController.text,
 | 
				
			||||||
@@ -198,6 +197,6 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    MainController.of(context).popPage();
 | 
					    MainController.of(context).popPage();
 | 
				
			||||||
    if (!isUpdating)
 | 
					    if (!isUpdating)
 | 
				
			||||||
      MainController.of(context).openConversation(conversationID);
 | 
					      MainController.of(context).openConversation(conversationID);*/
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
import 'package:comunic/models/user.dart';
 | 
					import 'package:comunic/models/user.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/account_image_widget.dart';
 | 
					import 'package:comunic/ui/widgets/account_image_widget.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
					 | 
				
			||||||
import 'package:comunic/ui/widgets/text_widget.dart';
 | 
					import 'package:comunic/ui/widgets/text_widget.dart';
 | 
				
			||||||
import 'package:comunic/utils/date_utils.dart';
 | 
					import 'package:comunic/utils/date_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
@@ -72,7 +71,9 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Build widget image
 | 
					  /// Build widget image
 | 
				
			||||||
  Widget _buildMessageImage(BuildContext context) {
 | 
					  Widget _buildMessageImage(BuildContext context) {
 | 
				
			||||||
    return Container(
 | 
					    return Text("");
 | 
				
			||||||
 | 
					    // TODO : fix file
 | 
				
			||||||
 | 
					    /*return Container(
 | 
				
			||||||
      margin: EdgeInsets.only(bottom: 2),
 | 
					      margin: EdgeInsets.only(bottom: 2),
 | 
				
			||||||
      child: NetworkImageWidget(
 | 
					      child: NetworkImageWidget(
 | 
				
			||||||
        url: message.imageURL,
 | 
					        url: message.imageURL,
 | 
				
			||||||
@@ -80,7 +81,7 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
        width: 200,
 | 
					        width: 200,
 | 
				
			||||||
        height: 200,
 | 
					        height: 200,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );*/
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Build message date
 | 
					  /// Build message date
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,7 +80,7 @@ class ConversationTile extends StatelessWidget {
 | 
				
			|||||||
        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
					        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
				
			||||||
        children: <Widget>[
 | 
					        children: <Widget>[
 | 
				
			||||||
          _buildSubInformation(
 | 
					          _buildSubInformation(
 | 
				
			||||||
              Icons.access_time, diffTimeFromNowToStr(conversation.lastActive)),
 | 
					              Icons.access_time, diffTimeFromNowToStr(conversation.lastActivity)),
 | 
				
			||||||
          _buildSubInformation(
 | 
					          _buildSubInformation(
 | 
				
			||||||
            Icons.group,
 | 
					            Icons.group,
 | 
				
			||||||
            conversation.members.length == 1
 | 
					            conversation.members.length == 1
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,33 +119,36 @@ class PostsListWidgetState extends SafeState<PostsListWidget> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    _loading = true;
 | 
					    _loading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final list = !getOlder
 | 
					    try {
 | 
				
			||||||
        ? await widget.getPostsList()
 | 
					      final list = !getOlder
 | 
				
			||||||
        : await widget.getOlder(_list.oldestID);
 | 
					          ? await widget.getPostsList()
 | 
				
			||||||
 | 
					          : await widget.getOlder(_list.oldestID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (list == null) return _loadError();
 | 
					      if (list == null) return _loadError();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final users = await _usersHelper.getList(list.usersID);
 | 
					      final users = await _usersHelper.getList(list.usersID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (users == null) return _loadError();
 | 
					      final groups = await _groupsHelper.getList(list.groupsID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final groups = await _groupsHelper.getList(list.groupsID);
 | 
					      if (groups == null) return _loadError();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (groups == null) return _loadError();
 | 
					      if (!mounted) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!mounted) return;
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        if (!getOlder) {
 | 
				
			||||||
    setState(() {
 | 
					          _list = list;
 | 
				
			||||||
      if (!getOlder) {
 | 
					          _users = users;
 | 
				
			||||||
        _list = list;
 | 
					          _groups = groups;
 | 
				
			||||||
        _users = users;
 | 
					        } else {
 | 
				
			||||||
        _groups = groups;
 | 
					          _list.addAll(list);
 | 
				
			||||||
      } else {
 | 
					          _users.addAll(users);
 | 
				
			||||||
        _list.addAll(list);
 | 
					          _groups.addAll(groups);
 | 
				
			||||||
        _users.addAll(users);
 | 
					        }
 | 
				
			||||||
        _groups.addAll(groups);
 | 
					      });
 | 
				
			||||||
      }
 | 
					    } catch (e, s) {
 | 
				
			||||||
    });
 | 
					      print("Failed to load post information ! $e => $s");
 | 
				
			||||||
 | 
					      _loadError();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _loading = false;
 | 
					    _loading = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ class _ConversationWindowState extends SafeState<ConversationWindow> {
 | 
				
			|||||||
    _refresh();
 | 
					    _refresh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    listen<NewConversationMessageEvent>((e) {
 | 
					    listen<NewConversationMessageEvent>((e) {
 | 
				
			||||||
      if (e.msg.conversationID == _convID &&
 | 
					      if (e.msg.convID == _convID &&
 | 
				
			||||||
          _collapsed &&
 | 
					          _collapsed &&
 | 
				
			||||||
          e.msg.userID != userID()) setState(() => _hasNewMessages = true);
 | 
					          e.msg.userID != userID()) setState(() => _hasNewMessages = true);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,16 +10,16 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Open a private conversation with a given [userID]
 | 
					/// Open a private conversation with a given [userID]
 | 
				
			||||||
Future<bool> openPrivateConversation(BuildContext context, int userID) async {
 | 
					Future<bool> openPrivateConversation(BuildContext context, int userID) async {
 | 
				
			||||||
  final convID = await ConversationsHelper().getPrivate(userID);
 | 
					  try {
 | 
				
			||||||
 | 
					    final convID = await ConversationsHelper().getPrivate(userID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (convID == null) {
 | 
					    // Open the conversation
 | 
				
			||||||
 | 
					    MainController.of(context).openConversation(convID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  } catch (e, s) {
 | 
				
			||||||
 | 
					    print("Failed to find private conversation! $e => $s");
 | 
				
			||||||
    showSimpleSnack(context, tr("Could not find a private conversation!"));
 | 
					    showSimpleSnack(context, tr("Could not find a private conversation!"));
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Open the conversation
 | 
					 | 
				
			||||||
  MainController.of(context).openConversation(convID);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Success
 | 
					 | 
				
			||||||
  return true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -346,7 +346,7 @@ packages:
 | 
				
			|||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.8.0-nullsafety.1"
 | 
					    version: "1.8.0-nullsafety.1"
 | 
				
			||||||
  path_provider:
 | 
					  path_provider:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: path_provider
 | 
					      name: path_provider
 | 
				
			||||||
      url: "https://pub.dartlang.org"
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ description: Comunic client
 | 
				
			|||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 | 
					# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 | 
				
			||||||
# Read more about iOS versioning at
 | 
					# Read more about iOS versioning at
 | 
				
			||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
					# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
				
			||||||
version: 1.1.1+5
 | 
					version: 1.1.2+6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ">=2.1.0 <3.0.0"
 | 
					  sdk: ">=2.1.0 <3.0.0"
 | 
				
			||||||
@@ -91,6 +91,8 @@ dependencies:
 | 
				
			|||||||
  # Version manager
 | 
					  # Version manager
 | 
				
			||||||
  version: ^1.2.0
 | 
					  version: ^1.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  path_provider: ^1.6.27
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    sdk: flutter
 | 
					    sdk: flutter
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user