Add custom tag suggestion handler (#424)

This commit is contained in:
Gandum2077
2025-06-24 19:47:14 +08:00
committed by GitHub
parent 838d5c9c3e
commit d5d72911ed
6 changed files with 48 additions and 11 deletions

View File

@@ -363,6 +363,11 @@ This part is used to load comics of a category.
// enable tags suggestions // enable tags suggestions
enableTagsSuggestions: false, enableTagsSuggestions: false,
// [Optional] handle tag suggestion click
onTagSuggestionSelected: (namespace, tag) => {
// return the text to insert into search box
return `${namespace}:${tag}`
},
} }
``` ```

View File

@@ -184,6 +184,9 @@ class ComicSource {
final HandleClickTagEvent? handleClickTagEvent; final HandleClickTagEvent? handleClickTagEvent;
/// Callback when a tag suggestion is selected in search.
final TagSuggestionSelectFunc? onTagSuggestionSelected;
final LinkHandler? linkHandler; final LinkHandler? linkHandler;
final bool enableTagsSuggestions; final bool enableTagsSuggestions;
@@ -259,6 +262,7 @@ class ComicSource {
this.idMatcher, this.idMatcher,
this.translations, this.translations,
this.handleClickTagEvent, this.handleClickTagEvent,
this.onTagSuggestionSelected,
this.linkHandler, this.linkHandler,
this.enableTagsSuggestions, this.enableTagsSuggestions,
this.enableTagsTranslate, this.enableTagsTranslate,

View File

@@ -148,6 +148,7 @@ class ComicSourceParser {
_parseIdMatch(), _parseIdMatch(),
_parseTranslation(), _parseTranslation(),
_parseClickTagEvent(), _parseClickTagEvent(),
_parseTagSuggestionSelectFunc(),
_parseLinkHandler(), _parseLinkHandler(),
_getValue("search.enableTagsSuggestions") ?? false, _getValue("search.enableTagsSuggestions") ?? false,
_getValue("comic.enableTagsTranslate") ?? false, _getValue("comic.enableTagsTranslate") ?? false,
@@ -1057,6 +1058,19 @@ class ComicSourceParser {
}; };
} }
TagSuggestionSelectFunc? _parseTagSuggestionSelectFunc() {
if (!_checkExists("search.onTagSuggestionSelected")) {
return null;
}
return (namespace, tag) {
var res = JsEngine().runCode("""
ComicSource.sources.$_key.search.onTagSuggestionSelected(
${jsonEncode(namespace)}, ${jsonEncode(tag)})
""");
return res is String ? res : "$namespace:$tag";
};
}
LinkHandler? _parseLinkHandler() { LinkHandler? _parseLinkHandler() {
if (!_checkExists("comic.link")) { if (!_checkExists("comic.link")) {
return null; return null;

View File

@@ -44,5 +44,10 @@ typedef VoteCommentFunc = Future<Res<int?>> Function(
typedef HandleClickTagEvent = PageJumpTarget? Function( typedef HandleClickTagEvent = PageJumpTarget? Function(
String namespace, String tag); String namespace, String tag);
/// Handle tag suggestion selection event. Should return the text to insert
/// into the search field.
typedef TagSuggestionSelectFunc = String Function(
String namespace, String tag);
/// [rating] is the rating value, 0-10. 1 represents 0.5 star. /// [rating] is the rating value, 0-10. 1 represents 0.5 star.
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating); typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);

View File

@@ -376,11 +376,16 @@ class _SearchPageState extends State<SearchPage> {
controller.text = controller.text =
controller.text.replaceLast(words[words.length - 1], ""); controller.text.replaceLast(words[words.length - 1], "");
} }
if (type != null) { final source = ComicSource.find(searchTarget);
controller.text += "${type.name}:$text "; String insert;
if (source?.onTagSuggestionSelected != null) {
insert = source!.onTagSuggestionSelected!(type?.name ?? '', text);
} else { } else {
controller.text += "$text "; var t = text;
if (t.contains(' ')) t = "'$t'";
insert = type != null ? "${type.name}:$t" : t;
} }
controller.text += "$insert ";
suggestions.clear(); suggestions.clear();
update(); update();
focusNode.requestFocus(); focusNode.requestFocus();

View File

@@ -124,7 +124,7 @@ class _SearchResultPageState extends State<SearchResultPage> {
options = widget.options ?? const []; options = widget.options ?? const [];
validateOptions(); validateOptions();
appdata.addSearchHistory(text); appdata.addSearchHistory(text);
suggestionsController = _SuggestionsController(controller); suggestionsController = _SuggestionsController(controller, sourceKey);
super.initState(); super.initState();
} }
@@ -213,6 +213,8 @@ class _SuggestionsController {
final SearchBarController controller; final SearchBarController controller;
final String sourceKey;
OverlayEntry? entry; OverlayEntry? entry;
void updateWidget() { void updateWidget() {
@@ -270,7 +272,7 @@ class _SuggestionsController {
find(TagsTranslation.cosplayerTags, TranslationType.cosplayer); find(TagsTranslation.cosplayerTags, TranslationType.cosplayer);
} }
_SuggestionsController(this.controller); _SuggestionsController(this.controller, this.sourceKey);
} }
class _Suggestions extends StatefulWidget { class _Suggestions extends StatefulWidget {
@@ -400,14 +402,16 @@ class _SuggestionsState extends State<_Suggestions> {
controller.text = controller.text =
controller.text.replaceLast(words[words.length - 1], ""); controller.text.replaceLast(words[words.length - 1], "");
} }
if (text.contains(' ')) { final source = ComicSource.find(widget.controller.sourceKey);
text = "'$text'"; String insert;
} if (source?.onTagSuggestionSelected != null) {
if (type != null) { insert = source!.onTagSuggestionSelected!(type?.name ?? '', text);
controller.text += "${type.name}:$text ";
} else { } else {
controller.text += "$text "; var t = text;
if (t.contains(' ')) t = "'$t'";
insert = type != null ? "${type.name}:$t" : t;
} }
controller.text += "$insert ";
widget.controller.suggestions.clear(); widget.controller.suggestions.clear();
widget.controller.remove(); widget.controller.remove();
} }