From 8f16f745bed7335cae6cbfb83afff42390b6c02b Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Tue, 27 Dec 2022 20:15:56 -0500 Subject: [PATCH] Added categorize in multi select menu --- assets/translations/de.json | 1 + assets/translations/en.json | 1 + assets/translations/hu.json | 1 + assets/translations/it.json | 1 + assets/translations/ja.json | 1 + assets/translations/zh.json | 1 + lib/components/generated_form_modal.dart | 30 +++++---- lib/pages/apps.dart | 86 ++++++++++++++++++++++-- lib/providers/settings_provider.dart | 3 + 9 files changed, 106 insertions(+), 19 deletions(-) diff --git a/assets/translations/de.json b/assets/translations/de.json index 9b0208d..5a3bc13 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -210,6 +210,7 @@ "label": "Bezeichnung", "language": "Sprache", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" diff --git a/assets/translations/en.json b/assets/translations/en.json index c9925cf..1fae8c8 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -210,6 +210,7 @@ "label": "Label", "language": "Language", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "Too many requests (rate limited) - try again in {} minute", "other": "Too many requests (rate limited) - try again in {} minutes" diff --git a/assets/translations/hu.json b/assets/translations/hu.json index ebf0698..f6461d9 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -209,6 +209,7 @@ "label": "Címke", "language": "Language", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva", "other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva" diff --git a/assets/translations/it.json b/assets/translations/it.json index 31c657c..a0f01f0 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -210,6 +210,7 @@ "label": "Etichetta", "language": "Lingua", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" diff --git a/assets/translations/ja.json b/assets/translations/ja.json index bab4d54..03c3be3 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -210,6 +210,7 @@ "label": "ラベル", "language": "言語", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 510c76d..e193fbb 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -210,6 +210,7 @@ "label": "Label", "language": "Language", "storagePermissionDenied": "Storage permission denied", + "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "tooManyRequestsTryAgainInMinutes": { "one": "请求过多 (API 限制) - 在 {} 分钟后重试", "other": "请求过多 (API 限制) - 在 {} 分钟后重试" diff --git a/lib/components/generated_form_modal.dart b/lib/components/generated_form_modal.dart index b961a9c..f44acc4 100644 --- a/lib/components/generated_form_modal.dart +++ b/lib/components/generated_form_modal.dart @@ -10,13 +10,15 @@ class GeneratedFormModal extends StatefulWidget { required this.items, this.initValid = false, this.message = '', - this.additionalWidgets = const []}); + this.additionalWidgets = const [], + this.singleNullReturnButton}); final String title; final String message; final List> items; final bool initValid; final List additionalWidgets; + final String? singleNullReturnButton; @override State createState() => _GeneratedFormModalState(); @@ -64,17 +66,21 @@ class _GeneratedFormModalState extends State { onPressed: () { Navigator.of(context).pop(null); }, - child: Text(tr('cancel'))), - TextButton( - onPressed: !valid - ? null - : () { - if (valid) { - HapticFeedback.selectionClick(); - Navigator.of(context).pop(values); - } - }, - child: Text(tr('continue'))) + child: Text(widget.singleNullReturnButton == null + ? tr('cancel') + : widget.singleNullReturnButton!)), + widget.singleNullReturnButton == null + ? TextButton( + onPressed: !valid + ? null + : () { + if (valid) { + HapticFeedback.selectionClick(); + Navigator.of(context).pop(values); + } + }, + child: Text(tr('continue'))) + : const SizedBox.shrink() ], ); } diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index c403672..3a37581 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -55,7 +55,8 @@ class AppsPageState extends State { var appsProvider = context.watch(); var settingsProvider = context.watch(); var sortedApps = appsProvider.apps.values.toList(); - var currentFilterIsUpdatesOnly = filter.isIdenticalTo(updatesOnlyFilter); + var currentFilterIsUpdatesOnly = + filter.isIdenticalTo(updatesOnlyFilter, settingsProvider); selectedApps = selectedApps .where((element) => sortedApps.map((e) => e.app).contains(element)) @@ -348,6 +349,7 @@ class AppsPageState extends State { children: [ selectedApps.isEmpty ? IconButton( + visualDensity: VisualDensity.compact, onPressed: () { selectThese(sortedApps.map((e) => e.app).toList()); }, @@ -357,6 +359,8 @@ class AppsPageState extends State { ), tooltip: tr('selectAll')) : TextButton.icon( + style: + const ButtonStyle(visualDensity: VisualDensity.compact), onPressed: () { selectedApps.isEmpty ? selectThese(sortedApps.map((e) => e.app).toList()) @@ -501,6 +505,73 @@ class AppsPageState extends State { icon: const Icon( Icons.file_download_outlined, )), + selectedApps.isEmpty + ? const SizedBox() + : IconButton( + visualDensity: VisualDensity.compact, + onPressed: () async { + try { + Set? preselected; + var showPrompt = false; + for (var element in selectedApps) { + var currentCats = element.categories.toSet(); + if (preselected == null) { + preselected = currentCats; + } else { + if (!settingsProvider.setEqual( + currentCats, preselected)) { + showPrompt = true; + break; + } + } + } + var cont = true; + if (showPrompt) { + cont = await showDialog?>( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + title: tr('categorize'), + items: const [], + initValid: true, + message: + tr('selectedCategorizeWarning'), + ); + }) != + null; + } + if (cont) { + await showDialog?>( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + title: tr('categorize'), + items: const [], + initValid: true, + singleNullReturnButton: tr('ok'), + additionalWidgets: [ + CategoryEditorSelector( + preselected: preselected ?? {}, + showLabelWhenNotEmpty: false, + onSelected: (categories) { + appsProvider + .saveApps(selectedApps.map((e) { + e.categories = categories; + return e; + }).toList()); + }, + ) + ], + ); + }); + } + } catch (err) { + showError(err, context); + } + }, + tooltip: tr('categorize'), + icon: const Icon(Icons.category_outlined), + ), selectedApps.isEmpty ? const SizedBox() : IconButton( @@ -697,12 +768,15 @@ class AppsPageState extends State { appsProvider.apps.isEmpty ? const SizedBox() : TextButton.icon( + style: + const ButtonStyle(visualDensity: VisualDensity.compact), label: Text( - filter.isIdenticalTo(neutralFilter) + filter.isIdenticalTo(neutralFilter, settingsProvider) ? tr('filter') : tr('filterActive'), style: TextStyle( - fontWeight: filter.isIdenticalTo(neutralFilter) + fontWeight: filter.isIdenticalTo( + neutralFilter, settingsProvider) ? FontWeight.normal : FontWeight.bold), ), @@ -794,12 +868,10 @@ class AppsFilter { includeNonInstalled = values['nonInstalledApps']; } - bool isIdenticalTo(AppsFilter other) => + bool isIdenticalTo(AppsFilter other, SettingsProvider settingsProvider) => authorFilter.trim() == other.authorFilter.trim() && nameFilter.trim() == other.nameFilter.trim() && includeUptodate == other.includeUptodate && includeNonInstalled == other.includeNonInstalled && - categoryFilter.length == other.categoryFilter.length && - categoryFilter.union(other.categoryFilter).length == - categoryFilter.length; + settingsProvider.setEqual(categoryFilter, other.categoryFilter); } diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 6110049..068ebf8 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -176,4 +176,7 @@ class SettingsProvider with ChangeNotifier { } notifyListeners(); } + + bool setEqual(Set a, Set b) => + a.length == b.length && a.union(b).length == a.length; }