Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-02-24 16:54:58 +08:00
parent e88cd12dfa
commit 4a3d827f7a
10 changed files with 133 additions and 102 deletions

View File

@@ -3,7 +3,7 @@ import 'dart:async';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart' show ScrollController;
import 'package:get/get.dart'; import 'package:get/get.dart';
mixin ScrollOrRefreshMixin { mixin ScrollOrRefreshMixin {

View File

@@ -32,7 +32,9 @@ class ActionPanel extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Expanded( Expanded(
child: TextButton.icon( child: Builder(
builder: (context) {
return TextButton.icon(
onPressed: () => showModalBottomSheet( onPressed: () => showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@@ -56,8 +58,12 @@ class ActionPanel extends StatelessWidget {
), ),
style: btnStyle, style: btnStyle,
label: Text( label: Text(
forward.count != null ? NumUtils.numFormat(forward.count) : '转发', forward.count != null
? NumUtils.numFormat(forward.count)
: '转发',
), ),
);
},
), ),
), ),
Expanded( Expanded(
@@ -76,32 +82,40 @@ class ActionPanel extends StatelessWidget {
), ),
), ),
Expanded( Expanded(
child: TextButton.icon( child: Builder(
onPressed: () => RequestUtils.onLikeDynamic(item, () { builder: (context) {
if (context.mounted) { final likeIcon = Icon(
(context as Element?)?.markNeedsBuild();
}
}),
icon: Icon(
like.status! like.status!
? FontAwesomeIcons.solidThumbsUp ? FontAwesomeIcons.solidThumbsUp
: FontAwesomeIcons.thumbsUp, : FontAwesomeIcons.thumbsUp,
size: 16, size: 16,
color: like.status! ? primary : outline, color: like.status! ? primary : outline,
semanticLabel: like.status! ? "已赞" : "点赞", semanticLabel: like.status! ? "已赞" : "点赞",
);
return TextButton.icon(
onPressed: () => RequestUtils.onLikeDynamic(
item,
likeIcon.color == primary,
() {
if (context.mounted) {
(context as Element?)?.markNeedsBuild();
}
},
), ),
icon: likeIcon,
style: btnStyle, style: btnStyle,
label: AnimatedSwitcher( label: AnimatedSwitcher(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
transitionBuilder: (Widget child, Animation<double> animation) { transitionBuilder: (child, animation) =>
return ScaleTransition(scale: animation, child: child); ScaleTransition(scale: animation, child: child),
},
child: Text( child: Text(
like.count != null ? NumUtils.numFormat(like.count) : '点赞', like.count != null ? NumUtils.numFormat(like.count) : '点赞',
key: ValueKey<int?>(like.count), key: ValueKey<int?>(like.count),
style: TextStyle(color: like.status! ? primary : outline), style: TextStyle(color: like.status! ? primary : outline),
), ),
), ),
);
},
), ),
), ),
], ],

View File

@@ -368,18 +368,19 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
required IconData icon, required IconData icon,
required String text, required String text,
required DynamicStat? stat, required DynamicStat? stat,
required VoidCallback onPressed, required ValueChanged<Color> onPressed,
IconData? activatedIcon, IconData? activatedIcon,
}) { }) {
final status = stat?.status == true; final status = stat?.status == true;
final color = status ? primary : outline; final color = status ? primary : outline;
return TextButton.icon( final iconWidget = Icon(
onPressed: onPressed,
icon: Icon(
status ? activatedIcon : icon, status ? activatedIcon : icon,
size: 16, size: 16,
color: color, color: color,
), );
return TextButton.icon(
onPressed: () => onPressed(iconWidget.color!),
icon: iconWidget,
style: btnStyle, style: btnStyle,
label: Text( label: Text(
stat?.count != null ? NumUtils.numFormat(stat!.count) : text, stat?.count != null ? NumUtils.numFormat(stat!.count) : text,
@@ -422,7 +423,7 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
icon: FontAwesomeIcons.shareFromSquare, icon: FontAwesomeIcons.shareFromSquare,
text: '转发', text: '转发',
stat: forward, stat: forward,
onPressed: () => showModalBottomSheet( onPressed: (_) => showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
useSafeArea: true, useSafeArea: true,
@@ -449,7 +450,7 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
icon: CustomIcons.share_node, icon: CustomIcons.share_node,
text: '分享', text: '分享',
stat: null, stat: null,
onPressed: () => Utils.shareText( onPressed: (_) => Utils.shareText(
'${HttpString.dynamicShareBaseUrl}/${controller.dynItem.idStr}', '${HttpString.dynamicShareBaseUrl}/${controller.dynItem.idStr}',
), ),
), ),
@@ -462,8 +463,10 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
activatedIcon: FontAwesomeIcons.solidThumbsUp, activatedIcon: FontAwesomeIcons.solidThumbsUp,
text: '点赞', text: '点赞',
stat: moduleStat?.like, stat: moduleStat?.like,
onPressed: () => RequestUtils.onLikeDynamic( onPressed: (iconColor) =>
RequestUtils.onLikeDynamic(
controller.dynItem, controller.dynItem,
iconColor == primary,
() { () {
if (context.mounted) { if (context.mounted) {
(context as Element).markNeedsBuild(); (context as Element).markNeedsBuild();

View File

@@ -8,6 +8,8 @@ import 'package:PiliPlus/models_new/live/live_second_list/data.dart';
import 'package:PiliPlus/models_new/live/live_second_list/tag.dart'; import 'package:PiliPlus/models_new/live/live_second_list/tag.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
import 'package:flutter/widgets.dart' show ScrollController;
import 'package:get/get.dart'; import 'package:get/get.dart';
class LiveController extends CommonListController with AccountMixin { class LiveController extends CommonListController with AccountMixin {
@@ -32,6 +34,8 @@ class LiveController extends CommonListController with AccountMixin {
final Rx<Pair<LiveCardList?, LiveCardList?>> topState = final Rx<Pair<LiveCardList?, LiveCardList?>> topState =
Pair<LiveCardList?, LiveCardList?>(first: null, second: null).obs; Pair<LiveCardList?, LiveCardList?>(first: null, second: null).obs;
final followController = ScrollController();
@override @override
void checkIsEnd(int length) { void checkIsEnd(int length) {
if (count != null && length >= count!) { if (count != null && length >= count!) {
@@ -87,10 +91,11 @@ class LiveController extends CommonListController with AccountMixin {
page = 1; page = 1;
isEnd = false; isEnd = false;
if (areaIndex.value != 0) { if (areaIndex.value != 0) {
queryTop(); queryTop().whenComplete(followController.jumpToTop);
}
return queryData(); return queryData();
} }
return queryData().whenComplete(followController.jumpToTop);
}
Future<void> queryTop() async { Future<void> queryTop() async {
final res = await LiveHttp.liveFeedIndex(pn: page, moduleSelect: true); final res = await LiveHttp.liveFeedIndex(pn: page, moduleSelect: true);
@@ -143,4 +148,10 @@ class LiveController extends CommonListController with AccountMixin {
@override @override
void onChangeAccount(bool isLogin) => onReload(); void onChangeAccount(bool isLogin) => onReload();
@override
void onClose() {
followController.dispose();
super.onClose();
}
} }

View File

@@ -295,6 +295,7 @@ class _LivePageState extends State<LivePage>
height: 68.0 + textScaler.scale(12), height: 68.0 + textScaler.scale(12),
child: CustomScrollView( child: CustomScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
controller: controller.followController,
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
slivers: [ slivers: [
SliverFixedExtentList.builder( SliverFixedExtentList.builder(

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/pages/common/common_data_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';
import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/login_utils.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_key.dart';
@@ -135,6 +136,7 @@ class MineController extends CommonDataController<FavFolderData, FavFolderData>
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) { bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
favFolderCount = response.response.count; favFolderCount = response.response.count;
loadingState.value = response; loadingState.value = response;
scrollController.jumpToTop();
return true; return true;
} }

View File

@@ -78,7 +78,6 @@ class _MediaPageState extends CommonPageState<MinePage>
child: onBuild( child: onBuild(
ListView( ListView(
padding: const .only(bottom: 100), padding: const .only(bottom: 100),
controller: controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
children: [ children: [
_buildUserInfo(theme, secondary), _buildUserInfo(theme, secondary),
@@ -505,6 +504,7 @@ class _MediaPageState extends CommonPageState<MinePage>
return SizedBox( return SizedBox(
height: 200, height: 200,
child: ListView.separated( child: ListView.separated(
controller: controller.scrollController,
padding: const .only(left: 20, top: 10, right: 20), padding: const .only(left: 20, top: 10, right: 20),
itemCount: response.list.length + (flag ? 1 : 0), itemCount: response.list.length + (flag ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {

View File

@@ -9,14 +9,17 @@ import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart' show ScrollController;
import 'package:get/get.dart'; import 'package:get/get.dart';
class PgcController class PgcController
extends CommonListController<List<PgcIndexItem>?, PgcIndexItem> extends CommonListController<List<PgcIndexItem>?, PgcIndexItem>
with AccountMixin { with AccountMixin {
PgcController({required this.tabType}); PgcController({required this.tabType})
: indexType = tabType == HomeTabType.cinema ? 102 : null;
final HomeTabType tabType; final HomeTabType tabType;
final int? indexType;
late final showPgcTimeline = late final showPgcTimeline =
tabType == HomeTabType.bangumi && Pref.showPgcTimeline; tabType == HomeTabType.bangumi && Pref.showPgcTimeline;
@@ -33,9 +36,6 @@ class PgcController
if (showPgcTimeline) { if (showPgcTimeline) {
queryPgcTimeline(); queryPgcTimeline();
} }
if (accountService.isLogin.value) {
followController = ScrollController();
}
} }
@override @override
@@ -62,7 +62,7 @@ class PgcController
late bool followEnd = false; late bool followEnd = false;
late Rx<LoadingState<List<FavPgcItemModel>?>> followState = late Rx<LoadingState<List<FavPgcItemModel>?>> followState =
LoadingState<List<FavPgcItemModel>?>.loading().obs; LoadingState<List<FavPgcItemModel>?>.loading().obs;
ScrollController? followController; final followController = ScrollController();
// timeline // timeline
late Rx<LoadingState<List<TimelineResult>?>> timelineState = late Rx<LoadingState<List<TimelineResult>?>> timelineState =
@@ -117,7 +117,7 @@ class PgcController
followEnd = true; followEnd = true;
} }
followState.value = Success(list); followState.value = Success(list);
followController?.animToTop(); followController.jumpToTop();
} else if (followState.value case Success(:final response)) { } else if (followState.value case Success(:final response)) {
final currentList = response!..addAll(list); final currentList = response!..addAll(list);
if (currentList.length >= followCount.value) { if (currentList.length >= followCount.value) {
@@ -135,12 +135,12 @@ class PgcController
@override @override
Future<LoadingState<List<PgcIndexItem>?>> customGetData() => PgcHttp.pgcIndex( Future<LoadingState<List<PgcIndexItem>?>> customGetData() => PgcHttp.pgcIndex(
page: page, page: page,
indexType: tabType == HomeTabType.cinema ? 102 : null, indexType: indexType,
); );
@override @override
void onClose() { void onClose() {
followController?.dispose(); followController.dispose();
super.onClose(); super.onClose();
} }

View File

@@ -417,9 +417,7 @@ class _PgcPageState extends State<PgcPage> with AutomaticKeepAliveClientMixin {
? StyleString.safeSpace ? StyleString.safeSpace
: 0, : 0,
), ),
child: PgcCardV( child: PgcCardV(item: response[index]),
item: response[index],
),
); );
}, },
) )

View File

@@ -362,27 +362,29 @@ abstract final class RequestUtils {
// 动态点赞 // 动态点赞
static Future<void> onLikeDynamic( static Future<void> onLikeDynamic(
DynamicItemModel item, DynamicItemModel item,
bool uiStatus,
VoidCallback onSuccess, VoidCallback onSuccess,
) async { ) async {
feedBack(); feedBack();
String dynamicId = item.idStr!;
// 1 已点赞 2 不喜欢 0 未操作 final like = item.modules.moduleStat?.like;
DynamicStat? like = item.modules.moduleStat?.like; final status = like?.status ?? false;
int count = like?.count ?? 0;
bool status = like?.status ?? false; if (status ^ uiStatus) {
int up = status ? 2 : 1; SmartDialog.showToast(status ? '点赞成功' : '取消赞');
final res = await DynamicsHttp.thumbDynamic(dynamicId: dynamicId, up: up); onSuccess();
if (res.isSuccess) { return;
SmartDialog.showToast(!status ? '点赞成功' : '取消赞');
if (up == 1) {
like
?..count = count + 1
..status = true;
} else {
like
?..count = count - 1
..status = false;
} }
final res = await DynamicsHttp.thumbDynamic(
dynamicId: item.idStr!,
up: status ? 2 : 1, // 1 已点赞 2 不喜欢 0 未操作
);
if (res.isSuccess) {
SmartDialog.showToast(status ? '取消赞' : '点赞成功');
like
?..count = (like.count ?? 0) + (status ? -1 : 1)
..status = !status;
onSuccess(); onSuccess();
} else { } else {
res.toast(); res.toast();