import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/helpers/notifications_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/notifications_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/notification.dart' as n; import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/widgets/account_image_widget.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/navigation_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Notifications screen /// /// @author Pierre HUBERT enum _Status { LOADING, ERROR, NONE } enum _PopupMenuActions { DELETE } class NotificationsScreen extends StatefulWidget { @override _NotificationsScreenState createState() => _NotificationsScreenState(); } class _NotificationsScreenState extends SafeState { NotificationsList _list; UsersList _users; GroupsList _groups; _Status _status = _Status.LOADING; final _refreshKey = GlobalKey(); void setStatus(_Status s) => setState(() => _status = s); Future _loadList() async { setStatus(_Status.LOADING); try { final list = await NotificationsHelper().getUnread(); final users = await UsersHelper().getListWithThrow(list.usersIds); final groups = await GroupsHelper().getListOrThrow(list.groupsIds); setState(() { _list = list; _users = users; _groups = groups; }); setStatus(_Status.NONE); } on Exception catch (e) { print("Exception while getting the list of notifications!"); print(e); } on Error catch (e) { print("Error while getting the list of notifications!"); print(e.stackTrace); } } @override void initState() { super.initState(); this.listen((d) => _refreshKey.currentState.show()); } @override void didChangeDependencies() { super.didChangeDependencies(); _loadList(); } @override Widget build(BuildContext context) { // Loading status if (_list == null || _status == _Status.LOADING) return Center( child: CircularProgressIndicator(), ); return Container( height: double.infinity, child: Stack(children: [ Container( height: double.infinity, child: RefreshIndicator( key: _refreshKey, child: _buildBody(), onRefresh: _loadList, ), ), // Add conversation button Positioned( right: 20.0, bottom: 20.0, child: FloatingActionButton( onPressed: () => _deleteAllNotifications(), child: Icon(Icons.delete), ), ), ]), ); } /// Build body Widget _buildBody() { if (_status == _Status.ERROR || _list.length == 0) return SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), child: _buildSingleChildCases(), ); return ListView( children: _list .map((f) => _NotificationTile( notification: f, usersList: _users, groupsList: _groups, onDelete: _deleteNotification, )) .toList(), ); } Widget _buildSingleChildCases() { // In case of error if (_status == _Status.ERROR) return buildErrorCard(tr("Could not get the list of notifications!"), actions: [ MaterialButton( onPressed: () => _loadList(), child: Text(tr("Try again".toUpperCase())), ) ]); // When there is no notification if (_list.length == 0) return Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Text(tr("You do not have any notification now.")), ), ); throw Error(); } /// Delete a notification void _deleteNotification(n.Notification notif) async { setState(() { _list.remove(notif); }); NotificationsHelper().markSeen(notif); } /// Delete all the notifications void _deleteAllNotifications() async { if (!await showConfirmDialog( context: context, message: tr("Do you really want to delete all your notifications?"))) return; if (!await NotificationsHelper().deleteAllNotifications()) { showSimpleSnack(context, tr("Could not delete all your notifications!")); return; } _refreshKey.currentState.show(); } } class _NotificationTile extends StatelessWidget { final n.Notification notification; final UsersList usersList; final GroupsList groupsList; final void Function(n.Notification) onDelete; const _NotificationTile({ Key key, @required this.notification, @required this.usersList, @required this.groupsList, @required this.onDelete, }) : assert(notification != null), assert(usersList != null), assert(groupsList != null), assert(onDelete != null), super(key: key); @override Widget build(BuildContext context) { final srcUser = usersList.getUser(notification.fromUser); String message = "${srcUser.fullName} "; switch (notification.type) { // Comment case n.NotificationType.COMMENT_CREATED: message += tr("posted a comment"); break; // Friendship requests case n.NotificationType.SENT_FRIEND_REQUEST: message += tr("sent you a friendship request."); break; case n.NotificationType.ACCEPTED_FRIEND_REQUEST: message += tr("accepted your friendship request."); break; case n.NotificationType.REJECTED_FRIEND_REQUEST: message += tr("rejected your friendship request."); break; // Groups membership case n.NotificationType.SENT_GROUP_MEMBERSHIP_INVITATION: message += tr("invited you to join the group"); break; case n.NotificationType.ACCEPTED_GROUP_MEMBERSHIP_INVITATION: message += tr("accepted his invitation to join the group"); break; case n.NotificationType.REJECTED_GROUP_MEMBERSHIP_INVITATION: message += tr("rejected his invitation to join the group"); break; case n.NotificationType.SENT_GROUP_MEMBERSHIP_REQUEST: message += tr("sent a request to join the group"); break; case n.NotificationType.ACCEPTED_GROUP_MEMBERSHIP_REQUEST: message += tr("accepted you request to join the group"); break; case n.NotificationType.REJECTED_GROUP_MEMBERSHIP_REQUEST: message += tr("rejected your request to join the group"); break; // Generic element creation case n.NotificationType.ELEM_CREATED: if (notification.onElemType == n.NotificationElementType.POST) message += tr("created a new post"); break; case n.NotificationType.ELEM_UPDATED: // Operation not implemented break; } // Separator message += " "; // Notification target // User page if (notification.fromContainerType == n.NotificationElementType.USER_PAGE) { if (notification.fromUser == notification.fromContainerId) message += tr("on his / her page"); else message += tr("on %user_name%'s page", args: { "user_name": usersList.getUser(notification.fromContainerId).fullName }); } // Group page if (notification.fromContainerType == n.NotificationElementType.GROUP_PAGE) { message += tr("on the group %group%.", args: { "group": groupsList[notification.fromContainerId].displayName }); } // Group membership if (notification.onElemType == n.NotificationElementType.GROUP_MEMBERSHIP) message += groupsList[notification.onElemId].displayName; return ListTile( leading: AccountImageWidget( user: srcUser, ), onTap: () => _onTap(context), title: Text(message), subtitle: Text(diffTimeFromNowToStr(notification.timeCreate)), trailing: PopupMenuButton<_PopupMenuActions>( onSelected: _popupMenuAction, itemBuilder: (c) => [ PopupMenuItem( child: Text(tr("Delete")), value: _PopupMenuActions.DELETE, ), ], ), ); } void _popupMenuAction(_PopupMenuActions value) { switch (value) { case _PopupMenuActions.DELETE: onDelete(notification); break; } } void _onTap(BuildContext context) { if (notification.onElemType == n.NotificationElementType.POST) { openPostFullScreen(notification.onElemId, context); } else if (notification.onElemType == n.NotificationElementType.FRIENDSHIP_REQUEST) { openUserPage(userID: notification.fromUser, context: context); } else if (notification.onElemType == n.NotificationElementType.GROUP_MEMBERSHIP) { MainController.of(context).openGroup(notification.onElemId); } else { showSimpleSnack(context, tr("This kind of notification is not supported yet by this application.")); return; } onDelete(notification); } }