From 7d3d8e834607235e134148f99e435dc64cc7fc52 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 06:38:29 +0530 Subject: [PATCH 01/26] feat: add taskchampion sync --- lib/api_service.dart | 490 ++++++++++++++++++ .../home/controllers/home_controller.dart | 1 + .../controllers/settings_controller.dart | 3 + .../settings/views/settings_page_body.dart | 16 +- .../views/settings_page_taskchampion.dart | 29 ++ lib/app/utils/language/english_sentences.dart | 7 + lib/app/utils/language/hindi_sentences.dart | 7 + lib/app/utils/language/marathi_sentences.dart | 7 + lib/app/utils/language/sentences.dart | 3 + lib/app/utils/taskchampion/taskchampion.dart | 182 +++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 24 + pubspec.yaml | 1 + 13 files changed, 771 insertions(+), 1 deletion(-) create mode 100644 lib/api_service.dart create mode 100644 lib/app/modules/settings/views/settings_page_taskchampion.dart create mode 100644 lib/app/utils/taskchampion/taskchampion.dart diff --git a/lib/api_service.dart b/lib/api_service.dart new file mode 100644 index 00000000..a6b9f668 --- /dev/null +++ b/lib/api_service.dart @@ -0,0 +1,490 @@ +// ignore_for_file: depend_on_referenced_packages, unnecessary_null_in_if_null_operators + +import 'dart:convert'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'package:sqflite/sqflite.dart'; +import 'package:path/path.dart'; +import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; + +class Tasks { + final int id; + final String description; + final String? project; + final String status; + final String? uuid; + final double? urgency; + final String? priority; + final String? due; + final String? end; + final String entry; + final String? modified; + + Tasks({ + required this.id, + required this.description, + required this.project, + required this.status, + required this.uuid, + required this.urgency, + required this.priority, + required this.due, + required this.end, + required this.entry, + required this.modified, + }); + + factory Tasks.fromJson(Map json) { + return Tasks( + id: json['id'], + description: json['description'], + project: json['project'], + status: json['status'], + uuid: json['uuid'], + urgency: json['urgency'].toDouble(), + priority: json['priority'], + due: json['due'], + end: json['end'], + entry: json['entry'], + modified: json['modified'], + ); + } + + Map toJson() { + return { + 'id': id, + 'description': description, + 'project': project, + 'status': status, + 'uuid': uuid, + 'urgency': urgency, + 'priority': priority, + 'due': due, + 'end': end, + 'entry': entry, + 'modified': modified, + }; + } +} + +String baseUrl = 'YOUR_IP'; +String origin = 'CONTAINER_ORIGIN'; + +Future> fetchTasks(String uuid, String encryptionSecret) async { + String url = + '$baseUrl/tasks?email=$origin&origin=$origin&UUID=$uuid&encryptionSecret=$encryptionSecret'; + + var response = await http.get(Uri.parse(url), headers: { + "Content-Type": "application/json", + }).timeout(const Duration(seconds: 10000)); + if (response.statusCode == 200) { + List allTasks = jsonDecode(response.body); + debugPrint(allTasks.toString()); + return allTasks.map((task) => Tasks.fromJson(task)).toList(); + } else { + throw Exception('Failed to load tasks'); + } +} + +Future updateTasksInDatabase(List tasks) async { + var taskDatabase = TaskDatabase(); + await taskDatabase.open(); + // find tasks without UUID + List tasksWithoutUUID = await taskDatabase.findTasksWithoutUUIDs(); + + //add tasks without UUID to the server and delete them from database + for (var task in tasksWithoutUUID) { + try { + await addTaskAndDeleteFromDatabase( + task.description, task.project!, task.due!, task.priority!); + } catch (e) { + debugPrint('Failed to add task without UUID to server: $e'); + } + } + + // update existing tasks in db + for (var task in tasks) { + var existingTask = await taskDatabase.getTaskByUuid(task.uuid!); + if (existingTask != null) { + if (task.modified!.compareTo(existingTask.modified!) > 0) { + await taskDatabase.updateTask(task); + } + } else { + // add new tasks to db + await taskDatabase.insertTask(task); + } + } + + var localTasks = await taskDatabase.fetchTasksFromDatabase(); + var localTasksMap = {for (var task in localTasks) task.uuid: task}; + + for (var serverTask in tasks) { + var localTask = localTasksMap[serverTask.uuid]; + + if (localTask == null) { + // Task doesn't exist in the local database, insert it + await taskDatabase.insertTask(serverTask); + } else { + var serverTaskModifiedDate = DateTime.parse(serverTask.modified!); + var localTaskModifiedDate = DateTime.parse(localTask.modified!); + + if (serverTaskModifiedDate.isAfter(localTaskModifiedDate)) { + // Server task is newer, update local database + await taskDatabase.updateTask(serverTask); + } else if (serverTaskModifiedDate.isBefore(localTaskModifiedDate)) { + // local task is newer, update server + await modifyTaskOnTaskwarrior( + localTask.description, + localTask.project!, + localTask.due!, + localTask.priority!, + localTask.status, + localTask.uuid!, + ); + } + } + } +} + +Future deleteTask( + String email, String encryptionSecret, String uuid, String taskUuid) async { + final url = Uri.parse('$baseUrl/delete-task'); + final body = jsonEncode({ + 'email': email, + 'encryptionSecret': encryptionSecret, + 'UUID': uuid, + 'taskuuid': taskUuid, + }); + + try { + final response = await http.post( + url, + headers: { + 'Content-Type': 'application/json', + }, + body: body, + ); + + if (response.statusCode == 200) { + debugPrint('Task deleted successfully on server'); + } else { + debugPrint('Failed to delete task: ${response.statusCode}'); + } + } catch (e) { + debugPrint('Error deleting task: $e'); + } +} + +Future completeTask( + String email, String encryptionSecret, String uuid, String taskUuid) async { + final url = Uri.parse('$baseUrl/complete-task'); + final body = jsonEncode({ + 'email': email, + 'encryptionSecret': encryptionSecret, + 'UUID': uuid, + 'taskuuid': taskUuid, + }); + + try { + final response = await http.post( + url, + headers: { + 'Content-Type': 'application/json', + }, + body: body, + ); + + if (response.statusCode == 200) { + debugPrint('Task completed successfully on server'); + } else { + debugPrint('Failed to complete task: ${response.statusCode}'); + } + } catch (e) { + debugPrint('Error completing task: $e'); + } +} + +Future addTaskAndDeleteFromDatabase( + String description, String project, String due, String priority) async { + String apiUrl = '$baseUrl/add-task'; + var c = await CredentialsStorage.getClientId(); + var e = await CredentialsStorage.getEncryptionSecret(); + debugPrint(c); + debugPrint(e); + await http.post( + Uri.parse(apiUrl), + headers: { + 'Content-Type': 'text/plain', + }, + body: jsonEncode({ + 'email': 'email', + 'encryptionSecret': e, + 'UUID': c, + 'description': description, + 'project': project, + 'due': due, + 'priority': priority, + }), + ); + + var taskDatabase = TaskDatabase(); + await taskDatabase.open(); + await taskDatabase._database!.delete( + 'Tasks', + where: 'description = ? AND due = ? AND project = ? AND priority = ?', + whereArgs: [description, due, project, priority], + ); +} + +Future modifyTaskOnTaskwarrior(String description, String project, + String due, String priority, String status, String taskuuid) async { + String apiUrl = '$baseUrl/modify-task'; + var c = await CredentialsStorage.getClientId(); + var e = await CredentialsStorage.getEncryptionSecret(); + debugPrint(c); + debugPrint(e); + await http.post( + Uri.parse(apiUrl), + headers: { + 'Content-Type': 'text/plain', + }, + body: jsonEncode({ + "email": "e", + "encryptionSecret": e, + "UUID": c, + "description": description, + "priority": priority, + "project": project, + "due": due, + "status": status, + "taskuuid": taskuuid, + }), + ); + + var taskDatabase = TaskDatabase(); + await taskDatabase.open(); + await taskDatabase._database!.delete( + 'Tasks', + where: 'description = ? AND due = ? AND project = ? AND priority = ?', + whereArgs: [description, due, project, priority], + ); +} + +class TaskDatabase { + Database? _database; + + Future open() async { + var databasesPath = await getDatabasesPath(); + String path = join(databasesPath, 'tasks.db'); + + _database = await openDatabase(path, version: 1, + onCreate: (Database db, version) async { + await db.execute(''' + CREATE TABLE Tasks ( + uuid TEXT PRIMARY KEY, + id INTEGER, + description TEXT, + project TEXT, + status TEXT, + urgency REAL, + priority TEXT, + due TEXT, + end TEXT, + entry TEXT, + modified TEXT + ) + '''); + }); + } + + Future ensureDatabaseIsOpen() async { + if (_database == null) { + await open(); + } + } + + Future> fetchTasksFromDatabase() async { + await ensureDatabaseIsOpen(); + + final List> maps = await _database!.query('Tasks'); + var a = List.generate(maps.length, (i) { + return Tasks( + id: maps[i]['id'], + description: maps[i]['description'], + project: maps[i]['project'], + status: maps[i]['status'], + uuid: maps[i]['uuid'], + urgency: maps[i]['urgency'], + priority: maps[i]['priority'], + due: maps[i]['due'], + end: maps[i]['end'], + entry: maps[i]['entry'], + modified: maps[i]['modified'], + ); + }); + // debugPrint('Tasks from db'); + // debugPrint(a.toString()); + return a; + } + + Future deleteAllTasksInDB() async { + await ensureDatabaseIsOpen(); + + await _database!.delete('Tasks'); + debugPrint('Deleted all tasks'); + await open(); + debugPrint('Created new task table'); + } + + Future printDatabaseContents() async { + await ensureDatabaseIsOpen(); + + List> maps = await _database!.query('Tasks'); + for (var map in maps) { + map.forEach((key, value) { + debugPrint('Key: $key, Value: $value, Type: ${value.runtimeType}'); + }); + } + } + + Future insertTask(Tasks task) async { + await ensureDatabaseIsOpen(); + + await _database!.insert( + 'Tasks', + task.toJson(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + Future updateTask(Tasks task) async { + await ensureDatabaseIsOpen(); + + await _database!.update( + 'Tasks', + task.toJson(), + where: 'uuid = ?', + whereArgs: [task.uuid], + ); + } + + Future getTaskByUuid(String uuid) async { + await ensureDatabaseIsOpen(); + + List> maps = await _database!.query( + 'Tasks', + where: 'uuid = ?', + whereArgs: [uuid], + ); + + if (maps.isNotEmpty) { + return Tasks.fromJson(maps.first); + } else { + return null; + } + } + + Future markTaskAsCompleted(String uuid) async { + await ensureDatabaseIsOpen(); + + await _database!.update( + 'Tasks', + {'modified': (DateTime.now()).toIso8601String(), 'status': 'completed'}, + where: 'uuid = ?', + whereArgs: [uuid], + ); + debugPrint('task${uuid}completed'); + debugPrint({DateTime.now().toIso8601String()}.toString()); + } + + Future markTaskAsDeleted(String uuid) async { + await ensureDatabaseIsOpen(); + + await _database!.update( + 'Tasks', + {'status': 'deleted'}, + where: 'uuid = ?', + whereArgs: [uuid], + ); + debugPrint('task${uuid}deleted'); + } + + Future saveEditedTaskInDB( + String uuid, + String newDescription, + String newProject, + String newStatus, + String newPriority, + String newDue, + ) async { + await ensureDatabaseIsOpen(); + + debugPrint('task${uuid}deleted'); + await _database!.update( + 'Tasks', + { + 'description': newDescription, + 'project': newProject, + 'status': newStatus, + 'priority': newPriority, + 'due': newDue, + }, + where: 'uuid = ?', + whereArgs: [uuid], + ); + debugPrint('task${uuid}edited'); + } + + Future> findTasksWithoutUUIDs() async { + await ensureDatabaseIsOpen(); + + List> maps = await _database!.query( + 'Tasks', + where: 'uuid IS NULL OR uuid = ?', + whereArgs: [''], + ); + + return List.generate(maps.length, (i) { + return Tasks.fromJson(maps[i]); + }); + } + + Future> getTasksByProject(String project) async { + List> maps = await _database!.query( + 'Tasks', + where: 'project = ?', + whereArgs: [project], + ); + + return List.generate(maps.length, (i) { + return Tasks( + uuid: maps[i]['uuid'], + id: maps[i]['id'], + description: maps[i]['description'], + project: maps[i]['project'], + status: maps[i]['status'], + urgency: maps[i]['urgency'], + priority: maps[i]['priority'], + due: maps[i]['due'], + end: maps[i]['end'], + entry: maps[i]['entry'], + modified: maps[i]['modified'], + ); + }); + } + + Future> searchTasks(String query) async { + final List> maps = await _database!.query( + 'tasks', + where: 'description LIKE ? OR project LIKE ?', + whereArgs: ['%$query%', '%$query%'], + ); + return List.generate(maps.length, (i) { + return Tasks.fromJson(maps[i]); + }); + } + + Future close() async { + await _database!.close(); + } +} diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 466feb62..701a9bf2 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -435,6 +435,7 @@ class HomeController extends GetxController { RxBool syncOnTaskCreate = false.obs; RxBool delaytask = false.obs; RxBool change24hr = false.obs; + RxBool taskchampion = false.obs; // dialogue box diff --git a/lib/app/modules/settings/controllers/settings_controller.dart b/lib/app/modules/settings/controllers/settings_controller.dart index 9e08f687..c1f89163 100644 --- a/lib/app/modules/settings/controllers/settings_controller.dart +++ b/lib/app/modules/settings/controllers/settings_controller.dart @@ -4,6 +4,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get/get_rx/get_rx.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -163,6 +164,7 @@ class SettingsController extends GetxController { RxBool isSyncOnTaskCreateActivel = false.obs; RxBool delaytask = false.obs; RxBool change24hr = false.obs; + RxBool taskchampion = false.obs; RxBool isDarkModeOn = false.obs; void initDarkMode() { @@ -177,6 +179,7 @@ class SettingsController extends GetxController { prefs.getBool('sync-OnTaskCreate') ?? false; delaytask.value = prefs.getBool('delaytask') ?? false; change24hr.value = prefs.getBool('24hourformate') ?? false; + taskchampion.value = prefs.getBool('taskc') ?? false; initDarkMode(); baseDirectory.value = await getBaseDirectory(); super.onInit(); diff --git a/lib/app/modules/settings/views/settings_page_body.dart b/lib/app/modules/settings/views/settings_page_body.dart index c69bbe0f..207c1e64 100644 --- a/lib/app/modules/settings/views/settings_page_body.dart +++ b/lib/app/modules/settings/views/settings_page_body.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:taskwarrior/app/modules/settings/views/settings_page_taskchampion.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/language/sentence_manager.dart'; @@ -23,7 +24,7 @@ import 'package:taskwarrior/app/utils/theme/app_settings.dart'; class SettingsPageBody extends StatelessWidget { final SettingsController controller; - + const SettingsPageBody({required this.controller, super.key}); @override @@ -115,6 +116,19 @@ class SettingsPageBody extends StatelessWidget { controller: controller, ), ), + SettingsPageListTile( + title: SentenceManager( + currentLanguage: controller.selectedLanguage.value) + .sentences + .taskchampionTileTitle, + subTitle: SentenceManager( + currentLanguage: controller.selectedLanguage.value) + .sentences + .taskchampionTileDescription, + trailing: SettingsPageTaskchampionTileListTileTrailing( + controller: controller, + ), + ), const Divider(), SettingsPageListTile( title: SentenceManager( diff --git a/lib/app/modules/settings/views/settings_page_taskchampion.dart b/lib/app/modules/settings/views/settings_page_taskchampion.dart new file mode 100644 index 00000000..2582cd00 --- /dev/null +++ b/lib/app/modules/settings/views/settings_page_taskchampion.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; + +import '../controllers/settings_controller.dart'; + +class SettingsPageTaskchampionTileListTileTrailing extends StatelessWidget { + final SettingsController controller; + const SettingsPageTaskchampionTileListTileTrailing( + {required this.controller, super.key}); + + @override + Widget build(BuildContext context) { + return Obx( + () => Switch( + value: controller.taskchampion.value, + onChanged: (bool value) async { + controller.taskchampion.value = value; + + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setBool('taskc', value); + Get.find().taskchampion.value = value; + }, + ), + ); + } +} diff --git a/lib/app/utils/language/english_sentences.dart b/lib/app/utils/language/english_sentences.dart index e4334550..197b48df 100644 --- a/lib/app/utils/language/english_sentences.dart +++ b/lib/app/utils/language/english_sentences.dart @@ -187,4 +187,11 @@ class EnglishSentences extends Sentences { String get reportsPageNoTasksFound => 'No Tasks Found'; @override String get reportsPageAddTasksToSeeReports => 'Add Tasks To See Reports'; + + @override + String get taskchampionTileDescription => + 'Switch to TaskWarrior sync with CCSync or Taskchampion Sync Server'; + + @override + String get taskchampionTileTitle => 'Taskchampion sync'; } diff --git a/lib/app/utils/language/hindi_sentences.dart b/lib/app/utils/language/hindi_sentences.dart index d60f036a..f34c6c28 100644 --- a/lib/app/utils/language/hindi_sentences.dart +++ b/lib/app/utils/language/hindi_sentences.dart @@ -188,4 +188,11 @@ class HindiSentences extends Sentences { @override String get reportsPageAddTasksToSeeReports => 'रिपोर्ट देखने के लिए कार्य जोड़ें'; + + @override + String get taskchampionTileDescription => + 'CCSync या Taskchampion सिंक सर्वर के साथ TaskWarrior सिंक पर स्विच करें'; + + @override + String get taskchampionTileTitle => 'Taskchampion सिंक'; } diff --git a/lib/app/utils/language/marathi_sentences.dart b/lib/app/utils/language/marathi_sentences.dart index 41b9d53f..d5382917 100644 --- a/lib/app/utils/language/marathi_sentences.dart +++ b/lib/app/utils/language/marathi_sentences.dart @@ -187,4 +187,11 @@ class MarathiSentences extends Sentences { String get reportsPageNoTasksFound => 'कोणतेही काम सापडले नाहीत'; @override String get reportsPageAddTasksToSeeReports => 'अहवाल पाहण्यासाठी काम जोडा'; + + @override + String get taskchampionTileDescription => + 'CCSync किंवा Taskchampion Sync Server सह TaskWarrior सिंक वर स्विच करा'; + + @override + String get taskchampionTileTitle => 'Taskchampion सिंक'; } diff --git a/lib/app/utils/language/sentences.dart b/lib/app/utils/language/sentences.dart index ac050117..29ef47c0 100644 --- a/lib/app/utils/language/sentences.dart +++ b/lib/app/utils/language/sentences.dart @@ -33,6 +33,9 @@ abstract class Sentences { String get settingsPageEnable24hrFormatTitle; String get settingsPageEnable24hrFormatDescription; + String get taskchampionTileTitle; + String get taskchampionTileDescription; + String get settingsPageSelectLanguage; String get settingsPageToggleNativeLanguage; diff --git a/lib/app/utils/taskchampion/taskchampion.dart b/lib/app/utils/taskchampion/taskchampion.dart new file mode 100644 index 00000000..2047aff9 --- /dev/null +++ b/lib/app/utils/taskchampion/taskchampion.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.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/theme/app_settings.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ManageTaskChampionCreds extends StatefulWidget { + const ManageTaskChampionCreds({super.key}); + + @override + State createState() => + _ManageTaskChampionCredsState(); +} + +class _ManageTaskChampionCredsState extends State { + String encryptionSecret = ''; + String clientId = ''; + + final TextEditingController _encryptionSecretController = + TextEditingController(); + final TextEditingController _clientIdController = TextEditingController(); + + @override + void initState() { + super.initState(); + _loadCredentials(); + } + + Future _loadCredentials() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + encryptionSecret = prefs.getString('encryptionSecret') ?? ''; + clientId = prefs.getString('clientId') ?? ''; + _encryptionSecretController.text = encryptionSecret; + _clientIdController.text = clientId; + }); + } + + Future _saveCredentials() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('encryptionSecret', _encryptionSecretController.text); + await prefs.setString('clientId', _clientIdController.text); + } + + @override + void dispose() { + _encryptionSecretController.dispose(); + _clientIdController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, + titleSpacing: 0, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Configure TaskChampion", + style: GoogleFonts.poppins( + color: TaskWarriorColors.white, + fontSize: TaskWarriorFonts.fontSizeLarge, + ), + ), + ], + ), + actions: [ + IconButton( + icon: Icon( + Icons.info, + color: TaskWarriorColors.white, + ), + onPressed: () async { + String url = "https://github.com/its-me-abhishek/ccsync"; + if (!await launchUrl(Uri.parse(url))) { + throw Exception('Could not launch $url'); + } + }, + ), + ], + leading: BackButton( + color: TaskWarriorColors.white, + ), + ), + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.kLightPrimaryBackgroundColor, + body: Padding( + padding: const EdgeInsets.only(left: 20, right: 20), + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10, bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + controller: _encryptionSecretController, + decoration: InputDecoration( + labelText: 'Encryption Secret', + labelStyle: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + border: const OutlineInputBorder(), + ), + ), + const SizedBox(height: 10), + TextField( + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + controller: _clientIdController, + decoration: InputDecoration( + labelText: 'Client ID', + labelStyle: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + border: const OutlineInputBorder(), + ), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () async { + await _saveCredentials(); + // ignore: use_build_context_synchronously + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Credentials saved successfully')), + ); + }, + child: const Text('Save Credentials'), + ), + const SizedBox(height: 10), + Text( + 'Tip: Click on the info icon in the top right corner to get your credentials', + style: TextStyle( + fontSize: 15, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +class CredentialsStorage { + static const String _encryptionSecretKey = 'encryptionSecret'; + static const String _clientIdKey = 'clientId'; + + static Future getEncryptionSecret() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(_encryptionSecretKey); + } + + static Future getClientId() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(_clientIdKey); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b21ae466..9d87193a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -14,6 +14,7 @@ import package_info_plus import path_provider_foundation import permission_handler_apple import shared_preferences_foundation +import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -26,5 +27,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PermissionHandlerPlugin.register(with: registry.registrar(forPlugin: "PermissionHandlerPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index c8a884d7..14d42c7e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -640,6 +640,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.6.0" + in_app_update: + dependency: "direct main" + description: + name: in_app_update + sha256: "489572accaa55b51518b2d64676ca8c3c6d4c989fa53cf718001882237691a3c" + url: "https://pub.dev" + source: hosted + version: "4.2.3" intl: dependency: "direct main" description: @@ -1066,6 +1074,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + url: "https://pub.dev" + source: hosted + version: "2.3.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + url: "https://pub.dev" + source: hosted + version: "2.5.4" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c0e89b43..86c46f43 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: shared_preferences: ^2.2.2 shared_preferences_web: ^2.0.3 sizer: ^2.0.15 + sqflite: ^2.3.3+1 syncfusion_flutter_charts: ^23.2.7 timezone: ^0.9.2 tuple: ^2.0.0 From 121596a41ce92aff2fd4927ed00ddc2959186922 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 06:46:53 +0530 Subject: [PATCH 02/26] feat: add ccsync credentials to drawer --- lib/app/modules/home/views/nav_drawer.dart | 45 ++++++++++++++----- lib/app/utils/language/english_sentences.dart | 4 +- lib/app/utils/language/hindi_sentences.dart | 3 ++ lib/app/utils/language/marathi_sentences.dart | 3 ++ lib/app/utils/language/sentences.dart | 1 + 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index cb9f4c62..35191bf5 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -10,6 +10,7 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.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/theme/app_settings.dart'; class NavDrawer extends StatelessWidget { @@ -41,9 +42,11 @@ class NavDrawer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - SentenceManager(currentLanguage: homeController.selectedLanguage.value) - .sentences - .homePageMenu, + SentenceManager( + currentLanguage: + homeController.selectedLanguage.value) + .sentences + .homePageMenu, style: TextStyle( fontSize: TaskWarriorFonts.fontSizeExtraLarge, fontWeight: TaskWarriorFonts.bold, @@ -115,6 +118,22 @@ class NavDrawer extends StatelessWidget { }, ), ), + Visibility( + visible: homeController.taskchampion.value, + child: NavDrawerMenuItem( + icon: Icons.task_alt, + text: SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.ccsyncCredentials, + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const ManageTaskChampionCreds(), + ), + ); + }, + ), + ), Obx( () => NavDrawerMenuItem( icon: Icons.settings, @@ -162,7 +181,8 @@ class NavDrawer extends StatelessWidget { builder: (BuildContext context) { return Utils.showAlertDialog( title: Text( - SentenceManager(currentLanguage: homeController.selectedLanguage.value) + SentenceManager( + currentLanguage: homeController.selectedLanguage.value) .sentences .homePageExitApp, style: TextStyle( @@ -172,7 +192,8 @@ class NavDrawer extends StatelessWidget { ), ), content: Text( - SentenceManager(currentLanguage: homeController.selectedLanguage.value) + SentenceManager( + currentLanguage: homeController.selectedLanguage.value) .sentences .homePageAreYouSureYouWantToExit, style: TextStyle( @@ -184,9 +205,10 @@ class NavDrawer extends StatelessWidget { actions: [ TextButton( child: Text( - SentenceManager(currentLanguage: homeController.selectedLanguage.value) - .sentences - .homePageCancel, + SentenceManager( + currentLanguage: homeController.selectedLanguage.value) + .sentences + .homePageCancel, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white @@ -199,9 +221,10 @@ class NavDrawer extends StatelessWidget { ), TextButton( child: Text( - SentenceManager(currentLanguage: homeController.selectedLanguage.value) - .sentences - .homePageExit, + SentenceManager( + currentLanguage: homeController.selectedLanguage.value) + .sentences + .homePageExit, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white diff --git a/lib/app/utils/language/english_sentences.dart b/lib/app/utils/language/english_sentences.dart index 197b48df..866e7e4d 100644 --- a/lib/app/utils/language/english_sentences.dart +++ b/lib/app/utils/language/english_sentences.dart @@ -191,7 +191,9 @@ class EnglishSentences extends Sentences { @override String get taskchampionTileDescription => 'Switch to TaskWarrior sync with CCSync or Taskchampion Sync Server'; - @override String get taskchampionTileTitle => 'Taskchampion sync'; + + @override + String get ccsyncCredentials => 'CCync credentials'; } diff --git a/lib/app/utils/language/hindi_sentences.dart b/lib/app/utils/language/hindi_sentences.dart index f34c6c28..87ce9d39 100644 --- a/lib/app/utils/language/hindi_sentences.dart +++ b/lib/app/utils/language/hindi_sentences.dart @@ -195,4 +195,7 @@ class HindiSentences extends Sentences { @override String get taskchampionTileTitle => 'Taskchampion सिंक'; + + @override + String get ccsyncCredentials => 'CCync क्रेडेन्शियल'; } diff --git a/lib/app/utils/language/marathi_sentences.dart b/lib/app/utils/language/marathi_sentences.dart index d5382917..7111029e 100644 --- a/lib/app/utils/language/marathi_sentences.dart +++ b/lib/app/utils/language/marathi_sentences.dart @@ -194,4 +194,7 @@ class MarathiSentences extends Sentences { @override String get taskchampionTileTitle => 'Taskchampion सिंक'; + + @override + String get ccsyncCredentials => 'CCync क्रेडेन्शियल'; } diff --git a/lib/app/utils/language/sentences.dart b/lib/app/utils/language/sentences.dart index 29ef47c0..2ad3b3f0 100644 --- a/lib/app/utils/language/sentences.dart +++ b/lib/app/utils/language/sentences.dart @@ -46,6 +46,7 @@ abstract class Sentences { String get navDrawerReports; String get navDrawerAbout; String get navDrawerSettings; + String get ccsyncCredentials; String get navDrawerExit; String get detailPageDescription; From 3aa305fd38d97f04709a797e28beaa3a60f1e74f Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 07:37:59 +0530 Subject: [PATCH 03/26] feat: add task views for taskc --- .../modules/home/views/home_page_app_bar.dart | 136 ++++++-- .../modules/home/views/home_page_body.dart | 39 ++- lib/app/modules/home/views/show_details.dart | 320 +++++++++++++++++ lib/app/modules/home/views/show_tasks.dart | 327 ++++++++++++++++++ 4 files changed, 789 insertions(+), 33 deletions(-) create mode 100644 lib/app/modules/home/views/show_details.dart create mode 100644 lib/app/modules/home/views/show_tasks.dart 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 b739aa8f..460087e7 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/home/views/home_view.dart'; import 'package:taskwarrior/app/routes/app_pages.dart'; 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/taskchampion.dart'; import 'package:taskwarrior/app/utils/taskserver/taskserver.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; @@ -55,14 +58,69 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { builder: (context) => IconButton( key: controller.refreshKey, icon: Icon(Icons.refresh, color: TaskWarriorColors.white), - onPressed: () { - if (server != null || credentials != null) { - controller.synchronize(context, true); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Obx( - () => Text( + onPressed: () async { + if (controller.taskchampion.value) { + var c = await CredentialsStorage.getClientId(); + var e = await CredentialsStorage.getEncryptionSecret(); + if (c != null && e != null) { + try { + List tasks = await fetchTasks(c, e); + print(tasks.toList()); + await updateTasksInDatabase(tasks); + print('Tasks updated successfully'); + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + const HomeView(), + transitionDuration: Duration.zero, + reverseTransitionDuration: Duration.zero, + )); + } catch (e) { + print('Failed to update tasks: $e'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.ksecondaryBackgroundColor + : TaskWarriorColors.kLightSecondaryBackgroundColor, + content: Text( + SentenceManager( + currentLanguage: + controller.selectedLanguage.value) + .sentences + .homePageTaskWarriorNotConfigured, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + action: SnackBarAction( + label: SentenceManager( + currentLanguage: + controller.selectedLanguage.value) + .sentences + .homePageSetup, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + const ManageTaskChampionCreds(), + )).then((value) {}); + }, + textColor: TaskWarriorColors.purple, + ), + ), + ); + } + } else if (c == null || e == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.ksecondaryBackgroundColor + : TaskWarriorColors.kLightSecondaryBackgroundColor, + content: Text( SentenceManager( currentLanguage: controller.selectedLanguage.value) @@ -74,20 +132,58 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { : TaskWarriorColors.black, ), ), + action: SnackBarAction( + label: SentenceManager( + currentLanguage: + controller.selectedLanguage.value) + .sentences + .homePageSetup, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const ManageTaskChampionCreds(), + )).then((value) {}); + }, + textColor: TaskWarriorColors.purple, + ), ), - action: SnackBarAction( - label: SentenceManager( - currentLanguage: - controller.selectedLanguage.value) - .sentences - .homePageSetup, - onPressed: () { - Get.toNamed(Routes.MANAGE_TASK_SERVER); - }, - textColor: TaskWarriorColors.purple, + ); + } + } else { + if (server != null || credentials != null) { + controller.synchronize(context, true); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Obx( + () => Text( + SentenceManager( + currentLanguage: + controller.selectedLanguage.value) + .sentences + .homePageTaskWarriorNotConfigured, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ), + action: SnackBarAction( + label: SentenceManager( + currentLanguage: + controller.selectedLanguage.value) + .sentences + .homePageSetup, + onPressed: () { + Get.toNamed(Routes.MANAGE_TASK_SERVER); + }, + textColor: TaskWarriorColors.purple, + ), ), - ), - ); + ); + } } }, ), diff --git a/lib/app/modules/home/views/home_page_body.dart b/lib/app/modules/home/views/home_page_body.dart index 404cf341..7341270c 100644 --- a/lib/app/modules/home/views/home_page_body.dart +++ b/lib/app/modules/home/views/home_page_body.dart @@ -2,6 +2,7 @@ import 'package:double_back_to_close_app/double_back_to_close_app.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:taskwarrior/app/modules/home/views/show_tasks.dart'; import 'package:taskwarrior/app/modules/home/views/tasks_builder.dart'; import 'package:taskwarrior/app/utils/constants/palette.dart'; @@ -88,23 +89,35 @@ class HomePageBody extends StatelessWidget { hintText: 'Search', ), ), - Expanded( - child: Scrollbar( - child: Obx( - () => TasksBuilder( - // darkmode: AppSettings.isDarkMode, - useDelayTask: controller.useDelayTask.value, - taskData: controller.searchedTasks, - pendingFilter: controller.pendingFilter.value, - waitingFilter: controller.waitingFilter.value, - searchVisible: controller.searchVisible.value, - selectedLanguage: controller.selectedLanguage.value, - scrollController: controller.scrollController, - showbtn: controller.showbtn.value, + Visibility( + visible: !controller.taskchampion.value, + child: Expanded( + child: Scrollbar( + child: Obx( + () => TasksBuilder( + // darkmode: AppSettings.isDarkMode, + useDelayTask: controller.useDelayTask.value, + taskData: controller.searchedTasks, + pendingFilter: controller.pendingFilter.value, + waitingFilter: controller.waitingFilter.value, + searchVisible: controller.searchVisible.value, + selectedLanguage: controller.selectedLanguage.value, + scrollController: controller.scrollController, + showbtn: controller.showbtn.value, + ), ), ), ), ), + Visibility( + visible: controller.taskchampion.value, + child: Expanded( + child: Scrollbar( + child: TaskViewBuilder( + pendingFilter: controller.pendingFilter.value, + selectedSort: controller.selectedSort.value, + ), + ))) ], ), ), diff --git a/lib/app/modules/home/views/show_details.dart b/lib/app/modules/home/views/show_details.dart new file mode 100644 index 00000000..55688375 --- /dev/null +++ b/lib/app/modules/home/views/show_details.dart @@ -0,0 +1,320 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; +import 'package:taskwarrior/app/utils/constants/utilites.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class TaskDetails extends StatelessWidget { + final Tasks task; + const TaskDetails({super.key, required this.task}); + + @override + Widget build(BuildContext context) { + String description = task.description; + String project = task.project ?? ''; + String status = task.status; + String priority = task.priority ?? ''; + String due = task.due ?? '-'; + due = _buildDate(due); + bool hasChanges = false; + + return Scaffold( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.kLightPrimaryBackgroundColor, + appBar: AppBar( + foregroundColor: TaskWarriorColors.lightGrey, + backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, + title: Text( + 'Task: ${task.description}', + style: GoogleFonts.poppins(color: TaskWarriorColors.white), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: ListView( + children: [ + _buildEditableDetail(context, 'Description:', description, (value) { + description = value; + hasChanges = true; + }), + _buildEditableDetail(context, 'Project:', project, (value) { + project = value; + hasChanges = true; + }), + _buildSelectableDetail( + context, 'Status:', status, ['pending', 'completed'], (value) { + status = value; + hasChanges = true; + }), + _buildSelectableDetail( + context, 'Priority:', priority, ['H', 'M', 'L'], (value) { + priority = value; + hasChanges = true; + }), + _buildDatePickerDetail(context, 'Due:', due, (value) { + due = value; + hasChanges = true; + }), + _buildDetail('UUID:', task.uuid!), + _buildDetail('Urgency:', task.urgency.toString()), + _buildDetail('End:', _buildDate(task.end)), + _buildDetail('Entry:', _buildDate(task.entry)), + _buildDetail('Modified:', _buildDate(task.modified)), + ], + ), + ), + floatingActionButton: hasChanges + ? FloatingActionButton( + onPressed: () async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + await taskDatabase.saveEditedTaskInDB( + task.uuid!, description, project, status, priority, due); + hasChanges = false; + modifyTaskOnTaskwarrior( + description, project, due, priority, status, task.uuid!); + }, + child: const Icon(Icons.save), + ) + : null, + ); + } + + Widget _buildEditableDetail(BuildContext context, String label, String value, + Function(String) onChanged) { + return InkWell( + onTap: () async { + final result = await _showEditDialog(context, label, value); + if (result != null) { + onChanged(result); + } + }, + child: _buildDetail(label, value), + ); + } + + Widget _buildSelectableDetail(BuildContext context, String label, + String value, List options, Function(String) onChanged) { + return InkWell( + onTap: () async { + final result = await _showSelectDialog(context, label, value, options); + if (result != null) { + onChanged(result); + } + }, + child: _buildDetail(label, value), + ); + } + + Widget _buildDatePickerDetail(BuildContext context, String label, + String value, Function(String) onChanged) { + return InkWell( + onTap: () async { + final DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: value != '-' ? DateTime.parse(value) : DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2101), + builder: (BuildContext context, Widget? child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: AppSettings.isDarkMode + ? ColorScheme.dark( + primary: TaskWarriorColors.white, + onPrimary: TaskWarriorColors.black, + onSurface: TaskWarriorColors.white, + ) + : ColorScheme.light( + primary: TaskWarriorColors.black, + onPrimary: TaskWarriorColors.white, + onSurface: TaskWarriorColors.black, + ), + ), + child: child!, + ); + }, + ); + if (pickedDate != null) { + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime( + value != '-' ? DateTime.parse(value) : DateTime.now()), + ); + if (pickedTime != null) { + final DateTime fullDateTime = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + pickedTime.hour, + pickedTime.minute); + onChanged(DateFormat('yyyy-MM-dd HH:mm:ss').format(fullDateTime)); + } + } + }, + child: _buildDetail(label, value), + ); + } + + Widget _buildDetail(String label, String value) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: AppSettings.isDarkMode + ? TaskWarriorColors.ksecondaryBackgroundColor + : TaskWarriorColors.kLightSecondaryBackgroundColor, + borderRadius: BorderRadius.circular(8.0), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 4.0, + offset: Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.all(16.0), + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightSecondaryTextColor, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + value, + style: TextStyle( + fontSize: 18, + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightSecondaryTextColor, + ), + ), + ), + ], + ), + ); + } + + Future _showEditDialog( + BuildContext context, String label, String initialValue) async { + final TextEditingController controller = + TextEditingController(text: initialValue); + return await showDialog( + context: context, + builder: (context) { + return Utils.showAlertDialog( + title: Text( + 'Edit $label', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + content: TextField( + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + controller: controller, + decoration: InputDecoration( + hintText: 'Enter new $label', + hintStyle: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + 'Cancel', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(controller.text); + }, + child: Text( + 'Save', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + ), + ], + ); + }, + ); + } + + Future _showSelectDialog(BuildContext context, String label, + String initialValue, List options) async { + return await showDialog( + context: context, + builder: (context) { + return Utils.showAlertDialog( + title: Text( + 'Select $label', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: options.map((option) { + return RadioListTile( + title: Text( + option, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + value: option, + groupValue: initialValue, + onChanged: (value) { + Navigator.of(context).pop(value); + }, + ); + }).toList(), + ), + ); + }, + ); + } + + String _buildDate(String? dateString) { + if (dateString == null || dateString.isEmpty || dateString == '-') { + return '-'; + } + + try { + DateTime parsedDate = DateTime.parse(dateString); + return DateFormat('yyyy-MM-dd HH:mm:ss').format(parsedDate); + } catch (e) { + debugPrint('Error parsing date: $dateString'); + return '-'; + } + } +} diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart new file mode 100644 index 00000000..2cbd4448 --- /dev/null +++ b/lib/app/modules/home/views/show_tasks.dart @@ -0,0 +1,327 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/home/views/show_details.dart'; +import 'package:taskwarrior/app/utils/constants/palette.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; +import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class TaskViewBuilder extends StatelessWidget { + const TaskViewBuilder({ + super.key, + this.project, + required this.pendingFilter, + required this.selectedSort, + }); + + final String selectedSort; + final bool pendingFilter; + final String? project; + + Future _loadCredentials( + Function(TaskDatabase, String, String) callback) async { + String uuid = (await CredentialsStorage.getClientId()) ?? ''; + String encryptionSecret = + (await CredentialsStorage.getEncryptionSecret()) ?? ''; + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + callback(taskDatabase, uuid, encryptionSecret); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _loadCredentials((taskDatabase, uuid, encryptionSecret) {}), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else { + return FutureBuilder( + future: _fetchTasks(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return const Center(child: Text('No tasks available')); + } else { + // Sorting tasks by ID in ascending order + List tasks = List.from(snapshot.data!); + tasks.sort((a, b) => b.id.compareTo(a.id)); + + _updateTasksInDatabase(tasks); + _findTasksWithoutUUIDs(); + + tasks = tasks.where((task) { + if (pendingFilter) { + return task.status == 'pending'; + } else { + return task.status == 'completed'; + } + }).toList(); + + // Apply sorting based on selectedSort + tasks.sort((a, b) { + switch (selectedSort) { + case 'Created+': + return a.entry.compareTo(b.entry); + case 'Created-': + return b.entry.compareTo(a.entry); + case 'Modified+': + return a.modified!.compareTo(b.modified!); + case 'Modified-': + return b.modified!.compareTo(a.modified!); + case 'Due till+': + return a.due!.compareTo(b.due!); + case 'Due till-': + return b.due!.compareTo(a.due!); + case 'Priority-': + return b.priority!.compareTo(a.priority!); + case 'Priority+': + return a.priority!.compareTo(b.priority!); + case 'Project+': + return a.project!.compareTo(b.project!); + case 'Project-': + return b.project!.compareTo(a.project!); + case 'Urgency-': + return b.urgency!.compareTo(a.urgency!); + case 'Urgency+': + return a.urgency!.compareTo(b.urgency!); + default: + return 0; + } + }); + + // Filter tasks based on the pendingFilter + tasks = tasks.where((task) { + if (pendingFilter) { + return task.status == 'pending'; + } else { + return task.status == 'completed'; + } + }).toList(); + + return Scaffold( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.kLightPrimaryBackgroundColor, + body: tasks.isEmpty + ? Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + 'Click on the bottom right button to start adding tasks', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: TaskWarriorFonts.fontSizeLarge, + color: AppSettings.isDarkMode + ? TaskWarriorColors + .kLightPrimaryBackgroundColor + : TaskWarriorColors.kprimaryBackgroundColor, + ), + ), + ), + ) + : ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.only( + top: 4, + left: 2, + right: 2, + bottom: MediaQuery.of(context).size.height * 0.1, + ), + itemCount: tasks.length, + itemBuilder: (context, index) { + Tasks task = tasks[index]; + return Slidable( + startActionPane: ActionPane( + motion: const BehindMotion(), + children: [ + SlidableAction( + onPressed: (context) { + _markTaskAsCompleted(task.uuid!); + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + 'Task Marked As Completed. Refresh to view changes!', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors + .kprimaryTextColor + : TaskWarriorColors + .kLightPrimaryTextColor, + ), + ), + ), + ); + }, + icon: Icons.done, + label: "COMPLETE", + backgroundColor: TaskWarriorColors.green, + ), + ], + ), + endActionPane: ActionPane( + motion: const DrawerMotion(), + children: [ + SlidableAction( + onPressed: (context) { + _markTaskAsDeleted(task.uuid!); + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + 'Task Marked As Deleted. Refresh to view changes!', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors + .kprimaryTextColor + : TaskWarriorColors + .kLightPrimaryTextColor, + ), + ), + ), + ); + }, + icon: Icons.delete, + label: "DELETE", + backgroundColor: TaskWarriorColors.red, + ), + ], + ), + child: Card( + color: AppSettings.isDarkMode + ? Palette.kToDark + : TaskWarriorColors.white, + child: InkWell( + splashColor: AppSettings.isDarkMode + ? TaskWarriorColors.black + : TaskWarriorColors.borderColor, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + TaskDetails(task: task), + ), + ), + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: AppSettings.isDarkMode + ? TaskWarriorColors.borderColor + : TaskWarriorColors.black, + ), + color: AppSettings.isDarkMode + ? Palette.kToDark + : TaskWarriorColors.white, + borderRadius: BorderRadius.circular(8.0), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 4.0, + offset: Offset(0, 2), + ), + ], + ), + child: ListTile( + leading: CircleAvatar( + backgroundColor: + _getPriorityColor(task.priority!), + radius: 8, + ), + title: Text( + task.description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + color: AppSettings.isDarkMode + ? TaskWarriorColors + .kLightDialogBackGroundColor + : TaskWarriorColors + .kprimaryBackgroundColor, + ), + ), + subtitle: Text( + 'Urgency: ${task.urgency!.floorToDouble()} | Status: ${task.status}', + style: GoogleFonts.poppins( + color: AppSettings.isDarkMode + ? TaskWarriorColors + .ksecondaryTextColor + : TaskWarriorColors + .kLightSecondaryTextColor, + ), + ), + ), + ), + ), + ), + ); + }, + ), + ); + } + }, + ); + } + }, + ); + } + + Future _fetchTasks() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + return await taskDatabase.fetchTasksFromDatabase(); + } + + void _updateTasksInDatabase(List tasks) async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + // Perform update logic + } + + void _findTasksWithoutUUIDs() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + // Perform find logic + } + + void _markTaskAsCompleted(String uuid) async { + String clientId = (await CredentialsStorage.getClientId()) ?? ''; + String encryptionSecret = + (await CredentialsStorage.getEncryptionSecret()) ?? ''; + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + taskDatabase.markTaskAsCompleted(uuid); + completeTask('email', encryptionSecret, clientId, uuid); + } + + void _markTaskAsDeleted(String uuid) async { + String clientId = (await CredentialsStorage.getClientId()) ?? ''; + String encryptionSecret = + (await CredentialsStorage.getEncryptionSecret()) ?? ''; + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + taskDatabase.markTaskAsDeleted(uuid); + deleteTask('email', encryptionSecret, clientId, uuid); + } + + Color _getPriorityColor(String priority) { + switch (priority) { + case 'H': + return Colors.red; + case 'M': + return Colors.yellow; + case 'L': + return Colors.green; + default: + return Colors.grey; + } + } +} From 5d59d038268d395f74a0da4af7da17936c082a30 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 08:29:15 +0530 Subject: [PATCH 04/26] feat: add task --- .../home/controllers/home_controller.dart | 7 +- .../views/add_task_to_taskc_bottom_sheet.dart | 417 ++++++++++++++++++ .../home_page_floating_action_button.dart | 32 +- lib/app/modules/home/views/home_view.dart | 4 +- 4 files changed, 447 insertions(+), 13 deletions(-) create mode 100644 lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 701a9bf2..248abe68 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -66,6 +66,12 @@ class HomeController extends GetxController { if (Platform.isAndroid) { handleHomeWidgetClicked(); } + _loadTaskChampion(); + } + + Future _loadTaskChampion() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + taskchampion.value = prefs.getBool('taskchampion') ?? false; } void addListenerToScrollController() { @@ -438,7 +444,6 @@ class HomeController extends GetxController { RxBool taskchampion = false.obs; // dialogue box - final formKey = GlobalKey(); final namecontroller = TextEditingController(); var due = Rxn(); diff --git a/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart b/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart new file mode 100644 index 00000000..30d6a9c8 --- /dev/null +++ b/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart @@ -0,0 +1,417 @@ +// ignore_for_file: use_build_context_synchronously, file_names +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class AddTaskToTaskcBottomSheet extends StatefulWidget { + const AddTaskToTaskcBottomSheet({super.key}); + + @override + State createState() => + _AddTaskToTaskcBottomSheetState(); +} + +class _AddTaskToTaskcBottomSheetState extends State { + final formKey = GlobalKey(); + final namecontroller = TextEditingController(); + DateTime? due; + String dueString = ''; + String priority = 'M'; + final projectController = TextEditingController(); + String project = ''; + bool inThePast = false; + bool change24hr = false; + late TaskDatabase taskdb; + + Future checkto24hr() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + change24hr = prefs.getBool('24hourformate') ?? false; + }); + } + + @override + void initState() { + super.initState(); + checkto24hr(); + taskdb = TaskDatabase(); + taskdb.open(); + } + + @override + void dispose() { + projectController.dispose(); + namecontroller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + const title = 'Add Task'; + return Scaffold( + backgroundColor: Colors.transparent, + body: Center( + child: SingleChildScrollView( + child: AlertDialog( + surfaceTintColor: AppSettings.isDarkMode + ? TaskWarriorColors.kdialogBackGroundColor + : TaskWarriorColors.kLightDialogBackGroundColor, + shadowColor: AppSettings.isDarkMode + ? TaskWarriorColors.kdialogBackGroundColor + : TaskWarriorColors.kLightDialogBackGroundColor, + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kdialogBackGroundColor + : TaskWarriorColors.kLightDialogBackGroundColor, + title: Center( + child: Text( + title, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ), + content: Form( + key: formKey, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.8, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + buildName(), + const SizedBox(height: 8), + buildProject(), + const SizedBox(height: 12), + buildDueDate(context), + const SizedBox(height: 8), + buildPriority(), + ], + ), + ), + ), + actions: [ + buildCancelButton(context), + buildAddButton(context), + ], + ), + ), + ), + ); + } + + Widget buildName() => TextFormField( + autofocus: true, + controller: namecontroller, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + decoration: InputDecoration( + hintText: 'Enter Task', + hintStyle: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + validator: (name) => name != null && name.isEmpty + ? 'You cannot leave this field empty!' + : null, + ); + + Widget buildProject() => TextFormField( + autofocus: true, + controller: projectController, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + decoration: InputDecoration( + hintText: 'Enter Project', + hintStyle: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ); + + Widget buildDueDate(BuildContext context) => Row( + children: [ + Text( + "Due : ", + style: GoogleFonts.poppins( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + fontWeight: TaskWarriorFonts.bold, + height: 3.3, + ), + ), + Expanded( + child: GestureDetector( + child: TextFormField( + style: inThePast + ? TextStyle(color: TaskWarriorColors.red) + : TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + readOnly: true, + controller: TextEditingController( + text: (due != null) ? dueString : null, + ), + decoration: InputDecoration( + hintText: 'Select due date', + hintStyle: inThePast + ? TextStyle(color: TaskWarriorColors.red) + : TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + onTap: () async { + var date = await showDatePicker( + builder: (BuildContext context, Widget? child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: AppSettings.isDarkMode + ? ColorScheme( + brightness: Brightness.dark, + primary: TaskWarriorColors.white, + onPrimary: TaskWarriorColors.black, + secondary: TaskWarriorColors.black, + onSecondary: TaskWarriorColors.white, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.black, + surface: TaskWarriorColors.black, + onSurface: TaskWarriorColors.white, + ) + : ColorScheme( + brightness: Brightness.light, + primary: TaskWarriorColors.black, + onPrimary: TaskWarriorColors.white, + secondary: TaskWarriorColors.white, + onSecondary: TaskWarriorColors.black, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.white, + surface: TaskWarriorColors.white, + onSurface: TaskWarriorColors.black, + ), + ), + child: child!, + ); + }, + fieldHintText: "Month/Date/Year", + context: context, + initialDate: due ?? DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime(2037, 12, 31), + ); + if (date != null) { + var time = await showTimePicker( + builder: (BuildContext context, Widget? child) { + return Theme( + data: Theme.of(context).copyWith( + textTheme: const TextTheme(), + colorScheme: AppSettings.isDarkMode + ? ColorScheme( + brightness: Brightness.dark, + primary: TaskWarriorColors.white, + onPrimary: TaskWarriorColors.black, + secondary: TaskWarriorColors.black, + onSecondary: TaskWarriorColors.white, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.black, + surface: TaskWarriorColors.black, + onSurface: TaskWarriorColors.white, + ) + : ColorScheme( + brightness: Brightness.light, + primary: TaskWarriorColors.black, + onPrimary: TaskWarriorColors.white, + secondary: TaskWarriorColors.white, + onSecondary: TaskWarriorColors.black, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.white, + surface: TaskWarriorColors.white, + onSurface: TaskWarriorColors.black, + ), + ), + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: change24hr, + ), + child: child!), + ); + }, + context: context, + initialTime: + TimeOfDay.fromDateTime(due ?? DateTime.now()), + ); + if (time != null) { + var dateTime = date.add( + Duration( + hours: time.hour, + minutes: time.minute, + ), + ); + due = dateTime.toUtc(); + dueString = DateFormat("yyyy-MM-dd").format(dateTime); + if (dateTime.isBefore(DateTime.now())) { + setState(() { + inThePast = true; + }); + + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + "The selected time is in the past.", + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor, + ), + ), + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.ksecondaryBackgroundColor + : TaskWarriorColors + .kLightSecondaryBackgroundColor, + duration: const Duration(seconds: 2))); + } else { + setState(() { + inThePast = false; + }); + } + } + } + }, + ), + ), + ), + ], + ); + + Widget buildPriority() => Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Priority : ', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + textAlign: TextAlign.left, + ), + DropdownButton( + dropdownColor: AppSettings.isDarkMode + ? TaskWarriorColors.kdialogBackGroundColor + : TaskWarriorColors.kLightDialogBackGroundColor, + value: priority, + elevation: 16, + style: GoogleFonts.poppins( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + underline: Container( + height: 1.5, + color: AppSettings.isDarkMode + ? TaskWarriorColors.kdialogBackGroundColor + : TaskWarriorColors.kLightDialogBackGroundColor, + ), + onChanged: (String? newValue) { + setState(() { + priority = newValue!; + }); + }, + items: ['H', 'M', 'L', 'None'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(' $value'), + ); + }).toList(), + ) + ], + ), + ], + ); + + Widget buildCancelButton(BuildContext context) => TextButton( + child: Text( + 'Cancel', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + onPressed: () => Navigator.of(context).pop("cancel"), + ); + + Widget buildAddButton(BuildContext context) { + return TextButton( + child: Text( + "Add", + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + onPressed: () async { + if (formKey.currentState!.validate()) { + var task = Tasks( + description: namecontroller.text, + status: 'pending', + priority: priority, + entry: DateTime.now().toIso8601String(), + id: 0, + project: projectController.text, + uuid: '', + urgency: 0, + due: dueString, + // dueString.toIso8601String(), + end: '', + modified: 'r'); + await taskdb.insertTask(task); + namecontroller.text = ''; + due = null; + priority = 'M'; + project = ''; + setState(() {}); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Task Added Successfully!', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor, + ), + ), + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.ksecondaryBackgroundColor + : TaskWarriorColors.kLightSecondaryBackgroundColor, + duration: const Duration(seconds: 2))); + Navigator.of(context).pop(); + } + }, + ); + } +} diff --git a/lib/app/modules/home/views/home_page_floating_action_button.dart b/lib/app/modules/home/views/home_page_floating_action_button.dart index 2ddf18c7..961b13b7 100644 --- a/lib/app/modules/home/views/home_page_floating_action_button.dart +++ b/lib/app/modules/home/views/home_page_floating_action_button.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:taskwarrior/app/modules/home/views/add_task_bottom_sheet.dart'; +import 'package:taskwarrior/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; @@ -9,7 +10,7 @@ import '../controllers/home_controller.dart'; class HomePageFloatingActionButton extends StatelessWidget { final HomeController controller; - const HomePageFloatingActionButton({required this.controller,super.key}); + const HomePageFloatingActionButton({required this.controller, super.key}); @override Widget build(BuildContext context) { @@ -28,16 +29,25 @@ class HomePageFloatingActionButton extends StatelessWidget { : TaskWarriorColors.white, ), ), - onPressed: () => showDialog( - context: context, - builder: (context) => AddTaskBottomSheet( - homeController: controller, - ), - ).then((value) { - if (controller.isSyncNeeded.value && value != "cancel") { - controller.isNeededtoSyncOnStart(context); - } - }) + onPressed: () => (controller.taskchampion.value) + ? (showDialog( + context: context, + builder: (context) => const AddTaskToTaskcBottomSheet(), + ).then((value) { + if (controller.isSyncNeeded.value && value != "cancel") { + controller.isNeededtoSyncOnStart(context); + } + })) + : (showDialog( + context: context, + builder: (context) => AddTaskBottomSheet( + homeController: controller, + ), + ).then((value) { + if (controller.isSyncNeeded.value && value != "cancel") { + controller.isNeededtoSyncOnStart(context); + } + })) // .then((value) { // // print(value); diff --git a/lib/app/modules/home/views/home_view.dart b/lib/app/modules/home/views/home_view.dart index 744a13c5..81dcc95a 100644 --- a/lib/app/modules/home/views/home_view.dart +++ b/lib/app/modules/home/views/home_view.dart @@ -50,7 +50,9 @@ class HomeView extends GetView { ? TaskWarriorColors.kprimaryBackgroundColor : TaskWarriorColors.kLightPrimaryBackgroundColor, drawer: NavDrawer(homeController: controller), - body: HomePageBody(controller: controller), + body: HomePageBody( + controller: controller, + ), endDrawer: Obx( () => FilterDrawer( filters: controller.getFilters(), From 1fd1c7a3628ce1b5a8bc32395b96502588e946d8 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 08:42:58 +0530 Subject: [PATCH 05/26] feat: add reports and fix the broken reports tour --- lib/app/modules/home/views/nav_drawer.dart | 45 ++-- .../controllers/reports_tour_controller.dart | 20 ++ .../reports/views/burn_down_daily_taskc.dart | 177 +++++++++++++++ .../views/burn_down_monthly_taskc.dart | 184 +++++++++++++++ .../reports/views/burn_down_weekly_taskc.dart | 195 ++++++++++++++++ .../modules/reports/views/reports_tour.dart | 112 ++++++++++ .../reports/views/reports_view_taskc.dart | 209 ++++++++++++++++++ .../settings/views/settings_page_body.dart | 1 + 8 files changed, 922 insertions(+), 21 deletions(-) create mode 100644 lib/app/modules/reports/controllers/reports_tour_controller.dart create mode 100644 lib/app/modules/reports/views/burn_down_daily_taskc.dart create mode 100644 lib/app/modules/reports/views/burn_down_monthly_taskc.dart create mode 100644 lib/app/modules/reports/views/burn_down_weekly_taskc.dart create mode 100644 lib/app/modules/reports/views/reports_tour.dart create mode 100644 lib/app/modules/reports/views/reports_view_taskc.dart diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index 35191bf5..fca2f22b 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -85,17 +85,36 @@ class NavDrawer extends StatelessWidget { : TaskWarriorColors.kLightPrimaryBackgroundColor, height: Get.height * 0.03, ), - Obx( - () => NavDrawerMenuItem( - icon: Icons.person_rounded, + Visibility( + visible: homeController.taskchampion.value, + child: NavDrawerMenuItem( + icon: Icons.task_alt, text: SentenceManager( currentLanguage: homeController.selectedLanguage.value, - ).sentences.navDrawerProfile, + ).sentences.ccsyncCredentials, onTap: () { - Get.toNamed(Routes.PROFILE); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const ManageTaskChampionCreds(), + ), + ); }, ), ), + Visibility( + visible: !homeController.taskchampion.value, + child: Obx( + () => NavDrawerMenuItem( + icon: Icons.person_rounded, + text: SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.navDrawerProfile, + onTap: () { + Get.toNamed(Routes.PROFILE); + }, + ), + ), + ), Obx( () => NavDrawerMenuItem( icon: Icons.summarize, @@ -118,22 +137,6 @@ class NavDrawer extends StatelessWidget { }, ), ), - Visibility( - visible: homeController.taskchampion.value, - child: NavDrawerMenuItem( - icon: Icons.task_alt, - text: SentenceManager( - currentLanguage: homeController.selectedLanguage.value, - ).sentences.ccsyncCredentials, - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const ManageTaskChampionCreds(), - ), - ); - }, - ), - ), Obx( () => NavDrawerMenuItem( icon: Icons.settings, diff --git a/lib/app/modules/reports/controllers/reports_tour_controller.dart b/lib/app/modules/reports/controllers/reports_tour_controller.dart new file mode 100644 index 00000000..35e3162b --- /dev/null +++ b/lib/app/modules/reports/controllers/reports_tour_controller.dart @@ -0,0 +1,20 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class SaveReportsTour { + Future data = SharedPreferences.getInstance(); + + void saveReportsTourStatus() async { + final value = await data; + value.setBool('reports_tour', true); + } + + Future getReportsTourStatus() async { + final value = await data; + if (value.containsKey('reports_tour')) { + bool? getData = value.getBool('reports_tour'); + return getData!; + } else { + return false; + } + } +} diff --git a/lib/app/modules/reports/views/burn_down_daily_taskc.dart b/lib/app/modules/reports/views/burn_down_daily_taskc.dart new file mode 100644 index 00000000..59a7aa45 --- /dev/null +++ b/lib/app/modules/reports/views/burn_down_daily_taskc.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/models/chart.dart'; +import 'package:taskwarrior/app/modules/reports/views/common_chart_indicator.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/utilites.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class BurnDownDailyTaskc extends StatefulWidget { + const BurnDownDailyTaskc({super.key}); + + @override + State createState() => _BurnDownDailyTaskcState(); +} + +class _BurnDownDailyTaskcState extends State + with TickerProviderStateMixin { + late TaskDatabase taskDatabase; + late TooltipBehavior _dailyBurndownTooltipBehaviour; + Map> dailyInfo = {}; + + @override + void initState() { + super.initState(); + + // Initialize the tooltip behavior for the chart + _dailyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String date = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; + + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Date: $date', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + ), + ), + Text( + 'Pending: $pendingCount', + ), + Text( + 'Completed: $completedCount', + ), + ], + ), + ); + }, + ); + + // Initialize the database and fetch data + taskDatabase = TaskDatabase(); + taskDatabase.open().then((_) { + taskDatabase.fetchTasksFromDatabase().then((tasks) { + setState(() { + // Process the data and update the chart + _processData(tasks); + }); + }); + }); + } + + void _processData(List tasks) { + dailyInfo = {}; + + // Sort tasks by entry date in ascending order + tasks.sort((a, b) => a.entry.compareTo(b.entry)); + + for (var task in tasks) { + final String date = Utils.formatDate(DateTime.parse(task.entry), 'MM-dd'); + + if (dailyInfo.containsKey(date)) { + if (task.status == 'pending') { + dailyInfo[date]!['pending'] = (dailyInfo[date]!['pending'] ?? 0) + 1; + } else if (task.status == 'completed') { + dailyInfo[date]!['completed'] = + (dailyInfo[date]!['completed'] ?? 0) + 1; + } + } else { + dailyInfo[date] = { + 'pending': task.status == 'pending' ? 1 : 0, + 'completed': task.status == 'completed' ? 1 : 0, + }; + } + } + } + + @override + Widget build(BuildContext context) { + double height = MediaQuery.of(context).size.height; // Screen height + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Day - Month', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + fontSize: TaskWarriorFonts.fontSizeSmall, + ), + ), + ), + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + ), + ), + ), + tooltipBehavior: _dailyBurndownTooltipBehaviour, + series: [ + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: dailyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: dailyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], + ), + ), + ), + const CommonChartIndicator( + title: 'Daily Burndown Chart', + ), + ], + ); + } +} diff --git a/lib/app/modules/reports/views/burn_down_monthly_taskc.dart b/lib/app/modules/reports/views/burn_down_monthly_taskc.dart new file mode 100644 index 00000000..3e75115b --- /dev/null +++ b/lib/app/modules/reports/views/burn_down_monthly_taskc.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/models/chart.dart'; +import 'package:taskwarrior/app/modules/reports/views/common_chart_indicator.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/utilites.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class BurnDownMonthlyTaskc extends StatefulWidget { + const BurnDownMonthlyTaskc({super.key}); + + @override + State createState() => _BurnDownMonthlyTaskcState(); +} + +class _BurnDownMonthlyTaskcState extends State + with TickerProviderStateMixin { + late TaskDatabase taskDatabase; + Map> monthlyInfo = {}; + + late TooltipBehavior _weeklyBurndownTooltipBehaviour; + + @override + void initState() { + super.initState(); + + _weeklyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String monthYear = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; + + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Month-Year: $monthYear', + style: const TextStyle( + fontWeight: TaskWarriorFonts.bold, + ), + ), + Text( + 'Pending: $pendingCount', + ), + Text( + 'Completed: $completedCount', + ), + ], + ), + ); + }, + ); + + taskDatabase = TaskDatabase(); + + ///fetch all data from the database + fetchAllData(); + } + + void fetchAllData() async { + List allData = await taskDatabase.fetchTasksFromDatabase(); + if (allData.isNotEmpty) { + sortBurnDownMonthly(allData); + } + } + + void sortBurnDownMonthly(List allData) { + monthlyInfo = {}; + + allData.sort((a, b) => a.entry.compareTo(b.entry)); + + for (int i = 0; i < allData.length; i++) { + final DateTime entryDate = DateTime.parse(allData[i].entry); + final String monthYear = + '${Utils.getMonthName(entryDate.month)} ${entryDate.year}'; + + if (monthlyInfo.containsKey(monthYear)) { + if (allData[i].status == 'pending') { + monthlyInfo[monthYear]!['pending'] = + (monthlyInfo[monthYear]!['pending'] ?? 0) + 1; + } else if (allData[i].status == 'completed') { + monthlyInfo[monthYear]!['completed'] = + (monthlyInfo[monthYear]!['completed'] ?? 0) + 1; + } + } else { + monthlyInfo[monthYear] = { + 'pending': allData[i].status == 'pending' ? 1 : 0, + 'completed': allData[i].status == 'completed' ? 1 : 0, + }; + } + } + + debugPrint("monthlyInfo: $monthlyInfo"); + + // Update the state to refresh the chart + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final double height = MediaQuery.of(context).size.height; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Month - Year', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + ), + ), + ), + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + ), + ), + ), + tooltipBehavior: _weeklyBurndownTooltipBehaviour, + series: [ + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: monthlyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: monthlyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], + ), + ), + ), + const CommonChartIndicator( + title: 'Monthly Burndown Chart', + ) + ], + ); + } +} diff --git a/lib/app/modules/reports/views/burn_down_weekly_taskc.dart b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart new file mode 100644 index 00000000..193600a2 --- /dev/null +++ b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart @@ -0,0 +1,195 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/models/chart.dart'; +import 'package:taskwarrior/app/modules/reports/views/common_chart_indicator.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/utilites.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; + +class BurnDownWeeklyTask extends StatefulWidget { + const BurnDownWeeklyTask({super.key}); + + @override + State createState() => _BurnDownWeeklyTaskState(); +} + +class _BurnDownWeeklyTaskState extends State + with TickerProviderStateMixin { + late TaskDatabase taskDatabase; + late TooltipBehavior _weeklyBurndownTooltipBehaviour; + Map> weeklyInfo = {}; + + @override + void initState() { + super.initState(); + + ///initialize the _weeklyBurndownTooltipBehaviour tooltip behavior + _weeklyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String weekNumber = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; + + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + weekNumber, + style: const TextStyle( + fontWeight: TaskWarriorFonts.bold, + ), + ), + Text( + 'Pending: $pendingCount', + ), + Text( + 'Completed: $completedCount', + ), + ], + ), + ); + }, + ); + + taskDatabase = TaskDatabase(); + + ///fetch all data from the database + fetchAllData(); + } + + void fetchAllData() async { + List allData = await taskDatabase.fetchTasksFromDatabase(); + if (allData.isNotEmpty) { + sortBurnDownWeekly(allData); + } + } + + void sortBurnDownWeekly(List allData) { + // Initialize weeklyInfo map + weeklyInfo = {}; + + // Sort allData by entry date in ascending order + allData.sort((a, b) => a.entry.compareTo(b.entry)); + + ///loop through allData and get the week number + for (int i = 0; i < allData.length; i++) { + final int weekNumber = + Utils.getWeekNumbertoInt(DateTime.parse(allData[i].entry)); + + ///check if weeklyInfo contains the week number + if (weeklyInfo.containsKey(weekNumber)) { + ///check if the status is pending or completed + if (allData[i].status == 'pending') { + ///if the status is pending then add 1 to the pending count + weeklyInfo[weekNumber]!['pending'] = + (weeklyInfo[weekNumber]!['pending'] ?? 0) + 1; + } else if (allData[i].status == 'completed') { + ///if the status is completed then add 1 to the completed count + weeklyInfo[weekNumber]!['completed'] = + (weeklyInfo[weekNumber]!['completed'] ?? 0) + 1; + } + } else { + ///if weeklyInfo does not contain the week number + weeklyInfo[weekNumber] = { + 'pending': allData[i].status == 'pending' ? 1 : 0, + 'completed': allData[i].status == 'completed' ? 1 : 0, + }; + } + } + + debugPrint("weeklyInfo $weeklyInfo"); + + // Update the state to refresh the chart + setState(() {}); + } + + @override + Widget build(BuildContext context) { + double height = MediaQuery.of(context).size.height; // Screen height + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Weeks - Year', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + ), + ), + ), + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + color: AppSettings.isDarkMode ? Colors.white : Colors.black, + fontSize: TaskWarriorFonts.fontSizeSmall, + ), + ), + ), + tooltipBehavior: _weeklyBurndownTooltipBehaviour, + series: [ + ///this is the completed tasks + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: weeklyInfo.entries + .map((entry) => ChartData( + 'Week ${entry.key}', + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + + ///this is the pending tasks + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: weeklyInfo.entries + .map((entry) => ChartData( + 'Week ${entry.key}', + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], + ), + ), + ), + const CommonChartIndicator( + title: 'Weekly Burndown Chart', + ), + ], + ); + } +} diff --git a/lib/app/modules/reports/views/reports_tour.dart b/lib/app/modules/reports/views/reports_tour.dart new file mode 100644 index 00000000..51f17360 --- /dev/null +++ b/lib/app/modules/reports/views/reports_tour.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; + +List reportsDrawer({ + required GlobalKey daily, + required GlobalKey weekly, + required GlobalKey monthly, +}) { + List targets = []; + + // daily + targets.add( + TargetFocus( + keyTarget: daily, + alignSkip: Alignment.topRight, + radius: 10, + shape: ShapeLightFocus.RRect, + contents: [ + TargetContent( + align: ContentAlign.bottom, + builder: (context, controller) { + return Container( + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Access your daily task report here", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: Colors.white, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + + // weekly + targets.add( + TargetFocus( + keyTarget: weekly, + alignSkip: Alignment.topRight, + radius: 10, + shape: ShapeLightFocus.RRect, + contents: [ + TargetContent( + align: ContentAlign.bottom, + builder: (context, controller) { + return Container( + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Access your weekly task reports here", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: Colors.white, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + + // monthly + targets.add( + TargetFocus( + keyTarget: monthly, + alignSkip: Alignment.bottomCenter, + radius: 10, + shape: ShapeLightFocus.RRect, + contents: [ + TargetContent( + align: ContentAlign.bottom, + builder: (context, controller) { + return Container( + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Access your monthly task reports here", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: Colors.white, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + + return targets; +} diff --git a/lib/app/modules/reports/views/reports_view_taskc.dart b/lib/app/modules/reports/views/reports_view_taskc.dart new file mode 100644 index 00000000..277a7c03 --- /dev/null +++ b/lib/app/modules/reports/views/reports_view_taskc.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/reports/controllers/reports_tour_controller.dart'; +import 'package:taskwarrior/app/modules/reports/views/burn_down_daily_taskc.dart'; +import 'package:taskwarrior/app/modules/reports/views/burn_down_monthly_taskc.dart'; +import 'package:taskwarrior/app/modules/reports/views/burn_down_weekly_taskc.dart'; +import 'package:taskwarrior/app/tour/reports_page_tour.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; +import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; + +class ReportsHomeTaskc extends StatefulWidget { + const ReportsHomeTaskc({ + super.key, + }); + + @override + State createState() => _ReportsHomeTaskcState(); +} + +class _ReportsHomeTaskcState extends State + with TickerProviderStateMixin { + late TabController _tabController; + final GlobalKey daily = GlobalKey(); + final GlobalKey weekly = GlobalKey(); + final GlobalKey monthly = GlobalKey(); + + bool isSaved = false; + late TutorialCoachMark tutorialCoachMark; + + int _selectedIndex = 0; + late TaskDatabase taskDatabase; + List allTasks = []; + + void _initReportsTour() { + tutorialCoachMark = TutorialCoachMark( + targets: reportsDrawer( + daily: daily, + weekly: weekly, + monthly: monthly, + ), + colorShadow: TaskWarriorColors.black, + paddingFocus: 10, + opacityShadow: 0.8, + hideSkip: true, + onFinish: () { + SaveReportsTour().saveReportsTourStatus(); + }, + ); + } + + void _showReportsTour() { + Future.delayed( + const Duration(seconds: 2), + () { + SaveReportsTour().getReportsTourStatus().then((value) => { + if (value == false) + { + tutorialCoachMark.show(context: context), + } + else + { + // ignore: avoid_print + print('User has seen this page'), + } + }); + }, + ); + } + + @override + void initState() { + super.initState(); + _initReportsTour(); + _showReportsTour(); + + _tabController = TabController(length: 3, vsync: this); + + // Initialize the database and fetch data + taskDatabase = TaskDatabase(); + taskDatabase.open().then((_) { + taskDatabase.fetchTasksFromDatabase().then((tasks) { + setState(() { + allTasks = tasks; + }); + }); + }); + } + + @override + Widget build(BuildContext context) { + double height = MediaQuery.of(context).size.height; // Screen height + + return Scaffold( + appBar: AppBar( + backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, + title: Text( + 'Reports', + style: GoogleFonts.poppins(color: TaskWarriorColors.white), + ), + leading: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Icon( + Icons.chevron_left, + color: TaskWarriorColors.white, + ), + ), + bottom: PreferredSize( + preferredSize: Size.fromHeight( + height * 0.1), // Adjust the preferred height as needed + child: TabBar( + controller: _tabController, + labelColor: TaskWarriorColors.white, + labelStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.medium, + fontSize: TaskWarriorFonts.fontSizeSmall, + ), + unselectedLabelStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.light, + ), + onTap: (value) { + setState(() { + _selectedIndex = value; + }); + }, + tabs: [ + Tab( + key: daily, + icon: const Icon(Icons.schedule), + text: 'Daily', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + Tab( + key: weekly, + icon: const Icon(Icons.today), + text: 'Weekly', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + Tab( + key: monthly, + icon: const Icon(Icons.date_range), + text: 'Monthly', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + ], + ), + ), + ), + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.white, + body: allTasks.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.heart_broken, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'No Task found', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.medium, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Add a task to see reports', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.light, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ], + ), + ], + ) + : IndexedStack( + index: _selectedIndex, + children: const [ + BurnDownDailyTaskc(), + BurnDownWeeklyTask(), + BurnDownMonthlyTaskc(), + ], + ), + ); + } +} diff --git a/lib/app/modules/settings/views/settings_page_body.dart b/lib/app/modules/settings/views/settings_page_body.dart index 207c1e64..4e1a2043 100644 --- a/lib/app/modules/settings/views/settings_page_body.dart +++ b/lib/app/modules/settings/views/settings_page_body.dart @@ -116,6 +116,7 @@ class SettingsPageBody extends StatelessWidget { controller: controller, ), ), + const Divider(), SettingsPageListTile( title: SentenceManager( currentLanguage: controller.selectedLanguage.value) From 303ca109658bdc73bdb622c74a3d2769a8ed2d49 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 08:44:08 +0530 Subject: [PATCH 06/26] fix: move reports_tour --- lib/app/modules/reports/{views => controllers}/reports_tour.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/app/modules/reports/{views => controllers}/reports_tour.dart (100%) diff --git a/lib/app/modules/reports/views/reports_tour.dart b/lib/app/modules/reports/controllers/reports_tour.dart similarity index 100% rename from lib/app/modules/reports/views/reports_tour.dart rename to lib/app/modules/reports/controllers/reports_tour.dart From 373c8d8588a3841f490ab577917480819ceba045 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 11:25:57 +0530 Subject: [PATCH 07/26] feat: add project filter for taskc --- lib/api_service.dart | 12 ++ .../home/controllers/home_controller.dart | 11 ++ .../home/views/filter_drawer_home_page.dart | 169 +++++++++++------- .../modules/home/views/home_page_body.dart | 1 + .../home/views/project_column_home_page.dart | 27 --- .../home/views/project_column_taskc.dart | 131 ++++++++++++++ lib/app/modules/home/views/show_details.dart | 149 ++++++++++----- lib/app/modules/home/views/show_tasks.dart | 26 ++- lib/app/tour/filter_drawer_tour.dart | 34 ++++ 9 files changed, 408 insertions(+), 152 deletions(-) create mode 100644 lib/app/modules/home/views/project_column_taskc.dart diff --git a/lib/api_service.dart b/lib/api_service.dart index a6b9f668..f6188851 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -473,6 +473,18 @@ class TaskDatabase { }); } + Future> fetchUniqueProjects() async { + var taskDatabase = TaskDatabase(); + await taskDatabase.open(); + await taskDatabase.ensureDatabaseIsOpen(); + + final List> result = await taskDatabase._database! + .rawQuery( + 'SELECT DISTINCT project FROM Tasks WHERE project IS NOT NULL'); + + return result.map((row) => row['project'] as String).toList(); + } + Future> searchTasks(String query) async { final List> maps = await _database!.query( 'tasks', diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 248abe68..167f47e9 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -9,6 +9,7 @@ import 'package:get/get.dart'; import 'package:home_widget/home_widget.dart'; import 'package:loggy/loggy.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:taskwarrior/api_service.dart'; import 'package:taskwarrior/app/models/filters.dart'; import 'package:taskwarrior/app/models/json/task.dart'; @@ -66,9 +67,17 @@ class HomeController extends GetxController { if (Platform.isAndroid) { handleHomeWidgetClicked(); } + getUniqueProjects(); _loadTaskChampion(); } + Future> getUniqueProjects() async { + var taskDatabase = TaskDatabase(); + List uniqueProjects = await taskDatabase.fetchUniqueProjects(); + debugPrint('Unique projects: $uniqueProjects'); + return uniqueProjects; + } + Future _loadTaskChampion() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); taskchampion.value = prefs.getBool('taskchampion') ?? false; @@ -563,6 +572,7 @@ class HomeController extends GetxController { final GlobalKey statusKey = GlobalKey(); final GlobalKey projectsKey = GlobalKey(); + final GlobalKey projectsKeyTaskc = GlobalKey(); final GlobalKey filterTagKey = GlobalKey(); final GlobalKey sortByKey = GlobalKey(); @@ -571,6 +581,7 @@ class HomeController extends GetxController { targets: filterDrawer( statusKey: statusKey, projectsKey: projectsKey, + projectsKeyTaskc: projectsKeyTaskc, filterTagKey: filterTagKey, sortByKey: sortByKey, ), diff --git a/lib/app/modules/home/views/filter_drawer_home_page.dart b/lib/app/modules/home/views/filter_drawer_home_page.dart index 37dd569e..8a447ea6 100644 --- a/lib/app/modules/home/views/filter_drawer_home_page.dart +++ b/lib/app/modules/home/views/filter_drawer_home_page.dart @@ -3,6 +3,7 @@ import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:taskwarrior/app/models/filters.dart'; import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; import 'package:taskwarrior/app/modules/home/views/project_column_home_page.dart'; +import 'package:taskwarrior/app/modules/home/views/project_column_taskc.dart'; import 'package:taskwarrior/app/services/tag_filter.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; @@ -13,7 +14,6 @@ import 'package:taskwarrior/app/utils/theme/app_settings.dart'; class FilterDrawer extends StatelessWidget { final Filters filters; final HomeController homeController; - const FilterDrawer( {required this.filters, required this.homeController, super.key}); @@ -165,81 +165,120 @@ class FilterDrawer extends StatelessWidget { const Divider( color: Color.fromARGB(0, 48, 46, 46), ), - Container( - key: homeController.projectsKey, - width: MediaQuery.of(context).size.width * 1, - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: tileColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: TaskWarriorColors.borderColor, + Visibility( + visible: !homeController.taskchampion.value, + child: Container( + key: homeController.projectsKey, + width: MediaQuery.of(context).size.width * 1, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: tileColor, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: TaskWarriorColors.borderColor, + ), + ), + child: ProjectsColumn( + projects: filters.projects, + projectFilter: filters.projectFilter, + callback: filters.toggleProjectFilter, ), ), - child: ProjectsColumn( - projects: filters.projects, - projectFilter: filters.projectFilter, - callback: filters.toggleProjectFilter, + ), + Visibility( + visible: homeController.taskchampion.value, + child: FutureBuilder>( + future: homeController.getUniqueProjects(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.hasData) { + return Container( + key: homeController.projectsKeyTaskc, + width: MediaQuery.of(context).size.width * 1, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: tileColor, + borderRadius: BorderRadius.circular(8), + border: + Border.all(color: TaskWarriorColors.borderColor), + ), + child: ProjectColumnTaskc( + callback: filters.toggleProjectFilter, + projects: snapshot.data!, + projectFilter: filters.projectFilter, + ), + ); + } else { + return const Center( + child: Text('No projects available.')); + } + }, ), ), const Divider( color: Color.fromARGB(0, 48, 46, 46), ), - Container( - key: homeController.filterTagKey, - width: MediaQuery.of(context).size.width * 1, - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: tileColor, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: TaskWarriorColors.borderColor), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Divider( - color: Color.fromARGB(0, 48, 46, 46), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 0.0), - child: Text( - SentenceManager( - currentLanguage: - homeController.selectedLanguage.value) - .sentences - .filterDrawerFilterTagBy, - // style: GoogleFonts.poppins( - // color: (AppSettings.isDarkMode - // ? TaskWarriorColors.kprimaryTextColor - // : TaskWarriorColors.kLightSecondaryTextColor), - // // - // fontSize: TaskWarriorFonts.fontSizeLarge), - //textAlign: TextAlign.right, - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: AppSettings.isDarkMode - ? TaskWarriorColors.kprimaryTextColor - : TaskWarriorColors.kLightSecondaryTextColor, + Visibility( + visible: !homeController.taskchampion.value, + child: Container( + key: homeController.filterTagKey, + width: MediaQuery.of(context).size.width * 1, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: tileColor, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: TaskWarriorColors.borderColor), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Divider( + color: Color.fromARGB(0, 48, 46, 46), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 0.0), + child: Text( + SentenceManager( + currentLanguage: + homeController.selectedLanguage.value) + .sentences + .filterDrawerFilterTagBy, + // style: GoogleFonts.poppins( + // color: (AppSettings.isDarkMode + // ? TaskWarriorColors.kprimaryTextColor + // : TaskWarriorColors.kLightSecondaryTextColor), + // // + // fontSize: TaskWarriorFonts.fontSizeLarge), + //textAlign: TextAlign.right, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightSecondaryTextColor, + ), ), ), - ), - const Divider( - color: Color.fromARGB(0, 48, 46, 46), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: TagFiltersWrap(filters.tagFilters), - ), - const Divider( - color: Color.fromARGB(0, 48, 46, 46), - ), - ], + const Divider( + color: Color.fromARGB(0, 48, 46, 46), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: TagFiltersWrap(filters.tagFilters), + ), + const Divider( + color: Color.fromARGB(0, 48, 46, 46), + ), + ], + ), ), ), - const Divider( - color: Color.fromARGB(0, 48, 46, 46), + Visibility( + visible: !homeController.taskchampion.value, + child: const Divider( + color: Color.fromARGB(0, 48, 46, 46), + ), ), Container( key: homeController.sortByKey, diff --git a/lib/app/modules/home/views/home_page_body.dart b/lib/app/modules/home/views/home_page_body.dart index 7341270c..e08bcc97 100644 --- a/lib/app/modules/home/views/home_page_body.dart +++ b/lib/app/modules/home/views/home_page_body.dart @@ -116,6 +116,7 @@ class HomePageBody extends StatelessWidget { child: TaskViewBuilder( pendingFilter: controller.pendingFilter.value, selectedSort: controller.selectedSort.value, + project: controller.projectFilter.value, ), ))) ], diff --git a/lib/app/modules/home/views/project_column_home_page.dart b/lib/app/modules/home/views/project_column_home_page.dart index 57cd68d6..a01b2e93 100644 --- a/lib/app/modules/home/views/project_column_home_page.dart +++ b/lib/app/modules/home/views/project_column_home_page.dart @@ -32,13 +32,6 @@ class ProjectsColumn extends StatelessWidget { children: [ Text( "Project : ", - // style: GoogleFonts.poppins( - // fontWeight: TaskWarriorFonts.bold, - // fontSize: TaskWarriorFonts.fontSizeMedium, - // color: AppSettings.isDarkMode - // ? TaskWarriorColors.white - // : TaskWarriorColors.black, - // ), style: TextStyle( fontFamily: FontFamily.poppins, fontWeight: TaskWarriorFonts.bold, @@ -57,12 +50,6 @@ class ProjectsColumn extends StatelessWidget { children: [ Text( projectFilter == "" ? "Not selected" : projectFilter, - // style: GoogleFonts.poppins( - // fontSize: TaskWarriorFonts.fontSizeSmall, - // color: AppSettings.isDarkMode - // ? TaskWarriorColors.white - // : TaskWarriorColors.black, - // ), style: TextStyle( fontFamily: FontFamily.poppins, fontSize: TaskWarriorFonts.fontSizeSmall, @@ -84,13 +71,6 @@ class ProjectsColumn extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ Text("All Projects", - // style: GoogleFonts.poppins( - // fontWeight: TaskWarriorFonts.semiBold, - // fontSize: TaskWarriorFonts.fontSizeSmall, - // color: AppSettings.isDarkMode - // ? TaskWarriorColors.white - // : TaskWarriorColors.black, - // ), style: TextStyle( fontFamily: FontFamily.poppins, fontSize: TaskWarriorFonts.fontSizeSmall, @@ -115,13 +95,6 @@ class ProjectsColumn extends StatelessWidget { children: [ Text( "No Projects Found", - // style: GoogleFonts.poppins( - // color: AppSettings.isDarkMode - // ? TaskWarriorColors.white - // : TaskWarriorColors.black, - // fontSize: TaskWarriorFonts.fontSizeSmall, - // ), - style: TextStyle( fontFamily: FontFamily.poppins, fontSize: TaskWarriorFonts.fontSizeSmall, diff --git a/lib/app/modules/home/views/project_column_taskc.dart b/lib/app/modules/home/views/project_column_taskc.dart new file mode 100644 index 00000000..134a260e --- /dev/null +++ b/lib/app/modules/home/views/project_column_taskc.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; +import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; +import 'package:taskwarrior/app/utils/gen/fonts.gen.dart'; +import 'package:taskwarrior/app/utils/theme/app_settings.dart'; // Import your necessary dependencies + +class ProjectColumnTaskc extends StatelessWidget { + const ProjectColumnTaskc({ + super.key, + required this.projects, + required this.projectFilter, + required this.callback, + }); + + final List projects; + final String projectFilter; + final void Function(String) callback; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "Project : ", + style: TextStyle( + fontFamily: FontFamily.poppins, + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Text( + projectFilter.isEmpty ? "Not selected" : projectFilter, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ), + ), + ], + ), + ), + ProjectTileTaskc( + project: 'All Projects', + projectFilter: projectFilter, + callback: callback), + if (projects.isNotEmpty) + ...projects.map((entry) => ProjectTileTaskc( + project: entry, + projectFilter: projectFilter, + callback: callback, + )) + else + Column( + children: [ + Text( + "No Projects Found", + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + const SizedBox( + height: 10, + ), + ], + ), + ], + ); + } +} + +class ProjectTileTaskc extends StatelessWidget { + const ProjectTileTaskc({ + super.key, + required this.project, + required this.projectFilter, + required this.callback, + }); + + final String project; + final String projectFilter; + final void Function(String) callback; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => callback(project), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Radio( + value: project, + groupValue: projectFilter, + onChanged: (_) => callback(project), + ), + Text( + project, + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + const Spacer(), + ], + ), + ); + } +} diff --git a/lib/app/modules/home/views/show_details.dart b/lib/app/modules/home/views/show_details.dart index 55688375..7f6dd4c9 100644 --- a/lib/app/modules/home/views/show_details.dart +++ b/lib/app/modules/home/views/show_details.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; @@ -6,20 +8,48 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/utilites.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -class TaskDetails extends StatelessWidget { +class TaskDetails extends StatefulWidget { final Tasks task; const TaskDetails({super.key, required this.task}); @override - Widget build(BuildContext context) { - String description = task.description; - String project = task.project ?? ''; - String status = task.status; - String priority = task.priority ?? ''; - String due = task.due ?? '-'; + State createState() => _TaskDetailsState(); +} + +class _TaskDetailsState extends State { + late String description; + late String project; + late String status; + late String priority; + late String due; + late TaskDatabase taskDatabase; + + bool hasChanges = false; + + @override + void initState() { + super.initState(); + description = widget.task.description; + project = widget.task.project!; + status = widget.task.status; + priority = widget.task.priority!; + due = widget.task.due ?? '-'; due = _buildDate(due); - bool hasChanges = false; + setState(() { + taskDatabase = TaskDatabase(); + taskDatabase.open(); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + taskDatabase = TaskDatabase(); + taskDatabase.open(); + } + @override + Widget build(BuildContext context) { return Scaffold( backgroundColor: AppSettings.isDarkMode ? TaskWarriorColors.kprimaryBackgroundColor @@ -28,7 +58,7 @@ class TaskDetails extends StatelessWidget { foregroundColor: TaskWarriorColors.lightGrey, backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, title: Text( - 'Task: ${task.description}', + 'Task: ${widget.task.description}', style: GoogleFonts.poppins(color: TaskWarriorColors.white), ), ), @@ -36,46 +66,63 @@ class TaskDetails extends StatelessWidget { padding: const EdgeInsets.all(16.0), child: ListView( children: [ - _buildEditableDetail(context, 'Description:', description, (value) { - description = value; - hasChanges = true; + _buildEditableDetail('Description:', description, (value) { + setState(() { + description = value; + hasChanges = true; + }); }), - _buildEditableDetail(context, 'Project:', project, (value) { - project = value; - hasChanges = true; + _buildEditableDetail('Project:', project, (value) { + setState(() { + project = value; + hasChanges = true; + }); }), - _buildSelectableDetail( - context, 'Status:', status, ['pending', 'completed'], (value) { - status = value; - hasChanges = true; + _buildSelectableDetail('Status:', status, ['pending', 'completed'], + (value) { + setState(() { + status = value; + hasChanges = true; + }); }), - _buildSelectableDetail( - context, 'Priority:', priority, ['H', 'M', 'L'], (value) { - priority = value; - hasChanges = true; + _buildSelectableDetail('Priority:', priority, ['H', 'M', 'L'], + (value) { + setState(() { + priority = value; + hasChanges = true; + }); }), - _buildDatePickerDetail(context, 'Due:', due, (value) { - due = value; - hasChanges = true; + _buildDatePickerDetail('Due:', due, (value) { + setState(() { + due = value; + hasChanges = true; + }); }), - _buildDetail('UUID:', task.uuid!), - _buildDetail('Urgency:', task.urgency.toString()), - _buildDetail('End:', _buildDate(task.end)), - _buildDetail('Entry:', _buildDate(task.entry)), - _buildDetail('Modified:', _buildDate(task.modified)), + _buildDetail('UUID:', widget.task.uuid!), + _buildDetail('Urgency:', widget.task.urgency.toString()), + _buildDetail('End:', _buildDate(widget.task.end)), + _buildDetail('Entry:', _buildDate(widget.task.entry)), + _buildDetail('Modified:', _buildDate(widget.task.modified)), ], ), ), floatingActionButton: hasChanges ? FloatingActionButton( onPressed: () async { - TaskDatabase taskDatabase = TaskDatabase(); - await taskDatabase.open(); - await taskDatabase.saveEditedTaskInDB( - task.uuid!, description, project, status, priority, due); - hasChanges = false; + await taskDatabase.saveEditedTaskInDB(widget.task.uuid!, + description, project, status, priority, due); + setState(() { + hasChanges = false; + }); modifyTaskOnTaskwarrior( - description, project, due, priority, status, task.uuid!); + description, + project, + due, + priority, + status, + widget.task.uuid!, + ); + // used this for testing }, child: const Icon(Icons.save), ) @@ -83,8 +130,8 @@ class TaskDetails extends StatelessWidget { ); } - Widget _buildEditableDetail(BuildContext context, String label, String value, - Function(String) onChanged) { + Widget _buildEditableDetail( + String label, String value, Function(String) onChanged) { return InkWell( onTap: () async { final result = await _showEditDialog(context, label, value); @@ -96,8 +143,8 @@ class TaskDetails extends StatelessWidget { ); } - Widget _buildSelectableDetail(BuildContext context, String label, - String value, List options, Function(String) onChanged) { + Widget _buildSelectableDetail(String label, String value, + List options, Function(String) onChanged) { return InkWell( onTap: () async { final result = await _showSelectDialog(context, label, value, options); @@ -109,8 +156,8 @@ class TaskDetails extends StatelessWidget { ); } - Widget _buildDatePickerDetail(BuildContext context, String label, - String value, Function(String) onChanged) { + Widget _buildDatePickerDetail( + String label, String value, Function(String) onChanged) { return InkWell( onTap: () async { final DateTime? pickedDate = await showDatePicker( @@ -122,14 +169,26 @@ class TaskDetails extends StatelessWidget { return Theme( data: Theme.of(context).copyWith( colorScheme: AppSettings.isDarkMode - ? ColorScheme.dark( + ? ColorScheme( + brightness: Brightness.dark, primary: TaskWarriorColors.white, onPrimary: TaskWarriorColors.black, + secondary: TaskWarriorColors.black, + onSecondary: TaskWarriorColors.white, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.black, + surface: TaskWarriorColors.black, onSurface: TaskWarriorColors.white, ) - : ColorScheme.light( + : ColorScheme( + brightness: Brightness.light, primary: TaskWarriorColors.black, onPrimary: TaskWarriorColors.white, + secondary: TaskWarriorColors.white, + onSecondary: TaskWarriorColors.black, + error: TaskWarriorColors.red, + onError: TaskWarriorColors.white, + surface: TaskWarriorColors.white, onSurface: TaskWarriorColors.black, ), ), diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart index 2cbd4448..bde8de80 100644 --- a/lib/app/modules/home/views/show_tasks.dart +++ b/lib/app/modules/home/views/show_tasks.dart @@ -41,7 +41,7 @@ class TaskViewBuilder extends StatelessWidget { } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { - return FutureBuilder( + return FutureBuilder>( future: _fetchTasks(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { @@ -51,12 +51,17 @@ class TaskViewBuilder extends StatelessWidget { } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('No tasks available')); } else { - // Sorting tasks by ID in ascending order + // Filter tasks based on the selected project List tasks = List.from(snapshot.data!); - tasks.sort((a, b) => b.id.compareTo(a.id)); + if (project != 'All Projects') { + tasks = + tasks.where((task) => task.project == project).toList(); + } else { + tasks = List.from(snapshot.data!); + } - _updateTasksInDatabase(tasks); - _findTasksWithoutUUIDs(); + // Apply other filters and sorting + tasks.sort((a, b) => b.id.compareTo(a.id)); tasks = tasks.where((task) { if (pendingFilter) { @@ -98,15 +103,6 @@ class TaskViewBuilder extends StatelessWidget { } }); - // Filter tasks based on the pendingFilter - tasks = tasks.where((task) { - if (pendingFilter) { - return task.status == 'pending'; - } else { - return task.status == 'completed'; - } - }).toList(); - return Scaffold( backgroundColor: AppSettings.isDarkMode ? TaskWarriorColors.kprimaryBackgroundColor @@ -274,7 +270,7 @@ class TaskViewBuilder extends StatelessWidget { ); } - Future _fetchTasks() async { + Future> _fetchTasks() async { TaskDatabase taskDatabase = TaskDatabase(); await taskDatabase.open(); return await taskDatabase.fetchTasksFromDatabase(); diff --git a/lib/app/tour/filter_drawer_tour.dart b/lib/app/tour/filter_drawer_tour.dart index 67eb4655..4ddcfaaf 100644 --- a/lib/app/tour/filter_drawer_tour.dart +++ b/lib/app/tour/filter_drawer_tour.dart @@ -6,6 +6,7 @@ import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; List filterDrawer({ required GlobalKey statusKey, required GlobalKey projectsKey, + required GlobalKey projectsKeyTaskc, required GlobalKey filterTagKey, required GlobalKey sortByKey, }) { @@ -77,6 +78,39 @@ List filterDrawer({ ), ); + // projectsKeyTaskc + targets.add( + TargetFocus( + keyTarget: projectsKeyTaskc, + alignSkip: Alignment.topRight, + radius: 10, + shape: ShapeLightFocus.RRect, + contents: [ + TargetContent( + align: ContentAlign.top, + builder: (context, controller) { + return Container( + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Filter tasks based on the projects", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: TaskWarriorColors.white, + ), + ), + ], + ), + ); + }, + ), + ], + ), + ); + // filterTagByKey targets.add( TargetFocus( From 07d9982291b9f3a9d11adbff36ceecf744366230 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:28:02 +0530 Subject: [PATCH 08/26] feat: add refresh tasks to the refresh button --- .../modules/home/views/home_page_app_bar.dart | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) 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 460087e7..763dfa4f 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -64,18 +64,22 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { var e = await CredentialsStorage.getEncryptionSecret(); if (c != null && e != null) { try { - List tasks = await fetchTasks(c, e); - print(tasks.toList()); - await updateTasksInDatabase(tasks); - print('Tasks updated successfully'); - Navigator.pushReplacement( - context, - PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => - const HomeView(), - transitionDuration: Duration.zero, - reverseTransitionDuration: Duration.zero, - )); + // List tasks = await fetchTasks(c, e); + // print( + // '///////////////////////////////////////////////////////////'); + // print(tasks.toList()); + // await updateTasksInDatabase(tasks); + // print('Tasks updated successfully'); + // controller.fetchTasksFromDB(); + controller.refreshTasks(c, e); + // Navigator.pushReplacement( + // context, + // PageRouteBuilder( + // pageBuilder: (context, animation1, animation2) => + // const HomeView(), + // transitionDuration: Duration.zero, + // reverseTransitionDuration: Duration.zero, + // )); } catch (e) { print('Failed to update tasks: $e'); ScaffoldMessenger.of(context).showSnackBar( From 0942a26fd88ad547610fd4084dd89bd2de3896e7 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:34:12 +0530 Subject: [PATCH 09/26] fix: api service --- lib/api_service.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/api_service.dart b/lib/api_service.dart index f6188851..fbd699f3 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -67,12 +67,12 @@ class Tasks { } } -String baseUrl = 'YOUR_IP'; -String origin = 'CONTAINER_ORIGIN'; +String baseUrl = 'http://YOUR_IP:8000'; +String origin = 'http://localhost:8080'; Future> fetchTasks(String uuid, String encryptionSecret) async { String url = - '$baseUrl/tasks?email=$origin&origin=$origin&UUID=$uuid&encryptionSecret=$encryptionSecret'; + '$baseUrl/tasks?email=email&origin=$origin&UUID=$uuid&encryptionSecret=$encryptionSecret'; var response = await http.get(Uri.parse(url), headers: { "Content-Type": "application/json", From 5193d4476458897e8c1ee4e0492952f33ed5a536 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:34:38 +0530 Subject: [PATCH 10/26] feat: load tasks from Db anad sync tasks on init if enabled --- .../home/controllers/home_controller.dart | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 167f47e9..8532008a 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -23,6 +23,7 @@ import 'package:taskwarrior/app/tour/filter_drawer_tour.dart'; import 'package:taskwarrior/app/tour/home_page_tour.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/language/supported_language.dart'; +import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; import 'package:taskwarrior/app/utils/taskfunctions/comparator.dart'; import 'package:taskwarrior/app/utils/taskfunctions/projects.dart'; import 'package:taskwarrior/app/utils/taskfunctions/query.dart'; @@ -50,6 +51,7 @@ class HomeController extends GetxController { final Rx selectedLanguage = SupportedLanguage.english.obs; final ScrollController scrollController = ScrollController(); final RxBool showbtn = false.obs; + var tasks = [].obs; @override void onInit() { @@ -69,6 +71,7 @@ class HomeController extends GetxController { } getUniqueProjects(); _loadTaskChampion(); + fetchTasksFromDB(); } Future> getUniqueProjects() async { @@ -78,6 +81,28 @@ class HomeController extends GetxController { return uniqueProjects; } + Future deleteAllTasksInDB() async { + var taskDatabase = TaskDatabase(); + await taskDatabase.deleteAllTasksInDB(); + debugPrint('Deleted all tasks from db'); + } + + Future refreshTasks(String clientId, String encryptionSecret) async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + List tasksFromServer = await fetchTasks(clientId, encryptionSecret); + await updateTasksInDatabase(tasksFromServer); + List fetchedTasks = await taskDatabase.fetchTasksFromDatabase(); + tasks.value = fetchedTasks; + } + + Future fetchTasksFromDB() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + List fetchedTasks = await taskDatabase.fetchTasksFromDatabase(); + tasks.value = fetchedTasks; + } + Future _loadTaskChampion() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); taskchampion.value = prefs.getBool('taskchampion') ?? false; @@ -440,9 +465,12 @@ class HomeController extends GetxController { final SharedPreferences prefs = await SharedPreferences.getInstance(); bool? value; value = prefs.getBool('sync-onStart') ?? false; - + String? clientId, encryptionSecret; + clientId = await CredentialsStorage.getClientId(); + encryptionSecret = await CredentialsStorage.getEncryptionSecret(); if (value) { synchronize(context, false); + refreshTasks(clientId!, encryptionSecret!); } else {} } From d6a0a26579a76700692f2c47cb159da5bc0c92ff Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:35:09 +0530 Subject: [PATCH 11/26] fix: update tasks without reload --- lib/app/modules/home/views/show_tasks.dart | 430 ++++++++++----------- 1 file changed, 196 insertions(+), 234 deletions(-) diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart index bde8de80..a2067f7b 100644 --- a/lib/app/modules/home/views/show_tasks.dart +++ b/lib/app/modules/home/views/show_tasks.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; import 'package:taskwarrior/app/modules/home/views/show_details.dart'; import 'package:taskwarrior/app/utils/constants/palette.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; @@ -29,245 +31,217 @@ class TaskViewBuilder extends StatelessWidget { TaskDatabase taskDatabase = TaskDatabase(); await taskDatabase.open(); callback(taskDatabase, uuid, encryptionSecret); + // taskDatabase.printDatabaseContents(); } @override Widget build(BuildContext context) { - return FutureBuilder( - future: _loadCredentials((taskDatabase, uuid, encryptionSecret) {}), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else { - return FutureBuilder>( - future: _fetchTasks(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text('No tasks available')); - } else { - // Filter tasks based on the selected project - List tasks = List.from(snapshot.data!); - if (project != 'All Projects') { - tasks = - tasks.where((task) => task.project == project).toList(); - } else { - tasks = List.from(snapshot.data!); - } + final HomeController taskController = Get.find(); + + return Obx(() { + List tasks = List.from(taskController.tasks); - // Apply other filters and sorting - tasks.sort((a, b) => b.id.compareTo(a.id)); + // Filter tasks based on the selected project + if (project != 'All Projects') { + tasks = tasks.where((task) => task.project == project).toList(); + } else { + tasks = List.from(tasks); + } - tasks = tasks.where((task) { - if (pendingFilter) { - return task.status == 'pending'; - } else { - return task.status == 'completed'; - } - }).toList(); + // Apply other filters and sorting + tasks.sort((a, b) => b.id.compareTo(a.id)); - // Apply sorting based on selectedSort - tasks.sort((a, b) { - switch (selectedSort) { - case 'Created+': - return a.entry.compareTo(b.entry); - case 'Created-': - return b.entry.compareTo(a.entry); - case 'Modified+': - return a.modified!.compareTo(b.modified!); - case 'Modified-': - return b.modified!.compareTo(a.modified!); - case 'Due till+': - return a.due!.compareTo(b.due!); - case 'Due till-': - return b.due!.compareTo(a.due!); - case 'Priority-': - return b.priority!.compareTo(a.priority!); - case 'Priority+': - return a.priority!.compareTo(b.priority!); - case 'Project+': - return a.project!.compareTo(b.project!); - case 'Project-': - return b.project!.compareTo(a.project!); - case 'Urgency-': - return b.urgency!.compareTo(a.urgency!); - case 'Urgency+': - return a.urgency!.compareTo(b.urgency!); - default: - return 0; - } - }); + tasks = tasks.where((task) { + if (pendingFilter) { + return task.status == 'pending'; + } else { + return task.status == 'completed'; + } + }).toList(); + // Apply sorting based on selectedSort + tasks.sort((a, b) { + switch (selectedSort) { + case 'Created+': + return a.entry.compareTo(b.entry); + case 'Created-': + return b.entry.compareTo(a.entry); + case 'Modified+': + return a.modified!.compareTo(b.modified!); + case 'Modified-': + return b.modified!.compareTo(a.modified!); + case 'Due till+': + return a.due!.compareTo(b.due!); + case 'Due till-': + return b.due!.compareTo(a.due!); + case 'Priority-': + return b.priority!.compareTo(a.priority!); + case 'Priority+': + return a.priority!.compareTo(b.priority!); + case 'Project+': + return a.project!.compareTo(b.project!); + case 'Project-': + return b.project!.compareTo(a.project!); + case 'Urgency-': + return b.urgency!.compareTo(a.urgency!); + case 'Urgency+': + return a.urgency!.compareTo(b.urgency!); + default: + return 0; + } + }); - return Scaffold( - backgroundColor: AppSettings.isDarkMode - ? TaskWarriorColors.kprimaryBackgroundColor - : TaskWarriorColors.kLightPrimaryBackgroundColor, - body: tasks.isEmpty - ? Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - 'Click on the bottom right button to start adding tasks', - textAlign: TextAlign.center, + return Scaffold( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.kLightPrimaryBackgroundColor, + body: tasks.isEmpty + ? Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + 'Click on the bottom right button to start adding tasks', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: TaskWarriorFonts.fontSizeLarge, + color: AppSettings.isDarkMode + ? TaskWarriorColors.kLightPrimaryBackgroundColor + : TaskWarriorColors.kprimaryBackgroundColor, + ), + ), + ), + ) + : ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.only( + top: 4, + left: 2, + right: 2, + bottom: MediaQuery.of(context).size.height * 0.1, + ), + itemCount: tasks.length, + itemBuilder: (context, index) { + Tasks task = tasks[index]; + return Slidable( + startActionPane: ActionPane( + motion: const BehindMotion(), + children: [ + SlidableAction( + onPressed: (context) { + _markTaskAsCompleted(task.uuid!); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Task Marked As Completed. Refresh to view changes!', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors + .kLightPrimaryTextColor, + ), + ), + ), + ); + }, + icon: Icons.done, + label: "COMPLETE", + backgroundColor: TaskWarriorColors.green, + ), + ], + ), + endActionPane: ActionPane( + motion: const DrawerMotion(), + children: [ + SlidableAction( + onPressed: (context) { + _markTaskAsDeleted(task.uuid!); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Task Marked As Deleted. Refresh to view changes!', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors + .kLightPrimaryTextColor, + ), + ), + ), + ); + }, + icon: Icons.delete, + label: "DELETE", + backgroundColor: TaskWarriorColors.red, + ), + ], + ), + child: Card( + color: AppSettings.isDarkMode + ? Palette.kToDark + : TaskWarriorColors.white, + child: InkWell( + splashColor: AppSettings.isDarkMode + ? TaskWarriorColors.black + : TaskWarriorColors.borderColor, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TaskDetails(task: task), + ), + ), + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: AppSettings.isDarkMode + ? TaskWarriorColors.borderColor + : TaskWarriorColors.black, + ), + color: AppSettings.isDarkMode + ? Palette.kToDark + : TaskWarriorColors.white, + borderRadius: BorderRadius.circular(8.0), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 4.0, + offset: Offset(0, 2), + ), + ], + ), + child: ListTile( + leading: CircleAvatar( + backgroundColor: + _getPriorityColor(task.priority!), + radius: 8, + ), + title: Text( + task.description, + maxLines: 1, + overflow: TextOverflow.ellipsis, style: GoogleFonts.poppins( - fontSize: TaskWarriorFonts.fontSizeLarge, color: AppSettings.isDarkMode ? TaskWarriorColors - .kLightPrimaryBackgroundColor + .kLightDialogBackGroundColor : TaskWarriorColors.kprimaryBackgroundColor, ), ), - ), - ) - : ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.only( - top: 4, - left: 2, - right: 2, - bottom: MediaQuery.of(context).size.height * 0.1, - ), - itemCount: tasks.length, - itemBuilder: (context, index) { - Tasks task = tasks[index]; - return Slidable( - startActionPane: ActionPane( - motion: const BehindMotion(), - children: [ - SlidableAction( - onPressed: (context) { - _markTaskAsCompleted(task.uuid!); - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'Task Marked As Completed. Refresh to view changes!', - style: TextStyle( - color: AppSettings.isDarkMode - ? TaskWarriorColors - .kprimaryTextColor - : TaskWarriorColors - .kLightPrimaryTextColor, - ), - ), - ), - ); - }, - icon: Icons.done, - label: "COMPLETE", - backgroundColor: TaskWarriorColors.green, - ), - ], - ), - endActionPane: ActionPane( - motion: const DrawerMotion(), - children: [ - SlidableAction( - onPressed: (context) { - _markTaskAsDeleted(task.uuid!); - ScaffoldMessenger.of(context) - .showSnackBar( - SnackBar( - content: Text( - 'Task Marked As Deleted. Refresh to view changes!', - style: TextStyle( - color: AppSettings.isDarkMode - ? TaskWarriorColors - .kprimaryTextColor - : TaskWarriorColors - .kLightPrimaryTextColor, - ), - ), - ), - ); - }, - icon: Icons.delete, - label: "DELETE", - backgroundColor: TaskWarriorColors.red, - ), - ], - ), - child: Card( + subtitle: Text( + 'Urgency: ${task.urgency!.floorToDouble()} | Status: ${task.status}', + style: GoogleFonts.poppins( color: AppSettings.isDarkMode - ? Palette.kToDark - : TaskWarriorColors.white, - child: InkWell( - splashColor: AppSettings.isDarkMode - ? TaskWarriorColors.black - : TaskWarriorColors.borderColor, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - TaskDetails(task: task), - ), - ), - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: AppSettings.isDarkMode - ? TaskWarriorColors.borderColor - : TaskWarriorColors.black, - ), - color: AppSettings.isDarkMode - ? Palette.kToDark - : TaskWarriorColors.white, - borderRadius: BorderRadius.circular(8.0), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 4.0, - offset: Offset(0, 2), - ), - ], - ), - child: ListTile( - leading: CircleAvatar( - backgroundColor: - _getPriorityColor(task.priority!), - radius: 8, - ), - title: Text( - task.description, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: GoogleFonts.poppins( - color: AppSettings.isDarkMode - ? TaskWarriorColors - .kLightDialogBackGroundColor - : TaskWarriorColors - .kprimaryBackgroundColor, - ), - ), - subtitle: Text( - 'Urgency: ${task.urgency!.floorToDouble()} | Status: ${task.status}', - style: GoogleFonts.poppins( - color: AppSettings.isDarkMode - ? TaskWarriorColors - .ksecondaryTextColor - : TaskWarriorColors - .kLightSecondaryTextColor, - ), - ), - ), - ), - ), + ? TaskWarriorColors.ksecondaryTextColor + : TaskWarriorColors + .kLightSecondaryTextColor, ), - ); - }, + ), + ), ), - ); - } - }, - ); - } - }, - ); + ), + ), + ); + }, + ), + ); + }); } Future> _fetchTasks() async { @@ -276,18 +250,6 @@ class TaskViewBuilder extends StatelessWidget { return await taskDatabase.fetchTasksFromDatabase(); } - void _updateTasksInDatabase(List tasks) async { - TaskDatabase taskDatabase = TaskDatabase(); - await taskDatabase.open(); - // Perform update logic - } - - void _findTasksWithoutUUIDs() async { - TaskDatabase taskDatabase = TaskDatabase(); - await taskDatabase.open(); - // Perform find logic - } - void _markTaskAsCompleted(String uuid) async { String clientId = (await CredentialsStorage.getClientId()) ?? ''; String encryptionSecret = From 06a71e77a915b593b42766696cd467f522af8b59 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:35:30 +0530 Subject: [PATCH 12/26] feat: delete all tasks from DB --- lib/app/modules/home/views/nav_drawer.dart | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index fca2f22b..56096812 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -101,6 +101,66 @@ class NavDrawer extends StatelessWidget { }, ), ), + Visibility( + visible: homeController.taskchampion.value, + child: NavDrawerMenuItem( + icon: Icons.delete, + text: 'Delete tasks', + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return Utils.showAlertDialog( + title: Text( + 'Delete All Tasks?', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + content: Text( + 'The action is irreversible and will delete all the tasks that are stored locally.', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + actions: [ + TextButton( + child: Text( + 'Cancel', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + }, + ), + TextButton( + child: Text( + 'Confirm', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + onPressed: () { + homeController.deleteAllTasksInDB(); + Navigator.of(context).pop(); // Close the dialog + }, + ), + ], + ); + }, + ); + }), + ), Visibility( visible: !homeController.taskchampion.value, child: Obx( From 92469373d4354df7346aebe1c26885556f52cf37 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 14:56:33 +0530 Subject: [PATCH 13/26] fix: reports tour --- .../reports/controllers/reports_tour.dart | 112 ------------------ .../reports/views/reports_view_taskc.dart | 8 +- 2 files changed, 6 insertions(+), 114 deletions(-) delete mode 100644 lib/app/modules/reports/controllers/reports_tour.dart diff --git a/lib/app/modules/reports/controllers/reports_tour.dart b/lib/app/modules/reports/controllers/reports_tour.dart deleted file mode 100644 index 51f17360..00000000 --- a/lib/app/modules/reports/controllers/reports_tour.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; - -List reportsDrawer({ - required GlobalKey daily, - required GlobalKey weekly, - required GlobalKey monthly, -}) { - List targets = []; - - // daily - targets.add( - TargetFocus( - keyTarget: daily, - alignSkip: Alignment.topRight, - radius: 10, - shape: ShapeLightFocus.RRect, - contents: [ - TargetContent( - align: ContentAlign.bottom, - builder: (context, controller) { - return Container( - alignment: Alignment.center, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Access your daily task report here", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - color: Colors.white, - ), - ), - ], - ), - ); - }, - ), - ], - ), - ); - - // weekly - targets.add( - TargetFocus( - keyTarget: weekly, - alignSkip: Alignment.topRight, - radius: 10, - shape: ShapeLightFocus.RRect, - contents: [ - TargetContent( - align: ContentAlign.bottom, - builder: (context, controller) { - return Container( - alignment: Alignment.center, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Access your weekly task reports here", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - color: Colors.white, - ), - ), - ], - ), - ); - }, - ), - ], - ), - ); - - // monthly - targets.add( - TargetFocus( - keyTarget: monthly, - alignSkip: Alignment.bottomCenter, - radius: 10, - shape: ShapeLightFocus.RRect, - contents: [ - TargetContent( - align: ContentAlign.bottom, - builder: (context, controller) { - return Container( - alignment: Alignment.center, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Access your monthly task reports here", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - color: Colors.white, - ), - ), - ], - ), - ); - }, - ), - ], - ), - ); - - return targets; -} diff --git a/lib/app/modules/reports/views/reports_view_taskc.dart b/lib/app/modules/reports/views/reports_view_taskc.dart index 277a7c03..76f82fd4 100644 --- a/lib/app/modules/reports/views/reports_view_taskc.dart +++ b/lib/app/modules/reports/views/reports_view_taskc.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/reports/controllers/reports_controller.dart'; import 'package:taskwarrior/app/modules/reports/controllers/reports_tour_controller.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_daily_taskc.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_monthly_taskc.dart'; @@ -29,7 +31,7 @@ class _ReportsHomeTaskcState extends State bool isSaved = false; late TutorialCoachMark tutorialCoachMark; - + late ReportsController reportsController; int _selectedIndex = 0; late TaskDatabase taskDatabase; List allTasks = []; @@ -75,7 +77,9 @@ class _ReportsHomeTaskcState extends State super.initState(); _initReportsTour(); _showReportsTour(); - + reportsController = Get.find(); + reportsController.initReportsTour(); + reportsController.showReportsTour(context); _tabController = TabController(length: 3, vsync: this); // Initialize the database and fetch data From 3e493df2347a39f4ab9cd2b752c45d78af15cde5 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 15:01:05 +0530 Subject: [PATCH 14/26] fix: fixed linting issues --- .../controllers/detail_route_controller.dart | 4 +++- .../detailRoute/views/detail_route_view.dart | 2 ++ .../home/controllers/home_controller.dart | 2 +- .../home/views/add_task_bottom_sheet.dart | 11 +++++------ .../home/views/filter_drawer_home_page.dart | 2 ++ .../modules/home/views/home_page_app_bar.dart | 6 ++---- lib/app/modules/home/views/show_tasks.dart | 17 ----------------- .../views/manage_task_server_page_app_bar.dart | 2 ++ .../reports/controllers/reports_controller.dart | 2 ++ .../controllers/settings_controller.dart | 3 ++- ...ettings_page_select_directory_list_tile.dart | 2 ++ .../splash/controllers/splash_controller.dart | 2 ++ lib/app/routes/app_pages.dart | 2 ++ lib/app/routes/app_routes.dart | 2 ++ 14 files changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/app/modules/detailRoute/controllers/detail_route_controller.dart b/lib/app/modules/detailRoute/controllers/detail_route_controller.dart index eb26f4ca..2a536712 100644 --- a/lib/app/modules/detailRoute/controllers/detail_route_controller.dart +++ b/lib/app/modules/detailRoute/controllers/detail_route_controller.dart @@ -1,3 +1,5 @@ +// ignore_for_file: depend_on_referenced_packages + import 'package:built_collection/built_collection.dart'; import 'package:get/get.dart'; import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; @@ -46,7 +48,7 @@ class DetailRouteController extends GetxController { // 'status': controller.modify.draft.status, // 'entry': controller.modify.draft.entry, // 'modified': controller.modify.draft.modified, - // 'start': controller.modify.draft.start, + // 'start': controller.modify.draft.start, // 'end': controller.modify.draft.end, // 'due': controller.dueValue.value, // 'wait': controller.modify.draft.wait, diff --git a/lib/app/modules/detailRoute/views/detail_route_view.dart b/lib/app/modules/detailRoute/views/detail_route_view.dart index a947f48a..5568d7c6 100644 --- a/lib/app/modules/detailRoute/views/detail_route_view.dart +++ b/lib/app/modules/detailRoute/views/detail_route_view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: depend_on_referenced_packages, deprecated_member_use + import 'package:built_collection/built_collection.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 8532008a..6a4488b8 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -548,7 +548,7 @@ class HomeController extends GetxController { void initLanguageAndDarkMode() { isDarkModeOn.value = AppSettings.isDarkMode; selectedLanguage.value = AppSettings.selectedLanguage; - print("called and value is${isDarkModeOn.value}"); + // print("called and value is${isDarkModeOn.value}"); } final addKey = GlobalKey(); diff --git a/lib/app/modules/home/views/add_task_bottom_sheet.dart b/lib/app/modules/home/views/add_task_bottom_sheet.dart index 6a8df7ce..13b598b1 100644 --- a/lib/app/modules/home/views/add_task_bottom_sheet.dart +++ b/lib/app/modules/home/views/add_task_bottom_sheet.dart @@ -269,7 +269,7 @@ class AddTaskBottomSheet extends StatelessWidget { initialTime: TimeOfDay.fromDateTime( homeController.due.value ?? DateTime.now()), ); - print("date$date Time : $time"); + // print("date$date Time : $time"); if (time != null) { var dateTime = date.add( Duration( @@ -277,13 +277,13 @@ class AddTaskBottomSheet extends StatelessWidget { minutes: time.minute, ), ); - print(dateTime); + // print(dateTime); homeController.due.value = dateTime.toUtc(); - print("due value ${homeController.due}"); + // print("due value ${homeController.due}"); homeController.dueString.value = DateFormat("dd-MM-yyyy HH:mm").format(dateTime); - print(homeController.dueString.value); + // print(homeController.dueString.value); if (dateTime.isBefore(DateTime.now())) { //Try changing the color. in the settings and Due display. @@ -391,7 +391,6 @@ class AddTaskBottomSheet extends StatelessWidget { ); Widget buildAddButton(BuildContext context) { - return TextButton( child: Text( "Add", @@ -402,7 +401,7 @@ class AddTaskBottomSheet extends StatelessWidget { ), ), onPressed: () async { - print(homeController.formKey.currentState); + // print(homeController.formKey.currentState); if (homeController.formKey.currentState!.validate()) { try { var task = taskParser(homeController.namecontroller.text) diff --git a/lib/app/modules/home/views/filter_drawer_home_page.dart b/lib/app/modules/home/views/filter_drawer_home_page.dart index 8a447ea6..548f8c9c 100644 --- a/lib/app/modules/home/views/filter_drawer_home_page.dart +++ b/lib/app/modules/home/views/filter_drawer_home_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unrelated_type_equality_checks + import 'package:flutter/material.dart'; import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:taskwarrior/app/models/filters.dart'; 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 763dfa4f..645102d8 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -1,9 +1,8 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:taskwarrior/api_service.dart'; -import 'package:taskwarrior/app/modules/home/views/home_view.dart'; - import 'package:taskwarrior/app/routes/app_pages.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/gen/fonts.gen.dart'; @@ -81,7 +80,6 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { // reverseTransitionDuration: Duration.zero, // )); } catch (e) { - print('Failed to update tasks: $e'); ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: AppSettings.isDarkMode diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart index a2067f7b..822edcea 100644 --- a/lib/app/modules/home/views/show_tasks.dart +++ b/lib/app/modules/home/views/show_tasks.dart @@ -23,17 +23,6 @@ class TaskViewBuilder extends StatelessWidget { final bool pendingFilter; final String? project; - Future _loadCredentials( - Function(TaskDatabase, String, String) callback) async { - String uuid = (await CredentialsStorage.getClientId()) ?? ''; - String encryptionSecret = - (await CredentialsStorage.getEncryptionSecret()) ?? ''; - TaskDatabase taskDatabase = TaskDatabase(); - await taskDatabase.open(); - callback(taskDatabase, uuid, encryptionSecret); - // taskDatabase.printDatabaseContents(); - } - @override Widget build(BuildContext context) { final HomeController taskController = Get.find(); @@ -244,12 +233,6 @@ class TaskViewBuilder extends StatelessWidget { }); } - Future> _fetchTasks() async { - TaskDatabase taskDatabase = TaskDatabase(); - await taskDatabase.open(); - return await taskDatabase.fetchTasksFromDatabase(); - } - void _markTaskAsCompleted(String uuid) async { String clientId = (await CredentialsStorage.getClientId()) ?? ''; String encryptionSecret = diff --git a/lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart b/lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart index 0d05c240..ce35ad18 100644 --- a/lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart +++ b/lib/app/modules/manageTaskServer/views/manage_task_server_page_app_bar.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:math'; import 'package:flutter/foundation.dart'; diff --git a/lib/app/modules/reports/controllers/reports_controller.dart b/lib/app/modules/reports/controllers/reports_controller.dart index e5544f7c..abfe98fd 100644 --- a/lib/app/modules/reports/controllers/reports_controller.dart +++ b/lib/app/modules/reports/controllers/reports_controller.dart @@ -1,3 +1,5 @@ +// ignore_for_file: prefer_typing_uninitialized_variables + import 'dart:io'; import 'package:flutter/material.dart'; diff --git a/lib/app/modules/settings/controllers/settings_controller.dart b/lib/app/modules/settings/controllers/settings_controller.dart index c1f89163..049b37f2 100644 --- a/lib/app/modules/settings/controllers/settings_controller.dart +++ b/lib/app/modules/settings/controllers/settings_controller.dart @@ -1,10 +1,11 @@ +// ignore_for_file: depend_on_referenced_packages + import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:get/get_rx/get_rx.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/app/modules/settings/views/settings_page_select_directory_list_tile.dart b/lib/app/modules/settings/views/settings_page_select_directory_list_tile.dart index d11712f5..70334a60 100644 --- a/lib/app/modules/settings/views/settings_page_select_directory_list_tile.dart +++ b/lib/app/modules/settings/views/settings_page_select_directory_list_tile.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'dart:io'; import 'package:flutter/material.dart'; diff --git a/lib/app/modules/splash/controllers/splash_controller.dart b/lib/app/modules/splash/controllers/splash_controller.dart index 05d7d5a0..c6427533 100644 --- a/lib/app/modules/splash/controllers/splash_controller.dart +++ b/lib/app/modules/splash/controllers/splash_controller.dart @@ -1,3 +1,5 @@ +// ignore_for_file: depend_on_referenced_packages + import 'dart:io'; import 'package:get/get.dart'; diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index ec084a3e..f6925604 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + import 'package:get/get.dart'; import '../modules/about/bindings/about_binding.dart'; diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 5a1af88c..5cf53cc3 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + part of 'app_pages.dart'; // DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart From e5c9cce20a800f3767dbb8ab53ea84a84ca1fb12 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 15:11:06 +0530 Subject: [PATCH 15/26] fix: add languages for delete tasks --- lib/app/modules/home/views/nav_drawer.dart | 14 +++++++++++--- lib/app/utils/language/english_sentences.dart | 10 ++++++++++ lib/app/utils/language/hindi_sentences.dart | 10 ++++++++++ lib/app/utils/language/marathi_sentences.dart | 10 ++++++++++ lib/app/utils/language/sentences.dart | 3 +++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index 56096812..3822d8e9 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -105,14 +105,19 @@ class NavDrawer extends StatelessWidget { visible: homeController.taskchampion.value, child: NavDrawerMenuItem( icon: Icons.delete, - text: 'Delete tasks', + text: SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.deleteTaskTitle, onTap: () { showDialog( context: context, builder: (BuildContext context) { return Utils.showAlertDialog( title: Text( - 'Delete All Tasks?', + SentenceManager( + currentLanguage: + homeController.selectedLanguage.value, + ).sentences.deleteTaskConfirmation, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white @@ -120,7 +125,10 @@ class NavDrawer extends StatelessWidget { ), ), content: Text( - 'The action is irreversible and will delete all the tasks that are stored locally.', + SentenceManager( + currentLanguage: + homeController.selectedLanguage.value, + ).sentences.deleteTaskWarning, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white diff --git a/lib/app/utils/language/english_sentences.dart b/lib/app/utils/language/english_sentences.dart index 866e7e4d..4a799845 100644 --- a/lib/app/utils/language/english_sentences.dart +++ b/lib/app/utils/language/english_sentences.dart @@ -196,4 +196,14 @@ class EnglishSentences extends Sentences { @override String get ccsyncCredentials => 'CCync credentials'; + + @override + String get deleteTaskConfirmation => 'Delete Tasks'; + + @override + String get deleteTaskTitle => 'Delete All Tasks?'; + + @override + String get deleteTaskWarning => + 'The action is irreversible and will delete all the tasks that are stored locally.'; } diff --git a/lib/app/utils/language/hindi_sentences.dart b/lib/app/utils/language/hindi_sentences.dart index 87ce9d39..99a5cadb 100644 --- a/lib/app/utils/language/hindi_sentences.dart +++ b/lib/app/utils/language/hindi_sentences.dart @@ -198,4 +198,14 @@ class HindiSentences extends Sentences { @override String get ccsyncCredentials => 'CCync क्रेडेन्शियल'; + + @override + String get deleteTaskConfirmation => 'कार्य हटाएं'; + + @override + String get deleteTaskTitle => 'सभी कार्य हटाएं?'; + + @override + String get deleteTaskWarning => + 'यह क्रिया अपरिवर्तनीय है और यह सभी स्थानीय रूप से संग्रहीत कार्यों को हटा देगी।'; } diff --git a/lib/app/utils/language/marathi_sentences.dart b/lib/app/utils/language/marathi_sentences.dart index 7111029e..27bbaa91 100644 --- a/lib/app/utils/language/marathi_sentences.dart +++ b/lib/app/utils/language/marathi_sentences.dart @@ -197,4 +197,14 @@ class MarathiSentences extends Sentences { @override String get ccsyncCredentials => 'CCync क्रेडेन्शियल'; + + @override + String get deleteTaskConfirmation => 'कार्य हटवा'; + + @override + String get deleteTaskTitle => 'सर्व कार्य हटवायचे का?'; + + @override + String get deleteTaskWarning => + 'ही क्रिया अपरिवर्तनीय आहे आणि हे सर्व स्थानिक पातळीवर संग्रहित केलेले कार्य हटवेल.'; } diff --git a/lib/app/utils/language/sentences.dart b/lib/app/utils/language/sentences.dart index 2ad3b3f0..4939cd3c 100644 --- a/lib/app/utils/language/sentences.dart +++ b/lib/app/utils/language/sentences.dart @@ -47,6 +47,9 @@ abstract class Sentences { String get navDrawerAbout; String get navDrawerSettings; String get ccsyncCredentials; + String get deleteTaskTitle; + String get deleteTaskConfirmation; + String get deleteTaskWarning; String get navDrawerExit; String get detailPageDescription; From bdfbdbf82fae3488ed9c9e9e76ed23d0b6460658 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 5 Jul 2024 19:38:30 +0530 Subject: [PATCH 16/26] fix: fix merge conflict --- lib/app/modules/home/views/tas_list_item.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/app/modules/home/views/tas_list_item.dart b/lib/app/modules/home/views/tas_list_item.dart index 95c4c369..5ad6e023 100644 --- a/lib/app/modules/home/views/tas_list_item.dart +++ b/lib/app/modules/home/views/tas_list_item.dart @@ -32,6 +32,7 @@ class TaskListItem extends StatelessWidget { @override Widget build(BuildContext context) { + // ignore: unused_element void saveChanges() async { var now = DateTime.now().toUtc(); modify.save( From 0e9c5b57e78ddedaa991f701628a2c43a60e1499 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 13:17:06 +0530 Subject: [PATCH 17/26] fix: remove dependancy on redundant reports_tour_controller --- .../controllers/reports_tour_controller.dart | 20 ---------- .../reports/views/reports_view_taskc.dart | 40 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 lib/app/modules/reports/controllers/reports_tour_controller.dart diff --git a/lib/app/modules/reports/controllers/reports_tour_controller.dart b/lib/app/modules/reports/controllers/reports_tour_controller.dart deleted file mode 100644 index 35e3162b..00000000 --- a/lib/app/modules/reports/controllers/reports_tour_controller.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:shared_preferences/shared_preferences.dart'; - -class SaveReportsTour { - Future data = SharedPreferences.getInstance(); - - void saveReportsTourStatus() async { - final value = await data; - value.setBool('reports_tour', true); - } - - Future getReportsTourStatus() async { - final value = await data; - if (value.containsKey('reports_tour')) { - bool? getData = value.getBool('reports_tour'); - return getData!; - } else { - return false; - } - } -} diff --git a/lib/app/modules/reports/views/reports_view_taskc.dart b/lib/app/modules/reports/views/reports_view_taskc.dart index 76f82fd4..5e7f7684 100644 --- a/lib/app/modules/reports/views/reports_view_taskc.dart +++ b/lib/app/modules/reports/views/reports_view_taskc.dart @@ -3,11 +3,9 @@ import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:taskwarrior/api_service.dart'; import 'package:taskwarrior/app/modules/reports/controllers/reports_controller.dart'; -import 'package:taskwarrior/app/modules/reports/controllers/reports_tour_controller.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_daily_taskc.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_monthly_taskc.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_weekly_taskc.dart'; -import 'package:taskwarrior/app/tour/reports_page_tour.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; @@ -36,47 +34,9 @@ class _ReportsHomeTaskcState extends State late TaskDatabase taskDatabase; List allTasks = []; - void _initReportsTour() { - tutorialCoachMark = TutorialCoachMark( - targets: reportsDrawer( - daily: daily, - weekly: weekly, - monthly: monthly, - ), - colorShadow: TaskWarriorColors.black, - paddingFocus: 10, - opacityShadow: 0.8, - hideSkip: true, - onFinish: () { - SaveReportsTour().saveReportsTourStatus(); - }, - ); - } - - void _showReportsTour() { - Future.delayed( - const Duration(seconds: 2), - () { - SaveReportsTour().getReportsTourStatus().then((value) => { - if (value == false) - { - tutorialCoachMark.show(context: context), - } - else - { - // ignore: avoid_print - print('User has seen this page'), - } - }); - }, - ); - } - @override void initState() { super.initState(); - _initReportsTour(); - _showReportsTour(); reportsController = Get.find(); reportsController.initReportsTour(); reportsController.showReportsTour(context); From 7becfecf93295ec3adfae1677ad55518682fc95f Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 13:32:00 +0530 Subject: [PATCH 18/26] fix: make reports tour stateless --- .../reports/views/reports_view_taskc.dart | 268 ++++++++---------- 1 file changed, 121 insertions(+), 147 deletions(-) diff --git a/lib/app/modules/reports/views/reports_view_taskc.dart b/lib/app/modules/reports/views/reports_view_taskc.dart index 5e7f7684..7b5cf61e 100644 --- a/lib/app/modules/reports/views/reports_view_taskc.dart +++ b/lib/app/modules/reports/views/reports_view_taskc.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:taskwarrior/api_service.dart'; import 'package:taskwarrior/app/modules/reports/controllers/reports_controller.dart'; @@ -9,165 +8,140 @@ import 'package:taskwarrior/app/modules/reports/views/burn_down_weekly_taskc.dar import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; -class ReportsHomeTaskc extends StatefulWidget { - const ReportsHomeTaskc({ - super.key, - }); +class ReportsHomeTaskc extends StatelessWidget { + final ReportsController reportsController = ReportsController(); + final TaskDatabase taskDatabase = TaskDatabase(); - @override - State createState() => _ReportsHomeTaskcState(); -} - -class _ReportsHomeTaskcState extends State - with TickerProviderStateMixin { - late TabController _tabController; - final GlobalKey daily = GlobalKey(); - final GlobalKey weekly = GlobalKey(); - final GlobalKey monthly = GlobalKey(); + ReportsHomeTaskc({super.key}); - bool isSaved = false; - late TutorialCoachMark tutorialCoachMark; - late ReportsController reportsController; - int _selectedIndex = 0; - late TaskDatabase taskDatabase; - List allTasks = []; - - @override - void initState() { - super.initState(); - reportsController = Get.find(); - reportsController.initReportsTour(); - reportsController.showReportsTour(context); - _tabController = TabController(length: 3, vsync: this); - - // Initialize the database and fetch data - taskDatabase = TaskDatabase(); - taskDatabase.open().then((_) { - taskDatabase.fetchTasksFromDatabase().then((tasks) { - setState(() { - allTasks = tasks; - }); - }); - }); + Future> fetchTasks() async { + await taskDatabase.open(); + return await taskDatabase.fetchTasksFromDatabase(); } @override Widget build(BuildContext context) { - double height = MediaQuery.of(context).size.height; // Screen height + double height = MediaQuery.of(context).size.height; + reportsController.initReportsTour(); + reportsController.showReportsTour(context); + return FutureBuilder>( + future: fetchTasks(), + builder: (context, snapshot) { + List allTasks = snapshot.data ?? []; - return Scaffold( - appBar: AppBar( - backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, - title: Text( - 'Reports', - style: GoogleFonts.poppins(color: TaskWarriorColors.white), - ), - leading: GestureDetector( - onTap: () { - Navigator.pop(context); - }, - child: Icon( - Icons.chevron_left, - color: TaskWarriorColors.white, - ), - ), - bottom: PreferredSize( - preferredSize: Size.fromHeight( - height * 0.1), // Adjust the preferred height as needed - child: TabBar( - controller: _tabController, - labelColor: TaskWarriorColors.white, - labelStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.medium, - fontSize: TaskWarriorFonts.fontSizeSmall, + return Scaffold( + appBar: AppBar( + backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, + title: Text( + 'Reports', + style: GoogleFonts.poppins(color: TaskWarriorColors.white), ), - unselectedLabelStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.light, - ), - onTap: (value) { - setState(() { - _selectedIndex = value; - }); - }, - tabs: [ - Tab( - key: daily, - icon: const Icon(Icons.schedule), - text: 'Daily', - iconMargin: const EdgeInsets.only(bottom: 0.0), - ), - Tab( - key: weekly, - icon: const Icon(Icons.today), - text: 'Weekly', - iconMargin: const EdgeInsets.only(bottom: 0.0), - ), - Tab( - key: monthly, - icon: const Icon(Icons.date_range), - text: 'Monthly', - iconMargin: const EdgeInsets.only(bottom: 0.0), + leading: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Icon( + Icons.chevron_left, + color: TaskWarriorColors.white, ), - ], - ), - ), - ), - backgroundColor: AppSettings.isDarkMode - ? TaskWarriorColors.kprimaryBackgroundColor - : TaskWarriorColors.white, - body: allTasks.isEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon( - Icons.heart_broken, - color: AppSettings.isDarkMode - ? TaskWarriorColors.white - : TaskWarriorColors.black, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'No Task found', - style: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.medium, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode - ? TaskWarriorColors.white - : TaskWarriorColors.black, - ), - ), - ], + ), + bottom: PreferredSize( + preferredSize: Size.fromHeight(height * 0.1), + child: TabBar( + controller: reportsController.tabController, + labelColor: TaskWarriorColors.white, + labelStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.medium, + fontSize: TaskWarriorFonts.fontSizeSmall, ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Add a task to see reports', - style: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.light, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode - ? TaskWarriorColors.white - : TaskWarriorColors.black, - ), - ), - ], + unselectedLabelStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.light, ), - ], - ) - : IndexedStack( - index: _selectedIndex, - children: const [ - BurnDownDailyTaskc(), - BurnDownWeeklyTask(), - BurnDownMonthlyTaskc(), - ], + onTap: (value) { + reportsController.selectedIndex.value = value; + }, + tabs: [ + Tab( + key: reportsController.daily, + icon: const Icon(Icons.schedule), + text: 'Daily', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + Tab( + key: reportsController.weekly, + icon: const Icon(Icons.today), + text: 'Weekly', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + Tab( + key: reportsController.monthly, + icon: const Icon(Icons.date_range), + text: 'Monthly', + iconMargin: const EdgeInsets.only(bottom: 0.0), + ), + ], + ), ), + ), + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.white, + body: snapshot.connectionState == ConnectionState.waiting + ? const Center(child: CircularProgressIndicator()) + : allTasks.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.heart_broken, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'No Task found', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.medium, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Add a task to see reports', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.light, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? TaskWarriorColors.white + : TaskWarriorColors.black, + ), + ), + ], + ), + ], + ) + : IndexedStack( + index: reportsController.selectedIndex.value, + children: const [ + BurnDownDailyTaskc(), + BurnDownWeeklyTask(), + BurnDownMonthlyTaskc(), + ], + ), + ); + }, ); } } From 8aa5612ed0d80c88908a9b5c43bf9547b63e65f6 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 15:17:24 +0530 Subject: [PATCH 19/26] fix: fixed reports for taskc --- lib/app/modules/home/views/nav_drawer.dart | 36 ++- .../controllers/reports_controller.dart | 8 +- .../reports/views/burn_down_daily_taskc.dart | 247 ++++++++-------- .../views/burn_down_monthly_taskc.dart | 260 ++++++++--------- .../reports/views/burn_down_weekly_taskc.dart | 273 +++++++++--------- .../reports/views/reports_view_taskc.dart | 21 +- 6 files changed, 430 insertions(+), 415 deletions(-) diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index 3822d8e9..2904963d 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -5,6 +5,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; import 'package:taskwarrior/app/modules/home/views/home_page_nav_drawer_menu_item.dart'; import 'package:taskwarrior/app/modules/home/views/theme_clipper.dart'; +import 'package:taskwarrior/app/modules/reports/views/reports_view_taskc.dart'; import 'package:taskwarrior/app/routes/app_pages.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; @@ -183,15 +184,32 @@ class NavDrawer extends StatelessWidget { ), ), ), - Obx( - () => NavDrawerMenuItem( - icon: Icons.summarize, - text: SentenceManager( - currentLanguage: homeController.selectedLanguage.value, - ).sentences.navDrawerReports, - onTap: () { - Get.toNamed(Routes.REPORTS); - }, + Visibility( + visible: !homeController.taskchampion.value, + child: Obx( + () => NavDrawerMenuItem( + icon: Icons.summarize, + text: SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.navDrawerReports, + onTap: () { + Get.toNamed(Routes.REPORTS); + }, + ), + ), + ), + Visibility( + visible: homeController.taskchampion.value, + child: Obx( + () => NavDrawerMenuItem( + icon: Icons.summarize, + text: SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.navDrawerReports, + onTap: () { + Get.to(() => ReportsHomeTaskc()); + }, + ), ), ), Obx( diff --git a/lib/app/modules/reports/controllers/reports_controller.dart b/lib/app/modules/reports/controllers/reports_controller.dart index abfe98fd..739f4b963 100644 --- a/lib/app/modules/reports/controllers/reports_controller.dart +++ b/lib/app/modules/reports/controllers/reports_controller.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:taskwarrior/api_service.dart'; import 'package:taskwarrior/app/models/json/task.dart'; import 'package:taskwarrior/app/models/storage.dart'; import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; @@ -24,7 +25,7 @@ class ReportsController extends GetxController final GlobalKey daily = GlobalKey(); final GlobalKey weekly = GlobalKey(); final GlobalKey monthly = GlobalKey(); - + late TaskDatabase taskDatabase; var isSaved = false.obs; late TutorialCoachMark tutorialCoachMark; @@ -344,6 +345,11 @@ class ReportsController extends GetxController }); } + Future> fetchTasks() async { + await taskDatabase.open(); + return await taskDatabase.fetchTasksFromDatabase(); + } + // monthly report late TooltipBehavior monthlyBurndownTooltipBehaviour; RxMap> monthlyInfo = diff --git a/lib/app/modules/reports/views/burn_down_daily_taskc.dart b/lib/app/modules/reports/views/burn_down_daily_taskc.dart index 59a7aa45..ed9e946c 100644 --- a/lib/app/modules/reports/views/burn_down_daily_taskc.dart +++ b/lib/app/modules/reports/views/burn_down_daily_taskc.dart @@ -9,74 +9,50 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/constants/utilites.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -class BurnDownDailyTaskc extends StatefulWidget { - const BurnDownDailyTaskc({super.key}); +class BurnDownDailyTaskc extends StatelessWidget { + BurnDownDailyTaskc({super.key}); - @override - State createState() => _BurnDownDailyTaskcState(); -} - -class _BurnDownDailyTaskcState extends State - with TickerProviderStateMixin { - late TaskDatabase taskDatabase; - late TooltipBehavior _dailyBurndownTooltipBehaviour; - Map> dailyInfo = {}; - - @override - void initState() { - super.initState(); - - // Initialize the tooltip behavior for the chart - _dailyBurndownTooltipBehaviour = TooltipBehavior( - enable: true, - builder: (dynamic data, dynamic point, dynamic series, int pointIndex, - int seriesIndex) { - final String date = data.x; - final int pendingCount = data.y1; - final int completedCount = data.y2; + final TooltipBehavior _dailyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String date = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Date: $date', - style: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - ), - ), - Text( - 'Pending: $pendingCount', - ), - Text( - 'Completed: $completedCount', + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Date: $date', + style: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, ), - ], - ), - ); - }, - ); + ), + Text('Pending: $pendingCount'), + Text('Completed: $completedCount'), + ], + ), + ); + }, + ); - // Initialize the database and fetch data - taskDatabase = TaskDatabase(); - taskDatabase.open().then((_) { - taskDatabase.fetchTasksFromDatabase().then((tasks) { - setState(() { - // Process the data and update the chart - _processData(tasks); - }); - }); - }); + Future>> fetchDailyInfo() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + List tasks = await taskDatabase.fetchTasksFromDatabase(); + return _processData(tasks); } - void _processData(List tasks) { - dailyInfo = {}; + Map> _processData(List tasks) { + Map> dailyInfo = {}; // Sort tasks by entry date in ascending order tasks.sort((a, b) => a.entry.compareTo(b.entry)); @@ -98,80 +74,101 @@ class _BurnDownDailyTaskcState extends State }; } } + + return dailyInfo; } @override Widget build(BuildContext context) { double height = MediaQuery.of(context).size.height; // Screen height - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: SizedBox( - height: height * 0.6, - child: SfCartesianChart( - primaryXAxis: CategoryAxis( - title: AxisTitle( - text: 'Day - Month', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, - fontSize: TaskWarriorFonts.fontSizeSmall, + return FutureBuilder>>( + future: fetchDailyInfo(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + Map> dailyInfo = snapshot.data ?? {}; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Day - Month', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + fontSize: TaskWarriorFonts.fontSizeSmall, + ), + ), ), - ), - ), - primaryYAxis: NumericAxis( - title: AxisTitle( - text: 'Tasks', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + ), + ), ), + tooltipBehavior: _dailyBurndownTooltipBehaviour, + series: [ + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: dailyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: dailyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], ), ), - tooltipBehavior: _dailyBurndownTooltipBehaviour, - series: [ - StackedColumnSeries( - groupName: 'Group A', - enableTooltip: true, - color: TaskWarriorColors.green, - dataSource: dailyInfo.entries - .map((entry) => ChartData( - entry.key, - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y2, - name: 'Completed', - ), - StackedColumnSeries( - groupName: 'Group A', - color: TaskWarriorColors.yellow, - enableTooltip: true, - dataSource: dailyInfo.entries - .map((entry) => ChartData( - entry.key, - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y1, - name: 'Pending', - ), - ], ), - ), - ), - const CommonChartIndicator( - title: 'Daily Burndown Chart', - ), - ], + const CommonChartIndicator( + title: 'Daily Burndown Chart', + ), + ], + ); + }, ); } } diff --git a/lib/app/modules/reports/views/burn_down_monthly_taskc.dart b/lib/app/modules/reports/views/burn_down_monthly_taskc.dart index 3e75115b..2d1477f9 100644 --- a/lib/app/modules/reports/views/burn_down_monthly_taskc.dart +++ b/lib/app/modules/reports/views/burn_down_monthly_taskc.dart @@ -9,75 +9,54 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/constants/utilites.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -class BurnDownMonthlyTaskc extends StatefulWidget { - const BurnDownMonthlyTaskc({super.key}); - - @override - State createState() => _BurnDownMonthlyTaskcState(); -} - -class _BurnDownMonthlyTaskcState extends State - with TickerProviderStateMixin { - late TaskDatabase taskDatabase; - Map> monthlyInfo = {}; - - late TooltipBehavior _weeklyBurndownTooltipBehaviour; - - @override - void initState() { - super.initState(); - - _weeklyBurndownTooltipBehaviour = TooltipBehavior( - enable: true, - builder: (dynamic data, dynamic point, dynamic series, int pointIndex, - int seriesIndex) { - final String monthYear = data.x; - final int pendingCount = data.y1; - final int completedCount = data.y2; - - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Month-Year: $monthYear', - style: const TextStyle( - fontWeight: TaskWarriorFonts.bold, - ), - ), - Text( - 'Pending: $pendingCount', - ), - Text( - 'Completed: $completedCount', +class BurnDownMonthlyTaskc extends StatelessWidget { + BurnDownMonthlyTaskc({super.key}); + + final _monthlyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String monthYear = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; + + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Month-Year: $monthYear', + style: const TextStyle( + fontWeight: TaskWarriorFonts.bold, ), - ], - ), - ); - }, - ); - - taskDatabase = TaskDatabase(); - - ///fetch all data from the database - fetchAllData(); - } - - void fetchAllData() async { - List allData = await taskDatabase.fetchTasksFromDatabase(); - if (allData.isNotEmpty) { - sortBurnDownMonthly(allData); - } + ), + Text( + 'Pending: $pendingCount', + ), + Text( + 'Completed: $completedCount', + ), + ], + ), + ); + }, + ); + + Future>> fetchMonthlyInfo() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + List tasks = await taskDatabase.fetchTasksFromDatabase(); + return sortBurnDownMonthly(tasks); } - void sortBurnDownMonthly(List allData) { - monthlyInfo = {}; + Map> sortBurnDownMonthly(List allData) { + Map> monthlyInfo = {}; allData.sort((a, b) => a.entry.compareTo(b.entry)); @@ -103,82 +82,99 @@ class _BurnDownMonthlyTaskcState extends State } debugPrint("monthlyInfo: $monthlyInfo"); - - // Update the state to refresh the chart - setState(() {}); + return monthlyInfo; } @override Widget build(BuildContext context) { final double height = MediaQuery.of(context).size.height; - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: SizedBox( - height: height * 0.6, - child: SfCartesianChart( - primaryXAxis: CategoryAxis( - title: AxisTitle( - text: 'Month - Year', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, + + return FutureBuilder>>( + future: fetchMonthlyInfo(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + Map> monthlyInfo = snapshot.data ?? {}; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Month - Year', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + ), + ), ), - ), - ), - primaryYAxis: NumericAxis( - title: AxisTitle( - text: 'Tasks', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + ), + ), ), + tooltipBehavior: _monthlyBurndownTooltipBehaviour, + series: [ + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: monthlyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: monthlyInfo.entries + .map((entry) => ChartData( + entry.key, + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], ), ), - tooltipBehavior: _weeklyBurndownTooltipBehaviour, - series: [ - StackedColumnSeries( - groupName: 'Group A', - enableTooltip: true, - color: TaskWarriorColors.green, - dataSource: monthlyInfo.entries - .map((entry) => ChartData( - entry.key, - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y2, - name: 'Completed', - ), - StackedColumnSeries( - groupName: 'Group A', - color: TaskWarriorColors.yellow, - enableTooltip: true, - dataSource: monthlyInfo.entries - .map((entry) => ChartData( - entry.key, - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y1, - name: 'Pending', - ), - ], ), - ), - ), - const CommonChartIndicator( - title: 'Monthly Burndown Chart', - ) - ], + const CommonChartIndicator( + title: 'Monthly Burndown Chart', + ), + ], + ); + }, ); } } diff --git a/lib/app/modules/reports/views/burn_down_weekly_taskc.dart b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart index 193600a2..6d2430db 100644 --- a/lib/app/modules/reports/views/burn_down_weekly_taskc.dart +++ b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart @@ -9,76 +9,55 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/constants/utilites.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -class BurnDownWeeklyTask extends StatefulWidget { - const BurnDownWeeklyTask({super.key}); - - @override - State createState() => _BurnDownWeeklyTaskState(); -} - -class _BurnDownWeeklyTaskState extends State - with TickerProviderStateMixin { - late TaskDatabase taskDatabase; - late TooltipBehavior _weeklyBurndownTooltipBehaviour; - Map> weeklyInfo = {}; - - @override - void initState() { - super.initState(); - - ///initialize the _weeklyBurndownTooltipBehaviour tooltip behavior - _weeklyBurndownTooltipBehaviour = TooltipBehavior( - enable: true, - builder: (dynamic data, dynamic point, dynamic series, int pointIndex, - int seriesIndex) { - final String weekNumber = data.x; - final int pendingCount = data.y1; - final int completedCount = data.y2; - - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - weekNumber, - style: const TextStyle( - fontWeight: TaskWarriorFonts.bold, - ), - ), - Text( - 'Pending: $pendingCount', - ), - Text( - 'Completed: $completedCount', +class BurnDownWeeklyTask extends StatelessWidget { + BurnDownWeeklyTask({super.key}); + + final TooltipBehavior _weeklyBurndownTooltipBehaviour = TooltipBehavior( + enable: true, + builder: (dynamic data, dynamic point, dynamic series, int pointIndex, + int seriesIndex) { + final String weekNumber = data.x; + final int pendingCount = data.y1; + final int completedCount = data.y2; + + return Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + weekNumber, + style: const TextStyle( + fontWeight: TaskWarriorFonts.bold, ), - ], - ), - ); - }, - ); - - taskDatabase = TaskDatabase(); - - ///fetch all data from the database - fetchAllData(); - } - - void fetchAllData() async { - List allData = await taskDatabase.fetchTasksFromDatabase(); - if (allData.isNotEmpty) { - sortBurnDownWeekly(allData); - } + ), + Text( + 'Pending: $pendingCount', + ), + Text( + 'Completed: $completedCount', + ), + ], + ), + ); + }, + ); + + Future>> fetchWeeklyInfo() async { + TaskDatabase taskDatabase = TaskDatabase(); + await taskDatabase.open(); + List tasks = await taskDatabase.fetchTasksFromDatabase(); + return sortBurnDownWeekly(tasks); } - void sortBurnDownWeekly(List allData) { + Map> sortBurnDownWeekly(List allData) { // Initialize weeklyInfo map - weeklyInfo = {}; + Map> weeklyInfo = {}; // Sort allData by entry date in ascending order allData.sort((a, b) => a.entry.compareTo(b.entry)); @@ -102,7 +81,8 @@ class _BurnDownWeeklyTaskState extends State } } else { ///if weeklyInfo does not contain the week number - weeklyInfo[weekNumber] = { + // ignore: collection_methods_unrelated_type + weeklyInfo[weekNumber.toString()] = { 'pending': allData[i].status == 'pending' ? 1 : 0, 'completed': allData[i].status == 'completed' ? 1 : 0, }; @@ -110,86 +90,101 @@ class _BurnDownWeeklyTaskState extends State } debugPrint("weeklyInfo $weeklyInfo"); - - // Update the state to refresh the chart - setState(() {}); + return weeklyInfo; } @override Widget build(BuildContext context) { double height = MediaQuery.of(context).size.height; // Screen height - - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: SizedBox( - height: height * 0.6, - child: SfCartesianChart( - primaryXAxis: CategoryAxis( - title: AxisTitle( - text: 'Weeks - Year', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - fontSize: TaskWarriorFonts.fontSizeSmall, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, + return FutureBuilder>>( + future: fetchWeeklyInfo(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + Map> weeklyInfo = snapshot.data ?? {}; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + height: height * 0.6, + child: SfCartesianChart( + primaryXAxis: CategoryAxis( + title: AxisTitle( + text: 'Weeks - Year', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + fontSize: TaskWarriorFonts.fontSizeSmall, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + ), + ), + ), + primaryYAxis: NumericAxis( + title: AxisTitle( + text: 'Tasks', + textStyle: GoogleFonts.poppins( + fontWeight: TaskWarriorFonts.bold, + color: AppSettings.isDarkMode + ? Colors.white + : Colors.black, + fontSize: TaskWarriorFonts.fontSizeSmall, + ), + ), + ), + tooltipBehavior: _weeklyBurndownTooltipBehaviour, + series: [ + ///this is the completed tasks + StackedColumnSeries( + groupName: 'Group A', + enableTooltip: true, + color: TaskWarriorColors.green, + dataSource: weeklyInfo.entries + .map((entry) => ChartData( + 'Week ${entry.key}', + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y2, + name: 'Completed', + ), + + ///this is the pending tasks + StackedColumnSeries( + groupName: 'Group A', + color: TaskWarriorColors.yellow, + enableTooltip: true, + dataSource: weeklyInfo.entries + .map((entry) => ChartData( + 'Week ${entry.key}', + entry.value['pending'] ?? 0, + entry.value['completed'] ?? 0, + )) + .toList(), + xValueMapper: (ChartData data, _) => data.x, + yValueMapper: (ChartData data, _) => data.y1, + name: 'Pending', + ), + ], ), ), ), - primaryYAxis: NumericAxis( - title: AxisTitle( - text: 'Tasks', - textStyle: GoogleFonts.poppins( - fontWeight: TaskWarriorFonts.bold, - color: AppSettings.isDarkMode ? Colors.white : Colors.black, - fontSize: TaskWarriorFonts.fontSizeSmall, - ), - ), + const CommonChartIndicator( + title: 'Weekly Burndown Chart', ), - tooltipBehavior: _weeklyBurndownTooltipBehaviour, - series: [ - ///this is the completed tasks - StackedColumnSeries( - groupName: 'Group A', - enableTooltip: true, - color: TaskWarriorColors.green, - dataSource: weeklyInfo.entries - .map((entry) => ChartData( - 'Week ${entry.key}', - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y2, - name: 'Completed', - ), - - ///this is the pending tasks - StackedColumnSeries( - groupName: 'Group A', - color: TaskWarriorColors.yellow, - enableTooltip: true, - dataSource: weeklyInfo.entries - .map((entry) => ChartData( - 'Week ${entry.key}', - entry.value['pending'] ?? 0, - entry.value['completed'] ?? 0, - )) - .toList(), - xValueMapper: (ChartData data, _) => data.x, - yValueMapper: (ChartData data, _) => data.y1, - name: 'Pending', - ), - ], - ), - ), - ), - const CommonChartIndicator( - title: 'Weekly Burndown Chart', - ), - ], - ); + ], + ); + }); } } diff --git a/lib/app/modules/reports/views/reports_view_taskc.dart b/lib/app/modules/reports/views/reports_view_taskc.dart index 7b5cf61e..202f9eb5 100644 --- a/lib/app/modules/reports/views/reports_view_taskc.dart +++ b/lib/app/modules/reports/views/reports_view_taskc.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:taskwarrior/api_service.dart'; +import 'package:get/get.dart'; import 'package:taskwarrior/app/modules/reports/controllers/reports_controller.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_daily_taskc.dart'; import 'package:taskwarrior/app/modules/reports/views/burn_down_monthly_taskc.dart'; @@ -8,9 +8,10 @@ import 'package:taskwarrior/app/modules/reports/views/burn_down_weekly_taskc.dar import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; +import 'package:taskwarrior/api_service.dart'; class ReportsHomeTaskc extends StatelessWidget { - final ReportsController reportsController = ReportsController(); + final ReportsController reportsController = Get.put(ReportsController()); final TaskDatabase taskDatabase = TaskDatabase(); ReportsHomeTaskc({super.key}); @@ -132,13 +133,15 @@ class ReportsHomeTaskc extends StatelessWidget { ), ], ) - : IndexedStack( - index: reportsController.selectedIndex.value, - children: const [ - BurnDownDailyTaskc(), - BurnDownWeeklyTask(), - BurnDownMonthlyTaskc(), - ], + : Obx( + () => IndexedStack( + index: reportsController.selectedIndex.value, + children: [ + BurnDownDailyTaskc(), + BurnDownWeeklyTask(), + BurnDownMonthlyTaskc(), + ], + ), ), ); }, From 1d09d36c8f382f80a1d7ed0d556443b1672dac46 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 15:23:06 +0530 Subject: [PATCH 20/26] fix: merge conflict --- .../modules/reports/views/burn_down_weekly_taskc.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/app/modules/reports/views/burn_down_weekly_taskc.dart b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart index 6d2430db..6d9f5821 100644 --- a/lib/app/modules/reports/views/burn_down_weekly_taskc.dart +++ b/lib/app/modules/reports/views/burn_down_weekly_taskc.dart @@ -68,16 +68,16 @@ class BurnDownWeeklyTask extends StatelessWidget { Utils.getWeekNumbertoInt(DateTime.parse(allData[i].entry)); ///check if weeklyInfo contains the week number - if (weeklyInfo.containsKey(weekNumber)) { + if (weeklyInfo.containsKey(weekNumber.toString())) { ///check if the status is pending or completed if (allData[i].status == 'pending') { ///if the status is pending then add 1 to the pending count - weeklyInfo[weekNumber]!['pending'] = - (weeklyInfo[weekNumber]!['pending'] ?? 0) + 1; + weeklyInfo[weekNumber.toString()]!['pending'] = + (weeklyInfo[weekNumber.toString()]!['pending'] ?? 0) + 1; } else if (allData[i].status == 'completed') { ///if the status is completed then add 1 to the completed count - weeklyInfo[weekNumber]!['completed'] = - (weeklyInfo[weekNumber]!['completed'] ?? 0) + 1; + weeklyInfo[weekNumber.toString()]!['completed'] = + (weeklyInfo[weekNumber.toString()]!['completed'] ?? 0) + 1; } } else { ///if weeklyInfo does not contain the week number From 16722bf01d95c81f75690669d18331f0c577f105 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 18:00:24 +0530 Subject: [PATCH 21/26] feat: make taskchampion view stateless --- .../modules/home/views/home_page_app_bar.dart | 5 +- lib/app/modules/home/views/nav_drawer.dart | 2 +- lib/app/utils/taskchampion/taskchampion.dart | 47 ++++--------------- 3 files changed, 13 insertions(+), 41 deletions(-) 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 645102d8..5c7b2033 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -107,8 +107,7 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { Navigator.push( context, MaterialPageRoute( - builder: (_) => - const ManageTaskChampionCreds(), + builder: (_) => ManageTaskChampionCreds(), )).then((value) {}); }, textColor: TaskWarriorColors.purple, @@ -144,7 +143,7 @@ class HomePageAppBar extends StatelessWidget implements PreferredSizeWidget { Navigator.push( context, MaterialPageRoute( - builder: (_) => const ManageTaskChampionCreds(), + builder: (_) => ManageTaskChampionCreds(), )).then((value) {}); }, textColor: TaskWarriorColors.purple, diff --git a/lib/app/modules/home/views/nav_drawer.dart b/lib/app/modules/home/views/nav_drawer.dart index 2904963d..bed358f0 100644 --- a/lib/app/modules/home/views/nav_drawer.dart +++ b/lib/app/modules/home/views/nav_drawer.dart @@ -96,7 +96,7 @@ class NavDrawer extends StatelessWidget { onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: (context) => const ManageTaskChampionCreds(), + builder: (context) => ManageTaskChampionCreds(), ), ); }, diff --git a/lib/app/utils/taskchampion/taskchampion.dart b/lib/app/utils/taskchampion/taskchampion.dart index 2047aff9..566e3c90 100644 --- a/lib/app/utils/taskchampion/taskchampion.dart +++ b/lib/app/utils/taskchampion/taskchampion.dart @@ -6,49 +6,29 @@ import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; import 'package:url_launcher/url_launcher.dart'; -class ManageTaskChampionCreds extends StatefulWidget { - const ManageTaskChampionCreds({super.key}); - - @override - State createState() => - _ManageTaskChampionCredsState(); -} - -class _ManageTaskChampionCredsState extends State { - String encryptionSecret = ''; - String clientId = ''; - +class ManageTaskChampionCreds extends StatelessWidget { final TextEditingController _encryptionSecretController = TextEditingController(); final TextEditingController _clientIdController = TextEditingController(); - @override - void initState() { - super.initState(); + ManageTaskChampionCreds({super.key}) { _loadCredentials(); } Future _loadCredentials() async { SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - encryptionSecret = prefs.getString('encryptionSecret') ?? ''; - clientId = prefs.getString('clientId') ?? ''; - _encryptionSecretController.text = encryptionSecret; - _clientIdController.text = clientId; - }); + _encryptionSecretController.text = + prefs.getString('encryptionSecret') ?? ''; + _clientIdController.text = prefs.getString('clientId') ?? ''; } - Future _saveCredentials() async { + Future _saveCredentials(BuildContext context) async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('encryptionSecret', _encryptionSecretController.text); await prefs.setString('clientId', _clientIdController.text); - } - - @override - void dispose() { - _encryptionSecretController.dispose(); - _clientIdController.dispose(); - super.dispose(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Credentials saved successfully')), + ); } @override @@ -136,14 +116,7 @@ class _ManageTaskChampionCredsState extends State { ), const SizedBox(height: 20), ElevatedButton( - onPressed: () async { - await _saveCredentials(); - // ignore: use_build_context_synchronously - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Credentials saved successfully')), - ); - }, + onPressed: () => _saveCredentials(context), child: const Text('Save Credentials'), ), const SizedBox(height: 10), From 1c2898f16b4bc8b9d662bc49fb78d6e2dfecc97c Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 18:08:36 +0530 Subject: [PATCH 22/26] fix: minor bug fix --- lib/app/utils/taskchampion/taskchampion.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/app/utils/taskchampion/taskchampion.dart b/lib/app/utils/taskchampion/taskchampion.dart index 566e3c90..16b218e5 100644 --- a/lib/app/utils/taskchampion/taskchampion.dart +++ b/lib/app/utils/taskchampion/taskchampion.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_build_context_synchronously + import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; From 5a7d7afdfcc1557cf713b6681b973e458982cc71 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 18:11:13 +0530 Subject: [PATCH 23/26] fix: move CredentialStorage --- lib/api_service.dart | 2 +- .../home/controllers/home_controller.dart | 2 +- .../modules/home/views/home_page_app_bar.dart | 1 + lib/app/modules/home/views/show_tasks.dart | 2 +- .../utils/taskchampion/credentials_storage.dart | 16 ++++++++++++++++ lib/app/utils/taskchampion/taskchampion.dart | 15 --------------- 6 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 lib/app/utils/taskchampion/credentials_storage.dart diff --git a/lib/api_service.dart b/lib/api_service.dart index fbd699f3..5df62b72 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; +import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; class Tasks { final int id; diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 6a4488b8..3da42168 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -23,7 +23,7 @@ import 'package:taskwarrior/app/tour/filter_drawer_tour.dart'; import 'package:taskwarrior/app/tour/home_page_tour.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/language/supported_language.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; +import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; import 'package:taskwarrior/app/utils/taskfunctions/comparator.dart'; import 'package:taskwarrior/app/utils/taskfunctions/projects.dart'; import 'package:taskwarrior/app/utils/taskfunctions/query.dart'; 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 5c7b2033..1b766270 100644 --- a/lib/app/modules/home/views/home_page_app_bar.dart +++ b/lib/app/modules/home/views/home_page_app_bar.dart @@ -7,6 +7,7 @@ import 'package:taskwarrior/app/routes/app_pages.dart'; 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/theme/app_settings.dart'; diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart index 822edcea..26b987f4 100644 --- a/lib/app/modules/home/views/show_tasks.dart +++ b/lib/app/modules/home/views/show_tasks.dart @@ -8,7 +8,7 @@ import 'package:taskwarrior/app/modules/home/views/show_details.dart'; import 'package:taskwarrior/app/utils/constants/palette.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; -import 'package:taskwarrior/app/utils/taskchampion/taskchampion.dart'; +import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; class TaskViewBuilder extends StatelessWidget { diff --git a/lib/app/utils/taskchampion/credentials_storage.dart b/lib/app/utils/taskchampion/credentials_storage.dart new file mode 100644 index 00000000..3e449963 --- /dev/null +++ b/lib/app/utils/taskchampion/credentials_storage.dart @@ -0,0 +1,16 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class CredentialsStorage { + static const String _encryptionSecretKey = 'encryptionSecret'; + static const String _clientIdKey = 'clientId'; + + static Future getEncryptionSecret() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(_encryptionSecretKey); + } + + static Future getClientId() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(_clientIdKey); + } +} diff --git a/lib/app/utils/taskchampion/taskchampion.dart b/lib/app/utils/taskchampion/taskchampion.dart index 16b218e5..79da30c3 100644 --- a/lib/app/utils/taskchampion/taskchampion.dart +++ b/lib/app/utils/taskchampion/taskchampion.dart @@ -140,18 +140,3 @@ class ManageTaskChampionCreds extends StatelessWidget { ); } } - -class CredentialsStorage { - static const String _encryptionSecretKey = 'encryptionSecret'; - static const String _clientIdKey = 'clientId'; - - static Future getEncryptionSecret() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getString(_encryptionSecretKey); - } - - static Future getClientId() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getString(_clientIdKey); - } -} From 33a10cbb9b1799136a0f09f304758213d93aa5b2 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 18:33:07 +0530 Subject: [PATCH 24/26] fix: make addtasktotaskc stateless --- .../home/controllers/home_controller.dart | 4 + .../views/add_task_to_taskc_bottom_sheet.dart | 109 ++++++------------ .../home_page_floating_action_button.dart | 4 +- 3 files changed, 41 insertions(+), 76 deletions(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 3da42168..78502dd0 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -51,6 +51,7 @@ class HomeController extends GetxController { final Rx selectedLanguage = SupportedLanguage.english.obs; final ScrollController scrollController = ScrollController(); final RxBool showbtn = false.obs; + late TaskDatabase taskdb; var tasks = [].obs; @override @@ -69,6 +70,8 @@ class HomeController extends GetxController { if (Platform.isAndroid) { handleHomeWidgetClicked(); } + taskdb = TaskDatabase(); + taskdb.open(); getUniqueProjects(); _loadTaskChampion(); fetchTasksFromDB(); @@ -483,6 +486,7 @@ class HomeController extends GetxController { // dialogue box final formKey = GlobalKey(); final namecontroller = TextEditingController(); + final projectcontroller = TextEditingController(); var due = Rxn(); RxString dueString = ''.obs; RxString priority = 'M'.obs; diff --git a/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart b/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart index 30d6a9c8..00e6587a 100644 --- a/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart +++ b/lib/app/modules/home/views/add_task_to_taskc_bottom_sheet.dart @@ -2,53 +2,15 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; -class AddTaskToTaskcBottomSheet extends StatefulWidget { - const AddTaskToTaskcBottomSheet({super.key}); - - @override - State createState() => - _AddTaskToTaskcBottomSheetState(); -} - -class _AddTaskToTaskcBottomSheetState extends State { - final formKey = GlobalKey(); - final namecontroller = TextEditingController(); - DateTime? due; - String dueString = ''; - String priority = 'M'; - final projectController = TextEditingController(); - String project = ''; - bool inThePast = false; - bool change24hr = false; - late TaskDatabase taskdb; - - Future checkto24hr() async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - setState(() { - change24hr = prefs.getBool('24hourformate') ?? false; - }); - } - - @override - void initState() { - super.initState(); - checkto24hr(); - taskdb = TaskDatabase(); - taskdb.open(); - } - - @override - void dispose() { - projectController.dispose(); - namecontroller.dispose(); - super.dispose(); - } +class AddTaskToTaskcBottomSheet extends StatelessWidget { + final HomeController homeController; + const AddTaskToTaskcBottomSheet({super.key, required this.homeController}); @override Widget build(BuildContext context) { @@ -78,7 +40,7 @@ class _AddTaskToTaskcBottomSheetState extends State { ), ), content: Form( - key: formKey, + key: homeController.formKey, child: SizedBox( width: MediaQuery.of(context).size.width * 0.8, child: Column( @@ -108,7 +70,7 @@ class _AddTaskToTaskcBottomSheetState extends State { Widget buildName() => TextFormField( autofocus: true, - controller: namecontroller, + controller: homeController.namecontroller, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white @@ -129,7 +91,7 @@ class _AddTaskToTaskcBottomSheetState extends State { Widget buildProject() => TextFormField( autofocus: true, - controller: projectController, + controller: homeController.projectcontroller, style: TextStyle( color: AppSettings.isDarkMode ? TaskWarriorColors.white @@ -160,7 +122,7 @@ class _AddTaskToTaskcBottomSheetState extends State { Expanded( child: GestureDetector( child: TextFormField( - style: inThePast + style: homeController.inThePast.value ? TextStyle(color: TaskWarriorColors.red) : TextStyle( color: AppSettings.isDarkMode @@ -169,11 +131,13 @@ class _AddTaskToTaskcBottomSheetState extends State { ), readOnly: true, controller: TextEditingController( - text: (due != null) ? dueString : null, + text: (homeController.due.value != null) + ? homeController.dueString.value + : null, ), decoration: InputDecoration( hintText: 'Select due date', - hintStyle: inThePast + hintStyle: homeController.inThePast.value ? TextStyle(color: TaskWarriorColors.red) : TextStyle( color: AppSettings.isDarkMode @@ -215,7 +179,7 @@ class _AddTaskToTaskcBottomSheetState extends State { }, fieldHintText: "Month/Date/Year", context: context, - initialDate: due ?? DateTime.now(), + initialDate: homeController.due.value ?? DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime(2037, 12, 31), ); @@ -251,14 +215,15 @@ class _AddTaskToTaskcBottomSheetState extends State { ), child: MediaQuery( data: MediaQuery.of(context).copyWith( - alwaysUse24HourFormat: change24hr, + alwaysUse24HourFormat: + homeController.change24hr.value, ), child: child!), ); }, context: context, - initialTime: - TimeOfDay.fromDateTime(due ?? DateTime.now()), + initialTime: TimeOfDay.fromDateTime( + homeController.due.value ?? DateTime.now()), ); if (time != null) { var dateTime = date.add( @@ -267,12 +232,11 @@ class _AddTaskToTaskcBottomSheetState extends State { minutes: time.minute, ), ); - due = dateTime.toUtc(); - dueString = DateFormat("yyyy-MM-dd").format(dateTime); + homeController.due.value = dateTime.toUtc(); + homeController.dueString.value = + DateFormat("yyyy-MM-dd").format(dateTime); if (dateTime.isBefore(DateTime.now())) { - setState(() { - inThePast = true; - }); + homeController.inThePast.value = true; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( @@ -289,9 +253,7 @@ class _AddTaskToTaskcBottomSheetState extends State { .kLightSecondaryBackgroundColor, duration: const Duration(seconds: 2))); } else { - setState(() { - inThePast = false; - }); + homeController.inThePast.value = false; } } } @@ -322,7 +284,7 @@ class _AddTaskToTaskcBottomSheetState extends State { dropdownColor: AppSettings.isDarkMode ? TaskWarriorColors.kdialogBackGroundColor : TaskWarriorColors.kLightDialogBackGroundColor, - value: priority, + value: homeController.priority.value, elevation: 16, style: GoogleFonts.poppins( color: AppSettings.isDarkMode @@ -336,9 +298,7 @@ class _AddTaskToTaskcBottomSheetState extends State { : TaskWarriorColors.kLightDialogBackGroundColor, ), onChanged: (String? newValue) { - setState(() { - priority = newValue!; - }); + homeController.priority.value = newValue!; }, items: ['H', 'M', 'L', 'None'] .map>((String value) { @@ -376,26 +336,25 @@ class _AddTaskToTaskcBottomSheetState extends State { ), ), onPressed: () async { - if (formKey.currentState!.validate()) { + if (homeController.formKey.currentState!.validate()) { var task = Tasks( - description: namecontroller.text, + description: homeController.namecontroller.text, status: 'pending', - priority: priority, + priority: homeController.priority.value, entry: DateTime.now().toIso8601String(), id: 0, - project: projectController.text, + project: homeController.projectcontroller.text, uuid: '', urgency: 0, - due: dueString, + due: homeController.dueString.value, // dueString.toIso8601String(), end: '', modified: 'r'); - await taskdb.insertTask(task); - namecontroller.text = ''; - due = null; - priority = 'M'; - project = ''; - setState(() {}); + await homeController.taskdb.insertTask(task); + homeController.namecontroller.text = ''; + homeController.due.value = null; + homeController.priority.value = 'M'; + homeController.projectcontroller.text = ''; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( 'Task Added Successfully!', diff --git a/lib/app/modules/home/views/home_page_floating_action_button.dart b/lib/app/modules/home/views/home_page_floating_action_button.dart index 961b13b7..9770b3f9 100644 --- a/lib/app/modules/home/views/home_page_floating_action_button.dart +++ b/lib/app/modules/home/views/home_page_floating_action_button.dart @@ -32,7 +32,9 @@ class HomePageFloatingActionButton extends StatelessWidget { onPressed: () => (controller.taskchampion.value) ? (showDialog( context: context, - builder: (context) => const AddTaskToTaskcBottomSheet(), + builder: (context) => AddTaskToTaskcBottomSheet( + homeController: controller, + ), ).then((value) { if (controller.isSyncNeeded.value && value != "cancel") { controller.isNeededtoSyncOnStart(context); From c190e1a444b272b480480b67075b8cb5dbb94963 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 19:16:31 +0530 Subject: [PATCH 25/26] fix: complete and delete task logic --- lib/api_service.dart | 23 ++++++++++++++-------- lib/app/modules/home/views/show_tasks.dart | 11 ++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/api_service.dart b/lib/api_service.dart index 5df62b72..3cb5f2cd 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -141,18 +141,24 @@ Future updateTasksInDatabase(List tasks) async { localTask.status, localTask.uuid!, ); + if (localTask.status == 'completed') { + completeTask('email', localTask.uuid!); + } else if (localTask.status == 'deleted') { + deleteTask('email', localTask.uuid!); + } } } } } -Future deleteTask( - String email, String encryptionSecret, String uuid, String taskUuid) async { +Future deleteTask(String email, String taskUuid) async { + var c = await CredentialsStorage.getClientId(); + var e = await CredentialsStorage.getEncryptionSecret(); final url = Uri.parse('$baseUrl/delete-task'); final body = jsonEncode({ 'email': email, - 'encryptionSecret': encryptionSecret, - 'UUID': uuid, + 'encryptionSecret': e, + 'UUID': c, 'taskuuid': taskUuid, }); @@ -175,13 +181,14 @@ Future deleteTask( } } -Future completeTask( - String email, String encryptionSecret, String uuid, String taskUuid) async { +Future completeTask(String email, String taskUuid) async { + var c = await CredentialsStorage.getClientId(); + var e = await CredentialsStorage.getEncryptionSecret(); final url = Uri.parse('$baseUrl/complete-task'); final body = jsonEncode({ 'email': email, - 'encryptionSecret': encryptionSecret, - 'UUID': uuid, + 'encryptionSecret': e, + 'UUID': c, 'taskuuid': taskUuid, }); diff --git a/lib/app/modules/home/views/show_tasks.dart b/lib/app/modules/home/views/show_tasks.dart index 26b987f4..2b45bdc5 100644 --- a/lib/app/modules/home/views/show_tasks.dart +++ b/lib/app/modules/home/views/show_tasks.dart @@ -8,7 +8,6 @@ import 'package:taskwarrior/app/modules/home/views/show_details.dart'; import 'package:taskwarrior/app/utils/constants/palette.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_fonts.dart'; -import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; import 'package:taskwarrior/app/utils/theme/app_settings.dart'; class TaskViewBuilder extends StatelessWidget { @@ -234,23 +233,17 @@ class TaskViewBuilder extends StatelessWidget { } void _markTaskAsCompleted(String uuid) async { - String clientId = (await CredentialsStorage.getClientId()) ?? ''; - String encryptionSecret = - (await CredentialsStorage.getEncryptionSecret()) ?? ''; TaskDatabase taskDatabase = TaskDatabase(); await taskDatabase.open(); taskDatabase.markTaskAsCompleted(uuid); - completeTask('email', encryptionSecret, clientId, uuid); + completeTask('email', uuid); } void _markTaskAsDeleted(String uuid) async { - String clientId = (await CredentialsStorage.getClientId()) ?? ''; - String encryptionSecret = - (await CredentialsStorage.getEncryptionSecret()) ?? ''; TaskDatabase taskDatabase = TaskDatabase(); await taskDatabase.open(); taskDatabase.markTaskAsDeleted(uuid); - deleteTask('email', encryptionSecret, clientId, uuid); + deleteTask('email', uuid); } Color _getPriorityColor(String priority) { From 2d730685c38539f34b8e4c979b7cc028d6d3cd48 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sat, 6 Jul 2024 21:12:58 +0530 Subject: [PATCH 26/26] feat: add confirmation dialog for task details --- lib/app/modules/home/views/show_details.dart | 234 ++++++++++++------- 1 file changed, 147 insertions(+), 87 deletions(-) diff --git a/lib/app/modules/home/views/show_details.dart b/lib/app/modules/home/views/show_details.dart index 7f6dd4c9..038daab1 100644 --- a/lib/app/modules/home/views/show_details.dart +++ b/lib/app/modules/home/views/show_details.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: deprecated_member_use, use_build_context_synchronously import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -50,83 +50,83 @@ class _TaskDetailsState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppSettings.isDarkMode - ? TaskWarriorColors.kprimaryBackgroundColor - : TaskWarriorColors.kLightPrimaryBackgroundColor, - appBar: AppBar( - foregroundColor: TaskWarriorColors.lightGrey, - backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, - title: Text( - 'Task: ${widget.task.description}', - style: GoogleFonts.poppins(color: TaskWarriorColors.white), - ), - ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: ListView( - children: [ - _buildEditableDetail('Description:', description, (value) { - setState(() { - description = value; - hasChanges = true; - }); - }), - _buildEditableDetail('Project:', project, (value) { - setState(() { - project = value; - hasChanges = true; - }); - }), - _buildSelectableDetail('Status:', status, ['pending', 'completed'], - (value) { - setState(() { - status = value; - hasChanges = true; - }); - }), - _buildSelectableDetail('Priority:', priority, ['H', 'M', 'L'], - (value) { - setState(() { - priority = value; - hasChanges = true; - }); - }), - _buildDatePickerDetail('Due:', due, (value) { - setState(() { - due = value; - hasChanges = true; - }); - }), - _buildDetail('UUID:', widget.task.uuid!), - _buildDetail('Urgency:', widget.task.urgency.toString()), - _buildDetail('End:', _buildDate(widget.task.end)), - _buildDetail('Entry:', _buildDate(widget.task.entry)), - _buildDetail('Modified:', _buildDate(widget.task.modified)), - ], + return WillPopScope( + onWillPop: () async { + if (hasChanges) { + final action = await _showUnsavedChangesDialog(context); + if (action == UnsavedChangesAction.cancel) { + return Future.value(false); + } else if (action == UnsavedChangesAction.save) { + await _saveTask(); + } + } + return Future.value(true); + }, + child: Scaffold( + backgroundColor: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryBackgroundColor + : TaskWarriorColors.kLightPrimaryBackgroundColor, + appBar: AppBar( + foregroundColor: TaskWarriorColors.lightGrey, + backgroundColor: TaskWarriorColors.kprimaryBackgroundColor, + title: Text( + 'Task: ${widget.task.description}', + style: GoogleFonts.poppins(color: TaskWarriorColors.white), + ), ), - ), - floatingActionButton: hasChanges - ? FloatingActionButton( - onPressed: () async { - await taskDatabase.saveEditedTaskInDB(widget.task.uuid!, - description, project, status, priority, due); + body: Padding( + padding: const EdgeInsets.all(16.0), + child: ListView( + children: [ + _buildEditableDetail('Description:', description, (value) { setState(() { - hasChanges = false; + description = value; + hasChanges = true; }); - modifyTaskOnTaskwarrior( - description, - project, - due, - priority, - status, - widget.task.uuid!, - ); - // used this for testing - }, - child: const Icon(Icons.save), - ) - : null, + }), + _buildEditableDetail('Project:', project, (value) { + setState(() { + project = value; + hasChanges = true; + }); + }), + _buildSelectableDetail( + 'Status:', status, ['pending', 'completed'], (value) { + setState(() { + status = value; + hasChanges = true; + }); + }), + _buildSelectableDetail('Priority:', priority, ['H', 'M', 'L'], + (value) { + setState(() { + priority = value; + hasChanges = true; + }); + }), + _buildDatePickerDetail('Due:', due, (value) { + setState(() { + due = value; + hasChanges = true; + }); + }), + _buildDetail('UUID:', widget.task.uuid!), + _buildDetail('Urgency:', widget.task.urgency.toString()), + _buildDetail('End:', _buildDate(widget.task.end)), + _buildDetail('Entry:', _buildDate(widget.task.entry)), + _buildDetail('Modified:', _buildDate(widget.task.modified)), + ], + ), + ), + floatingActionButton: hasChanges + ? FloatingActionButton( + onPressed: () async { + await _saveTask(); + }, + child: const Icon(Icons.save), + ) + : null, + ), ); } @@ -169,25 +169,15 @@ class _TaskDetailsState extends State { return Theme( data: Theme.of(context).copyWith( colorScheme: AppSettings.isDarkMode - ? ColorScheme( - brightness: Brightness.dark, + ? ColorScheme.dark( primary: TaskWarriorColors.white, onPrimary: TaskWarriorColors.black, - secondary: TaskWarriorColors.black, - onSecondary: TaskWarriorColors.white, - error: TaskWarriorColors.red, - onError: TaskWarriorColors.black, surface: TaskWarriorColors.black, onSurface: TaskWarriorColors.white, ) - : ColorScheme( - brightness: Brightness.light, + : ColorScheme.light( primary: TaskWarriorColors.black, onPrimary: TaskWarriorColors.white, - secondary: TaskWarriorColors.white, - onSecondary: TaskWarriorColors.black, - error: TaskWarriorColors.red, - onError: TaskWarriorColors.white, surface: TaskWarriorColors.white, onSurface: TaskWarriorColors.black, ), @@ -376,4 +366,74 @@ class _TaskDetailsState extends State { return '-'; } } + + Future _showUnsavedChangesDialog( + BuildContext context) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Utils.showAlertDialog( + title: Text( + 'Unsaved Changes', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + content: Text( + 'You have unsaved changes. What would you like to do?', + style: TextStyle( + color: AppSettings.isDarkMode + ? TaskWarriorColors.kprimaryTextColor + : TaskWarriorColors.kLightPrimaryTextColor), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(UnsavedChangesAction.cancel); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(UnsavedChangesAction.discard); + }, + child: const Text('Don\'t Save'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(UnsavedChangesAction.save); + }, + child: const Text('Save'), + ), + ], + ); + }, + ); + } + + Future _saveTask() async { + await taskDatabase.saveEditedTaskInDB( + widget.task.uuid!, + description, + project, + status, + priority, + due, + ); + setState(() { + hasChanges = false; + }); + modifyTaskOnTaskwarrior( + description, + project, + due, + priority, + status, + widget.task.uuid!, + ); + } } + +enum UnsavedChangesAction { save, discard, cancel }