mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-16 14:46:44 +02:00
Added categorize in multi select menu
This commit is contained in:
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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": "リクエストが多すぎます(レート制限)- {}分後に再試行してください"
|
||||||
|
@ -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 限制) - 在 {} 分钟后重试"
|
||||||
|
@ -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()
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user