import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/notifications_helper.dart'; import 'package:comunic/models/count_unread_notifications.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/page_info.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'; /// Navigation bar widget /// /// @author Pierre HUBERT typedef OnSelectMenuAction = void Function(BarCallbackActions); /// Callback actions enum BarCallbackActions { OPEN_NOTIFICATIONS, OPEN_CONVERSATIONS, OPEN_NEWEST_POSTS, OPEN_FRIENDS, OPEN_MY_PAGE, OPEN_SEARCH_PAGE, OPEN_GROUPS, OPEN_ACCOUNT_SETTINGS, OPEN_APP_SETTINGS, ACTION_LOGOUT } Color _primaryColor() => darkTheme() ? Colors.black : Colors.blue; Color _secondaryColor() => darkTheme() ? darkAccentColor : Colors.white; /// Menu item information class _MenuItem { final String label; final Widget icon; final BarCallbackActions action; final bool isMenu; final PageType pageType; const _MenuItem({ @required this.label, @required this.icon, @required this.action, @required this.pageType, this.isMenu = false, }) : assert(label != null), assert(isMenu != null), assert(isMenu || icon != null), assert(isMenu || action != null), assert(isMenu || pageType != null); } /// Item of action menu class _ActionMenuItem { final String label; final BarCallbackActions action; const _ActionMenuItem({@required this.label, @required this.action}) : assert(label != null), assert(action != null); } /// List of menu items to show final _menuItems = <_MenuItem>[ _MenuItem( label: tr("Notifications"), icon: Icon(Icons.notifications), action: BarCallbackActions.OPEN_NOTIFICATIONS, pageType: PageType.NOTIFICATIONS_PAGE), _MenuItem( label: tr("Conversations"), icon: Icon(Icons.comment), action: BarCallbackActions.OPEN_CONVERSATIONS, pageType: PageType.CONVERSATIONS_LIST_PAGE), _MenuItem( label: tr("Newest"), icon: Icon(Icons.refresh), action: BarCallbackActions.OPEN_NEWEST_POSTS, pageType: PageType.LATEST_POSTS_PAGE), _MenuItem( label: tr("Friends"), icon: Icon(Icons.group), action: BarCallbackActions.OPEN_FRIENDS, pageType: PageType.FRIENDS_LIST_PAGE), _MenuItem( label: tr("Menu"), icon: Icon(Icons.more_vert), isMenu: true, action: null, pageType: null) ]; /// List of menu actions items final _menuActionsItem = <_ActionMenuItem>[ _ActionMenuItem( label: tr("My Page"), action: BarCallbackActions.OPEN_MY_PAGE), _ActionMenuItem(label: tr("Groups"), action: BarCallbackActions.OPEN_GROUPS), _ActionMenuItem( label: tr("Search"), action: BarCallbackActions.OPEN_SEARCH_PAGE), _ActionMenuItem( label: tr("Account settings"), action: BarCallbackActions.OPEN_ACCOUNT_SETTINGS), _ActionMenuItem( label: tr("App settings"), action: BarCallbackActions.OPEN_APP_SETTINGS), _ActionMenuItem( label: tr("Sign out"), action: BarCallbackActions.ACTION_LOGOUT), ]; /// Public widget class ComunicMobileAppBar extends StatefulWidget implements PreferredSizeWidget { final PageInfo currentPage; const ComunicMobileAppBar({ Key key, @required this.currentPage, }) : assert(currentPage != null), super(key: key); @override _ComunicMobileAppBarState createState() => _ComunicMobileAppBarState(); @override Size get preferredSize => Size.fromHeight(40); } class _ComunicMobileAppBarState extends SafeState { var _unreadNotifications = CountUnreadNotifications(notifications: 0, conversations: 0); @override void initState() { _refreshCountUnread(); super.initState(); // Listen to notifications number update this.listenChangeState( (d) => _unreadNotifications.notifications = d.newNum); this.listenChangeState( (d) => _unreadNotifications.conversations = d.newNum); } 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( color: darkTheme() ? Colors.black : Colors.blue, child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: List.generate( _menuItems.length, (i) => _MenuItemWidget( item: _menuItems[i], onTap: _handleAction, isSelected: _menuItems[i].pageType == widget.currentPage.type, newNotice: getNumberUnread(_menuItems[i].action), ), ), ), ); } void _handleAction(BarCallbackActions action) { final controller = MainController.of(context); switch (action) { case BarCallbackActions.OPEN_NOTIFICATIONS: controller.openNotificationsPage(); break; case BarCallbackActions.OPEN_CONVERSATIONS: controller.openConversationsPage(); break; case BarCallbackActions.OPEN_NEWEST_POSTS: controller.openLatestPostsPage(); break; case BarCallbackActions.OPEN_FRIENDS: controller.openFriendsList(); break; case BarCallbackActions.OPEN_MY_PAGE: controller.openCurrentUserPage(); break; case BarCallbackActions.OPEN_SEARCH_PAGE: controller.openSearchPage(); break; case BarCallbackActions.OPEN_GROUPS: controller.openGroupsListPage(); break; case BarCallbackActions.OPEN_ACCOUNT_SETTINGS: controller.openAccountSettings(); break; case BarCallbackActions.OPEN_APP_SETTINGS: controller.openAppSettings(); break; case BarCallbackActions.ACTION_LOGOUT: controller.requestLogout(); break; } } } /// The [Widget] part of a menu item class _MenuItemWidget extends StatelessWidget { final _MenuItem item; final OnSelectMenuAction onTap; final bool isSelected; /// Notifications notice 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); @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); return Expanded( child: Material( color: isSelected ? _secondaryColor() : _primaryColor(), child: !item.isMenu ? InkWell( child: _buildIconContainer(), onTap: () => onTap(item.action), ) : _buildContextMenuPopupButton(), ), ); } Widget _buildIconContainer() { 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), ], ); } // Build context menu Widget _buildContextMenuPopupButton() { return PopupMenuButton( child: _buildIconContainer(), itemBuilder: (i) => _menuActionsItem .map((f) => PopupMenuItem( child: Text(f.label), value: f.action, )) .toList(), onSelected: onTap, ); } }