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_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("Settings")!, action: BarCallbackActions.OPEN_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_SETTINGS: controller!.openSettings(); 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, }) : 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, ); } }