mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 20:48:24 +00:00
Fix install/status related issues
This commit is contained in:
+26
-18
@@ -54,7 +54,7 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
.firstOrNull;
|
||||
}
|
||||
|
||||
bool get canInstall => localApp.value?.status == AppInstallStatus.installable;
|
||||
bool get canInstall => localApp.value?.status == null;
|
||||
bool get canUpdate => localApp.value?.status == AppInstallStatus.updatable;
|
||||
bool get isUpdated => localApp.value?.status == AppInstallStatus.updated;
|
||||
|
||||
@@ -65,7 +65,7 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
|
||||
final adapter = DataModel.adapterFor(this) as AppAdapter;
|
||||
final notifier =
|
||||
adapter.ref.read(installationProgressProvider(id!.toString()).notifier);
|
||||
adapter.ref.read(installationProgressProvider(id!).notifier);
|
||||
|
||||
final installPermission = await Permission.requestInstallPackages.status;
|
||||
if (!installPermission.isGranted) {
|
||||
@@ -99,7 +99,7 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
if (result['isSuccess']) {
|
||||
await file.delete();
|
||||
await adapter.ref.localApps.localAppAdapter
|
||||
.updateInstallStatus(appId: id?.toString());
|
||||
.refreshUpdateStatus(appId: identifier);
|
||||
notifier.state = IdleInstallProgress();
|
||||
} else {
|
||||
const msg = 'Android rejected installation';
|
||||
@@ -112,6 +112,7 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
await installOnDevice();
|
||||
} else {
|
||||
if (fileExists) {
|
||||
// If file exists download was probably partial, remove
|
||||
await file.delete();
|
||||
}
|
||||
|
||||
@@ -180,24 +181,31 @@ mixin AppAdapter on Adapter<App> {
|
||||
final releases = await ref.releases.findAll(
|
||||
params: {'#d': latestReleaseIdentifiers},
|
||||
);
|
||||
|
||||
// TODO: Deprecated, will be removed
|
||||
// Some developers without access to the latest zapstore-cli
|
||||
// have not published their apps with latest release identifiers
|
||||
// so load as usual
|
||||
final oldApps =
|
||||
apps.where((app) => !latestReleaseIdentifiers.contains(app.identifier));
|
||||
final oldReleases = await ref.releases.findAll(
|
||||
final deprecatedApps =
|
||||
apps.where((app) => app.linkedReplaceableEvents.isEmpty);
|
||||
final deprecatedReleases = await ref.releases.findAll(
|
||||
params: {
|
||||
'#a': oldApps.map((app) => app.getReplaceableEventLink().formatted)
|
||||
'#a':
|
||||
deprecatedApps.map((app) => app.getReplaceableEventLink().formatted)
|
||||
},
|
||||
);
|
||||
final oldReleasesLatest =
|
||||
oldReleases.sorted((a, b) => b.createdAt!.compareTo(a.createdAt!));
|
||||
|
||||
final metadataIds = [...releases, ...oldReleases]
|
||||
.map((r) => r.linkedEvents)
|
||||
.nonNulls
|
||||
.expand((_) => _);
|
||||
final groupedDeprecatedReleases =
|
||||
deprecatedReleases.groupListsBy((r) => r.app.value!);
|
||||
for (final e in groupedDeprecatedReleases.entries) {
|
||||
final mostRecentRelease =
|
||||
e.value.sorted((a, b) => b.createdAt!.compareTo(a.createdAt!)).first;
|
||||
releases.add(mostRecentRelease);
|
||||
}
|
||||
// End deprecated
|
||||
|
||||
final metadataIds =
|
||||
releases.map((r) => r.linkedEvents).nonNulls.expand((_) => _);
|
||||
|
||||
final userIds = {
|
||||
for (final app in apps) app.signer.id,
|
||||
@@ -206,7 +214,7 @@ mixin AppAdapter on Adapter<App> {
|
||||
|
||||
// Metadata and users probably go to separate relays
|
||||
// so query in parallel
|
||||
final rs = await Future.wait([
|
||||
await Future.wait([
|
||||
ref.fileMetadata.findAll(params: {
|
||||
'ids': metadataIds,
|
||||
'#m': [kAndroidMimeType],
|
||||
@@ -214,7 +222,7 @@ mixin AppAdapter on Adapter<App> {
|
||||
}),
|
||||
ref.users.findAll(params: {'authors': userIds}),
|
||||
]);
|
||||
await ref.localApps.localAppAdapter.updateInstallStatus();
|
||||
await ref.localApps.localAppAdapter.refreshUpdateStatus();
|
||||
return apps;
|
||||
}
|
||||
|
||||
@@ -235,7 +243,7 @@ mixin AppAdapter on Adapter<App> {
|
||||
params.remove('installed');
|
||||
final apps = await fetchAppModels(params);
|
||||
// Once apps are loaded, check for installed status
|
||||
await ref.localApps.localAppAdapter.updateInstallStatus();
|
||||
await ref.localApps.localAppAdapter.refreshUpdateStatus();
|
||||
return apps;
|
||||
}
|
||||
}
|
||||
@@ -269,7 +277,7 @@ mixin AppAdapter on Adapter<App> {
|
||||
return null;
|
||||
}
|
||||
await ref.localApps.localAppAdapter
|
||||
.updateInstallStatus(appId: id.toString());
|
||||
.refreshUpdateStatus(appId: id.toString());
|
||||
return apps.first;
|
||||
}
|
||||
|
||||
@@ -348,7 +356,7 @@ class ErrorInstallProgress extends AppInstallProgress {
|
||||
}
|
||||
|
||||
final installationProgressProvider =
|
||||
StateProvider.family<AppInstallProgress, String>(
|
||||
StateProvider.family<AppInstallProgress, Object>(
|
||||
(_, arg) => IdleInstallProgress());
|
||||
|
||||
final appsToUpdateProvider = StateProvider((_) => 0);
|
||||
|
||||
+15
-25
@@ -39,7 +39,7 @@ class LocalApp extends DataModel<LocalApp> {
|
||||
}
|
||||
|
||||
mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
Future<void> updateInstallStatus({String? appId}) async {
|
||||
Future<void> refreshUpdateStatus({String? appId}) async {
|
||||
if (!Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
@@ -55,18 +55,24 @@ mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
: installedPackageInfos.map((i) => i.packageName).nonNulls;
|
||||
|
||||
final localApps = findManyLocalByIds(ids);
|
||||
final apps = ref.apps.appAdapter.findWhereIdInLocal(ids);
|
||||
|
||||
for (final i in installedPackageInfos) {
|
||||
final appId = i.packageName!;
|
||||
final localApp = localApps.firstWhereOrNull((app) => appId == app.id) ??
|
||||
LocalApp(id: i.packageName!);
|
||||
final app = apps.firstWhereOrNull((a) => a.identifier == appId);
|
||||
|
||||
final installedVersion = i.versionName;
|
||||
final installedVersionCode = i.versionCode;
|
||||
|
||||
final app = ref.apps.appAdapter.findWhereIdInLocal([appId]).firstOrNull;
|
||||
final status =
|
||||
determineInstallStatus(app, installedVersion, installedVersionCode);
|
||||
determineUpdateStatus(app, installedVersion, installedVersionCode);
|
||||
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final localApp = localApps.firstWhereOrNull((app) => appId == app.id) ??
|
||||
LocalApp(id: i.packageName!);
|
||||
localApp
|
||||
.copyWith(
|
||||
installedVersion: installedVersion,
|
||||
@@ -81,18 +87,16 @@ mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
ref.read(appsToUpdateProvider.notifier).state = rs.first['c'];
|
||||
}
|
||||
|
||||
AppInstallStatus? determineInstallStatus(
|
||||
AppInstallStatus? determineUpdateStatus(
|
||||
App? app, String? installedVersion, int? installedVersionCode) {
|
||||
if (app == null || app.releases.isEmpty || app.latestMetadata == null) {
|
||||
return null;
|
||||
}
|
||||
if (installedVersion == null) {
|
||||
return AppInstallStatus.installable;
|
||||
if (installedVersion == null || installedVersionCode == null) {
|
||||
return null;
|
||||
}
|
||||
var comp = 0;
|
||||
if (app.latestMetadata!.versionCode != null &&
|
||||
installedVersionCode != null &&
|
||||
app.id != 'store.zap.app') {
|
||||
if (app.latestMetadata!.versionCode != null && app.id != 'store.zap.app') {
|
||||
// Note: need to exclude zap.store because development versions always
|
||||
// carry a lower version code (e.g. 12) than published ones (e.g. 2012)
|
||||
comp = app.latestMetadata!.versionCode!.compareTo(installedVersionCode);
|
||||
@@ -107,24 +111,10 @@ mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
// else it's a downgrade, which is not installable
|
||||
return AppInstallStatus.downgrade;
|
||||
}
|
||||
|
||||
@override
|
||||
LocalApp deserializeLocal(Map<String, dynamic> map, {String? key}) {
|
||||
// TODO: implement deserializeLocal
|
||||
return super.deserializeLocal(map, key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> serializeLocal(LocalApp model,
|
||||
{bool withRelationships = true}) {
|
||||
final z = super.serializeLocal(model, withRelationships: withRelationships);
|
||||
return z;
|
||||
}
|
||||
}
|
||||
|
||||
enum AppInstallStatus {
|
||||
updated,
|
||||
updatable,
|
||||
installable,
|
||||
downgrade,
|
||||
}
|
||||
|
||||
@@ -63,6 +63,5 @@ Map<String, dynamic> _$LocalAppToJson(LocalApp instance) => <String, dynamic>{
|
||||
const _$AppInstallStatusEnumMap = {
|
||||
AppInstallStatus.updated: 'updated',
|
||||
AppInstallStatus.updatable: 'updatable',
|
||||
AppInstallStatus.installable: 'installable',
|
||||
AppInstallStatus.downgrade: 'downgrade',
|
||||
};
|
||||
|
||||
@@ -163,10 +163,11 @@ final dataLibrariesInitializer = FutureProvider<void>((ref) async {
|
||||
]);
|
||||
|
||||
// Trigger app install status calculations
|
||||
ref.localApps.localAppAdapter.refreshUpdateStatus(); // do not await
|
||||
_lifecycleListener = AppLifecycleListener(
|
||||
onStateChange: (state) async {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
await ref.localApps.localAppAdapter.updateInstallStatus();
|
||||
await ref.localApps.localAppAdapter.refreshUpdateStatus();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -27,14 +27,14 @@ class InstallButton extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final progress = ref.watch(installationProgressProvider(app.identifier!));
|
||||
final progress = ref.watch(installationProgressProvider(app.id!));
|
||||
final status = app.localApp.value?.status;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: switch (status) {
|
||||
AppInstallStatus.downgrade => null,
|
||||
AppInstallStatus.updated => () {
|
||||
packageManager.openApp(app.id!.toString());
|
||||
packageManager.openApp(app.identifier!);
|
||||
},
|
||||
_ => switch (progress) {
|
||||
IdleInstallProgress() => () {
|
||||
@@ -56,10 +56,8 @@ class InstallButton extends ConsumerWidget {
|
||||
ErrorInstallProgress(:final e) => () {
|
||||
// show error and reset state to idle
|
||||
context.showError((e as dynamic).message);
|
||||
ref
|
||||
.read(installationProgressProvider(app.id!.toString())
|
||||
.notifier)
|
||||
.state = IdleInstallProgress();
|
||||
ref.read(installationProgressProvider(app.id!).notifier).state =
|
||||
IdleInstallProgress();
|
||||
},
|
||||
_ => null,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user