From 1188a2e74f0c8aa0d6321d8801ba14c77279f88d Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Sat, 3 Jan 2026 13:04:17 +0530 Subject: [PATCH 1/6] Add Deleted status option to filter drawer UI --- lib/app/models/data.dart | 17 ++- lib/app/models/filters.dart | 6 + .../home/controllers/home_controller.dart | 24 +++- .../home/views/filter_drawer_home_page.dart | 136 ++++++++++++------ lib/app/utils/language/bengali_sentences.dart | 2 + lib/app/utils/language/english_sentences.dart | 2 + lib/app/utils/language/french_sentences.dart | 2 + lib/app/utils/language/hindi_sentences.dart | 2 + lib/app/utils/language/marathi_sentences.dart | 2 + lib/app/utils/language/sentences.dart | 1 + lib/app/utils/language/spanish_sentences.dart | 2 + lib/app/utils/taskfunctions/query.dart | 20 ++- test/models/filters_test.dart | 6 + .../language/bengali_sentences_test.dart | 1 + .../language/english_sentences_test.dart | 1 + .../utils/language/french_sentences_test.dart | 1 + test/utils/language/hindi_sentences_test.dart | 1 + .../language/marathi_sentences_test.dart | 1 + test/utils/language/sentences_test.dart | 1 + .../language/spanish_sentences_test.dart | 1 + 20 files changed, 178 insertions(+), 51 deletions(-) diff --git a/lib/app/models/data.dart b/lib/app/models/data.dart index 3fe5c66c..d99e42de 100644 --- a/lib/app/models/data.dart +++ b/lib/app/models/data.dart @@ -61,7 +61,7 @@ class Data { List _completedData() { var data = _allData().where( - (task) => task.status == 'completed' || task.status == 'deleted'); + (task) => task.status == 'completed'); return [ for (var task in data) task.rebuild((b) => b..id = 0), ]; @@ -69,7 +69,7 @@ class Data { List completedData() { var data = _allData().where( - (task) => task.status == 'completed' || task.status == 'deleted'); + (task) => task.status == 'completed'); return data .toList() .asMap() @@ -85,8 +85,19 @@ class Data { ]; } + List deletedData() { + var data = _allData().where( + (task) => task.status == 'deleted'); + return data + .toList() + .asMap() + .entries + .map((entry) => entry.value.rebuild((b) => b..id = entry.key + 1)) + .toList(); + } + List allData() { - var data = pendingData()..addAll(_completedData()); + var data = pendingData()..addAll(_completedData())..addAll(deletedData()); return data; } diff --git a/lib/app/models/filters.dart b/lib/app/models/filters.dart index a2296246..edd6212a 100644 --- a/lib/app/models/filters.dart +++ b/lib/app/models/filters.dart @@ -4,8 +4,10 @@ class Filters { const Filters({ required this.pendingFilter, required this.waitingFilter, + required this.deletedFilter, required this.togglePendingFilter, required this.toggleWaitingFilter, + required this.toggleDeletedFilter, required this.tagFilters, required this.projects, required this.projectFilter, @@ -14,8 +16,12 @@ class Filters { final bool pendingFilter; final bool waitingFilter; + final bool deletedFilter; + final void Function() togglePendingFilter; final void Function() toggleWaitingFilter; + final void Function() toggleDeletedFilter; + final TagFilters tagFilters; final dynamic projects; final String projectFilter; diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index a7b6d353..5d2debad 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -49,6 +49,8 @@ class HomeController extends GetxController { late Storage storage; final RxBool pendingFilter = false.obs; final RxBool waitingFilter = false.obs; + final RxBool deletedFilter = false.obs; + final RxString projectFilter = ''.obs; final RxBool tagUnion = false.obs; final RxString selectedSort = ''.obs; @@ -95,6 +97,7 @@ class HomeController extends GetxController { everAll([ pendingFilter, waitingFilter, + deletedFilter, projectFilter, tagUnion, selectedSort, @@ -232,13 +235,12 @@ class HomeController extends GetxController { void _refreshTasks() { if (pendingFilter.value) { - queriedTasks.value = storage.data - .pendingData() - .where((task) => task.status == 'pending') - .toList(); - } else { - queriedTasks.value = storage.data.completedData(); - } + queriedTasks.value = storage.data.pendingData(); + } else if (deletedFilter.value) { + queriedTasks.value = storage.data.deletedData(); + } else { + queriedTasks.value = storage.data.completedData(); + } if (waitingFilter.value) { var currentTime = DateTime.now(); @@ -341,6 +343,12 @@ class HomeController extends GetxController { _refreshTasks(); } + void toggleDeletedFilter() { + Query(storage.tabs.tab()).togggleDeletedFilter(); + deletedFilter.value = Query(storage.tabs.tab()).getDeletedFilter(); + _refreshTasks(); + } + void toggleProjectFilter(String project) { Query(storage.tabs.tab()).toggleProjectFilter(project); projectFilter.value = Query(storage.tabs.tab()).projectFilter(); @@ -611,8 +619,10 @@ class HomeController extends GetxController { var filters = Filters( pendingFilter: pendingFilter.value, waitingFilter: waitingFilter.value, + deletedFilter: deletedFilter.value, togglePendingFilter: togglePendingFilter, toggleWaitingFilter: toggleWaitingFilter, + toggleDeletedFilter: toggleDeletedFilter, projects: projects, projectFilter: projectFilter.value, toggleProjectFilter: toggleProjectFilter, 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 085a969d..aafd464a 100644 --- a/lib/app/modules/home/views/filter_drawer_home_page.dart +++ b/lib/app/modules/home/views/filter_drawer_home_page.dart @@ -79,46 +79,102 @@ class FilterDrawer extends StatelessWidget { borderRadius: BorderRadius.circular(8), border: Border.all(color: TaskWarriorColors.borderColor), ), - child: ListTile( - contentPadding: const EdgeInsets.only( - left: 8, - ), - title: RichText( - key: homeController.statusKey, - maxLines: 2, - text: TextSpan( - children: [ - TextSpan( - text: - '${SentenceManager(currentLanguage: homeController.selectedLanguage.value).sentences.filterDrawerStatus} : ', - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: tColors.primaryTextColor, - )), - TextSpan( - text: filters.pendingFilter - ? SentenceManager( - currentLanguage: homeController - .selectedLanguage.value) - .sentences - .filterDrawerPending - : SentenceManager( - currentLanguage: homeController - .selectedLanguage.value) - .sentences - .filterDrawerCompleted, - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: tColors.primaryTextColor, - )), - ], - ), - ), - onTap: filters.togglePendingFilter, - textColor: tColors.primaryTextColor, - ), + child: Column(children: [ + SwitchListTile( + key: homeController.statusKey, + title: Text( + SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.filterDrawerPending, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: tColors.primaryTextColor, + ), + ), + value: filters.pendingFilter, + onChanged: (_) => filters.togglePendingFilter(), + ), + + // Completed + SwitchListTile( + title: Text( + SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.filterDrawerCompleted, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: tColors.primaryTextColor, + ), + ), + value: !filters.pendingFilter && !filters.deletedFilter, + onChanged: (_) { + if (filters.deletedFilter) { + filters.toggleDeletedFilter(); + } + if (filters.pendingFilter) { + filters.togglePendingFilter(); + } + }, + ), + + // 🆕 Deleted + SwitchListTile( + title: Text( + SentenceManager( + currentLanguage: homeController.selectedLanguage.value, + ).sentences.filterDrawerDeleted, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: tColors.primaryTextColor, + ), + ), + value: filters.deletedFilter, + onChanged: (_) => filters.toggleDeletedFilter(), + ), + ],) + // child: ListTile( + // contentPadding: const EdgeInsets.only( + // left: 8, + // ), + // title: RichText( + // key: homeController.statusKey, + // maxLines: 2, + // text: TextSpan( + // children: [ + // TextSpan( + // text: + // '${SentenceManager(currentLanguage: homeController.selectedLanguage.value).sentences.filterDrawerStatus} : ', + // style: TextStyle( + // fontFamily: FontFamily.poppins, + // fontSize: TaskWarriorFonts.fontSizeMedium, + // color: tColors.primaryTextColor, + // )), + // TextSpan( + // text: filters.pendingFilter + // ? SentenceManager( + // currentLanguage: homeController + // .selectedLanguage.value) + // .sentences + // .filterDrawerPending + // : SentenceManager( + // currentLanguage: homeController + // .selectedLanguage.value) + // .sentences + // .filterDrawerCompleted, + // style: TextStyle( + // fontFamily: FontFamily.poppins, + // fontSize: TaskWarriorFonts.fontSizeMedium, + // color: tColors.primaryTextColor, + // )), + // ], + // ), + // ), + // onTap: filters.togglePendingFilter, + // textColor: tColors.primaryTextColor, + // ), ), const Divider( color: Color.fromARGB(0, 48, 46, 46), diff --git a/lib/app/utils/language/bengali_sentences.dart b/lib/app/utils/language/bengali_sentences.dart index 997f695b..5858d188 100644 --- a/lib/app/utils/language/bengali_sentences.dart +++ b/lib/app/utils/language/bengali_sentences.dart @@ -127,6 +127,8 @@ class BengaliSentences extends Sentences { @override String get filterDrawerCompleted => 'সম্পন্ন'; @override + String get filterDrawerDeleted => 'মুছে ফেলা হয়েছে'; + @override String get filterDrawerFilterTagBy => 'ট্যাগ দ্বারা ফিল্টার করুন'; @override String get filterDrawerAND => 'এবং'; diff --git a/lib/app/utils/language/english_sentences.dart b/lib/app/utils/language/english_sentences.dart index efddd0c0..a622df96 100644 --- a/lib/app/utils/language/english_sentences.dart +++ b/lib/app/utils/language/english_sentences.dart @@ -140,6 +140,8 @@ class EnglishSentences extends Sentences { @override String get filterDrawerCompleted => 'Completed'; @override + String get filterDrawerDeleted => 'Deleted'; + @override String get filterDrawerFilterTagBy => 'Filter Tag By'; @override String get filterDrawerAND => 'AND'; diff --git a/lib/app/utils/language/french_sentences.dart b/lib/app/utils/language/french_sentences.dart index 8f6ac18f..7d5b6cfb 100644 --- a/lib/app/utils/language/french_sentences.dart +++ b/lib/app/utils/language/french_sentences.dart @@ -126,6 +126,8 @@ class FrenchSentences extends Sentences { @override String get filterDrawerCompleted => 'Complété'; @override + String get filterDrawerDeleted => 'Supprimé'; + @override String get filterDrawerFilterTagBy => 'Filtrer par tag'; @override String get filterDrawerAND => 'et'; diff --git a/lib/app/utils/language/hindi_sentences.dart b/lib/app/utils/language/hindi_sentences.dart index 64da4f91..6227ffeb 100644 --- a/lib/app/utils/language/hindi_sentences.dart +++ b/lib/app/utils/language/hindi_sentences.dart @@ -140,6 +140,8 @@ class HindiSentences extends Sentences { @override String get filterDrawerCompleted => 'पूर्ण'; @override + String get filterDrawerDeleted => 'हटाए गए'; + @override String get filterDrawerFilterTagBy => 'टैग से फ़िल्टर करें'; @override String get filterDrawerAND => 'और'; diff --git a/lib/app/utils/language/marathi_sentences.dart b/lib/app/utils/language/marathi_sentences.dart index 12c2c45e..60b9a83d 100644 --- a/lib/app/utils/language/marathi_sentences.dart +++ b/lib/app/utils/language/marathi_sentences.dart @@ -127,6 +127,8 @@ class MarathiSentences extends Sentences { @override String get filterDrawerCompleted => 'पूर्ण'; @override + String get filterDrawerDeleted => 'काढून टाकलेले'; + @override String get filterDrawerFilterTagBy => 'टॅगवर फिल्टर करा'; @override String get filterDrawerAND => 'आणि'; diff --git a/lib/app/utils/language/sentences.dart b/lib/app/utils/language/sentences.dart index 4de41df3..58afd8cb 100644 --- a/lib/app/utils/language/sentences.dart +++ b/lib/app/utils/language/sentences.dart @@ -84,6 +84,7 @@ abstract class Sentences { String get filterDrawerShowWaiting; String get filterDrawerPending; String get filterDrawerCompleted; + String get filterDrawerDeleted; String get filterDrawerFilterTagBy; String get filterDrawerAND; String get filterDrawerOR; diff --git a/lib/app/utils/language/spanish_sentences.dart b/lib/app/utils/language/spanish_sentences.dart index 4281e103..00e6935a 100644 --- a/lib/app/utils/language/spanish_sentences.dart +++ b/lib/app/utils/language/spanish_sentences.dart @@ -128,6 +128,8 @@ class SpanishSentences extends Sentences { @override String get filterDrawerCompleted => 'Completado'; @override + String get filterDrawerDeleted => 'Eliminado'; + @override String get filterDrawerFilterTagBy => 'Filtrar por etiqueta'; @override String get filterDrawerAND => 'y'; diff --git a/lib/app/utils/taskfunctions/query.dart b/lib/app/utils/taskfunctions/query.dart index 3040830c..c60f083a 100644 --- a/lib/app/utils/taskfunctions/query.dart +++ b/lib/app/utils/taskfunctions/query.dart @@ -9,6 +9,7 @@ class Query { File get _selectedSort => File('${_queryStorage.path}/selectedSort'); File get _pendingFilter => File('${_queryStorage.path}/pendingFilter'); File get _waitingFilter => File('${_queryStorage.path}/waitingFilter'); + File get _deletedFilter => File('${_queryStorage.path}/deletedFilter'); File get _projectFilter => File('${_queryStorage.path}/projectFilter'); File get _tagUnion => File('${_queryStorage.path}/tagUnion'); File get _selectedTags => File('${_queryStorage.path}/selectedTags'); @@ -59,6 +60,21 @@ class Query { return json.decode(_waitingFilter.readAsStringSync()); } + void togggleDeletedFilter() { + _deletedFilter.writeAsStringSync( + json.encode(!getDeletedFilter()), + ); + } + + bool getDeletedFilter() { + if (!_deletedFilter.existsSync()) { + _deletedFilter + ..createSync(recursive: true) + ..writeAsStringSync('true'); + } + return json.decode(_deletedFilter.readAsStringSync()); + } + void toggleProjectFilter(String project) { _projectFilter.writeAsStringSync( (project == projectFilter()) ? '' : project, @@ -107,6 +123,8 @@ class Query { ..createSync(recursive: true) ..writeAsStringSync(json.encode([])); } - return (json.decode(_selectedTags.readAsStringSync()) as List).cast().toSet(); + return (json.decode(_selectedTags.readAsStringSync()) as List) + .cast() + .toSet(); } } diff --git a/test/models/filters_test.dart b/test/models/filters_test.dart index 8d577349..306feede 100644 --- a/test/models/filters_test.dart +++ b/test/models/filters_test.dart @@ -7,6 +7,7 @@ void main() { late Filters filters; late bool pendingFilter; late bool waitingFilter; + late bool deletedFilter; late String projectFilter; late bool tagUnion; late Map tags; @@ -14,6 +15,7 @@ void main() { setUp(() { pendingFilter = false; waitingFilter = false; + deletedFilter = false; projectFilter = 'TestProject'; tagUnion = false; tags = { @@ -24,12 +26,16 @@ void main() { filters = Filters( pendingFilter: pendingFilter, waitingFilter: waitingFilter, + deletedFilter: deletedFilter, togglePendingFilter: () { pendingFilter = !pendingFilter; }, toggleWaitingFilter: () { waitingFilter = !waitingFilter; }, + toggleDeletedFilter: () { + deletedFilter = !deletedFilter; + }, tagFilters: TagFilters( tagUnion: tagUnion, toggleTagUnion: () { diff --git a/test/utils/language/bengali_sentences_test.dart b/test/utils/language/bengali_sentences_test.dart index 413c587c..7972ac7e 100644 --- a/test/utils/language/bengali_sentences_test.dart +++ b/test/utils/language/bengali_sentences_test.dart @@ -72,6 +72,7 @@ void main() { expect(bengali.filterDrawerShowWaiting, 'অপেক্ষা প্রদর্শন করুন'); expect(bengali.filterDrawerPending, 'মুলতুবি'); expect(bengali.filterDrawerCompleted, 'সম্পন্ন'); + expect(bengali.filterDrawerDeleted, 'মুছে ফেলা হয়েছে'); expect(bengali.filterDrawerFilterTagBy, 'ট্যাগ দ্বারা ফিল্টার করুন'); expect(bengali.filterDrawerAND, 'এবং'); expect(bengali.filterDrawerOR, 'অথবা'); diff --git a/test/utils/language/english_sentences_test.dart b/test/utils/language/english_sentences_test.dart index a2e14071..d1656f47 100644 --- a/test/utils/language/english_sentences_test.dart +++ b/test/utils/language/english_sentences_test.dart @@ -71,6 +71,7 @@ void main() { expect(english.filterDrawerShowWaiting, 'Show Waiting'); expect(english.filterDrawerPending, 'Pending'); expect(english.filterDrawerCompleted, 'Completed'); + expect(english.filterDrawerDeleted, 'Deleted'); expect(english.filterDrawerFilterTagBy, 'Filter Tag By'); expect(english.filterDrawerAND, 'AND'); expect(english.filterDrawerOR, 'OR'); diff --git a/test/utils/language/french_sentences_test.dart b/test/utils/language/french_sentences_test.dart index 023647ac..1559fde0 100644 --- a/test/utils/language/french_sentences_test.dart +++ b/test/utils/language/french_sentences_test.dart @@ -73,6 +73,7 @@ void main() { expect(french.filterDrawerShowWaiting, 'Afficher les en attente'); expect(french.filterDrawerPending, 'En attente'); expect(french.filterDrawerCompleted, 'Complété'); + expect(french.filterDrawerDeleted, 'Supprimé'); expect(french.filterDrawerFilterTagBy, 'Filtrer par tag'); expect(french.filterDrawerAND, 'et'); expect(french.filterDrawerOR, 'ou'); diff --git a/test/utils/language/hindi_sentences_test.dart b/test/utils/language/hindi_sentences_test.dart index 45c41702..9c309cd4 100644 --- a/test/utils/language/hindi_sentences_test.dart +++ b/test/utils/language/hindi_sentences_test.dart @@ -73,6 +73,7 @@ void main() { expect(hindi.filterDrawerShowWaiting, 'इंतजार दिखाएं'); expect(hindi.filterDrawerPending, 'अपूर्ण'); expect(hindi.filterDrawerCompleted, 'पूर्ण'); + expect(hindi.filterDrawerDeleted, 'हटाए गए'); expect(hindi.filterDrawerFilterTagBy, 'टैग से फ़िल्टर करें'); expect(hindi.filterDrawerAND, 'और'); expect(hindi.filterDrawerOR, 'या'); diff --git a/test/utils/language/marathi_sentences_test.dart b/test/utils/language/marathi_sentences_test.dart index 57e8104c..50806c1d 100644 --- a/test/utils/language/marathi_sentences_test.dart +++ b/test/utils/language/marathi_sentences_test.dart @@ -72,6 +72,7 @@ void main() { expect(marathi.filterDrawerShowWaiting, 'वाट दाखवा'); expect(marathi.filterDrawerPending, 'प्रलंबित'); expect(marathi.filterDrawerCompleted, 'पूर्ण'); + expect(marathi.filterDrawerDeleted, 'काढून टाकलेले'); expect(marathi.filterDrawerFilterTagBy, 'टॅगवर फिल्टर करा'); expect(marathi.filterDrawerAND, 'आणि'); expect(marathi.filterDrawerOR, 'किंवा'); diff --git a/test/utils/language/sentences_test.dart b/test/utils/language/sentences_test.dart index 7c073bdf..2b88216c 100644 --- a/test/utils/language/sentences_test.dart +++ b/test/utils/language/sentences_test.dart @@ -84,6 +84,7 @@ void main() { expect(sentences.filterDrawerShowWaiting, isA()); expect(sentences.filterDrawerPending, isA()); expect(sentences.filterDrawerCompleted, isA()); + expect(sentences.filterDrawerDeleted, isA()); expect(sentences.filterDrawerFilterTagBy, isA()); expect(sentences.filterDrawerAND, isA()); expect(sentences.filterDrawerOR, isA()); diff --git a/test/utils/language/spanish_sentences_test.dart b/test/utils/language/spanish_sentences_test.dart index f51722c4..674e431b 100644 --- a/test/utils/language/spanish_sentences_test.dart +++ b/test/utils/language/spanish_sentences_test.dart @@ -74,6 +74,7 @@ void main() { expect(spanish.filterDrawerShowWaiting, 'Mostrar pendientes'); expect(spanish.filterDrawerPending, 'Pendiente'); expect(spanish.filterDrawerCompleted, 'Completado'); + expect(spanish.filterDrawerDeleted, 'Eliminado'); expect(spanish.filterDrawerFilterTagBy, 'Filtrar por etiqueta'); expect(spanish.filterDrawerAND, 'y'); expect(spanish.filterDrawerOR, 'o'); From c420fe1434bd4d0ddafe9e75d461da0c7140fb99 Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Sat, 3 Jan 2026 17:04:57 +0530 Subject: [PATCH 2/6] feat: Add delete option instead of toggle --- lib/app/models/filters.dart | 2 - .../home/controllers/home_controller.dart | 52 ++++--- .../home/views/filter_drawer_home_page.dart | 142 ++++++------------ lib/app/utils/taskfunctions/query.dart | 8 +- test/models/filters_test.dart | 3 - 5 files changed, 78 insertions(+), 129 deletions(-) diff --git a/lib/app/models/filters.dart b/lib/app/models/filters.dart index edd6212a..524208a4 100644 --- a/lib/app/models/filters.dart +++ b/lib/app/models/filters.dart @@ -7,7 +7,6 @@ class Filters { required this.deletedFilter, required this.togglePendingFilter, required this.toggleWaitingFilter, - required this.toggleDeletedFilter, required this.tagFilters, required this.projects, required this.projectFilter, @@ -20,7 +19,6 @@ class Filters { final void Function() togglePendingFilter; final void Function() toggleWaitingFilter; - final void Function() toggleDeletedFilter; final TagFilters tagFilters; final dynamic projects; diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 5d2debad..ad580fcc 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -213,21 +213,21 @@ class HomeController extends GetxController { } void _profileSet() { - pendingFilter.value = Query(storage.tabs.tab()).getPendingFilter(); - if (!Query(storage.tabs.tab()).getWaitingFilter()) { - waitingFilter.value = Query(storage.tabs.tab()).getWaitingFilter(); - } else { - Query(storage.tabs.tab()).toggleWaitingFilter(); - waitingFilter.value = Query(storage.tabs.tab()).getWaitingFilter(); - } + final bool isPending = Query(storage.tabs.tab()).getPendingFilter(); + + pendingFilter.value = isPending; + deletedFilter.value = false; + waitingFilter.value = Query(storage.tabs.tab()).getWaitingFilter(); + projectFilter.value = Query(storage.tabs.tab()).projectFilter(); tagUnion.value = Query(storage.tabs.tab()).tagUnion(); selectedSort.value = Query(storage.tabs.tab()).getSelectedSort(); - selectedTags.addAll(Query(storage.tabs.tab()).getSelectedTags()); + selectedTags.assignAll(Query(storage.tabs.tab()).getSelectedTags()); _refreshTasks(); pendingTags.value = _pendingTags(); projects.value = _projects(); + if (searchVisible.value) { toggleSearch(); } @@ -235,12 +235,12 @@ class HomeController extends GetxController { void _refreshTasks() { if (pendingFilter.value) { - queriedTasks.value = storage.data.pendingData(); - } else if (deletedFilter.value) { - queriedTasks.value = storage.data.deletedData(); - } else { - queriedTasks.value = storage.data.completedData(); - } + queriedTasks.value = storage.data.pendingData(); + } else if (deletedFilter.value) { + queriedTasks.value = storage.data.deletedData(); + } else { + queriedTasks.value = storage.data.completedData(); + } if (waitingFilter.value) { var currentTime = DateTime.now(); @@ -343,12 +343,6 @@ class HomeController extends GetxController { _refreshTasks(); } - void toggleDeletedFilter() { - Query(storage.tabs.tab()).togggleDeletedFilter(); - deletedFilter.value = Query(storage.tabs.tab()).getDeletedFilter(); - _refreshTasks(); - } - void toggleProjectFilter(String project) { Query(storage.tabs.tab()).toggleProjectFilter(project); projectFilter.value = Query(storage.tabs.tab()).projectFilter(); @@ -382,6 +376,23 @@ class HomeController extends GetxController { _refreshTasks(); } + void toggleStatusFilter() { + if (pendingFilter.value) { + // Pending → Completed + pendingFilter.value = false; + deletedFilter.value = false; + } else if (!pendingFilter.value && !deletedFilter.value) { + // Completed → Deleted + deletedFilter.value = true; + } else { + // Deleted → Pending + pendingFilter.value = true; + deletedFilter.value = false; + } + + _refreshTasks(); + } + Task getTask(String uuid) { return storage.data.getTask(uuid); } @@ -622,7 +633,6 @@ class HomeController extends GetxController { deletedFilter: deletedFilter.value, togglePendingFilter: togglePendingFilter, toggleWaitingFilter: toggleWaitingFilter, - toggleDeletedFilter: toggleDeletedFilter, projects: projects, projectFilter: projectFilter.value, toggleProjectFilter: toggleProjectFilter, 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 aafd464a..a08e55a3 100644 --- a/lib/app/modules/home/views/filter_drawer_home_page.dart +++ b/lib/app/modules/home/views/filter_drawer_home_page.dart @@ -79,102 +79,52 @@ class FilterDrawer extends StatelessWidget { borderRadius: BorderRadius.circular(8), border: Border.all(color: TaskWarriorColors.borderColor), ), - child: Column(children: [ - SwitchListTile( - key: homeController.statusKey, - title: Text( - SentenceManager( - currentLanguage: homeController.selectedLanguage.value, - ).sentences.filterDrawerPending, - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: tColors.primaryTextColor, - ), - ), - value: filters.pendingFilter, - onChanged: (_) => filters.togglePendingFilter(), - ), - - // Completed - SwitchListTile( - title: Text( - SentenceManager( - currentLanguage: homeController.selectedLanguage.value, - ).sentences.filterDrawerCompleted, - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: tColors.primaryTextColor, - ), - ), - value: !filters.pendingFilter && !filters.deletedFilter, - onChanged: (_) { - if (filters.deletedFilter) { - filters.toggleDeletedFilter(); - } - if (filters.pendingFilter) { - filters.togglePendingFilter(); - } - }, - ), - - // 🆕 Deleted - SwitchListTile( - title: Text( - SentenceManager( - currentLanguage: homeController.selectedLanguage.value, - ).sentences.filterDrawerDeleted, - style: TextStyle( - fontFamily: FontFamily.poppins, - fontSize: TaskWarriorFonts.fontSizeMedium, - color: tColors.primaryTextColor, - ), - ), - value: filters.deletedFilter, - onChanged: (_) => filters.toggleDeletedFilter(), - ), - ],) - // child: ListTile( - // contentPadding: const EdgeInsets.only( - // left: 8, - // ), - // title: RichText( - // key: homeController.statusKey, - // maxLines: 2, - // text: TextSpan( - // children: [ - // TextSpan( - // text: - // '${SentenceManager(currentLanguage: homeController.selectedLanguage.value).sentences.filterDrawerStatus} : ', - // style: TextStyle( - // fontFamily: FontFamily.poppins, - // fontSize: TaskWarriorFonts.fontSizeMedium, - // color: tColors.primaryTextColor, - // )), - // TextSpan( - // text: filters.pendingFilter - // ? SentenceManager( - // currentLanguage: homeController - // .selectedLanguage.value) - // .sentences - // .filterDrawerPending - // : SentenceManager( - // currentLanguage: homeController - // .selectedLanguage.value) - // .sentences - // .filterDrawerCompleted, - // style: TextStyle( - // fontFamily: FontFamily.poppins, - // fontSize: TaskWarriorFonts.fontSizeMedium, - // color: tColors.primaryTextColor, - // )), - // ], - // ), - // ), - // onTap: filters.togglePendingFilter, - // textColor: tColors.primaryTextColor, - // ), + child: ListTile( + contentPadding: const EdgeInsets.only(left: 8), + onTap: + homeController.toggleStatusFilter, + title: RichText( + key: homeController.statusKey, + maxLines: 2, + text: TextSpan( + children: [ + TextSpan( + text: + '${SentenceManager(currentLanguage: homeController.selectedLanguage.value).sentences.filterDrawerStatus} : ', + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: tColors.primaryTextColor, + ), + ), + TextSpan( + text: filters.deletedFilter + ? SentenceManager( + currentLanguage: + homeController.selectedLanguage.value) + .sentences + .filterDrawerDeleted + : filters.pendingFilter + ? SentenceManager( + currentLanguage: homeController + .selectedLanguage.value) + .sentences + .filterDrawerPending + : SentenceManager( + currentLanguage: homeController + .selectedLanguage.value) + .sentences + .filterDrawerCompleted, + style: TextStyle( + fontFamily: FontFamily.poppins, + fontSize: TaskWarriorFonts.fontSizeMedium, + color: tColors.primaryTextColor, + ), + ), + ], + ), + ), + ), ), const Divider( color: Color.fromARGB(0, 48, 46, 46), diff --git a/lib/app/utils/taskfunctions/query.dart b/lib/app/utils/taskfunctions/query.dart index c60f083a..44c24062 100644 --- a/lib/app/utils/taskfunctions/query.dart +++ b/lib/app/utils/taskfunctions/query.dart @@ -60,17 +60,11 @@ class Query { return json.decode(_waitingFilter.readAsStringSync()); } - void togggleDeletedFilter() { - _deletedFilter.writeAsStringSync( - json.encode(!getDeletedFilter()), - ); - } - bool getDeletedFilter() { if (!_deletedFilter.existsSync()) { _deletedFilter ..createSync(recursive: true) - ..writeAsStringSync('true'); + ..writeAsStringSync('false'); } return json.decode(_deletedFilter.readAsStringSync()); } diff --git a/test/models/filters_test.dart b/test/models/filters_test.dart index 306feede..be225d5f 100644 --- a/test/models/filters_test.dart +++ b/test/models/filters_test.dart @@ -33,9 +33,6 @@ void main() { toggleWaitingFilter: () { waitingFilter = !waitingFilter; }, - toggleDeletedFilter: () { - deletedFilter = !deletedFilter; - }, tagFilters: TagFilters( tagUnion: tagUnion, toggleTagUnion: () { From 6cbccef7adc9d45b801a1223909cfb94c26e9123 Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Sat, 3 Jan 2026 17:53:50 +0530 Subject: [PATCH 3/6] fix: It can on any profile server --- .../home/controllers/home_controller.dart | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index ad580fcc..e3fd4eb7 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -234,70 +234,77 @@ class HomeController extends GetxController { } void _refreshTasks() { + List baseTasks; + if (pendingFilter.value) { - queriedTasks.value = storage.data.pendingData(); + baseTasks = storage.data.pendingData(); } else if (deletedFilter.value) { - queriedTasks.value = storage.data.deletedData(); + baseTasks = storage.data.deletedData(); } else { - queriedTasks.value = storage.data.completedData(); + baseTasks = storage.data.completedData(); } + queriedTasks.assignAll(baseTasks); + if (waitingFilter.value) { - var currentTime = DateTime.now(); - queriedTasks.value = queriedTasks - .where((task) => task.wait != null && task.wait!.isAfter(currentTime)) - .toList(); + final now = DateTime.now(); + queriedTasks.value = queriedTasks.where((task) { + return task.wait != null && task.wait!.isAfter(now); + }).toList(); } if (projectFilter.value.isNotEmpty) { queriedTasks.value = queriedTasks.where((task) { - if (task.project == null) { - return false; - } else { - return task.project!.startsWith(projectFilter.value); - } + return task.project?.startsWith(projectFilter.value) ?? false; }).toList(); } queriedTasks.value = queriedTasks.where((task) { - var tags = task.tags?.toSet() ?? {}; + final tags = task.tags?.toSet() ?? {}; + + if (selectedTags.isEmpty) return true; + if (tagUnion.value) { - if (selectedTags.isEmpty) { - return true; - } - return selectedTags.any((tag) => (tag.startsWith('+')) - ? tags.contains(tag.substring(1)) - : !tags.contains(tag.substring(1))); + return selectedTags.any((tag) { + final clean = tag.substring(1); + return tag.startsWith('+') + ? tags.contains(clean) + : !tags.contains(clean); + }); } else { - return selectedTags.every((tag) => (tag.startsWith('+')) - ? tags.contains(tag.substring(1)) - : !tags.contains(tag.substring(1))); + return selectedTags.every((tag) { + final clean = tag.substring(1); + return tag.startsWith('+') + ? tags.contains(clean) + : !tags.contains(clean); + }); } }).toList(); - var sortColumn = - selectedSort.value.substring(0, selectedSort.value.length - 1); - var ascending = selectedSort.value.endsWith('+'); - queriedTasks.sort((a, b) { - int result; - if (sortColumn == 'id') { - result = a.id!.compareTo(b.id!); - } else { - result = compareTasks(sortColumn)(a, b); - } - return ascending ? result : -result; - }); + if (selectedSort.value.isNotEmpty) { + final column = + selectedSort.value.substring(0, selectedSort.value.length - 1); + final ascending = selectedSort.value.endsWith('+'); + + queriedTasks.sort((a, b) { + final result = column == 'id' + ? a.id!.compareTo(b.id!) + : compareTasks(column)(a, b); + return ascending ? result : -result; + }); + } searchedTasks.assignAll(queriedTasks); - var searchTerm = searchController.text; - if (searchVisible.value) { - searchedTasks.value = searchedTasks - .where((task) => - task.description.contains(searchTerm) || - (task.annotations?.asList() ?? []).any( - (annotation) => annotation.description.contains(searchTerm))) - .toList(); + if (searchVisible.value && searchController.text.isNotEmpty) { + final term = searchController.text.toLowerCase(); + searchedTasks.value = searchedTasks.where((task) { + return task.description.toLowerCase().contains(term) || + (task.annotations?.asList() ?? []).any( + (a) => a.description.toLowerCase().contains(term), + ); + }).toList(); } + pendingTags.value = _pendingTags(); projects.value = _projects(); } From c2b18d34f74f9fdb22b90ccece48d5e94c5b197b Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Sun, 1 Feb 2026 14:17:57 +0530 Subject: [PATCH 4/6] fix: disable delete filter for taskchampion/taskReplica --- .../home/controllers/home_controller.dart | 136 ++++++++++++------ 1 file changed, 90 insertions(+), 46 deletions(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index cb3bb483..bad16746 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -195,6 +195,10 @@ class HomeController extends GetxController { final SharedPreferences prefs = await SharedPreferences.getInstance(); taskchampion.value = prefs.getBool('settings_taskc') ?? false; taskReplica.value = prefs.getBool('settings_taskr_repl') ?? false; + + if (taskchampion.value || taskReplica.value) { + deletedFilter.value = false; + } } Future refreshReplicaTasks() async { @@ -241,80 +245,113 @@ class HomeController extends GetxController { void _refreshTasks() { List baseTasks; + if (taskchampion.value || taskReplica.value) { + final source = _dedupeReplicaTasks(tasksFromReplica); - if (pendingFilter.value) { - baseTasks = storage.data.pendingData(); - } else if (deletedFilter.value) { - baseTasks = storage.data.deletedData(); + if (pendingFilter.value) { + baseTasks = source.where((t) => t.status == 'pending').map(_mapReplicaToTask).toList(); + } else { + baseTasks = source + .where((t) => t.status == 'completed').map(_mapReplicaToTask).toList(); + } } else { - baseTasks = storage.data.completedData(); + if (pendingFilter.value) { + baseTasks = storage.data.pendingData(); + } else if (deletedFilter.value) { + baseTasks = storage.data.deletedData(); + } else { + baseTasks = storage.data.completedData(); + } } - queriedTasks.assignAll(baseTasks); - if (waitingFilter.value) { - final now = DateTime.now(); - queriedTasks.value = queriedTasks.where((task) { - return task.wait != null && task.wait!.isAfter(now); - }).toList(); + var currentTime = DateTime.now(); + queriedTasks.value = queriedTasks + .where((task) => + task.wait != null && task.wait!.isAfter(currentTime)) + .toList(); } if (projectFilter.value.isNotEmpty) { queriedTasks.value = queriedTasks.where((task) { - return task.project?.startsWith(projectFilter.value) ?? false; + if(task.project == null) { + return false; + } else { + return task.project!.startsWith(projectFilter.value); + } }).toList(); } queriedTasks.value = queriedTasks.where((task) { - final tags = task.tags?.toSet() ?? {}; - - if (selectedTags.isEmpty) return true; - - if (tagUnion.value) { - return selectedTags.any((tag) { - final clean = tag.substring(1); - return tag.startsWith('+') - ? tags.contains(clean) - : !tags.contains(clean); - }); - } else { - return selectedTags.every((tag) { - final clean = tag.substring(1); - return tag.startsWith('+') - ? tags.contains(clean) - : !tags.contains(clean); - }); + var tags = task.tags?.toSet() ?? {}; + if(tagUnion.value) { + if (selectedTags.isEmpty){ + return true; + } + return selectedTags.any((tag) => (tag.startsWith('+')) + ? tags.contains(tag.substring(1)) + : !tags.contains(tag.substring(1))); + } + else { + return selectedTags.every((tag) => (tag.startsWith('+')) + ? tags.contains(tag.substring(1)) + : !tags.contains(tag.substring(1))); } }).toList(); - if (selectedSort.value.isNotEmpty) { - final column = + var sortcolumn = selectedSort.value.substring(0, selectedSort.value.length - 1); - final ascending = selectedSort.value.endsWith('+'); - + var ascending = selectedSort.value.endsWith('+'); queriedTasks.sort((a, b) { - final result = column == 'id' - ? a.id!.compareTo(b.id!) - : compareTasks(column)(a, b); + int result; + if (sortcolumn == 'id') { + result = a.id!.compareTo(b.id!); + } else { + result = compareTasks(sortcolumn)(a, b); + } return ascending ? result : -result; }); - } + searchedTasks.assignAll(queriedTasks); - if (searchVisible.value && searchController.text.isNotEmpty) { - final term = searchController.text.toLowerCase(); - searchedTasks.value = searchedTasks.where((task) { - return task.description.toLowerCase().contains(term) || + var searchterm = searchController.text; + if (searchVisible.value) { + searchedTasks.value = searchedTasks + .where((task) => + task.description.contains(searchterm) || (task.annotations?.asList() ?? []).any( - (a) => a.description.toLowerCase().contains(term), - ); - }).toList(); + (a) => a.description.contains(searchterm), + )).toList(); } - pendingTags.value = _pendingTags(); projects.value = _projects(); } + Task _mapReplicaToTask(TaskForReplica t) { + return Task((b) => b + ..description = t.description + ..project = t.project + ..priority = t.priority + ..status = t.status + ..tags.replace(t.tags ?? [])); + } + + List _dedupeReplicaTasks( + List source, + ) { + final Map latest = {}; + + for (final task in source) { + final existing = latest[task.uuid]; + + if (existing == null || (task.modified ?? 0) > (existing.modified ?? 0)) { + latest[task.uuid] = task; + } + } + + return latest.values.toList(); + } + Map _pendingTags() { var frequency = tagFrequencies(storage.data.pendingData()); var modified = tagsLastModified(storage.data.pendingData()); @@ -390,6 +427,13 @@ class HomeController extends GetxController { } void toggleStatusFilter() { + // for TaskChampion and Replica, only Pending and Completed statuses are relevant + if (taskchampion.value || taskReplica.value) { + pendingFilter.value = !pendingFilter.value; + deletedFilter.value = false; + _refreshTasks(); + return; + } if (pendingFilter.value) { // Pending → Completed pendingFilter.value = false; From 97b69fa0ee8458ec5b93539b37c111fd7ccd196b Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Sun, 1 Feb 2026 16:48:53 +0530 Subject: [PATCH 5/6] fix: disable delete filter for taskchampion or taskReplica --- .../home/controllers/home_controller.dart | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index bad16746..2090242a 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -242,7 +242,7 @@ class HomeController extends GetxController { toggleSearch(); } } - + void _refreshTasks() { List baseTasks; if (taskchampion.value || taskReplica.value) { @@ -265,68 +265,69 @@ class HomeController extends GetxController { } queriedTasks.assignAll(baseTasks); if (waitingFilter.value) { - var currentTime = DateTime.now(); - queriedTasks.value = queriedTasks - .where((task) => - task.wait != null && task.wait!.isAfter(currentTime)) - .toList(); + final now = DateTime.now(); + queriedTasks.value = queriedTasks.where((task) { + return task.wait != null && task.wait!.isAfter(now); + }).toList(); } if (projectFilter.value.isNotEmpty) { queriedTasks.value = queriedTasks.where((task) { - if(task.project == null) { - return false; - } else { - return task.project!.startsWith(projectFilter.value); - } + return task.project?.startsWith(projectFilter.value) ?? false; }).toList(); } queriedTasks.value = queriedTasks.where((task) { - var tags = task.tags?.toSet() ?? {}; - if(tagUnion.value) { - if (selectedTags.isEmpty){ - return true; - } - return selectedTags.any((tag) => (tag.startsWith('+')) - ? tags.contains(tag.substring(1)) - : !tags.contains(tag.substring(1))); - } - else { - return selectedTags.every((tag) => (tag.startsWith('+')) - ? tags.contains(tag.substring(1)) - : !tags.contains(tag.substring(1))); + final tags = task.tags?.toSet() ?? {}; + + if (selectedTags.isEmpty) return true; + + if (tagUnion.value) { + return selectedTags.any((tag) { + final clean = tag.substring(1); + return tag.startsWith('+') + ? tags.contains(clean) + : !tags.contains(clean); + }); + } else { + return selectedTags.every((tag) { + final clean = tag.substring(1); + return tag.startsWith('+') + ? tags.contains(clean) + : !tags.contains(clean); + }); } }).toList(); - var sortcolumn = + if (selectedSort.value.isNotEmpty) { + final column = selectedSort.value.substring(0, selectedSort.value.length - 1); - var ascending = selectedSort.value.endsWith('+'); + final ascending = selectedSort.value.endsWith('+'); + queriedTasks.sort((a, b) { - int result; - if (sortcolumn == 'id') { - result = a.id!.compareTo(b.id!); - } else { - result = compareTasks(sortcolumn)(a, b); - } + final result = column == 'id' + ? a.id!.compareTo(b.id!) + : compareTasks(column)(a, b); return ascending ? result : -result; }); - + } searchedTasks.assignAll(queriedTasks); - var searchterm = searchController.text; - if (searchVisible.value) { - searchedTasks.value = searchedTasks - .where((task) => - task.description.contains(searchterm) || + if (searchVisible.value && searchController.text.isNotEmpty) { + final term = searchController.text.toLowerCase(); + searchedTasks.value = searchedTasks.where((task) { + return task.description.toLowerCase().contains(term) || (task.annotations?.asList() ?? []).any( - (a) => a.description.contains(searchterm), - )).toList(); + (a) => a.description.toLowerCase().contains(term), + ); + }).toList(); } + pendingTags.value = _pendingTags(); projects.value = _projects(); } + Task _mapReplicaToTask(TaskForReplica t) { return Task((b) => b ..description = t.description @@ -427,7 +428,7 @@ class HomeController extends GetxController { } void toggleStatusFilter() { - // for TaskChampion and Replica, only Pending and Completed statuses are relevant + // for taskchampion and replica mode, only toggle between pending and completed if (taskchampion.value || taskReplica.value) { pendingFilter.value = !pendingFilter.value; deletedFilter.value = false; From 4653c895c075309977ac331474d026c45e01ab5c Mon Sep 17 00:00:00 2001 From: Prashik-Sasane Date: Mon, 2 Feb 2026 14:49:53 +0530 Subject: [PATCH 6/6] feat: add tag filter for Taskchampion(v3) server --- .../modules/home/controllers/home_controller.dart | 13 ++++++++++--- .../modules/home/views/filter_drawer_home_page.dart | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 2090242a..85fc5519 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -330,10 +330,13 @@ class HomeController extends GetxController { Task _mapReplicaToTask(TaskForReplica t) { return Task((b) => b + ..uuid = t.uuid ..description = t.description ..project = t.project ..priority = t.priority ..status = t.status + ..entry = DateTime.now() + ..modified = DateTime.now() ..tags.replace(t.tags ?? [])); } @@ -354,10 +357,14 @@ class HomeController extends GetxController { } Map _pendingTags() { - var frequency = tagFrequencies(storage.data.pendingData()); - var modified = tagsLastModified(storage.data.pendingData()); - var setOfTags = tagSet(storage.data.pendingData()); + final tasksSource = (taskchampion.value || taskReplica.value) + ? queriedTasks + : storage.data.pendingData(); + + var frequency = tagFrequencies(tasksSource); + var modified = tagsLastModified(tasksSource); + var setOfTags = tagSet(tasksSource); return SplayTreeMap.of({ for (var tag in setOfTags) tag: TagMetadata( 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 a08e55a3..ec270400 100644 --- a/lib/app/modules/home/views/filter_drawer_home_page.dart +++ b/lib/app/modules/home/views/filter_drawer_home_page.dart @@ -231,8 +231,8 @@ class FilterDrawer extends StatelessWidget { color: Color.fromARGB(0, 48, 46, 46), ), Visibility( - visible: !homeController.taskchampion.value && - !homeController.taskReplica.value, + visible: !homeController.taskchampion.value || + homeController.taskReplica.value, child: Container( key: homeController.filterTagKey, width: MediaQuery.of(context).size.width * 1,