diff --git a/lib/models/app.dart b/lib/models/app.dart index 37ff4b9..2152ccb 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -171,6 +171,30 @@ class App extends BaseApp with DataModelMixin { mixin AppAdapter on Adapter { final _queriedAtMap = {}; + @override + Future> findAll( + {bool? remote = true, + bool? background, + Map? params = const {}, + Map? headers, + bool? syncLocal, + OnSuccessAll? onSuccess, + OnErrorAll? onError, + DataRequestLabel? label}) async { + return await fetchAppModels(params ?? {}); + } + + Future> findInstalled() async { + final appIds = await _installedIdentifiers(); + if (appIds.isNotEmpty) { + final apps = await fetchAppModels({'#d': appIds}); + // Once apps are loaded, check for installed status + await ref.localApps.localAppAdapter.refreshUpdateStatus(); + return apps; + } + return []; + } + Future> fetchAppModels(Map params) async { final byRelease = params.remove('by-release') != null; @@ -214,6 +238,7 @@ mixin AppAdapter on Adapter { 'since': earliestQueryAt, }, ); + // print('cached ${cachedApps.length} ($earliestQueryAt)'); } // For apps not in cache, query without a since @@ -304,38 +329,6 @@ mixin AppAdapter on Adapter { return apps; } - @override - Future> findAll( - {bool? remote = true, - bool? background, - Map? params = const {}, - Map? headers, - bool? syncLocal, - OnSuccessAll? onSuccess, - OnErrorAll? onError, - DataRequestLabel? label}) async { - if (params!.containsKey('installed')) { - final appIds = await _installedIdentifiers(); - if (appIds.isNotEmpty) { - params['#d'] = appIds; - params.remove('installed'); - final apps = await fetchAppModels(params); - // Once apps are loaded, check for installed status - await ref.localApps.localAppAdapter.refreshUpdateStatus(); - return apps; - } - } - return await fetchAppModels(params); - } - - List findWhereIdInLocal(Iterable appIds) { - const len = 5 + 64 + 3; // kind + pubkey + separators length - final result = db.select( - 'SELECT key, data, substr(json_extract(data, \'\$.id\'), $len) AS id from apps where id in (${appIds.map((_) => '?').join(', ')})', - appIds.toList()); - return deserializeFromResult(result); - } - @override Future findOne(Object id, {bool remote = true, @@ -359,6 +352,14 @@ mixin AppAdapter on Adapter { return apps.first; } + List findWhereIdInLocal(Iterable appIds) { + const len = 5 + 64 + 3; // kind + pubkey + separators length + final result = db.select( + 'SELECT key, data, substr(json_extract(data, \'\$.id\'), $len) AS id from apps where id in (${appIds.map((_) => '?').join(', ')})', + appIds.toList()); + return deserializeFromResult(result); + } + Future> _installedIdentifiers() async { if (!Platform.isAndroid) { return {}; diff --git a/lib/navigation/app_initializer.dart b/lib/navigation/app_initializer.dart index 50f4bd5..c2e73a5 100644 --- a/lib/navigation/app_initializer.dart +++ b/lib/navigation/app_initializer.dart @@ -9,6 +9,7 @@ import 'package:zapstore/models/local_app.dart'; import 'package:zapstore/models/nostr_adapter.dart'; import 'package:zapstore/navigation/router.dart'; import 'package:zapstore/widgets/app_curation_container.dart'; +import 'package:zapstore/widgets/latest_releases_container.dart'; AppLifecycleListener? _lifecycleListener; @@ -48,17 +49,17 @@ final appInitializer = FutureProvider((ref) async { }, ); - // In this initial phase, load there more or less fixed curation sets here + // Load curation sets (here for now) await ref.appCurationSets.findAll(); + // Preload zapstore's nostr curation set await ref.read(appCurationSetProvider(kNostrCurationSet).future); - await ref.apps.findAll( - params: { - 'by-release': true, - 'limit': 10, - }, - ); + // Preload latest releases + await ref.read(latestReleasesAppProvider.notifier).fetch(); + + // Prefetch updates in the background + ref.apps.appAdapter.findInstalled(); // Handle deep links final appLinksSub = appLinks.uriLinkStream.listen((uri) async { diff --git a/lib/screens/updates_screen.dart b/lib/screens/updates_screen.dart index 42fdaf8..744b3e1 100644 --- a/lib/screens/updates_screen.dart +++ b/lib/screens/updates_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:zapstore/main.data.dart'; +import 'package:zapstore/models/app.dart'; import 'package:zapstore/widgets/app_card.dart'; class UpdatesScreen extends HookConsumerWidget { @@ -12,8 +13,8 @@ class UpdatesScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // TODO: Workaround for bug in watchAll (when remote=true) - final snapshot = useFuture(useMemoized( - () => ref.apps.findAll(remote: true, params: {'installed': true}))); + final snapshot = + useFuture(useMemoized(() => ref.apps.appAdapter.findInstalled())); final state = ref.apps.watchAll(); ref.localApps.watchAll(); @@ -29,7 +30,7 @@ class UpdatesScreen extends HookConsumerWidget { return RefreshIndicator( onRefresh: () { - return ref.apps.findAll(remote: true, params: {'installed': true}); + return ref.apps.appAdapter.findInstalled(); }, child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), diff --git a/lib/widgets/latest_releases_container.dart b/lib/widgets/latest_releases_container.dart index 1dd6049..b3fc298 100644 --- a/lib/widgets/latest_releases_container.dart +++ b/lib/widgets/latest_releases_container.dart @@ -94,9 +94,6 @@ class LatestReleasesAppNotifier extends AutoDisposeAsyncNotifier> { // TODO: Should be ref.watching a pool state change (from purplebase) final timer = Timer.periodic(Duration(minutes: 10), (_) => fetch()); ref.onDispose(timer.cancel); - - // Do not fetch here as first 10 are preloaded - // await fetch(); return localFetch(); }