diff --git a/lib/ui/routes/conversation_message_stats_route.dart b/lib/ui/routes/conversation_message_stats_route.dart new file mode 100644 index 0000000..1b52480 --- /dev/null +++ b/lib/ui/routes/conversation_message_stats_route.dart @@ -0,0 +1,95 @@ +import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/lists/users_list.dart'; +import 'package:comunic/models/conversation.dart'; +import 'package:comunic/models/conversation_message.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/utils/date_utils.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/material.dart'; + +/// Conversation message statistics route +/// +/// @author Pierre Hubert + +class ConversationMessageStatsRoute extends StatefulWidget { + final Conversation conv; + final ConversationMessage message; + + const ConversationMessageStatsRoute({ + Key key, + @required this.conv, + @required this.message, + }) : assert(conv != null), + assert(message != null), + super(key: key); + + @override + _ConversationMessageStatsRouteState createState() => + _ConversationMessageStatsRouteState(); +} + +class _ConversationMessageStatsRouteState + extends State { + UsersList _users; + + Future _init() async { + _users = await UsersHelper() + .getList(widget.conv.membersID..add(widget.message.userID)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(tr("Message statistics")), + leading: IconButton( + icon: Icon(Icons.close), + onPressed: () => MainController.of(context).popPage(), + ), + ), + body: AsyncScreenWidget( + onReload: _init, + onBuild: _buildScreen, + errorMessage: tr("Failed to load message information!")), + ); + } + + List get _firstItems => [ + ListTile( + leading: Icon(Icons.access_time_rounded), + title: Text(tr("Created on")), + subtitle: Text(dateTimeToString(widget.message.date)), + ), + ListTile( + leading: AccountImageWidget( + user: _users.getUser(widget.message.userID), + ), + title: Text(_users.getUser(widget.message.userID).fullName), + subtitle: Text(tr("Creator")), + ), + ]; + + Widget _buildScreen() => ListView.builder( + itemCount: _firstItems.length + widget.conv.members.length, + itemBuilder: (c, i) { + final firstItems = _firstItems; + if (i < firstItems.length) return firstItems[i]; + + final convMember = widget.conv.members[i - firstItems.length]; + + if (convMember.userID == widget.message.userID) return Container(); + + return ListTile( + leading: AccountImageWidget( + user: _users.getUser(convMember.userID), + ), + title: Text(_users.getUser(convMember.userID).fullName), + subtitle: Text(convMember.lastMessageSeen < widget.message.id + ? tr("Message not seen yet") + : tr("Message seen")), + ); + }, + ); +} diff --git a/lib/ui/routes/main_route/main_route.dart b/lib/ui/routes/main_route/main_route.dart index ea053d3..b7f3b3d 100644 --- a/lib/ui/routes/main_route/main_route.dart +++ b/lib/ui/routes/main_route/main_route.dart @@ -1,4 +1,7 @@ import 'package:comunic/helpers/account_helper.dart'; +import 'package:comunic/models/conversation.dart'; +import 'package:comunic/models/conversation_message.dart'; +import 'package:comunic/ui/routes/conversation_message_stats_route.dart'; import 'package:comunic/ui/routes/conversation_route.dart'; import 'package:comunic/ui/routes/main_route/page_info.dart'; import 'package:comunic/ui/routes/settings/account_settings_route.dart'; @@ -151,6 +154,18 @@ abstract class MainController extends State { hideNavBar: true, )); + /// Open a conversation message statistics page + void openConversationMessageStats( + Conversation conv, ConversationMessage message) => + pushPage(PageInfo( + child: ConversationMessageStatsRoute( + conv: conv, + message: message, + ), + hideNavBar: true, + canShowAsDialog: true, + )); + /// Start a call for a given conversation void startCall(int convID) => pushPage(PageInfo(child: CallScreen(convID: convID), hideNavBar: true)); diff --git a/lib/ui/screens/conversation_screen.dart b/lib/ui/screens/conversation_screen.dart index fc4de50..9d16351 100644 --- a/lib/ui/screens/conversation_screen.dart +++ b/lib/ui/screens/conversation_screen.dart @@ -8,6 +8,7 @@ import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/new_conversation_message.dart'; +import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/tiles/conversation_message_tile.dart'; import 'package:comunic/ui/tiles/server_conversation_message_tile.dart'; import 'package:comunic/ui/widgets/safe_state.dart'; @@ -331,6 +332,7 @@ class _ConversationScreenState extends SafeState { userInfo: _usersInfo.getUser(_messages[i].userID), isLastMessage: _isLastMessage(i), isFirstMessage: _isFirstMessage(i), + onRequestMessageStats: _requestMessageStats, onRequestMessageUpdate: _updateMessage, onRequestMessageDelete: _deleteMessage, ); @@ -425,6 +427,12 @@ class _ConversationScreenState extends SafeState { ); } + /// Request message statistics + void _requestMessageStats(ConversationMessage message) async { + MainController.of(context) + .openConversationMessageStats(_conversation, message); + } + /// Request message content update Future _updateMessage(ConversationMessage message) async { final newContent = await askUserString( diff --git a/lib/ui/tiles/conversation_message_tile.dart b/lib/ui/tiles/conversation_message_tile.dart index 208511d..cdae381 100644 --- a/lib/ui/tiles/conversation_message_tile.dart +++ b/lib/ui/tiles/conversation_message_tile.dart @@ -18,9 +18,11 @@ enum _MenuChoices { COPY_URL, COPY_MESSAGE, DELETE, - REQUEST_UPDATE_CONTENT + REQUEST_UPDATE_CONTENT, + GET_STATS, } +typedef OnRequestMessageStats = void Function(ConversationMessage); typedef OnRequestMessageUpdate = void Function(ConversationMessage); typedef OnRequestMessageDelete = void Function(ConversationMessage); @@ -30,6 +32,7 @@ class ConversationMessageTile extends StatelessWidget { final User userInfo; final bool isLastMessage; final bool isFirstMessage; + final OnRequestMessageStats onRequestMessageStats; final OnRequestMessageUpdate onRequestMessageUpdate; final OnRequestMessageDelete onRequestMessageDelete; @@ -40,12 +43,14 @@ class ConversationMessageTile extends StatelessWidget { @required this.userInfo, @required this.isLastMessage, @required this.isFirstMessage, + @required this.onRequestMessageStats, @required this.onRequestMessageUpdate, @required this.onRequestMessageDelete, }) : assert(message != null), assert(userInfo != null), assert(isLastMessage != null), assert(isFirstMessage != null), + assert(onRequestMessageStats != null), assert(onRequestMessageUpdate != null), assert(onRequestMessageDelete != null), super(key: key); @@ -66,7 +71,6 @@ class ConversationMessageTile extends StatelessWidget { width: 35.0, ), itemBuilder: (c) => [ - PopupMenuItem( enabled: (message.message?.content ?? "") != "", value: _MenuChoices.COPY_MESSAGE, @@ -78,6 +82,12 @@ class ConversationMessageTile extends StatelessWidget { value: _MenuChoices.COPY_URL, child: Text(tr("Copy URL")), ), + + PopupMenuItem( + value: _MenuChoices.GET_STATS, + child: Text(tr("Statistics")), + ), + // Update message content PopupMenuItem( enabled: message.isOwner, @@ -275,6 +285,10 @@ class ConversationMessageTile extends StatelessWidget { FlutterClipboard.copy(message.file.url); break; + case _MenuChoices.GET_STATS: + onRequestMessageStats(message); + break; + case _MenuChoices.REQUEST_UPDATE_CONTENT: onRequestMessageUpdate(message); break;