mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
Add search bar
This commit is contained in:
@@ -181,3 +181,59 @@ class GMetaInfos {
|
||||
ApiResult<GMeta>.fromJson(value as Map<String, dynamic>,
|
||||
(json) => GMeta.fromJson(json as Map<String, dynamic>)))));
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class GMetaSearchInfo {
|
||||
const GMetaSearchInfo({
|
||||
required this.gid,
|
||||
required this.token,
|
||||
required this.title,
|
||||
required this.titleJpn,
|
||||
required this.category,
|
||||
required this.uploader,
|
||||
required this.posted,
|
||||
required this.filecount,
|
||||
required this.filesize,
|
||||
required this.expunged,
|
||||
required this.rating,
|
||||
required this.tags,
|
||||
this.parentGid,
|
||||
this.parentToken,
|
||||
this.firstGid,
|
||||
this.firstToken,
|
||||
});
|
||||
final int gid;
|
||||
final String token;
|
||||
final String title;
|
||||
@JsonKey(name: 'title_jpn')
|
||||
final String titleJpn;
|
||||
final String category;
|
||||
final String uploader;
|
||||
@JsonKey(fromJson: _fromJson, toJson: _toJson)
|
||||
final DateTime posted;
|
||||
final int filecount;
|
||||
final int filesize;
|
||||
final bool expunged;
|
||||
final double rating;
|
||||
final List<Tag> tags;
|
||||
@JsonKey(name: 'parent_gid')
|
||||
final int? parentGid;
|
||||
@JsonKey(name: 'parent_token')
|
||||
final String? parentToken;
|
||||
@JsonKey(name: 'first_gid')
|
||||
final int? firstGid;
|
||||
@JsonKey(name: 'first_token')
|
||||
final String? firstToken;
|
||||
|
||||
static DateTime _fromJson(int posted) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(posted * 1000);
|
||||
static int _toJson(DateTime posted) => posted.millisecondsSinceEpoch ~/ 1000;
|
||||
factory GMetaSearchInfo.fromJson(Map<String, dynamic> json) =>
|
||||
_$GMetaSearchInfoFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GMetaSearchInfoToJson(this);
|
||||
String get preferredTitle => prefs.getBool("useTitleJpn") == true
|
||||
? titleJpn.isEmpty
|
||||
? title
|
||||
: titleJpn
|
||||
: title;
|
||||
}
|
||||
|
||||
@@ -134,3 +134,45 @@ Map<String, dynamic> _$GalleryDataToJson(GalleryData instance) =>
|
||||
'tags': instance.tags,
|
||||
'pages': instance.pages,
|
||||
};
|
||||
|
||||
GMetaSearchInfo _$GMetaSearchInfoFromJson(Map<String, dynamic> json) =>
|
||||
GMetaSearchInfo(
|
||||
gid: (json['gid'] as num).toInt(),
|
||||
token: json['token'] as String,
|
||||
title: json['title'] as String,
|
||||
titleJpn: json['title_jpn'] as String,
|
||||
category: json['category'] as String,
|
||||
uploader: json['uploader'] as String,
|
||||
posted: GMetaSearchInfo._fromJson((json['posted'] as num).toInt()),
|
||||
filecount: (json['filecount'] as num).toInt(),
|
||||
filesize: (json['filesize'] as num).toInt(),
|
||||
expunged: json['expunged'] as bool,
|
||||
rating: (json['rating'] as num).toDouble(),
|
||||
tags: (json['tags'] as List<dynamic>)
|
||||
.map((e) => Tag.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
parentGid: (json['parent_gid'] as num?)?.toInt(),
|
||||
parentToken: json['parent_token'] as String?,
|
||||
firstGid: (json['first_gid'] as num?)?.toInt(),
|
||||
firstToken: json['first_token'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GMetaSearchInfoToJson(GMetaSearchInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'gid': instance.gid,
|
||||
'token': instance.token,
|
||||
'title': instance.title,
|
||||
'title_jpn': instance.titleJpn,
|
||||
'category': instance.category,
|
||||
'uploader': instance.uploader,
|
||||
'posted': GMetaSearchInfo._toJson(instance.posted),
|
||||
'filecount': instance.filecount,
|
||||
'filesize': instance.filesize,
|
||||
'expunged': instance.expunged,
|
||||
'rating': instance.rating,
|
||||
'tags': instance.tags,
|
||||
'parent_gid': instance.parentGid,
|
||||
'parent_token': instance.parentToken,
|
||||
'first_gid': instance.firstGid,
|
||||
'first_token': instance.firstToken,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:meilisearch/meilisearch.dart';
|
||||
import 'api/status.dart';
|
||||
import 'api/token.dart';
|
||||
import 'api/user.dart';
|
||||
@@ -35,16 +36,25 @@ class AuthInfo {
|
||||
_user?.permissions.has(UserPermission.manageTasks);
|
||||
bool? get canShareGallery =>
|
||||
_user?.permissions.has(UserPermission.shareGallery);
|
||||
MeilisearchInfo? get meilisearch => _status?.meilisearch;
|
||||
MeiliSearchClient? _meiliSearchClient;
|
||||
MeiliSearchClient? get meiliSearchClient => _meiliSearchClient;
|
||||
|
||||
void clear() {
|
||||
_user = null;
|
||||
_status = null;
|
||||
_meiliSearchClient = null;
|
||||
_token = null;
|
||||
_checked = false;
|
||||
}
|
||||
|
||||
Future<void> getServerStatus() async {
|
||||
_status = (await api.getStatus()).unwrap();
|
||||
if (_status?.meilisearch != null) {
|
||||
_meiliSearchClient = MeiliSearchClient(
|
||||
_status!.meilisearch!.host, _status!.meilisearch!.key);
|
||||
listener.tryEmit("meilisearch_enabled", null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkSessionInfo() async {
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'api/client.dart';
|
||||
import 'api/gallery.dart';
|
||||
import 'auth.dart';
|
||||
import 'config/base.dart';
|
||||
import 'config/shared_preferences.dart';
|
||||
@@ -309,6 +310,31 @@ Widget buildMoreVertSettingsButon(BuildContext context) {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSearchButton(BuildContext context, {bool openGallery = true}) {
|
||||
return auth.meilisearch != null
|
||||
? SearchAnchor(builder: (context, controller) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
controller.openView();
|
||||
},
|
||||
icon: const Icon(Icons.search));
|
||||
}, suggestionsBuilder: (context, controller) async {
|
||||
final c = auth.meiliSearchClient!;
|
||||
final re = await c.index("gmeta").search(controller.text);
|
||||
return re.asSearchResult().hits.map((e) {
|
||||
final m = GMetaSearchInfo.fromJson(e);
|
||||
return ListTile(
|
||||
title: Text(m.preferredTitle),
|
||||
onTap: () {
|
||||
controller.closeView(m.preferredTitle);
|
||||
if (openGallery) context.push("/gallery/${m.gid}");
|
||||
},
|
||||
);
|
||||
}).toList();
|
||||
})
|
||||
: Container();
|
||||
}
|
||||
|
||||
ThemeMode themeModeNext(ThemeMode mode) {
|
||||
if (mode == ThemeMode.system) return ThemeMode.light;
|
||||
if (mode == ThemeMode.dark) return ThemeMode.system;
|
||||
|
||||
@@ -119,6 +119,7 @@ class _GalleriesPage extends State<GalleriesPage>
|
||||
});
|
||||
_translatedTag = widget.translatedTag;
|
||||
listener.on("user_logined", _onStateChanged);
|
||||
listener.on("meilisearch_enabled", _onStateChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@@ -202,6 +203,7 @@ class _GalleriesPage extends State<GalleriesPage>
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (context) =>
|
||||
[PopupMenuItem(child: sortByGidMenu)]),
|
||||
buildSearchButton(context),
|
||||
buildThemeModeIcon(context),
|
||||
buildMoreVertSettingsButon(context),
|
||||
]),
|
||||
@@ -226,6 +228,7 @@ class _GalleriesPage extends State<GalleriesPage>
|
||||
_pagingController.dispose();
|
||||
_tagCancel?.cancel();
|
||||
listener.removeEventListener("user_logined", _onStateChanged);
|
||||
listener.removeEventListener("meilisearch_enabled", _onStateChanged);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ class HomePage extends HookWidget with IsTopWidget {
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.titleBar),
|
||||
actions: [
|
||||
buildSearchButton(context),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
final n = themeModeNext(mode.value);
|
||||
|
||||
@@ -192,6 +192,7 @@ class _TaskManagerPage extends State<TaskManagerPage>
|
||||
),
|
||||
title: Text(i18n.taskManager),
|
||||
actions: [
|
||||
buildSearchButton(context),
|
||||
buildThemeModeIcon(context),
|
||||
buildMoreVertSettingsButon(context),
|
||||
],
|
||||
|
||||
@@ -563,6 +563,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meilisearch:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: meilisearch
|
||||
sha256: "0567639a4cccca84bf7c13fc34c9d8a277f896a57388d1afde69ad0084f49a46"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.16.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -29,6 +29,7 @@ dependencies:
|
||||
json_annotation: ^4.9.0
|
||||
keymap: ^0.0.92
|
||||
logging: ^1.2.0
|
||||
meilisearch: ^0.16.0
|
||||
mutex: ^3.1.0
|
||||
package_info_plus: ^8.0.0
|
||||
palette_generator: ^0.3.3+3
|
||||
|
||||
Reference in New Issue
Block a user