Merge pull request #309 from zapstore/fix/install-button-state

Fix Install/Delete buttons after install
This commit is contained in:
franzap
2026-02-06 16:04:59 +00:00
committed by GitHub
3 changed files with 28 additions and 4 deletions
@@ -330,7 +330,18 @@ abstract class PackageManager extends StateNotifier<PackageManagerState> {
String? displayName,
}) async {
await _ensureDownloaderReady();
if (hasOperation(appId)) return false;
final existing = getOperation(appId);
if (existing != null) {
// We keep terminal states (Completed/Failed) in the operations map briefly so
// batch progress UI can derive totals. However, starting a new download for the
// same app should not be blocked by a stale terminal op (e.g. install -> uninstall
// -> install again). Only in-flight operations should block.
if (existing is Completed || existing is OperationFailed) {
clearOperation(appId);
} else {
return false;
}
}
final downloadUrl = target.urls.firstOrNull;
if (downloadUrl == null || downloadUrl.isEmpty) {
+1
View File
@@ -11,6 +11,7 @@ bool isDebugMode(String? pubkey) {
const allowedHexKeys = {
'726a1e261cc6474674e8285e3951b3bb139be9a773d1acf49dc868db861a1c11',
'227ce06aa5fb84bf70f25d887519374d1bfd3e1ffb75697bb0aa8f6396f32e43',
'24480686b56234a240fd9827209b584847f3d4f9657f0d9a97aec5320a264acb',
};
// Try direct comparison first (hex format)
+15 -3
View File
@@ -32,6 +32,12 @@ class InstallButton extends ConsumerWidget {
// Derive what to display
final isInstalled = installedPkg != null;
// Note: PackageManager keeps terminal operations (e.g. Completed) in the
// operations map for a while so batch UX (e.g. "All done") can derive counts.
// For the detail screen action row, Completed should be treated as "not busy"
// so the user can immediately Open/Delete after install.
final canShowActionButtons =
isInstalled && (operation == null || operation is Completed);
final hasUpdate = app.hasUpdate;
final hasDowngrade = app.hasDowngrade;
final hasRelease = release != null;
@@ -77,7 +83,7 @@ class InstallButton extends ConsumerWidget {
children: [
Expanded(child: SizedBox(height: 48, child: button)),
// Show action buttons only for installed apps with no active operation
if (isInstalled && operation == null) ...[
if (canShowActionButtons) ...[
if (hasUpdate) ...[
const SizedBox(width: 8),
_buildOpenIconButton(context, ref),
@@ -102,9 +108,15 @@ class InstallButton extends ConsumerWidget {
required FileMetadata? fileMetadata,
required double fontSize,
}) {
// Completed is a terminal "result" state and may linger for batch progress UX.
// If the app is no longer installed (e.g. user uninstalled right after install),
// ignore a stale Completed op so we fall back to the normal "Install" UI.
final effectiveOperation =
(!isInstalled && operation is Completed) ? null : operation;
// Handle operation states first
if (operation != null) {
return switch (operation) {
if (effectiveOperation != null) {
return switch (effectiveOperation) {
DownloadQueued() => _buildSimpleButton(
context,
'Queued for download',