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/custom_list_tile.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 { final bool useSmallDeleteButton; const NotificationsScreen({ Key? key, this.useSmallDeleteButton = false, }) : assert(useSmallDeleteButton != null), super(key: key); @override _NotificationsScreenState createState() => _NotificationsScreenState(); } class _NotificationsScreenState extends SafeState { NotificationsList? _list; late UsersList _users; late 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, s) { print("Exception while getting the list of notifications!"); print("$e, $s"); } on Error catch (e, s) { print("Error while getting the list of notifications!"); print("$e, $s"); } } @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: widget.useSmallDeleteButton ? 0.0 : 20.0, bottom: widget.useSmallDeleteButton ? 0.0 : 20.0, child: FloatingActionButton( mini: widget.useSmallDeleteButton, 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 CustomListTile( leading: AccountImageWidget( user: srcUser, ), onTap: () => _onTap(context), title: Text(message), subtitle: Text(diffTimeFromNowToStr(notification.timeCreate)!), onLongPressOpenMenu: (position) { showMenu(context: context, position: position, items: [ PopupMenuItem( child: Text(tr("Delete")!), value: _PopupMenuActions.DELETE, ), ]).then(_popupMenuAction); }, ); } 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); } }