From 2141e144be86d5a14f7f86ba7158fbf160b176ca Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Tue, 13 Sep 2022 11:45:10 +0700 Subject: [PATCH] add version field, installable app caching --- lib/bloc/scoop_search_bloc.dart | 36 +++++++++++++++++++++++++++--- lib/bloc/scoop_search_event.dart | 2 ++ lib/main.dart | 3 +-- lib/models/scoop_app_model.dart | 7 +++++- lib/pages/app_search_fragment.dart | 19 +++++++++++++++- lib/pages/home_page.dart | 7 ++++-- lib/utils/scoop_utils.dart | 14 ++++++++++++ 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/lib/bloc/scoop_search_bloc.dart b/lib/bloc/scoop_search_bloc.dart index 0b37ab9..4a4f506 100644 --- a/lib/bloc/scoop_search_bloc.dart +++ b/lib/bloc/scoop_search_bloc.dart @@ -9,17 +9,47 @@ part 'scoop_search_event.dart'; part 'scoop_search_state.dart'; class ScoopSearchBloc extends Bloc { + final Map> _cachedApps = {}; + ScoopSearchBloc() : super(ScoopSearchInitial()) { + on( + (event, emit) async { + _cachedApps.clear(); + try { + Map> data = await getAllInstallableApps(); + _cachedApps.addAll(data); + emit(ScoopSearchLoaded(data)); + } catch (e) { + emit(const ScoopSearchLoaded({})); + } + }, + transformer: droppable(), + ); on( (event, emit) async { emit(ScoopSearchLoading()); try { Map> data = {}; - if (event.query.isEmpty) { - data = await getAllInstallableApps(); + if (_cachedApps.isEmpty) { + _cachedApps.addAll(await getAllInstallableApps()); + } + + if (event.query.isNotEmpty) { + for (final bucket in _cachedApps.keys) { + data[bucket] = _cachedApps[bucket] + ?.where((app) => + app.name + .toLowerCase() + .contains(event.query.toLowerCase()) || + app.description + .toLowerCase() + .contains(event.query.toLowerCase())) + .toList() ?? + []; + } } else { - data = await searchInstallableApps(event.query); + data.addAll(_cachedApps); } emit(ScoopSearchLoaded(data)); } catch (e) { diff --git a/lib/bloc/scoop_search_event.dart b/lib/bloc/scoop_search_event.dart index 77709ed..daa8aca 100644 --- a/lib/bloc/scoop_search_event.dart +++ b/lib/bloc/scoop_search_event.dart @@ -7,6 +7,8 @@ abstract class ScoopSearchEvent extends Equatable { List get props => []; } +class ScoopSearchReload extends ScoopSearchEvent {} + class ScoopSearchQueryChanged extends ScoopSearchEvent { final String query; diff --git a/lib/main.dart b/lib/main.dart index ffc753f..e48f230 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,8 +31,7 @@ void main() async { create: (context) => ScoopListBloc()..add(ScoopLocate()), ), BlocProvider( - create: (context) => - ScoopSearchBloc()..add(const ScoopSearchQueryChanged("")), + create: (context) => ScoopSearchBloc()..add(ScoopSearchReload()), ), ], child: const LadleApp(), diff --git a/lib/models/scoop_app_model.dart b/lib/models/scoop_app_model.dart index 9be26c6..c77e922 100644 --- a/lib/models/scoop_app_model.dart +++ b/lib/models/scoop_app_model.dart @@ -4,6 +4,8 @@ class ScoopAppModel extends Equatable { final String name; final String description; final String bucket; + final String homepage; + final String version; final DateTime updatedAt; const ScoopAppModel({ @@ -11,8 +13,11 @@ class ScoopAppModel extends Equatable { required this.description, required this.bucket, required this.updatedAt, + required this.homepage, + required this.version, }); @override - List get props => [name, description, bucket, updatedAt]; + List get props => + [name, description, bucket, homepage, version, updatedAt]; } diff --git a/lib/pages/app_search_fragment.dart b/lib/pages/app_search_fragment.dart index 8bb0ca1..494cec3 100644 --- a/lib/pages/app_search_fragment.dart +++ b/lib/pages/app_search_fragment.dart @@ -9,6 +9,7 @@ import 'package:ladle/models/scoop_app_model.dart'; import 'package:ladle/utils/scoop_utils.dart'; import 'package:ladle/utils/set_extension.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import '../bloc/scoop_list_bloc.dart'; @@ -218,7 +219,14 @@ class _AppSearchFragmentState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(appModel.name).fontSize(32).bold().paddingDirectional(bottom: 16), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(appModel.name).fontSize(32).bold(), + Text(appModel.version).padding(all: 4) + ], + ).paddingDirectional(bottom: 16), Row( children: [ Chip( @@ -251,6 +259,15 @@ class _AppSearchFragmentState extends State { }, ), ], + ).paddingDirectional(bottom: 8), + Row( + children: [ + OutlinedButton( + child: const Text("Homepage"), + onPressed: () { + launchUrlString(appModel.homepage); + }).padding(right: 8), + ], ).paddingDirectional(bottom: 16), Text( appModel.description, diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 4b6bbdc..20ef959 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -1,6 +1,7 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ladle/bloc/scoop_search_bloc.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -92,8 +93,10 @@ class _HomePageState extends State { ), onPressed: state is ScoopListLoading ? null - : () => - context.read().add(ScoopUpdateRequested()), + : () { + context.read().add(ScoopUpdateRequested()); + context.read().add(ScoopSearchReload()); + }, ), WindowButton( iconBuilder: (buttonContext) => const Icon( diff --git a/lib/utils/scoop_utils.dart b/lib/utils/scoop_utils.dart index ab4cb29..8739403 100644 --- a/lib/utils/scoop_utils.dart +++ b/lib/utils/scoop_utils.dart @@ -50,6 +50,8 @@ Future>> searchInstallableApps( String appName = file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), ""); String appDescription = data["description"] ?? "No description"; + String appHomepage = data["homepage"] ?? ""; + String appVersion = data["version"] ?? "0.0.0"; DateTime appUpdatedAt = await file.lastModified(); if (appName.toLowerCase().contains(query.toLowerCase()) || @@ -58,6 +60,8 @@ Future>> searchInstallableApps( name: appName, description: appDescription, bucket: bucket, + homepage: appHomepage, + version: appVersion, updatedAt: appUpdatedAt, )); } @@ -91,12 +95,16 @@ Future>> getAllInstallableApps() async { String appName = file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), ""); String appDescription = data["description"] ?? "No description"; + String appHomepage = data["homepage"] ?? ""; + String appVersion = data["version"] ?? "0.0.0"; DateTime appUpdatedAt = await file.lastModified(); apps.add(ScoopAppModel( name: appName, description: appDescription, bucket: bucket, + homepage: appHomepage, + version: appVersion, updatedAt: appUpdatedAt, )); } @@ -117,12 +125,16 @@ Future> getInstalledScoopApps() async { String appName = element.path.split("\\").last; String appDescription = "No description"; String appBucket = "UNKNOWN"; + String appHomepage = ""; + String appVersion = "0.0.0"; DateTime appUpdatedAt = DateTime.fromMicrosecondsSinceEpoch(0); final manifestFile = File("${element.path}/current/manifest.json"); if (await manifestFile.exists()) { final manifestData = jsonDecode(await manifestFile.readAsString()); appDescription = manifestData["description"] ?? appDescription; + appHomepage = manifestData["homepage"] ?? appHomepage; + appVersion = manifestData["version"] ?? appVersion; appUpdatedAt = await manifestFile.lastModified(); } @@ -136,6 +148,8 @@ Future> getInstalledScoopApps() async { name: appName, description: appDescription, bucket: appBucket, + homepage: appHomepage, + version: appVersion, updatedAt: appUpdatedAt, )); }