From 1ec197202c510bf85fda3aa7d7bdb641fe449b81 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Wed, 24 Apr 2019 17:46:25 +0200 Subject: [PATCH] Created conversation route --- lib/helpers/conversations_helper.dart | 76 +++++++++++++--- lib/models/conversation.dart | 2 +- lib/ui/routes/conversation_route.dart | 90 +++++++++++++++++++ lib/ui/routes/home_route.dart | 4 +- ...en.dart => conversations_list_screen.dart} | 14 ++- lib/ui/tiles/conversation_tile.dart | 10 ++- lib/utils/list_utils.dart | 4 +- 7 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 lib/ui/routes/conversation_route.dart rename lib/ui/screens/{conversations_screen.dart => conversations_list_screen.dart} (86%) diff --git a/lib/helpers/conversations_helper.dart b/lib/helpers/conversations_helper.dart index a2c88e5..cea727b 100644 --- a/lib/helpers/conversations_helper.dart +++ b/lib/helpers/conversations_helper.dart @@ -1,4 +1,5 @@ import 'package:comunic/helpers/database/conversations_database_helper.dart'; +import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/lists/conversations_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/api_request.dart'; @@ -22,15 +23,7 @@ class ConversationsHelper { try { ConversationsList list = ConversationsList(); - response.getArray().forEach((f) => list.add(Conversation( - id: f["ID"], - ownerID: f["ID_owner"], - lastActive: f["last_active"], - name: f["name"] == false ? null : f["name"], - following: f["following"] == 1, - sawLastMessage: f["saw_last_message"] == 1, - members: f["members"].map((f) => int.parse(f)).toList(), - ))); + response.getArray().forEach((f) => list.add(_apiToConversation(f))); // Update the database await _conversationsDatabaseHelper.clearTable(); @@ -50,8 +43,38 @@ class ConversationsHelper { return list; } - /// Get the name of a [conversation]. This requires information about the - /// users of this conversation + /// Get information about a single conversation specified by its [id] + Future _downloadSingle(int id) async { + try { + final response = await APIRequest( + uri: "conversations/getInfoOne", + needLogin: true, + args: {"conversationID": id.toString()}).exec(); + + if (response.code != 200) return null; + + final conversation = _apiToConversation(response.getObject()); + _conversationsDatabaseHelper.insertOrUpdate(conversation); + return conversation; + } on Exception catch (e) { + print(e.toString()); + print("Could not get information about a single conversation !"); + return null; + } + } + + /// Get information about a single conversation. If [force] is set to false, + /// cached version of the conversation will be used, else it will always get + /// the information from the server + Future getSingle(int id, {bool force = false}) async { + if(force || ! await _conversationsDatabaseHelper.has(id)) + return await _downloadSingle(id); + else + return _conversationsDatabaseHelper.get(id); + } + + /// Get the name of a [conversation]. This requires information + /// about the users of this conversation static String getConversationName( Conversation conversation, UsersList users) { if (conversation.has_name) return conversation.name; @@ -69,4 +92,35 @@ class ConversationsHelper { return name; } + + /// Asynchronously get the name fo the conversation + /// + /// Unlike the synchronous method, this method does not need information + /// about the members of the conversation + /// + /// Returns null in case of failure + static Future getConversationNameAsync( + Conversation conversation) async { + if (conversation.has_name) return conversation.name; + + //Get information about the members of the conversation + final members = await UsersHelper().getUsersInfo(conversation.members); + + if (members == null) return null; + + return ConversationsHelper.getConversationName(conversation, members); + } + + /// Turn an API entry into a [Conversation] object + Conversation _apiToConversation(Map map){ + return Conversation( + id: map["ID"], + ownerID: map["ID_owner"], + lastActive: map["last_active"], + name: map["name"] == false ? null : map["name"], + following: map["following"] == 1, + sawLastMessage: map["saw_last_message"] == 1, + members: map["members"].map((f) => int.parse(f)).toList(), + ); + } } diff --git a/lib/models/conversation.dart b/lib/models/conversation.dart index f9047ed..7375a52 100644 --- a/lib/models/conversation.dart +++ b/lib/models/conversation.dart @@ -40,7 +40,7 @@ class Conversation extends CacheModel implements Comparable { name = map[ConversationTableContract.C_NAME], following = map[ConversationTableContract.C_FOLLOWING] == 1, sawLastMessage = map[ConversationTableContract.C_SAW_LAST_MESSAGE] == 1, - members = stringListToIntList( + members = listToIntList( map[ConversationTableContract.C_MEMBERS].split(",")), super.fromMap(map); diff --git a/lib/ui/routes/conversation_route.dart b/lib/ui/routes/conversation_route.dart new file mode 100644 index 0000000..1c90388 --- /dev/null +++ b/lib/ui/routes/conversation_route.dart @@ -0,0 +1,90 @@ +import 'package:comunic/helpers/conversations_helper.dart'; +import 'package:comunic/models/conversation.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +/// Single conversation route +/// +/// @author Pierre HUBERT + +class ConversationRoute extends StatefulWidget { + final int conversationID; + + const ConversationRoute({ + Key key, + @required this.conversationID, + }) : assert(conversationID != null), + super(key: key); + + @override + State createState() => _ConversationRouteState(); +} + +class _ConversationRouteState extends State { + final ConversationsHelper _conversationsHelper = ConversationsHelper(); + Conversation _conversation; + String _conversationName; + bool _error = false; + + setError(bool err) => setState(() => _error = err); + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _loadConversation(); + } + + Future _loadConversation() async { + setError(false); + + _conversation = + await _conversationsHelper.getSingle(widget.conversationID); + + if (_conversation == null) return setError(true); + + final conversationName = + await ConversationsHelper.getConversationNameAsync(_conversation); + + if(conversationName == null) + return setError(true); + + setState(() { + _conversationName = conversationName; + }); + } + + Widget _buildRouteBody() { + //Handle errors + if (_error != null && _error) + return buildErrorCard( + tr("Could not get conversation information!"), + actions: [ + FlatButton( + onPressed: _loadConversation, + child: Text( + tr("Try again").toUpperCase(), + style: TextStyle( + color: Colors.white, + ), + ), + ), + ], + ); + + //if (_conversationName == null || _conversation == null) + return buildCenteredProgressBar(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + _conversationName == null ? tr("Loading") : _conversationName, + ), + ), + body: _buildRouteBody(), + ); + } +} diff --git a/lib/ui/routes/home_route.dart b/lib/ui/routes/home_route.dart index 622611c..2b63ddf 100644 --- a/lib/ui/routes/home_route.dart +++ b/lib/ui/routes/home_route.dart @@ -1,4 +1,4 @@ -import 'package:comunic/ui/screens/conversations_screen.dart'; +import 'package:comunic/ui/screens/conversations_list_screen.dart'; import 'package:comunic/ui/screens/menus_screen.dart'; import 'package:comunic/ui/tiles/custom_bottom_navigation_bar_item.dart'; import 'package:flutter/material.dart'; @@ -49,7 +49,7 @@ class _HomeRouteState extends State { Widget _buildBody(BuildContext context) { switch (_currTab) { case 0: - return ConversationsScreen(); + return ConversationsListScreen(); case 1: return MenuScreen(); diff --git a/lib/ui/screens/conversations_screen.dart b/lib/ui/screens/conversations_list_screen.dart similarity index 86% rename from lib/ui/screens/conversations_screen.dart rename to lib/ui/screens/conversations_list_screen.dart index 1207e5d..a2e7b1b 100644 --- a/lib/ui/screens/conversations_screen.dart +++ b/lib/ui/screens/conversations_list_screen.dart @@ -2,6 +2,7 @@ import 'package:comunic/enums/load_error_level.dart'; import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/lists/conversations_list.dart'; +import 'package:comunic/ui/routes/conversation_route.dart'; import 'package:comunic/ui/tiles/conversation_tile.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; @@ -11,12 +12,12 @@ import 'package:flutter/material.dart'; /// /// @author Pierre HUBERT -class ConversationsScreen extends StatefulWidget { +class ConversationsListScreen extends StatefulWidget { @override State createState() => _ConversationScreenState(); } -class _ConversationScreenState extends State { +class _ConversationScreenState extends State { final ConversationsHelper _conversationsHelper = ConversationsHelper(); final UsersHelper _usersHelper = UsersHelper(); ConversationsList _list; @@ -88,6 +89,13 @@ class _ConversationScreenState extends State { ); } + /// Open a conversation + void _openConversation(BuildContext context, int conversationId){ + Navigator.of(context).push(MaterialPageRoute(builder: (c){ + return ConversationRoute(conversationID: conversationId,); + })); + } + @override Widget build(BuildContext context) { if (_error == LoadErrorLevel.MAJOR) return _buildErrorCard(); @@ -103,10 +111,12 @@ class _ConversationScreenState extends State { ), Expanded( child: ListView.builder( + controller: ScrollController(), itemBuilder: (context, index) { return ConversationTile( conversation: _list.elementAt(index), usersList: _list.users, + onOpen: (c){_openConversation(context, c.id);}, ); }, itemCount: _list.length, diff --git a/lib/ui/tiles/conversation_tile.dart b/lib/ui/tiles/conversation_tile.dart index 6efe938..7471208 100644 --- a/lib/ui/tiles/conversation_tile.dart +++ b/lib/ui/tiles/conversation_tile.dart @@ -9,14 +9,21 @@ import 'package:flutter/material.dart'; /// /// @author Pierre HUBERT +typedef OpenConversationCallback = void Function(Conversation); + class ConversationTile extends StatelessWidget { final Conversation conversation; final UsersList usersList; + final OpenConversationCallback onOpen; const ConversationTile( - {Key key, @required this.conversation, @required this.usersList}) + {Key key, + @required this.conversation, + @required this.usersList, + @required this.onOpen}) : assert(conversation != null), assert(usersList != null), + assert(onOpen != null), super(key: key); _buildSubInformation(IconData icon, String content) { @@ -35,6 +42,7 @@ class ConversationTile extends StatelessWidget { @override Widget build(BuildContext context) { return ListTile( + onTap: () => onOpen(conversation), // Conversation name title: Text( ConversationsHelper.getConversationName( diff --git a/lib/utils/list_utils.dart b/lib/utils/list_utils.dart index a1eb577..33a0a65 100644 --- a/lib/utils/list_utils.dart +++ b/lib/utils/list_utils.dart @@ -2,8 +2,8 @@ /// /// @author Pierre HUBERT -/// Transform a list of string into something else -List stringListToIntList(List srcList){ +/// Transform a list of dynamic thins into something a list of ints +List listToIntList(List srcList){ List list = List(); srcList.forEach((e){