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