From 61a5352bb876ab47cd8809f71bf488abadb54c10 Mon Sep 17 00:00:00 2001 From: otsmr Date: Mon, 9 Feb 2026 23:45:34 +0100 Subject: [PATCH] switch to go_router --- android/app/src/main/AndroidManifest.xml | 1 - lib/app.dart | 45 +-- lib/src/constants/routes.keys.dart | 55 ++++ lib/src/providers/routing.provider.dart | 285 ++++++++++++++++++ lib/src/services/intent/links.intent.dart | 44 +-- lib/src/themes/dark.dart | 13 + lib/src/themes/light.dart | 10 + .../views/camera/camera_qr_scanner.view.dart | 8 +- lib/src/views/chats/chat_list.view.dart | 116 ++----- .../chat_list_components/feedback_btn.dart | 13 +- .../chat_list_components/group_list_item.dart | 75 ++--- lib/src/views/chats/chat_messages.view.dart | 24 +- .../chat_list_entry.dart | 15 +- .../message_send_state_icon.dart | 14 +- lib/src/views/chats/start_new_chat.view.dart | 24 +- .../group_context_menu.component.dart | 17 +- .../components/max_flame_list_title.dart | 12 +- .../user_context_menu.component.dart | 14 +- lib/src/views/components/verified_shield.dart | 14 +- lib/src/views/groups/group.view.dart | 60 ++-- .../group_create_select_members.view.dart | 17 +- .../views/groups/group_member.context.dart | 10 +- lib/src/views/home.view.dart | 28 +- lib/src/views/onboarding/recover.view.dart | 13 +- lib/src/views/onboarding/register.view.dart | 16 +- lib/src/views/public_profile.view.dart | 13 +- .../views/settings/backup/backup.view.dart | 14 +- ...rver.view.dart => backup_server.view.dart} | 8 +- ...ackup.view.dart => setup_backup.view.dart} | 23 +- .../settings/chat/chat_settings.view.dart | 14 +- .../views/settings/data_and_storage.view.dart | 27 +- .../settings/developer/developer.view.dart | 28 +- lib/src/views/settings/help/help.view.dart | 116 +------ lib/src/views/settings/privacy.view.dart | 14 +- ...sers.dart => privacy_view_block.view.dart} | 8 +- .../settings/profile/modify_avatar.view.dart | 8 +- .../views/settings/profile/profile.view.dart | 10 +- .../views/settings/settings_main.view.dart | 156 +--------- .../user_study_questionnaire.view.dart | 13 +- .../user_study/user_study_welcome.view.dart | 21 +- pubspec.lock | 8 + pubspec.yaml | 1 + 42 files changed, 632 insertions(+), 793 deletions(-) create mode 100644 lib/src/constants/routes.keys.dart create mode 100644 lib/src/providers/routing.provider.dart create mode 100644 lib/src/themes/dark.dart create mode 100644 lib/src/themes/light.dart rename lib/src/views/settings/backup/{twonly_safe_server.view.dart => backup_server.view.dart} (95%) rename lib/src/views/settings/backup/{twonly_safe_backup.view.dart => setup_backup.view.dart} (91%) rename lib/src/views/settings/{privacy_view_block.users.dart => privacy_view_block.view.dart} (95%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7f54da8..d6847c0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ android:icon="@mipmap/ic_launcher"> with WidgetsBindingObserver { return ListenableBuilder( listenable: context.watch(), builder: (BuildContext context, Widget? child) { - return MaterialApp( + return MaterialApp.router( + routerConfig: routerProvider, scaffoldMessengerKey: globalRootScaffoldMessengerKey, - restorationScopeId: 'app', localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], + debugShowCheckedModeBanner: false, supportedLocales: const [ Locale('en', ''), Locale('de', ''), ], - onGenerateTitle: (BuildContext context) => 'twonly', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: const Color(0xFF57CC99), - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: PredictiveBackPageTransitionsBuilder(), - }, - ), - inputDecorationTheme: const InputDecorationTheme( - border: OutlineInputBorder(), - ), - ), - darkTheme: ThemeData.dark().copyWith( - colorScheme: ColorScheme.fromSeed( - brightness: Brightness.dark, - seedColor: const Color(0xFF57CC99), - surface: const Color.fromARGB(255, 20, 18, 23), - surfaceContainer: const Color.fromARGB(255, 33, 30, 39), - ), - inputDecorationTheme: const InputDecorationTheme( - border: OutlineInputBorder(), - ), - ), + title: 'twonly', + theme: lightTheme, + darkTheme: darkTheme, themeMode: context.watch().themeMode, - initialRoute: '/', - routes: { - '/': (context) => const AppMainWidget(initialPage: 1), - '/chats': (context) => const AppMainWidget(initialPage: 0), - }, ); }, ); @@ -213,7 +190,7 @@ class _AppMainWidgetState extends State { child = const DatabaseMigrationView(); } else if (_isUserCreated) { if (gUser.twonlySafeBackup == null && !_skipBackup && kReleaseMode) { - child = TwonlyIdentityBackupView( + child = SetupBackupView( callBack: () { _skipBackup = true; setState(() {}); diff --git a/lib/src/constants/routes.keys.dart b/lib/src/constants/routes.keys.dart new file mode 100644 index 0000000..aa8bab5 --- /dev/null +++ b/lib/src/constants/routes.keys.dart @@ -0,0 +1,55 @@ +class Routes { + static const String home = '/'; + static const String chats = '/chats'; + static const String chatsAddNewUser = '/chats/add_new_user'; + static const String chatsArchived = '/chats/archived'; + static const String chatsStartNewChat = '/chats/start_new_chat'; + static const String chatsCameraSendTo = '/chats/camera_send_to'; + static const String chatsMediaViewer = '/chats/media_viewer'; + static const String chatsMessages = '/chats/messages'; + + static String groupCreateSelectMember(String? groupId) => + '/group/create/select_member${groupId == null ? '' : '/$groupId'}'; + + static String profileGroup(String groupId) => '/profile/group/$groupId'; + static String profileContact(int contactId) => '/profile/contact/$contactId'; + + static const String cameraQRScanner = '/camera/qr_scanner'; + + static const String settings = '/settings'; + static const String settingsProfile = '/settings/profile'; + static const String settingsPublicProfile = '/settings/public_profile'; + static const String settingsProfileModifyAvatar = + '/settings/profile/modify_avatar'; + static const String settingsAccount = '/settings/account'; + static const String settingsSubscription = '/settings/subscription'; + static const String settingsBackup = '/settings/backup'; + static const String settingsBackupServer = '/settings/backup/server'; + static const String settingsBackupRecovery = '/settings/backup/recovery'; + static const String settingsBackupSetup = '/settings/backup/setup'; + static const String settingsAppearance = '/settings/appearance'; + static const String settingsChats = '/settings/chats'; + static const String settingsChatsReactions = '/settings/chats/reactions'; + static const String settingsPrivacy = '/settings/privacy'; + static const String settingsPrivacyBlockUsers = + '/settings/privacy/block_users'; + static const String settingsNotification = '/settings/notification'; + static const String settingsStorage = '/settings/storage_data'; + static const String settingsStorageImport = '/settings/storage_data/import'; + static const String settingsStorageExport = '/settings/storage_data/export'; + static const String settingsHelp = '/settings/help'; + static const String settingsHelpFaq = '/settings/help/faq'; + static const String settingsHelpContactUs = '/settings/help/contact_us'; + static const String settingsHelpDiagnostics = '/settings/help/diagnostics'; + static const String settingsHelpUserStudy = '/settings/help/user_study'; + static const String settingsHelpUserStudyQuestionnaire = + '/settings/help/user_study/questionnaire'; + static const String settingsHelpCredits = '/settings/help/credits'; + static const String settingsHelpChangelog = '/settings/help/changelog'; + static const String settingsDeveloper = '/settings/developer'; + static const String settingsDeveloperRetransmissionDatabase = + '/settings/developer/retransmission_database'; + static const String settingsDeveloperAutomatedTesting = + '/settings/developer/automated_testing'; + static const String settingsInvite = '/settings/invite'; +} diff --git a/lib/src/providers/routing.provider.dart b/lib/src/providers/routing.provider.dart new file mode 100644 index 0000000..44dc744 --- /dev/null +++ b/lib/src/providers/routing.provider.dart @@ -0,0 +1,285 @@ +import 'package:go_router/go_router.dart'; +import 'package:twonly/app.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; +import 'package:twonly/src/database/twonly.db.dart'; +import 'package:twonly/src/views/camera/camera_qr_scanner.view.dart'; +import 'package:twonly/src/views/camera/camera_send_to.view.dart'; +import 'package:twonly/src/views/chats/add_new_user.view.dart'; +import 'package:twonly/src/views/chats/archived_chats.view.dart'; +import 'package:twonly/src/views/chats/chat_messages.view.dart'; +import 'package:twonly/src/views/chats/media_viewer.view.dart'; +import 'package:twonly/src/views/chats/start_new_chat.view.dart'; +import 'package:twonly/src/views/contact/contact.view.dart'; +import 'package:twonly/src/views/groups/group.view.dart'; +import 'package:twonly/src/views/groups/group_create_select_members.view.dart'; +import 'package:twonly/src/views/onboarding/recover.view.dart'; +import 'package:twonly/src/views/public_profile.view.dart'; +import 'package:twonly/src/views/settings/account.view.dart'; +import 'package:twonly/src/views/settings/appearance.view.dart'; +import 'package:twonly/src/views/settings/backup/backup.view.dart'; +import 'package:twonly/src/views/settings/backup/backup_server.view.dart'; +import 'package:twonly/src/views/settings/backup/setup_backup.view.dart'; +import 'package:twonly/src/views/settings/chat/chat_reactions.view.dart'; +import 'package:twonly/src/views/settings/chat/chat_settings.view.dart'; +import 'package:twonly/src/views/settings/data_and_storage.view.dart'; +import 'package:twonly/src/views/settings/data_and_storage/export_media.view.dart'; +import 'package:twonly/src/views/settings/data_and_storage/import_media.view.dart'; +import 'package:twonly/src/views/settings/developer/automated_testing.view.dart'; +import 'package:twonly/src/views/settings/developer/developer.view.dart'; +import 'package:twonly/src/views/settings/developer/retransmission_data.view.dart'; +import 'package:twonly/src/views/settings/help/changelog.view.dart'; +import 'package:twonly/src/views/settings/help/contact_us.view.dart'; +import 'package:twonly/src/views/settings/help/credits.view.dart'; +import 'package:twonly/src/views/settings/help/diagnostics.view.dart'; +import 'package:twonly/src/views/settings/help/faq.view.dart'; +import 'package:twonly/src/views/settings/help/help.view.dart'; +import 'package:twonly/src/views/settings/notification.view.dart'; +import 'package:twonly/src/views/settings/privacy.view.dart'; +import 'package:twonly/src/views/settings/privacy_view_block.view.dart'; +import 'package:twonly/src/views/settings/profile/modify_avatar.view.dart'; +import 'package:twonly/src/views/settings/profile/profile.view.dart'; +import 'package:twonly/src/views/settings/settings_main.view.dart'; +import 'package:twonly/src/views/settings/share_with_friends.view.dart'; +import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; +import 'package:twonly/src/views/user_study/user_study_questionnaire.view.dart'; +import 'package:twonly/src/views/user_study/user_study_welcome.view.dart'; + +final routerProvider = GoRouter( + routes: [ + GoRoute( + path: Routes.home, + builder: (context, state) => const AppMainWidget(initialPage: 1), + ), + + // Chats + GoRoute( + path: Routes.chats, + builder: (context, state) => const AppMainWidget(initialPage: 0), + routes: [ + GoRoute( + path: 'add_new_user', + builder: (context, state) => const AddNewUserView(), + ), + GoRoute( + path: 'archived', + builder: (context, state) => const ArchivedChatsView(), + ), + GoRoute( + path: 'start_new_chat', + builder: (context, state) => const StartNewChatView(), + ), + GoRoute( + path: 'camera_send_to', + builder: (context, state) { + final group = state.extra! as Group; + return CameraSendToView(group); + }, + ), + GoRoute( + path: 'media_viewer', + builder: (context, state) { + final group = state.extra! as Group; + return MediaViewerView(group); + }, + ), + GoRoute( + path: 'messages', + builder: (context, state) { + final group = state.extra! as Group; + return ChatMessagesView(group); + }, + ), + ], + ), + + GoRoute( + path: '/profile/contact/:contactId', + builder: (context, state) { + final contactId = state.pathParameters['contactId']!; + return ContactView(int.parse(contactId)); + }, + ), + + GoRoute( + path: '/profile/group/:groupId', + builder: (context, state) { + final groupId = state.pathParameters['groupId']!; + return GroupView(groupId); + }, + ), + + GoRoute( + path: '/group/create/select_member', + builder: (context, state) { + return const GroupCreateSelectMembersView(); + }, + routes: [ + GoRoute( + path: ':groupId', + builder: (context, state) { + final groupId = state.pathParameters['groupId']; + return GroupCreateSelectMembersView(groupId: groupId); + }, + ), + ], + ), + + GoRoute( + path: Routes.cameraQRScanner, + builder: (context, state) { + return const QrCodeScannerView(); + }, + ), + + // settings + GoRoute( + path: Routes.settings, + builder: (context, state) => const SettingsMainView(), + routes: [ + GoRoute( + path: 'profile', + builder: (context, state) => const ProfileView(), + routes: [ + GoRoute( + path: 'modify_avatar', + builder: (context, state) => const ModifyAvatarView(), + ) + ], + ), + GoRoute( + path: 'public_profile', + builder: (context, state) => const PublicProfileView(), + ), + GoRoute( + path: 'account', + builder: (context, state) => const AccountView(), + ), + GoRoute( + path: 'subscription', + builder: (context, state) => const SubscriptionView(), + ), + GoRoute( + path: 'backup', + builder: (context, state) => const BackupView(), + routes: [ + GoRoute( + path: 'server', + builder: (context, state) => const BackupServerView(), + ), + GoRoute( + path: 'recovery', + builder: (context, state) => const BackupRecoveryView(), + ), + GoRoute( + path: 'setup', + builder: (context, state) => SetupBackupView( + isPasswordChangeOnly: state.extra as bool? ?? false, + ), + ), + ], + ), + GoRoute( + path: 'appearance', + builder: (context, state) => const AppearanceView(), + ), + GoRoute( + path: 'chats', + builder: (context, state) => const ChatSettingsView(), + routes: [ + GoRoute( + path: 'reactions', + builder: (context, state) => const ChatReactionSelectionView(), + ), + ], + ), + GoRoute( + path: 'privacy', + builder: (context, state) => const PrivacyView(), + routes: [ + GoRoute( + path: 'block_users', + builder: (context, state) => const PrivacyViewBlockUsersView(), + ) + ], + ), + GoRoute( + path: 'notification', + builder: (context, state) => const NotificationView(), + ), + GoRoute( + path: 'storage_data', + builder: (context, state) => const DataAndStorageView(), + routes: [ + GoRoute( + path: 'import', + builder: (context, state) => const ImportMediaView(), + ), + GoRoute( + path: 'export', + builder: (context, state) => const ExportMediaView(), + ), + ], + ), + GoRoute( + path: 'help', + builder: (context, state) => const HelpView(), + routes: [ + GoRoute( + path: 'faq', + builder: (context, state) => const FaqView(), + ), + GoRoute( + path: 'contact_us', + builder: (context, state) => const ContactUsView(), + ), + GoRoute( + path: 'diagnostics', + builder: (context, state) => const DiagnosticsView(), + ), + GoRoute( + path: 'user_study', + builder: (context, state) => UserStudyWelcomeView( + wasOpenedAutomatic: state.extra as bool? ?? false, + ), + routes: [ + GoRoute( + path: 'questionnaire', + builder: (context, state) => + const UserStudyQuestionnaireView(), + ), + ], + ), + GoRoute( + path: 'credits', + builder: (context, state) => const CreditsView(), + ), + GoRoute( + path: 'changelog', + builder: (context, state) => ChangeLogView( + changeLog: state.extra as String?, + ), + ), + ], + ), + GoRoute( + path: 'developer', + builder: (context, state) => const DeveloperSettingsView(), + routes: [ + GoRoute( + path: 'retransmission_database', + builder: (context, state) => const RetransmissionDataView(), + ), + GoRoute( + path: 'automated_testing', + builder: (context, state) => const AutomatedTestingView(), + ), + ], + ), + GoRoute( + path: 'invite', + builder: (context, state) => const ShareWithFriendsView(), + ), + ], + ), + ], +); diff --git a/lib/src/services/intent/links.intent.dart b/lib/src/services/intent/links.intent.dart index 425d60a..03d70db 100644 --- a/lib/src/services/intent/links.intent.dart +++ b/lib/src/services/intent/links.intent.dart @@ -5,8 +5,11 @@ import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; +import 'package:flutter_sharing_intent/flutter_sharing_intent.dart'; import 'package:flutter_sharing_intent/model/sharing_file.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/services/api/mediafiles/upload.service.dart'; @@ -16,8 +19,6 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/camera/share_image_editor.view.dart'; import 'package:twonly/src/views/chats/add_new_user.view.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; -import 'package:twonly/src/views/contact/contact.view.dart'; -import 'package:twonly/src/views/public_profile.view.dart'; Future handleIntentUrl(BuildContext context, Uri uri) async { if (!uri.scheme.startsWith('http')) return false; @@ -32,14 +33,7 @@ Future handleIntentUrl(BuildContext context, Uri uri) async { if (!context.mounted) return false; if (username == gUser.username) { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PublicProfileView(); - }, - ), - ); + await context.push(Routes.settingsPublicProfile); return true; } @@ -91,14 +85,7 @@ Future handleIntentUrl(BuildContext context, Uri uri) async { ); } } else { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ContactView(contact.userId); - }, - ), - ); + await context.push(Routes.profileContact(contact.userId)); } } else { await showAlertDialog( @@ -149,6 +136,27 @@ Future handleIntentMediaFile( ); } +StreamSubscription> initIntentStreams( + BuildContext context, + void Function(Uri) onUrlCallBack, +) { + FlutterSharingIntent.instance.getInitialSharing().then((f) { + if (!context.mounted) return; + handleIntentSharedFile(context, f, onUrlCallBack); + }); + + return FlutterSharingIntent.instance.getMediaStream().listen( + (f) { + if (!context.mounted) return; + handleIntentSharedFile(context, f, onUrlCallBack); + }, + // ignore: inference_failure_on_untyped_parameter + onError: (err) { + Log.error('getIntentDataStream error: $err'); + }, + ); +} + Future handleIntentSharedFile( BuildContext context, List files, diff --git a/lib/src/themes/dark.dart b/lib/src/themes/dark.dart new file mode 100644 index 0000000..621e0b6 --- /dev/null +++ b/lib/src/themes/dark.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +final ThemeData darkTheme = ThemeData.dark().copyWith( + colorScheme: ColorScheme.fromSeed( + brightness: Brightness.dark, + seedColor: const Color(0xFF57CC99), + surface: const Color.fromARGB(255, 20, 18, 23), + surfaceContainer: const Color.fromARGB(255, 33, 30, 39), + ), + inputDecorationTheme: const InputDecorationTheme( + border: OutlineInputBorder(), + ), +); diff --git a/lib/src/themes/light.dart b/lib/src/themes/light.dart new file mode 100644 index 0000000..88bfc71 --- /dev/null +++ b/lib/src/themes/light.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +final ThemeData lightTheme = ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF57CC99), + ), + inputDecorationTheme: const InputDecorationTheme( + border: OutlineInputBorder(), + ), +); diff --git a/lib/src/views/camera/camera_qr_scanner.view.dart b/lib/src/views/camera/camera_qr_scanner.view.dart index 32703cc..6d6201b 100644 --- a/lib/src/views/camera/camera_qr_scanner.view.dart +++ b/lib/src/views/camera/camera_qr_scanner.view.dart @@ -4,13 +4,13 @@ import 'package:twonly/src/views/camera/camera_preview_components/camera_preview import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart'; import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart'; -class QrCodeScanner extends StatefulWidget { - const QrCodeScanner({super.key}); +class QrCodeScannerView extends StatefulWidget { + const QrCodeScannerView({super.key}); @override - State createState() => QrCodeScannerState(); + State createState() => QrCodeScannerViewState(); } -class QrCodeScannerState extends State { +class QrCodeScannerViewState extends State { final MainCameraController _mainCameraController = MainCameraController(); @override diff --git a/lib/src/views/chats/chat_list.view.dart b/lib/src/views/chats/chat_list.view.dart index e15f2d0..d49791b 100644 --- a/lib/src/views/chats/chat_list.view.dart +++ b/lib/src/views/chats/chat_list.view.dart @@ -4,28 +4,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/providers/connection.provider.dart'; import 'package:twonly/src/providers/purchases.provider.dart'; import 'package:twonly/src/services/subscription.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; -import 'package:twonly/src/views/chats/add_new_user.view.dart'; -import 'package:twonly/src/views/chats/archived_chats.view.dart'; import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp.dart'; import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart'; import 'package:twonly/src/views/chats/chat_list_components/group_list_item.dart'; -import 'package:twonly/src/views/chats/start_new_chat.view.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/notification_badge.dart'; -import 'package:twonly/src/views/public_profile.view.dart'; -import 'package:twonly/src/views/settings/help/changelog.view.dart'; -import 'package:twonly/src/views/settings/profile/profile.view.dart'; -import 'package:twonly/src/views/settings/settings_main.view.dart'; -import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; -import 'package:twonly/src/views/user_study/user_study_welcome.view.dart'; class ChatListView extends StatefulWidget { const ChatListView({super.key}); @@ -64,15 +57,9 @@ class _ChatListViewState extends State { WidgetsBinding.instance.addPostFrameCallback((_) async { if (gUser.subscriptionPlan == SubscriptionPlan.Tester.name && !gUser.askedForUserStudyPermission) { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const UserStudyWelcomeView( - wasOpenedAutomatic: true, - ); - }, - ), + await context.push( + Routes.settingsHelpUserStudy, + extra: true, ); } @@ -89,15 +76,9 @@ class _ChatListViewState extends State { // only show changelog to people who already have contacts // this prevents that this is shown directly after the user registered if (_groupsNotPinned.isNotEmpty) { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ChangeLogView( - changeLog: changeLog, - ); - }, - ), + await context.push( + Routes.settingsHelpChangelog, + extra: changeLog, ); } } @@ -120,14 +101,7 @@ class _ChatListViewState extends State { children: [ GestureDetector( onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ProfileView(); - }, - ), - ); + await context.push(Routes.settingsProfile); if (!mounted) return; setState(() {}); // gUser has updated }, @@ -141,16 +115,7 @@ class _ChatListViewState extends State { const Text('twonly '), if (plan != SubscriptionPlan.Free) GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const SubscriptionView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsSubscription), child: Container( decoration: BoxDecoration( color: context.color.primary, @@ -184,28 +149,15 @@ class _ChatListViewState extends State { child: IconButton( key: searchForOtherUsers, icon: const FaIcon(FontAwesomeIcons.userPlus, size: 18), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AddNewUserView(), - ), - ); - }, + onPressed: () => context.push(Routes.chatsAddNewUser), ), ); }, ), IconButton( onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsMainView(), - ), - ); - if (!mounted) return; - setState(() {}); // gUser may has changed... + await context.push(Routes.settings); + if (mounted) setState(() {}); // gUser may has changed... }, icon: const FaIcon(FontAwesomeIcons.gear, size: 19), ), @@ -234,14 +186,7 @@ class _ChatListViewState extends State { padding: const EdgeInsets.all(10), child: OutlinedButton.icon( icon: const Icon(Icons.person_add), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AddNewUserView(), - ), - ); - }, + onPressed: () => context.push(Routes.chatsAddNewUser), label: Text( context.lang.chatListViewSearchUserNameBtn, ), @@ -265,16 +210,7 @@ class _ChatListViewState extends State { textAlign: TextAlign.center, style: const TextStyle(fontSize: 13), ), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ArchivedChatsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.chatsArchived), ); } // Check if the index is for the pinned users @@ -320,16 +256,7 @@ class _ChatListViewState extends State { color: context.color.primary, child: InkWell( borderRadius: BorderRadius.circular(12), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PublicProfileView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsPublicProfile), child: SizedBox( width: 45, height: 45, @@ -345,16 +272,7 @@ class _ChatListViewState extends State { const SizedBox(height: 12), FloatingActionButton( backgroundColor: context.color.primary, - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const StartNewChatView(); - }, - ), - ); - }, + onPressed: () => context.push(Routes.chatsStartNewChat), child: FaIcon( FontAwesomeIcons.penToSquare, color: isDarkMode(context) ? Colors.black : Colors.white, diff --git a/lib/src/views/chats/chat_list_components/feedback_btn.dart b/lib/src/views/chats/chat_list_components/feedback_btn.dart index dd25ecf..412ab19 100644 --- a/lib/src/views/chats/chat_list_components/feedback_btn.dart +++ b/lib/src/views/chats/chat_list_components/feedback_btn.dart @@ -1,10 +1,10 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; -import 'package:twonly/src/views/settings/help/contact_us.view.dart'; class FeedbackIconButton extends StatefulWidget { const FeedbackIconButton({super.key}); @@ -37,14 +37,7 @@ class _FeedbackIconButtonState extends State { } return IconButton( - onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ContactUsView(), - ), - ); - }, + onPressed: () => context.push(Routes.settingsHelpContactUs), color: Colors.grey, tooltip: context.lang.feedbackTooltip, icon: const FaIcon(FontAwesomeIcons.commentDots, size: 19), diff --git a/lib/src/views/chats/chat_list_components/group_list_item.dart b/lib/src/views/chats/chat_list_components/group_list_item.dart index 763e389..b760097 100644 --- a/lib/src/views/chats/chat_list_components/group_list_item.dart +++ b/lib/src/views/chats/chat_list_components/group_list_item.dart @@ -1,24 +1,22 @@ import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:mutex/mutex.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/services/api/mediafiles/download.service.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/camera/camera_send_to.view.dart'; import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart'; -import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart'; -import 'package:twonly/src/views/chats/media_viewer.view.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/group_context_menu.component.dart'; -import 'package:twonly/src/views/contact/contact.view.dart'; -import 'package:twonly/src/views/groups/group.view.dart'; class GroupListItem extends StatefulWidget { const GroupListItem({ @@ -161,13 +159,9 @@ class _UserListItem extends State { Future onTap() async { if (_currentMessage == null && widget.group.totalMediaCounter == 0) { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return CameraSendToView(widget.group); - }, - ), + await context.push( + Routes.chatsCameraSendTo, + extra: widget.group, ); return; } @@ -185,26 +179,18 @@ class _UserListItem extends State { } if (mediaFile.downloadState! == DownloadState.ready) { if (!mounted) return; - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return MediaViewerView(widget.group); - }, - ), + await context.push( + Routes.chatsMediaViewer, + extra: widget.group, ); return; } } } if (!mounted) return; - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ChatMessagesView(widget.group); - }, - ), + await context.push( + Routes.chatsMessages, + extra: widget.group, ); } @@ -250,42 +236,27 @@ class _UserListItem extends State { ), leading: GestureDetector( onTap: () async { - Widget pushWidget = GroupView(widget.group); - if (widget.group.isDirectChat) { final contacts = await twonlyDB.groupsDao .getGroupContact(widget.group.groupId); - pushWidget = ContactView(contacts.first.userId); + if (!context.mounted) return; + await context.push(Routes.profileContact(contacts.first.userId)); + return; + } else { + await context.push(Routes.profileGroup(widget.group.groupId)); } - if (!context.mounted) return; - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return pushWidget; - }, - ), - ); }, child: AvatarIcon(group: widget.group), ), trailing: (widget.group.leftGroup) ? null : IconButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - if (_hasNonOpenedMediaFile) { - return ChatMessagesView(widget.group); - } else { - return CameraSendToView(widget.group); - } - }, - ), - ); - }, + onPressed: () => context.push( + _hasNonOpenedMediaFile + ? Routes.chatsMessages + : Routes.chatsCameraSendTo, + extra: widget.group, + ), icon: FaIcon( _hasNonOpenedMediaFile ? FontAwesomeIcons.solidComments diff --git a/lib/src/views/chats/chat_messages.view.dart b/lib/src/views/chats/chat_messages.view.dart index 273aca8..771ac88 100644 --- a/lib/src/views/chats/chat_messages.view.dart +++ b/lib/src/views/chats/chat_messages.view.dart @@ -1,11 +1,12 @@ import 'dart:async'; import 'dart:collection'; - import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:mutex/mutex.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; @@ -21,8 +22,6 @@ import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/blink.component.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/verified_shield.dart'; -import 'package:twonly/src/views/contact/contact.view.dart'; -import 'package:twonly/src/views/groups/group.view.dart'; Color getMessageColor(Message message) { return (message.senderId == null) @@ -291,23 +290,10 @@ class _ChatMessagesViewState extends State { await twonlyDB.groupsDao.getAllGroupMembers(group.groupId); if (!context.mounted) return; if (member.isEmpty) return; - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ContactView(member.first.contactId); - }, - ), - ); + await context + .push(Routes.profileContact(member.first.contactId)); } else { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return GroupView(group); - }, - ), - ); + await context.push(Routes.profileGroup(group.groupId)); } }, child: Row( diff --git a/lib/src/views/chats/chat_messages_components/chat_list_entry.dart b/lib/src/views/chats/chat_messages_components/chat_list_entry.dart index 44e51e3..7b05a6d 100644 --- a/lib/src/views/chats/chat_messages_components/chat_list_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_list_entry.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart' hide MessageActions; @@ -17,7 +19,6 @@ import 'package:twonly/src/views/chats/chat_messages_components/message_actions. import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart'; import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; -import 'package:twonly/src/views/contact/contact.view.dart'; class ChatListEntry extends StatefulWidget { const ChatListEntry({ @@ -204,15 +205,9 @@ class _ChatListEntryState extends State { hideContactAvatar ? const SizedBox(width: 24) : GestureDetector( - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - ContactView(widget.message.senderId!), - ), - ); - }, + onTap: () => context.push( + Routes.profileContact(widget.message.senderId!), + ), child: AvatarIcon( contactId: widget.message.senderId, fontSize: 12, diff --git a/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart b/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart index a2cec19..c8400b2 100644 --- a/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart +++ b/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart @@ -2,13 +2,14 @@ import 'dart:collection'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/animate_icon.dart'; -import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; enum MessageSendState { received, @@ -163,16 +164,7 @@ class _MessageSendStateIconState extends State { style: const TextStyle(fontSize: 9), ); - onTap = () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const SubscriptionView(); - }, - ), - ); - }; + onTap = () => context.push(Routes.settingsSubscription); } if (mediaFile.uploadState == UploadState.preprocessing || mediaFile.uploadState == UploadState.initialized) { diff --git a/lib/src/views/chats/start_new_chat.view.dart b/lib/src/views/chats/start_new_chat.view.dart index 5689112..fad23f3 100644 --- a/lib/src/views/chats/start_new_chat.view.dart +++ b/lib/src/views/chats/start_new_chat.view.dart @@ -2,17 +2,17 @@ import 'dart:async'; import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/chats/add_new_user.view.dart'; import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/group_context_menu.component.dart'; import 'package:twonly/src/views/components/user_context_menu.component.dart'; -import 'package:twonly/src/views/groups/group_create_select_members.view.dart'; class StartNewChatView extends StatefulWidget { const StartNewChatView({super.key}); @@ -165,15 +165,8 @@ class _StartNewChatView extends State { size: 13, ), ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const GroupCreateSelectMembersView(), - ), - ); - }, + onTap: () => context + .push(Routes.groupCreateSelectMember(null)), ); } if (i == 1) { @@ -185,14 +178,7 @@ class _StartNewChatView extends State { size: 13, ), ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AddNewUserView(), - ), - ); - }, + onTap: () => context.push(Routes.chatsAddNewUser), ); } if (i == 2) { diff --git a/lib/src/views/components/group_context_menu.component.dart b/lib/src/views/components/group_context_menu.component.dart index 438a77d..ce856f4 100644 --- a/lib/src/views/components/group_context_menu.component.dart +++ b/lib/src/views/components/group_context_menu.component.dart @@ -1,10 +1,11 @@ import 'package:drift/drift.dart' hide Column; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/components/context_menu.component.dart'; @@ -45,16 +46,10 @@ class GroupContextMenu extends StatelessWidget { ), ContextMenuItem( title: context.lang.contextMenuOpenChat, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ChatMessagesView(group); - }, - ), - ); - }, + onTap: () => context.push( + Routes.chatsMessages, + extra: group, + ), icon: FontAwesomeIcons.comments, ), if (!group.archived) diff --git a/lib/src/views/components/max_flame_list_title.dart b/lib/src/views/components/max_flame_list_title.dart index 5029bbb..adddb1f 100644 --- a/lib/src/views/components/max_flame_list_title.dart +++ b/lib/src/views/components/max_flame_list_title.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:clock/clock.dart'; import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/subscription.service.dart'; @@ -10,7 +12,6 @@ import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/animate_icon.dart'; import 'package:twonly/src/views/components/better_list_title.dart'; -import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; class MaxFlameListTitle extends StatefulWidget { const MaxFlameListTitle({ @@ -46,14 +47,7 @@ class _MaxFlameListTitleState extends State { Future _restoreFlames() async { if (!isPayingUser(getCurrentPlan())) { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const SubscriptionView(); - }, - ), - ); + await context.push(Routes.settingsSubscription); return; } Log.info( diff --git a/lib/src/views/components/user_context_menu.component.dart b/lib/src/views/components/user_context_menu.component.dart index 7151848..6471bd9 100644 --- a/lib/src/views/components/user_context_menu.component.dart +++ b/lib/src/views/components/user_context_menu.component.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/context_menu.component.dart'; -import 'package:twonly/src/views/contact/contact.view.dart'; class UserContextMenu extends StatelessWidget { const UserContextMenu({ @@ -20,16 +21,7 @@ class UserContextMenu extends StatelessWidget { items: [ ContextMenuItem( title: context.lang.contextMenuUserProfile, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return ContactView(contact.userId); - }, - ), - ); - }, + onTap: () => context.push(Routes.profileContact(contact.userId)), icon: FontAwesomeIcons.user, ), ], diff --git a/lib/src/views/components/verified_shield.dart b/lib/src/views/components/verified_shield.dart index 94c4389..73b6ab2 100644 --- a/lib/src/views/components/verified_shield.dart +++ b/lib/src/views/components/verified_shield.dart @@ -1,9 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/twonly.db.dart'; -import 'package:twonly/src/views/public_profile.view.dart'; class VerifiedShield extends StatefulWidget { const VerifiedShield({ @@ -58,16 +59,7 @@ class _VerifiedShieldState extends State { return GestureDetector( onTap: (contact == null) ? null - : () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PublicProfileView(); - }, - ), - ); - }, + : () => context.push(Routes.settingsPublicProfile), child: Tooltip( message: isVerified ? 'You verified this contact' diff --git a/lib/src/views/groups/group.view.dart b/lib/src/views/groups/group.view.dart index 918068b..2279dc0 100644 --- a/lib/src/views/groups/group.view.dart +++ b/lib/src/views/groups/group.view.dart @@ -19,16 +19,16 @@ import 'package:twonly/src/views/groups/group_member.context.dart'; import 'package:twonly/src/views/settings/profile/profile.view.dart'; class GroupView extends StatefulWidget { - const GroupView(this.group, {super.key}); + const GroupView(this.groupId, {super.key}); - final Group group; + final String groupId; @override State createState() => _GroupViewState(); } class _GroupViewState extends State { - late Group group; + Group? _group; List<(Contact, GroupMember)> members = []; @@ -37,7 +37,6 @@ class _GroupViewState extends State { @override void initState() { - group = widget.group; initAsync(); super.initState(); } @@ -50,16 +49,15 @@ class _GroupViewState extends State { } Future initAsync() async { - final groupStream = twonlyDB.groupsDao.watchGroup(widget.group.groupId); + final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId); groupSub = groupStream.listen((update) { if (update != null) { setState(() { - group = update; + _group = update; }); } }); - final membersStream = - twonlyDB.groupsDao.watchGroupMembers(widget.group.groupId); + final membersStream = twonlyDB.groupsDao.watchGroupMembers(widget.groupId); membersSub = membersStream.listen((update) { setState(() { members = update; @@ -71,13 +69,13 @@ class _GroupViewState extends State { } Future _updateGroupName() async { - final newGroupName = await showGroupNameChangeDialog(context, group); + final newGroupName = await showGroupNameChangeDialog(context, _group!); if (context.mounted && newGroupName != null && newGroupName != '' && - newGroupName != group.groupName) { - if (!await updateGroupName(group, newGroupName)) { + newGroupName != _group!.groupName) { + if (!await updateGroupName(_group!, newGroupName)) { if (mounted) { showNetworkIssue(context); } @@ -89,11 +87,13 @@ class _GroupViewState extends State { final selectedUserIds = await Navigator.push( context, MaterialPageRoute( - builder: (context) => GroupCreateSelectMembersView(group: group), + builder: (context) => GroupCreateSelectMembersView( + groupId: _group?.groupId, + ), ), ) as List?; if (selectedUserIds == null) return; - if (!await addNewGroupMembers(group, selectedUserIds)) { + if (!await addNewGroupMembers(_group!, selectedUserIds)) { if (mounted) { showNetworkIssue(context); } @@ -115,7 +115,7 @@ class _GroupViewState extends State { if (members.isNotEmpty) { // In case there are other members, check that there is at least one other admin before I leave the group. - if (group.isGroupAdmin) { + if (_group!.isGroupAdmin) { if (!members.any((m) => m.$2.memberState == MemberState.admin)) { if (!mounted) return; await showAlertDialog( @@ -131,16 +131,17 @@ class _GroupViewState extends State { late bool success; - if (group.isGroupAdmin) { + if (_group!.isGroupAdmin) { // Current user is a admin, to the state can be updated by the user him self. - final keyPair = IdentityKeyPair.fromSerialized(group.myGroupPrivateKey!); + final keyPair = + IdentityKeyPair.fromSerialized(_group!.myGroupPrivateKey!); success = await removeMemberFromGroup( - group, + _group!, keyPair.getPublicKey().serialize(), gUser.userId, ); } else { - success = await leaveAsNonAdminFromGroup(group); + success = await leaveAsNonAdminFromGroup(_group!); } if (!success) { @@ -153,6 +154,9 @@ class _GroupViewState extends State { @override Widget build(BuildContext context) { + if (_group == null) { + return Container(); + } return Scaffold( appBar: AppBar( title: const Text(''), @@ -162,7 +166,7 @@ class _GroupViewState extends State { Padding( padding: const EdgeInsets.all(10), child: AvatarIcon( - group: group, + group: _group, fontSize: 30, ), ), @@ -171,24 +175,24 @@ class _GroupViewState extends State { children: [ Padding( padding: const EdgeInsets.only(right: 10), - child: VerifiedShield(key: Key(group.groupId), group: group), + child: VerifiedShield(key: Key(_group!.groupId), group: _group), ), Text( - substringBy(group.groupName, 25), + substringBy(_group!.groupName, 25), style: const TextStyle(fontSize: 20), ), ], ), const SizedBox(height: 50), - if (group.isGroupAdmin && !group.leftGroup) + if (_group!.isGroupAdmin && !_group!.leftGroup) BetterListTile( icon: FontAwesomeIcons.pencil, text: context.lang.groupNameInput, onTap: _updateGroupName, ), SelectChatDeletionTimeListTitle( - groupId: widget.group.groupId, - disabled: !group.isGroupAdmin, + groupId: widget.groupId, + disabled: !_group!.isGroupAdmin, ), const Divider(), ListTile( @@ -203,7 +207,7 @@ class _GroupViewState extends State { ), ), ), - if (group.isGroupAdmin && !group.leftGroup) + if (_group!.isGroupAdmin && !_group!.leftGroup) BetterListTile( icon: FontAwesomeIcons.plus, text: context.lang.addMember, @@ -216,7 +220,7 @@ class _GroupViewState extends State { fontSize: 16, ), text: context.lang.you, - trailing: (group.isGroupAdmin) ? Text(context.lang.admin) : null, + trailing: (_group!.isGroupAdmin) ? Text(context.lang.admin) : null, onTap: () async { await Navigator.push( context, @@ -229,7 +233,7 @@ class _GroupViewState extends State { ...members.map((member) { return GroupMemberContextMenu( key: ValueKey(member.$1.userId), - group: group, + group: _group!, contact: member.$1, member: member.$2, child: BetterListTile( @@ -256,7 +260,7 @@ class _GroupViewState extends State { const SizedBox(height: 10), const Divider(), const SizedBox(height: 10), - if (!group.leftGroup) + if (!_group!.leftGroup) BetterListTile( icon: FontAwesomeIcons.rightFromBracket, color: Colors.red, diff --git a/lib/src/views/groups/group_create_select_members.view.dart b/lib/src/views/groups/group_create_select_members.view.dart index 4306c86..c4dfe71 100644 --- a/lib/src/views/groups/group_create_select_members.view.dart +++ b/lib/src/views/groups/group_create_select_members.view.dart @@ -12,8 +12,8 @@ import 'package:twonly/src/views/components/user_context_menu.component.dart'; import 'package:twonly/src/views/groups/group_create_select_group_name.view.dart'; class GroupCreateSelectMembersView extends StatefulWidget { - const GroupCreateSelectMembersView({this.group, super.key}); - final Group? group; + const GroupCreateSelectMembersView({this.groupId, super.key}); + final String? groupId; @override State createState() => _StartNewChatView(); } @@ -46,9 +46,8 @@ class _StartNewChatView extends State { } Future initAsync() async { - if (widget.group != null) { - final members = - await twonlyDB.groupsDao.getGroupContact(widget.group!.groupId); + if (widget.groupId != null) { + final members = await twonlyDB.groupsDao.getGroupContact(widget.groupId!); for (final member in members) { alreadyInGroup.add(member.userId); } @@ -101,7 +100,7 @@ class _StartNewChatView extends State { } Future submitChanges() async { - if (widget.group != null) { + if (widget.groupId != null) { Navigator.pop(context, selectedUsers.toList()); return; } @@ -125,7 +124,7 @@ class _StartNewChatView extends State { child: Scaffold( appBar: AppBar( title: Text( - widget.group == null + widget.groupId == null ? context.lang.selectMembers : context.lang.addMember, ), @@ -133,7 +132,9 @@ class _StartNewChatView extends State { floatingActionButton: FilledButton.icon( onPressed: selectedUsers.isEmpty ? null : submitChanges, label: Text( - widget.group == null ? context.lang.next : context.lang.updateGroup, + widget.groupId == null + ? context.lang.next + : context.lang.updateGroup, ), icon: const FaIcon(FontAwesomeIcons.penToSquare), ), diff --git a/lib/src/views/groups/group_member.context.dart b/lib/src/views/groups/group_member.context.dart index 687401f..543e0c8 100644 --- a/lib/src/views/groups/group_member.context.dart +++ b/lib/src/views/groups/group_member.context.dart @@ -1,7 +1,9 @@ import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/tables/groups.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; @@ -9,7 +11,6 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/services/group.services.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/components/context_menu.component.dart'; import 'package:twonly/src/views/groups/group.view.dart'; @@ -128,12 +129,7 @@ class GroupMemberContextMenu extends StatelessWidget { return; } if (!context.mounted) return; - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ChatMessagesView(directChat), - ), - ); + await context.push(Routes.chatsMessages, extra: directChat); }, icon: FontAwesomeIcons.message, ), diff --git a/lib/src/views/home.view.dart b/lib/src/views/home.view.dart index b4e5069..09d40e9 100644 --- a/lib/src/views/home.view.dart +++ b/lib/src/views/home.view.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:flutter_sharing_intent/flutter_sharing_intent.dart'; import 'package:flutter_sharing_intent/model/sharing_file.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/globals.dart'; @@ -126,31 +125,10 @@ class HomeViewState extends State { } }); - _intentStreamSub = FlutterSharingIntent.instance.getMediaStream().listen( - (f) { - if (mounted) { - handleIntentSharedFile( - context, - f, - _mainCameraController.setSharedLinkForPreview, - ); - } - }, - // ignore: inference_failure_on_untyped_parameter - onError: (err) { - Log.error('getIntentDataStream error: $err'); - }, + _intentStreamSub = initIntentStreams( + context, + _mainCameraController.setSharedLinkForPreview, ); - - FlutterSharingIntent.instance.getInitialSharing().then((f) { - if (mounted) { - handleIntentSharedFile( - context, - f, - _mainCameraController.setSharedLinkForPreview, - ); - } - }); } @override diff --git a/lib/src/views/onboarding/recover.view.dart b/lib/src/views/onboarding/recover.view.dart index e9b061c..5b9fc6b 100644 --- a/lib/src/views/onboarding/recover.view.dart +++ b/lib/src/views/onboarding/recover.view.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:restart_app/restart_app.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/services/twonly_safe/restore.twonly_safe.dart'; import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; -import 'package:twonly/src/views/settings/backup/twonly_safe_server.view.dart'; class BackupRecoveryView extends StatefulWidget { const BackupRecoveryView({super.key}); @@ -135,14 +136,8 @@ class _BackupRecoveryViewState extends State { Center( child: OutlinedButton( onPressed: () async { - backupServer = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const TwonlySafeServerView(); - }, - ), - ); + backupServer = + await context.push(Routes.settingsBackupServer); setState(() {}); }, child: Text(context.lang.backupExpertSettings), diff --git a/lib/src/views/onboarding/register.view.dart b/lib/src/views/onboarding/register.view.dart index 32a932b..7da4a58 100644 --- a/lib/src/views/onboarding/register.view.dart +++ b/lib/src/views/onboarding/register.view.dart @@ -2,11 +2,12 @@ import 'dart:async'; import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/constants/secure_storage_keys.dart'; import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; @@ -17,7 +18,6 @@ import 'package:twonly/src/utils/pow.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; import 'package:twonly/src/views/groups/group.view.dart'; -import 'package:twonly/src/views/onboarding/recover.view.dart'; class RegisterView extends StatefulWidget { const RegisterView({ @@ -302,16 +302,8 @@ class _RegisterViewState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ OutlinedButton.icon( - onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const BackupRecoveryView(); - }, - ), - ); - }, + onPressed: () => + context.push(Routes.settingsBackupRecovery), label: Text(context.lang.twonlySafeRecoverBtn), ), ], diff --git a/lib/src/views/public_profile.view.dart b/lib/src/views/public_profile.view.dart index 3fc405e..e3a937b 100644 --- a/lib/src/views/public_profile.view.dart +++ b/lib/src/views/public_profile.view.dart @@ -1,15 +1,15 @@ import 'dart:convert'; import 'dart:typed_data'; - import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/avatars.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/qr.dart'; -import 'package:twonly/src/views/camera/camera_qr_scanner.view.dart'; import 'package:twonly/src/views/components/better_list_title.dart'; class PublicProfileView extends StatefulWidget { @@ -96,14 +96,7 @@ class _PublicProfileViewState extends State { BetterListTile( leading: const FaIcon(FontAwesomeIcons.qrcode), text: context.lang.scanOtherProfile, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const QrCodeScanner(), - ), - ); - }, + onTap: () => context.push(Routes.cameraQRScanner), ), BetterListTile( leading: const FaIcon( diff --git a/lib/src/views/settings/backup/backup.view.dart b/lib/src/views/settings/backup/backup.view.dart index 1720925..f2fc158 100644 --- a/lib/src/views/settings/backup/backup.view.dart +++ b/lib/src/views/settings/backup/backup.view.dart @@ -1,11 +1,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/settings/backup/twonly_safe_backup.view.dart'; void Function() gUpdateBackupView = () {}; @@ -60,16 +61,7 @@ class _BackupViewState extends State { } Future changeTwonlySafePassword() async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const TwonlyIdentityBackupView( - isPasswordChangeOnly: true, - ); - }, - ), - ); + await context.push(Routes.settingsBackupSetup, extra: true); setState(() { // gUser was updated }); diff --git a/lib/src/views/settings/backup/twonly_safe_server.view.dart b/lib/src/views/settings/backup/backup_server.view.dart similarity index 95% rename from lib/src/views/settings/backup/twonly_safe_server.view.dart rename to lib/src/views/settings/backup/backup_server.view.dart index c0e4bfd..871ed6e 100644 --- a/lib/src/views/settings/backup/twonly_safe_server.view.dart +++ b/lib/src/views/settings/backup/backup_server.view.dart @@ -11,14 +11,14 @@ import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; -class TwonlySafeServerView extends StatefulWidget { - const TwonlySafeServerView({super.key}); +class BackupServerView extends StatefulWidget { + const BackupServerView({super.key}); @override - State createState() => _TwonlySafeServerViewState(); + State createState() => _BackupServerViewState(); } -class _TwonlySafeServerViewState extends State { +class _BackupServerViewState extends State { final TextEditingController _urlController = TextEditingController(); final TextEditingController _usernameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); diff --git a/lib/src/views/settings/backup/twonly_safe_backup.view.dart b/lib/src/views/settings/backup/setup_backup.view.dart similarity index 91% rename from lib/src/views/settings/backup/twonly_safe_backup.view.dart rename to lib/src/views/settings/backup/setup_backup.view.dart index 3162fde..eb9d242 100644 --- a/lib/src/views/settings/backup/twonly_safe_backup.view.dart +++ b/lib/src/views/settings/backup/setup_backup.view.dart @@ -2,13 +2,14 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; -import 'package:twonly/src/views/settings/backup/twonly_safe_server.view.dart'; -class TwonlyIdentityBackupView extends StatefulWidget { - const TwonlyIdentityBackupView({ +class SetupBackupView extends StatefulWidget { + const SetupBackupView({ this.isPasswordChangeOnly = false, this.callBack, super.key, @@ -20,11 +21,10 @@ class TwonlyIdentityBackupView extends StatefulWidget { final bool isPasswordChangeOnly; @override - State createState() => - _TwonlyIdentityBackupViewState(); + State createState() => _SetupBackupViewState(); } -class _TwonlyIdentityBackupViewState extends State { +class _SetupBackupViewState extends State { bool obscureText = true; bool isLoading = false; final TextEditingController passwordCtrl = TextEditingController(); @@ -179,16 +179,7 @@ class _TwonlyIdentityBackupViewState extends State { const SizedBox(height: 10), Center( child: OutlinedButton( - onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const TwonlySafeServerView(); - }, - ), - ); - }, + onPressed: () => context.push(Routes.settingsBackupServer), child: Text(context.lang.backupExpertSettings), ), ), diff --git a/lib/src/views/settings/chat/chat_settings.view.dart b/lib/src/views/settings/chat/chat_settings.view.dart index 1a20c27..db3667b 100644 --- a/lib/src/views/settings/chat/chat_settings.view.dart +++ b/lib/src/views/settings/chat/chat_settings.view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/settings/chat/chat_reactions.view.dart'; class ChatSettingsView extends StatefulWidget { const ChatSettingsView({super.key}); @@ -25,16 +26,7 @@ class _ChatSettingsViewState extends State { children: [ ListTile( title: Text(context.lang.settingsPreSelectedReactions), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ChatReactionSelectionView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsChatsReactions), ), ], ), diff --git a/lib/src/views/settings/data_and_storage.view.dart b/lib/src/views/settings/data_and_storage.view.dart index 5ff3161..1088a27 100644 --- a/lib/src/views/settings/data_and_storage.view.dart +++ b/lib/src/views/settings/data_and_storage.view.dart @@ -1,14 +1,13 @@ import 'dart:async'; import 'dart:io'; - import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/services/api/mediafiles/download.service.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; -import 'package:twonly/src/views/settings/data_and_storage/export_media.view.dart'; -import 'package:twonly/src/views/settings/data_and_storage/import_media.view.dart'; class DataAndStorageView extends StatefulWidget { const DataAndStorageView({super.key}); @@ -91,32 +90,14 @@ class _DataAndStorageViewState extends State { title: Text( context.lang.exportMemories, ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (_) { - return const ExportMediaView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsStorageExport), ), if (Platform.isAndroid) ListTile( title: Text( context.lang.importMemories, ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (_) { - return const ImportMediaView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsStorageImport), ), const Divider(), ListTile( diff --git a/lib/src/views/settings/developer/developer.view.dart b/lib/src/views/settings/developer/developer.view.dart index 1dcc1d8..37413cf 100644 --- a/lib/src/views/settings/developer/developer.view.dart +++ b/lib/src/views/settings/developer/developer.view.dart @@ -1,12 +1,12 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:restart_app/restart_app.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; -import 'package:twonly/src/views/settings/developer/automated_testing.view.dart'; -import 'package:twonly/src/views/settings/developer/retransmission_data.view.dart'; class DeveloperSettingsView extends StatefulWidget { const DeveloperSettingsView({super.key}); @@ -55,16 +55,8 @@ class _DeveloperSettingsViewState extends State { ), ListTile( title: const Text('Show Retransmission Database'), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const RetransmissionDataView(); - }, - ), - ); - }, + onTap: () => + context.push(Routes.settingsDeveloperRetransmissionDatabase), ), ListTile( title: const Text('Delete all (!) app data'), @@ -97,16 +89,8 @@ class _DeveloperSettingsViewState extends State { if (!kReleaseMode) ListTile( title: const Text('Automated Testing'), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const AutomatedTestingView(); - }, - ), - ); - }, + onTap: () => + context.push(Routes.settingsDeveloperAutomatedTesting), ), ], ), diff --git a/lib/src/views/settings/help/help.view.dart b/lib/src/views/settings/help/help.view.dart index 89b3dd2..00d6c97 100644 --- a/lib/src/views/settings/help/help.view.dart +++ b/lib/src/views/settings/help/help.view.dart @@ -1,17 +1,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/alert_dialog.dart'; -import 'package:twonly/src/views/settings/help/changelog.view.dart'; -import 'package:twonly/src/views/settings/help/contact_us.view.dart'; -import 'package:twonly/src/views/settings/help/credits.view.dart'; -import 'package:twonly/src/views/settings/help/diagnostics.view.dart'; -import 'package:twonly/src/views/settings/help/faq.view.dart'; -import 'package:twonly/src/views/user_study/user_study_welcome.view.dart'; import 'package:url_launcher/url_launcher.dart'; class HelpView extends StatefulWidget { @@ -40,50 +36,12 @@ class _HelpViewState extends State { children: [ ListTile( title: Text(context.lang.settingsHelpFAQ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const FaqView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelpFaq), ), ListTile( title: Text(context.lang.settingsHelpContactUs), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ContactUsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelpContactUs), ), - // ListTile( - // title: Text(context.lang.settingsResetTutorials), - // subtitle: Text( - // context.lang.settingsResetTutorialsDesc, - // style: const TextStyle(fontSize: 12), - // ), - // onTap: () async { - // await updateUserdata((user) { - // user.tutorialDisplayed = []; - // return user; - // }); - // if (!context.mounted) return; - // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar( - // content: Text(context.lang.settingsResetTutorialsSuccess), - // duration: const Duration(seconds: 3), - // ), - // ); - // }, - // ), const Divider(), ListTile( title: Text(context.lang.allowErrorTracking), @@ -99,32 +57,13 @@ class _HelpViewState extends State { ), ListTile( title: Text(context.lang.settingsHelpDiagnostics), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const DiagnosticsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelpDiagnostics), ), const Divider(), if (gUser.userStudyParticipantsToken == null || kDebugMode) ListTile( title: const Text('Teilnahme an Nutzerstudie'), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const UserStudyWelcomeView(); - }, - ), - ); - setState(() {}); // gUser has changed - }, + onTap: () => context.push(Routes.settingsHelpUserStudy), ), FutureBuilder( future: PackageInfo.fromPlatform(), @@ -141,59 +80,34 @@ class _HelpViewState extends State { ), ListTile( title: Text(context.lang.settingsHelpLicenses), - onTap: () { - showLicensePage(context: context); - }, + onTap: () => showLicensePage(context: context), ), ListTile( title: Text(context.lang.settingsHelpCredits), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const CreditsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelpCredits), ), ListTile( title: const Text('Changelog'), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ChangeLogView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelpChangelog), ), ListTile( title: const Text('Open Source'), - onTap: () async { - await launchUrl( - Uri.parse('https://github.com/twonlyapp/twonly-app'), - ); - }, + onTap: () => launchUrl( + Uri.parse('https://github.com/twonlyapp/twonly-app'), + ), trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ), ListTile( title: Text(context.lang.settingsHelpImprint), - onTap: () async { - await launchUrl(Uri.parse('https://twonly.eu/de/legal/')); - }, + onTap: () => launchUrl(Uri.parse('https://twonly.eu/de/legal/')), trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ), ListTile( title: Text(context.lang.settingsHelpTerms), - onTap: () async { - await launchUrl(Uri.parse('https://twonly.eu/de/legal/agb.html')); - }, + onTap: () => + launchUrl(Uri.parse('https://twonly.eu/de/legal/agb.html')), trailing: const FaIcon(FontAwesomeIcons.arrowUpRightFromSquare, size: 15), ), diff --git a/lib/src/views/settings/privacy.view.dart b/lib/src/views/settings/privacy.view.dart index c3c50c3..5e006c4 100644 --- a/lib/src/views/settings/privacy.view.dart +++ b/lib/src/views/settings/privacy.view.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/misc.dart'; -import 'package:twonly/src/views/settings/privacy_view_block.users.dart'; class PrivacyView extends StatefulWidget { const PrivacyView({super.key}); @@ -40,16 +41,7 @@ class _PrivacyViewState extends State { } }, ), - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PrivacyViewBlockUsers(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsPrivacyBlockUsers), ), ], ), diff --git a/lib/src/views/settings/privacy_view_block.users.dart b/lib/src/views/settings/privacy_view_block.view.dart similarity index 95% rename from lib/src/views/settings/privacy_view_block.users.dart rename to lib/src/views/settings/privacy_view_block.view.dart index e0038c3..dda1aa1 100644 --- a/lib/src/views/settings/privacy_view_block.users.dart +++ b/lib/src/views/settings/privacy_view_block.view.dart @@ -7,14 +7,14 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/user_context_menu.component.dart'; -class PrivacyViewBlockUsers extends StatefulWidget { - const PrivacyViewBlockUsers({super.key}); +class PrivacyViewBlockUsersView extends StatefulWidget { + const PrivacyViewBlockUsersView({super.key}); @override - State createState() => _PrivacyViewBlockUsers(); + State createState() => _PrivacyViewBlockUsers(); } -class _PrivacyViewBlockUsers extends State { +class _PrivacyViewBlockUsers extends State { late Stream> allUsers; List filteredUsers = []; String filter = ''; diff --git a/lib/src/views/settings/profile/modify_avatar.view.dart b/lib/src/views/settings/profile/modify_avatar.view.dart index fd95b5c..1ba50c0 100644 --- a/lib/src/views/settings/profile/modify_avatar.view.dart +++ b/lib/src/views/settings/profile/modify_avatar.view.dart @@ -6,14 +6,14 @@ import 'package:twonly/globals.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; -class ModifyAvatar extends StatefulWidget { - const ModifyAvatar({super.key}); +class ModifyAvatarView extends StatefulWidget { + const ModifyAvatarView({super.key}); @override - State createState() => _ModifyAvatarState(); + State createState() => _ModifyAvatarViewState(); } -class _ModifyAvatarState extends State { +class _ModifyAvatarViewState extends State { final AvatarMakerController _avatarMakerController = PersistentAvatarMakerController(customizedPropertyCategories: []); diff --git a/lib/src/views/settings/profile/profile.view.dart b/lib/src/views/settings/profile/profile.view.dart index 61de089..e7b0b56 100644 --- a/lib/src/views/settings/profile/profile.view.dart +++ b/lib/src/views/settings/profile/profile.view.dart @@ -3,7 +3,9 @@ import 'package:avatar_maker/avatar_maker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart'; import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart'; @@ -11,7 +13,6 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/components/better_list_title.dart'; import 'package:twonly/src/views/groups/group.view.dart'; -import 'package:twonly/src/views/settings/profile/modify_avatar.view.dart'; class ProfileView extends StatefulWidget { const ProfileView({super.key}); @@ -110,12 +111,7 @@ class _ProfileViewState extends State { icon: const Icon(Icons.edit), label: Text(context.lang.settingsProfileCustomizeAvatar), onPressed: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ModifyAvatar(), - ), - ); + await context.push(Routes.settingsProfileModifyAvatar); await _avatarMakerController.performRestore(); setState(() {}); }, diff --git a/lib/src/views/settings/settings_main.view.dart b/lib/src/views/settings/settings_main.view.dart index 7a25ede..a8b353f 100644 --- a/lib/src/views/settings/settings_main.view.dart +++ b/lib/src/views/settings/settings_main.view.dart @@ -1,23 +1,12 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/better_list_title.dart'; -import 'package:twonly/src/views/public_profile.view.dart'; -import 'package:twonly/src/views/settings/account.view.dart'; -import 'package:twonly/src/views/settings/appearance.view.dart'; -import 'package:twonly/src/views/settings/backup/backup.view.dart'; -import 'package:twonly/src/views/settings/chat/chat_settings.view.dart'; -import 'package:twonly/src/views/settings/data_and_storage.view.dart'; -import 'package:twonly/src/views/settings/developer/developer.view.dart'; -import 'package:twonly/src/views/settings/help/help.view.dart'; -import 'package:twonly/src/views/settings/notification.view.dart'; -import 'package:twonly/src/views/settings/privacy.view.dart'; -import 'package:twonly/src/views/settings/profile/profile.view.dart'; -import 'package:twonly/src/views/settings/share_with_friends.view.dart'; -import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; class SettingsMainView extends StatefulWidget { const SettingsMainView({super.key}); @@ -42,14 +31,7 @@ class _SettingsMainViewState extends State { Expanded( child: GestureDetector( onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ProfileView(); - }, - ), - ); + await context.push(Routes.settingsProfile); setState(() {}); }, child: ColoredBox( @@ -86,16 +68,7 @@ class _SettingsMainViewState extends State { Align( alignment: Alignment.centerRight, child: IconButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PublicProfileView(); - }, - ), - ); - }, + onPressed: () => context.push(Routes.settingsPublicProfile), icon: const FaIcon(FontAwesomeIcons.qrcode), ), ), @@ -105,160 +78,61 @@ class _SettingsMainViewState extends State { BetterListTile( icon: FontAwesomeIcons.user, text: context.lang.settingsAccount, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const AccountView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsAccount), ), BetterListTile( icon: FontAwesomeIcons.shieldHeart, text: context.lang.settingsSubscription, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const SubscriptionView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsSubscription), ), BetterListTile( icon: Icons.lock_clock_rounded, text: context.lang.settingsBackup, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const BackupView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsBackup), ), const Divider(), BetterListTile( icon: FontAwesomeIcons.sun, text: context.lang.settingsAppearance, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const AppearanceView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsAppearance), ), BetterListTile( icon: FontAwesomeIcons.comment, text: context.lang.settingsChats, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ChatSettingsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsChats), ), BetterListTile( icon: FontAwesomeIcons.lock, text: context.lang.settingsPrivacy, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const PrivacyView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsPrivacy), ), BetterListTile( icon: FontAwesomeIcons.bell, text: context.lang.settingsNotification, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const NotificationView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsNotification), ), BetterListTile( icon: FontAwesomeIcons.chartPie, iconSize: 15, text: context.lang.settingsStorageData, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const DataAndStorageView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsStorage), ), const Divider(), BetterListTile( icon: FontAwesomeIcons.circleQuestion, text: context.lang.settingsHelp, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const HelpView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsHelp), ), if (gUser.isDeveloper) BetterListTile( icon: FontAwesomeIcons.code, text: 'Developer Settings', - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const DeveloperSettingsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsDeveloper), ), BetterListTile( icon: FontAwesomeIcons.shareFromSquare, text: context.lang.inviteFriends, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return const ShareWithFriendsView(); - }, - ), - ); - }, + onTap: () => context.push(Routes.settingsInvite), ), ], ), diff --git a/lib/src/views/user_study/user_study_questionnaire.view.dart b/lib/src/views/user_study/user_study_questionnaire.view.dart index 09469be..c23a4da 100644 --- a/lib/src/views/user_study/user_study_questionnaire.view.dart +++ b/lib/src/views/user_study/user_study_questionnaire.view.dart @@ -2,19 +2,22 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/user_study/user_study_data_collection.dart'; -class UserStudyQuestionnaire extends StatefulWidget { - const UserStudyQuestionnaire({super.key}); +class UserStudyQuestionnaireView extends StatefulWidget { + const UserStudyQuestionnaireView({super.key}); @override - State createState() => _UserStudyQuestionnaireState(); + State createState() => + _UserStudyQuestionnaireViewState(); } -class _UserStudyQuestionnaireState extends State { +class _UserStudyQuestionnaireViewState + extends State { final Map _responses = { 'age': null, 'education': null, @@ -62,7 +65,7 @@ class _UserStudyQuestionnaireState extends State { const SnackBar(content: Text('Vielen Dank für deine Teilnahme!')), ); - Navigator.pop(context); + context.pop(); } @override diff --git a/lib/src/views/user_study/user_study_welcome.view.dart b/lib/src/views/user_study/user_study_welcome.view.dart index ed8adb1..c11e12c 100644 --- a/lib/src/views/user_study/user_study_welcome.view.dart +++ b/lib/src/views/user_study/user_study_welcome.view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:twonly/src/constants/routes.keys.dart'; import 'package:twonly/src/utils/storage.dart'; -import 'package:twonly/src/views/user_study/user_study_questionnaire.view.dart'; class UserStudyWelcomeView extends StatefulWidget { const UserStudyWelcomeView({super.key, this.wasOpenedAutomatic = false}); @@ -54,16 +55,8 @@ class _UserStudyWelcomeViewState extends State { const SizedBox(height: 40), Center( child: FilledButton( - onPressed: () { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) { - return const UserStudyQuestionnaire(); - }, - ), - ); - }, + onPressed: () => context + .pushReplacement(Routes.settingsHelpUserStudyQuestionnaire), child: const Padding( padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15), child: Text( @@ -77,9 +70,7 @@ class _UserStudyWelcomeViewState extends State { if (widget.wasOpenedAutomatic) Center( child: OutlinedButton( - onPressed: () { - Navigator.pop(context); - }, + onPressed: () => context.pop(), child: const Padding( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Text( @@ -98,7 +89,7 @@ class _UserStudyWelcomeViewState extends State { u.askedForUserStudyPermission = true; return u; }); - if (context.mounted) Navigator.pop(context); + if (context.mounted) context.pop(); }, child: const Text( 'Nicht mehr anzeigen', diff --git a/pubspec.lock b/pubspec.lock index 1227333..d9c8ce6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -852,6 +852,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896" + url: "https://pub.dev" + source: hosted + version: "17.1.0" google_mlkit_barcode_scanning: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 72fb8fd..1a48e4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: vector_graphics: ^1.1.19 video_player: ^2.10.1 in_app_purchase: ^3.2.3 + go_router: ^17.1.0 # Trusted publisher fluttercommunity.dev