diff --git a/lib/ui/router.dart b/lib/ui/router.dart index ae1b3598..e119051e 100644 --- a/lib/ui/router.dart +++ b/lib/ui/router.dart @@ -6,14 +6,21 @@ import 'package:go_router/go_router.dart'; import 'package:unn_mobile/core/misc/app_settings.dart'; import 'package:unn_mobile/ui/views/auth_page/auth_page.dart'; import 'package:unn_mobile/ui/views/loading_page/loading_page.dart'; +import 'package:unn_mobile/ui/views/main_page/about/about.dart'; +import 'package:unn_mobile/ui/views/main_page/donations/donations.dart'; import 'package:unn_mobile/ui/views/main_page/main_page.dart'; import 'package:unn_mobile/ui/views/main_page/main_page_routing.dart'; +import 'package:unn_mobile/ui/views/main_page/settings/settings.dart'; -const loadingPageRoute = '/loading'; -const mainPageRoute = '/'; +const loadingPageRoute = '/'; +const mainPageRoute = '/main'; const authPageRoute = '/auth'; const drawerRoutePrefix = 'drawer'; +const settingsPath = 'settings'; +const aboutPath = 'about'; +const supportPath = 'support'; + final shellBranchKeys = [ for (final route in MainPageRouting.navbarRoutes) (key: GlobalKey(), route: route), @@ -25,6 +32,36 @@ final mainRouter = GoRouter( initialLocation: loadingPageRoute, initialExtra: {}, routes: [ + GoRoute( + path: loadingPageRoute, + pageBuilder: (context, state) => const NoTransitionPage( + child: LoadingPage(), + ), + ), + GoRoute( + path: authPageRoute, + name: 'auth', + builder: (context, state) => const AuthPage(), + routes: [ + GoRoute( + path: settingsPath, + name: 'settings', + builder: (_, __) => const SettingsScreenView( + hasAccount: false, + ), + ), + GoRoute( + path: aboutPath, + name: 'about', + builder: (_, __) => const AboutScreenView(), + ), + GoRoute( + path: supportPath, + name: 'support', + builder: (_, __) => const DonationsScreenView(), + ), + ], + ), StatefulShellRoute.indexedStack( builder: (context, state, shell) => MainPage( key: mainPageKey, @@ -36,7 +73,7 @@ final mainRouter = GoRouter( navigatorKey: route.key, routes: [ GoRoute( - path: route.route.pageRoute, + path: '$mainPageRoute${route.route.pagePath}', name: route.route.pageTitle, pageBuilder: (context, state) => NoTransitionPage( child: route.route.builder(context, state), @@ -53,17 +90,6 @@ final mainRouter = GoRouter( ), ], ), - GoRoute( - path: authPageRoute, - name: 'auth', - builder: (context, state) => const AuthPage(), - ), - GoRoute( - path: loadingPageRoute, - pageBuilder: (context, state) => const NoTransitionPage( - child: LoadingPage(), - ), - ), ], redirect: (context, state) { if (state.uri.path == mainPageRoute) { @@ -71,10 +97,11 @@ final mainRouter = GoRouter( AppSettings.initialPage < MainPageRouting.activeNavbarRoutes.length ? AppSettings.initialPage : 0; - return MainPageRouting.navbarRoutes - .where((r) => !r.isDisabled) - .toList()[pageIndex] - .pageRoute; + return mainPageRoute + + MainPageRouting.navbarRoutes + .where((r) => !r.isDisabled) + .toList()[pageIndex] + .pagePath; } return null; }, @@ -87,7 +114,7 @@ List _buildSubroutes( routes .map( (route) => GoRoute( - path: '$prefix${route.pageRoute}', + path: '$prefix${route.pagePath}', builder: (context, state) => route.builder(context, state), routes: _buildSubroutes(route.subroutes), ), diff --git a/lib/ui/views/auth_page/auth_page.dart b/lib/ui/views/auth_page/auth_page.dart index d54b4de5..3f3d7a02 100644 --- a/lib/ui/views/auth_page/auth_page.dart +++ b/lib/ui/views/auth_page/auth_page.dart @@ -15,6 +15,7 @@ import 'package:unn_mobile/ui/views/auth_page/widgets/auth_text_field.dart'; import 'package:unn_mobile/ui/views/base_view.dart'; import 'package:unn_mobile/ui/widgets/dialogs/analytics_confirm_dialog.dart'; import 'package:unn_mobile/ui/widgets/dialogs/changelog_dialog.dart'; +import 'package:unn_mobile/ui/widgets/menu_button.dart'; import 'package:unn_mobile/ui/widgets/wide_button.dart'; enum _InputType { @@ -22,6 +23,12 @@ enum _InputType { password, } +class _MenuItemValues { + static const settings = 'settings'; + static const about = 'about'; + static const support = 'support'; +} + class AuthPage extends StatefulWidget { const AuthPage({super.key}); @@ -126,22 +133,55 @@ class _AuthPageState extends State { return Scaffold( backgroundColor: theme.colorScheme.surfaceContainerLowest, + appBar: AppBar( + titleTextStyle: TextStyle( + fontSize: _titleFontSize, + height: _titleHeightFactor, + fontWeight: FontWeight.bold, + color: theme.colorScheme.primary, + ), + centerTitle: true, + title: const Text( + _title, + ), + actions: [ + MenuButton( + enabled: true, + items: [ + const SideMenuItem( + title: 'Настройки', + value: _MenuItemValues.settings, + ), + const SideMenuItem( + title: 'Поддержать', + value: _MenuItemValues.support, + ), + const SideMenuItem( + title: 'О нас', + value: _MenuItemValues.about, + ), + ], + onSelected: (item) { + switch (item) { + case _MenuItemValues.settings: + GoRouter.of(context).go('$authPageRoute/$settingsPath'); + break; + case _MenuItemValues.about: + GoRouter.of(context).go('$authPageRoute/$aboutPath'); + break; + case _MenuItemValues.support: + GoRouter.of(context).go('$authPageRoute/$supportPath'); + break; + } + }, + ), + ], + ), body: SafeArea( bottom: false, child: LayoutBuilder( builder: (context, constraints) => Column( children: [ - const SizedBox(height: _titleTopPadding), - Text( - _title, - style: TextStyle( - fontFamily: 'Inter', - fontSize: _titleFontSize, - height: _titleHeightFactor, - fontWeight: FontWeight.bold, - color: theme.colorScheme.primary, - ), - ), const SizedBox(height: _logoTopPadding), AnimatedContainer( duration: _resizeDuration, diff --git a/lib/ui/views/main_page/feed/feed.dart b/lib/ui/views/main_page/feed/feed.dart index 64556fc7..0eb29d65 100644 --- a/lib/ui/views/main_page/feed/feed.dart +++ b/lib/ui/views/main_page/feed/feed.dart @@ -372,7 +372,7 @@ class FeedScreenViewState extends State void openPinned(BuildContext context) { GoRouter.of(context).go( '${GoRouter.of(context).routeInformationProvider.value.uri.path}/' - '${pinnedPostsRoute.pageRoute}', + '${pinnedPostsRoute.pagePath}', ); } diff --git a/lib/ui/views/main_page/feed/widgets/feed_post.dart b/lib/ui/views/main_page/feed/widgets/feed_post.dart index e101a5b1..6cbe8829 100644 --- a/lib/ui/views/main_page/feed/widgets/feed_post.dart +++ b/lib/ui/views/main_page/feed/widgets/feed_post.dart @@ -281,7 +281,7 @@ class _FeedPostState extends State { } GoRouter.of(context).go( '${GoRouter.of(context).routeInformationProvider.value.uri.path}/' - '${postCommentsRoute.pageRoute.replaceAll(':postId', post.blogData!.id.toString())}', + '${postCommentsRoute.pagePath.replaceAll(':postId', post.blogData!.id.toString())}', ); } diff --git a/lib/ui/views/main_page/main_page.dart b/lib/ui/views/main_page/main_page.dart index a7719bfd..8e534ed0 100644 --- a/lib/ui/views/main_page/main_page.dart +++ b/lib/ui/views/main_page/main_page.dart @@ -66,7 +66,8 @@ class MainPageState extends State { bool isRootScreen(BuildContext context) { final location = GoRouterState.of(context).uri.path; - return MainPageRouting.navbarRoutes.any((r) => r.pageRoute == location); + return MainPageRouting.navbarRoutes + .any((r) => mainPageRoute + r.pagePath == location); } @override @@ -83,12 +84,13 @@ class MainPageState extends State { onDestinationSelected: (value) { Scaffold.of(context).closeDrawer(); final selectedBarIndex = widget.shell.currentIndex; - final currentPageRoute = MainPageRouting - .navbarRoutes[selectedBarIndex].pageRoute; + final currentPageRoute = mainPageRoute + + MainPageRouting + .navbarRoutes[selectedBarIndex].pagePath; final destinationSubroute = model.routes .where((r) => model.isOnline || !r.onlineOnly) .elementAt(value) - .pageRoute; + .pagePath; GoRouter.of(context).go( '$currentPageRoute/$drawerRoutePrefix/$destinationSubroute', ); @@ -127,6 +129,6 @@ class MainPageState extends State { void goToRootScreen(BuildContext context, int currentRouteIndex) { GoRouter.of(context) - .go(MainPageRouting.navbarRoutes[currentRouteIndex].pageRoute); + .go(MainPageRouting.navbarRoutes[currentRouteIndex].pagePath); } } diff --git a/lib/ui/views/main_page/main_page_navigation_bar.dart b/lib/ui/views/main_page/main_page_navigation_bar.dart index 967684a5..3ad30b86 100644 --- a/lib/ui/views/main_page/main_page_navigation_bar.dart +++ b/lib/ui/views/main_page/main_page_navigation_bar.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:unn_mobile/core/viewmodels/main_page/main_page_view_model.dart'; +import 'package:unn_mobile/ui/router.dart'; import 'package:unn_mobile/ui/views/main_page/main_page_routing.dart'; class MainPageNavigationBar extends StatelessWidget { @@ -42,8 +43,9 @@ class MainPageNavigationBar extends StatelessWidget { static int getSelectedBarIndex(BuildContext context) { final String location = GoRouterState.of(context).uri.path; - return MainPageRouting.navbarRoutes - .indexWhere((route) => location.startsWith(route.pageRoute)); + return MainPageRouting.navbarRoutes.indexWhere( + (route) => location.startsWith(mainPageRoute + route.pagePath), + ); } List _getNavbarDestinations( diff --git a/lib/ui/views/main_page/main_page_routing.dart b/lib/ui/views/main_page/main_page_routing.dart index 31c1acef..7b7fdaf2 100644 --- a/lib/ui/views/main_page/main_page_routing.dart +++ b/lib/ui/views/main_page/main_page_routing.dart @@ -21,7 +21,7 @@ class MainPageRouteData { final IconData selectedIcon; final IconData unselectedIcon; final String pageTitle; - final String pageRoute; + final String pagePath; final Widget Function(BuildContext, GoRouterState) builder; final bool isDisabled; final List userTypes; @@ -32,7 +32,7 @@ class MainPageRouteData { this.selectedIcon, this.unselectedIcon, this.pageTitle, - this.pageRoute, { + this.pagePath, { required this.userTypes, required this.builder, this.onlineOnly = false, @@ -44,7 +44,7 @@ class MainPageRouteData { IconData? selectedIcon, IconData? unselectedIcon, String? pageTitle, - String? pageRoute, + String? pagePath, Widget Function(BuildContext, GoRouterState)? builder, bool? isDisabled, List? userTypes, @@ -55,7 +55,7 @@ class MainPageRouteData { selectedIcon ?? this.selectedIcon, unselectedIcon ?? this.unselectedIcon, pageTitle ?? this.pageTitle, - pageRoute ?? this.pageRoute, + pagePath ?? this.pagePath, onlineOnly: onlineOnly ?? this.onlineOnly, subroutes: subroutes ?? this.subroutes, isDisabled: isDisabled ?? this.isDisabled, diff --git a/lib/ui/views/main_page/settings/settings.dart b/lib/ui/views/main_page/settings/settings.dart index aba722e3..a8c4ab8b 100644 --- a/lib/ui/views/main_page/settings/settings.dart +++ b/lib/ui/views/main_page/settings/settings.dart @@ -12,8 +12,13 @@ import 'package:unn_mobile/ui/views/main_page/main_page.dart'; class SettingsScreenView extends StatelessWidget { final int? bottomRouteIndex; + final bool hasAccount; - const SettingsScreenView({super.key, this.bottomRouteIndex}); + const SettingsScreenView({ + super.key, + this.bottomRouteIndex, + this.hasAccount = true, + }); @override Widget build(BuildContext context) { @@ -47,31 +52,33 @@ class SettingsScreenView extends StatelessWidget { model.analyticsEnabled = value; }, ), - SwitchListTile.adaptive( - title: const Text('Порядок комментариев'), - subtitle: model.reverseComments - ? const Text('Сначала новые') - : const Text('Сначала старые'), - value: model.reverseComments, - onChanged: (value) { - model.reverseComments = value; - }, - ), - ListTile( - title: const Text('Начальный экран'), - trailing: Padding( - padding: const EdgeInsets.all(6.0), - child: Text( - model.initialScreenName, - style: const TextStyle(fontSize: 16), - softWrap: false, - overflow: TextOverflow.visible, + if (hasAccount) ...[ + SwitchListTile.adaptive( + title: const Text('Порядок комментариев'), + subtitle: model.reverseComments + ? const Text('Сначала новые') + : const Text('Сначала старые'), + value: model.reverseComments, + onChanged: (value) { + model.reverseComments = value; + }, + ), + ListTile( + title: const Text('Начальный экран'), + trailing: Padding( + padding: const EdgeInsets.all(6.0), + child: Text( + model.initialScreenName, + style: const TextStyle(fontSize: 16), + softWrap: false, + overflow: TextOverflow.visible, + ), ), + onTap: () async { + await _showScreenChoiceModal(context, model); + }, ), - onTap: () async { - await _showScreenChoiceModal(context, model); - }, - ), + ], ListTile( title: const Text('Очистить кэш'), onTap: () async { @@ -85,29 +92,30 @@ class SettingsScreenView extends StatelessWidget { } }, ), - ListTile( - title: const Text('Выйти из аккаунта'), - onTap: () async { - if (context.mounted) { - final result = await showOkCancelAlertDialog( - context: context, - title: 'Выйти из аккаунта?', - okLabel: 'Выйти', - cancelLabel: 'Отмена', - isDestructiveAction: true, - ); + if (hasAccount) + ListTile( + title: const Text('Выйти из аккаунта'), + onTap: () async { + if (context.mounted) { + final result = await showOkCancelAlertDialog( + context: context, + title: 'Выйти из аккаунта?', + okLabel: 'Выйти', + cancelLabel: 'Отмена', + isDestructiveAction: true, + ); - if (result == OkCancelResult.ok && - context.mounted) { - await model.logout(); - if (context.mounted) { - GoRouter.of(context).go(loadingPageRoute); + if (result == OkCancelResult.ok && + context.mounted) { + await model.logout(); + if (context.mounted) { + GoRouter.of(context).go(loadingPageRoute); + } } } - } - }, - textColor: theme.colorScheme.error, - ), + }, + textColor: theme.colorScheme.error, + ), ], ), ], diff --git a/pubspec.yaml b/pubspec.yaml index 62fcb383..8f05b648 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: unn_mobile description: A mobile application for UNN Portal website publish_to: 'none' -version: 0.6.1+430 +version: 0.6.1+431 environment: sdk: '>=3.1.2 <4.0.0'