From d5d72911ed68541175ccdfde575cb62aeb459902 Mon Sep 17 00:00:00 2001 From: Gandum2077 Date: Tue, 24 Jun 2025 19:47:14 +0800 Subject: [PATCH] Add custom tag suggestion handler (#424) --- doc/comic_source.md | 5 +++++ lib/foundation/comic_source/comic_source.dart | 4 ++++ lib/foundation/comic_source/parser.dart | 14 +++++++++++++ lib/foundation/comic_source/types.dart | 5 +++++ lib/pages/search_page.dart | 11 +++++++--- lib/pages/search_result_page.dart | 20 +++++++++++-------- 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/doc/comic_source.md b/doc/comic_source.md index 4ddecd1..3b136bd 100644 --- a/doc/comic_source.md +++ b/doc/comic_source.md @@ -363,6 +363,11 @@ This part is used to load comics of a category. // enable tags suggestions enableTagsSuggestions: false, + // [Optional] handle tag suggestion click + onTagSuggestionSelected: (namespace, tag) => { + // return the text to insert into search box + return `${namespace}:${tag}` + }, } ``` diff --git a/lib/foundation/comic_source/comic_source.dart b/lib/foundation/comic_source/comic_source.dart index 3e4b524..d0a3080 100644 --- a/lib/foundation/comic_source/comic_source.dart +++ b/lib/foundation/comic_source/comic_source.dart @@ -184,6 +184,9 @@ class ComicSource { final HandleClickTagEvent? handleClickTagEvent; + /// Callback when a tag suggestion is selected in search. + final TagSuggestionSelectFunc? onTagSuggestionSelected; + final LinkHandler? linkHandler; final bool enableTagsSuggestions; @@ -259,6 +262,7 @@ class ComicSource { this.idMatcher, this.translations, this.handleClickTagEvent, + this.onTagSuggestionSelected, this.linkHandler, this.enableTagsSuggestions, this.enableTagsTranslate, diff --git a/lib/foundation/comic_source/parser.dart b/lib/foundation/comic_source/parser.dart index 0d92978..219fdd7 100644 --- a/lib/foundation/comic_source/parser.dart +++ b/lib/foundation/comic_source/parser.dart @@ -148,6 +148,7 @@ class ComicSourceParser { _parseIdMatch(), _parseTranslation(), _parseClickTagEvent(), + _parseTagSuggestionSelectFunc(), _parseLinkHandler(), _getValue("search.enableTagsSuggestions") ?? 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() { if (!_checkExists("comic.link")) { return null; diff --git a/lib/foundation/comic_source/types.dart b/lib/foundation/comic_source/types.dart index d75c3eb..c38f26a 100644 --- a/lib/foundation/comic_source/types.dart +++ b/lib/foundation/comic_source/types.dart @@ -44,5 +44,10 @@ typedef VoteCommentFunc = Future> Function( typedef HandleClickTagEvent = PageJumpTarget? Function( 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. typedef StarRatingFunc = Future> Function(String comicId, int rating); \ No newline at end of file diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 6190930..934446c 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -376,11 +376,16 @@ class _SearchPageState extends State { controller.text = controller.text.replaceLast(words[words.length - 1], ""); } - if (type != null) { - controller.text += "${type.name}:$text "; + final source = ComicSource.find(searchTarget); + String insert; + if (source?.onTagSuggestionSelected != null) { + insert = source!.onTagSuggestionSelected!(type?.name ?? '', text); } else { - controller.text += "$text "; + var t = text; + if (t.contains(' ')) t = "'$t'"; + insert = type != null ? "${type.name}:$t" : t; } + controller.text += "$insert "; suggestions.clear(); update(); focusNode.requestFocus(); diff --git a/lib/pages/search_result_page.dart b/lib/pages/search_result_page.dart index c0abf46..fab1c4e 100644 --- a/lib/pages/search_result_page.dart +++ b/lib/pages/search_result_page.dart @@ -124,7 +124,7 @@ class _SearchResultPageState extends State { options = widget.options ?? const []; validateOptions(); appdata.addSearchHistory(text); - suggestionsController = _SuggestionsController(controller); + suggestionsController = _SuggestionsController(controller, sourceKey); super.initState(); } @@ -213,6 +213,8 @@ class _SuggestionsController { final SearchBarController controller; + final String sourceKey; + OverlayEntry? entry; void updateWidget() { @@ -270,7 +272,7 @@ class _SuggestionsController { find(TagsTranslation.cosplayerTags, TranslationType.cosplayer); } - _SuggestionsController(this.controller); + _SuggestionsController(this.controller, this.sourceKey); } class _Suggestions extends StatefulWidget { @@ -400,14 +402,16 @@ class _SuggestionsState extends State<_Suggestions> { controller.text = controller.text.replaceLast(words[words.length - 1], ""); } - if (text.contains(' ')) { - text = "'$text'"; - } - if (type != null) { - controller.text += "${type.name}:$text "; + final source = ComicSource.find(widget.controller.sourceKey); + String insert; + if (source?.onTagSuggestionSelected != null) { + insert = source!.onTagSuggestionSelected!(type?.name ?? '', text); } 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.remove(); }