refactor dyn uplist

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-06-27 09:45:18 +08:00
parent 840f8d5a25
commit 5d15bf5e59
9 changed files with 228 additions and 275 deletions

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/pair.dart'; import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/error_msg.dart';
import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/reply.dart'; import 'package:PiliPlus/http/reply.dart';
@@ -33,19 +34,14 @@ import 'package:dio/dio.dart';
abstract final class DynamicsHttp { abstract final class DynamicsHttp {
@pragma('vm:notify-debugger-on-exception') @pragma('vm:notify-debugger-on-exception')
static Future<LoadingState<DynamicsDataModel>> followDynamic({ static Future<LoadingState<DynamicsDataModel>> followDynamic({
DynamicsTabType type = DynamicsTabType.all, int? hostMid,
String? offset, String? offset,
int? mid,
Set<int>? tempBannedList, Set<int>? tempBannedList,
DynamicsTabType type = .all,
}) async { }) async {
Map<String, dynamic> data = { Map<String, dynamic> data = {
if (type == DynamicsTabType.up) if (type == .up) 'host_mid': hostMid else 'type': type.name,
'host_mid': mid 'offset': ?offset,
else ...{
'type': type.name,
'timezone_offset': '-480',
},
'offset': offset,
'features': Constants.dynFeatures, 'features': Constants.dynFeatures,
}; };
final res = await Request().get(Api.followDynamic, queryParameters: data); final res = await Request().get(Api.followDynamic, queryParameters: data);
@@ -61,7 +57,7 @@ abstract final class DynamicsHttp {
return await followDynamic( return await followDynamic(
type: type, type: type,
offset: data.offset, offset: data.offset,
mid: mid, hostMid: hostMid,
tempBannedList: tempBannedList, tempBannedList: tempBannedList,
); );
} }
@@ -89,22 +85,45 @@ abstract final class DynamicsHttp {
} }
} }
static Future<LoadingState<DynUpList>> dynUpList(String? offset) async { static Future<LoadingState<FollowUpModel>> dynUpList(String? offset) async {
final res = await Request().get( final res = await Request().get(
Api.dynUplist, Api.dynUplist,
queryParameters: { queryParameters: {
'offset': offset, 'offset': ?offset,
'platform': 'web', 'platform': 'web',
'web_location': 333.1365, 'web_location': 333.1365,
}, },
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return Success(DynUpList.fromJson(res.data['data'])); return Success(FollowUpModel.fromUpList(res.data['data']));
} else { } else {
return Error(res.data['message']); return Error(res.data['message']);
} }
} }
static Future<LoadingState<FollowUpModel>> followings({
int? vmid,
int? pn,
int ps = 20,
String orderType = '', // ''=>最近关注,'attention'=>最常访问
}) async {
final res = await Request().get(
Api.followings,
queryParameters: {
'vmid': vmid,
'pn': pn,
'ps': ps,
'order': 'desc',
'order_type': orderType,
},
);
if (res.data['code'] == 0) {
return Success(FollowUpModel.fromFollowList(res.data['data']));
} else {
return Error(errorMsg[res.data['code']] ?? res.data['message']);
}
}
// 动态点赞 // 动态点赞
// static Future likeDynamic({ // static Future likeDynamic({
// required String? dynamicId, // required String? dynamicId,
@@ -817,7 +836,7 @@ abstract final class DynamicsHttp {
Api.dynReaction, Api.dynReaction,
queryParameters: { queryParameters: {
'id': id, 'id': id,
'offset': offset, 'offset': ?offset,
'web_location': 333.1369, 'web_location': 333.1369,
}, },
); );

View File

@@ -132,7 +132,7 @@ class Word {
style = json['style'] == null ? null : Style.fromJson(json['style']); style = json['style'] == null ? null : Style.fromJson(json['style']);
if (json['color'] case final String rawColor if (json['color'] case final String rawColor
when rawColor.startsWith('#')) { when rawColor.startsWith('#')) {
color = ColourUtils.parse2Int(json['color']); color = ColourUtils.parse2Int(rawColor);
} }
fontLevel = json['font_level']; fontLevel = json['font_level'];
} }

View File

@@ -1,41 +1,43 @@
import 'package:PiliPlus/models_new/follow/list.dart';
import 'package:PiliPlus/utils/parse_int.dart'; import 'package:PiliPlus/utils/parse_int.dart';
class FollowUpModel { class FollowUpModel {
FollowUpModel({
this.liveUsers,
required this.upList,
});
LiveUsers? liveUsers; LiveUsers? liveUsers;
late List<UpItem> upList;
bool? hasMore;
String? offset;
FollowUpModel.fromJson(Map<String, dynamic> json) {
liveUsers = json['live_users'] != null
? LiveUsers.fromJson(json['live_users'])
: null;
upList =
(json['up_list']?['items'] as List?)
?.map<UpItem>((e) => UpItem.fromJson(e))
.toList() ??
<UpItem>[];
hasMore = json['up_list']?['has_more'];
offset = json['up_list']?['offset'];
}
}
class DynUpList {
List<UpItem>? upList; List<UpItem>? upList;
bool? hasMore; bool? hasMore;
String? offset; String? offset;
DynUpList.fromJson(Map<String, dynamic> json) { void addAllUpList(List<UpItem> newList) {
upList = (json['items'] as List?) if (upList != null) {
?.map<UpItem>((e) => UpItem.fromJson(e)) upList!.addAll(newList);
} else {
upList = newList;
}
}
factory FollowUpModel.fromJson(Map<String, dynamic> json) {
final model = FollowUpModel.fromUpList(json['up_list']);
final liveUsers = json['live_users'];
if (liveUsers != null) {
model.liveUsers = LiveUsers.fromJson(liveUsers);
}
return model;
}
FollowUpModel.fromUpList(Map<String, dynamic>? json) {
if (json != null) {
upList = (json['items'] as List?)
?.map((e) => UpItem.fromJson(e))
.toList();
hasMore = json['has_more'];
offset = json['offset'];
}
}
FollowUpModel.fromFollowList(Map<String, dynamic> json) {
upList = (json['list'] as List?)
?.map((e) => FollowItemModel.fromJson(e))
.toList(); .toList();
hasMore = json['has_more'];
offset = json['offset'];
} }
} }

View File

@@ -1,17 +1,15 @@
import 'package:PiliPlus/models_new/follow/list.dart'; import 'package:PiliPlus/models_new/follow/list.dart';
class FollowData { class FollowData {
late List<FollowItemModel> list; List<FollowItemModel>? list;
int? total; int? total;
FollowData({required this.list, this.total}); FollowData({required this.list, this.total});
factory FollowData.fromJson(Map<String, dynamic> json) => FollowData( factory FollowData.fromJson(Map<String, dynamic> json) => FollowData(
list: list: (json['list'] as List<dynamic>?)
(json['list'] as List<dynamic>?) ?.map((e) => FollowItemModel.fromJson(e as Map<String, dynamic>))
?.map((e) => FollowItemModel.fromJson(e as Map<String, dynamic>)) .toList(),
.toList() ??
<FollowItemModel>[],
total: json['total'] as int?, total: json['total'] as int?,
); );
} }

View File

@@ -1,12 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/follow.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart'; import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
import 'package:PiliPlus/models/dynamics/up.dart'; import 'package:PiliPlus/models/dynamics/up.dart';
import 'package:PiliPlus/models_new/follow/data.dart'; import 'package:PiliPlus/pages/common/common_data_controller.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/dynamics_tab/controller.dart'; import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts.dart';
@@ -17,24 +15,20 @@ import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class DynamicsController extends GetxController class DynamicsController
with GetSingleTickerProviderStateMixin, ScrollOrRefreshMixin, AccountMixin { extends CommonDataController<FollowUpModel, FollowUpModel>
@override with GetSingleTickerProviderStateMixin, AccountMixin {
final ScrollController scrollController = ScrollController();
late final TabController tabController; late final TabController tabController;
late final RxInt mid = (-1).obs; final Set<int> tempBannedList = <int>{};
late int currentMid = -1;
Set<int> tempBannedList = <int>{}; String? _offset;
late int _page = 1;
final Rx<LoadingState<FollowUpModel>> upState = late bool _isEnd = false;
LoadingState<FollowUpModel>.loading().obs;
late int _upPage = 1;
late bool _upEnd = false;
Set<UpItem>? _cacheUpList; Set<UpItem>? _cacheUpList;
late final _showAllUp = Pref.dynamicsShowAllFollowedUp; late int hostMid = -1, currentMid = -1;
late bool showLiveUp = Pref.expandDynLivePanel; late bool showLiveUp = Pref.expandDynLivePanel;
late final _showAllUp = Pref.dynamicsShowAllFollowedUp;
final upPanelPosition = Pref.upPanelPosition; final upPanelPosition = Pref.upPanelPosition;
@@ -55,154 +49,56 @@ class DynamicsController extends GetxController
void onInit() { void onInit() {
super.onInit(); super.onInit();
tabController = TabController( tabController = TabController(
length: DynamicsTabType.values.length,
vsync: this, vsync: this,
length: DynamicsTabType.values.length,
initialIndex: Pref.defaultDynamicTypeIndex, initialIndex: Pref.defaultDynamicTypeIndex,
); );
queryFollowUp(); queryData();
} }
void onLoadMoreUp() { void _jumpToTab(int mid) {
if (_showAllUp) { tabController.index = mid == -1 ? 0 : 4;
queryAllUp();
} else {
queryUpList();
}
}
Future<void> queryUpList() async {
if (isQuerying || _upEnd) return;
isQuerying = true;
final res = await DynamicsHttp.dynUpList(upState.value.data.offset);
if (res case Success(:final response)) {
if (response.hasMore == false || response.offset.isNullOrEmpty) {
_upEnd = true;
}
final upData = upState.value.data
..hasMore = response.hasMore
..offset = response.offset;
final list = response.upList;
if (list != null && list.isNotEmpty) {
upData.upList.addAll(list);
upState.refresh();
}
}
isQuerying = false;
}
Future<void> queryAllUp() async {
if (isQuerying || _upEnd) return;
isQuerying = true;
final res = await FollowHttp.followings(
vmid: Accounts.main.mid,
pn: _upPage,
orderType: 'attention',
ps: 50,
);
if (res case Success(:final response)) {
_upPage++;
final list = response.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<void> queryFollowUp() async {
if (isQuerying) return;
isQuerying = true;
if (!accountService.isLogin.value) {
upState.value = const Error(null);
isQuerying = false;
return;
}
// reset
_upEnd = false;
if (_showAllUp) _upPage = 1;
final res = await Future.wait([
DynamicsHttp.followUp(),
if (_showAllUp)
FollowHttp.followings(
vmid: Accounts.main.mid,
pn: _upPage,
orderType: 'attention',
ps: 50,
),
]);
final first = res.first;
if (first case final Success<FollowUpModel> i) {
final data = i.response;
final second = res.elementAtOrNull(1);
if (second case final Success<FollowData> j) {
final data1 = j.response;
final list1 = data1.list;
_upPage++;
if (list1.isEmpty || list1.length >= (data1.total ?? 0)) {
_upEnd = true;
}
final list = data.upList;
list.addAll(list1..removeWhere((_cacheUpList = list.toSet()).contains));
}
if (!_showAllUp) {
if (data.hasMore == false || data.offset.isNullOrEmpty) {
_upEnd = true;
}
}
upState.value = Success(data);
} else {
upState.value = const Error(null);
}
isQuerying = false;
} }
void onSelectUp(int mid) { void onSelectUp(int mid) {
if (this.mid.value == mid) { if (currentMid == mid) {
tabController.index = (mid == -1 ? 0 : 4); _jumpToTab(mid);
if (mid == -1) { if (mid == -1) {
queryFollowUp(); singleRefresh();
} }
controller?.onReload(); controller?.onReload();
return; return;
} }
this.mid.value = mid; if (mid != -1) {
tabController.index = (mid == -1 ? 0 : 4); hostMid = mid;
try {
Get.find<DynamicsTabController>(
tag: DynamicsTabType.up.name,
).onReload();
} catch (_) {}
}
currentMid = mid;
_jumpToTab(mid);
}
Future<void> singleRefresh() {
if (_showAllUp) {
_page = 1;
_cacheUpList = null;
}
_offset = null;
_isEnd = false;
return super.onRefresh();
} }
@override @override
Future<void> onRefresh() { Future<void> onRefresh() {
_refreshFollowUp(); singleRefresh();
return controller!.onRefresh(); return controller!.onRefresh();
} }
void _refreshFollowUp() {
if (_showAllUp) {
_upPage = 1;
_cacheUpList = null;
}
queryFollowUp();
}
@override @override
void animateToTop() { void animateToTop() {
controller?.animateToTop(); controller?.animateToTop();
@@ -234,10 +130,71 @@ class DynamicsController extends GetxController
@override @override
void onClose() { void onClose() {
tabController.dispose(); tabController.dispose();
scrollController.dispose();
super.onClose(); super.onClose();
} }
@override @override
void onChangeAccount(bool isLogin) => _refreshFollowUp(); void onChangeAccount(bool isLogin) => onReload();
@override
Future<LoadingState<FollowUpModel>> customGetData() {
if (_offset == null) {
return DynamicsHttp.followUp();
}
if (_showAllUp) {
return DynamicsHttp.followings(
vmid: Accounts.main.mid,
pn: _page,
orderType: 'attention',
ps: 50,
);
} else {
return DynamicsHttp.dynUpList(_offset);
}
}
@override
Future<void> queryData([bool isRefresh = true]) {
if (!isRefresh && _isEnd) return Future.value();
return super.queryData(isRefresh);
}
@override
bool customHandleResponse(bool isRefresh, Success<FollowUpModel> response) {
final res = response.response;
if (_showAllUp) {
if (res.upList?.isNotEmpty != true) {
_isEnd = true;
}
} else {
_offset = res.offset;
if (res.hasMore != true || _offset.isNullOrEmpty) {
_isEnd = true;
}
}
if (isRefresh) {
if (_showAllUp) {
_offset = '';
_cacheUpList = res.upList?.toSet();
}
loadingState.value = response;
} else {
if (_showAllUp) {
_page++;
}
if (res.upList case final upList? when upList.isNotEmpty) {
if (_showAllUp && _cacheUpList != null) {
upList.removeWhere(_cacheUpList!.contains);
}
loadingState
..value.data.addAllUpList(upList)
..refresh();
}
}
return true;
}
} }

View File

@@ -65,11 +65,13 @@ class _DynamicsPageState extends CommonPageState<DynamicsPage>
onNotification: (notification) { onNotification: (notification) {
final metrics = notification.metrics; final metrics = notification.metrics;
if (metrics.pixels >= metrics.maxScrollExtent - 300) { if (metrics.pixels >= metrics.maxScrollExtent - 300) {
_dynamicsController.onLoadMoreUp(); _dynamicsController.onLoadMore();
} }
return false; return false;
}, },
child: Obx(() => _buildUpPanel(_dynamicsController.upState.value)), child: Obx(
() => _buildUpPanel(_dynamicsController.loadingState.value),
),
), ),
), ),
); );
@@ -78,15 +80,14 @@ class _DynamicsPageState extends CommonPageState<DynamicsPage>
Widget _buildUpPanel(LoadingState<FollowUpModel> upState) { Widget _buildUpPanel(LoadingState<FollowUpModel> upState) {
return switch (upState) { return switch (upState) {
Loading() => const SizedBox.shrink(), Loading() => const SizedBox.shrink(),
Success<FollowUpModel>() => UpPanel( Success(:final response) => UpPanel(
upData: response,
dynamicsController: _dynamicsController, dynamicsController: _dynamicsController,
), ),
Error() => Center( Error() => Center(
child: IconButton( child: IconButton(
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
onPressed: () => _dynamicsController onPressed: _dynamicsController.onReload,
..upState.value = LoadingState<FollowUpModel>.loading()
..queryFollowUp(),
), ),
), ),
}; };

View File

@@ -1,7 +1,6 @@
import 'package:PiliPlus/common/assets.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/dynamics/up.dart'; import 'package:PiliPlus/models/dynamics/up.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/live_follow/view.dart'; import 'package:PiliPlus/pages/live_follow/view.dart';
@@ -15,10 +14,12 @@ import 'package:get/get.dart';
class UpPanel extends StatefulWidget { class UpPanel extends StatefulWidget {
const UpPanel({ const UpPanel({
required this.dynamicsController,
super.key, super.key,
required this.upData,
required this.dynamicsController,
}); });
final FollowUpModel upData;
final DynamicsController dynamicsController; final DynamicsController dynamicsController;
@override @override
@@ -33,16 +34,12 @@ class _UpPanelState extends State<UpPanel> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final accountService = controller.accountService;
if (!accountService.isLogin.value) {
return const SizedBox.shrink();
}
final theme = Theme.of(context); final theme = Theme.of(context);
final upData = controller.upState.value.data; final upData = widget.upData;
final List<UpItem> upList = upData.upList; final upList = upData.upList;
final List<LiveUserItem>? liveList = upData.liveUsers?.items; final liveList = upData.liveUsers?.items;
return CustomScrollView( return CustomScrollView(
scrollDirection: isTop ? Axis.horizontal : Axis.vertical, scrollDirection: isTop ? .horizontal : .vertical,
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController, controller: controller.scrollController,
slivers: [ slivers: [
@@ -54,11 +51,11 @@ class _UpPanelState extends State<UpPanel> {
onLongPress: toFollowPage, onLongPress: toFollowPage,
onSecondaryTap: PlatformUtils.isMobile ? null : toFollowPage, onSecondaryTap: PlatformUtils.isMobile ? null : toFollowPage,
child: Container( child: Container(
alignment: Alignment.center, alignment: .center,
height: isTop ? 76 : 60, height: isTop ? 76 : 60,
padding: isTop ? const EdgeInsets.only(left: 12, right: 6) : null, padding: isTop ? const .only(left: 12, right: 6) : null,
child: Text.rich( child: Text.rich(
textAlign: TextAlign.center, textAlign: .center,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: theme.colorScheme.primary, color: theme.colorScheme.primary,
@@ -71,7 +68,7 @@ class _UpPanelState extends State<UpPanel> {
if (!isTop) ...[ if (!isTop) ...[
const TextSpan(text: '\n'), const TextSpan(text: '\n'),
WidgetSpan( WidgetSpan(
alignment: PlaceholderAlignment.middle, alignment: .middle,
child: Icon( child: Icon(
controller.showLiveUp controller.showLiveUp
? Icons.expand_less ? Icons.expand_less
@@ -82,7 +79,7 @@ class _UpPanelState extends State<UpPanel> {
), ),
] else ] else
WidgetSpan( WidgetSpan(
alignment: PlaceholderAlignment.middle, alignment: .middle,
child: Icon( child: Icon(
controller.showLiveUp controller.showLiveUp
? Icons.keyboard_arrow_right ? Icons.keyboard_arrow_right
@@ -113,13 +110,13 @@ class _UpPanelState extends State<UpPanel> {
theme, theme,
UpItem( UpItem(
uname: '', uname: '',
face: accountService.face.value, face: controller.accountService.face.value,
mid: Accounts.main.mid, mid: Accounts.main.mid,
), ),
), ),
), ),
), ),
if (upList.isNotEmpty) if (upList != null && upList.isNotEmpty)
SliverList.builder( SliverList.builder(
itemCount: upList.length, itemCount: upList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -131,23 +128,19 @@ class _UpPanelState extends State<UpPanel> {
); );
} }
void _onSelect(UpItem data) { void _onSelect(UpItem item) {
controller item.hasUpdate = false;
..currentMid = data.mid controller.onSelectUp(item.mid);
..onSelectUp(data.mid);
data.hasUpdate = false;
setState(() {}); setState(() {});
} }
Widget upItemBuild(ThemeData theme, UpItem data) { Widget upItemBuild(ThemeData theme, UpItem item) {
final currentMid = controller.currentMid; final currentMid = controller.currentMid;
final isLive = data is LiveUserItem; final isLive = item is LiveUserItem;
final isCurrent = isLive || currentMid == data.mid || currentMid == -1; final isCurrent = isLive || currentMid == item.mid || currentMid == -1;
final isAll = data.mid == -1; final isAll = item.mid == -1;
void toMemberPage() => Get.toNamed('/member?mid=${data.mid}'); void toMemberPage() => Get.toNamed('/member?mid=${item.mid}');
Widget avatar; Widget avatar;
if (isAll) { if (isAll) {
@@ -166,12 +159,12 @@ class _UpPanelState extends State<UpPanel> {
); );
} else { } else {
avatar = Padding( avatar = Padding(
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const .symmetric(horizontal: 4),
child: NetworkImgLayer( child: NetworkImgLayer(
width: 38, width: 38,
height: 38, height: 38,
src: data.face, src: item.face,
type: ImageType.avatar, type: .avatar,
), ),
); );
if (isLive) { if (isLive) {
@@ -191,7 +184,7 @@ class _UpPanelState extends State<UpPanel> {
), ),
], ],
); );
} else if (data.hasUpdate ?? false) { } else if (item.hasUpdate ?? false) {
avatar = Stack( avatar = Stack(
clipBehavior: .none, clipBehavior: .none,
children: [ children: [
@@ -216,9 +209,9 @@ class _UpPanelState extends State<UpPanel> {
onTap: () { onTap: () {
feedBack(); feedBack();
if (isLive) { if (isLive) {
PageUtils.toLiveRoom(data.roomId); PageUtils.toLiveRoom(item.roomId);
} else { } else {
_onSelect(data); _onSelect(item);
} }
}, },
// onDoubleTap: isLive ? () => _onSelect(data) : null, // onDoubleTap: isLive ? () => _onSelect(data) : null,
@@ -228,18 +221,18 @@ class _UpPanelState extends State<UpPanel> {
opacity: isCurrent ? 1 : 0.6, opacity: isCurrent ? 1 : 0.6,
child: Column( child: Column(
spacing: 4, spacing: 4,
mainAxisSize: MainAxisSize.min, mainAxisSize: .min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: .center,
children: [ children: [
avatar, avatar,
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const .symmetric(horizontal: 4),
child: Text( child: Text(
isTop ? '${data.uname}\n' : data.uname!, isTop ? '${item.uname}\n' : item.uname!,
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: .center,
style: TextStyle( style: TextStyle(
color: currentMid == data.mid color: currentMid == item.mid
? theme.colorScheme.primary ? theme.colorScheme.primary
: theme.colorScheme.outline, : theme.colorScheme.outline,
height: 1.1, height: 1.1,

View File

@@ -16,9 +16,10 @@ class DynamicsTabController
with AccountMixin { with AccountMixin {
DynamicsTabController({required this.dynamicsType}); DynamicsTabController({required this.dynamicsType});
final DynamicsTabType dynamicsType; final DynamicsTabType dynamicsType;
String offset = '';
int? mid; String? offset;
late final MainController mainController = Get.find<MainController>();
late final mainController = Get.find<MainController>();
final dynamicsController = Get.find<DynamicsController>(); final dynamicsController = Get.find<DynamicsController>();
@override @override
@@ -29,25 +30,25 @@ class DynamicsTabController
@override @override
Future<void> onRefresh() { Future<void> onRefresh() {
if (dynamicsType == DynamicsTabType.all) { if (dynamicsType == .all) {
mainController.setDynCount(); mainController.setDynCount();
} }
offset = ''; offset = null;
return super.onRefresh(); return super.onRefresh();
} }
@override @override
List<DynamicItemModel>? getDataList(DynamicsDataModel response) { List<DynamicItemModel>? getDataList(DynamicsDataModel response) {
offset = response.offset ?? ''; offset = response.offset;
return response.items; return response.items;
} }
@override @override
Future<LoadingState<DynamicsDataModel>> customGetData() => Future<LoadingState<DynamicsDataModel>> customGetData() =>
DynamicsHttp.followDynamic( DynamicsHttp.followDynamic(
type: dynamicsType,
offset: offset, offset: offset,
mid: mid, type: dynamicsType,
hostMid: dynamicsController.hostMid,
tempBannedList: dynamicsController.tempBannedList, tempBannedList: dynamicsController.tempBannedList,
); );
@@ -70,7 +71,7 @@ class DynamicsTabController
} }
void onBlock(int index) { void onBlock(int index) {
if (dynamicsType != DynamicsTabType.up) { if (dynamicsType != .up) {
loadingState loadingState
..value.data!.removeAt(index) ..value.data!.removeAt(index)
..refresh(); ..refresh();

View File

@@ -27,9 +27,7 @@ class DynamicsTabPage extends StatefulWidget {
class _DynamicsTabPageState extends State<DynamicsTabPage> class _DynamicsTabPageState extends State<DynamicsTabPage>
with AutomaticKeepAliveClientMixin, DynMixin { with AutomaticKeepAliveClientMixin, DynMixin {
StreamSubscription? _listener; final dynamicsController = Get.putOrFind(DynamicsController.new);
DynamicsController dynamicsController = Get.putOrFind(DynamicsController.new);
late final DynamicsTabController controller; late final DynamicsTabController controller;
@override @override
@@ -38,38 +36,22 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
@override @override
void initState() { void initState() {
controller = Get.putOrFind( controller = Get.putOrFind(
() => () => DynamicsTabController(dynamicsType: widget.dynamicsType),
DynamicsTabController(dynamicsType: widget.dynamicsType)
..mid = dynamicsController.mid.value,
tag: widget.dynamicsType.name, tag: widget.dynamicsType.name,
); );
super.initState(); super.initState();
if (widget.dynamicsType == DynamicsTabType.up) {
_listener = dynamicsController.mid.listen((mid) {
if (mid != -1) {
controller
..mid = mid
..onReload();
}
});
}
} }
@override Future<void> onRefresh() {
void dispose() { dynamicsController.singleRefresh();
_listener?.cancel(); return controller.onRefresh();
dynamicsController.mid.close();
super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return refreshIndicator( return refreshIndicator(
onRefresh: () { onRefresh: onRefresh,
dynamicsController.queryFollowUp();
return controller.onRefresh();
},
child: CustomScrollView( child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController, controller: controller.scrollController,