From f54859098c14c7e30d978886230dbc09ac09cd91 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 21 Nov 2024 12:21:35 +0800 Subject: [PATCH] refactor: member dynamic page fix: DynamicsDataModel parsing error Signed-off-by: bggRGjQaUbCoE --- lib/http/member.dart | 26 +-- lib/models/dynamics/result.dart | 2 +- lib/pages/common/reply_controller.dart | 3 +- lib/pages/fan/controller.dart | 3 +- lib/pages/fav/controller.dart | 3 +- lib/pages/fav_detail/controller.dart | 3 +- lib/pages/fav_search/controller.dart | 3 +- .../content/article/member_article_ctr.dart | 3 +- .../content/bangumi/member_bangumi_ctr.dart | 3 +- .../content/video/member_video_ctr.dart | 3 +- .../member_dynamic/member_dynamic_ctr.dart | 3 +- lib/pages/member/new/member_page.dart | 1 + lib/pages/member_dynamics/controller.dart | 67 +++--- lib/pages/member_dynamics/view.dart | 208 ++++++++---------- 14 files changed, 155 insertions(+), 176 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index e7d56827..68f182cb 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -350,8 +350,10 @@ class MemberHttp { 'web_location': 1550101, 'order_avoided': orderAvoided, 'dm_img_list': '[]', - 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), - 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), + 'dm_img_str': dmImgStr.substring( + 0, (dmImgStr.length - 2).clamp(0, dmImgStr.length - 1)), + 'dm_cover_img_str': dmCoverImgStr.substring( + 0, (dmCoverImgStr.length - 2).clamp(0, dmCoverImgStr.length - 1)), 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', }); var res = await Request().get( @@ -377,7 +379,7 @@ class MemberHttp { } // 用户动态 - static Future memberDynamic({String? offset, int? mid}) async { + static Future memberDynamic({String? offset, int? mid}) async { String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); Map params = await WbiSign().makSign({ @@ -388,27 +390,23 @@ class MemberHttp { 'platform': 'web', 'web_location': '333.999', 'dm_img_list': '[]', - 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), - 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), + 'dm_img_str': dmImgStr.substring( + 0, (dmImgStr.length - 2).clamp(0, dmImgStr.length - 1)), + 'dm_cover_img_str': dmCoverImgStr.substring( + 0, (dmCoverImgStr.length - 2).clamp(0, dmCoverImgStr.length - 1)), 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', 'x-bili-device-req-json': jsonEncode({"platform": "web", "device": "pc"}), 'x-bili-web-req-json': jsonEncode({"spm_id": "333.999"}), }); var res = await Request().get(Api.memberDynamic, data: params); if (res.data['code'] == 0) { - return { - 'status': true, - 'data': DynamicsDataModel.fromJson(res.data['data']), - }; + return LoadingState.success(DynamicsDataModel.fromJson(res.data['data'])); } else { Map errMap = { -352: '风控校验失败,请检查登录状态', }; - return { - 'status': false, - 'data': [], - 'msg': errMap[res.data['code']] ?? res.data['message'], - }; + return LoadingState.error( + errMap[res.data['code']] ?? res.data['message']); } } diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 8cc6e284..8793d626 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -31,7 +31,7 @@ class DynamicItemModel { }); Map? basic; - String? idStr; + dynamic idStr; ItemModulesModel? modules; ItemOrigModel? orig; String? type; diff --git a/lib/pages/common/reply_controller.dart b/lib/pages/common/reply_controller.dart index 0967e7a5..5acb2da1 100644 --- a/lib/pages/common/reply_controller.dart +++ b/lib/pages/common/reply_controller.dart @@ -3,6 +3,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/models/common/reply_type.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/video/detail/reply_new/reply_page.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -56,7 +57,7 @@ abstract class ReplyController extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (noMore.value == '没有更多了') return Future.value(); + if (isRefresh.not && noMore.value == '没有更多了') return Future.value(); return super.queryData(isRefresh); } diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart index dde01cc7..87a00d4d 100644 --- a/lib/pages/fan/controller.dart +++ b/lib/pages/fan/controller.dart @@ -1,6 +1,7 @@ import 'package:PiliPalaX/http/fan.dart'; import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:PiliPalaX/utils/storage.dart'; @@ -36,7 +37,7 @@ class FansController extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (isEnd) { + if (isRefresh.not && isEnd) { return Future.value(); } return super.queryData(isRefresh); diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 2df04cb4..0dbffd89 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -1,5 +1,6 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:hive/hive.dart'; import 'package:PiliPalaX/http/user.dart'; import 'package:PiliPalaX/models/user/info.dart'; @@ -23,7 +24,7 @@ class FavController extends CommonController { loadingState.value = LoadingState.error('账号未登录'); return Future.value(); } - if (!hasMore) { + if (isRefresh.not && hasMore.not) { return Future.value(); } return super.queryData(isRefresh); diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 4f06b6a1..20e0e276 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -1,6 +1,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/user.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:PiliPalaX/utils/storage.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -38,7 +39,7 @@ class FavDetailController extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (loadingText.value == '没有更多了') { + if (isRefresh.not && loadingText.value == '没有更多了') { return Future.value(); } return super.queryData(isRefresh); diff --git a/lib/pages/fav_search/controller.dart b/lib/pages/fav_search/controller.dart index 3ec5b218..8554a58e 100644 --- a/lib/pages/fav_search/controller.dart +++ b/lib/pages/fav_search/controller.dart @@ -2,6 +2,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/fav_search/view.dart' show SearchType; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -58,7 +59,7 @@ class FavSearchController extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (!hasMore) { + if (isRefresh.not && hasMore.not) { return Future.value(); } return super.queryData(isRefresh); diff --git a/lib/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart b/lib/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart index bd89919d..cbf37724 100644 --- a/lib/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart +++ b/lib/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart @@ -2,6 +2,7 @@ import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/models/space_article/data.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; class MemberArticleCtr extends CommonController { MemberArticleCtr({ @@ -27,7 +28,7 @@ class MemberArticleCtr extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (isEnd) return Future.value(); + if (isRefresh.not && isEnd) return Future.value(); return super.queryData(isRefresh); } diff --git a/lib/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart b/lib/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart index 8834be4d..26f8b47d 100644 --- a/lib/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart +++ b/lib/pages/member/new/content/member_contribute/content/bangumi/member_bangumi_ctr.dart @@ -5,6 +5,7 @@ import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart' show ContributeType; import 'package:PiliPalaX/pages/member/new/controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:get/get.dart'; class MemberBangumiCtr extends CommonController { @@ -38,7 +39,7 @@ class MemberBangumiCtr extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (isEnd) return Future.value(); + if (isRefresh.not && isEnd) return Future.value(); return super.queryData(isRefresh); } diff --git a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart index ccfb5b1d..359391c3 100644 --- a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart +++ b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart @@ -4,6 +4,7 @@ import 'package:PiliPalaX/models/space_archive/data.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart' show ContributeType; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:get/get.dart'; class MemberVideoCtr extends CommonController { @@ -43,7 +44,7 @@ class MemberVideoCtr extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (isEnd) return Future.value(); + if (isRefresh.not && isEnd) return Future.value(); return super.queryData(isRefresh); } diff --git a/lib/pages/member/new/content/member_dynamic/member_dynamic_ctr.dart b/lib/pages/member/new/content/member_dynamic/member_dynamic_ctr.dart index 3b7fab72..b39ae1ad 100644 --- a/lib/pages/member/new/content/member_dynamic/member_dynamic_ctr.dart +++ b/lib/pages/member/new/content/member_dynamic/member_dynamic_ctr.dart @@ -2,6 +2,7 @@ import 'package:PiliPalaX/grpc/app/dynamic/v2/dynamic.pb.dart'; import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; class MemberDynamicCtr extends CommonController { MemberDynamicCtr({ @@ -24,7 +25,7 @@ class MemberDynamicCtr extends CommonController { @override Future queryData([bool isRefresh = true]) { - if (isEnd) return Future.value(); + if (isRefresh.not && isEnd) return Future.value(); return super.queryData(isRefresh); } diff --git a/lib/pages/member/new/member_page.dart b/lib/pages/member/new/member_page.dart index 71f254d7..c2874492 100644 --- a/lib/pages/member/new/member_page.dart +++ b/lib/pages/member/new/member_page.dart @@ -136,6 +136,7 @@ class _MemberPageNewState extends State Widget get _buildBody => SafeArea( top: false, + bottom: false, child: TabBarView( controller: _userController.tabController, children: _userController.tab2!.map((item) { diff --git a/lib/pages/member_dynamics/controller.dart b/lib/pages/member_dynamics/controller.dart index e7cadbca..ea091f8c 100644 --- a/lib/pages/member_dynamics/controller.dart +++ b/lib/pages/member_dynamics/controller.dart @@ -1,48 +1,51 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; +import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/common/common_controller.dart'; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/models/dynamics/result.dart'; -class MemberDynamicsController extends GetxController { +class MemberDynamicsController extends CommonController { MemberDynamicsController(this.mid); - final ScrollController scrollController = ScrollController(); - int? mid; + int mid; String offset = ''; - int count = 0; bool hasMore = true; - RxList dynamicsList = [].obs; - - // TODO: refactor - late Future futureBuilderFuture; @override void onInit() async { super.onInit(); - futureBuilderFuture = getMemberDynamic('onRefresh'); + queryData(); } - Future getMemberDynamic(type) async { - if (type == 'onRefresh') { - offset = ''; - dynamicsList.clear(); - } - if (offset == '-1') { - return; - } - var res = await MemberHttp.memberDynamic( - offset: offset, - mid: mid, - ); - if (res['status']) { - dynamicsList.addAll(res['data'].items); - offset = res['data'].offset != '' ? res['data'].offset : '-1'; - hasMore = res['data'].hasMore; - } - return res; + @override + Future onRefresh() { + offset = ''; + hasMore = true; + return super.onRefresh(); } - // 上拉加载 - Future onLoad() async { - getMemberDynamic('onLoad'); + @override + Future queryData([bool isRefresh = true]) { + if (isRefresh.not && (hasMore.not || offset == '-1')) { + return Future.value(); + } + return super.queryData(isRefresh); } + + @override + bool customHandleResponse(Success response) { + DynamicsDataModel data = response.response; + offset = data.offset?.isNotEmpty == true ? data.offset! : '-1'; + hasMore = data.hasMore ?? false; + if (currentPage != 1 && loadingState.value is Success) { + data.items?.insertAll(0, (loadingState.value as Success).response); + } + loadingState.value = LoadingState.success(data.items); + return true; + } + + @override + Future customGetData() => MemberHttp.memberDynamic( + offset: offset, + mid: mid, + ); } diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index d80871da..c555c42e 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -1,3 +1,5 @@ +import 'package:PiliPalaX/common/widgets/refresh_indicator.dart'; +import 'package:PiliPalaX/http/loading_state.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -36,29 +38,10 @@ class _MemberDynamicsPageState extends State final String heroTag = Utils.makeHeroTag(mid); _memberDynamicController = Get.put(MemberDynamicsController(mid), tag: heroTag); - // _memberDynamicController.scrollController.addListener( - // () { - // if (_memberDynamicController.scrollController.position.pixels >= - // _memberDynamicController.scrollController.position.maxScrollExtent - - // 200) { - // EasyThrottle.throttle( - // 'member_dynamics', const Duration(milliseconds: 1000), () { - // _memberDynamicController.onLoad(); - // }); - // } - // }, - // ); dynamicsWaterfallFlow = GStorage.setting .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); } - @override - void dispose() { - // _memberDynamicController.scrollController.removeListener(() {}); - _memberDynamicController.scrollController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { super.build(context); @@ -75,106 +58,91 @@ class _MemberDynamicsPageState extends State : _buildBody; } - Widget get _buildBody => CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - // controller: _memberDynamicController.scrollController, - slivers: [ - FutureBuilder( - future: _memberDynamicController.futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data != null) { - Map data = snapshot.data as Map; - List list = _memberDynamicController.dynamicsList; - if (data['status']) { - return Obx(() { - if (list.isEmpty) { - return const SliverToBoxAdapter(); - } - if (!dynamicsWaterfallFlow) { - return SliverCrossAxisGroup( - slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.maxRowWidth * 2, - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == list.length - 1) { - EasyThrottle.throttle('member_dynamics', - const Duration(milliseconds: 1000), - () { - _memberDynamicController.onLoad(); - }); - } - return DynamicPanel(item: list[index]); - }, - childCount: list.length, - ), - )), - const SliverFillRemaining(), - ], - ); - } - return SliverWaterfallFlow.extent( - maxCrossAxisExtent: Grid.maxRowWidth * 2, - //cacheExtent: 0.0, - crossAxisSpacing: StyleString.safeSpace, - mainAxisSpacing: StyleString.safeSpace, - - /// follow max child trailing layout offset and layout with full cross axis extend - /// last child as loadmore item/no more item in [GridView] and [WaterfallFlow] - /// with full cross axis extend - // LastChildLayoutType.fullCrossAxisExtend, - - /// as foot at trailing and layout with full cross axis extend - /// show no more item at trailing when children are not full of viewport - /// if children is full of viewport, it's the same as fullCrossAxisExtend - // LastChildLayoutType.foot, - lastChildLayoutTypeBuilder: (index) { - if (index == list.length - 1) { - EasyThrottle.throttle('member_dynamics', - const Duration(milliseconds: 1000), () { - _memberDynamicController.onLoad(); - }); - } - return index == list.length - ? LastChildLayoutType.foot - : LastChildLayoutType.none; - }, - children: [ - for (var i in list) DynamicPanel(item: i), - ]); - }); - } else { - return HttpError( - errMsg: snapshot.data['msg'], - fn: () { - setState(() { - _memberDynamicController.futureBuilderFuture = - _memberDynamicController - .getMemberDynamic('onRefresh'); - }); - }, - ); - } - } else { - return HttpError( - errMsg: 'NULL', - fn: () { - setState(() { - _memberDynamicController.futureBuilderFuture = - _memberDynamicController - .getMemberDynamic('onRefresh'); - }); - }, - ); - } - } else { - return const SliverToBoxAdapter(); - } - }, - ), - ], + Widget get _buildBody => refreshIndicator( + onRefresh: () async { + await _memberDynamicController.onRefresh(); + }, + child: Obx( + () => _memberDynamicController.loadingState.value is Loading + ? Center( + child: CircularProgressIndicator(), + ) + : CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + _buildContent(_memberDynamicController.loadingState.value), + ], + ), + ), ); + + _buildContent(LoadingState loadingState) { + return loadingState is Success + ? SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom, + ), + sliver: dynamicsWaterfallFlow + ? SliverWaterfallFlow.extent( + maxCrossAxisExtent: Grid.maxRowWidth * 2, + //cacheExtent: 0.0, + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + + /// follow max child trailing layout offset and layout with full cross axis extend + /// last child as loadmore item/no more item in [GridView] and [WaterfallFlow] + /// with full cross axis extend + // LastChildLayoutType.fullCrossAxisExtend, + + /// as foot at trailing and layout with full cross axis extend + /// show no more item at trailing when children are not full of viewport + /// if children is full of viewport, it's the same as fullCrossAxisExtend + // LastChildLayoutType.foot, + lastChildLayoutTypeBuilder: (index) { + if (index == loadingState.response.length - 1) { + EasyThrottle.throttle('member_dynamics', + const Duration(milliseconds: 1000), () { + _memberDynamicController.onLoadMore(); + }); + } + return index == loadingState.response.length + ? LastChildLayoutType.foot + : LastChildLayoutType.none; + }, + children: (loadingState.response as List) + .map((item) => DynamicPanel(item: item)) + .toList(), + ) + : SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.maxRowWidth * 2, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == loadingState.response.length - 1) { + EasyThrottle.throttle('member_dynamics', + const Duration(milliseconds: 1000), () { + _memberDynamicController.onLoadMore(); + }); + } + return DynamicPanel( + item: loadingState.response[index]); + }, + childCount: loadingState.response.length, + ), + ), + ), + const SliverFillRemaining(), + ], + ), + ) + : HttpError( + errMsg: loadingState is Error ? loadingState.errMsg : null, + fn: () { + _memberDynamicController.onReload(); + }, + ); + } }