Compare commits

...

2 Commits

Author SHA1 Message Date
Imran Remtulla
77e1768f3b Bugfix 2022-09-25 11:46:25 -04:00
Imran Remtulla
da9e5aed5e Apps page UI improvements 2022-09-25 11:32:57 -04:00
3 changed files with 161 additions and 145 deletions

View File

@@ -13,7 +13,7 @@ import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
const String currentReleaseTag = const String currentReleaseTag =
'v0.4.0-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES 'v0.4.1-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
@pragma('vm:entry-point') @pragma('vm:entry-point')
void bgTaskCallback() { void bgTaskCallback() {

View File

@@ -18,6 +18,8 @@ class AppsPage extends StatefulWidget {
class AppsPageState extends State<AppsPage> { class AppsPageState extends State<AppsPage> {
AppsFilter? filter; AppsFilter? filter;
var updatesOnlyFilter =
AppsFilter(includeUptodate: false, includeNonInstalled: false);
Set<String> selectedIds = {}; Set<String> selectedIds = {};
clearSelected() { clearSelected() {
@@ -45,6 +47,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) ?? false;
selectedIds = selectedIds selectedIds = selectedIds
.where((element) => sortedApps.map((e) => e.app.id).contains(element)) .where((element) => sortedApps.map((e) => e.app.id).contains(element))
@@ -112,6 +116,19 @@ class AppsPageState extends State<AppsPage> {
sortedApps = sortedApps.reversed.toList(); sortedApps = sortedApps.reversed.toList();
} }
var existingUpdateIdsAllOrSelected = appsProvider
.getExistingUpdates(installedOnly: true)
.where((element) => selectedIds.isEmpty
? sortedApps.where((a) => a.app.id == element).isNotEmpty
: selectedIds.contains(element))
.toList();
var newInstallIdsAllOrSelected = appsProvider
.getExistingUpdates(nonInstalledOnly: true)
.where((element) => selectedIds.isEmpty
? sortedApps.where((a) => a.app.id == element).isNotEmpty
: selectedIds.contains(element))
.toList();
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
body: RefreshIndicator( body: RefreshIndicator(
@@ -133,8 +150,9 @@ class AppsPageState extends State<AppsPage> {
: Text( : Text(
appsProvider.apps.isEmpty appsProvider.apps.isEmpty
? 'No Apps' ? 'No Apps'
: 'No Search Results', : 'No Apps for Filter',
style: Theme.of(context).textTheme.headlineMedium, style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
))), ))),
SliverList( SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
@@ -175,26 +193,29 @@ class AppsPageState extends State<AppsPage> {
persistentFooterButtons: [ persistentFooterButtons: [
Row( Row(
children: [ children: [
TextButton.icon( IconButton(
onPressed: () { onPressed: () {
selectedIds.isEmpty selectedIds.isEmpty
? selectThese(sortedApps.map((e) => e.app.id).toList()) ? selectThese(sortedApps.map((e) => e.app.id).toList())
: clearSelected(); : clearSelected();
}, },
icon: Icon(selectedIds.isEmpty icon: Icon(
selectedIds.isEmpty
? Icons.select_all_outlined ? Icons.select_all_outlined
: Icons.deselect_outlined), : Icons.deselect_outlined,
label: Text(selectedIds.isEmpty color: Theme.of(context).colorScheme.primary,
),
tooltip: selectedIds.isEmpty
? 'Select All' ? 'Select All'
: 'Deselect ${selectedIds.length.toString()}')), : 'Deselect ${selectedIds.length.toString()}'),
const VerticalDivider(), const VerticalDivider(),
Expanded( Expanded(
child: selectedIds.isEmpty child: Row(
? Container()
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
IconButton( selectedIds.isEmpty
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: () { onPressed: () {
showDialog<List<String>?>( showDialog<List<String>?>(
@@ -220,44 +241,24 @@ class AppsPageState extends State<AppsPage> {
IconButton( IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: appsProvider.areDownloadsRunning() || onPressed: appsProvider.areDownloadsRunning() ||
selectedIds (existingUpdateIdsAllOrSelected.isEmpty &&
.where((id) => newInstallIdsAllOrSelected.isEmpty)
appsProvider.apps[id]!.app
.installedVersion !=
appsProvider
.apps[id]!.app.latestVersion)
.isEmpty
? null ? null
: () { : () {
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
var existingUpdateIdsSelected = List<List<GeneratedFormItem>> formInputs = [];
appsProvider if (existingUpdateIdsAllOrSelected.isNotEmpty &&
.getExistingUpdates( newInstallIdsAllOrSelected.isNotEmpty) {
installedOnly: true)
.where((element) =>
selectedIds.contains(element))
.toList();
var newInstallIdsSelected = appsProvider
.getExistingUpdates(
nonInstalledOnly: true)
.where((element) =>
selectedIds.contains(element))
.toList();
List<List<GeneratedFormItem>> formInputs =
[];
if (existingUpdateIdsSelected
.isNotEmpty &&
newInstallIdsSelected.isNotEmpty) {
formInputs.add([ formInputs.add([
GeneratedFormItem( GeneratedFormItem(
label: label:
'Update ${existingUpdateIdsSelected.length} Apps?', 'Update ${existingUpdateIdsAllOrSelected.length} App${existingUpdateIdsAllOrSelected.length == 1 ? '' : 's'}',
type: FormItemType.bool) type: FormItemType.bool)
]); ]);
formInputs.add([ formInputs.add([
GeneratedFormItem( GeneratedFormItem(
label: label:
'Install ${newInstallIdsSelected.length} new Apps?', 'Install ${newInstallIdsAllOrSelected.length} new App${newInstallIdsAllOrSelected.length == 1 ? '' : 's'}',
type: FormItemType.bool) type: FormItemType.bool)
]); ]);
} }
@@ -265,48 +266,46 @@ class AppsPageState extends State<AppsPage> {
context: context, context: context,
builder: (BuildContext ctx) { builder: (BuildContext ctx) {
return GeneratedFormModal( return GeneratedFormModal(
title: 'Install Selected Apps?', title:
'Install${selectedIds.isEmpty ? ' ' : ' Selected '}Apps?',
message: message:
'${existingUpdateIdsSelected.length} update${existingUpdateIdsSelected.length == 1 ? '' : 's'} and ${newInstallIdsSelected.length} new install${newInstallIdsSelected.length == 1 ? '' : 's'}.', '${existingUpdateIdsAllOrSelected.length} update${existingUpdateIdsAllOrSelected.length == 1 ? '' : 's'} and ${newInstallIdsAllOrSelected.length} new install${newInstallIdsAllOrSelected.length == 1 ? '' : 's'}.',
items: formInputs, items: formInputs,
defaultValues: const [ defaultValues: const ['true', 'true'],
'true',
'true'
],
initValid: true, initValid: true,
); );
}).then((values) { }).then((values) {
if (values != null) { if (values != null) {
bool shouldInstallUpdates = bool shouldInstallUpdates =
values.length < 2 || values.length < 2 || values[0] == 'true';
values[0] == 'true';
bool shouldInstallNew = bool shouldInstallNew =
values.length < 2 || values.length < 2 || values[1] == 'true';
values[1] == 'true';
settingsProvider settingsProvider
.getInstallPermission() .getInstallPermission()
.then((_) { .then((_) {
List<String> toInstall = []; List<String> toInstall = [];
if (shouldInstallUpdates) { if (shouldInstallUpdates) {
toInstall.addAll( toInstall
existingUpdateIdsSelected); .addAll(existingUpdateIdsAllOrSelected);
} }
if (shouldInstallNew) { if (shouldInstallNew) {
toInstall.addAll( toInstall
newInstallIdsSelected); .addAll(newInstallIdsAllOrSelected);
} }
appsProvider appsProvider.downloadAndInstallLatestApp(
.downloadAndInstallLatestApp(
toInstall, context); toInstall, context);
}); });
} }
}); });
}, },
tooltip: 'Install/Update Selected Apps', tooltip:
'Install/Update${selectedIds.isEmpty ? ' ' : ' Selected '}Apps',
icon: const Icon( icon: const Icon(
Icons.file_download_outlined, Icons.file_download_outlined,
)), )),
IconButton( selectedIds.isEmpty
? const SizedBox()
: IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onPressed: () { onPressed: () {
String urls = ''; String urls = '';
@@ -323,6 +322,27 @@ class AppsPageState extends State<AppsPage> {
], ],
)), )),
const VerticalDivider(), const VerticalDivider(),
IconButton(
visualDensity: VisualDensity.compact,
onPressed: () {
setState(() {
if (currentFilterIsUpdatesOnly) {
filter = null;
} else {
filter = updatesOnlyFilter;
}
});
},
tooltip: currentFilterIsUpdatesOnly
? 'Remove Out-of-Date App Filter'
: 'Show Out-of-Date Apps Only',
icon: Icon(
currentFilterIsUpdatesOnly
? Icons.update_disabled_rounded
: Icons.update_rounded,
color: Theme.of(context).colorScheme.primary,
),
),
appsProvider.apps.isEmpty appsProvider.apps.isEmpty
? const SizedBox() ? const SizedBox()
: TextButton.icon( : TextButton.icon(
@@ -368,10 +388,6 @@ class AppsPageState extends State<AppsPage> {
filter = null; filter = null;
} }
}); });
} else {
setState(() {
filter = null;
});
} }
}); });
}, },

View File

@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.4.0+19 # When changing this, update the tag in main() accordingly version: 0.4.1+20 # When changing this, update the tag in main() accordingly
environment: environment:
sdk: '>=2.19.0-79.0.dev <3.0.0' sdk: '>=2.19.0-79.0.dev <3.0.0'