diff --git a/lib/components/appbar.dart b/lib/components/appbar.dart index c1dfebf..c0112e2 100644 --- a/lib/components/appbar.dart +++ b/lib/components/appbar.dart @@ -269,12 +269,19 @@ class _MySliverAppBarDelegate extends SliverPersistentHeaderDelegate { } class AppTabBar extends StatefulWidget { - const AppTabBar({super.key, this.controller, required this.tabs}); + const AppTabBar({ + super.key, + this.controller, + required this.tabs, + this.actionButton, + }); final TabController? controller; final List tabs; + final Widget? actionButton; + @override State createState() => _AppTabBarState(); } @@ -378,25 +385,27 @@ class _AppTabBarState extends State { painter: painter, child: _TabRow( callback: _tabLayoutCallback, - children: List.generate(widget.tabs.length, buildTab), + children: List.generate(widget.tabs.length, buildTab) + ..addIfNotNull(widget.actionButton?.padding(tabPadding)), ), ).paddingHorizontal(4), ); }, ); return Container( - key: tabBarKey, - height: _kTabHeight, - width: double.infinity, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: context.colorScheme.outlineVariant, - width: 0.6, - ), + key: tabBarKey, + height: _kTabHeight, + width: double.infinity, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: context.colorScheme.outlineVariant, + width: 0.6, ), ), - child: widget.tabs.isEmpty ? const SizedBox() : child); + ), + child: widget.tabs.isEmpty ? const SizedBox() : child, + ); } int? previousIndex; @@ -633,7 +642,6 @@ class _TabViewBodyState extends State { } } - class SearchBarController { _SearchBarMixin? _state; @@ -906,3 +914,42 @@ class _SearchBarState extends State with _SearchBarMixin { ); } } + +class TabActionButton extends StatelessWidget { + const TabActionButton({ + super.key, + required this.icon, + required this.text, + required this.onPressed, + }); + + final Icon icon; + + final String text; + + final void Function() onPressed; + + static const _kTabHeight = 46.0; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onPressed, + borderRadius: BorderRadius.circular(8), + child: Container( + height: _kTabHeight, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: IconTheme( + data: IconThemeData(size: 20, color: context.colorScheme.primary), + child: Row( + children: [ + icon, + const SizedBox(width: 8), + Text(text, style: ts.withColor(context.colorScheme.primary)), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/categories_page.dart b/lib/pages/categories_page.dart index 2b2c039..c47192f 100644 --- a/lib/pages/categories_page.dart +++ b/lib/pages/categories_page.dart @@ -3,80 +3,119 @@ 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/foundation/state_controller.dart'; import 'package:venera/pages/ranking_page.dart'; import 'package:venera/pages/search_result_page.dart'; +import 'package:venera/pages/settings/settings_page.dart'; +import 'package:venera/utils/ext.dart'; import 'package:venera/utils/translations.dart'; import 'category_comics_page.dart'; -class CategoriesPage extends StatelessWidget { +class CategoriesPage extends StatefulWidget { const CategoriesPage({super.key}); + @override + State createState() => _CategoriesPageState(); +} + +class _CategoriesPageState extends State { + var categories = []; + + void onSettingsChanged() { + var categories = + List.from(appdata.settings["categories"]).whereType().toList(); + var allCategories = ComicSource.all() + .map((e) => e.categoryData?.key) + .where((element) => element != null) + .map((e) => e!) + .toList(); + categories = + categories.where((element) => allCategories.contains(element)).toList(); + if (!categories.isEqualsTo(this.categories)) { + setState(() { + this.categories = categories; + }); + } + } + + @override + void initState() { + super.initState(); + var categories = + List.from(appdata.settings["categories"]).whereType().toList(); + var allCategories = ComicSource.all() + .map((e) => e.categoryData?.key) + .where((element) => element != null) + .map((e) => e!) + .toList(); + this.categories = + categories.where((element) => allCategories.contains(element)).toList(); + appdata.settings.addListener(onSettingsChanged); + } + + void addPage() { + showPopUpWidget(App.rootContext, setCategoryPagesWidget()); + } + + @override + void dispose() { + super.dispose(); + appdata.settings.removeListener(onSettingsChanged); + } + @override Widget build(BuildContext context) { - return StateBuilder( - tag: "category", - init: SimpleController(), - builder: (controller) { - var categories = List.from(appdata.settings["categories"]); - var allCategories = ComicSource.all() - .map((e) => e.categoryData?.key) - .where((element) => element != null) - .map((e) => e!) - .toList(); - categories = categories - .where((element) => allCategories.contains(element)) - .toList(); + if (categories.isEmpty) { + var msg = "No Category Pages".tl; + msg += '\n'; + if (ComicSource.isEmpty) { + msg += "Add a comic source in home page".tl; + } else { + msg += "Please check your settings".tl; + } + return NetworkError( + message: msg, + retry: () { + setState(() {}); + }, + withAppbar: false, + ); + } - if(categories.isEmpty) { - var msg = "No Category Pages".tl; - msg += '\n'; - if(ComicSource.isEmpty) { - msg += "Add a comic source in home page".tl; - } else { - msg += "Please check your settings".tl; - } - return NetworkError( - message: msg, - retry: () { - controller.update(); - }, - withAppbar: false, - ); - } - - return Material( - child: DefaultTabController( - length: categories.length, - key: Key(categories.toString()), - child: Column( - children: [ - AppTabBar( - key: PageStorageKey(categories.toString()), - tabs: categories.map((e) { - String title = e; - try { - title = getCategoryDataWithKey(e).title; - } catch (e) { - // - } - return Tab( - text: title, - key: Key(e), - ); - }).toList(), - ).paddingTop(context.padding.top), - Expanded( - child: TabBarView( - children: - categories.map((e) => _CategoryPage(e)).toList()), - ) - ], - ), - ), - ); - }, + return Material( + child: DefaultTabController( + length: categories.length, + key: Key(categories.toString()), + child: Column( + children: [ + AppTabBar( + key: PageStorageKey(categories.toString()), + tabs: categories.map((e) { + String title = e; + try { + title = getCategoryDataWithKey(e).title; + } catch (e) { + // + } + return Tab( + text: title, + key: Key(e), + ); + }).toList(), + actionButton: TabActionButton( + icon: const Icon(Icons.add), + text: "Add".tl, + onPressed: addPage, + ), + ).paddingTop(context.padding.top), + Expanded( + child: TabBarView( + children: categories.map((e) => _CategoryPage(e)).toList(), + ), + ) + ], + ), + ), ); } } diff --git a/lib/pages/explore_page.dart b/lib/pages/explore_page.dart index 9a0d63a..6821c6f 100644 --- a/lib/pages/explore_page.dart +++ b/lib/pages/explore_page.dart @@ -6,6 +6,7 @@ import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/state_controller.dart'; import 'package:venera/pages/search_result_page.dart'; +import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/utils/ext.dart'; import 'package:venera/utils/translations.dart'; @@ -56,6 +57,10 @@ class _ExplorePageState extends State } } + void addPage() { + showPopUpWidget(App.rootContext, setExplorePagesWidget()); + } + NaviPaneState? naviPane; @override @@ -141,6 +146,11 @@ class _ExplorePageState extends State key: PageStorageKey(pages.toString()), tabs: pages.map((e) => buildTab(e)).toList(), controller: controller, + actionButton: TabActionButton( + icon: const Icon(Icons.add), + text: "Add".tl, + onPressed: addPage, + ), ), ).paddingTop(context.padding.top); diff --git a/lib/pages/settings/explore_settings.dart b/lib/pages/settings/explore_settings.dart index 459678b..2d93a9f 100644 --- a/lib/pages/settings/explore_settings.dart +++ b/lib/pages/settings/explore_settings.dart @@ -30,35 +30,11 @@ class _ExploreSettingsState extends State { ).toSliver(), _PopupWindowSetting( title: "Explore Pages".tl, - builder: () { - var pages = {}; - for (var c in ComicSource.all()) { - for (var page in c.explorePages) { - pages[page.title] = page.title; - } - } - return _MultiPagesFilter( - title: "Explore Pages".tl, - settingsIndex: "explore_pages", - pages: pages, - ); - }, + builder: setExplorePagesWidget, ).toSliver(), _PopupWindowSetting( title: "Category Pages".tl, - builder: () { - var pages = {}; - for (var c in ComicSource.all()) { - if (c.categoryData != null) { - pages[c.categoryData!.key] = c.categoryData!.title; - } - } - return _MultiPagesFilter( - title: "Category Pages".tl, - settingsIndex: "categories", - pages: pages, - ); - }, + builder: setCategoryPagesWidget, ).toSliver(), _PopupWindowSetting( title: "Network Favorite Pages".tl, @@ -205,3 +181,31 @@ class _ManageBlockingWordViewState extends State<_ManageBlockingWordView> { ); } } + +Widget setExplorePagesWidget() { + var pages = {}; + for (var c in ComicSource.all()) { + for (var page in c.explorePages) { + pages[page.title] = page.title; + } + } + return _MultiPagesFilter( + title: "Explore Pages".tl, + settingsIndex: "explore_pages", + pages: pages, + ); +} + +Widget setCategoryPagesWidget() { + var pages = {}; + for (var c in ComicSource.all()) { + if (c.categoryData != null) { + pages[c.categoryData!.key] = c.categoryData!.title; + } + } + return _MultiPagesFilter( + title: "Category Pages".tl, + settingsIndex: "categories", + pages: pages, + ); +} \ No newline at end of file