diff --git a/assets/translation.json b/assets/translation.json index a279368..73d62cc 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -323,7 +323,8 @@ "Cloudflare verification required": "需要Cloudflare验证", "Success": "成功", "Compressing": "压缩中", - "Exporting": "导出中" + "Exporting": "导出中", + "Search Sources": "搜索源" }, "zh_TW": { "Home": "首頁", @@ -649,6 +650,7 @@ "Cloudflare verification required": "需要Cloudflare驗證", "Success": "成功", "Compressing": "壓縮中", - "Exporting": "匯出中" + "Exporting": "匯出中", + "Search Sources": "搜索源" } } \ No newline at end of file diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index 1e083df..65ec128 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -126,6 +126,7 @@ class _Settings with ChangeNotifier { 'explore_pages': [], 'categories': [], 'favorites': [], + 'searchSources': null, 'showFavoriteStatusOnTile': true, 'showHistoryStatusOnTile': false, 'blockedWords': [], diff --git a/lib/init.dart b/lib/init.dart index f3f4c0d..11c4bc3 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -42,11 +42,16 @@ Future init() async { await ComicSource.init().wait(); await LocalManager().init().wait(); CacheManager().setLimitSize(appdata.settings['cacheSize']); + if (appdata.settings['searchSources'] == null) { + appdata.settings['searchSources'] = ComicSource.all() + .where((e) => e.searchPageData != null) + .map((e) => e.key) + .toList(); + } if (App.isAndroid) { handleLinks(); } FlutterError.onError = (details) { - Log.error( - "Unhandled Exception", "${details.exception}\n${details.stack}"); + Log.error("Unhandled Exception", "${details.exception}\n${details.stack}"); }; -} \ No newline at end of file +} diff --git a/lib/pages/aggregated_search_page.dart b/lib/pages/aggregated_search_page.dart index 06093a4..48fc849 100644 --- a/lib/pages/aggregated_search_page.dart +++ b/lib/pages/aggregated_search_page.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import 'package:shimmer_animation/shimmer_animation.dart'; import "package:venera/components/components.dart"; import "package:venera/foundation/app.dart"; +import "package:venera/foundation/appdata.dart"; import "package:venera/foundation/comic_source/comic_source.dart"; import "package:venera/pages/search_result_page.dart"; import "package:venera/utils/translations.dart"; @@ -24,7 +25,18 @@ class _AggregatedSearchPageState extends State { @override void initState() { - sources = ComicSource.all().where((e) => e.searchPageData != null).toList(); + var all = ComicSource.all() + .where((e) => e.searchPageData != null) + .map((e) => e.key) + .toList(); + var settings = appdata.settings['searchSources'] as List; + var sources = []; + for (var source in settings) { + if (all.contains(source)) { + sources.add(source); + } + } + this.sources = sources.map((e) => ComicSource.find(e)!).toList(); _keyword = widget.keyword; controller = SearchBarController( currentText: widget.keyword, diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index f3d6cbe..6258e0d 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -10,12 +10,14 @@ import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/state_controller.dart'; import 'package:venera/pages/aggregated_search_page.dart'; import 'package:venera/pages/search_result_page.dart'; +import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/utils/app_links.dart'; import 'package:venera/utils/ext.dart'; import 'package:venera/utils/tags_translation.dart'; import 'package:venera/utils/translations.dart'; import 'comic_page.dart'; +import 'comic_source_page.dart'; class SearchPage extends StatefulWidget { const SearchPage({super.key}); @@ -27,8 +29,13 @@ class SearchPage extends StatefulWidget { class _SearchPageState extends State { late final SearchBarController controller; + late List searchSources; + String searchTarget = ""; + SearchPageData get currentSearchPageData => + ComicSource.find(searchTarget)!.searchPageData!; + bool aggregatedSearch = false; var focusNode = FocusNode(); @@ -139,31 +146,85 @@ class _SearchPageState extends State { @override void initState() { + findSearchSources(); var defaultSearchTarget = appdata.settings['defaultSearchTarget']; if (defaultSearchTarget == "_aggregated_") { aggregatedSearch = true; - searchTarget = ComicSource.all().where((e) => e.searchPageData != null) - .toList().first.key; } else if (defaultSearchTarget != null && - ComicSource.find(defaultSearchTarget) != null) { + searchSources.contains(defaultSearchTarget)) { searchTarget = defaultSearchTarget; - } else { - searchTarget = ComicSource.all().first.key; } controller = SearchBarController( onSearch: search, ); + appdata.settings.addListener(updateSearchSourcesIfNeeded); super.initState(); } @override void dispose() { focusNode.dispose(); + appdata.settings.removeListener(updateSearchSourcesIfNeeded); super.dispose(); } + void findSearchSources() { + var all = ComicSource.all() + .where((e) => e.searchPageData != null) + .map((e) => e.key) + .toList(); + var settings = appdata.settings['searchSources'] as List; + var sources = []; + for (var source in settings) { + if (all.contains(source)) { + sources.add(source); + } + } + searchSources = sources; + if (!searchSources.contains(searchTarget)) { + searchTarget = searchSources.firstOrNull ?? ""; + } + } + + void updateSearchSourcesIfNeeded() { + var old = searchSources; + findSearchSources(); + if (old.isEqualsTo(searchSources)) { + return; + } + setState(() {}); + } + + void manageSearchSources() { + showPopUpWidget(App.rootContext, setSearchSourcesWidget()); + } + + Widget buildEmpty() { + var msg = "No Search Sources".tl; + msg += '\n'; + VoidCallback onTap; + if (ComicSource.isEmpty) { + msg += "Please add some sources".tl; + onTap = () { + context.to(() => ComicSourcePage()); + }; + } else { + msg += "Please check your settings".tl; + onTap = manageSearchSources; + } + return NetworkError( + message: msg, + retry: onTap, + withAppbar: true, + buttonText: "Manage".tl, + ); + } + @override Widget build(BuildContext context) { + if (searchSources.isEmpty) { + return buildEmpty(); + } return Scaffold( body: SmoothCustomScrollView( slivers: buildSlivers().toList(), @@ -192,8 +253,7 @@ class _SearchPageState extends State { } Widget buildSearchTarget() { - var sources = - ComicSource.all().where((e) => e.searchPageData != null).toList(); + var sources = searchSources.map((e) => ComicSource.find(e)!).toList(); return SliverToBoxAdapter( child: Container( width: double.infinity, @@ -205,6 +265,10 @@ class _SearchPageState extends State { contentPadding: EdgeInsets.zero, leading: const Icon(Icons.search), title: Text("Search in".tl), + trailing: IconButton( + icon: const Icon(Icons.settings), + onPressed: manageSearchSources, + ), ), Wrap( spacing: 8, @@ -231,11 +295,6 @@ class _SearchPageState extends State { onChanged: (value) { setState(() { aggregatedSearch = value ?? false; - if (!aggregatedSearch && - appdata.settings['defaultSearchTarget'] == - "_aggregated_") { - searchTarget = sources.first.key; - } }); }, ), @@ -247,9 +306,7 @@ class _SearchPageState extends State { } void useDefaultOptions() { - final searchOptions = - ComicSource.find(searchTarget)!.searchPageData!.searchOptions ?? - []; + final searchOptions = currentSearchPageData.searchOptions ?? []; options = searchOptions.map((e) => e.defaultValue).toList(); } @@ -260,9 +317,7 @@ class _SearchPageState extends State { var children = []; - final searchOptions = - ComicSource.find(searchTarget)!.searchPageData!.searchOptions ?? - []; + final searchOptions = currentSearchPageData.searchOptions ?? []; if (searchOptions.length != options.length) { useDefaultOptions(); } @@ -396,7 +451,9 @@ class _SearchPageState extends State { Text( subTitle, style: TextStyle( - fontSize: 14, color: Theme.of(context).colorScheme.outline), + fontSize: 14, + color: Theme.of(context).colorScheme.outline, + ), ) ], ), diff --git a/lib/pages/settings/explore_settings.dart b/lib/pages/settings/explore_settings.dart index d945d08..957182e 100644 --- a/lib/pages/settings/explore_settings.dart +++ b/lib/pages/settings/explore_settings.dart @@ -40,6 +40,10 @@ class _ExploreSettingsState extends State { title: "Network Favorite Pages".tl, builder: setFavoritesPagesWidget, ).toSliver(), + _PopupWindowSetting( + title: "Search Sources".tl, + builder: setSearchSourcesWidget, + ).toSliver(), _SwitchSetting( title: "Show favorite status on comic tile".tl, settingKey: "showFavoriteStatusOnTile", @@ -210,4 +214,18 @@ Widget setFavoritesPagesWidget() { settingsIndex: "favorites", pages: pages, ); +} + +Widget setSearchSourcesWidget() { + var pages = {}; + for (var c in ComicSource.all()) { + if (c.searchPageData != null) { + pages[c.key] = c.name; + } + } + return _MultiPagesFilter( + title: "Search Sources".tl, + settingsIndex: "searchSources", + pages: pages, + ); } \ No newline at end of file