diff --git a/lib/http/search.dart b/lib/http/search.dart index 0fcd6224..c8c0c4ee 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:PiliPalaX/http/loading_state.dart'; import 'package:hive/hive.dart'; import '../models/bangumi/info.dart'; import '../models/common/search_type.dart'; @@ -68,7 +69,7 @@ class SearchHttp { } // 分类搜索 - static Future searchByType({ + static Future searchByType({ required SearchType searchType, required String keyword, required page, @@ -86,14 +87,12 @@ class SearchHttp { }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { - Object data; + dynamic data; try { switch (searchType) { case SearchType.video: List blackMidsList = localCache - .get(LocalCacheKey.blackMidsList, defaultValue: [-1]) - .map((i) => i as int) - .toList(); + .get(LocalCacheKey.blackMidsList, defaultValue: []); for (var i in res.data['data']['result']) { // 屏蔽推广和拉黑用户 i['available'] = !blackMidsList.contains(i['mid']); @@ -113,21 +112,20 @@ class SearchHttp { data = SearchArticleModel.fromJson(res.data['data']); break; } - return { - 'status': true, - 'data': data, - }; + if (data.list.isNotEmpty) { + return LoadingState.success(data.list); + } else { + return LoadingState.empty(); + } } catch (err) { print(err); + return LoadingState.error(err.toString()); } } else { - return { - 'status': false, - 'data': [], - 'msg': res.data['data'] != null && res.data['data']['numPages'] == 0 - ? '没有相关数据' - : res.data['message'], - }; + return LoadingState.error( + res.data['data'] != null && res.data['data']['numPages'] == 0 + ? '没有相关数据' + : res.data['message']); } } diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 5f92bea8..cbcf7316 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -1,53 +1,32 @@ -import 'package:PiliPalaX/utils/extension.dart'; -import 'package:flutter/material.dart'; +import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/http/search.dart'; import 'package:PiliPalaX/models/common/search_type.dart'; import 'package:PiliPalaX/utils/id_utils.dart'; import 'package:PiliPalaX/utils/utils.dart'; -class SearchPanelController extends GetxController { +class SearchPanelController extends CommonController { SearchPanelController({this.keyword, this.searchType}); - ScrollController scrollController = ScrollController(); String? keyword; SearchType? searchType; - RxInt page = 1.obs; - RxList resultList = [].obs; // 结果排序方式 搜索类型为视频、专栏及相簿时 RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; - Future onSearch({type = 'init'}) async { - var result = await SearchHttp.searchByType( - searchType: searchType!, - keyword: keyword!, - page: page.value, - order: searchType!.type != 'video' ? null : order.value, - duration: searchType!.type != 'video' ? null : duration.value); - if (result['status']) { - if (type == 'onRefresh') { - resultList.value = result['data'].list; - } else { - resultList.addAll(result['data'].list); - } - page.value++; - onPushDetail(keyword, resultList); - } - return result; + @override + void onInit() { + super.onInit(); + queryData(); } - Future onRefresh() async { - page.value = 1; - await onSearch(type: 'onRefresh'); + @override + void handleSuccess(List currentList, List dataList) { + onPushDetail(dataList); } - // 返回顶部并刷新 - void animateToTop() { - scrollController.animToTop(); - } - - void onPushDetail(keyword, resultList) async { + void onPushDetail(resultList) async { // 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页 Map matchRes = IdUtils.matchAvorBv(input: keyword); List matchKeys = matchRes.keys.toList(); @@ -82,4 +61,13 @@ class SearchPanelController extends GetxController { } } } + + @override + Future customGetData() => SearchHttp.searchByType( + searchType: searchType!, + keyword: keyword!, + page: currentPage, + order: searchType!.type != 'video' ? null : order.value, + duration: searchType!.type != 'video' ? null : duration.value, + ); } diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index 08fac406..c95ba496 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/http/loading_state.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -31,8 +32,6 @@ class _SearchPanelState extends State with AutomaticKeepAliveClientMixin { late SearchPanelController _searchPanelController; - late Future _futureBuilderFuture; - @override bool get wantKeepAlive => true; @@ -51,17 +50,15 @@ class _SearchPanelState extends State _searchPanelController.scrollController.position.maxScrollExtent - 100) { EasyThrottle.throttle('history', const Duration(seconds: 1), () { - _searchPanelController.onSearch(type: 'onLoad'); + _searchPanelController.onLoadMore(); }); } }); - _futureBuilderFuture = _searchPanelController.onSearch(); } @override void dispose() { _searchPanelController.scrollController.removeListener(() {}); - _searchPanelController.scrollController.dispose(); super.dispose(); } @@ -72,101 +69,86 @@ class _SearchPanelState extends State onRefresh: () async { await _searchPanelController.onRefresh(); }, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data != null) { - Map data = snapshot.data; - var ctr = _searchPanelController; - RxList list = ctr.resultList; - if (data['status']) { - return Obx(() { - list.length; - switch (widget.searchType) { - case SearchType.video: - return SearchVideoPanel( - ctr: _searchPanelController, - // ignore: invalid_use_of_protected_member - list: list.value, - ); - case SearchType.media_bangumi: - return searchBangumiPanel(context, ctr, list); - case SearchType.bili_user: - return searchUserPanel(context, ctr, list); - case SearchType.live_room: - return searchLivePanel(context, ctr, list); - case SearchType.article: - return searchArticlePanel(context, ctr, list); - default: - return const SizedBox(); - } - }); - } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () { - setState(() { - _futureBuilderFuture = - _searchPanelController.onSearch(); - }); - }, - ), - ], - ); - } - } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: '没有相关数据', - fn: () { - setState(() { - _futureBuilderFuture = - _searchPanelController.onSearch(); - }); - }, - ), - ], - ); - } - } else { - // 骨架屏 - return CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - mainAxisSpacing: StyleString.safeSpace, - crossAxisSpacing: StyleString.safeSpace, - maxCrossAxisExtent: Grid.maxRowWidth * 2, - childAspectRatio: StyleString.aspectRatio * 2.4, - mainAxisExtent: 0), - delegate: SliverChildBuilderDelegate( - (context, index) { - switch (widget.searchType) { - case SearchType.video: - return const VideoCardHSkeleton(); - case SearchType.media_bangumi: - return const MediaBangumiSkeleton(); - case SearchType.bili_user: - return const VideoCardHSkeleton(); - case SearchType.live_room: - return const VideoCardHSkeleton(); - default: - return const VideoCardHSkeleton(); - } - }, - childCount: 15, - )) - ]); - } - }, - ), + child: Obx(() => _buildBody(_searchPanelController.loadingState.value)), ); } + + Widget _buildBody(LoadingState loadingState) { + if (loadingState is Success) { + switch (widget.searchType) { + case SearchType.video: + return SearchVideoPanel( + ctr: _searchPanelController, + list: loadingState.response, + ); + case SearchType.media_bangumi: + return searchBangumiPanel( + context, + _searchPanelController, + loadingState.response, + ); + case SearchType.bili_user: + return searchUserPanel( + context, + _searchPanelController, + loadingState.response, + ); + case SearchType.live_room: + return searchLivePanel( + context, + _searchPanelController, + loadingState.response, + ); + case SearchType.article: + return searchArticlePanel( + context, + _searchPanelController, + loadingState.response, + ); + default: + return const SizedBox(); + } + } else if (loadingState is Loading) { + return CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + mainAxisSpacing: StyleString.safeSpace, + crossAxisSpacing: StyleString.safeSpace, + maxCrossAxisExtent: Grid.maxRowWidth * 2, + childAspectRatio: StyleString.aspectRatio * 2.4, + mainAxisExtent: 0), + delegate: SliverChildBuilderDelegate( + (context, index) { + switch (widget.searchType) { + case SearchType.video: + return const VideoCardHSkeleton(); + case SearchType.media_bangumi: + return const MediaBangumiSkeleton(); + case SearchType.bili_user: + return const VideoCardHSkeleton(); + case SearchType.live_room: + return const VideoCardHSkeleton(); + default: + return const VideoCardHSkeleton(); + } + }, + childCount: 15, + ), + ), + ], + ); + } else { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + HttpError( + errMsg: loadingState is Error ? loadingState.errMsg : '没有相关数据', + fn: _searchPanelController.onReload, + ), + ], + ); + } + } }