From e2202a479411448c6962f895c7e0634b28d9db59 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 25 Apr 2019 08:56:16 +0200 Subject: [PATCH] Get conversation message --- lib/helpers/conversations_helper.dart | 37 +++++++- lib/lists/conversation_messages_list.dart | 35 ++++++++ lib/lists/users_list.dart | 16 ++-- lib/models/conversation_message.dart | 29 +++++++ lib/ui/routes/conversation_route.dart | 13 +-- lib/ui/screens/conversation_screen.dart | 94 +++++++++++++++++++++ lib/ui/tiles/conversation_message_tile.dart | 22 +++++ lib/utils/list_utils.dart | 13 +++ 8 files changed, 245 insertions(+), 14 deletions(-) create mode 100644 lib/lists/conversation_messages_list.dart create mode 100644 lib/models/conversation_message.dart create mode 100644 lib/ui/screens/conversation_screen.dart create mode 100644 lib/ui/tiles/conversation_message_tile.dart diff --git a/lib/helpers/conversations_helper.dart b/lib/helpers/conversations_helper.dart index cea727b..d9a2bc6 100644 --- a/lib/helpers/conversations_helper.dart +++ b/lib/helpers/conversations_helper.dart @@ -1,9 +1,11 @@ import 'package:comunic/helpers/database/conversations_database_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/lists/conversation_messages_list.dart'; import 'package:comunic/lists/conversations_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/conversation.dart'; +import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/utils/account_utils.dart'; /// Conversation helper @@ -67,7 +69,7 @@ class ConversationsHelper { /// cached version of the conversation will be used, else it will always get /// the information from the server Future getSingle(int id, {bool force = false}) async { - if(force || ! await _conversationsDatabaseHelper.has(id)) + if (force || !await _conversationsDatabaseHelper.has(id)) return await _downloadSingle(id); else return _conversationsDatabaseHelper.get(id); @@ -112,7 +114,7 @@ class ConversationsHelper { } /// Turn an API entry into a [Conversation] object - Conversation _apiToConversation(Map map){ + Conversation _apiToConversation(Map map) { return Conversation( id: map["ID"], ownerID: map["ID_owner"], @@ -123,4 +125,35 @@ class ConversationsHelper { members: map["members"].map((f) => int.parse(f)).toList(), ); } + + /// Refresh the list of messages of a conversation + /// + /// Set [lastMessageID] to 0 to specify that we do not have any message of the + /// conversation yet or another value else + Future downloadNewMessagesSingle(int conversationID, + {int lastMessageID = 0}) async { + // Execute the request on the server + final response = await APIRequest( + uri: "conversations/refresh_single", + needLogin: true, + args: { + "conversationID": conversationID.toString(), + "last_message_id": lastMessageID.toString() + }).exec(); + + if (response.code != 200) return null; + + // Parse the response of the server + ConversationMessagesList list = ConversationMessagesList(); + response.getArray().forEach((f) { + list.add(ConversationMessage( + id: f["ID"], + userID: f["ID_user"], + timeInsert: f["time_insert"], + message: f["message"], + imageURL: f["image_path"])); + }); + + return list; + } } diff --git a/lib/lists/conversation_messages_list.dart b/lib/lists/conversation_messages_list.dart new file mode 100644 index 0000000..527fa58 --- /dev/null +++ b/lib/lists/conversation_messages_list.dart @@ -0,0 +1,35 @@ +import 'dart:collection'; + +import 'package:comunic/models/conversation_message.dart'; + +/// Conversations messages list +/// +/// @author Pierre HUBERT + +class ConversationMessagesList extends ListBase { + final List _list = List(); + + set length(int v) => _list.length = v; + + int get length => _list.length; + + @override + ConversationMessage operator [](int index) { + return _list[index]; + } + + @override + void operator []=(int index, ConversationMessage value) { + _list[index] = value; + } + + /// Get the list of the users ID who own a message in this list + List getUsersID() { + final List users = List(); + + for (ConversationMessage message in this) + if (!users.contains(message.userID)) users.add(message.userID); + + return users; + } +} diff --git a/lib/lists/users_list.dart b/lib/lists/users_list.dart index 12dd5e4..933dc98 100644 --- a/lib/lists/users_list.dart +++ b/lib/lists/users_list.dart @@ -7,10 +7,10 @@ import 'package:comunic/models/user.dart'; /// @author Pierre HUBERT class UsersList extends ListBase { - List _list = List(); int get length => _list.length; + set length(l) => _list.length = l; @override @@ -24,11 +24,13 @@ class UsersList extends ListBase { } /// Find a user with a specific ID - User getUser(int userID){ - for(int i = 0; i < this.length; i++) - if(this[i].id == userID) - return this[i]; + User getUser(int userID) { + for (int i = 0; i < this.length; i++) + if (this[i].id == userID) return this[i]; - throw "User not found in the list!"; + throw "User not found in the list!"; } -} \ No newline at end of file + + /// Get the list of users ID present in this list + List get usersID => List.generate(length, (i) => this[i].id); +} diff --git a/lib/models/conversation_message.dart b/lib/models/conversation_message.dart new file mode 100644 index 0000000..26c54d1 --- /dev/null +++ b/lib/models/conversation_message.dart @@ -0,0 +1,29 @@ +import 'package:meta/meta.dart'; + +/// Single conversation message +/// +/// @author Pierre HUBERT + +class ConversationMessage implements Comparable { + final int id; + final int userID; + final int timeInsert; + final String message; + final String imageURL; + + const ConversationMessage({ + @required this.id, + @required this.userID, + @required this.timeInsert, + @required this.message, + @required this.imageURL, + }) : assert(id != null), + assert(userID != null), + assert(timeInsert != null), + assert(message != null); + + @override + int compareTo(other) { + return id.compareTo(other.id); + } +} diff --git a/lib/ui/routes/conversation_route.dart b/lib/ui/routes/conversation_route.dart index 1c90388..4d217fc 100644 --- a/lib/ui/routes/conversation_route.dart +++ b/lib/ui/routes/conversation_route.dart @@ -1,5 +1,6 @@ import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/models/conversation.dart'; +import 'package:comunic/ui/screens/conversation_screen.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; @@ -38,16 +39,14 @@ class _ConversationRouteState extends State { Future _loadConversation() async { setError(false); - _conversation = - await _conversationsHelper.getSingle(widget.conversationID); + _conversation = await _conversationsHelper.getSingle(widget.conversationID); if (_conversation == null) return setError(true); final conversationName = await ConversationsHelper.getConversationNameAsync(_conversation); - if(conversationName == null) - return setError(true); + if (conversationName == null) return setError(true); setState(() { _conversationName = conversationName; @@ -72,8 +71,12 @@ class _ConversationRouteState extends State { ], ); - //if (_conversationName == null || _conversation == null) + if (_conversationName == null || _conversation == null) return buildCenteredProgressBar(); + + return ConversationScreen( + conversationID: widget.conversationID, + ); } @override diff --git a/lib/ui/screens/conversation_screen.dart b/lib/ui/screens/conversation_screen.dart new file mode 100644 index 0000000..e97a1e8 --- /dev/null +++ b/lib/ui/screens/conversation_screen.dart @@ -0,0 +1,94 @@ +import 'package:comunic/helpers/conversations_helper.dart'; +import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/lists/conversation_messages_list.dart'; +import 'package:comunic/lists/users_list.dart'; +import 'package:comunic/ui/tiles/conversation_message_tile.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/list_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +/// Conversation screen +/// +/// @author Pierre HUBERT + +enum ErrorLevel { NONE, MINOR, MAJOR } + +class ConversationScreen extends StatefulWidget { + final int conversationID; + + const ConversationScreen({Key key, this.conversationID}) + : assert(conversationID != null), + super(key: key); + + @override + State createState() => _ConversationScreenState(); +} + +class _ConversationScreenState extends State { + final ConversationsHelper _conversationsHelper = ConversationsHelper(); + final UsersHelper _usersHelper = UsersHelper(); + ConversationMessagesList _messages; + UsersList _usersInfo = UsersList(); + ErrorLevel _error = ErrorLevel.NONE; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _loadMessages(); + } + + void _setError(ErrorLevel err) => setState(() => _error = err); + + /// Method called when an error occurred while loading messages + void _errorLoading() { + _setError(_messages == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR); + } + + /// Load a list of messages + Future _loadMessages() async { + //First, get the messages + final messages = await _conversationsHelper + .downloadNewMessagesSingle(widget.conversationID); + + if (messages == null) return _errorLoading(); + + //Then get information about users + final usersToGet = + findMissingFromList(_usersInfo.usersID, messages.getUsersID()); + + final users = await _usersHelper.getUsersInfo(usersToGet); + + if (users == null) _errorLoading(); + + // Save the new list of messages + setState(() { + _usersInfo.addAll(users); + if (_messages == null) + _messages = messages; + else + _messages.addAll(messages); + }); + } + + /// Error handling + Widget _buildError() { + return buildErrorCard(tr("Could not load the list of messages!")); + } + + @override + Widget build(BuildContext context) { + if (_error == ErrorLevel.MAJOR) return _buildError(); + + if (_messages == null) return buildCenteredProgressBar(); + + return ListView.builder( + itemCount: _messages.length, + itemBuilder: (c, i) { + return ConversationMessageTile( + message: _messages.elementAt(i), + userInfo: _usersInfo.getUser(_messages[i].userID), + ); + }); + } +} diff --git a/lib/ui/tiles/conversation_message_tile.dart b/lib/ui/tiles/conversation_message_tile.dart new file mode 100644 index 0000000..dc60871 --- /dev/null +++ b/lib/ui/tiles/conversation_message_tile.dart @@ -0,0 +1,22 @@ +import 'package:comunic/models/conversation_message.dart'; +import 'package:comunic/models/user.dart'; +import 'package:flutter/material.dart'; + +/// Conversation message tile +/// +/// @author Pierre HUBERT + +class ConversationMessageTile extends StatelessWidget { + final ConversationMessage message; + final User userInfo; + + const ConversationMessageTile({Key key, this.message, this.userInfo}) + : assert(message != null), + assert(userInfo != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return Text(message.message); + } +} diff --git a/lib/utils/list_utils.dart b/lib/utils/list_utils.dart index 33a0a65..8de683f 100644 --- a/lib/utils/list_utils.dart +++ b/lib/utils/list_utils.dart @@ -11,4 +11,17 @@ List listToIntList(List srcList){ }); return list; +} + +/// Find the list of missing elements of a [testList] from a [srcList] +List findMissingFromList(List srcList, ListtestList) { + + List dest = List(); + + testList.forEach((f){ + if(!srcList.contains(f) && !dest.contains(f)) + dest.add(f); + }); + + return dest; } \ No newline at end of file