diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 04fe6670..a400600f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - gt3_flutter_plugin (0.0.8): + - gt3_flutter_plugin (0.0.9): - Flutter - GT3Captcha-iOS - GT3Captcha-iOS (0.15.8.3) @@ -171,13 +171,13 @@ SPEC CHECKSUMS: flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 + gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart index 2284df6c..b888be17 100644 --- a/lib/models/common/action_type.dart +++ b/lib/models/common/action_type.dart @@ -11,7 +11,6 @@ enum ActionType { dislike, downloadCover, copyLink, - threeAction, // backgroundPlay, // listenVideo, // downloadVideo, diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart index f7d92e17..7a4fd9ba 100644 --- a/lib/pages/setting/pages/action_menu_set.dart +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/action_type.dart'; +import 'package:pilipala/utils/global_data.dart'; import '../../../utils/storage.dart'; class ActionMenuSetPage extends StatefulWidget { @@ -12,14 +13,14 @@ class ActionMenuSetPage extends StatefulWidget { } class _ActionMenuSetPageState extends State { - Box settingStorage = GStrorage.setting; + Box setting = GStrorage.setting; late List actionTypeSort; late List allLabels; @override void initState() { super.initState(); - actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); allLabels = actionMenuConfig; allLabels.sort((a, b) { @@ -36,8 +37,9 @@ class _ActionMenuSetPageState extends State { .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) .map((i) => (i['value'] as ActionType).value) .toList(); - settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); - SmartDialog.showToast('保存成功,下次启动时生效'); + setting.put(SettingBoxKey.actionTypeSort, sortedTabbar); + GlobalData().actionTypeSort = sortedTabbar; + SmartDialog.showToast('操作成功'); } void onReorder(int oldIndex, int newIndex) { diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 20fdada0..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,11 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), - ListTile( - dense: false, - onTap: () => Get.toNamed('/actionMenuSet'), - title: Text('操作菜单设置', style: titleStyle), - ), + // ListTile( + // dense: false, + // onTap: () => Get.toNamed('/actionMenuSet'), + // title: Text('操作菜单设置', style: titleStyle), + // ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9ed04870..9c542f21 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -38,6 +38,8 @@ class VideoIntroController extends GetxController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + // 是否不喜欢 + RxBool hasDisLike = false.obs; Box userInfoCache = GStrorage.userInfo; bool userLogin = false; Rx favFolderData = FavFolderData().obs; @@ -153,36 +155,16 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('🙏 UP已经收到了~'); return false; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('一键三连 给UP送温暖'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('点错了')), - TextButton( - onPressed: () async { - var result = await VideoHttp.oneThree(bvid: bvid); - if (result['status']) { - hasLike.value = result["data"]["like"]; - hasCoin.value = result["data"]["coin"]; - hasFav.value = result["data"]["fav"]; - SmartDialog.showToast('三连成功 🎉'); - } else { - SmartDialog.showToast(result['msg']); - } - SmartDialog.dismiss(); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); + var result = await VideoHttp.oneThree(bvid: bvid); + print('🤣🦴:${result["data"]}'); + if (result['status']) { + hasLike.value = result["data"]["like"]; + hasCoin.value = result["data"]["coin"]; + hasFav.value = result["data"]["fav"]; + SmartDialog.showToast('三连成功'); + } else { + SmartDialog.showToast(result['msg']); + } } // (取消)点赞 @@ -193,9 +175,8 @@ class VideoIntroController extends GetxController { } var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value); if (result['status']) { - // hasLike.value = result["data"] == 1 ? true : false; if (!hasLike.value) { - SmartDialog.showToast('点赞成功 👍'); + SmartDialog.showToast('点赞成功'); hasLike.value = true; videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1; } else if (hasLike.value) { @@ -215,6 +196,10 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } + if (hasCoin.value) { + SmartDialog.showToast('已投过币了'); + return; + } showDialog( context: Get.context!, builder: (context) { @@ -236,7 +221,7 @@ class VideoIntroController extends GetxController { var res = await VideoHttp.coinVideo( bvid: bvid, multiply: _tempThemeValue); if (res['status']) { - SmartDialog.showToast('投币成功 👏'); + SmartDialog.showToast('投币成功'); hasCoin.value = true; videoDetail.value.stat!.coin = videoDetail.value.stat!.coin! + _tempThemeValue; @@ -269,7 +254,7 @@ class VideoIntroController extends GetxController { if (result['status']) { // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } @@ -299,7 +284,7 @@ class VideoIntroController extends GetxController { Get.back(); // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 4f9a8172..ee2eeb2c 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -15,6 +18,7 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; @@ -146,13 +150,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - void Function()? handleState(Future Function() action) { + // 一键三连动画 + late AnimationController _controller; + late Animation _scaleTransition; + final RxDouble _progress = 0.0.obs; + + void Function()? handleState(Future Function() action) { return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); + isProcessing = true; + await action.call(); + isProcessing = false; }; } @@ -169,6 +178,25 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); + + /// 一键三连动画 + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + reverseDuration: const Duration(milliseconds: 300), + vsync: this, + ); + _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) + ..addListener(() async { + _progress.value = _scaleTransition.value - 0.5; + if (_progress.value == 1) { + if (_controller.status == AnimationStatus.completed) { + await videoIntroController.actionOneThree(); + } + _progress.value = 0; + _scaleTransition.removeListener(() {}); + _controller.stop(); + } + }); } // 收藏 @@ -249,6 +277,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); + _controller.dispose(); + _scaleTransition.removeListener(() {}); super.dispose(); } @@ -528,6 +558,157 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } Widget actionGrid(BuildContext context, videoIntroController) { + final actionTypeSort = GlobalData().actionTypeSort; + + Widget progressWidget(progress) { + return SizedBox( + width: 68, + height: 68, + child: CircularProgressIndicator( + value: progress.value, + strokeWidth: 4, + ), + ); + } + + Map menuListWidgets = { + 'like': Obx( + () { + bool likeStatus = videoIntroController.hasLike.value; + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + InkWell( + onTapDown: (details) { + feedBack(); + _controller.forward(); + }, + onTapUp: (TapUpDetails details) { + if (_progress.value == 0) { + feedBack(); + EasyThrottle.throttle( + 'my-throttler', const Duration(milliseconds: 200), () { + videoIntroController.actionLikeVideo(); + }); + } + _controller.reverse(); + }, + borderRadius: StyleString.mdRadius, + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Icon( + key: ValueKey(likeStatus), + likeStatus + ? Icons.thumb_up + : Icons.thumb_up_alt_outlined, + color: likeStatus + ? colorScheme.primary + : colorScheme.outline, + ), + ), + const SizedBox(height: 6), + Text( + widget.videoDetail!.stat!.like!.toString(), + style: TextStyle( + color: likeStatus ? colorScheme.primary : null, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), + ), + ), + ], + ); + }, + ), + 'coin': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: Image.asset('assets/images/coin.png', width: 30), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), + ), + ], + ), + ), + 'collect': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), + ), + ], + ), + ), + 'watchLater': ActionItem( + icon: const Icon(Icons.watch_later_outlined), + onTap: () async { + final res = + await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); + SmartDialog.showToast(res['msg']); + }, + selectStatus: false, + text: '稍后看', + ), + 'share': ActionItem( + icon: const Icon(Icons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + text: '分享', + ), + 'dislike': Obx( + () => ActionItem( + icon: const Icon(Icons.thumb_down_alt_outlined), + selectIcon: const Icon(Icons.thumb_down), + onTap: () {}, + selectStatus: videoIntroController.hasDisLike.value, + text: '不喜欢', + ), + ), + 'downloadCover': ActionItem( + icon: const Icon(Icons.image_outlined), + onTap: () {}, + selectStatus: false, + text: '下载封面', + ), + 'copyLink': ActionItem( + icon: const Icon(Icons.link_outlined), + onTap: () {}, + selectStatus: false, + text: '复制链接', + ), + }; + final List list = []; + for (var i = 0; i < actionTypeSort.length; i++) { + list.add(menuListWidgets[actionTypeSort[i]]!); + } + return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Container( @@ -535,50 +716,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: constraints.maxWidth / 5, child: ListView( scrollDirection: Axis.horizontal, - children: [ - Obx( - () => ActionItem( - icon: const Icon(Icons.thumb_up_alt_outlined), - selectIcon: const Icon(Icons.thumb_up), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - text: widget.videoDetail!.stat!.like!.toString()), - ), - Obx( - () => ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ), - Obx( - () => ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ), - ActionItem( - icon: const Icon(Icons.watch_later_outlined), - onTap: () async { - final res = - await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); - SmartDialog.showToast(res['msg']); - }, - selectStatus: false, - text: '稍后看', - ), - ActionItem( - icon: const Icon(Icons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - text: '分享', - ), - ], + children: list, ), ); }); diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 2a89afe7..af409dfc 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -38,20 +38,29 @@ class ActionItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 4), - icon is Icon - ? Icon( - selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ) - : Image.asset( - 'assets/images/coin.png', - width: 25, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: icon is Icon + ? Icon( + selectStatus + ? selectIcon!.icon ?? icon!.icon + : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + key: ValueKey(selectStatus), + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), const SizedBox(height: 6), Text( text ?? '', @@ -60,7 +69,7 @@ class ActionItem extends StatelessWidget { selectStatus ? Theme.of(context).colorScheme.primary : null, fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, ), - ) + ), ], ), ), diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 29791210..97bff5a5 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -11,7 +11,8 @@ class GlobalData { bool enablePlayerControlAnimation = true; final bool enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - + List actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); // 私有构造函数 GlobalData._();