diff --git a/lib/http/member.dart b/lib/http/member.dart index 6f07321bc..2b57967e4 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -671,7 +671,7 @@ class MemberHttp { } // 搜索follow - static Future getfollowSearch({ + static Future> getfollowSearch({ required int mid, required int ps, required int pn, diff --git a/lib/http/user.dart b/lib/http/user.dart index 0a66ff18e..71c067d8c 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -443,7 +443,7 @@ class UserHttp { // } // 搜索历史记录 - static Future searchHistory( + static Future> searchHistory( {required int pn, required String keyword}) async { var res = await Request().get( Api.searchHistory, diff --git a/lib/pages/common/common_search_controller.dart b/lib/pages/common/common_search_controller.dart new file mode 100644 index 000000000..d5fdd0beb --- /dev/null +++ b/lib/pages/common/common_search_controller.dart @@ -0,0 +1,31 @@ +import 'package:PiliPlus/pages/common/common_list_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +abstract class CommonSearchController extends CommonListController { + final editController = TextEditingController(); + final focusNode = FocusNode(); + + void onClear() { + if (editController.text.isNotEmpty) { + editController.clear(); + } else { + Get.back(); + } + } + + @override + Future onRefresh() { + if (editController.value.text.isEmpty) { + return Future.value(); + } + return super.onRefresh(); + } + + @override + void onClose() { + editController.dispose(); + focusNode.dispose(); + super.onClose(); + } +} diff --git a/lib/pages/common/common_search_page.dart b/lib/pages/common/common_search_page.dart new file mode 100644 index 000000000..52f74dc60 --- /dev/null +++ b/lib/pages/common/common_search_page.dart @@ -0,0 +1,72 @@ +import 'package:PiliPlus/common/widgets/loading_widget.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/pages/common/common_search_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +abstract class CommonSearchPage extends StatefulWidget { + const CommonSearchPage({super.key}); +} + +abstract class CommonSearchPageState + extends State { + CommonSearchController get controller; + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + actions: [ + IconButton( + tooltip: '搜索', + onPressed: controller.onRefresh, + icon: const Icon(Icons.search_outlined, size: 22), + ), + const SizedBox(width: 10) + ], + title: TextField( + autofocus: true, + focusNode: controller.focusNode, + controller: controller.editController, + textInputAction: TextInputAction.search, + textAlignVertical: TextAlignVertical.center, + decoration: InputDecoration( + hintText: '搜索', + border: InputBorder.none, + suffixIcon: IconButton( + tooltip: '清空', + icon: const Icon(Icons.clear, size: 22), + onPressed: () { + controller + ..loadingState.value = LoadingState.loading() + ..onClear() + ..focusNode.requestFocus(); + }, + ), + ), + onSubmitted: (value) => controller.onReload(), + ), + ), + body: Obx(() => _buildBody(controller.loadingState.value)), + ); + } + + Widget _buildBody(LoadingState?> loadingState) { + return switch (loadingState) { + Loading() => errorWidget(), + Success() => loadingState.response?.isNotEmpty == true + ? buildList(loadingState.response!) + : errorWidget( + callback: controller.onReload, + ), + Error() => errorWidget( + errMsg: loadingState.errMsg, + callback: controller.onReload, + ), + _ => throw UnimplementedError(), + }; + } + + Widget buildList(List list); +} diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 77678cc90..a1c355aa5 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -6,7 +6,6 @@ import 'package:PiliPlus/pages/fav/note/view.dart'; import 'package:PiliPlus/pages/fav/pgc/view.dart'; import 'package:PiliPlus/pages/fav/video/fav_folder_sort_page.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'; @@ -117,7 +116,6 @@ class _FavPageState extends State with SingleTickerProviderStateMixin { 'mediaId': item.id, 'title': item.title, 'count': item.mediaCount, - 'searchType': SearchType.fav, 'isOwner': true, }, ); diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index f42e746f0..3fa58aa21 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -5,7 +5,6 @@ import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/models/user/fav_detail.dart'; import 'package:PiliPlus/models/user/fav_folder.dart'; import 'package:PiliPlus/pages/fav_detail/fav_sort_page.dart'; -import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; @@ -206,16 +205,11 @@ class _FavDetailPageState extends State { 'title': _favDetailController.item.value.title, 'count': _favDetailController.item.value.mediaCount, - 'searchType': SearchType.fav, 'isOwner': _favDetailController.isOwner.value, }, ), icon: const Icon(Icons.search_outlined), ), - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.more_vert), - // ), Obx( () => _favDetailController.isOwner.value ? PopupMenuButton( diff --git a/lib/pages/fav_search/controller.dart b/lib/pages/fav_search/controller.dart index 882ab4ef1..ea67bffe5 100644 --- a/lib/pages/fav_search/controller.dart +++ b/lib/pages/fav_search/controller.dart @@ -1,69 +1,43 @@ import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/http/member.dart'; -import 'package:PiliPlus/models/model_hot_video_item.dart'; -import 'package:PiliPlus/pages/common/common_list_controller.dart'; -import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType; -import 'package:flutter/material.dart'; +import 'package:PiliPlus/http/video.dart'; +import 'package:PiliPlus/models/user/fav_detail.dart'; +import 'package:PiliPlus/pages/common/common_search_controller.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPlus/http/user.dart'; -import '../../http/video.dart'; - -class FavSearchController extends CommonListController { - final controller = TextEditingController(); - final searchFocusNode = FocusNode(); - - int? type; - int? mediaId; - int? mid; - late SearchType searchType; - final bool? isOwner = Get.arguments['isOwner']; +class FavSearchController + extends CommonSearchController { + int type = Get.arguments['type']; + int mediaId = Get.arguments['mediaId']; + bool isOwner = Get.arguments['isOwner']; + dynamic count = Get.arguments['count']; + dynamic title = Get.arguments['title']; @override - void onInit() { - super.onInit(); - type = Get.arguments['type']; - mediaId = Get.arguments['mediaId']; - mid = Get.arguments['mid']; - searchType = Get.arguments['searchType']; - } - - // 清空搜索 - void onClear() { - if (controller.text.isNotEmpty) { - controller.clear(); - } else { - Get.back(); - } - } + Future> customGetData() => + UserHttp.userFavFolderDetail( + pn: currentPage, + ps: 20, + mediaId: mediaId, + keyword: editController.text, + type: type, + ); @override - Future onRefresh() { - if (controller.value.text.isEmpty) { - return Future.value(); - } - return super.onRefresh(); - } - - @override - List? getDataList(response) { - if (searchType == SearchType.later) { - return response['list']; - } + List? getDataList(FavDetailData response) { return response.list; } @override - bool customHandleResponse(bool isRefresh, Success response) { - if (searchType == SearchType.fav && response.response.hasMore == false) { + bool customHandleResponse(bool isRefresh, Success response) { + if (response.response.hasMore == false) { isEnd = true; } - return false; } - onCancelFav(int index, int id, int type) async { + onCancelFav(int index, int id, int? type) async { var result = await VideoHttp.favVideo( aid: id, addIds: '', @@ -71,69 +45,11 @@ class FavSearchController extends CommonListController { type: type, ); if (result['status']) { - List dataList = (loadingState.value as Success).response; + List dataList = + (loadingState.value as Success).response; dataList.removeAt(index); loadingState.refresh(); SmartDialog.showToast('取消收藏'); } } - - @override - Future customGetData() => switch (searchType) { - SearchType.fav => UserHttp.userFavFolderDetail( - pn: currentPage, - ps: 20, - mediaId: mediaId!, - keyword: controller.text, - type: type!, - ), - SearchType.follow => MemberHttp.getfollowSearch( - mid: mid!, - ps: 20, - pn: currentPage, - name: controller.value.text, - ), - SearchType.history => UserHttp.searchHistory( - pn: currentPage, - keyword: controller.value.text, - ), - SearchType.later => UserHttp.seeYouLater( - page: currentPage, - keyword: controller.value.text, - ), - }; - - @override - void onClose() { - searchFocusNode.dispose(); - controller.dispose(); - super.onClose(); - } - - Future onDelHistory(index, kid, business) async { - String resKid = 'archive_$kid'; - if (business == 'live') { - resKid = 'live_$kid'; - } else if (business.contains('article')) { - resKid = 'article_$kid'; - } - - var res = await UserHttp.delHistory([resKid]); - if (res['status']) { - List historyList = (loadingState.value as Success).response; - historyList.removeAt(index); - loadingState.refresh(); - SmartDialog.showToast(res['msg']); - } - } - - Future toViewDel(BuildContext context, int index, aid) async { - var res = await UserHttp.toViewDel(aids: [aid]); - if (res['status']) { - List list = (loadingState.value as Success).response; - list.removeAt(index); - loadingState.refresh(); - } - SmartDialog.showToast(res['msg']); - } } diff --git a/lib/pages/fav_search/index.dart b/lib/pages/fav_search/index.dart deleted file mode 100644 index b811585ff..000000000 --- a/lib/pages/fav_search/index.dart +++ /dev/null @@ -1,4 +0,0 @@ -library fav_search; - -export './controller.dart'; -export './view.dart'; diff --git a/lib/pages/fav_search/view.dart b/lib/pages/fav_search/view.dart index 9cd89a099..0bffb6b7b 100644 --- a/lib/pages/fav_search/view.dart +++ b/lib/pages/fav_search/view.dart @@ -1,10 +1,5 @@ -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/common/widgets/icon_button.dart'; -import 'package:PiliPlus/common/widgets/loading_widget.dart'; -import 'package:PiliPlus/common/widgets/video_card_h.dart'; -import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/pages/follow/widgets/follow_item.dart'; -import 'package:PiliPlus/pages/history/widgets/item.dart'; +import 'package:PiliPlus/models/user/fav_detail.dart'; +import 'package:PiliPlus/pages/common/common_search_page.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -14,269 +9,72 @@ import 'package:PiliPlus/pages/fav_detail/widget/fav_video_card.dart'; import 'controller.dart'; -enum SearchType { fav, follow, history, later } - -class FavSearchPage extends StatefulWidget { +class FavSearchPage extends CommonSearchPage { const FavSearchPage({super.key}); @override State createState() => _FavSearchPageState(); } -class _FavSearchPageState extends State { - final FavSearchController _favSearchCtr = Get.put( +class _FavSearchPageState extends CommonSearchPageState { + @override + final FavSearchController controller = Get.put( FavSearchController(), tag: Utils.generateRandomString(8), ); @override - Widget build(BuildContext context) { - return Scaffold( - resizeToAvoidBottomInset: false, - appBar: AppBar( - actions: [ - IconButton( - tooltip: '搜索', - onPressed: _favSearchCtr.onRefresh, - icon: const Icon(Icons.search_outlined, size: 22), + Widget buildList(List list) { + return CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: controller.scrollController, + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, ), - const SizedBox(width: 10) - ], - title: TextField( - autofocus: true, - focusNode: _favSearchCtr.searchFocusNode, - controller: _favSearchCtr.controller, - textInputAction: TextInputAction.search, - textAlignVertical: TextAlignVertical.center, - decoration: InputDecoration( - hintText: '搜索', - border: InputBorder.none, - suffixIcon: IconButton( - tooltip: '清空', - icon: const Icon(Icons.clear, size: 22), - onPressed: () { - _favSearchCtr - ..loadingState.value = LoadingState.loading() - ..onClear() - ..searchFocusNode.requestFocus(); + sliver: SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + final item = list[index]; + return FavVideoCardH( + videoItem: item, + onDelFav: controller.isOwner == true + ? () { + controller.onCancelFav( + index, + item.id!, + item.type, + ); + } + : null, + onViewFav: () { + PageUtils.toVideoPage( + 'bvid=${item.bvid}&cid=${item.cid}', + arguments: { + 'videoItem': item, + 'heroTag': Utils.makeHeroTag(item.bvid), + 'sourceType': 'fav', + 'mediaId': controller.mediaId, + 'oid': item.id, + 'favTitle': controller.title, + 'count': controller.count, + 'desc': true, + 'isContinuePlaying': true, + }, + ); + }, + ); }, ), ), - onSubmitted: (value) => _favSearchCtr.onReload(), ), - ), - body: Obx(() => _buildBody(_favSearchCtr.loadingState.value)), + ], ); } - - Widget _buildBody(LoadingState?> loadingState) { - return switch (loadingState) { - Loading() => errorWidget(), - Success() => loadingState.response?.isNotEmpty == true - ? switch (_favSearchCtr.searchType) { - SearchType.fav || - SearchType.history || - SearchType.later => - CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - controller: _favSearchCtr.scrollController, - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 80, - ), - sliver: SliverGrid( - gridDelegate: _favSearchCtr.searchType == SearchType.fav - ? Grid.videoCardHDelegate(context, minHeight: 110) - : Grid.videoCardHDelegate(context), - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == loadingState.response!.length - 1) { - _favSearchCtr.onLoadMore(); - } - final item = loadingState.response![index]; - if (_favSearchCtr.searchType == SearchType.fav) { - return FavVideoCardH( - videoItem: item, - onDelFav: _favSearchCtr.isOwner == true - ? () { - _favSearchCtr.onCancelFav( - index, - item.id!, - item.type, - ); - } - : null, - onViewFav: () { - PageUtils.toVideoPage( - 'bvid=${item.bvid}&cid=${item.cid}', - arguments: { - 'videoItem': item, - 'heroTag': Utils.makeHeroTag(item.bvid), - 'sourceType': 'fav', - 'mediaId': Get.arguments['mediaId'], - 'oid': item.id, - 'favTitle': Get.arguments['title'], - 'count': Get.arguments['count'], - 'desc': true, - 'isContinuePlaying': true, - }, - ); - }, - ); - } - - if (_favSearchCtr.searchType == - SearchType.history) { - return HistoryItem( - videoItem: item, - ctr: _favSearchCtr, - onChoose: null, - onDelete: (kid, business) { - _favSearchCtr.onDelHistory( - index, kid, business); - }, - ); - } - - return Stack( - children: [ - VideoCardH( - videoItem: item, - source: 'later', - onViewLater: (cid) { - PageUtils.toVideoPage( - 'bvid=${item.bvid}&cid=$cid', - arguments: { - 'videoItem': item, - 'oid': item.aid, - 'heroTag': Utils.makeHeroTag(item.bvid), - 'sourceType': 'watchLater', - 'count': Get.arguments['count'], - 'favTitle': '稍后再看', - 'mediaId': _favSearchCtr.mid, - 'desc': false, - 'isContinuePlaying': index != 0, - }, - ); - }, - ), - Positioned( - top: 5, - left: 12, - bottom: 5, - child: IgnorePointer( - child: LayoutBuilder( - builder: (context, constraints) => - AnimatedOpacity( - opacity: item.checked == true ? 1 : 0, - duration: - const Duration(milliseconds: 200), - child: Container( - alignment: Alignment.center, - height: constraints.maxHeight, - width: constraints.maxHeight * - StyleString.aspectRatio, - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(10), - color: - Colors.black.withOpacity(0.6), - ), - child: SizedBox( - width: 34, - height: 34, - child: AnimatedScale( - scale: - item.checked == true ? 1 : 0, - duration: const Duration( - milliseconds: 250), - curve: Curves.easeInOut, - child: IconButton( - tooltip: '取消选择', - style: ButtonStyle( - padding: - WidgetStateProperty.all( - EdgeInsets.zero), - backgroundColor: - WidgetStateProperty - .resolveWith( - (states) { - return Theme.of(context) - .colorScheme - .surface - .withOpacity(0.8); - }, - ), - ), - onPressed: null, - icon: Icon( - Icons.done_all_outlined, - color: Theme.of(context) - .colorScheme - .primary, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Positioned( - right: 12, - bottom: 0, - child: iconButton( - tooltip: '移除', - context: context, - onPressed: () { - _favSearchCtr.toViewDel( - context, - index, - item.aid, - ); - }, - icon: Icons.clear, - iconColor: Theme.of(context) - .colorScheme - .onSurfaceVariant, - bgColor: Colors.transparent, - ), - ), - ], - ); - }, - childCount: loadingState.response!.length, - ), - ), - ), - ], - ), - SearchType.follow => ListView.builder( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 80, - ), - controller: _favSearchCtr.scrollController, - itemCount: loadingState.response!.length, - itemBuilder: ((context, index) { - if (index == loadingState.response!.length - 1) { - _favSearchCtr.onLoadMore(); - } - return FollowItem( - item: loadingState.response![index], - ); - }), - ), - } - : errorWidget( - callback: _favSearchCtr.onReload, - ), - Error() => errorWidget( - errMsg: loadingState.errMsg, - callback: _favSearchCtr.onReload, - ), - LoadingState() => throw UnimplementedError(), - }; - } } diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index c3ebcf1b1..0883917b8 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -38,10 +37,9 @@ class _FollowPageState extends State { actions: [ IconButton( onPressed: () => Get.toNamed( - '/favSearch', + '/followSearch', arguments: { 'mid': int.parse(mid), - 'searchType': SearchType.follow, }, ), icon: const Icon(Icons.search_outlined), diff --git a/lib/pages/follow_search/controller.dart b/lib/pages/follow_search/controller.dart new file mode 100644 index 000000000..5ea0cf8c4 --- /dev/null +++ b/lib/pages/follow_search/controller.dart @@ -0,0 +1,24 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/member.dart'; +import 'package:PiliPlus/models/follow/result.dart'; +import 'package:PiliPlus/pages/common/common_search_controller.dart'; +import 'package:get/get.dart'; + +class FollowSearchController + extends CommonSearchController { + dynamic mid = Get.arguments['mid']; + + @override + Future> customGetData() => + MemberHttp.getfollowSearch( + mid: mid, + ps: 20, + pn: currentPage, + name: editController.value.text, + ); + + @override + List? getDataList(FollowDataModel response) { + return response.list; + } +} diff --git a/lib/pages/follow_search/view.dart b/lib/pages/follow_search/view.dart new file mode 100644 index 000000000..5626b67f3 --- /dev/null +++ b/lib/pages/follow_search/view.dart @@ -0,0 +1,41 @@ +import 'package:PiliPlus/models/follow/result.dart'; +import 'package:PiliPlus/pages/common/common_search_page.dart'; +import 'package:PiliPlus/pages/follow/widgets/follow_item.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class FollowSearchPage extends CommonSearchPage { + const FollowSearchPage({super.key}); + + @override + State createState() => _FollowSearchPageState(); +} + +class _FollowSearchPageState extends CommonSearchPageState { + @override + final FollowSearchController controller = Get.put( + FollowSearchController(), + tag: Utils.generateRandomString(8), + ); + + @override + Widget buildList(List list) { + return ListView.builder( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + controller: controller.scrollController, + itemCount: list.length, + itemBuilder: ((context, index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + return FollowItem(item: list[index]); + }), + ); + } +} diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index 7f5c2ed0d..a406d5e67 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -3,7 +3,6 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/user/history.dart'; -import 'package:PiliPlus/pages/fav_search/view.dart' show SearchType; import 'package:PiliPlus/pages/history/base_controller.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:flutter/material.dart'; @@ -77,12 +76,7 @@ class _HistoryPageState extends State actions: [ IconButton( tooltip: '搜索', - onPressed: () => Get.toNamed( - '/favSearch', - arguments: { - 'searchType': SearchType.history, - }, - ), + onPressed: () => Get.toNamed('/historySearch'), icon: const Icon(Icons.search_outlined), ), PopupMenuButton( diff --git a/lib/pages/history_search/controller.dart b/lib/pages/history_search/controller.dart new file mode 100644 index 000000000..776df39a7 --- /dev/null +++ b/lib/pages/history_search/controller.dart @@ -0,0 +1,36 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/user/history.dart'; +import 'package:PiliPlus/pages/common/common_search_controller.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:PiliPlus/http/user.dart'; + +class HistorySearchController + extends CommonSearchController { + @override + Future> customGetData() => UserHttp.searchHistory( + pn: currentPage, + keyword: editController.value.text, + ); + + @override + List? getDataList(HistoryData response) { + return response.list; + } + + Future onDelHistory(index, kid, business) async { + String resKid = 'archive_$kid'; + if (business == 'live') { + resKid = 'live_$kid'; + } else if (business.contains('article')) { + resKid = 'article_$kid'; + } + + var res = await UserHttp.delHistory([resKid]); + if (res['status']) { + List historyList = (loadingState.value as Success).response; + historyList.removeAt(index); + loadingState.refresh(); + SmartDialog.showToast(res['msg']); + } + } +} diff --git a/lib/pages/history_search/view.dart b/lib/pages/history_search/view.dart new file mode 100644 index 000000000..9d021a6c5 --- /dev/null +++ b/lib/pages/history_search/view.dart @@ -0,0 +1,59 @@ +import 'package:PiliPlus/models/user/history.dart'; +import 'package:PiliPlus/pages/common/common_search_page.dart'; +import 'package:PiliPlus/pages/history/widgets/item.dart'; +import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class HistorySearchPage extends CommonSearchPage { + const HistorySearchPage({super.key}); + + @override + State createState() => _HistorySearchPageState(); +} + +class _HistorySearchPageState + extends CommonSearchPageState { + @override + final HistorySearchController controller = Get.put( + HistorySearchController(), + tag: Utils.generateRandomString(8), + ); + + @override + Widget buildList(List list) { + return CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: controller.scrollController, + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + sliver: SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + final item = list[index]; + return HistoryItem( + videoItem: item, + ctr: controller, + onChoose: null, + onDelete: (kid, business) { + controller.onDelHistory(index, kid, business); + }, + ); + }, + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 9dfecd6d2..07d69a69c 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -1,7 +1,6 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; -import 'package:PiliPlus/pages/fav_search/view.dart'; import 'package:PiliPlus/pages/history/view.dart' show AppBarWidget; import 'package:PiliPlus/pages/later/base_controller.dart'; import 'package:PiliPlus/pages/later/child_view.dart'; @@ -138,14 +137,13 @@ class _LaterPageState extends State onPressed: () { final mid = Accounts.main.mid; Get.toNamed( - '/favSearch', + '/laterSearch', arguments: { 'type': 0, 'mediaId': mid, 'mid': mid, 'title': '稍后再看', 'count': _baseCtr.counts[LaterViewType.all], - 'searchType': SearchType.later, }, ); }, diff --git a/lib/pages/later_search/controller.dart b/lib/pages/later_search/controller.dart new file mode 100644 index 000000000..a5c4590a3 --- /dev/null +++ b/lib/pages/later_search/controller.dart @@ -0,0 +1,34 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/model_hot_video_item.dart'; +import 'package:PiliPlus/pages/common/common_search_controller.dart'; +import 'package:PiliPlus/http/user.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; + +class LaterSearchController + extends CommonSearchController { + dynamic mid = Get.arguments['mid']; + dynamic count = Get.arguments['count']; + + @override + Future> customGetData() => UserHttp.seeYouLater( + page: currentPage, + keyword: editController.value.text, + ); + + @override + List? getDataList(Map response) { + return response['list']; + } + + Future toViewDel(BuildContext context, int index, aid) async { + var res = await UserHttp.toViewDel(aids: [aid]); + if (res['status']) { + List list = (loadingState.value as Success).response; + list.removeAt(index); + loadingState.refresh(); + } + SmartDialog.showToast(res['msg']); + } +} diff --git a/lib/pages/later_search/view.dart b/lib/pages/later_search/view.dart new file mode 100644 index 000000000..fc2c0f8e9 --- /dev/null +++ b/lib/pages/later_search/view.dart @@ -0,0 +1,151 @@ +import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/widgets/icon_button.dart'; +import 'package:PiliPlus/common/widgets/video_card_h.dart'; +import 'package:PiliPlus/models/model_hot_video_item.dart'; +import 'package:PiliPlus/pages/common/common_search_page.dart'; +import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/page_utils.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class LaterSearchPage extends CommonSearchPage { + const LaterSearchPage({super.key}); + + @override + State createState() => _LaterSearchPageState(); +} + +class _LaterSearchPageState + extends CommonSearchPageState { + @override + final LaterSearchController controller = Get.put( + LaterSearchController(), + tag: Utils.generateRandomString(8), + ); + + @override + Widget buildList(List list) { + return CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: controller.scrollController, + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + sliver: SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context, minHeight: 110), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == list.length - 1) { + controller.onLoadMore(); + } + final item = list[index]; + return Stack( + children: [ + VideoCardH( + videoItem: item, + source: 'later', + onViewLater: (cid) { + PageUtils.toVideoPage( + 'bvid=${item.bvid}&cid=$cid', + arguments: { + 'videoItem': item, + 'oid': item.aid, + 'heroTag': Utils.makeHeroTag(item.bvid), + 'sourceType': 'watchLater', + 'count': controller.count, + 'favTitle': '稍后再看', + 'mediaId': controller.mid, + 'desc': false, + 'isContinuePlaying': index != 0, + }, + ); + }, + ), + Positioned( + top: 5, + left: 12, + bottom: 5, + child: IgnorePointer( + child: LayoutBuilder( + builder: (context, constraints) => AnimatedOpacity( + opacity: item.checked == true ? 1 : 0, + duration: const Duration(milliseconds: 200), + child: Container( + alignment: Alignment.center, + height: constraints.maxHeight, + width: constraints.maxHeight * + StyleString.aspectRatio, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.black.withOpacity(0.6), + ), + child: SizedBox( + width: 34, + height: 34, + child: AnimatedScale( + scale: item.checked == true ? 1 : 0, + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOut, + child: IconButton( + tooltip: '取消选择', + style: ButtonStyle( + padding: WidgetStateProperty.all( + EdgeInsets.zero), + backgroundColor: + WidgetStateProperty.resolveWith( + (states) { + return Theme.of(context) + .colorScheme + .surface + .withOpacity(0.8); + }, + ), + ), + onPressed: null, + icon: Icon( + Icons.done_all_outlined, + color: + Theme.of(context).colorScheme.primary, + ), + ), + ), + ), + ), + ), + ), + ), + ), + Positioned( + right: 12, + bottom: 0, + child: iconButton( + tooltip: '移除', + context: context, + onPressed: () { + controller.toViewDel( + context, + index, + item.aid, + ); + }, + icon: Icons.clear, + iconColor: + Theme.of(context).colorScheme.onSurfaceVariant, + bgColor: Colors.transparent, + ), + ), + ], + ); + }, + ), + ), + ), + ], + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index d147191a4..9191af400 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -1,4 +1,8 @@ import 'package:PiliPlus/pages/fav/view.dart'; +import 'package:PiliPlus/pages/fav_search/view.dart'; +import 'package:PiliPlus/pages/follow_search/view.dart'; +import 'package:PiliPlus/pages/history_search/view.dart'; +import 'package:PiliPlus/pages/later_search/view.dart'; import 'package:PiliPlus/pages/member/member_page.dart'; import 'package:PiliPlus/pages/member/widget/edit_profile_page.dart'; import 'package:PiliPlus/pages/search_trending/view.dart'; @@ -25,7 +29,6 @@ import '../pages/dynamics/detail/index.dart'; import '../pages/dynamics/index.dart'; import '../pages/fan/index.dart'; import '../pages/fav_detail/index.dart'; -import '../pages/fav_search/index.dart'; import '../pages/follow/index.dart'; import '../pages/history/index.dart'; import '../pages/home/index.dart'; @@ -132,6 +135,10 @@ class Routes { CustomGetPage(name: '/playSpeedSet', page: () => const PlaySpeedPage()), // 收藏搜索 CustomGetPage(name: '/favSearch', page: () => const FavSearchPage()), + CustomGetPage( + name: '/historySearch', page: () => const HistorySearchPage()), + CustomGetPage(name: '/laterSearch', page: () => const LaterSearchPage()), + CustomGetPage(name: '/followSearch', page: () => const FollowSearchPage()), // 消息页面 CustomGetPage(name: '/whisper', page: () => const WhisperPage()), // 私信详情