add version field, installable app caching
This commit is contained in:
parent
4833b5c18c
commit
2141e144be
7 changed files with 79 additions and 9 deletions
|
|
@ -9,17 +9,47 @@ part 'scoop_search_event.dart';
|
||||||
part 'scoop_search_state.dart';
|
part 'scoop_search_state.dart';
|
||||||
|
|
||||||
class ScoopSearchBloc extends Bloc<ScoopSearchEvent, ScoopSearchState> {
|
class ScoopSearchBloc extends Bloc<ScoopSearchEvent, ScoopSearchState> {
|
||||||
|
final Map<String, List<ScoopAppModel>> _cachedApps = {};
|
||||||
|
|
||||||
ScoopSearchBloc() : super(ScoopSearchInitial()) {
|
ScoopSearchBloc() : super(ScoopSearchInitial()) {
|
||||||
|
on<ScoopSearchReload>(
|
||||||
|
(event, emit) async {
|
||||||
|
_cachedApps.clear();
|
||||||
|
try {
|
||||||
|
Map<String, List<ScoopAppModel>> data = await getAllInstallableApps();
|
||||||
|
_cachedApps.addAll(data);
|
||||||
|
emit(ScoopSearchLoaded(data));
|
||||||
|
} catch (e) {
|
||||||
|
emit(const ScoopSearchLoaded({}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transformer: droppable(),
|
||||||
|
);
|
||||||
on<ScoopSearchQueryChanged>(
|
on<ScoopSearchQueryChanged>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
emit(ScoopSearchLoading());
|
emit(ScoopSearchLoading());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, List<ScoopAppModel>> data = {};
|
Map<String, List<ScoopAppModel>> data = {};
|
||||||
if (event.query.isEmpty) {
|
if (_cachedApps.isEmpty) {
|
||||||
data = await getAllInstallableApps();
|
_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 {
|
} else {
|
||||||
data = await searchInstallableApps(event.query);
|
data.addAll(_cachedApps);
|
||||||
}
|
}
|
||||||
emit(ScoopSearchLoaded(data));
|
emit(ScoopSearchLoaded(data));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ abstract class ScoopSearchEvent extends Equatable {
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScoopSearchReload extends ScoopSearchEvent {}
|
||||||
|
|
||||||
class ScoopSearchQueryChanged extends ScoopSearchEvent {
|
class ScoopSearchQueryChanged extends ScoopSearchEvent {
|
||||||
final String query;
|
final String query;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@ void main() async {
|
||||||
create: (context) => ScoopListBloc()..add(ScoopLocate()),
|
create: (context) => ScoopListBloc()..add(ScoopLocate()),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) =>
|
create: (context) => ScoopSearchBloc()..add(ScoopSearchReload()),
|
||||||
ScoopSearchBloc()..add(const ScoopSearchQueryChanged("")),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const LadleApp(),
|
child: const LadleApp(),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ class ScoopAppModel extends Equatable {
|
||||||
final String name;
|
final String name;
|
||||||
final String description;
|
final String description;
|
||||||
final String bucket;
|
final String bucket;
|
||||||
|
final String homepage;
|
||||||
|
final String version;
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
const ScoopAppModel({
|
const ScoopAppModel({
|
||||||
|
|
@ -11,8 +13,11 @@ class ScoopAppModel extends Equatable {
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.bucket,
|
required this.bucket,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
|
required this.homepage,
|
||||||
|
required this.version,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [name, description, bucket, updatedAt];
|
List<Object?> get props =>
|
||||||
|
[name, description, bucket, homepage, version, updatedAt];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'package:ladle/models/scoop_app_model.dart';
|
||||||
import 'package:ladle/utils/scoop_utils.dart';
|
import 'package:ladle/utils/scoop_utils.dart';
|
||||||
import 'package:ladle/utils/set_extension.dart';
|
import 'package:ladle/utils/set_extension.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
import '../bloc/scoop_list_bloc.dart';
|
import '../bloc/scoop_list_bloc.dart';
|
||||||
|
|
||||||
|
|
@ -218,7 +219,14 @@ class _AppSearchFragmentState extends State<AppSearchFragment> {
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Chip(
|
Chip(
|
||||||
|
|
@ -251,6 +259,15 @@ class _AppSearchFragmentState extends State<AppSearchFragment> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
).paddingDirectional(bottom: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
OutlinedButton(
|
||||||
|
child: const Text("Homepage"),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(appModel.homepage);
|
||||||
|
}).padding(right: 8),
|
||||||
|
],
|
||||||
).paddingDirectional(bottom: 16),
|
).paddingDirectional(bottom: 16),
|
||||||
Text(
|
Text(
|
||||||
appModel.description,
|
appModel.description,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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:styled_widget/styled_widget.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
|
@ -92,8 +93,10 @@ class _HomePageState extends State<HomePage> {
|
||||||
),
|
),
|
||||||
onPressed: state is ScoopListLoading
|
onPressed: state is ScoopListLoading
|
||||||
? null
|
? null
|
||||||
: () =>
|
: () {
|
||||||
context.read<ScoopListBloc>().add(ScoopUpdateRequested()),
|
context.read<ScoopListBloc>().add(ScoopUpdateRequested());
|
||||||
|
context.read<ScoopSearchBloc>().add(ScoopSearchReload());
|
||||||
|
},
|
||||||
),
|
),
|
||||||
WindowButton(
|
WindowButton(
|
||||||
iconBuilder: (buttonContext) => const Icon(
|
iconBuilder: (buttonContext) => const Icon(
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ Future<Map<String, List<ScoopAppModel>>> searchInstallableApps(
|
||||||
String appName =
|
String appName =
|
||||||
file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), "");
|
file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), "");
|
||||||
String appDescription = data["description"] ?? "No description";
|
String appDescription = data["description"] ?? "No description";
|
||||||
|
String appHomepage = data["homepage"] ?? "";
|
||||||
|
String appVersion = data["version"] ?? "0.0.0";
|
||||||
DateTime appUpdatedAt = await file.lastModified();
|
DateTime appUpdatedAt = await file.lastModified();
|
||||||
|
|
||||||
if (appName.toLowerCase().contains(query.toLowerCase()) ||
|
if (appName.toLowerCase().contains(query.toLowerCase()) ||
|
||||||
|
|
@ -58,6 +60,8 @@ Future<Map<String, List<ScoopAppModel>>> searchInstallableApps(
|
||||||
name: appName,
|
name: appName,
|
||||||
description: appDescription,
|
description: appDescription,
|
||||||
bucket: bucket,
|
bucket: bucket,
|
||||||
|
homepage: appHomepage,
|
||||||
|
version: appVersion,
|
||||||
updatedAt: appUpdatedAt,
|
updatedAt: appUpdatedAt,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -91,12 +95,16 @@ Future<Map<String, List<ScoopAppModel>>> getAllInstallableApps() async {
|
||||||
String appName =
|
String appName =
|
||||||
file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), "");
|
file.path.split("\\").last.replaceAll(RegExp(r"\.(json|ya?ml)"), "");
|
||||||
String appDescription = data["description"] ?? "No description";
|
String appDescription = data["description"] ?? "No description";
|
||||||
|
String appHomepage = data["homepage"] ?? "";
|
||||||
|
String appVersion = data["version"] ?? "0.0.0";
|
||||||
DateTime appUpdatedAt = await file.lastModified();
|
DateTime appUpdatedAt = await file.lastModified();
|
||||||
|
|
||||||
apps.add(ScoopAppModel(
|
apps.add(ScoopAppModel(
|
||||||
name: appName,
|
name: appName,
|
||||||
description: appDescription,
|
description: appDescription,
|
||||||
bucket: bucket,
|
bucket: bucket,
|
||||||
|
homepage: appHomepage,
|
||||||
|
version: appVersion,
|
||||||
updatedAt: appUpdatedAt,
|
updatedAt: appUpdatedAt,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -117,12 +125,16 @@ Future<List<ScoopAppModel>> getInstalledScoopApps() async {
|
||||||
String appName = element.path.split("\\").last;
|
String appName = element.path.split("\\").last;
|
||||||
String appDescription = "No description";
|
String appDescription = "No description";
|
||||||
String appBucket = "UNKNOWN";
|
String appBucket = "UNKNOWN";
|
||||||
|
String appHomepage = "";
|
||||||
|
String appVersion = "0.0.0";
|
||||||
DateTime appUpdatedAt = DateTime.fromMicrosecondsSinceEpoch(0);
|
DateTime appUpdatedAt = DateTime.fromMicrosecondsSinceEpoch(0);
|
||||||
|
|
||||||
final manifestFile = File("${element.path}/current/manifest.json");
|
final manifestFile = File("${element.path}/current/manifest.json");
|
||||||
if (await manifestFile.exists()) {
|
if (await manifestFile.exists()) {
|
||||||
final manifestData = jsonDecode(await manifestFile.readAsString());
|
final manifestData = jsonDecode(await manifestFile.readAsString());
|
||||||
appDescription = manifestData["description"] ?? appDescription;
|
appDescription = manifestData["description"] ?? appDescription;
|
||||||
|
appHomepage = manifestData["homepage"] ?? appHomepage;
|
||||||
|
appVersion = manifestData["version"] ?? appVersion;
|
||||||
appUpdatedAt = await manifestFile.lastModified();
|
appUpdatedAt = await manifestFile.lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,6 +148,8 @@ Future<List<ScoopAppModel>> getInstalledScoopApps() async {
|
||||||
name: appName,
|
name: appName,
|
||||||
description: appDescription,
|
description: appDescription,
|
||||||
bucket: appBucket,
|
bucket: appBucket,
|
||||||
|
homepage: appHomepage,
|
||||||
|
version: appVersion,
|
||||||
updatedAt: appUpdatedAt,
|
updatedAt: appUpdatedAt,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue