import 'package:comunic/helpers/account_helper.dart'; import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/ui/routes/conversation_message_stats_route.dart'; import 'package:comunic/ui/routes/conversation_route.dart'; import 'package:comunic/ui/routes/main_route/page_info.dart'; import 'package:comunic/ui/routes/settings/account_settings_route.dart'; import 'package:comunic/ui/routes/update_conversation_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/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 /// /// This mixin contains methods available in all display modes /// /// @author Pierre Hubert /// Main controller key statically shared across application /// /// Do not use it directly to avoid context leak, instead use the access method /// [MainController.of] final mainControllerKey = GlobalKey(); mixin MainRoute implements StatefulWidget {} /// Public interface of home controller abstract class MainController extends State { final _pagesStack = []; /// 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) { popUntilMainRoute(); 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); /// Go to the previous page (use for [WillPop] widget) @protected Future willPop() async { if (numberOfPages == 1) return true; popPage(); return false; } /// Open notifications page void openNotificationsPage() => pushPage(PageInfo( type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen())); /// 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), id: 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] /// /// [conversationID] is an optional parameter specifying a conversation /// that should be opened instead of posts thread void openGroup(int groupID, {int? conversationID}) => pushPage(PageInfo( type: PageType.GROUP_PAGE, child: GroupPageScreen( groupID: groupID, conversationID: conversationID, ), id: groupID)); /// Display the list of friends of current user void openFriendsList() => pushPage(PageInfo( type: PageType.FRIENDS_LIST_PAGE, child: FriendsListScreen(), canShowAsDialog: true, )); /// 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? id) { if (id != userID()) pushPage(PageInfo( child: OtherUserFriendsListScreen(userID: id!), canShowAsDialog: true, )); else openFriendsList(); } /// 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 in its context (in group page for group conversations) void openConversation(Conversation conv, {fullScreen: false}) { if (conv.isGroupConversation) openGroup(conv.groupID!, conversationID: conv.id); else openConversationById(conv.id!, fullScreen: fullScreen); } /// Open a conversation void openConversationById(int convID, {fullScreen: false}) => pushPage(PageInfo( type: PageType.CONVERSATION_PAGE, id: convID, child: ConversationRoute(conversationID: convID), hideNavBar: true, )); /// Open conversation settings route void openConversationSettingsRoute(Conversation conv) => pushPage(PageInfo( child: UpdateConversationRoute( conversationID: conv.id!, ), canShowAsDialog: true, hideNavBar: true, )); /// Open a conversation message statistics page void openConversationMessageStats( Conversation conv, ConversationMessage message) => pushPage(PageInfo( child: ConversationMessageStatsRoute( conv: conv, message: message, ), hideNavBar: true, canShowAsDialog: true, )); /// Start a call for a given conversation void startCall(int convID) => pushPage(PageInfo(child: CallScreen(convID: convID), hideNavBar: true)); /// Open settings void openSettings() => Navigator.of(context) .push(MaterialPageRoute(builder: (c) => AccountSettingsRoute())); /// 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(); } /// Pop current page. Last page can not be popped /// /// If the current route is not the main route, we pop one page void popPage() { if (!ModalRoute.of(context)!.isCurrent) Navigator.of(context).pop(); else doPopPage(); } }