From a119f60fdb72077529caf261ab758b4a8a3ca342 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sun, 10 May 2020 18:29:43 +0200 Subject: [PATCH] Simplify pages system --- lib/ui/routes/main_route/main_route.dart | 140 +++++++++-- lib/ui/routes/main_route/page_info.dart | 35 +++ .../routes/main_route/smartphone_route.dart | 233 +----------------- lib/ui/routes/main_route/tablet_route.dart | 55 ++--- lib/ui/screens/conversations_list_screen.dart | 2 +- .../mobile_mode/mobile_appbar_widget.dart | 86 +++++-- 6 files changed, 247 insertions(+), 304 deletions(-) create mode 100644 lib/ui/routes/main_route/page_info.dart diff --git a/lib/ui/routes/main_route/main_route.dart b/lib/ui/routes/main_route/main_route.dart index e71c74a..6faf3b6 100644 --- a/lib/ui/routes/main_route/main_route.dart +++ b/lib/ui/routes/main_route/main_route.dart @@ -1,3 +1,23 @@ +import 'package:comunic/helpers/account_helper.dart'; +import 'package:comunic/ui/routes/account_settings/account_settings_route.dart'; +import 'package:comunic/ui/routes/app_settings_route.dart'; +import 'package:comunic/ui/routes/conversation_route.dart'; +import 'package:comunic/ui/routes/login_route.dart'; +import 'package:comunic/ui/routes/main_route/page_info.dart'; +import 'package:comunic/ui/screens/call_screen.dart'; +import 'package:comunic/ui/screens/conversations_list_screen.dart'; +import 'package:comunic/ui/screens/friends_list_screen.dart'; +import 'package:comunic/ui/screens/group_screen.dart'; +import 'package:comunic/ui/screens/groups_list_screen.dart'; +import 'package:comunic/ui/screens/newest_posts.dart'; +import 'package:comunic/ui/screens/notifications_screen.dart'; +import 'package:comunic/ui/screens/other_friends_lists_screen.dart'; +import 'package:comunic/ui/screens/search_screen.dart'; +import 'package:comunic/ui/screens/user_access_denied_screen.dart'; +import 'package:comunic/ui/screens/user_page_screen.dart'; +import 'package:comunic/utils/account_utils.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Abstract main application route @@ -16,26 +36,123 @@ mixin MainRoute implements StatefulWidget {} /// Public interface of home controller abstract class MainController extends State { + final _pagesStack = List(); + + /// Default page of the application + PageInfo get defaultPage; + + /// Get the current page being active in the application + PageInfo get currentPage => _pagesStack.last; + + /// Get the current number of page in application stack + int get numberOfPages => _pagesStack.length; + /// Get current instance of Home controller static MainController of(BuildContext context) { assert(context != null); // A future implementation might need context again return mainControllerKey.currentState; } + @override + void initState() { + super.initState(); + _pagesStack.add(defaultPage); + } + + /// Push a new page + void pushPage(PageInfo page) => setState(() => _pagesStack.add(page)); + + /// Pop current page. Do not call this method directly. + @protected + void doPopPage() { + if (_pagesStack.length > 0) setState(() => _pagesStack.removeLast()); + } + /// Pop until main route is reached void popUntilMainRoute() => Navigator.of(context).popUntil((settings) => ModalRoute.of(context).isCurrent || !ModalRoute.of(context).isActive); - /// Open user page - void openUserPage(int userID); + /// Open notifications page + void openNotificationsPage() => pushPage(PageInfo( + type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen())); - void openUserAccessDeniedPage(int userID); + /// Open conversations page + void openConversationsPage() => pushPage(PageInfo( + type: PageType.CONVERSATIONS_LIST_PAGE, + child: ConversationsListScreen())); + + /// Open latest posts page + void openLatestPostsPage() => pushPage( + PageInfo(type: PageType.LATEST_POSTS_PAGE, child: NewestPostsScreen())); + + /// Open user page + void openUserPage(int userID) => pushPage(PageInfo( + type: PageType.USER_PAGE, child: UserPageScreen(userID: userID))); + + void openUserAccessDeniedPage(int userID) => + pushPage(PageInfo(child: UserAccessDeniedScreen(userID: userID))); + + /// Open current user page + void openCurrentUserPage() => this.openUserPage(userID()); /// Open a specific group page specified by its [groupID] - void openGroup(int groupID); + void openGroup(int groupID) => pushPage(PageInfo( + type: PageType.GROUP_PAGE, child: GroupPageScreen(groupID: groupID))); + + /// Display the list of friends of current user + void openFriendsList() => pushPage( + PageInfo(type: PageType.FRIENDS_LIST_PAGE, child: FriendsListScreen())); + + /// Open search page + void openSearchPage() => pushPage(PageInfo(child: SearchScreen())); + + /// Open groups list page + void openGroupsListPage() => pushPage(PageInfo(child: GroupsListScreen())); /// Display the list of friends of a user - void openUserFriendsList(int userID); + void openUserFriendsList(int userID) => + pushPage(PageInfo(child: OtherUserFriendsListScreen(userID: userID))); + + /// Push a new widget + void push(Widget w, + {bool hideNavBar = false, bool canShowAsDialog = false}) => + pushPage(PageInfo( + child: w, hideNavBar: hideNavBar, canShowAsDialog: canShowAsDialog)); + + /// Open a conversation + void openConversation(int convID, {fullScreen: false}) => pushPage(PageInfo( + type: PageType.CONVERSATION_PAGE, + child: ConversationRoute(conversationID: convID), + hideNavBar: true, + )); + + /// Start a call for a given conversation + void startCall(int convID) => + pushPage(PageInfo(child: CallScreen(convID: null), hideNavBar: true)); + + /// Open account settings + void openAccountSettings() => Navigator.of(context) + .push(MaterialPageRoute(builder: (c) => AccountSettingsRoute())); + + /// Open application settings + void openAppSettings() => Navigator.of(context) + .push(MaterialPageRoute(builder: (c) => AppSettingsRoute())); + + /// Handle logout requests + void requestLogout() async { + if (!await showConfirmDialog( + context: context, + message: tr("Do you really want to sign out from the application ?"), + title: tr("Sign out"))) return; + + popUntilMainRoute(); + + await AccountHelper().signOut(); + + Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) { + return LoginRoute(); + })); + } /// Pop current page. Last page can not be popped /// @@ -46,17 +163,4 @@ abstract class MainController extends State { else doPopPage(); } - - /// Pop current page. Do not call this method directly. - @protected - void doPopPage(); - - /// Push a new widget - void push(Widget w, {bool hideNavBar, bool allowAsDialog}); - - /// Open a conversation - void openConversation(int convID, {fullScreen: false}); - - /// Start a call for a given conversation - void startCall(int convID); } diff --git a/lib/ui/routes/main_route/page_info.dart b/lib/ui/routes/main_route/page_info.dart new file mode 100644 index 0000000..d16425c --- /dev/null +++ b/lib/ui/routes/main_route/page_info.dart @@ -0,0 +1,35 @@ +import 'package:flutter/cupertino.dart'; + +/// Single page information +/// +/// @author Pierre HUBERT + +enum PageType { + USER_PAGE, + GROUP_PAGE, + CONVERSATION_PAGE, + NOTIFICATIONS_PAGE, + CONVERSATIONS_LIST_PAGE, + FRIENDS_LIST_PAGE, + LATEST_POSTS_PAGE, + OTHER_PAGE +} + +class PageInfo { + final PageType type; + final Widget child; + final int id; + final bool hideNavBar; + final bool canShowAsDialog; + + const PageInfo({ + this.type = PageType.OTHER_PAGE, + @required this.child, + this.id, + this.hideNavBar = false, + this.canShowAsDialog = false, + }) : assert(type != null), + assert(child != null), + assert(hideNavBar != null), + assert(canShowAsDialog != null); +} diff --git a/lib/ui/routes/main_route/smartphone_route.dart b/lib/ui/routes/main_route/smartphone_route.dart index b0ac16d..d5a0338 100644 --- a/lib/ui/routes/main_route/smartphone_route.dart +++ b/lib/ui/routes/main_route/smartphone_route.dart @@ -1,27 +1,9 @@ -import 'package:comunic/helpers/account_helper.dart'; -import 'package:comunic/ui/routes/account_settings/account_settings_route.dart'; -import 'package:comunic/ui/routes/app_settings_route.dart'; -import 'package:comunic/ui/routes/conversation_route.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart'; -import 'package:comunic/ui/screens/call_screen.dart'; -import 'package:comunic/ui/screens/conversations_list_screen.dart'; -import 'package:comunic/ui/screens/friends_list_screen.dart'; -import 'package:comunic/ui/screens/group_screen.dart'; -import 'package:comunic/ui/screens/groups_list_screen.dart'; -import 'package:comunic/ui/screens/newest_posts.dart'; +import 'package:comunic/ui/routes/main_route/page_info.dart'; import 'package:comunic/ui/screens/notifications_screen.dart'; -import 'package:comunic/ui/screens/other_friends_lists_screen.dart'; -import 'package:comunic/ui/screens/search_screen.dart'; -import 'package:comunic/ui/screens/user_access_denied_screen.dart'; -import 'package:comunic/ui/screens/user_page_screen.dart'; import 'package:comunic/ui/widgets/mobile_mode/mobile_appbar_widget.dart'; -import 'package:comunic/utils/account_utils.dart'; -import 'package:comunic/utils/intl_utils.dart'; -import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; -import '../login_route.dart'; - /// Main route of the application /// /// @author Pierre HUBERT @@ -33,138 +15,20 @@ class SmartphoneMainRoute extends StatefulWidget implements MainRoute { State createState() => _MainRouteState(); } -class CurrPage { - final BarCallbackActions action; - final Map args; - final bool hideNavBar; - - const CurrPage( - this.action, { - this.args, - this.hideNavBar = false, - }) : assert(action != null), - assert(hideNavBar != null); - - @override - String toString() => - "CurrPage {\n\taction: " + - this.action.toString() + - "\n\targs: " + - this.args.toString() + - "\n}"; -} - /// Private implementation of HomeController class _MainRouteState extends MainController { - CurrPage get _currTab => history.last; - List history = List(); - - /// Change currently visible tab - void _pushPage(CurrPage newPage) { - setState(() { - history.add(newPage); - }); - } - - /// Pop the page - void doPopPage() { - if (history.length > 1) history.removeLast(); - setState(() {}); - } + @override + PageInfo get defaultPage => + PageInfo(type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen()); /// Allows to go to previous tab Future _willPop() async { - if (history.length == 1) return true; + if (numberOfPages == 1) return true; popPage(); return false; } - /// Handles a new tab being tapped - void _onTap(BarCallbackActions action) { - /// Check more quick actions - switch (action) { - - /// Open current user page - case BarCallbackActions.OPEN_MY_PAGE: - _openCurrentUserPage(); - break; - - /// Open app settings page - case BarCallbackActions.OPEN_APP_SETTINGS: - _openAppSettings(); - break; - - case BarCallbackActions.OPEN_ACCOUNT_SETTINGS: - _openAccountsSettings(); - break; - - /// Logout user - case BarCallbackActions.ACTION_LOGOUT: - _logoutRequested(); - break; - - default: - _pushPage(CurrPage(action)); - } - } - - @override - void initState() { - super.initState(); - - // Default page: conversations list - _pushPage(CurrPage(BarCallbackActions.OPEN_NOTIFICATIONS)); - } - - /// Build the body of the application - Widget _buildBody(BuildContext context) { - switch (_currTab.action) { - case BarCallbackActions.OPEN_CUSTOM_WIDGET: - return _currTab.args["widget"]; - - case BarCallbackActions.OPEN_NOTIFICATIONS: - return NotificationsScreen(); - - case BarCallbackActions.OPEN_CONVERSATIONS: - return ConversationsListScreen(); - - case BarCallbackActions.OPEN_NEWEST_POSTS: - return NewestPostsScreen(); - - case BarCallbackActions.OPEN_FRIENDS: - return FriendsListScreen(); - - case BarCallbackActions.OPEN_USER_PAGE: - return UserPageScreen(userID: _currTab.args["userID"]); - - case BarCallbackActions.OPEN_USER_ACCESS_DENIED_PAGE: - return UserAccessDeniedScreen(userID: _currTab.args["userID"]); - - case BarCallbackActions.OPEN_SEARCH_PAGE: - return SearchScreen(); - - case BarCallbackActions.OPEN_GROUPS: - return GroupsListScreen(); - - case BarCallbackActions.OPEN_GROUP_PAGE: - return GroupPageScreen(groupID: _currTab.args["groupID"]); - - case BarCallbackActions.OPEN_USER_FRIENDS_LIST: - return OtherUserFriendsListScreen( - userID: _currTab.args["userID"], - ); - - case BarCallbackActions.OPEN_CONVERSATION: - return ConversationRoute( - conversationID: _currTab.args["convID"], - ); - - default: - throw "Invalid tab : " + _currTab.toString(); - } - } - @override Widget build(BuildContext context) { return Container( @@ -174,94 +38,13 @@ class _MainRouteState extends MainController { child: WillPopScope( onWillPop: _willPop, child: Scaffold( - appBar: _currTab.hideNavBar + appBar: currentPage.hideNavBar ? null - : ComunicMobileAppBar( - onTap: _onTap, - selectedAction: _currTab.action, - ), - body: SafeArea( - child: _buildBody(context), - ), + : ComunicMobileAppBar(currentPage: currentPage), + body: SafeArea(child: currentPage.child), ), ), ), ); } - - /// Open current user page - Future _openCurrentUserPage() async { - this.openUserPage(userID()); - } - - void _openAppSettings() { - Navigator.of(context) - .push(MaterialPageRoute(builder: (c) => AppSettingsRoute())); - } - - void _openAccountsSettings() { - Navigator.of(context) - .push(MaterialPageRoute(builder: (c) => AccountSettingsRoute())); - } - - /// Handle logout requests from user - Future _logoutRequested() async { - if (!await showConfirmDialog( - context: context, - message: tr("Do you really want to sign out from the application ?"), - title: tr("Sign out"))) return; - - await AccountHelper().signOut(); - - Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) { - return LoginRoute(); - })); - } - - @override - void openGroup(int groupID) { - _pushPage(CurrPage(BarCallbackActions.OPEN_GROUP_PAGE, - args: {"groupID": groupID})); - } - - @override - void openUserPage(int userID) { - _pushPage( - CurrPage(BarCallbackActions.OPEN_USER_PAGE, args: {"userID": userID})); - } - - @override - void openUserAccessDeniedPage(int userID) { - _pushPage(CurrPage(BarCallbackActions.OPEN_USER_ACCESS_DENIED_PAGE, - args: {"userID": userID})); - } - - @override - void openUserFriendsList(int userID) { - _pushPage(CurrPage(BarCallbackActions.OPEN_USER_FRIENDS_LIST, - args: {"userID": userID})); - } - - @override - void push(Widget w, {bool hideNavBar = false, bool allowAsDialog}) { - _pushPage(CurrPage( - BarCallbackActions.OPEN_CUSTOM_WIDGET, - args: {"widget": w}, - hideNavBar: hideNavBar, - )); - } - - @override - void openConversation(int convID, {fullScreen: false}) { - _pushPage(CurrPage( - BarCallbackActions.OPEN_CONVERSATION, - args: {"convID": convID}, - hideNavBar: true, - )); - } - - @override - void startCall(int convID) { - push(CallScreen(convID: convID), hideNavBar: true); - } } diff --git a/lib/ui/routes/main_route/tablet_route.dart b/lib/ui/routes/main_route/tablet_route.dart index 586eb56..3c5209e 100644 --- a/lib/ui/routes/main_route/tablet_route.dart +++ b/lib/ui/routes/main_route/tablet_route.dart @@ -1,5 +1,7 @@ import 'package:comunic/ui/dialogs/screen_dialog.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/screens/newest_posts.dart'; import 'package:comunic/ui/widgets/tablet_mode/calls/calls_area.dart'; import 'package:comunic/ui/widgets/tablet_mode/conversations/conversations_area_widget.dart'; import 'package:comunic/ui/widgets/tablet_mode/current_user_panel.dart'; @@ -23,6 +25,9 @@ class _TabletRouteState extends MainController { final _conversationsKey = GlobalKey(); final _callsKey = GlobalKey(); + @override + PageInfo get defaultPage => PageInfo(child: NewestPostsScreen()); + @override Widget build(BuildContext context) { return Scaffold( @@ -67,47 +72,23 @@ class _TabletRouteState extends MainController { Widget _buildRightPane() => Container(); + @override + void pushPage(PageInfo page) { + if (page.canShowAsDialog == true) + showScreenDialog(context, page.child); + else + super.pushPage(page); + } + @override void openConversation(int convID, {fullScreen = false}) { - popUntilMainRoute(); - _conversationsKey.currentState.openConversations(convID); - } - - @override - void openGroup(int groupID) { - // TODO: implement openGroup - } - - @override - void openUserAccessDeniedPage(int userID) { - // TODO: implement openUserAccessDeniedPage - } - - @override - void openUserFriendsList(int userID) { - // TODO: implement openUserFriendsList - } - - @override - void openUserPage(int userID) { - // TODO: implement openUserPage - } - - @override - void push(Widget w, {bool hideNavBar, bool allowAsDialog}) { - if (allowAsDialog == true) { - showScreenDialog(context, w); - return; - } - - // TODO: implement push + if (!fullScreen) { + popUntilMainRoute(); + _conversationsKey.currentState.openConversations(convID); + } else + super.openConversation(convID, fullScreen: fullScreen); } @override void startCall(int convID) => _callsKey.currentState.openCall(convID); - - @override - void doPopPage() { - // TODO: implement doPopPage - } } diff --git a/lib/ui/screens/conversations_list_screen.dart b/lib/ui/screens/conversations_list_screen.dart index a76a347..e371509 100644 --- a/lib/ui/screens/conversations_list_screen.dart +++ b/lib/ui/screens/conversations_list_screen.dart @@ -117,7 +117,7 @@ class _ConversationScreenState extends SafeState { /// Create a new conversation void _createConversation() { MainController.of(context) - .push(CreateConversationScreen(), allowAsDialog: true); + .push(CreateConversationScreen(), canShowAsDialog: true); if (widget.onOpen != null) widget.onOpen(); } diff --git a/lib/ui/widgets/mobile_mode/mobile_appbar_widget.dart b/lib/ui/widgets/mobile_mode/mobile_appbar_widget.dart index 13d8bf8..494df42 100644 --- a/lib/ui/widgets/mobile_mode/mobile_appbar_widget.dart +++ b/lib/ui/widgets/mobile_mode/mobile_appbar_widget.dart @@ -1,6 +1,8 @@ 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'; @@ -14,7 +16,6 @@ typedef OnSelectMenuAction = void Function(BarCallbackActions); /// Callback actions enum BarCallbackActions { - OPEN_CUSTOM_WIDGET, OPEN_NOTIFICATIONS, OPEN_CONVERSATIONS, OPEN_NEWEST_POSTS, @@ -22,14 +23,8 @@ enum BarCallbackActions { OPEN_MY_PAGE, OPEN_SEARCH_PAGE, OPEN_GROUPS, - OPEN_GROUP_PAGE, - OPEN_USER_PAGE, - OPEN_USER_ACCESS_DENIED_PAGE, OPEN_ACCOUNT_SETTINGS, OPEN_APP_SETTINGS, - OPEN_USER_FRIENDS_LIST, - OPEN_CONVERSATION, - NONE, ACTION_LOGOUT } @@ -43,16 +38,19 @@ class _MenuItem { 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(icon != null || isMenu), - assert(action != null), - assert(isMenu != null); + assert(isMenu != null), + assert(isMenu || icon != null), + assert(isMenu || action != null), + assert(isMenu || pageType != null); } /// Item of action menu @@ -70,24 +68,29 @@ final _menuItems = <_MenuItem>[ _MenuItem( label: tr("Notifications"), icon: Icon(Icons.notifications), - action: BarCallbackActions.OPEN_NOTIFICATIONS), + action: BarCallbackActions.OPEN_NOTIFICATIONS, + pageType: PageType.NOTIFICATIONS_PAGE), _MenuItem( label: tr("Conversations"), icon: Icon(Icons.comment), - action: BarCallbackActions.OPEN_CONVERSATIONS), + action: BarCallbackActions.OPEN_CONVERSATIONS, + pageType: PageType.CONVERSATIONS_LIST_PAGE), _MenuItem( label: tr("Newest"), icon: Icon(Icons.refresh), - action: BarCallbackActions.OPEN_NEWEST_POSTS), + action: BarCallbackActions.OPEN_NEWEST_POSTS, + pageType: PageType.LATEST_POSTS_PAGE), _MenuItem( label: tr("Friends"), icon: Icon(Icons.group), - action: BarCallbackActions.OPEN_FRIENDS), + action: BarCallbackActions.OPEN_FRIENDS, + pageType: PageType.FRIENDS_LIST_PAGE), _MenuItem( label: tr("Menu"), icon: Icon(Icons.more_vert), isMenu: true, - action: BarCallbackActions.NONE) + action: null, + pageType: null) ]; /// List of menu actions items @@ -107,13 +110,14 @@ final _menuActionsItem = <_ActionMenuItem>[ ]; /// Public widget -class ComunicMobileAppBar extends StatefulWidget implements PreferredSizeWidget { - final OnSelectMenuAction onTap; - final BarCallbackActions selectedAction; +class ComunicMobileAppBar extends StatefulWidget + implements PreferredSizeWidget { + final PageInfo currentPage; - const ComunicMobileAppBar( - {Key key, @required this.onTap, @required this.selectedAction}) - : assert(onTap != null), + const ComunicMobileAppBar({ + Key key, + @required this.currentPage, + }) : assert(currentPage != null), super(key: key); @override @@ -178,14 +182,50 @@ class _ComunicMobileAppBarState extends SafeState { _menuItems.length, (i) => _MenuItemWidget( item: _menuItems[i], - onTap: widget.onTap, - isSelected: _menuItems[i].action == widget.selectedAction, + 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