Apps bottom bar tweaks (#216)

This commit is contained in:
Imran Remtulla
2023-01-06 20:47:22 -05:00
parent b68cf5a1be
commit 95f3362a84

View File

@@ -348,8 +348,9 @@ class AppsPageState extends State<AppsPage> {
Row( Row(
children: [ children: [
selectedApps.isEmpty selectedApps.isEmpty
? IconButton( ? TextButton.icon(
visualDensity: VisualDensity.compact, style:
const ButtonStyle(visualDensity: VisualDensity.compact),
onPressed: () { onPressed: () {
selectThese(sortedApps.map((e) => e.app).toList()); selectThese(sortedApps.map((e) => e.app).toList());
}, },
@@ -357,7 +358,7 @@ class AppsPageState extends State<AppsPage> {
Icons.select_all_outlined, Icons.select_all_outlined,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
tooltip: tr('selectAll')) label: Text(sortedApps.length.toString()))
: TextButton.icon( : TextButton.icon(
style: style:
const ButtonStyle(visualDensity: VisualDensity.compact), const ButtonStyle(visualDensity: VisualDensity.compact),
@@ -375,31 +376,36 @@ class AppsPageState extends State<AppsPage> {
label: Text(selectedApps.length.toString())), label: Text(selectedApps.length.toString())),
const VerticalDivider(), const VerticalDivider(),
Expanded( Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
selectedApps.isEmpty IconButton(
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: () { onPressed: selectedApps.isEmpty
? null
: () {
showDialog<Map<String, dynamic>?>( showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext ctx) {
return GeneratedFormModal( return GeneratedFormModal(
title: tr('removeSelectedAppsQuestion'), title:
tr('removeSelectedAppsQuestion'),
items: const [], items: const [],
initValid: true, initValid: true,
message: tr( message: tr(
'xWillBeRemovedButRemainInstalled', 'xWillBeRemovedButRemainInstalled',
args: [ args: [
plural('apps', selectedApps.length) plural(
'apps', selectedApps.length)
]), ]),
); );
}).then((values) { }).then((values) {
if (values != null) { if (values != null) {
appsProvider.removeApps( appsProvider.removeApps(selectedApps
selectedApps.map((e) => e.id).toList()); .map((e) => e.id)
.toList());
} }
}); });
}, },
@@ -416,50 +422,71 @@ class AppsPageState extends State<AppsPage> {
: () { : () {
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
List<GeneratedFormItem> formItems = []; List<GeneratedFormItem> formItems = [];
if (existingUpdateIdsAllOrSelected.isNotEmpty) { if (existingUpdateIdsAllOrSelected
formItems.add(GeneratedFormSwitch('updates', .isNotEmpty) {
formItems.add(GeneratedFormSwitch(
'updates',
label: tr('updateX', args: [ label: tr('updateX', args: [
plural('apps', plural(
existingUpdateIdsAllOrSelected.length) 'apps',
existingUpdateIdsAllOrSelected
.length)
]), ]),
defaultValue: true)); defaultValue: true));
} }
if (newInstallIdsAllOrSelected.isNotEmpty) { if (newInstallIdsAllOrSelected.isNotEmpty) {
formItems.add(GeneratedFormSwitch('installs', formItems.add(GeneratedFormSwitch(
'installs',
label: tr('installX', args: [ label: tr('installX', args: [
plural('apps', plural(
newInstallIdsAllOrSelected.length) 'apps',
newInstallIdsAllOrSelected
.length)
]), ]),
defaultValue: existingUpdateIdsAllOrSelected defaultValue:
existingUpdateIdsAllOrSelected
.isNotEmpty)); .isNotEmpty));
} }
if (trackOnlyUpdateIdsAllOrSelected.isNotEmpty) { if (trackOnlyUpdateIdsAllOrSelected
formItems.add(GeneratedFormSwitch('trackonlies', .isNotEmpty) {
label: tr('markXTrackOnlyAsUpdated', args: [ formItems.add(GeneratedFormSwitch(
plural('apps', 'trackonlies',
trackOnlyUpdateIdsAllOrSelected.length) label: tr('markXTrackOnlyAsUpdated',
args: [
plural(
'apps',
trackOnlyUpdateIdsAllOrSelected
.length)
]), ]),
defaultValue: existingUpdateIdsAllOrSelected defaultValue:
existingUpdateIdsAllOrSelected
.isNotEmpty || .isNotEmpty ||
newInstallIdsAllOrSelected.isNotEmpty)); newInstallIdsAllOrSelected
.isNotEmpty));
} }
showDialog<Map<String, dynamic>?>( showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext ctx) {
var totalApps = existingUpdateIdsAllOrSelected var totalApps =
existingUpdateIdsAllOrSelected.length +
newInstallIdsAllOrSelected
.length + .length +
newInstallIdsAllOrSelected.length + trackOnlyUpdateIdsAllOrSelected
trackOnlyUpdateIdsAllOrSelected.length; .length;
return GeneratedFormModal( return GeneratedFormModal(
title: tr('changeX', title: tr('changeX', args: [
args: [plural('apps', totalApps)]), plural('apps', totalApps)
items: formItems.map((e) => [e]).toList(), ]),
items: formItems
.map((e) => [e])
.toList(),
initValid: true, initValid: true,
); );
}).then((values) { }).then((values) {
if (values != null) { if (values != null) {
if (values.isEmpty) { if (values.isEmpty) {
values = getDefaultValuesFromFormItems( values =
getDefaultValuesFromFormItems(
[formItems]); [formItems]);
} }
bool shouldInstallUpdates = bool shouldInstallUpdates =
@@ -478,20 +505,22 @@ class AppsPageState extends State<AppsPage> {
.then((_) { .then((_) {
List<String> toInstall = []; List<String> toInstall = [];
if (shouldInstallUpdates) { if (shouldInstallUpdates) {
toInstall toInstall.addAll(
.addAll(existingUpdateIdsAllOrSelected); existingUpdateIdsAllOrSelected);
} }
if (shouldInstallNew) { if (shouldInstallNew) {
toInstall toInstall.addAll(
.addAll(newInstallIdsAllOrSelected); newInstallIdsAllOrSelected);
} }
if (shouldMarkTrackOnlies) { if (shouldMarkTrackOnlies) {
toInstall.addAll( toInstall.addAll(
trackOnlyUpdateIdsAllOrSelected); trackOnlyUpdateIdsAllOrSelected);
} }
appsProvider appsProvider
.downloadAndInstallLatestApps(toInstall, .downloadAndInstallLatestApps(
globalNavigatorKey.currentContext) toInstall,
globalNavigatorKey
.currentContext)
.catchError((e) { .catchError((e) {
showError(e, context); showError(e, context);
}); });
@@ -505,16 +534,17 @@ class AppsPageState extends State<AppsPage> {
icon: const Icon( icon: const Icon(
Icons.file_download_outlined, Icons.file_download_outlined,
)), )),
selectedApps.isEmpty IconButton(
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: () async { onPressed: selectedApps.isEmpty
? null
: () async {
try { try {
Set<String>? preselected; Set<String>? preselected;
var showPrompt = false; var showPrompt = false;
for (var element in selectedApps) { for (var element in selectedApps) {
var currentCats = element.categories.toSet(); var currentCats =
element.categories.toSet();
if (preselected == null) { if (preselected == null) {
preselected = currentCats; preselected = currentCats;
} else { } else {
@@ -527,15 +557,16 @@ class AppsPageState extends State<AppsPage> {
} }
var cont = true; var cont = true;
if (showPrompt) { if (showPrompt) {
cont = await showDialog<Map<String, dynamic>?>( cont = await showDialog<
Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext ctx) {
return GeneratedFormModal( return GeneratedFormModal(
title: tr('categorize'), title: tr('categorize'),
items: const [], items: const [],
initValid: true, initValid: true,
message: message: tr(
tr('selectedCategorizeWarning'), 'selectedCategorizeWarning'),
); );
}) != }) !=
null; null;
@@ -548,7 +579,8 @@ class AppsPageState extends State<AppsPage> {
title: tr('categorize'), title: tr('categorize'),
items: const [], items: const [],
initValid: true, initValid: true,
singleNullReturnButton: tr('continue'), singleNullReturnButton:
tr('continue'),
additionalWidgets: [ additionalWidgets: [
CategoryEditorSelector( CategoryEditorSelector(
preselected: !showPrompt preselected: !showPrompt
@@ -556,8 +588,8 @@ class AppsPageState extends State<AppsPage> {
: {}, : {},
showLabelWhenNotEmpty: false, showLabelWhenNotEmpty: false,
onSelected: (categories) { onSelected: (categories) {
appsProvider appsProvider.saveApps(
.saveApps(selectedApps.map((e) { selectedApps.map((e) {
e.categories = categories; e.categories = categories;
return e; return e;
}).toList()); }).toList());
@@ -574,30 +606,32 @@ class AppsPageState extends State<AppsPage> {
tooltip: tr('categorize'), tooltip: tr('categorize'),
icon: const Icon(Icons.category_outlined), icon: const Icon(Icons.category_outlined),
), ),
selectedApps.isEmpty IconButton(
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: () { onPressed: selectedApps.isEmpty
? null
: () {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext ctx) {
return AlertDialog( return AlertDialog(
scrollable: true, scrollable: true,
content: Padding( content: Padding(
padding: const EdgeInsets.only(top: 6), padding:
const EdgeInsets.only(top: 6),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceAround, MainAxisAlignment
.spaceAround,
children: [ children: [
IconButton( IconButton(
onPressed: onPressed: appsProvider
appsProvider
.areDownloadsRunning() .areDownloadsRunning()
? null ? null
: () { : () {
showDialog( showDialog(
context: context, context:
context,
builder: builder:
(BuildContext (BuildContext
ctx) { ctx) {
@@ -605,47 +639,39 @@ class AppsPageState extends State<AppsPage> {
title: Text(tr( title: Text(tr(
'markXSelectedAppsAsUpdated', 'markXSelectedAppsAsUpdated',
args: [ args: [
selectedApps selectedApps.length.toString()
.length
.toString()
])), ])),
content: Text( content:
Text(
tr('onlyWorksWithNonEVDApps'), tr('onlyWorksWithNonEVDApps'),
style: const TextStyle( style: const TextStyle(
fontWeight: fontWeight:
FontWeight FontWeight.bold,
.bold, fontStyle: FontStyle.italic),
fontStyle:
FontStyle.italic),
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: onPressed:
() { () {
Navigator.of(context) Navigator.of(context).pop();
.pop();
}, },
child: Text( child:
tr('no'))), Text(tr('no'))),
TextButton( TextButton(
onPressed: onPressed:
() { () {
HapticFeedback HapticFeedback.selectionClick();
.selectionClick(); appsProvider.saveApps(selectedApps.map((a) {
appsProvider if (a.installedVersion != null) {
.saveApps(selectedApps.map((a) {
if (a.installedVersion !=
null) {
a.installedVersion = a.latestVersion; a.installedVersion = a.latestVersion;
} }
return a; return a;
}).toList()); }).toList());
Navigator.of(context) Navigator.of(context).pop();
.pop();
}, },
child: Text( child:
tr('yes'))) Text(tr('yes')))
], ],
); );
}).whenComplete(() { }).whenComplete(() {
@@ -654,21 +680,25 @@ class AppsPageState extends State<AppsPage> {
.pop(); .pop();
}); });
}, },
tooltip: tooltip: tr(
tr('markSelectedAppsUpdated'), 'markSelectedAppsUpdated'),
icon: const Icon(Icons.done)), icon: const Icon(
Icons.done)),
IconButton( IconButton(
onPressed: () { onPressed: () {
var pinStatus = selectedApps var pinStatus =
selectedApps
.where((element) => .where((element) =>
element.pinned) element
.pinned)
.isEmpty; .isEmpty;
appsProvider.saveApps( appsProvider.saveApps(
selectedApps.map((e) { selectedApps.map((e) {
e.pinned = pinStatus; e.pinned = pinStatus;
return e; return e;
}).toList()); }).toList());
Navigator.of(context).pop(); Navigator.of(context)
.pop();
}, },
tooltip: selectedApps tooltip: selectedApps
.where((element) => .where((element) =>
@@ -680,14 +710,16 @@ class AppsPageState extends State<AppsPage> {
.where((element) => .where((element) =>
element.pinned) element.pinned)
.isEmpty .isEmpty
? Icons.bookmark_outline_rounded ? Icons
.bookmark_outline_rounded
: Icons : Icons
.bookmark_remove_outlined), .bookmark_remove_outlined),
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
String urls = ''; String urls = '';
for (var a in selectedApps) { for (var a
in selectedApps) {
urls += '${a.url}\n'; urls += '${a.url}\n';
} }
urls = urls.substring( urls = urls.substring(
@@ -695,16 +727,20 @@ class AppsPageState extends State<AppsPage> {
Share.share(urls, Share.share(urls,
subject: tr( subject: tr(
'selectedAppURLsFromObtainium')); 'selectedAppURLsFromObtainium'));
Navigator.of(context).pop(); Navigator.of(context)
.pop();
}, },
tooltip: tr('shareSelectedAppURLs'), tooltip: tr(
icon: const Icon(Icons.share), 'shareSelectedAppURLs'),
icon:
const Icon(Icons.share),
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext
ctx) {
return GeneratedFormModal( return GeneratedFormModal(
title: tr( title: tr(
'resetInstallStatusForSelectedAppsQuestion'), 'resetInstallStatusForSelectedAppsQuestion'),
@@ -722,18 +758,22 @@ class AppsPageState extends State<AppsPage> {
}).then((values) { }).then((values) {
if (values != null) { if (values != null) {
appsProvider.saveApps( appsProvider.saveApps(
selectedApps.map((e) { selectedApps
e.installedVersion = null; .map((e) {
e.installedVersion =
null;
return e; return e;
}).toList()); }).toList());
} }
}).whenComplete(() { }).whenComplete(() {
Navigator.of(context).pop(); Navigator.of(context)
.pop();
}); });
}, },
tooltip: tr('resetInstallStatus'), tooltip: tr(
icon: const Icon( 'resetInstallStatus'),
Icons.restore_page_outlined), icon: const Icon(Icons
.restore_page_outlined),
), ),
]), ]),
), ),
@@ -744,7 +784,7 @@ class AppsPageState extends State<AppsPage> {
icon: const Icon(Icons.more_horiz), icon: const Icon(Icons.more_horiz),
), ),
], ],
)), ))),
const VerticalDivider(), const VerticalDivider(),
IconButton( IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,