From 95f3362a8446c87cb1a4c06ca9cc7fb679038e8e Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 6 Jan 2023 20:47:22 -0500 Subject: [PATCH] Apps bottom bar tweaks (#216) --- lib/pages/apps.dart | 768 +++++++++++++++++++++++--------------------- 1 file changed, 404 insertions(+), 364 deletions(-) diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index b419eb4..1b422f8 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -348,8 +348,9 @@ class AppsPageState extends State { Row( children: [ selectedApps.isEmpty - ? IconButton( - visualDensity: VisualDensity.compact, + ? TextButton.icon( + style: + const ButtonStyle(visualDensity: VisualDensity.compact), onPressed: () { selectThese(sortedApps.map((e) => e.app).toList()); }, @@ -357,7 +358,7 @@ class AppsPageState extends State { Icons.select_all_outlined, color: Theme.of(context).colorScheme.primary, ), - tooltip: tr('selectAll')) + label: Text(sortedApps.length.toString())) : TextButton.icon( style: const ButtonStyle(visualDensity: VisualDensity.compact), @@ -375,376 +376,415 @@ class AppsPageState extends State { label: Text(selectedApps.length.toString())), const VerticalDivider(), Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - selectedApps.isEmpty - ? const SizedBox() - : IconButton( - visualDensity: VisualDensity.compact, - onPressed: () { - showDialog?>( - context: context, - builder: (BuildContext ctx) { - return GeneratedFormModal( - title: tr('removeSelectedAppsQuestion'), - items: const [], - initValid: true, - message: tr( - 'xWillBeRemovedButRemainInstalled', - args: [ - plural('apps', selectedApps.length) - ]), - ); - }).then((values) { - if (values != null) { - appsProvider.removeApps( - selectedApps.map((e) => e.id).toList()); - } - }); - }, - tooltip: tr('removeSelectedApps'), - icon: const Icon(Icons.delete_outline_outlined), - ), - IconButton( - visualDensity: VisualDensity.compact, - onPressed: appsProvider.areDownloadsRunning() || - (existingUpdateIdsAllOrSelected.isEmpty && - newInstallIdsAllOrSelected.isEmpty && - trackOnlyUpdateIdsAllOrSelected.isEmpty) - ? null - : () { - HapticFeedback.heavyImpact(); - List formItems = []; - if (existingUpdateIdsAllOrSelected.isNotEmpty) { - formItems.add(GeneratedFormSwitch('updates', - label: tr('updateX', args: [ - plural('apps', - existingUpdateIdsAllOrSelected.length) - ]), - defaultValue: true)); - } - if (newInstallIdsAllOrSelected.isNotEmpty) { - formItems.add(GeneratedFormSwitch('installs', - label: tr('installX', args: [ - plural('apps', - newInstallIdsAllOrSelected.length) - ]), - defaultValue: existingUpdateIdsAllOrSelected - .isNotEmpty)); - } - if (trackOnlyUpdateIdsAllOrSelected.isNotEmpty) { - formItems.add(GeneratedFormSwitch('trackonlies', - label: tr('markXTrackOnlyAsUpdated', args: [ - plural('apps', - trackOnlyUpdateIdsAllOrSelected.length) - ]), - defaultValue: existingUpdateIdsAllOrSelected - .isNotEmpty || - newInstallIdsAllOrSelected.isNotEmpty)); - } - showDialog?>( - context: context, - builder: (BuildContext ctx) { - var totalApps = existingUpdateIdsAllOrSelected - .length + - newInstallIdsAllOrSelected.length + - trackOnlyUpdateIdsAllOrSelected.length; - return GeneratedFormModal( - title: tr('changeX', - args: [plural('apps', totalApps)]), - items: formItems.map((e) => [e]).toList(), - initValid: true, - ); - }).then((values) { - if (values != null) { - if (values.isEmpty) { - values = getDefaultValuesFromFormItems( - [formItems]); - } - bool shouldInstallUpdates = - values['updates'] == true; - bool shouldInstallNew = - values['installs'] == true; - bool shouldMarkTrackOnlies = - values['trackonlies'] == true; - (() async { - if (shouldInstallNew || - shouldInstallUpdates) { - await settingsProvider - .getInstallPermission(); - } - })() - .then((_) { - List toInstall = []; - if (shouldInstallUpdates) { - toInstall - .addAll(existingUpdateIdsAllOrSelected); - } - if (shouldInstallNew) { - toInstall - .addAll(newInstallIdsAllOrSelected); - } - if (shouldMarkTrackOnlies) { - toInstall.addAll( - trackOnlyUpdateIdsAllOrSelected); - } - appsProvider - .downloadAndInstallLatestApps(toInstall, - globalNavigatorKey.currentContext) - .catchError((e) { - showError(e, context); - }); - }); - } - }); - }, - tooltip: selectedApps.isEmpty - ? tr('installUpdateApps') - : tr('installUpdateSelectedApps'), - 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?>( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + IconButton( + visualDensity: VisualDensity.compact, + onPressed: selectedApps.isEmpty + ? null + : () { + showDialog?>( context: context, builder: (BuildContext ctx) { return GeneratedFormModal( - title: tr('categorize'), + title: + tr('removeSelectedAppsQuestion'), items: const [], initValid: true, - message: - tr('selectedCategorizeWarning'), + message: tr( + 'xWillBeRemovedButRemainInstalled', + args: [ + plural( + 'apps', selectedApps.length) + ]), ); - }) != - null; - } - if (cont) { - await showDialog?>( - context: context, - builder: (BuildContext ctx) { - return GeneratedFormModal( - title: tr('categorize'), - items: const [], - initValid: true, - singleNullReturnButton: tr('continue'), - additionalWidgets: [ - CategoryEditorSelector( - preselected: !showPrompt - ? preselected ?? {} - : {}, - showLabelWhenNotEmpty: false, - onSelected: (categories) { - appsProvider - .saveApps(selectedApps.map((e) { - e.categories = categories; - return e; - }).toList()); - }, - ) - ], - ); + }).then((values) { + if (values != null) { + appsProvider.removeApps(selectedApps + .map((e) => e.id) + .toList()); + } }); - } - } catch (err) { - showError(err, context); - } - }, - tooltip: tr('categorize'), - icon: const Icon(Icons.category_outlined), - ), - selectedApps.isEmpty - ? const SizedBox() - : IconButton( - visualDensity: VisualDensity.compact, - onPressed: () { - showDialog( - context: context, - builder: (BuildContext ctx) { - return AlertDialog( - scrollable: true, - content: Padding( - padding: const EdgeInsets.only(top: 6), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceAround, - children: [ - IconButton( - onPressed: - appsProvider - .areDownloadsRunning() - ? null - : () { - showDialog( - context: context, - builder: - (BuildContext - ctx) { - return AlertDialog( - title: Text(tr( - 'markXSelectedAppsAsUpdated', - args: [ - selectedApps - .length - .toString() - ])), - content: Text( - tr('onlyWorksWithNonEVDApps'), - style: const TextStyle( - fontWeight: - FontWeight - .bold, - fontStyle: - FontStyle.italic), - ), - actions: [ - TextButton( - onPressed: - () { - Navigator.of(context) - .pop(); - }, - child: Text( - tr('no'))), - TextButton( - onPressed: - () { - HapticFeedback - .selectionClick(); - appsProvider - .saveApps(selectedApps.map((a) { - if (a.installedVersion != - null) { - a.installedVersion = a.latestVersion; - } - return a; - }).toList()); + }, + tooltip: tr('removeSelectedApps'), + icon: const Icon(Icons.delete_outline_outlined), + ), + IconButton( + visualDensity: VisualDensity.compact, + onPressed: appsProvider.areDownloadsRunning() || + (existingUpdateIdsAllOrSelected.isEmpty && + newInstallIdsAllOrSelected.isEmpty && + trackOnlyUpdateIdsAllOrSelected.isEmpty) + ? null + : () { + HapticFeedback.heavyImpact(); + List formItems = []; + if (existingUpdateIdsAllOrSelected + .isNotEmpty) { + formItems.add(GeneratedFormSwitch( + 'updates', + label: tr('updateX', args: [ + plural( + 'apps', + existingUpdateIdsAllOrSelected + .length) + ]), + defaultValue: true)); + } + if (newInstallIdsAllOrSelected.isNotEmpty) { + formItems.add(GeneratedFormSwitch( + 'installs', + label: tr('installX', args: [ + plural( + 'apps', + newInstallIdsAllOrSelected + .length) + ]), + defaultValue: + existingUpdateIdsAllOrSelected + .isNotEmpty)); + } + if (trackOnlyUpdateIdsAllOrSelected + .isNotEmpty) { + formItems.add(GeneratedFormSwitch( + 'trackonlies', + label: tr('markXTrackOnlyAsUpdated', + args: [ + plural( + 'apps', + trackOnlyUpdateIdsAllOrSelected + .length) + ]), + defaultValue: + existingUpdateIdsAllOrSelected + .isNotEmpty || + newInstallIdsAllOrSelected + .isNotEmpty)); + } + showDialog?>( + context: context, + builder: (BuildContext ctx) { + var totalApps = + existingUpdateIdsAllOrSelected.length + + newInstallIdsAllOrSelected + .length + + trackOnlyUpdateIdsAllOrSelected + .length; + return GeneratedFormModal( + title: tr('changeX', args: [ + plural('apps', totalApps) + ]), + items: formItems + .map((e) => [e]) + .toList(), + initValid: true, + ); + }).then((values) { + if (values != null) { + if (values.isEmpty) { + values = + getDefaultValuesFromFormItems( + [formItems]); + } + bool shouldInstallUpdates = + values['updates'] == true; + bool shouldInstallNew = + values['installs'] == true; + bool shouldMarkTrackOnlies = + values['trackonlies'] == true; + (() async { + if (shouldInstallNew || + shouldInstallUpdates) { + await settingsProvider + .getInstallPermission(); + } + })() + .then((_) { + List toInstall = []; + if (shouldInstallUpdates) { + toInstall.addAll( + existingUpdateIdsAllOrSelected); + } + if (shouldInstallNew) { + toInstall.addAll( + newInstallIdsAllOrSelected); + } + if (shouldMarkTrackOnlies) { + toInstall.addAll( + trackOnlyUpdateIdsAllOrSelected); + } + appsProvider + .downloadAndInstallLatestApps( + toInstall, + globalNavigatorKey + .currentContext) + .catchError((e) { + showError(e, context); + }); + }); + } + }); + }, + tooltip: selectedApps.isEmpty + ? tr('installUpdateApps') + : tr('installUpdateSelectedApps'), + icon: const Icon( + Icons.file_download_outlined, + )), + IconButton( + visualDensity: VisualDensity.compact, + onPressed: selectedApps.isEmpty + ? null + : () 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< + Map?>( + 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('continue'), + additionalWidgets: [ + CategoryEditorSelector( + preselected: !showPrompt + ? 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), + ), + IconButton( + visualDensity: VisualDensity.compact, + onPressed: selectedApps.isEmpty + ? null + : () { + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + scrollable: true, + content: Padding( + padding: + const EdgeInsets.only(top: 6), + child: Row( + mainAxisAlignment: + MainAxisAlignment + .spaceAround, + children: [ + IconButton( + onPressed: appsProvider + .areDownloadsRunning() + ? null + : () { + showDialog( + context: + context, + builder: + (BuildContext + ctx) { + return AlertDialog( + title: Text(tr( + 'markXSelectedAppsAsUpdated', + args: [ + selectedApps.length.toString() + ])), + content: + Text( + tr('onlyWorksWithNonEVDApps'), + style: const TextStyle( + fontWeight: + FontWeight.bold, + fontStyle: FontStyle.italic), + ), + actions: [ + TextButton( + onPressed: + () { + Navigator.of(context).pop(); + }, + child: + Text(tr('no'))), + TextButton( + onPressed: + () { + HapticFeedback.selectionClick(); + appsProvider.saveApps(selectedApps.map((a) { + if (a.installedVersion != null) { + a.installedVersion = a.latestVersion; + } + return a; + }).toList()); - Navigator.of(context) - .pop(); - }, - child: Text( - tr('yes'))) - ], - ); - }).whenComplete(() { - Navigator.of( - context) - .pop(); - }); - }, - tooltip: - tr('markSelectedAppsUpdated'), - icon: const Icon(Icons.done)), - IconButton( - onPressed: () { - var pinStatus = selectedApps - .where((element) => - element.pinned) - .isEmpty; - appsProvider.saveApps( - selectedApps.map((e) { - e.pinned = pinStatus; - return e; - }).toList()); - Navigator.of(context).pop(); - }, - tooltip: selectedApps - .where((element) => - element.pinned) - .isEmpty - ? tr('pinToTop') - : tr('unpinFromTop'), - icon: Icon(selectedApps - .where((element) => - element.pinned) - .isEmpty - ? Icons.bookmark_outline_rounded - : Icons - .bookmark_remove_outlined), + Navigator.of(context).pop(); + }, + child: + Text(tr('yes'))) + ], + ); + }).whenComplete(() { + Navigator.of( + context) + .pop(); + }); + }, + tooltip: tr( + 'markSelectedAppsUpdated'), + icon: const Icon( + Icons.done)), + IconButton( + onPressed: () { + var pinStatus = + selectedApps + .where((element) => + element + .pinned) + .isEmpty; + appsProvider.saveApps( + selectedApps.map((e) { + e.pinned = pinStatus; + return e; + }).toList()); + Navigator.of(context) + .pop(); + }, + tooltip: selectedApps + .where((element) => + element.pinned) + .isEmpty + ? tr('pinToTop') + : tr('unpinFromTop'), + icon: Icon(selectedApps + .where((element) => + element.pinned) + .isEmpty + ? Icons + .bookmark_outline_rounded + : Icons + .bookmark_remove_outlined), + ), + IconButton( + onPressed: () { + String urls = ''; + for (var a + in selectedApps) { + urls += '${a.url}\n'; + } + urls = urls.substring( + 0, urls.length - 1); + Share.share(urls, + subject: tr( + 'selectedAppURLsFromObtainium')); + Navigator.of(context) + .pop(); + }, + tooltip: tr( + 'shareSelectedAppURLs'), + icon: + const Icon(Icons.share), + ), + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + ctx) { + return GeneratedFormModal( + title: tr( + 'resetInstallStatusForSelectedAppsQuestion'), + items: const [], + initValid: true, + message: tr( + 'installStatusOfXWillBeResetExplanation', + args: [ + plural( + 'app', + selectedApps + .length) + ]), + ); + }).then((values) { + if (values != null) { + appsProvider.saveApps( + selectedApps + .map((e) { + e.installedVersion = + null; + return e; + }).toList()); + } + }).whenComplete(() { + Navigator.of(context) + .pop(); + }); + }, + tooltip: tr( + 'resetInstallStatus'), + icon: const Icon(Icons + .restore_page_outlined), + ), + ]), ), - IconButton( - onPressed: () { - String urls = ''; - for (var a in selectedApps) { - urls += '${a.url}\n'; - } - urls = urls.substring( - 0, urls.length - 1); - Share.share(urls, - subject: tr( - 'selectedAppURLsFromObtainium')); - Navigator.of(context).pop(); - }, - tooltip: tr('shareSelectedAppURLs'), - icon: const Icon(Icons.share), - ), - IconButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext ctx) { - return GeneratedFormModal( - title: tr( - 'resetInstallStatusForSelectedAppsQuestion'), - items: const [], - initValid: true, - message: tr( - 'installStatusOfXWillBeResetExplanation', - args: [ - plural( - 'app', - selectedApps - .length) - ]), - ); - }).then((values) { - if (values != null) { - appsProvider.saveApps( - selectedApps.map((e) { - e.installedVersion = null; - return e; - }).toList()); - } - }).whenComplete(() { - Navigator.of(context).pop(); - }); - }, - tooltip: tr('resetInstallStatus'), - icon: const Icon( - Icons.restore_page_outlined), - ), - ]), - ), - ); - }); - }, - tooltip: tr('more'), - icon: const Icon(Icons.more_horiz), - ), - ], - )), + ); + }); + }, + tooltip: tr('more'), + icon: const Icon(Icons.more_horiz), + ), + ], + ))), const VerticalDivider(), IconButton( visualDensity: VisualDensity.compact,