mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 04:49:21 +00:00
Start conversation upgrade
This commit is contained in:
parent
b094361f5a
commit
dacccf57b5
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;
|
|
||||||
|
|
||||||
try {
|
|
||||||
ConversationsList list = ConversationsList();
|
ConversationsList list = ConversationsList();
|
||||||
response.getArray().forEach((f) => list.add(apiToConversation(f)));
|
response.getArray().forEach((f) => list.add(apiToConversation(f)));
|
||||||
|
|
||||||
// Update the database
|
// Update the database
|
||||||
await _conversationsDatabaseHelper.clearTable();
|
await ConversationsSerializationHelper().setList(list);
|
||||||
await _conversationsDatabaseHelper.insertAll(list);
|
|
||||||
|
|
||||||
return 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,
|
|
||||||
args: {
|
|
||||||
"conversationID": conversationID.toString(),
|
"conversationID": conversationID.toString(),
|
||||||
"oldest_message_id": oldestMessagesID.toString(),
|
"oldest_message_id": oldestMessagesID.toString(),
|
||||||
"limit": limit.toString()
|
"limit": limit.toString()
|
||||||
}).exec();
|
}).execWithThrow();
|
||||||
|
|
||||||
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,
|
||||||
) {
|
) {
|
||||||
return ConversationMessage(
|
var file;
|
||||||
id: map["ID"],
|
if (map["file"] != null) {
|
||||||
conversationID: map["convID"],
|
final fileMap = map["file"];
|
||||||
userID: map["ID_user"],
|
file = ConversationMessageFile(
|
||||||
timeInsert: map["time_insert"],
|
url: fileMap["url"],
|
||||||
message: DisplayedString(map["message"]),
|
size: fileMap["size"],
|
||||||
imageURL: map["image_path"],
|
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"],
|
||||||
|
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";
|
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,8 +43,9 @@ 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);
|
||||||
|
|
||||||
@ -52,11 +54,11 @@ class _ConversationRouteState extends State<ConversationRoute> {
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
try {
|
||||||
final conversation = await ConversationsHelper()
|
final conversation = await ConversationsHelper()
|
||||||
.getSingle(widget.conversationID, force: true);
|
.getSingle(widget.conversationID, force: true);
|
||||||
|
|
||||||
if (conversation == null) return setError(true);
|
|
||||||
|
|
||||||
//Load information about the members of the conversation
|
//Load information about the members of the conversation
|
||||||
_membersInfo = await UsersHelper().getUsersInfo(conversation.members);
|
_membersInfo = await UsersHelper().getList(conversation.membersID);
|
||||||
|
|
||||||
if (_membersInfo == null) return setError(true);
|
setState(() => _conversation = conversation);
|
||||||
|
} catch (e, s) {
|
||||||
setState(() {
|
print("Failed to load conversation information! $e=>$s");
|
||||||
_conversation = conversation;
|
setError(true);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) {
|
||||||
|
try {
|
||||||
await _conversationsHelper.saveMessage(ev.msg);
|
await _conversationsHelper.saveMessage(ev.msg);
|
||||||
await _applyNewMessages(ConversationMessagesList()..add(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;
|
||||||
|
|
||||||
|
try {
|
||||||
//First, get the messages
|
//First, get the messages
|
||||||
final messages = await _conversationsHelper.getNewMessages(
|
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
|
// In case we are offline and we did not get any message we do not do
|
||||||
// anything (we wait for the online request)
|
// anything (we wait for the online request)
|
||||||
if (messages.length == 0 && !online) return;
|
if (messages.length == 0 && !online) return;
|
||||||
|
|
||||||
await _applyNewMessages(messages);
|
await _applyNewMessages(messages);
|
||||||
|
} catch (e, s) {
|
||||||
|
print("Failed to load messages! $e => $s");
|
||||||
|
_errorLoading();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get older messages
|
/// Get older messages
|
||||||
@ -136,7 +144,7 @@ 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
|
// Let's start to load older messages
|
||||||
_setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
|
_setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
|
||||||
|
|
||||||
@ -147,12 +155,6 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
|||||||
// Mark as not loading anymore
|
// Mark as not loading anymore
|
||||||
_setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
|
_setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
|
||||||
|
|
||||||
// Check for errors
|
|
||||||
if (messages == null) {
|
|
||||||
_errorLoading();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there is no more unread messages
|
// Check if there is no more unread messages
|
||||||
if (messages.length == 0) {
|
if (messages.length == 0) {
|
||||||
_setLoadingOlderMessagesState(_OlderMessagesLevel.NO_MORE_AVAILABLE);
|
_setLoadingOlderMessagesState(_OlderMessagesLevel.NO_MORE_AVAILABLE);
|
||||||
@ -161,20 +163,24 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
|||||||
|
|
||||||
// Apply the messages
|
// Apply the messages
|
||||||
_applyNewMessages(messages);
|
_applyNewMessages(messages);
|
||||||
|
} catch (e, s) {
|
||||||
|
print("Failed to load older messages! $e => $s");
|
||||||
|
_errorLoading();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
//Get information about the members of the conversations
|
||||||
list.users = await _usersHelper.getUsersInfo(list.allUsersID);
|
list.users = await _usersHelper.getList(list.allUsersID);
|
||||||
|
|
||||||
if (list.users == null) return _gotLoadingError();
|
setState(() => _list = list);
|
||||||
|
} catch (e, s) {
|
||||||
//Save list
|
debugPrint("Failed to get conversations list! $e => $s", wrapWidth: 1024);
|
||||||
setState(() {
|
setError(_list == null ? LoadErrorLevel.MAJOR : LoadErrorLevel.MINOR);
|
||||||
_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,6 +119,7 @@ class PostsListWidgetState extends SafeState<PostsListWidget> {
|
|||||||
|
|
||||||
_loading = true;
|
_loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
final list = !getOlder
|
final list = !getOlder
|
||||||
? await widget.getPostsList()
|
? await widget.getPostsList()
|
||||||
: await widget.getOlder(_list.oldestID);
|
: await widget.getOlder(_list.oldestID);
|
||||||
@ -127,8 +128,6 @@ class PostsListWidgetState extends SafeState<PostsListWidget> {
|
|||||||
|
|
||||||
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();
|
||||||
@ -146,6 +145,10 @@ class PostsListWidgetState extends SafeState<PostsListWidget> {
|
|||||||
_groups.addAll(groups);
|
_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 {
|
||||||
|
try {
|
||||||
final convID = await ConversationsHelper().getPrivate(userID);
|
final convID = await ConversationsHelper().getPrivate(userID);
|
||||||
|
|
||||||
if (convID == null) {
|
|
||||||
showSimpleSnack(context, tr("Could not find a private conversation!"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the conversation
|
// Open the conversation
|
||||||
MainController.of(context).openConversation(convID);
|
MainController.of(context).openConversation(convID);
|
||||||
|
|
||||||
// Success
|
|
||||||
return true;
|
return true;
|
||||||
|
} catch (e, s) {
|
||||||
|
print("Failed to find private conversation! $e => $s");
|
||||||
|
showSimpleSnack(context, tr("Could not find a private conversation!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user