diff --git a/lib/http/member.dart b/lib/http/member.dart index 2f53466bc..5a2942aa4 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/member_card_info/data.dart'; import 'package:PiliPlus/models/common/member/contribute_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/member/info.dart'; @@ -310,7 +311,10 @@ class MemberHttp { }, ); if (res.data['code'] == 0) { - return {'status': true, 'data': res.data['data']}; + return { + 'status': true, + 'data': MemberCardInfoData.fromJson(res.data['data']) + }; } else { return {'status': false, 'msg': res.data['message']}; } diff --git a/lib/http/video.dart b/lib/http/video.dart index 8de0c6450..1ff9e4344 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -16,6 +16,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_rank/pgc_rank_item_model.dart'; import 'package:PiliPlus/models_new/triple/pgc_triple.dart'; import 'package:PiliPlus/models_new/triple/ugc_triple.dart'; import 'package:PiliPlus/models_new/video/video_ai_conclusion/data.dart'; +import 'package:PiliPlus/models_new/video/video_detail/data.dart'; import 'package:PiliPlus/models_new/video/video_detail/video_detail_response.dart'; import 'package:PiliPlus/models_new/video/video_note_list/data.dart'; import 'package:PiliPlus/models_new/video/video_play_info/data.dart'; @@ -251,30 +252,15 @@ class VideoHttp { } // 视频信息 标题、简介 - static Future videoIntro({required String bvid}) async { + static Future> videoIntro( + {required String bvid}) async { var res = await Request().get(Api.videoIntro, queryParameters: {'bvid': bvid}); - VideoDetailResponse result = VideoDetailResponse.fromJson(res.data); - if (result.code == 0) { - return { - 'status': true, - 'data': result.data!, - }; + VideoDetailResponse data = VideoDetailResponse.fromJson(res.data); + if (data.code == 0) { + return Success(data.data!); } else { - // Map errMap = { - // -400: '请求错误', - // -403: '权限不足', - // -404: '视频资源失效', - // 62002: '稿件不可见', - // 62004: '稿件审核中', - // }; - return { - 'status': false, - 'data': result.data, - 'code': result.code, - 'msg': result.message, - // 'msg': errMap[result.code] ?? '请求异常', - }; + return Error(data.message); } } diff --git a/lib/member_card_info/card.dart b/lib/member_card_info/card.dart new file mode 100644 index 000000000..c6515acac --- /dev/null +++ b/lib/member_card_info/card.dart @@ -0,0 +1,36 @@ +import 'package:PiliPlus/models/model_avatar.dart'; + +class Card { + String? mid; + String? name; + String? face; + int? fans; + int? attention; + BaseOfficialVerify? officialVerify; + Vip? vip; + + Card({ + this.mid, + this.name, + this.face, + this.fans, + this.attention, + this.officialVerify, + this.vip, + }); + + factory Card.fromJson(Map json) => Card( + mid: json['mid'] as String?, + name: json['name'] as String?, + face: json['face'] as String?, + fans: json['fans'] as int?, + attention: json['attention'] as int?, + officialVerify: json['official_verify'] == null + ? null + : BaseOfficialVerify.fromJson( + json['official_verify'] as Map), + vip: json['vip'] == null + ? null + : Vip.fromJson(json['vip'] as Map), + ); +} diff --git a/lib/member_card_info/data.dart b/lib/member_card_info/data.dart new file mode 100644 index 000000000..ab46ceab5 --- /dev/null +++ b/lib/member_card_info/data.dart @@ -0,0 +1,31 @@ +import 'package:PiliPlus/member_card_info/card.dart'; + +class MemberCardInfoData { + Card? card; + bool? following; + int? archiveCount; + int? articleCount; + int? follower; + int? likeNum; + + MemberCardInfoData({ + this.card, + this.following, + this.archiveCount, + this.articleCount, + this.follower, + this.likeNum, + }); + + factory MemberCardInfoData.fromJson(Map json) => + MemberCardInfoData( + card: json['card'] == null + ? null + : Card.fromJson(json['card'] as Map), + following: json['following'] as bool?, + archiveCount: json['archive_count'] as int?, + articleCount: json['article_count'] as int?, + follower: json['follower'] as int?, + likeNum: json['like_num'] as int?, + ); +} diff --git a/lib/pages/common/common_intro_controller.dart b/lib/pages/common/common_intro_controller.dart index 952da7c8e..c39879e7e 100644 --- a/lib/pages/common/common_intro_controller.dart +++ b/lib/pages/common/common_intro_controller.dart @@ -12,18 +12,18 @@ abstract class CommonIntroController extends GetxController { String bvid = Get.parameters['bvid']!; // 是否点赞 - RxBool hasLike = false.obs; + final RxBool hasLike = false.obs; // 投币数量 final RxNum coinNum = RxNum(0); // 是否投币 bool get hasCoin => coinNum.value != 0; // 是否收藏 - RxBool hasFav = false.obs; + final RxBool hasFav = false.obs; - List? videoTags; + final Rx?> videoTags = Rx?>(null); Set? favIds; - Rx favFolderData = FavFolderData().obs; + final Rx favFolderData = FavFolderData().obs; AccountService accountService = Get.find(); @@ -54,10 +54,10 @@ abstract class CommonIntroController extends GetxController { } Future queryVideoTags() async { - videoTags = null; + videoTags.value = null; var result = await UserHttp.videoTags(bvid: bvid); if (result['status']) { - videoTags = result['data']; + videoTags.value = result['data']; } } } diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index 4ff52614a..6517f31d4 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -63,31 +63,30 @@ class VideoDetailController extends GetxController with GetTickerProviderStateMixin { /// 路由传参 String bvid = Get.parameters['bvid']!; - RxInt cid = int.parse(Get.parameters['cid']!).obs; - RxInt danmakuCid = 0.obs; - String heroTag = Get.arguments['heroTag']; - // 视频详情 - RxMap videoItem = {}.obs; + final RxInt cid = int.parse(Get.parameters['cid']!).obs; + final RxInt danmakuCid = 0.obs; + final heroTag = Get.arguments['heroTag']; + final RxString cover = ''.obs; // 视频类型 默认投稿视频 - SearchType videoType = Get.arguments['videoType'] ?? SearchType.video; + final videoType = Get.arguments['videoType'] ?? SearchType.video; /// tabs相关配置 late TabController tabCtr; // 请求返回的视频信息 late PlayUrlModel data; - Rx videoState = LoadingState.loading().obs; + final Rx videoState = LoadingState.loading().obs; /// 播放器配置 画质 音质 解码格式 late VideoQuality currentVideoQa; AudioQuality? currentAudioQa; late VideoDecodeFormatType currentDecodeFormats; // 是否开始自动播放 存在多p的情况下,第二p需要为true - RxBool autoPlay = true.obs; + final RxBool autoPlay = true.obs; // 封面图的展示 - RxBool isShowCover = true.obs; + final RxBool isShowCover = true.obs; - RxInt oid = 0.obs; + final RxInt oid = 0.obs; final scaffoldKey = GlobalKey(); final childKey = GlobalKey(); @@ -116,13 +115,13 @@ class VideoDetailController extends GetxController : plPlayerController.showBangumiReply; int? seasonCid; - late RxInt seasonIndex = 0.obs; + late final RxInt seasonIndex = 0.obs; PlayerStatus? playerStatus; StreamSubscription? positionSubscription; late final scrollKey = GlobalKey(); - late RxString direction = 'horizontal'.obs; + late final RxString direction = 'horizontal'.obs; late final RxDouble scrollRatio = 0.0.obs; late final ScrollController scrollCtr = ScrollController() ..addListener(scrollListener); @@ -235,7 +234,7 @@ class VideoDetailController extends GetxController // 页面来源 稍后再看 收藏夹 String sourceType = 'normal'; late bool _mediaDesc = false; - late RxList mediaList = [].obs; + late final RxList mediaList = [].obs; late String watchLaterTitle = ''; bool get isPlayAll => const ['watchLater', 'fav', 'archive', 'playlist'].contains(sourceType); @@ -255,18 +254,21 @@ class VideoDetailController extends GetxController super.onInit(); var keys = Get.arguments.keys.toList(); if (keys.isNotEmpty) { - if (keys.contains('videoItem')) { + if (keys.contains('pic')) { + cover.value = Get.arguments['pic']; + } else if (keys.contains('videoItem')) { var args = Get.arguments['videoItem']; try { if (args.pic != null && args.pic != '') { - videoItem['pic'] = args.pic; - } else if (args.cover != null && args.cover != '') { - videoItem['pic'] = args.cover; + cover.value = args.pic; } - } catch (_) {} - } - if (keys.contains('pic')) { - videoItem['pic'] = Get.arguments['pic']; + } catch (_) { + try { + if (args.cover != null && args.cover != '') { + cover.value = args.cover; + } + } catch (_) {} + } } } diff --git a/lib/pages/video/introduction/pgc/controller.dart b/lib/pages/video/introduction/pgc/controller.dart index 79d74a8da..41c67e99f 100644 --- a/lib/pages/video/introduction/pgc/controller.dart +++ b/lib/pages/video/introduction/pgc/controller.dart @@ -334,7 +334,7 @@ class PgcIntroController extends CommonIntroController { ..danmakuCid.value = cid ..queryVideoUrl(); if (cover is String && cover.isNotEmpty) { - videoDetailCtr.videoItem['pic'] = cover; + videoDetailCtr.cover.value = cover; } // 重新请求评论 diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index e66e87755..6b54c0816 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -271,7 +271,6 @@ class _PgcIntroPageState extends State onTap: () => handleState(pgcIntroController.actionLikeVideo), onLongPress: pgcIntroController.actionOneThree, selectStatus: pgcIntroController.hasLike.value, - isLoading: false, semanticsLabel: '点赞', text: NumUtil.numFormat(item.stat!.likes), needAnim: true, @@ -297,7 +296,6 @@ class _PgcIntroPageState extends State selectIcon: const Icon(FontAwesomeIcons.b), onTap: () => handleState(pgcIntroController.actionCoinVideo), selectStatus: pgcIntroController.hasCoin, - isLoading: false, semanticsLabel: '投币', text: NumUtil.numFormat(item.stat!.coins), needAnim: true, @@ -312,7 +310,6 @@ class _PgcIntroPageState extends State onLongPress: () => pgcIntroController.showFavBottomSheet(context, type: 'longPress'), selectStatus: pgcIntroController.hasFav.value, - isLoading: false, semanticsLabel: '收藏', text: NumUtil.numFormat(item.stat!.favorite), needAnim: true, @@ -323,7 +320,6 @@ class _PgcIntroPageState extends State selectIcon: const Icon(FontAwesomeIcons.reply), onTap: () => videoDetailCtr.tabCtr.animateTo(1), selectStatus: false, - isLoading: false, semanticsLabel: '评论', text: NumUtil.numFormat(item.stat!.reply), ), @@ -331,7 +327,6 @@ class _PgcIntroPageState extends State icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => pgcIntroController.actionShareVideo(context), selectStatus: false, - isLoading: false, semanticsLabel: '转发', text: NumUtil.numFormat(item.stat!.share), ), diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index 79fe7eda0..5f0e58870 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -9,6 +9,7 @@ import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/video.dart'; +import 'package:PiliPlus/member_card_info/data.dart'; import 'package:PiliPlus/models_new/triple/ugc_triple.dart'; import 'package:PiliPlus/models_new/video/video_ai_conclusion/data.dart'; import 'package:PiliPlus/models_new/video/video_ai_conclusion/model_result.dart'; @@ -42,75 +43,69 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; class VideoIntroController extends CommonIntroController { - // 视频详情 上个页面传入 - Map videoItem = {}; - late final RxMap staffRelations = {}.obs; + String heroTag = ''; - // 视频详情 请求返回 - Rx videoDetail = VideoDetailData().obs; + late ExpandableController expandableCtr; + + final RxBool status = true.obs; + final Rx videoDetail = VideoDetailData().obs; // up主粉丝数 - RxMap userStat = RxMap({'follower': '-'}); + final Rx userStat = MemberCardInfoData().obs; + // 关注状态 默认未关注 + late final RxMap followStatus = {}.obs; + late final RxMap staffRelations = {}.obs; // 是否点踩 - RxBool hasDislike = false.obs; + final RxBool hasDislike = false.obs; // 是否稍后再看 - RxBool hasLater = false.obs; + final RxBool hasLater = false.obs; - // 关注状态 默认未关注 - RxMap followStatus = {}.obs; - - RxInt lastPlayCid = 0.obs; + final RxInt lastPlayCid = 0.obs; // 同时观看 final bool isShowOnlineTotal = Pref.enableOnlineTotal; late final RxString total = '1'.obs; Timer? timer; - String heroTag = ''; - AiConclusionResult? aiConclusionResult; - RxMap queryVideoIntroData = - RxMap({"status": true}); - - ExpandableController? expandableCtr; late final showArgueMsg = Pref.showArgueMsg; late final enableAi = Pref.enableAi; + late final horizontalMemberPage = Pref.horizontalMemberPage; + + AiConclusionResult? aiConclusionResult; @override void onInit() { super.onInit(); + bool alwaysExapndIntroPanel = Pref.alwaysExapndIntroPanel; + expandableCtr = + ExpandableController(initialExpanded: alwaysExapndIntroPanel); + if (!alwaysExapndIntroPanel && Pref.exapndIntroPanelH) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (Get.context!.orientation == Orientation.landscape && + expandableCtr.expanded == false) { + expandableCtr.toggle(); + } + }); + } + try { if (heroTag.isEmpty) { heroTag = Get.arguments['heroTag']; } } catch (_) {} - if (Get.arguments.isNotEmpty) { - if (Get.arguments.containsKey('videoItem')) { - var args = Get.arguments['videoItem']; - var keys = Get.arguments.keys.toList(); - try { - if (args.pic != null && args.pic != '') { - videoItem['pic'] = args.pic; - } else if (args.cover != null && args.cover != '') { - videoItem['pic'] = args.cover; - } - } catch (_) {} - if (args.title is String) { - videoItem['title'] = args.title; - } else { - String str = ''; - for (Map map in args.title) { - str += map['text']; - } - videoItem['title'] = str; + try { + var videoItem = Get.arguments['videoItem']; + if (videoItem != null) { + if (videoItem.title case String e) { + videoDetail.value.title = e; + } else if (videoItem.title case List list) { + videoDetail.value.title = + list.fold('', (prev, val) => prev + val['text']); } - videoItem - ..['stat'] = keys.contains('stat') ? args.stat : null - ..['pubdate'] = keys.contains('pubdate') ? args.pubdate : null - ..['owner'] = keys.contains('owner') ? args.owner : null; } - } + } catch (_) {} lastPlayCid.value = int.parse(Get.parameters['cid']!); startTimer(); queryVideoIntro(); @@ -119,9 +114,9 @@ class VideoIntroController extends CommonIntroController { // 获取视频简介&分p Future queryVideoIntro() async { queryVideoTags(); - var result = await VideoHttp.videoIntro(bvid: bvid); - if (result['status']) { - VideoDetailData data = result['data']; + var res = await VideoHttp.videoIntro(bvid: bvid); + if (res.isSuccess) { + VideoDetailData data = res.data; videoPlayerServiceHandler.onVideoDetailChange(data, data.cid!, heroTag); if (videoDetail.value.ugcSeason?.id == data.ugcSeason?.id) { // keep reversed season @@ -134,15 +129,13 @@ class VideoIntroController extends CommonIntroController { ..isPageReversed = videoDetail.value.isPageReversed; } videoDetail.value = data; - videoItem['staff'] = data.staff; try { final videoDetailController = Get.find(tag: heroTag); - if (videoDetailController.videoItem['pic'] == null || - videoDetailController.videoItem['pic'] == '' || + if (videoDetailController.cover.value.isEmpty || (videoDetailController.videoUrl.isNullOrEmpty && !videoDetailController.isQuerying)) { - videoDetailController.videoItem['pic'] = data.pic; + videoDetailController.cover.value = data.pic ?? ''; } if (videoDetailController.showReply) { try { @@ -157,12 +150,12 @@ class VideoIntroController extends CommonIntroController { lastPlayCid.value == 0) { lastPlayCid.value = videoDetail.value.pages!.first.cid!; } - queryUserStat(); + queryUserStat(data.staff); } else { - SmartDialog.showToast( - "${result['code']} ${result['msg']} ${result['data']}"); + res.toast(); + status.value = false; } - queryVideoIntroData.addAll(result); + if (accountService.isLogin.value) { queryAllStatus(); queryFollowStatus(); @@ -170,15 +163,11 @@ class VideoIntroController extends CommonIntroController { } // 获取up主粉丝数 - Future queryUserStat() async { - if (videoItem['staff']?.isNotEmpty == true) { + Future queryUserStat(List? staff) async { + if (staff?.isNotEmpty == true) { Request().get( Api.relations, - queryParameters: { - 'fids': (videoItem['staff'] as List) - .map((item) => item.mid) - .join(',') - }, + queryParameters: {'fids': staff!.map((item) => item.mid).join(',')}, ).then((res) { if (res.data['code'] == 0) { staffRelations.addAll({ @@ -194,7 +183,7 @@ class VideoIntroController extends CommonIntroController { var result = await MemberHttp.memberCardInfo(mid: videoDetail.value.owner!.mid!); if (result['status']) { - userStat.addAll(result['data']); + userStat.value = result['data']; } } } @@ -323,8 +312,7 @@ class VideoIntroController extends CommonIntroController { return; } - int copyright = - (queryVideoIntroData['data'] as VideoDetailData?)?.copyright ?? 1; + int copyright = videoDetail.value.copyright ?? 1; if ((copyright != 1 && coinNum.value >= 1) || coinNum.value >= 2) { SmartDialog.showToast('达到投币上限啦~'); return; @@ -528,7 +516,8 @@ class VideoIntroController extends CommonIntroController { // 查询关注状态 Future queryFollowStatus() async { - if (videoDetail.value.owner == null) { + if (videoDetail.value.owner == null || + videoDetail.value.staff?.isNotEmpty == true) { return; } var result = await UserHttp.hasFollow(videoDetail.value.owner!.mid!); @@ -588,7 +577,7 @@ class VideoIntroController extends CommonIntroController { PageUtils.toVideoPage( 'bvid=$bvid&cid=$cid', arguments: { - if (cover != null) 'pic': cover, + 'pic': cover, 'heroTag': Utils.makeHeroTag(bvid), }, ); @@ -608,8 +597,10 @@ class VideoIntroController extends CommonIntroController { ..queryVideoUrl(); if (this.bvid != bvid) { + aiConclusionResult = null; + if (cover is String && cover.isNotEmpty) { - videoDetailCtr.videoItem['pic'] = cover; + videoDetailCtr.cover.value = cover; } // 重新请求相关视频 @@ -672,8 +663,7 @@ class VideoIntroController extends CommonIntroController { @override void onClose() { canelTimer(); - expandableCtr?.dispose(); - expandableCtr = null; + expandableCtr.dispose(); super.onClose(); } diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index 37826a770..2f24dd906 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -25,12 +25,10 @@ import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; -import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart' show HapticFeedback; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -56,81 +54,280 @@ class VideoIntroPanel extends StatefulWidget { class _VideoIntroPanelState extends State with AutomaticKeepAliveClientMixin { - late VideoIntroController videoIntroController; - - @override - bool get wantKeepAlive => true; - - @override - void initState() { - super.initState(); - - videoIntroController = Get.put(VideoIntroController(), tag: widget.heroTag) - ..heroTag = widget.heroTag; - } - - @override - Widget build(BuildContext context) { - super.build(context); - return Obx( - () => videoIntroController.videoDetail.value.title == null - ? VideoInfo( - isLoading: true, - videoIntroController: videoIntroController, - heroTag: widget.heroTag, - showAiBottomSheet: widget.showAiBottomSheet, - showEpisodes: widget.showEpisodes, - onShowMemberPage: widget.onShowMemberPage, - ) - : VideoInfo( - key: ValueKey(widget.heroTag), - isLoading: false, - videoIntroController: videoIntroController, - heroTag: widget.heroTag, - showAiBottomSheet: widget.showAiBottomSheet, - showEpisodes: widget.showEpisodes, - onShowMemberPage: widget.onShowMemberPage, - ), - ); - } -} - -class VideoInfo extends StatefulWidget { - final bool isLoading; - final String heroTag; - final Function showAiBottomSheet; - final Function showEpisodes; - final ValueChanged onShowMemberPage; - final VideoIntroController videoIntroController; - - const VideoInfo({ - super.key, - this.isLoading = false, - required this.heroTag, - required this.showAiBottomSheet, - required this.showEpisodes, - required this.onShowMemberPage, - required this.videoIntroController, - }); - - @override - State createState() => _VideoInfoState(); -} - -class _VideoInfoState extends State { - late final VideoDetailController videoDetailCtr; - - Map get videoItem => videoIntroController.videoItem; - VideoIntroController get videoIntroController => widget.videoIntroController; - VideoDetailData get videoDetail => - widget.videoIntroController.videoDetail.value; + late VideoIntroController introController; + late final VideoDetailController videoDetailCtr = + Get.find(tag: widget.heroTag); late final _coinKey = GlobalKey(); late final _favKey = GlobalKey(); bool isProcessing = false; - late final _horizontalMemberPage = Pref.horizontalMemberPage; + @override + void initState() { + super.initState(); + introController = Get.put(VideoIntroController(), tag: widget.heroTag) + ..heroTag = widget.heroTag; + } + + @override + Widget build(BuildContext context) { + super.build(context); + final ThemeData theme = Theme.of(context); + const expandTheme = ExpandableThemeData( + animationDuration: Duration(milliseconds: 300), + scrollAnimationDuration: Duration(milliseconds: 300), + crossFadePoint: 0, + fadeCurve: Curves.ease, + sizeCurve: Curves.linear, + ); + return SliverLayoutBuilder( + builder: (context, constraints) { + bool isHorizontal = context.orientation == Orientation.landscape && + constraints.crossAxisExtent > + constraints.viewportMainAxisExtent * 1.25; + return SliverPadding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + top: 10, + ), + sliver: Obx( + () { + VideoDetailData videoDetail = introController.videoDetail.value; + bool isLoading = videoDetail.bvid == null; + return SliverToBoxAdapter( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (isLoading) { + return; + } + feedBack(); + introController.expandableCtr.toggle(); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () {}, + child: Row( + children: [ + if (videoDetail.staff.isNullOrEmpty) ...[ + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: _buildAvatar( + theme, + () { + int? mid = videoDetail.owner?.mid; + if (mid != null) { + feedBack(); + if (context.orientation == + Orientation.landscape && + introController + .horizontalMemberPage) { + widget.onShowMemberPage(mid); + } else { + Get.toNamed( + '/member?mid=$mid&from_view_aid=${videoDetailCtr.oid.value}', + ); + } + } + }, + ), + ), + ), + followButton(context, theme), + ] else + Expanded( + child: SelfSizedHorizontalList( + gapSize: 25, + itemCount: videoDetail.staff!.length, + childBuilder: (index) { + return _buildStaff( + theme, + videoDetail.owner?.mid, + videoDetail.staff![index], + ); + }, + ), + ), + if (isHorizontal) ...[ + const SizedBox(width: 10), + Expanded( + child: actionGrid(context, isLoading, + videoDetail, introController)), + ] + ], + ), + ), + const SizedBox(height: 8), + ExpandablePanel( + controller: introController.expandableCtr, + collapsed: GestureDetector( + onLongPress: () { + Feedback.forLongPress(context); + Utils.copyText(videoDetail.title ?? ''); + }, + child: _buildVideoTitle(theme, videoDetail), + ), + expanded: GestureDetector( + onLongPress: () { + Feedback.forLongPress(context); + Utils.copyText(videoDetail.title ?? ''); + }, + child: _buildVideoTitle(theme, videoDetail, + isExpand: true), + ), + theme: expandTheme, + ), + const SizedBox(height: 8), + Stack( + clipBehavior: Clip.none, + children: [ + _buildInfo(theme, videoDetail), + if (introController.enableAi) _aiBtn, + ], + ), + if (introController.videoDetail.value.argueInfo?.argueMsg + ?.isNotEmpty == + true && + introController.showArgueMsg) ...[ + const SizedBox(height: 2), + Text.rich( + TextSpan( + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + size: 13, + Icons.error_outline, + color: theme.colorScheme.outline, + ), + ), + const WidgetSpan(child: SizedBox(width: 2)), + TextSpan( + text: + '${introController.videoDetail.value.argueInfo!.argueMsg}', + ) + ], + ), + style: TextStyle( + fontSize: 12, + color: theme.colorScheme.outline, + ), + ), + ], + ExpandablePanel( + controller: introController.expandableCtr, + collapsed: const SizedBox.shrink(), + expanded: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + GestureDetector( + onTap: () => Utils.copyText( + '${introController.videoDetail.value.bvid}'), + child: Text( + introController.videoDetail.value.bvid ?? '', + style: TextStyle( + fontSize: 14, + color: theme.colorScheme.secondary, + ), + ), + ), + if (introController + .videoDetail.value.descV2?.isNotEmpty == + true) ...[ + const SizedBox(height: 8), + SelectableText.rich( + style: const TextStyle( + height: 1.4, + ), + TextSpan( + children: [ + buildContent(theme, + introController.videoDetail.value), + ], + ), + ), + ], + Obx(() { + if (introController + .videoTags.value.isNullOrEmpty) { + return const SizedBox.shrink(); + } + return _buildTags( + introController.videoTags.value!); + }) + ], + ), + theme: expandTheme, + ), + Obx( + () => introController.status.value + ? const SizedBox.shrink() + : Center( + child: TextButton.icon( + icon: const Icon(Icons.refresh), + onPressed: () { + introController.status.value = true; + introController.queryVideoIntro(); + if (videoDetailCtr.videoUrl.isNullOrEmpty && + !videoDetailCtr.isQuerying) { + videoDetailCtr.queryVideoUrl(); + } + }, + label: const Text("点此重新加载"), + ), + ), + ), + // 点赞收藏转发 布局样式2 + if (!isHorizontal) ...[ + const SizedBox(height: 8), + actionGrid( + context, isLoading, videoDetail, introController), + ], + // 合集 + if (!isLoading && + videoDetail.ugcSeason != null && + (context.orientation != Orientation.landscape || + (context.orientation == Orientation.landscape && + !videoDetailCtr.plPlayerController + .horizontalSeasonPanel))) + SeasonPanel( + heroTag: widget.heroTag, + changeFuc: introController.changeSeasonOrbangu, + showEpisodes: widget.showEpisodes, + videoIntroController: introController, + ), + if (!isLoading && + videoDetail.pages != null && + videoDetail.pages!.length > 1 && + (context.orientation != Orientation.landscape || + (context.orientation == Orientation.landscape && + !videoDetailCtr.plPlayerController + .horizontalSeasonPanel))) ...[ + PagesPanel( + heroTag: widget.heroTag, + videoIntroController: introController, + bvid: introController.bvid, + showEpisodes: widget.showEpisodes, + ), + ], + ], + ), + ), + ); + }, + ), + ); + }, + ); + } WidgetSpan _labelWidget(String text, Color bgColor, Color textColor) { return WidgetSpan( @@ -163,7 +360,8 @@ class _VideoInfoState extends State { ); } - Widget _buildVideoTitle(ThemeData theme, [bool isExpand = false]) { + Widget _buildVideoTitle(ThemeData theme, VideoDetailData videoDetail, + {bool isExpand = false}) { late final isDark = theme.brightness == Brightness.dark; Widget child() => Text.rich( TextSpan( @@ -238,8 +436,7 @@ class _VideoInfoState extends State { ), const TextSpan(text: ' '), ], - TextSpan( - text: '${videoDetail.title ?? videoItem['title'] ?? ''}'), + TextSpan(text: videoDetail.title ?? ''), ], ), maxLines: isExpand ? null : 2, @@ -260,294 +457,12 @@ class _VideoInfoState extends State { } } - @override - void initState() { - super.initState(); - - videoDetailCtr = Get.find(tag: widget.heroTag); - - if (videoIntroController.expandableCtr == null) { - bool alwaysExapndIntroPanel = Pref.alwaysExapndIntroPanel; - - videoIntroController.expandableCtr = ExpandableController( - initialExpanded: alwaysExapndIntroPanel, - ); - - if (!alwaysExapndIntroPanel && Pref.exapndIntroPanelH) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (context.orientation == Orientation.landscape && - videoIntroController.expandableCtr?.expanded == false) { - videoIntroController.expandableCtr?.toggle(); - } - }); - } - } - } - - // 视频介绍 - void showIntroDetail() { - if (widget.isLoading) { - return; - } - feedBack(); - videoIntroController.expandableCtr?.toggle(); - } - - // 用户主页 - void onPushMember() { - feedBack(); - int? mid = - !widget.isLoading ? videoDetail.owner?.mid : videoItem['owner']?.mid; - if (mid != null) { - if (context.orientation == Orientation.landscape && - _horizontalMemberPage) { - widget.onShowMemberPage(mid); - } else { - Get.toNamed( - '/member?mid=$mid&from_view_aid=${videoDetailCtr.oid.value}', - ); - } - } - } - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - return SliverLayoutBuilder( - builder: (BuildContext context, SliverConstraints constraints) { - bool isHorizontal = context.orientation == Orientation.landscape && - constraints.crossAxisExtent > - constraints.viewportMainAxisExtent * 1.25; - return SliverPadding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - top: 10, - ), - sliver: SliverToBoxAdapter( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: showIntroDetail, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () {}, - child: Row( - children: [ - if (videoItem['staff'] == null) ...[ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: _buildAvatar(theme), - ), - ), - followButton(context, theme), - ] else - Expanded( - child: SelfSizedHorizontalList( - gapSize: 25, - itemCount: videoItem['staff'].length, - childBuilder: (index) { - return _buildStaff( - theme, videoItem['staff'][index]); - }, - ), - ), - if (isHorizontal) ...[ - const SizedBox(width: 10), - Expanded( - child: actionGrid(context, videoIntroController)), - ] - ], - ), - ), - const SizedBox(height: 8), - ExpandablePanel( - controller: videoIntroController.expandableCtr, - collapsed: GestureDetector( - onLongPress: () { - Feedback.forLongPress(context); - Utils.copyText( - '${videoDetail.title ?? videoItem['title'] ?? ''}'); - }, - child: _buildVideoTitle(theme), - ), - expanded: GestureDetector( - onLongPress: () { - Feedback.forLongPress(context); - Utils.copyText( - '${videoDetail.title ?? videoItem['title'] ?? ''}'); - }, - child: _buildVideoTitle(theme, true), - ), - theme: const ExpandableThemeData( - animationDuration: Duration(milliseconds: 300), - scrollAnimationDuration: Duration(milliseconds: 300), - crossFadePoint: 0, - fadeCurve: Curves.ease, - sizeCurve: Curves.linear, - ), - ), - const SizedBox(height: 8), - Stack( - clipBehavior: Clip.none, - children: [ - _buildInfo(theme), - if (videoIntroController.enableAi) _aiBtn, - ], - ), - if (videoIntroController.videoDetail.value.argueInfo?.argueMsg - ?.isNotEmpty == - true && - videoIntroController.showArgueMsg) ...[ - const SizedBox(height: 2), - Text.rich( - TextSpan( - children: [ - WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Icon( - size: 13, - Icons.error_outline, - color: theme.colorScheme.outline, - ), - ), - const WidgetSpan(child: SizedBox(width: 2)), - TextSpan( - text: - '${videoIntroController.videoDetail.value.argueInfo!.argueMsg}', - ) - ], - ), - style: TextStyle( - fontSize: 12, - color: theme.colorScheme.outline, - ), - ), - ], - ExpandablePanel( - controller: videoIntroController.expandableCtr, - collapsed: const SizedBox.shrink(), - expanded: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 8), - GestureDetector( - onTap: () => Utils.copyText( - '${videoIntroController.videoDetail.value.bvid}'), - child: Text( - videoIntroController.videoDetail.value.bvid ?? '', - style: TextStyle( - fontSize: 14, - color: theme.colorScheme.secondary, - ), - ), - ), - if (videoIntroController - .videoDetail.value.descV2?.isNotEmpty == - true) ...[ - const SizedBox(height: 8), - SelectableText.rich( - style: const TextStyle( - height: 1.4, - ), - TextSpan( - children: [ - buildContent(theme, - videoIntroController.videoDetail.value), - ], - ), - ), - ], - if (videoIntroController.videoTags?.isNotEmpty == - true) ...[ - _buildTags(videoIntroController.videoTags!), - ], - ], - ), - theme: const ExpandableThemeData( - animationDuration: Duration(milliseconds: 300), - scrollAnimationDuration: Duration(milliseconds: 300), - crossFadePoint: 0, - fadeCurve: Curves.ease, - sizeCurve: Curves.linear, - ), - ), - Obx( - () => videoIntroController.queryVideoIntroData["status"] - ? const SizedBox.shrink() - : Center( - child: TextButton.icon( - icon: const Icon(Icons.refresh), - onPressed: () { - videoIntroController - .queryVideoIntroData["status"] = true; - videoIntroController.queryVideoIntro(); - if (videoDetailCtr.videoUrl.isNullOrEmpty && - !videoDetailCtr.isQuerying) { - videoDetailCtr.queryVideoUrl(); - } - }, - label: const Text("点此重新加载"), - ), - ), - ), - // 点赞收藏转发 布局样式2 - if (!isHorizontal) ...[ - const SizedBox(height: 8), - actionGrid(context, videoIntroController), - ], - // 合集 - if (!widget.isLoading && - videoDetail.ugcSeason != null && - (context.orientation != Orientation.landscape || - (context.orientation == Orientation.landscape && - !videoDetailCtr - .plPlayerController.horizontalSeasonPanel))) - Obx( - () => SeasonPanel( - key: ValueKey(videoIntroController.videoDetail.value), - heroTag: widget.heroTag, - changeFuc: videoIntroController.changeSeasonOrbangu, - showEpisodes: widget.showEpisodes, - videoIntroController: videoIntroController, - ), - ), - if (!widget.isLoading && - videoDetail.pages != null && - videoDetail.pages!.length > 1 && - (context.orientation != Orientation.landscape || - (context.orientation == Orientation.landscape && - !videoDetailCtr.plPlayerController - .horizontalSeasonPanel))) ...[ - Obx( - () => PagesPanel( - key: ValueKey(videoIntroController.videoDetail.value), - heroTag: widget.heroTag, - videoIntroController: videoIntroController, - bvid: videoIntroController.bvid, - showEpisodes: widget.showEpisodes, - ), - ), - ], - ], - ), - ), - ), - ); - }, - ); - } - Widget followButton(BuildContext context, ThemeData t) { return Obx( () { - int attr = videoIntroController.followStatus['attribute'] ?? 0; + int attr = introController.followStatus['attribute'] ?? 0; return TextButton( - onPressed: () => videoIntroController.actionRelationMod(context), + onPressed: () => introController.actionRelationMod(context), style: TextButton.styleFrom( tapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: const VisualDensity(vertical: -2.8), @@ -575,7 +490,11 @@ class _VideoInfoState extends State { } Widget actionGrid( - BuildContext context, VideoIntroController videoIntroController) { + BuildContext context, + bool isLoading, + VideoDetailData videoDetail, + VideoIntroController videoIntroController, + ) { return SizedBox( height: 48, child: Row( @@ -588,11 +507,9 @@ class _VideoInfoState extends State { onLongPress: () => handleState(videoIntroController.actionOneThree), selectStatus: videoIntroController.hasLike.value, - isLoading: widget.isLoading, semanticsLabel: '点赞', - text: !widget.isLoading - ? NumUtil.numFormat(videoDetail.stat!.like!) - : '-', + text: + !isLoading ? NumUtil.numFormat(videoDetail.stat!.like!) : '-', needAnim: true, hasTriple: videoIntroController.hasLike.value && videoIntroController.hasCoin && @@ -615,7 +532,6 @@ class _VideoInfoState extends State { selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), onTap: () => handleState(videoIntroController.actionDislikeVideo), selectStatus: videoIntroController.hasDislike.value, - isLoading: widget.isLoading, semanticsLabel: '点踩', text: "点踩", ), @@ -627,11 +543,9 @@ class _VideoInfoState extends State { selectIcon: const Icon(FontAwesomeIcons.b), onTap: () => handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin, - isLoading: widget.isLoading, semanticsLabel: '投币', - text: !widget.isLoading - ? NumUtil.numFormat(videoDetail.stat!.coin!) - : '-', + text: + !isLoading ? NumUtil.numFormat(videoDetail.stat!.coin!) : '-', needAnim: true, ), ), @@ -644,9 +558,8 @@ class _VideoInfoState extends State { onLongPress: () => videoIntroController .showFavBottomSheet(context, type: 'longPress'), selectStatus: videoIntroController.hasFav.value, - isLoading: widget.isLoading, semanticsLabel: '收藏', - text: !widget.isLoading + text: !isLoading ? NumUtil.numFormat(videoDetail.stat!.favorite!) : '-', needAnim: true, @@ -658,7 +571,6 @@ class _VideoInfoState extends State { selectIcon: const Icon(FontAwesomeIcons.solidClock), onTap: () => handleState(videoIntroController.viewLater), selectStatus: videoIntroController.hasLater.value, - isLoading: widget.isLoading, semanticsLabel: '再看', text: '再看', ), @@ -667,11 +579,9 @@ class _VideoInfoState extends State { icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => videoIntroController.actionShareVideo(context), selectStatus: false, - isLoading: widget.isLoading, semanticsLabel: '分享', - text: !widget.isLoading - ? NumUtil.numFormat(videoDetail.stat!.share!) - : '分享', + text: + !isLoading ? NumUtil.numFormat(videoDetail.stat!.share!) : '分享', ), ], ), @@ -766,16 +676,13 @@ class _VideoInfoState extends State { return TextSpan(children: spanChildren); } - Widget _buildStaff(ThemeData theme, Staff item) { + Widget _buildStaff(ThemeData theme, int? ownerMid, Staff item) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { - int? ownerMid = !widget.isLoading - ? videoDetail.owner?.mid - : videoItem['owner']?.mid; if (item.mid == ownerMid && context.orientation == Orientation.landscape && - _horizontalMemberPage) { + introController.horizontalMemberPage) { widget.onShowMemberPage(ownerMid); } else { Get.toNamed( @@ -817,40 +724,38 @@ class _VideoInfoState extends State { Positioned( top: 0, right: -6, - child: Obx(() => - videoIntroController.staffRelations['status'] == true && - videoIntroController - .staffRelations['${item.mid}'] == - null - ? Material( - type: MaterialType.transparency, - shape: const CircleBorder(), - child: InkWell( - customBorder: const CircleBorder(), - onTap: () => RequestUtils.actionRelationMod( - context: context, - mid: item.mid, - isFollow: false, - callback: (val) { - videoIntroController - .staffRelations['${item.mid}'] = true; - }, - ), - child: Ink( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: theme.colorScheme.secondaryContainer, - shape: BoxShape.circle, - ), - child: Icon( - MdiIcons.plus, - size: 16, - color: theme.colorScheme.onSecondaryContainer, - ), - ), + child: Obx(() => introController.staffRelations['status'] == + true && + introController.staffRelations['${item.mid}'] == null + ? Material( + type: MaterialType.transparency, + shape: const CircleBorder(), + child: InkWell( + customBorder: const CircleBorder(), + onTap: () => RequestUtils.actionRelationMod( + context: context, + mid: item.mid, + isFollow: false, + callback: (val) { + introController.staffRelations['${item.mid}'] = + true; + }, + ), + child: Ink( + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: theme.colorScheme.secondaryContainer, + shape: BoxShape.circle, ), - ) - : const SizedBox.shrink()), + child: Icon( + MdiIcons.plus, + size: 16, + color: theme.colorScheme.onSecondaryContainer, + ), + ), + ), + ) + : const SizedBox.shrink()), ), ], ), @@ -884,84 +789,70 @@ class _VideoInfoState extends State { ); } - Widget _buildAvatar(ThemeData theme) => GestureDetector( + Widget _buildAvatar(ThemeData theme, VoidCallback onPushMember) => + GestureDetector( onTap: onPushMember, behavior: HitTestBehavior.opaque, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Obx(() => PendantAvatar( - avatar: videoIntroController.userStat['card']?['face'], + child: Obx( + () { + final userStat = introController.userStat.value; + final isVip = (userStat.card?.vip?.status ?? 0) > 0; + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + PendantAvatar( + avatar: userStat.card?.face, size: 35, badgeSize: 14, - isVip: (videoIntroController.userStat['card']?['vip'] - ?['status'] ?? - -1) > - 0, - officialType: videoIntroController.userStat['card'] - ?['official_verify']?['type'], - // garbPendantImage: videoIntroController.userStat.value['card']?['pendant']?['image'], - )), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Obx( - () => Text( - videoIntroController.userStat['card']?['name'] ?? "", - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13, - color: (videoIntroController.userStat['card']?['vip'] - ?['status'] ?? - -1) > - 0 && - videoIntroController.userStat['card']?['vip'] - ?['type'] == - 2 - ? context.vipColor - : null, - ), - ), + isVip: isVip, + officialType: userStat.card?.officialVerify?.type, ), - const SizedBox(height: 0), - Obx( - () => Text( - '${NumUtil.numFormat(videoIntroController.userStat['follower'])}粉丝 ${videoIntroController.userStat['archive_count'] != null ? '${NumUtil.numFormat(videoIntroController.userStat['archive_count'])}视频' : ''}', - style: TextStyle( - fontSize: 12, - color: theme.colorScheme.outline, + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + userStat.card?.name ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 13, + color: isVip && userStat.card?.vip?.type == 2 + ? context.vipColor + : null, + ), ), - ), + const SizedBox(height: 0), + Text( + '${NumUtil.numFormat(userStat.follower)}粉丝 ${'${NumUtil.numFormat(userStat.archiveCount)}视频'}', + style: TextStyle( + fontSize: 12, + color: theme.colorScheme.outline, + ), + ), + ], ), ], - ), - ], + ); + }, ), ); - Widget _buildInfo(ThemeData theme) => Row( + Widget _buildInfo(ThemeData theme, VideoDetailData videoDetail) => Row( spacing: 10, children: [ StatWidget( type: StatType.play, - value: !widget.isLoading - ? videoDetail.stat?.view - : videoItem['stat']?.view, + value: videoDetail.stat?.view, color: theme.colorScheme.outline, ), StatWidget( type: StatType.danmaku, - value: !widget.isLoading - ? videoDetail.stat?.danmaku - : videoItem['stat']?.danmu, + value: videoDetail.stat?.danmaku, color: theme.colorScheme.outline, ), Text( - DateUtil.format( - !widget.isLoading ? videoDetail.pubdate : videoItem['pubdate'], - ), + DateUtil.format(videoDetail.pubdate), style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -974,10 +865,10 @@ class _VideoInfoState extends State { color: theme.colorScheme.outline, semanticLabel: '无痕', ), - if (videoIntroController.isShowOnlineTotal) + if (introController.isShowOnlineTotal) Obx( () => Text( - '${videoIntroController.total.value}人在看', + '${introController.total.value}人在看', style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -996,17 +887,15 @@ class _VideoInfoState extends State { label: 'AI总结', child: GestureDetector( onTap: () async { - if (videoIntroController.aiConclusionResult == null) { - await videoIntroController.aiConclusion(); + if (introController.aiConclusionResult == null) { + await introController.aiConclusion(); } - if (videoIntroController.aiConclusionResult == null) { + if (introController.aiConclusionResult == null) { return; } - if (videoIntroController - .aiConclusionResult!.summary?.isNotEmpty == + if (introController.aiConclusionResult!.summary?.isNotEmpty == true || - videoIntroController - .aiConclusionResult!.outline?.isNotEmpty == + introController.aiConclusionResult!.outline?.isNotEmpty == true) { widget.showAiBottomSheet(); } else { @@ -1046,4 +935,7 @@ class _VideoInfoState extends State { ), ); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/pages/video/introduction/ugc/widgets/action_item.dart b/lib/pages/video/introduction/ugc/widgets/action_item.dart index af9c0101b..790751329 100644 --- a/lib/pages/video/introduction/ugc/widgets/action_item.dart +++ b/lib/pages/video/introduction/ugc/widgets/action_item.dart @@ -11,7 +11,6 @@ class ActionItem extends StatefulWidget { final Icon? selectIcon; final VoidCallback? onTap; final VoidCallback? onLongPress; - final bool? isLoading; final String? text; final bool selectStatus; final String semanticsLabel; @@ -26,7 +25,6 @@ class ActionItem extends StatefulWidget { this.selectIcon, this.onTap, this.onLongPress, - this.isLoading, this.text, this.selectStatus = false, this.needAnim = false, @@ -178,26 +176,22 @@ class ActionItemState extends State ], ), if (widget.text != null) - AnimatedOpacity( - opacity: widget.isLoading! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition(scale: animation, child: child); - }, - child: Text( - widget.text!, - key: ValueKey(widget.text!), - style: TextStyle( - color: widget.selectStatus - ? theme.colorScheme.primary - : theme.colorScheme.outline, - fontSize: theme.textTheme.labelSmall!.fontSize, - ), - semanticsLabel: "", + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + widget.text!, + key: ValueKey(widget.text!), + style: TextStyle( + color: widget.selectStatus + ? theme.colorScheme.primary + : theme.colorScheme.outline, + fontSize: theme.textTheme.labelSmall!.fontSize, ), + semanticsLabel: "", ), ), ], diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index c1617a2c0..fc993f903 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -751,7 +751,7 @@ class _VideoDetailPageVState extends State context, [ videoDetailController - .videoItem['pic'] + .cover.value ], ); break; @@ -772,8 +772,7 @@ class _VideoDetailPageVState extends State child: Text('查看笔记'), ), if (videoDetailController - .videoItem['pic'] != - null) + .cover.value.isNotEmpty) const PopupMenuItem( value: 'savePic', child: Text('保存封面'), @@ -1288,7 +1287,7 @@ class _VideoDetailPageVState extends State case 'savePic': ImageUtil.downloadImg( context, - [videoDetailController.videoItem['pic']], + [videoDetailController.cover.value], ); break; } @@ -1304,7 +1303,7 @@ class _VideoDetailPageVState extends State value: 'note', child: Text('查看笔记'), ), - if (videoDetailController.videoItem['pic'] != null) + if (videoDetailController.cover.value.isNotEmpty) const PopupMenuItem( value: 'savePic', child: Text('保存封面'), @@ -1584,10 +1583,8 @@ class _VideoDetailPageVState extends State onTap: handlePlay, child: Obx( () => CachedNetworkImage( - imageUrl: (videoDetailController.videoItem['pic'] - as String?) - ?.http2https ?? - '', + imageUrl: + videoDetailController.cover.value.http2https, width: videoWidth, height: videoHeight, fit: BoxFit.cover, @@ -1857,7 +1854,7 @@ class _VideoDetailPageVState extends State videoIntroController: videoIntroController, type: EpisodeType.part, list: [videoIntroController.videoDetail.value.pages!], - cover: videoDetailController.videoItem['pic'], + cover: videoDetailController.cover.value, bvid: videoDetailController.bvid, aid: IdUtils.bv2av(videoDetailController.bvid), cid: videoDetailController.cid.value, @@ -1905,7 +1902,7 @@ class _VideoDetailPageVState extends State videoIntroController: videoIntroController, type: EpisodeType.season, initialTabIndex: videoDetailController.seasonIndex.value, - cover: videoDetailController.videoItem['pic'], + cover: videoDetailController.cover.value, seasonId: videoIntroController.videoDetail.value.ugcSeason!.id, list: videoIntroController @@ -2034,7 +2031,7 @@ class _VideoDetailPageVState extends State : episodes is List ? EpisodeType.part : EpisodeType.pgc, - cover: videoDetailController.videoItem['pic'], + cover: videoDetailController.cover.value, enableSlide: enableSlide, initialTabIndex: index ?? 0, bvid: bvid, diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index afbdeadc8..cff7a1ce2 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -145,14 +145,14 @@ class HeaderControlState extends State { leading: const Icon(Icons.note_alt_outlined, size: 20), title: const Text('查看笔记', style: titleStyle), ), - if (widget.videoDetailCtr.videoItem['pic'] != null) + if (widget.videoDetailCtr.cover.value.isNotEmpty) ListTile( dense: true, onTap: () { Get.back(); ImageUtil.downloadImg( context, - [widget.videoDetailCtr.videoItem['pic']], + [widget.videoDetailCtr.cover.value], ); }, leading: const Icon(Icons.image_outlined, size: 20), diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index aedd8892e..d3ad86f01 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -167,7 +167,6 @@ class PiliScheme { PageUtils.toVideoPage( 'bvid=$bvid&cid=${queryParameters['cid']}', arguments: { - 'pic': null, 'heroTag': Utils.makeHeroTag(aid), if (queryParameters['dm_progress'] != null) 'progress': int.tryParse(queryParameters['dm_progress']!), @@ -877,7 +876,6 @@ class PiliScheme { PageUtils.toVideoPage( 'bvid=$bvid&cid=$cid', arguments: { - 'pic': null, 'heroTag': Utils.makeHeroTag(aid), if (progress != null) 'progress': int.tryParse(progress), }, diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index 8282b9d06..e599312f4 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -55,7 +55,6 @@ class UrlUtils { PageUtils.toVideoPage( 'bvid=$bvid&cid=$cid', arguments: { - 'pic': '', 'heroTag': Utils.makeHeroTag(bvid), }, preventDuplicates: false,