diff --git a/lib/pages/fav/index.dart b/lib/pages/fav/index.dart deleted file mode 100644 index 84d36325..00000000 --- a/lib/pages/fav/index.dart +++ /dev/null @@ -1,4 +0,0 @@ -library fav; - -export './controller.dart'; -export './view.dart'; diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/video/controller.dart similarity index 100% rename from lib/pages/fav/controller.dart rename to lib/pages/fav/video/controller.dart diff --git a/lib/pages/fav/video/index.dart b/lib/pages/fav/video/index.dart new file mode 100644 index 00000000..b77e006a --- /dev/null +++ b/lib/pages/fav/video/index.dart @@ -0,0 +1,4 @@ +library fav; + +export 'controller.dart'; +export 'view.dart'; diff --git a/lib/pages/fav/video/view.dart b/lib/pages/fav/video/view.dart new file mode 100644 index 00000000..3869cf72 --- /dev/null +++ b/lib/pages/fav/video/view.dart @@ -0,0 +1,118 @@ +import 'package:PiliPlus/common/skeleton/video_card_h.dart'; +import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:PiliPlus/common/widgets/http_error.dart'; +import 'package:PiliPlus/pages/fav/video/index.dart'; +import 'package:PiliPlus/pages/fav/video/widgets/item.dart'; + +import '../../../common/constants.dart'; +import '../../../utils/grid.dart'; + +class FavVideoPage extends StatefulWidget { + const FavVideoPage({super.key}); + + @override + State createState() => _FavVideoPageState(); +} + +class _FavVideoPageState extends State { + final FavController _favController = Get.find(); + + @override + Widget build(BuildContext context) { + return refreshIndicator( + onRefresh: () async { + await _favController.onRefresh(); + }, + child: CustomScrollView( + controller: _favController.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + Obx( + () => _buildBody(_favController.loadingState.value), + ), + ], + ), + ); + } + + Widget _buildBody(LoadingState loadingState) { + return switch (loadingState) { + Loading() => SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + mainAxisSpacing: 2, + maxCrossAxisExtent: Grid.mediumCardWidth * 2, + childAspectRatio: StyleString.aspectRatio * 2.2, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return const VideoCardHSkeleton(); + }, + childCount: 10, + ), + ), + Success() => (loadingState.response as List?)?.isNotEmpty == true + ? SliverPadding( + padding: EdgeInsets.only( + top: StyleString.safeSpace - 2, + bottom: 80 + MediaQuery.paddingOf(context).bottom, + ), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + mainAxisSpacing: 2, + maxCrossAxisExtent: Grid.mediumCardWidth * 2, + childAspectRatio: StyleString.aspectRatio * 2.2, + ), + delegate: SliverChildBuilderDelegate( + childCount: loadingState.response.length, + (BuildContext context, int index) { + if (index == loadingState.response.length - 1) { + _favController.onLoadMore(); + } + String heroTag = + Utils.makeHeroTag(loadingState.response[index].fid); + return FavItem( + heroTag: heroTag, + favFolderItem: loadingState.response[index], + onTap: () async { + dynamic res = await Get.toNamed( + '/favDetail', + arguments: loadingState.response[index], + parameters: { + 'heroTag': heroTag, + 'mediaId': + loadingState.response[index].id.toString(), + }, + ); + if (res == true) { + List list = + (_favController.loadingState.value as Success) + .response; + list.removeAt(index); + _favController.loadingState.value = + LoadingState.success(list); + } else { + Future.delayed(const Duration(milliseconds: 255), () { + _favController.onRefresh(); + }); + } + }, + ); + }, + ), + ), + ) + : HttpError( + callback: _favController.onReload, + ), + Error() => HttpError( + errMsg: loadingState.errMsg, + callback: _favController.onReload, + ), + LoadingState() => throw UnimplementedError(), + }; + } +} diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/video/widgets/item.dart similarity index 100% rename from lib/pages/fav/widgets/item.dart rename to lib/pages/fav/video/widgets/item.dart diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index a9fe03de..939ec4d7 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -1,16 +1,14 @@ -import 'package:PiliPlus/common/skeleton/video_card_h.dart'; -import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/pages/fav/video/index.dart'; +import 'package:PiliPlus/pages/fav_search/view.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:PiliPlus/common/widgets/http_error.dart'; -import 'package:PiliPlus/pages/fav/index.dart'; -import 'package:PiliPlus/pages/fav/widgets/item.dart'; -import '../../common/constants.dart'; -import '../../utils/grid.dart'; +enum _FavType { video, bangumi, cinema, article, note } + +extension _FavTypeExt on _FavType { + String get title => ['视频', '追番', '追剧', '专栏', '笔记'][index]; +} class FavPage extends StatefulWidget { const FavPage({super.key}); @@ -19,9 +17,19 @@ class FavPage extends StatefulWidget { State createState() => _FavPageState(); } -class _FavPageState extends State { +class _FavPageState extends State with SingleTickerProviderStateMixin { + late final TabController _tabController = TabController( + length: _FavType.values.length, + vsync: this, + ); final FavController _favController = Get.put(FavController()); + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -30,16 +38,19 @@ class _FavPageState extends State { actions: [ IconButton( onPressed: () { - Get.toNamed('/createFav')?.then((data) { - if (data != null) { - List list = _favController.loadingState.value is Success - ? (_favController.loadingState.value as Success).response - : []; - list.insert(list.isNotEmpty ? 1 : 0, data); - _favController.loadingState.value = - LoadingState.success(list); - } - }); + Get.toNamed('/createFav')?.then( + (data) { + if (data != null) { + List list = _favController.loadingState.value is Success + ? (_favController.loadingState.value as Success) + .response + : []; + list.insert(list.isNotEmpty ? 1 : 0, data); + _favController.loadingState.value = + LoadingState.success(list); + } + }, + ); }, icon: const Icon(Icons.add), tooltip: '新建收藏夹', @@ -66,96 +77,29 @@ class _FavPageState extends State { ), const SizedBox(width: 6), ], - ), - body: refreshIndicator( - onRefresh: () async { - await _favController.onRefresh(); - }, - child: CustomScrollView( - controller: _favController.scrollController, - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - Obx( - () => _buildBody(_favController.loadingState.value), - ), - ], + bottom: TabBar( + controller: _tabController, + tabs: _FavType.values + .map( + (item) => Tab(text: item.title), + ) + .toList(), ), ), + body: TabBarView( + controller: _tabController, + children: _FavType.values + .map( + (item) => switch (item) { + _FavType.video => const FavVideoPage(), + _FavType.bangumi => Center(child: Text(item.title)), + _FavType.cinema => Center(child: Text(item.title)), + _FavType.article => Center(child: Text(item.title)), + _FavType.note => Center(child: Text(item.title)), + }, + ) + .toList(), + ), ); } - - Widget _buildBody(LoadingState loadingState) { - return switch (loadingState) { - Loading() => SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - mainAxisSpacing: 2, - maxCrossAxisExtent: Grid.mediumCardWidth * 2, - childAspectRatio: StyleString.aspectRatio * 2.2, - ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return const VideoCardHSkeleton(); - }, - childCount: 10, - ), - ), - Success() => (loadingState.response as List?)?.isNotEmpty == true - ? SliverPadding( - padding: EdgeInsets.only( - bottom: 80 + MediaQuery.paddingOf(context).bottom), - sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - mainAxisSpacing: 2, - maxCrossAxisExtent: Grid.mediumCardWidth * 2, - childAspectRatio: StyleString.aspectRatio * 2.2, - ), - delegate: SliverChildBuilderDelegate( - childCount: loadingState.response.length, - (BuildContext context, int index) { - if (index == loadingState.response.length - 1) { - _favController.onLoadMore(); - } - String heroTag = - Utils.makeHeroTag(loadingState.response[index].fid); - return FavItem( - heroTag: heroTag, - favFolderItem: loadingState.response[index], - onTap: () async { - dynamic res = await Get.toNamed( - '/favDetail', - arguments: loadingState.response[index], - parameters: { - 'heroTag': heroTag, - 'mediaId': - loadingState.response[index].id.toString(), - }, - ); - if (res == true) { - List list = - (_favController.loadingState.value as Success) - .response; - list.removeAt(index); - _favController.loadingState.value = - LoadingState.success(list); - } else { - Future.delayed(const Duration(milliseconds: 255), () { - _favController.onRefresh(); - }); - } - }, - ); - }, - ), - ), - ) - : HttpError( - callback: _favController.onReload, - ), - Error() => HttpError( - errMsg: loadingState.errMsg, - callback: _favController.onReload, - ), - LoadingState() => throw UnimplementedError(), - }; - } } diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 6b94c20b..f7b68e75 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/pages/fav/view.dart'; import 'package:PiliPlus/pages/member/new/member_page.dart'; import 'package:PiliPlus/pages/member/new/widget/edit_profile_page.dart'; import 'package:PiliPlus/pages/setting/navigation_bar_set.dart'; @@ -20,7 +21,6 @@ import '../pages/danmaku_block/index.dart'; import '../pages/dynamics/detail/index.dart'; import '../pages/dynamics/index.dart'; import '../pages/fan/index.dart'; -import '../pages/fav/index.dart'; import '../pages/fav_detail/index.dart'; import '../pages/fav_search/index.dart'; import '../pages/follow/index.dart';