Added categorize in multi select menu

This commit is contained in:
Imran Remtulla
2022-12-27 20:15:56 -05:00
parent 8ddeb3d776
commit 8f16f745be
9 changed files with 106 additions and 19 deletions

View File

@ -210,6 +210,7 @@
"label": "Bezeichnung", "label": "Bezeichnung",
"language": "Sprache", "language": "Sprache",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"

View File

@ -210,6 +210,7 @@
"label": "Label", "label": "Label",
"language": "Language", "language": "Language",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Too many requests (rate limited) - try again in {} minute", "one": "Too many requests (rate limited) - try again in {} minute",
"other": "Too many requests (rate limited) - try again in {} minutes" "other": "Too many requests (rate limited) - try again in {} minutes"

View File

@ -209,6 +209,7 @@
"label": "Címke", "label": "Címke",
"language": "Language", "language": "Language",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva", "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" "other": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva"

View File

@ -210,6 +210,7 @@
"label": "Etichetta", "label": "Etichetta",
"language": "Lingua", "language": "Lingua",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"

View File

@ -210,6 +210,7 @@
"label": "ラベル", "label": "ラベル",
"language": "言語", "language": "言語",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください",
"other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください"

View File

@ -210,6 +210,7 @@
"label": "Label", "label": "Label",
"language": "Language", "language": "Language",
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "请求过多 (API 限制) - 在 {} 分钟后重试", "one": "请求过多 (API 限制) - 在 {} 分钟后重试",
"other": "请求过多 (API 限制) - 在 {} 分钟后重试" "other": "请求过多 (API 限制) - 在 {} 分钟后重试"

View File

@ -10,13 +10,15 @@ class GeneratedFormModal extends StatefulWidget {
required this.items, required this.items,
this.initValid = false, this.initValid = false,
this.message = '', this.message = '',
this.additionalWidgets = const []}); this.additionalWidgets = const [],
this.singleNullReturnButton});
final String title; final String title;
final String message; final String message;
final List<List<GeneratedFormItem>> items; final List<List<GeneratedFormItem>> items;
final bool initValid; final bool initValid;
final List<Widget> additionalWidgets; final List<Widget> additionalWidgets;
final String? singleNullReturnButton;
@override @override
State<GeneratedFormModal> createState() => _GeneratedFormModalState(); State<GeneratedFormModal> createState() => _GeneratedFormModalState();
@ -64,8 +66,11 @@ class _GeneratedFormModalState extends State<GeneratedFormModal> {
onPressed: () { onPressed: () {
Navigator.of(context).pop(null); Navigator.of(context).pop(null);
}, },
child: Text(tr('cancel'))), child: Text(widget.singleNullReturnButton == null
TextButton( ? tr('cancel')
: widget.singleNullReturnButton!)),
widget.singleNullReturnButton == null
? TextButton(
onPressed: !valid onPressed: !valid
? null ? null
: () { : () {
@ -75,6 +80,7 @@ class _GeneratedFormModalState extends State<GeneratedFormModal> {
} }
}, },
child: Text(tr('continue'))) child: Text(tr('continue')))
: const SizedBox.shrink()
], ],
); );
} }

View File

@ -55,7 +55,8 @@ class AppsPageState extends State<AppsPage> {
var appsProvider = context.watch<AppsProvider>(); var appsProvider = context.watch<AppsProvider>();
var settingsProvider = context.watch<SettingsProvider>(); var settingsProvider = context.watch<SettingsProvider>();
var sortedApps = appsProvider.apps.values.toList(); var sortedApps = appsProvider.apps.values.toList();
var currentFilterIsUpdatesOnly = filter.isIdenticalTo(updatesOnlyFilter); var currentFilterIsUpdatesOnly =
filter.isIdenticalTo(updatesOnlyFilter, settingsProvider);
selectedApps = selectedApps selectedApps = selectedApps
.where((element) => sortedApps.map((e) => e.app).contains(element)) .where((element) => sortedApps.map((e) => e.app).contains(element))
@ -348,6 +349,7 @@ class AppsPageState extends State<AppsPage> {
children: [ children: [
selectedApps.isEmpty selectedApps.isEmpty
? IconButton( ? IconButton(
visualDensity: VisualDensity.compact,
onPressed: () { onPressed: () {
selectThese(sortedApps.map((e) => e.app).toList()); selectThese(sortedApps.map((e) => e.app).toList());
}, },
@ -357,6 +359,8 @@ class AppsPageState extends State<AppsPage> {
), ),
tooltip: tr('selectAll')) tooltip: tr('selectAll'))
: TextButton.icon( : TextButton.icon(
style:
const ButtonStyle(visualDensity: VisualDensity.compact),
onPressed: () { onPressed: () {
selectedApps.isEmpty selectedApps.isEmpty
? selectThese(sortedApps.map((e) => e.app).toList()) ? selectThese(sortedApps.map((e) => e.app).toList())
@ -501,6 +505,73 @@ class AppsPageState extends State<AppsPage> {
icon: const Icon( icon: const Icon(
Icons.file_download_outlined, Icons.file_download_outlined,
)), )),
selectedApps.isEmpty
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact,
onPressed: () async {
try {
Set<String>? 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<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('categorize'),
items: const [],
initValid: true,
message:
tr('selectedCategorizeWarning'),
);
}) !=
null;
}
if (cont) {
await showDialog<Map<String, dynamic>?>(
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 selectedApps.isEmpty
? const SizedBox() ? const SizedBox()
: IconButton( : IconButton(
@ -697,12 +768,15 @@ class AppsPageState extends State<AppsPage> {
appsProvider.apps.isEmpty appsProvider.apps.isEmpty
? const SizedBox() ? const SizedBox()
: TextButton.icon( : TextButton.icon(
style:
const ButtonStyle(visualDensity: VisualDensity.compact),
label: Text( label: Text(
filter.isIdenticalTo(neutralFilter) filter.isIdenticalTo(neutralFilter, settingsProvider)
? tr('filter') ? tr('filter')
: tr('filterActive'), : tr('filterActive'),
style: TextStyle( style: TextStyle(
fontWeight: filter.isIdenticalTo(neutralFilter) fontWeight: filter.isIdenticalTo(
neutralFilter, settingsProvider)
? FontWeight.normal ? FontWeight.normal
: FontWeight.bold), : FontWeight.bold),
), ),
@ -794,12 +868,10 @@ class AppsFilter {
includeNonInstalled = values['nonInstalledApps']; includeNonInstalled = values['nonInstalledApps'];
} }
bool isIdenticalTo(AppsFilter other) => bool isIdenticalTo(AppsFilter other, SettingsProvider settingsProvider) =>
authorFilter.trim() == other.authorFilter.trim() && authorFilter.trim() == other.authorFilter.trim() &&
nameFilter.trim() == other.nameFilter.trim() && nameFilter.trim() == other.nameFilter.trim() &&
includeUptodate == other.includeUptodate && includeUptodate == other.includeUptodate &&
includeNonInstalled == other.includeNonInstalled && includeNonInstalled == other.includeNonInstalled &&
categoryFilter.length == other.categoryFilter.length && settingsProvider.setEqual(categoryFilter, other.categoryFilter);
categoryFilter.union(other.categoryFilter).length ==
categoryFilter.length;
} }

View File

@ -176,4 +176,7 @@ class SettingsProvider with ChangeNotifier {
} }
notifyListeners(); notifyListeners();
} }
bool setEqual(Set<String> a, Set<String> b) =>
a.length == b.length && a.union(b).length == a.length;
} }