mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 20:48:24 +00:00
Back up the list of installed apps to Nostr (encrypted AppStack) when batch installs complete or apps are uninstalled. Toggle in profile, off by default. Restore screen fetches backup and lets users reinstall apps. Changes by file: - installed_apps_backup_service: Listener for batch completion and installed-list changes; debounced backup (3s); restore-only batches ignored; timer cancelled on provider dispose; logs reduced to errors and completion only; null-aware assignment for baseline keys. - secure_storage_service: Persistence for backup-enabled toggle (off by default). - profile_screen: Toggle to enable/disable backup; entry point for restore flow. - restore_installed_apps_screen: Restore UI with shared AppBar; lists apps to install and already installed; cards navigate to app detail. - main_scaffold: Watches backup listener provider so it stays active. - package_manager: Removed unused installSourceProvider; install source handling for restore operations. - install_operation, android_package_manager, install_button, batch_progress_banner: InstallSource.restore support for restore flow. - app_card: Factory constructors (forRestore, forDiscovery, etc.) to simplify restore and other screens. - router, installed_packages_snapshot: Supporting wiring for the feature.
64 lines
2.0 KiB
Dart
64 lines
2.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:models/models.dart';
|
|
import 'package:zapstore/services/package_manager/package_manager.dart';
|
|
import 'package:zapstore/theme.dart';
|
|
import 'package:zapstore/utils/extensions.dart';
|
|
|
|
/// "Update All" / "Install All" button row.
|
|
/// Progress and "All done" states are handled by sticky banners in _UpdatesListBody.
|
|
class UpdateAllRow extends ConsumerWidget {
|
|
const UpdateAllRow({
|
|
super.key,
|
|
required this.allUpdates,
|
|
this.label = 'Update All',
|
|
this.installSource = InstallSource.normal,
|
|
});
|
|
|
|
final List<App> allUpdates;
|
|
final String label;
|
|
final InstallSource installSource;
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
if (allUpdates.length < 2) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
|
width: double.infinity,
|
|
child: TextButton.icon(
|
|
onPressed: () async {
|
|
final pm = ref.read(packageManagerProvider.notifier);
|
|
final items = allUpdates
|
|
.where((app) => app.latestFileMetadata != null)
|
|
.map(
|
|
(app) => (
|
|
appId: app.identifier,
|
|
target: app.latestFileMetadata!,
|
|
displayName: app.name,
|
|
source: installSource,
|
|
),
|
|
)
|
|
.toList();
|
|
await pm.queueDownloads(items);
|
|
},
|
|
icon: const Icon(Icons.download, size: 16, color: Colors.white),
|
|
label: Text(
|
|
'$label (${allUpdates.length})',
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: AppColors.darkPillBackground,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|