From e945daba3a70afc5579a31f9ac347c13747ea143 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Jul 2025 15:00:23 +0800 Subject: [PATCH] refa query follow up Signed-off-by: bggRGjQaUbCoE --- lib/http/member.dart | 8 +- lib/models/dynamics/up.dart | 57 +++---- lib/models_new/follow/data.dart | 12 +- lib/models_new/follow/list.dart | 14 +- lib/pages/dynamics/controller.dart | 191 +++++++++++----------- lib/pages/dynamics/view.dart | 62 +++---- lib/pages/dynamics/widgets/up_panel.dart | 26 +-- lib/pages/follow/widgets/follow_item.dart | 2 +- 8 files changed, 168 insertions(+), 204 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index a432f4b0..c82e1efc 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -544,9 +544,11 @@ class MemberHttp { if (res.data['code'] == 0) { return Success( FollowData( - list: (res.data['data'] as List?) - ?.map((e) => FollowItemModel.fromJson(e)) - .toList(), + list: + (res.data['data'] as List?) + ?.map((e) => FollowItemModel.fromJson(e)) + .toList() ?? + [], ), ); } else { diff --git a/lib/models/dynamics/up.dart b/lib/models/dynamics/up.dart index ad30effd..e176a98e 100644 --- a/lib/models/dynamics/up.dart +++ b/lib/models/dynamics/up.dart @@ -1,20 +1,21 @@ class FollowUpModel { FollowUpModel({ this.liveUsers, - this.upList, + required this.upList, }); - String? errMsg; LiveUsers? liveUsers; - List? upList; + late List upList; FollowUpModel.fromJson(Map json) { liveUsers = json['live_users'] != null ? LiveUsers.fromJson(json['live_users']) : null; - upList = (json['up_list'] as List?) - ?.map((e) => UpItem.fromJson(e)) - .toList(); + upList = + (json['up_list'] as List?) + ?.map((e) => UpItem.fromJson(e)) + .toList() ?? + []; } } @@ -38,54 +39,40 @@ class LiveUsers { } } -sealed class UserItem { - String? face; - bool? hasUpdate; - late int mid; - String? uname; - - UserItem({ - this.face, - this.hasUpdate, - int? mid, - this.uname, - }) : mid = mid ?? -1; -} - -class LiveUserItem extends UserItem { +class LiveUserItem extends UpItem { bool? isReserveRecall; String? jumpUrl; int? roomId; String? title; - LiveUserItem.fromJson(Map json) { + LiveUserItem.fromJson(Map json) + : super(mid: json['mid'] ?? 0) { face = json['face']; isReserveRecall = json['is_reserve_recall']; jumpUrl = json['jump_url']; - mid = json['mid'] ?? -1; roomId = json['room_id']; title = json['title']; uname = json['uname']; - hasUpdate = false; } } -class UpItem extends UserItem { - UpItem({ - super.face, - super.hasUpdate, - // this.isReserveRecall, - super.mid, - super.uname, - }); +class UpItem { + String? face; + bool? hasUpdate; + late int mid; + String? uname; - // bool? isReserveRecall; + UpItem({ + this.face, + this.hasUpdate, + required this.mid, + this.uname, + }); UpItem.fromJson(Map json) { face = json['face']; hasUpdate = json['has_update']; - // isReserveRecall = json['is_reserve_recall']; - mid = json['mid'] ?? -1; + mid = json['mid'] ?? 0; uname = json['uname']; } } diff --git a/lib/models_new/follow/data.dart b/lib/models_new/follow/data.dart index 66a3e6e6..1a24c24b 100644 --- a/lib/models_new/follow/data.dart +++ b/lib/models_new/follow/data.dart @@ -1,16 +1,18 @@ import 'package:PiliPlus/models_new/follow/list.dart'; class FollowData { - List? list; + late List list; int? reVersion; int? total; - FollowData({this.list, this.reVersion, this.total}); + FollowData({required this.list, this.reVersion, this.total}); factory FollowData.fromJson(Map json) => FollowData( - list: (json['list'] as List?) - ?.map((e) => FollowItemModel.fromJson(e as Map)) - .toList(), + list: + (json['list'] as List?) + ?.map((e) => FollowItemModel.fromJson(e as Map)) + .toList() ?? + [], reVersion: json['re_version'] as int?, total: json['total'] as int?, ); diff --git a/lib/models_new/follow/list.dart b/lib/models_new/follow/list.dart index 0099521a..21011408 100644 --- a/lib/models_new/follow/list.dart +++ b/lib/models_new/follow/list.dart @@ -1,13 +1,11 @@ +import 'package:PiliPlus/models/dynamics/up.dart'; import 'package:PiliPlus/models/model_avatar.dart'; -class FollowItemModel { - int? mid; +class FollowItemModel extends UpItem { int? attribute; int? mtime; dynamic tag; int? special; - String? uname; - String? face; String? sign; int? faceNft; BaseOfficialVerify? officialVerify; @@ -18,13 +16,13 @@ class FollowItemModel { String? followTime; FollowItemModel({ - this.mid, + required super.mid, this.attribute, this.mtime, this.tag, this.special, - this.uname, - this.face, + super.uname, + super.face, this.sign, this.faceNft, this.officialVerify, @@ -37,7 +35,7 @@ class FollowItemModel { factory FollowItemModel.fromJson(Map json) => FollowItemModel( - mid: json['mid'] as int?, + mid: json['mid'] as int? ?? 0, attribute: json['attribute'] as int?, mtime: json['mtime'] as int?, tag: json['tag'] as dynamic, diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index d470b5df..46d104e1 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/http/follow.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; import 'package:PiliPlus/models/dynamics/up.dart'; -import 'package:PiliPlus/models_new/follow/list.dart'; +import 'package:PiliPlus/models_new/follow/data.dart'; import 'package:PiliPlus/pages/common/common_controller.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/services/account_service.dart'; @@ -13,25 +13,30 @@ import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; class DynamicsController extends GetxController with GetSingleTickerProviderStateMixin, ScrollOrRefreshMixin { @override final ScrollController scrollController = ScrollController(); - String? offset = ''; - Rx upData = FollowUpModel().obs; - // 默认获取全部动态 - RxInt mid = (-1).obs; - late TabController tabController; - Set tempBannedList = {}; - List hasUpdatedUps = []; - int allFollowedUpsPage = 1; - int allFollowedUpsTotal = 0; + late final TabController tabController = TabController( + length: DynamicsTabType.values.length, + vsync: this, + initialIndex: Pref.defaultDynamicType, + ); + late final RxInt mid = (-1).obs; late int currentMid = -1; - late bool showLiveItems = Pref.expandDynLivePanel; + + Set tempBannedList = {}; + + final Rx> upState = + LoadingState.loading().obs; + late int _upPage = 1; + late bool _upEnd = false; + List? _cacheUpList; + late final showAllUp = Pref.dynamicsShowAllFollowedUp; + late bool showLiveUp = Pref.expandDynLivePanel; final upPanelPosition = Pref.upPanelPosition; @@ -50,114 +55,98 @@ class DynamicsController extends GetxController @override void onInit() { super.onInit(); - tabController = TabController( - length: DynamicsTabType.values.length, - vsync: this, - initialIndex: Pref.defaultDynamicType, - ); - + if (showAllUp) { + scrollController.addListener(listener); + } queryFollowUp(); } + void listener() { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 300) { + EasyThrottle.throttle( + 'following', + const Duration(seconds: 1), + queryFollowing2, + ); + } + } + Future queryFollowing2() async { - if (upData.value.upList != null && - upData.value.upList!.length >= allFollowedUpsTotal) { + if (isQuerying) return; + isQuerying = true; + if (_upEnd) { + isQuerying = false; return; } - var res = await FollowHttp.followings( + + final res = await FollowHttp.followings( vmid: accountService.mid, - pn: allFollowedUpsPage, + pn: _upPage, orderType: 'attention', ps: 50, ); + if (res.isSuccess) { - upData.value.upList ??= []; - upData.value.upList!.addAll( - res.data.list! - .where((e) => hasUpdatedUps.every((e1) => e.mid != e1.mid)) - .map( - (FollowItemModel e) => UpItem( - face: e.face, - mid: e.mid, - uname: e.uname, - hasUpdate: false, - ), - ), - ); - allFollowedUpsPage += 1; - allFollowedUpsTotal = res.data.total!; - upData.refresh(); - } else { - res.toast(); + _upPage++; + final list = res.data.list; + if (list.isEmpty) { + _upEnd = true; + } + upState + ..value.data.upList.addAll( + list..removeWhere((e) => _cacheUpList?.contains(e) == true), + ) + ..refresh(); } + + isQuerying = false; } late bool isQuerying = false; Future queryFollowUp() async { if (isQuerying) return; isQuerying = true; + if (!accountService.isLogin.value) { - upData - ..value.errMsg = '账号未登录' - ..refresh(); + upState.value = const Error(null); + isQuerying = false; + return; } - upData.value.errMsg = null; - if (Pref.dynamicsShowAllFollowedUp) { - allFollowedUpsPage = 1; - final f1 = DynamicsHttp.followUp(); - final f2 = FollowHttp.followings( - vmid: accountService.mid, - pn: allFollowedUpsPage, - orderType: 'attention', - ps: 50, - ); - final res0 = await f1; - if (!res0.isSuccess) { - SmartDialog.showToast("获取关注动态失败:$res0"); - upData - ..value.errMsg = (res0 as Error).errMsg - ..refresh(); - } else { - upData - ..value.liveUsers = res0.data.liveUsers - ..refresh(); - hasUpdatedUps = res0.data.upList!; - } - List allFollowedUps = []; - final res1 = await f2; - if (!res1.isSuccess) { - SmartDialog.showToast("获取关注列表失败:$res1"); - } else { - allFollowedUps = res1.data.list! - .where((e) => hasUpdatedUps.every((e1) => e.mid != e1.mid)) - .map( - (e) => UpItem( - face: e.face, - mid: e.mid, - uname: e.uname, - hasUpdate: false, - ), - ) - .toList(); - allFollowedUpsPage += 1; - allFollowedUpsTotal = res1.data.total!; - } - upData - ..value.upList = hasUpdatedUps + allFollowedUps - ..refresh(); - } else { - var res = await DynamicsHttp.followUp(); - if (res.isSuccess) { - upData.value = res.data; - if (upData.value.upList.isNullOrEmpty) { - mid.value = -1; + + final res = await Future.wait([ + DynamicsHttp.followUp(), + if (showAllUp) + FollowHttp.followings( + vmid: accountService.mid, + pn: _upPage, + orderType: 'attention', + ps: 50, + ), + ]); + + final first = res.first; + final second = res.getOrNull(1); + if (first.isSuccess) { + FollowUpModel data = first.data as FollowUpModel; + if (second != null && second.isSuccess) { + _cacheUpList = List.from(data.upList); + FollowData data1 = second.data as FollowData; + final list1 = data1.list; + + _upPage++; + if (list1.isEmpty || list1.length >= (data1.total ?? 0)) { + _upEnd = true; } - } else { - upData - ..value.errMsg = (res as Error).errMsg - ..refresh(); + + final list = data.upList; + list.addAll(list1..removeWhere((e) => list.contains(e))); } + upState.value = Success(data); + } else { + upState.value = const Error(null); } + isQuerying = false; } @@ -177,6 +166,10 @@ class DynamicsController extends GetxController @override Future onRefresh() async { + if (showAllUp) { + _upPage = 1; + _cacheUpList = null; + } queryFollowUp(); await controller?.onRefresh(); } @@ -212,7 +205,9 @@ class DynamicsController extends GetxController @override void onClose() { tabController.dispose(); - scrollController.dispose(); + scrollController + ..removeListener(listener) + ..dispose(); super.onClose(); } } diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 9ee0fd4f..3639145c 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -1,12 +1,12 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; +import 'package:PiliPlus/models/dynamics/up.dart'; import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/dynamics/widgets/up_panel.dart'; import 'package:PiliPlus/pages/dynamics_create/view.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; -import 'package:PiliPlus/utils/storage_pref.dart'; -import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart' hide DraggableScrollableSheet; import 'package:get/get.dart'; @@ -52,29 +52,6 @@ class _DynamicsPageState extends State ), ); - @override - void initState() { - super.initState(); - if (Pref.dynamicsShowAllFollowedUp) { - _dynamicsController.scrollController.addListener(listener); - } - } - - void listener() { - if (_dynamicsController.scrollController.position.pixels >= - _dynamicsController.scrollController.position.maxScrollExtent - 300) { - EasyThrottle.throttle('following', const Duration(seconds: 1), () { - _dynamicsController.queryFollowing2(); - }); - } - } - - @override - void dispose() { - _dynamicsController.scrollController.removeListener(listener); - super.dispose(); - } - Widget upPanelPart(ThemeData theme) { bool isTop = upPanelPosition == UpPanelPosition.top; bool needBg = upPanelPosition.index > 1; @@ -84,25 +61,28 @@ class _DynamicsPageState extends State child: SizedBox( width: isTop ? null : 64, height: isTop ? 76 : null, - child: Obx( - () { - final upData = _dynamicsController.upData.value; - if (upData.upList == null && upData.liveUsers == null) { - return const SizedBox.shrink(); - } else if (upData.errMsg != null) { - return Center( - child: IconButton( - icon: const Icon(Icons.refresh), - onPressed: _dynamicsController.queryFollowUp, - ), - ); - } else { - return UpPanel(dynamicsController: _dynamicsController); - } + child: Obx(() => _buildUpPanel(_dynamicsController.upState.value)), + ), + ); + } + + Widget _buildUpPanel(LoadingState upState) { + return switch (upState) { + Loading() => const SizedBox.shrink(), + Success() => UpPanel( + dynamicsController: _dynamicsController, + ), + Error() => Center( + child: IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + _dynamicsController + ..upState.value = LoadingState.loading() + ..queryFollowUp(); }, ), ), - ); + }; } @override diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index 36a54ba6..5146807d 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -30,8 +30,8 @@ class _UpPanelState extends State { return const SizedBox.shrink(); } final theme = Theme.of(context); - final upData = controller.upData.value; - final List? upList = upData.upList; + final upData = controller.upState.value.data; + final List upList = upData.upList; final List? liveList = upData.liveUsers?.items; return CustomScrollView( scrollDirection: isTop ? Axis.horizontal : Axis.vertical, @@ -41,7 +41,7 @@ class _UpPanelState extends State { SliverToBoxAdapter( child: InkWell( onTap: () => setState(() { - controller.showLiveItems = !controller.showLiveItems; + controller.showLiveUp = !controller.showLiveUp; }), onLongPress: () => Get.to(const LiveFollowPage()), child: Container( @@ -64,7 +64,7 @@ class _UpPanelState extends State { WidgetSpan( alignment: PlaceholderAlignment.middle, child: Icon( - controller.showLiveItems + controller.showLiveUp ? Icons.expand_less : Icons.expand_more, size: 12, @@ -75,7 +75,7 @@ class _UpPanelState extends State { WidgetSpan( alignment: PlaceholderAlignment.middle, child: Icon( - controller.showLiveItems + controller.showLiveUp ? Icons.keyboard_arrow_right : Icons.keyboard_arrow_left, color: theme.colorScheme.primary, @@ -88,7 +88,7 @@ class _UpPanelState extends State { ), ), ), - if (controller.showLiveItems && liveList?.isNotEmpty == true) + if (controller.showLiveUp && liveList?.isNotEmpty == true) SliverList.builder( itemCount: liveList!.length, itemBuilder: (context, index) { @@ -110,9 +110,9 @@ class _UpPanelState extends State { ), ), ), - if (upList?.isNotEmpty == true) + if (upList.isNotEmpty == true) SliverList.builder( - itemCount: upList!.length, + itemCount: upList.length, itemBuilder: (context, index) { return upItemBuild(theme, upList[index]); }, @@ -122,7 +122,7 @@ class _UpPanelState extends State { ); } - void _onSelect(UserItem data) { + void _onSelect(UpItem data) { controller ..currentMid = data.mid ..onSelectUp(data.mid); @@ -132,7 +132,7 @@ class _UpPanelState extends State { setState(() {}); } - Widget upItemBuild(ThemeData theme, UserItem data) { + Widget upItemBuild(ThemeData theme, UpItem data) { final currentMid = controller.currentMid; final isLive = data is LiveUserItem; bool isCurrent = isLive || currentMid == data.mid || currentMid == -1; @@ -143,14 +143,14 @@ class _UpPanelState extends State { onTap: () { feedBack(); switch (data) { + case LiveUserItem(): + Get.toNamed('/liveRoom?roomid=${data.roomId}'); case UpItem(): _onSelect(data); break; - case LiveUserItem(): - Get.toNamed('/liveRoom?roomid=${data.roomId}'); } }, - onDoubleTap: isLive ? () => _onSelect(data) : null, + // onDoubleTap: isLive ? () => _onSelect(data) : null, onLongPress: data.mid == -1 ? null : () => Get.toNamed('/member?mid=${data.mid}'), diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index 1a670296..4a916ac6 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -31,7 +31,7 @@ class FollowItem extends StatelessWidget { if (onSelect != null) { onSelect!.call( UserModel( - mid: item.mid!, + mid: item.mid, name: item.uname!, avatar: item.face!, ),