diff --git a/lib/pages/common/common_controller.dart b/lib/pages/common/common_controller.dart index 98eb357d1..e961bf93b 100644 --- a/lib/pages/common/common_controller.dart +++ b/lib/pages/common/common_controller.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.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'; mixin ScrollOrRefreshMixin { diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 655b6af94..ba33c7b31 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -32,32 +32,38 @@ class ActionPanel extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Expanded( - child: TextButton.icon( - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - builder: (_) => RepostPanel( - item: item, - onSuccess: () { - int count = forward.count ?? 0; - forward.count = count + 1; - if (context.mounted) { - (context as Element?)?.markNeedsBuild(); - } - }, - ), - ), - icon: Icon( - FontAwesomeIcons.shareFromSquare, - size: 16, - color: outline, - semanticLabel: "转发", - ), - style: btnStyle, - label: Text( - forward.count != null ? NumUtils.numFormat(forward.count) : '转发', - ), + child: Builder( + builder: (context) { + return TextButton.icon( + onPressed: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + builder: (_) => RepostPanel( + item: item, + onSuccess: () { + int count = forward.count ?? 0; + forward.count = count + 1; + if (context.mounted) { + (context as Element?)?.markNeedsBuild(); + } + }, + ), + ), + icon: Icon( + FontAwesomeIcons.shareFromSquare, + size: 16, + color: outline, + semanticLabel: "转发", + ), + style: btnStyle, + label: Text( + forward.count != null + ? NumUtils.numFormat(forward.count) + : '转发', + ), + ); + }, ), ), Expanded( @@ -76,32 +82,40 @@ class ActionPanel extends StatelessWidget { ), ), Expanded( - child: TextButton.icon( - onPressed: () => RequestUtils.onLikeDynamic(item, () { - if (context.mounted) { - (context as Element?)?.markNeedsBuild(); - } - }), - icon: Icon( - like.status! - ? FontAwesomeIcons.solidThumbsUp - : FontAwesomeIcons.thumbsUp, - size: 16, - color: like.status! ? primary : outline, - semanticLabel: like.status! ? "已赞" : "点赞", - ), - style: btnStyle, - label: AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: (Widget child, Animation animation) { - return ScaleTransition(scale: animation, child: child); - }, - child: Text( - like.count != null ? NumUtils.numFormat(like.count) : '点赞', - key: ValueKey(like.count), - style: TextStyle(color: like.status! ? primary : outline), - ), - ), + child: Builder( + builder: (context) { + final likeIcon = Icon( + like.status! + ? FontAwesomeIcons.solidThumbsUp + : FontAwesomeIcons.thumbsUp, + size: 16, + color: like.status! ? primary : outline, + semanticLabel: like.status! ? "已赞" : "点赞", + ); + return TextButton.icon( + onPressed: () => RequestUtils.onLikeDynamic( + item, + likeIcon.color == primary, + () { + if (context.mounted) { + (context as Element?)?.markNeedsBuild(); + } + }, + ), + icon: likeIcon, + style: btnStyle, + label: AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: (child, animation) => + ScaleTransition(scale: animation, child: child), + child: Text( + like.count != null ? NumUtils.numFormat(like.count) : '点赞', + key: ValueKey(like.count), + style: TextStyle(color: like.status! ? primary : outline), + ), + ), + ); + }, ), ), ], diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index b393005fd..e247734b8 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -368,18 +368,19 @@ class _DynamicDetailPageState extends CommonDynPageState { required IconData icon, required String text, required DynamicStat? stat, - required VoidCallback onPressed, + required ValueChanged onPressed, IconData? activatedIcon, }) { final status = stat?.status == true; final color = status ? primary : outline; + final iconWidget = Icon( + status ? activatedIcon : icon, + size: 16, + color: color, + ); return TextButton.icon( - onPressed: onPressed, - icon: Icon( - status ? activatedIcon : icon, - size: 16, - color: color, - ), + onPressed: () => onPressed(iconWidget.color!), + icon: iconWidget, style: btnStyle, label: Text( stat?.count != null ? NumUtils.numFormat(stat!.count) : text, @@ -422,7 +423,7 @@ class _DynamicDetailPageState extends CommonDynPageState { icon: FontAwesomeIcons.shareFromSquare, text: '转发', stat: forward, - onPressed: () => showModalBottomSheet( + onPressed: (_) => showModalBottomSheet( context: context, isScrollControlled: true, useSafeArea: true, @@ -449,7 +450,7 @@ class _DynamicDetailPageState extends CommonDynPageState { icon: CustomIcons.share_node, text: '分享', stat: null, - onPressed: () => Utils.shareText( + onPressed: (_) => Utils.shareText( '${HttpString.dynamicShareBaseUrl}/${controller.dynItem.idStr}', ), ), @@ -462,14 +463,16 @@ class _DynamicDetailPageState extends CommonDynPageState { activatedIcon: FontAwesomeIcons.solidThumbsUp, text: '点赞', stat: moduleStat?.like, - onPressed: () => RequestUtils.onLikeDynamic( - controller.dynItem, - () { - if (context.mounted) { - (context as Element).markNeedsBuild(); - } - }, - ), + onPressed: (iconColor) => + RequestUtils.onLikeDynamic( + controller.dynItem, + iconColor == primary, + () { + if (context.mounted) { + (context as Element).markNeedsBuild(); + } + }, + ), ); }, ), diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart index 0e470cbfd..7ddacfeed 100644 --- a/lib/pages/live/controller.dart +++ b/lib/pages/live/controller.dart @@ -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/pages/common/common_list_controller.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'; class LiveController extends CommonListController with AccountMixin { @@ -32,6 +34,8 @@ class LiveController extends CommonListController with AccountMixin { final Rx> topState = Pair(first: null, second: null).obs; + final followController = ScrollController(); + @override void checkIsEnd(int length) { if (count != null && length >= count!) { @@ -87,9 +91,10 @@ class LiveController extends CommonListController with AccountMixin { page = 1; isEnd = false; if (areaIndex.value != 0) { - queryTop(); + queryTop().whenComplete(followController.jumpToTop); + return queryData(); } - return queryData(); + return queryData().whenComplete(followController.jumpToTop); } Future queryTop() async { @@ -143,4 +148,10 @@ class LiveController extends CommonListController with AccountMixin { @override void onChangeAccount(bool isLogin) => onReload(); + + @override + void onClose() { + followController.dispose(); + super.onClose(); + } } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index b571845b9..248ac325e 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -295,6 +295,7 @@ class _LivePageState extends State height: 68.0 + textScaler.scale(12), child: CustomScrollView( scrollDirection: Axis.horizontal, + controller: controller.followController, physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverFixedExtentList.builder( diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index c4bdf6f7b..9a561eb08 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -10,6 +10,7 @@ import 'package:PiliPlus/pages/common/common_data_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/accounts.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/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; @@ -135,6 +136,7 @@ class MineController extends CommonDataController bool customHandleResponse(bool isRefresh, Success response) { favFolderCount = response.response.count; loadingState.value = response; + scrollController.jumpToTop(); return true; } diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index e537ee273..14b9351e9 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -78,7 +78,6 @@ class _MediaPageState extends CommonPageState child: onBuild( ListView( padding: const .only(bottom: 100), - controller: controller.scrollController, physics: const AlwaysScrollableScrollPhysics(), children: [ _buildUserInfo(theme, secondary), @@ -505,6 +504,7 @@ class _MediaPageState extends CommonPageState return SizedBox( height: 200, child: ListView.separated( + controller: controller.scrollController, padding: const .only(left: 20, top: 10, right: 20), itemCount: response.list.length + (flag ? 1 : 0), itemBuilder: (context, index) { diff --git a/lib/pages/pgc/controller.dart b/lib/pages/pgc/controller.dart index 032f28957..897c0113d 100644 --- a/lib/pages/pgc/controller.dart +++ b/lib/pages/pgc/controller.dart @@ -9,14 +9,17 @@ import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.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'; class PgcController extends CommonListController?, PgcIndexItem> with AccountMixin { - PgcController({required this.tabType}); + PgcController({required this.tabType}) + : indexType = tabType == HomeTabType.cinema ? 102 : null; + final HomeTabType tabType; + final int? indexType; late final showPgcTimeline = tabType == HomeTabType.bangumi && Pref.showPgcTimeline; @@ -33,9 +36,6 @@ class PgcController if (showPgcTimeline) { queryPgcTimeline(); } - if (accountService.isLogin.value) { - followController = ScrollController(); - } } @override @@ -62,7 +62,7 @@ class PgcController late bool followEnd = false; late Rx?>> followState = LoadingState?>.loading().obs; - ScrollController? followController; + final followController = ScrollController(); // timeline late Rx?>> timelineState = @@ -117,7 +117,7 @@ class PgcController followEnd = true; } followState.value = Success(list); - followController?.animToTop(); + followController.jumpToTop(); } else if (followState.value case Success(:final response)) { final currentList = response!..addAll(list); if (currentList.length >= followCount.value) { @@ -135,12 +135,12 @@ class PgcController @override Future?>> customGetData() => PgcHttp.pgcIndex( page: page, - indexType: tabType == HomeTabType.cinema ? 102 : null, + indexType: indexType, ); @override void onClose() { - followController?.dispose(); + followController.dispose(); super.onClose(); } diff --git a/lib/pages/pgc/view.dart b/lib/pages/pgc/view.dart index 2c8cc3779..b1dfae1e3 100644 --- a/lib/pages/pgc/view.dart +++ b/lib/pages/pgc/view.dart @@ -417,9 +417,7 @@ class _PgcPageState extends State with AutomaticKeepAliveClientMixin { ? StyleString.safeSpace : 0, ), - child: PgcCardV( - item: response[index], - ), + child: PgcCardV(item: response[index]), ); }, ) diff --git a/lib/utils/request_utils.dart b/lib/utils/request_utils.dart index a1a392e53..b5ae32b3b 100644 --- a/lib/utils/request_utils.dart +++ b/lib/utils/request_utils.dart @@ -362,27 +362,29 @@ abstract final class RequestUtils { // 动态点赞 static Future onLikeDynamic( DynamicItemModel item, + bool uiStatus, VoidCallback onSuccess, ) async { feedBack(); - String dynamicId = item.idStr!; - // 1 已点赞 2 不喜欢 0 未操作 - DynamicStat? like = item.modules.moduleStat?.like; - int count = like?.count ?? 0; - bool status = like?.status ?? false; - int up = status ? 2 : 1; - final res = await DynamicsHttp.thumbDynamic(dynamicId: dynamicId, up: up); + + final like = item.modules.moduleStat?.like; + final status = like?.status ?? false; + + if (status ^ uiStatus) { + SmartDialog.showToast(status ? '点赞成功' : '取消赞'); + onSuccess(); + return; + } + + final res = await DynamicsHttp.thumbDynamic( + dynamicId: item.idStr!, + up: status ? 2 : 1, // 1 已点赞 2 不喜欢 0 未操作 + ); if (res.isSuccess) { - SmartDialog.showToast(!status ? '点赞成功' : '取消赞'); - if (up == 1) { - like - ?..count = count + 1 - ..status = true; - } else { - like - ?..count = count - 1 - ..status = false; - } + SmartDialog.showToast(status ? '取消赞' : '点赞成功'); + like + ?..count = (like.count ?? 0) + (status ? -1 : 1) + ..status = !status; onSuccess(); } else { res.toast();