From 003eb1efc23ba0e2930a2a81f73aa2410d8018fb Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 17 Apr 2020 13:28:01 +0200 Subject: [PATCH] Diplay the number of unread notifications / conversations --- lib/helpers/notifications_helper.dart | 17 ++++ lib/models/count_unread_notifications.dart | 14 ++++ lib/ui/widgets/navbar_widget.dart | 98 ++++++++++++++++++---- 3 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 lib/models/count_unread_notifications.dart diff --git a/lib/helpers/notifications_helper.dart b/lib/helpers/notifications_helper.dart index ab2b7ed..2702bf9 100644 --- a/lib/helpers/notifications_helper.dart +++ b/lib/helpers/notifications_helper.dart @@ -1,5 +1,6 @@ import 'package:comunic/lists/notifications_list.dart'; import 'package:comunic/models/api_request.dart'; +import 'package:comunic/models/count_unread_notifications.dart'; import 'package:comunic/models/notification.dart'; /// Notifications helper @@ -47,6 +48,22 @@ const _NotificationsTypeAPImapping = { }; class NotificationsHelper { + /// Get the number of unread notifications + /// + /// This method throws in case of error + Future countUnread() async { + final response = + await APIRequest(uri: "notifications/count_all_news", needLogin: true) + .exec(); + + final content = response.assertOk().getObject(); + + return CountUnreadNotifications( + notifications: content["notifications"], + conversations: content["conversations"], + ); + } + /// Get the list of unread notifications of the user Future getUnread() async { final response = diff --git a/lib/models/count_unread_notifications.dart b/lib/models/count_unread_notifications.dart new file mode 100644 index 0000000..140a265 --- /dev/null +++ b/lib/models/count_unread_notifications.dart @@ -0,0 +1,14 @@ +/// Count the number of unread notifications +/// +/// @author Pierre Hubert + +class CountUnreadNotifications { + final int notifications; + final int conversations; + + CountUnreadNotifications({ + this.notifications, + this.conversations, + }) : assert(notifications != null), + assert(conversations != null); +} diff --git a/lib/ui/widgets/navbar_widget.dart b/lib/ui/widgets/navbar_widget.dart index 2dd9b0e..0976371 100644 --- a/lib/ui/widgets/navbar_widget.dart +++ b/lib/ui/widgets/navbar_widget.dart @@ -1,3 +1,6 @@ +import 'package:comunic/helpers/notifications_helper.dart'; +import 'package:comunic/models/count_unread_notifications.dart'; +import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; @@ -106,7 +109,7 @@ final _menuActionsItem = <_ActionMenuItem>[ ]; /// Public widget -class ComunicAppBar extends StatelessWidget implements PreferredSizeWidget { +class ComunicAppBar extends StatefulWidget implements PreferredSizeWidget { final OnSelectMenuAction onTap; final BarCallbackActions selectedAction; @@ -115,6 +118,51 @@ class ComunicAppBar extends StatelessWidget implements PreferredSizeWidget { : assert(onTap != null), super(key: key); + @override + _ComunicAppBarState createState() => _ComunicAppBarState(); + + @override + Size get preferredSize => Size.fromHeight(40); +} + +class _ComunicAppBarState extends SafeState { + CountUnreadNotifications _unreadNotifications; + + @override + void initState() { + _refreshCountUnread(); + super.initState(); + } + + void _refreshCountUnread() async { + try { + final count = await NotificationsHelper().countUnread(); + + setState(() { + _unreadNotifications = count; + }); + } catch (e, stack) { + print("Could not refresh the number of unread notifications: $e"); + print(stack); + } + } + + /// Get the number of unread notifications for the selected notice + int getNumberUnread(BarCallbackActions action) { + if (_unreadNotifications == null) return 0; + + switch (action) { + case BarCallbackActions.OPEN_NOTIFICATIONS: + return _unreadNotifications.notifications; + + case BarCallbackActions.OPEN_CONVERSATIONS: + return _unreadNotifications.conversations; + + default: + return 0; + } + } + @override Widget build(BuildContext context) { return Material( @@ -125,16 +173,14 @@ class ComunicAppBar extends StatelessWidget implements PreferredSizeWidget { _menuItems.length, (i) => _MenuItemWidget( item: _menuItems[i], - onTap: onTap, - isSelected: _menuItems[i].action == selectedAction, + onTap: widget.onTap, + isSelected: _menuItems[i].action == widget.selectedAction, + newNotice: getNumberUnread(_menuItems[i].action), ), ), ), ); } - - @override - Size get preferredSize => Size.fromHeight(40); } /// The [Widget] part of a menu item @@ -143,12 +189,16 @@ class _MenuItemWidget extends StatelessWidget { final OnSelectMenuAction onTap; final bool isSelected; - const _MenuItemWidget( - {Key key, - @required this.item, - @required this.onTap, - @required this.isSelected}) - : assert(item != null), + /// A number to notify of news. + final int newNotice; + + const _MenuItemWidget({ + Key key, + @required this.item, + @required this.onTap, + @required this.isSelected, + this.newNotice = 0, + }) : assert(item != null), assert(onTap != null), assert(isSelected != null), super(key: key); @@ -171,14 +221,34 @@ class _MenuItemWidget extends StatelessWidget { } Widget _buildIconContainer() { - return Column( + return Row( mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ + Spacer( + flex: 2, + ), IconTheme( data: IconThemeData( color: isSelected ? _primaryColor() : _secondaryColor()), child: item.icon, - ) + ), + newNotice > 0 ? Spacer() : Container(), + newNotice == 0 + ? Container() + : Material( + color: Colors.red, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Text(" $newNotice ", + style: TextStyle(color: Colors.white)), + ), + borderRadius: BorderRadius.all( + Radius.circular(50.0), + )), + Spacer( + flex: 2, + ), ], ); }