From 67cc9d743193535f0cfb0f52bc5e3fac51831b26 Mon Sep 17 00:00:00 2001 From: Shubham Ingale Date: Wed, 2 Jul 2025 19:01:35 +0530 Subject: [PATCH] getx: add ccsync creds widget as getx route --- .../modules/home/views/home_page_app_bar.dart | 7 +- lib/app/modules/home/views/nav_drawer.dart | 19 ++-- .../manage_task_champion_creds_binding.dart | 12 +++ ...manage_task_champion_creds_controller.dart | 37 ++++++++ .../manage_task_champion_creds_view.dart} | 87 +++++++------------ lib/app/routes/app_pages.dart | 12 ++- lib/app/routes/app_routes.dart | 2 + .../utils/taskchampion/taskchampion_test.dart | 68 +++++++-------- 8 files changed, 133 insertions(+), 111 deletions(-) create mode 100644 lib/app/modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart create mode 100644 lib/app/modules/manage_task_champion_creds/controllers/manage_task_champion_creds_controller.dart rename lib/app/{utils/taskchampion/taskchampion.dart => modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart} (54%) diff --git a/lib/app/modules/home/views/home_page_app_bar.dart b/lib/app/modules/home/views/home_page_app_bar.dart index 2d77f48e..a25b24c5 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -8,7 +8,6 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/gen/fonts.gen.dart'; import 'package:taskwarrior/app/utils/language/sentence_manager.dart'; import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; import 'package:taskwarrior/app/utils/taskserver/taskserver.dart'; import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; @@ -74,11 +73,7 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { .homePageSetup, onPressed: () { if (controller.taskchampion.value) { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => ManageTaskChampionCreds(), - )).then((value) {}); + Get.toNamed(Routes.MANAGE_TASK_CHAMPION_CREDS); } else { Get.toNamed(Routes.MANAGE_TASK_SERVER); } diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index d1f71cf5..25884549 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -12,7 +12,6 @@ import 'package:taskwarrior/app/utils/constants/constants.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/constants/utilites.dart'; import 'package:taskwarrior/app/utils/language/sentence_manager.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; import 'package:taskwarrior/app/utils/themes/dark_theme.dart'; import 'package:taskwarrior/app/utils/themes/light_theme.dart'; @@ -23,7 +22,8 @@ class NavDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - TaskwarriorColorTheme tColors = Theme.of(context).extension()!; + TaskwarriorColorTheme tColors = + Theme.of(context).extension()!; return Drawer( backgroundColor: tColors.dialogBackgroundColor, surfaceTintColor: tColors.dialogBackgroundColor, @@ -55,12 +55,14 @@ class NavDrawer extends StatelessWidget { child: ThemeSwitcherClipper( isDarkMode: AppSettings.isDarkMode, onTap: (bool newMode) async { - Get.changeThemeMode(newMode ? ThemeMode.dark : ThemeMode.light); + Get.changeThemeMode( + newMode ? ThemeMode.dark : ThemeMode.light); AppSettings.isDarkMode = newMode; await SelectedTheme.saveMode(AppSettings.isDarkMode); // Get.back(); homeController.initLanguageAndDarkMode(); - Get.changeTheme(AppSettings.isDarkMode ? darkTheme : lightTheme); + Get.changeTheme( + AppSettings.isDarkMode ? darkTheme : lightTheme); }, child: Icon( tColors.icons, @@ -84,11 +86,7 @@ class NavDrawer extends StatelessWidget { currentLanguage: homeController.selectedLanguage.value, ).sentences.ccsyncCredentials, onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => ManageTaskChampionCreds(), - ), - ); + Get.toNamed(Routes.MANAGE_TASK_CHAMPION_CREDS); }, ), ), @@ -245,7 +243,8 @@ class NavDrawer extends StatelessWidget { } Future _showExitConfirmationDialog(BuildContext context) async { - TaskwarriorColorTheme tColors = Theme.of(context).extension()!; + TaskwarriorColorTheme tColors = + Theme.of(context).extension()!; return showDialog( context: context, barrierDismissible: diff --git a/lib/app/modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart b/lib/app/modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart new file mode 100644 index 00000000..9f53578e --- /dev/null +++ b/lib/app/modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart @@ -0,0 +1,12 @@ +import 'package:get/get.dart'; + +import '../controllers/manage_task_champion_creds_controller.dart'; + +class ManageTaskChampionCredsBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => ManageTaskChampionCredsController(), + ); + } +} diff --git a/lib/app/modules/manage_task_champion_creds/controllers/manage_task_champion_creds_controller.dart b/lib/app/modules/manage_task_champion_creds/controllers/manage_task_champion_creds_controller.dart new file mode 100644 index 00000000..3b658cfe --- /dev/null +++ b/lib/app/modules/manage_task_champion_creds/controllers/manage_task_champion_creds_controller.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ManageTaskChampionCredsController extends GetxController { + final encryptionSecretController = TextEditingController(); + final clientIdController = TextEditingController(); + final ccsyncBackendUrlController = TextEditingController(); + + @override + void onInit() { + super.onInit(); + loadCredentials(); + } + + Future loadCredentials() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + encryptionSecretController.text = prefs.getString('encryptionSecret') ?? ''; + clientIdController.text = prefs.getString('clientId') ?? ''; + ccsyncBackendUrlController.text = prefs.getString('ccsyncBackendUrl') ?? ''; + } + + Future saveCredentials() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('encryptionSecret', encryptionSecretController.text); + await prefs.setString('clientId', clientIdController.text); + await prefs.setString('ccsyncBackendUrl', ccsyncBackendUrlController.text); + } + + @override + void onClose() { + encryptionSecretController.dispose(); + clientIdController.dispose(); + ccsyncBackendUrlController.dispose(); + super.onClose(); + } +} diff --git a/lib/app/utils/taskchampion/taskchampion.dart b/lib/app/modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart similarity index 54% rename from lib/app/utils/taskchampion/taskchampion.dart rename to lib/app/modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart index 28c2c8dc..0cca4977 100644 --- a/lib/app/utils/taskchampion/taskchampion.dart +++ b/lib/app/modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart @@ -1,47 +1,21 @@ -// ignore_for_file: use_build_context_synchronously - import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; -import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; +import 'package:taskwarrior/app/utils/constants/constants.dart'; import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; - import 'package:url_launcher/url_launcher.dart'; +import '../controllers/manage_task_champion_creds_controller.dart'; -class ManageTaskChampionCreds extends StatelessWidget { - final TextEditingController _encryptionSecretController = - TextEditingController(); - final TextEditingController _clientIdController = TextEditingController(); - final TextEditingController _ccsyncBackendUrlController = - TextEditingController(); - - ManageTaskChampionCreds({super.key}) { - _loadCredentials(); - } - - Future _loadCredentials() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - _encryptionSecretController.text = - prefs.getString('encryptionSecret') ?? ''; - _clientIdController.text = prefs.getString('clientId') ?? ''; - _ccsyncBackendUrlController.text = prefs.getString('ccsyncBackendUrl') ?? ''; - } - - Future _saveCredentials(BuildContext context) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setString('encryptionSecret', _encryptionSecretController.text); - await prefs.setString('clientId', _clientIdController.text); - await prefs.setString('ccsyncBackendUrl', _ccsyncBackendUrlController.text); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Credentials saved successfully')), - ); - } +class ManageTaskChampionCredsView + extends GetView { + const ManageTaskChampionCredsView({super.key}); @override Widget build(BuildContext context) { + debugPrint("Built ManageTaskChampionCredsView"); TaskwarriorColorTheme tColors = Theme.of(context).extension()!; + return Scaffold( appBar: AppBar( backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, @@ -72,8 +46,9 @@ class ManageTaskChampionCreds extends StatelessWidget { }, ), ], - leading: BackButton( - color: TaskWarriorColors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back, color: TaskWarriorColors.white), + onPressed: () => Get.back(), ), ), backgroundColor: tColors.primaryBackgroundColor, @@ -87,49 +62,45 @@ class ManageTaskChampionCreds extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( - style: TextStyle( - color: tColors.primaryTextColor, - ), - controller: _encryptionSecretController, + style: TextStyle(color: tColors.primaryTextColor), + controller: controller.encryptionSecretController, decoration: InputDecoration( labelText: 'Encryption Secret', - labelStyle: TextStyle( - color: tColors.primaryTextColor, - ), + labelStyle: TextStyle(color: tColors.primaryTextColor), border: const OutlineInputBorder(), ), ), const SizedBox(height: 10), TextField( - style: TextStyle( - color: tColors.primaryTextColor, - ), - controller: _clientIdController, + style: TextStyle(color: tColors.primaryTextColor), + controller: controller.clientIdController, decoration: InputDecoration( labelText: 'Client ID', - labelStyle: TextStyle( - color: tColors.primaryTextColor, - ), + labelStyle: TextStyle(color: tColors.primaryTextColor), border: const OutlineInputBorder(), ), ), const SizedBox(height: 10), TextField( - style: TextStyle( - color: tColors.primaryTextColor, - ), - controller: _ccsyncBackendUrlController, + style: TextStyle(color: tColors.primaryTextColor), + controller: controller.ccsyncBackendUrlController, decoration: InputDecoration( labelText: 'CCSync Backend URL', - labelStyle: TextStyle( - color: tColors.primaryTextColor, - ), + labelStyle: TextStyle(color: tColors.primaryTextColor), border: const OutlineInputBorder(), ), ), const SizedBox(height: 20), ElevatedButton( - onPressed: () => _saveCredentials(context), + onPressed: () async { + await controller.saveCredentials(); + Get.snackbar( + 'Success', + 'Credentials saved successfully', + snackPosition: SnackPosition.BOTTOM, + duration: Duration(seconds: 2), + ); + }, child: const Text('Save Credentials'), ), const SizedBox(height: 10), diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 0cc14c1b..276a297d 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,16 +1,15 @@ -// ignore_for_file: constant_identifier_names - import 'package:get/get.dart'; import '../modules/about/bindings/about_binding.dart'; import '../modules/about/views/about_view.dart'; - import '../modules/detailRoute/bindings/detail_route_binding.dart'; import '../modules/detailRoute/views/detail_route_view.dart'; import '../modules/home/bindings/home_binding.dart'; import '../modules/home/views/home_view.dart'; import '../modules/manageTaskServer/bindings/manage_task_server_binding.dart'; import '../modules/manageTaskServer/views/manage_task_server_view.dart'; +import '../modules/manage_task_champion_creds/bindings/manage_task_champion_creds_binding.dart'; +import '../modules/manage_task_champion_creds/views/manage_task_champion_creds_view.dart'; import '../modules/onboarding/bindings/onboarding_binding.dart'; import '../modules/onboarding/views/onboarding_view.dart'; import '../modules/permission/bindings/permission_binding.dart'; @@ -24,6 +23,8 @@ import '../modules/settings/views/settings_view.dart'; import '../modules/splash/bindings/splash_binding.dart'; import '../modules/splash/views/splash_view.dart'; +// ignore_for_file: constant_identifier_names + part 'app_routes.dart'; class AppPages { @@ -82,5 +83,10 @@ class AppPages { page: () => const PermissionView(), binding: PermissionBinding(), ), + GetPage( + name: _Paths.MANAGE_TASK_CHAMPION_CREDS, + page: () => const ManageTaskChampionCredsView(), + binding: ManageTaskChampionCredsBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index b04f381a..0729f447 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -15,6 +15,7 @@ abstract class Routes { static const REPORTS = _Paths.REPORTS; static const SETTINGS = _Paths.SETTINGS; static const PERMISSION = _Paths.PERMISSION; + static const MANAGE_TASK_CHAMPION_CREDS = _Paths.MANAGE_TASK_CHAMPION_CREDS; } abstract class _Paths { @@ -29,4 +30,5 @@ abstract class _Paths { static const REPORTS = '/reports'; static const SETTINGS = '/settings'; static const PERMISSION = '/permission'; + static const MANAGE_TASK_CHAMPION_CREDS = '/manage-task-champion-creds'; } diff --git a/test/utils/taskchampion/taskchampion_test.dart b/test/utils/taskchampion/taskchampion_test.dart index c4542359..0e0fddb1 100644 --- a/test/utils/taskchampion/taskchampion_test.dart +++ b/test/utils/taskchampion/taskchampion_test.dart @@ -1,54 +1,54 @@ -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; +import 'package:taskwarrior/app/modules/manage_task_champion_creds/controllers/manage_task_champion_creds_controller.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('ManageTaskChampionCreds', () { + group('ManageTaskChampionCredsController Test', () { + late ManageTaskChampionCredsController controller; + setUp(() { + // Start with empty SharedPreferences SharedPreferences.setMockInitialValues({}); + controller = ManageTaskChampionCredsController(); }); - testWidgets('should load credentials and display them in text fields', - (WidgetTester tester) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setString('encryptionSecret', 'testSecret'); - await prefs.setString('clientId', 'testClientId'); - - await tester.pumpWidget(MaterialApp(home: ManageTaskChampionCreds())); + test('should load empty credentials initially', () async { + // Wait for onInit() to complete + await Future.delayed(Duration(milliseconds: 10)); - await tester.pump(); - - expect(find.text('testSecret'), findsOneWidget); - expect(find.text('testClientId'), findsOneWidget); + expect(controller.encryptionSecretController.text, ''); + expect(controller.clientIdController.text, ''); + expect(controller.ccsyncBackendUrlController.text, ''); }); - testWidgets('should save credentials when the save button is pressed', - (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ManageTaskChampionCreds())); - - await tester.enterText(find.byType(TextField).at(0), 'newSecret'); - await tester.enterText(find.byType(TextField).at(1), 'newClientId'); - - await tester.tap(find.text('Save Credentials')); - await tester.pump(); - - SharedPreferences prefs = await SharedPreferences.getInstance(); - expect(prefs.getString('encryptionSecret'), 'newSecret'); - expect(prefs.getString('clientId'), 'newClientId'); + test('should load existing credentials', () async { + SharedPreferences.setMockInitialValues({ + 'encryptionSecret': 'mysecret', + 'clientId': 'client123', + 'ccsyncBackendUrl': 'https://example.com', + }); + + controller = ManageTaskChampionCredsController(); + await controller.loadCredentials(); + expect(controller.encryptionSecretController.text, 'mysecret'); + expect(controller.clientIdController.text, 'client123'); + expect(controller.ccsyncBackendUrlController.text, 'https://example.com'); }); - testWidgets('should show snackbar when credentials are saved', - (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ManageTaskChampionCreds())); + test('should save credentials', () async { + controller.encryptionSecretController.text = 'secret123'; + controller.clientIdController.text = 'clientABC'; + controller.ccsyncBackendUrlController.text = 'https://backend.url'; + + await controller.saveCredentials(); - await tester.tap(find.text('Save Credentials')); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); + final prefs = await SharedPreferences.getInstance(); - expect(find.text('Credentials saved successfully'), findsOneWidget); + expect(prefs.getString('encryptionSecret'), 'secret123'); + expect(prefs.getString('clientId'), 'clientABC'); + expect(prefs.getString('ccsyncBackendUrl'), 'https://backend.url'); }); }); }