diff --git a/lib/common/widgets/scroll_physics.dart b/lib/common/widgets/scroll_physics.dart index ddf7a6f0..8f7ba2db 100644 --- a/lib/common/widgets/scroll_physics.dart +++ b/lib/common/widgets/scroll_physics.dart @@ -1,4 +1,3 @@ -import 'package:PiliPlus/pages/member_video/controller.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; @@ -46,14 +45,18 @@ class CustomTabBarViewClampingScrollPhysics extends ClampingScrollPhysics { SpringDescription get spring => CustomSpringDescription(); } -class MemberVideoScrollPhysics extends AlwaysScrollableScrollPhysics { - const MemberVideoScrollPhysics({super.parent, required this.controller}); +mixin ReloadMixin { + late bool reload = false; +} - final MemberVideoCtr controller; +class ReloadScrollPhysics extends AlwaysScrollableScrollPhysics { + const ReloadScrollPhysics({super.parent, required this.controller}); + + final ReloadMixin controller; @override - MemberVideoScrollPhysics applyTo(ScrollPhysics? ancestor) { - return MemberVideoScrollPhysics( + ReloadScrollPhysics applyTo(ScrollPhysics? ancestor) { + return ReloadScrollPhysics( parent: buildParent(ancestor), controller: controller); } diff --git a/lib/pages/member_video/controller.dart b/lib/pages/member_video/controller.dart index 547eef5a..b3529110 100644 --- a/lib/pages/member_video/controller.dart +++ b/lib/pages/member_video/controller.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/member.dart'; import 'package:PiliPlus/http/search.dart'; @@ -14,7 +15,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; class MemberVideoCtr - extends CommonListController { + extends CommonListController + with ReloadMixin { MemberVideoCtr({ required this.type, required this.mid, @@ -213,8 +215,6 @@ class MemberVideoCtr } } - bool reload = false; - @override Future onReload() { reload = true; diff --git a/lib/pages/member_video/view.dart b/lib/pages/member_video/view.dart index a2b0fef7..6edc59d5 100644 --- a/lib/pages/member_video/view.dart +++ b/lib/pages/member_video/view.dart @@ -65,7 +65,7 @@ class _MemberVideoState extends State refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( - physics: MemberVideoScrollPhysics(controller: _controller), + physics: ReloadScrollPhysics(controller: _controller), slivers: [ SliverPadding( padding: EdgeInsets.only( diff --git a/lib/pages/save_panel/view.dart b/lib/pages/save_panel/view.dart index fe7e211c..0f4f861e 100644 --- a/lib/pages/save_panel/view.dart +++ b/lib/pages/save_panel/view.dart @@ -88,10 +88,11 @@ class _SavePanelState extends State { try { final heroTag = Get.arguments?['heroTag']; late final ctr = Get.find(tag: heroTag); - cover = ctr.videoDetail.value.pic; - title = ctr.videoDetail.value.title; - pubdate = ctr.videoDetail.value.pubdate; - uname = ctr.videoDetail.value.owner?.name; + final videoDetail = ctr.videoDetail.value; + cover = videoDetail.pic; + title = videoDetail.title; + pubdate = videoDetail.pubdate; + uname = videoDetail.owner?.name; } catch (_) {} uri = 'bilibili://video/${_item.oid}?comment_root_id=${hasRoot ? _item.root : _item.id}${hasRoot ? '&comment_secondary_id=${_item.id}' : ''}'; diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index 92a256b5..12fa49ad 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/fav.dart'; @@ -43,7 +44,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -class VideoIntroController extends CommonIntroController { +class VideoIntroController extends CommonIntroController with ReloadMixin { String heroTag = ''; late ExpandableController expandableCtr; @@ -177,11 +178,11 @@ class VideoIntroController extends CommonIntroController { } }); } else { - if (videoDetail.value.owner == null) { + final mid = videoDetail.value.owner?.mid; + if (mid == null) { return; } - var result = - await MemberHttp.memberCardInfo(mid: videoDetail.value.owner!.mid!); + var result = await MemberHttp.memberCardInfo(mid: mid); if (result['status']) { userStat.value = result['data']; } @@ -402,6 +403,7 @@ class VideoIntroController extends CommonIntroController { showDialog( context: context, builder: (_) { + final videoDetail = this.videoDetail.value; String videoUrl = '${HttpString.baseUrl}/video/$bvid'; return AlertDialog( clipBehavior: Clip.hardEdge, @@ -439,8 +441,8 @@ class VideoIntroController extends CommonIntroController { ), onTap: () { Get.back(); - Utils.shareText('${videoDetail.value.title} ' - 'UP主: ${videoDetail.value.owner!.name!}' + Utils.shareText('${videoDetail.title} ' + 'UP主: ${videoDetail.owner!.name!}' ' - $videoUrl'); }, ), @@ -457,11 +459,11 @@ class VideoIntroController extends CommonIntroController { isScrollControlled: true, useSafeArea: true, builder: (context) => RepostPanel( - rid: videoDetail.value.aid, + rid: videoDetail.aid, dynType: 8, - pic: videoDetail.value.pic, - title: videoDetail.value.title, - uname: videoDetail.value.owner?.name, + pic: videoDetail.pic, + title: videoDetail.title, + uname: videoDetail.owner?.name, ), ); }, @@ -478,13 +480,13 @@ class VideoIntroController extends CommonIntroController { PageUtils.pmShare( context, content: { - "id": videoDetail.value.aid!.toString(), - "title": videoDetail.value.title!, - "headline": videoDetail.value.title!, + "id": videoDetail.aid!.toString(), + "title": videoDetail.title!, + "headline": videoDetail.title!, "source": 5, - "thumb": videoDetail.value.pic!, - "author": videoDetail.value.owner!.name!, - "author_id": videoDetail.value.owner!.mid!.toString(), + "thumb": videoDetail.pic!, + "author": videoDetail.owner!.name!, + "author_id": videoDetail.owner!.mid!.toString(), }, ); } catch (e) { @@ -599,6 +601,7 @@ class VideoIntroController extends CommonIntroController { ..queryVideoUrl(); if (this.bvid != bvid) { + reload = true; aiConclusionResult = null; if (cover is String && cover.isNotEmpty) { @@ -675,15 +678,16 @@ class VideoIntroController extends CommonIntroController { bool isPages = false; final videoDetailCtr = Get.find(tag: heroTag); + final videoDetail = this.videoDetail.value; - if (!skipPages && (videoDetail.value.pages?.length ?? 0) > 1) { + if (!skipPages && (videoDetail.pages?.length ?? 0) > 1) { isPages = true; - final List pages = videoDetail.value.pages!; + final List pages = videoDetail.pages!; episodes.addAll(pages); } else if (videoDetailCtr.isPlayAll) { episodes.addAll(videoDetailCtr.mediaList); - } else if (videoDetail.value.ugcSeason != null) { - final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; + } else if (videoDetail.ugcSeason != null) { + final UgcSeason ugcSeason = videoDetail.ugcSeason!; final List sections = ugcSeason.sections!; for (int i = 0; i < sections.length; i++) { final List episodesList = sections[i].episodes!; @@ -694,9 +698,9 @@ class VideoIntroController extends CommonIntroController { final int currentIndex = episodes.indexWhere((e) => e.cid == (skipPages - ? videoDetail.value.isPageReversed == true - ? videoDetail.value.pages!.last.cid - : videoDetail.value.pages!.first.cid + ? videoDetail.isPageReversed == true + ? videoDetail.pages!.last.cid + : videoDetail.pages!.first.cid : lastPlayCid.value)); int prevIndex = currentIndex - 1; final PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat; @@ -704,7 +708,7 @@ class VideoIntroController extends CommonIntroController { // 列表循环 if (prevIndex < 0) { if (isPages && - (videoDetailCtr.isPlayAll || videoDetail.value.ugcSeason != null)) { + (videoDetailCtr.isPlayAll || videoDetail.ugcSeason != null)) { return prevPlay(true); } if (playRepeat == PlayRepeat.listCycle) { @@ -726,16 +730,17 @@ class VideoIntroController extends CommonIntroController { final List episodes = []; bool isPages = false; final videoDetailCtr = Get.find(tag: heroTag); + final videoDetail = this.videoDetail.value; // part -> playall -> season - if (!skipPages && (videoDetail.value.pages?.length ?? 0) > 1) { + if (!skipPages && (videoDetail.pages?.length ?? 0) > 1) { isPages = true; - final List pages = videoDetail.value.pages!; + final List pages = videoDetail.pages!; episodes.addAll(pages); } else if (videoDetailCtr.isPlayAll) { episodes.addAll(videoDetailCtr.mediaList); - } else if (videoDetail.value.ugcSeason != null) { - final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; + } else if (videoDetail.ugcSeason != null) { + final UgcSeason ugcSeason = videoDetail.ugcSeason!; final List sections = ugcSeason.sections!; for (int i = 0; i < sections.length; i++) { final List episodesList = sections[i].episodes!; @@ -757,9 +762,9 @@ class VideoIntroController extends CommonIntroController { final int currentIndex = episodes.indexWhere((e) => e.cid == (skipPages - ? videoDetail.value.isPageReversed == true - ? videoDetail.value.pages!.last.cid - : videoDetail.value.pages!.first.cid + ? videoDetail.isPageReversed == true + ? videoDetail.pages!.last.cid + : videoDetail.pages!.first.cid : videoDetailCtr.cid.value)); int nextIndex = currentIndex + 1; @@ -773,7 +778,7 @@ class VideoIntroController extends CommonIntroController { // 列表循环 if (nextIndex >= episodes.length) { if (isPages && - (videoDetailCtr.isPlayAll || videoDetail.value.ugcSeason != null)) { + (videoDetailCtr.isPlayAll || videoDetail.ugcSeason != null)) { return nextPlay(true); } diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index f51be307..c591c33e 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; -import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart'; +import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/common/widgets/stat/stat.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; @@ -96,6 +96,7 @@ class _VideoIntroPanelState extends State () { VideoDetailData videoDetail = introController.videoDetail.value; bool isLoading = videoDetail.bvid == null; + int? mid = videoDetail.owner?.mid; return SliverToBoxAdapter( child: GestureDetector( behavior: HitTestBehavior.opaque, @@ -121,7 +122,6 @@ class _VideoIntroPanelState extends State child: _buildAvatar( theme, () { - int? mid = videoDetail.owner?.mid; if (mid != null) { feedBack(); if (context.orientation == @@ -142,16 +142,16 @@ class _VideoIntroPanelState extends State followButton(context, theme), ] else Expanded( - child: SelfSizedHorizontalList( - gapSize: 25, - itemCount: videoDetail.staff!.length, - childBuilder: (index) { - return _buildStaff( - theme, - videoDetail.owner?.mid, - videoDetail.staff![index], - ); - }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + physics: ReloadScrollPhysics( + controller: introController), + child: Row( + spacing: 25, + children: videoDetail.staff! + .map((e) => _buildStaff(theme, mid, e)) + .toList(), + ), ), ), if (isHorizontal) ...[ @@ -194,9 +194,7 @@ class _VideoIntroPanelState extends State if (introController.enableAi) _aiBtn, ], ), - if (introController.videoDetail.value.argueInfo?.argueMsg - ?.isNotEmpty == - true && + if (videoDetail.argueInfo?.argueMsg?.isNotEmpty == true && introController.showArgueMsg) ...[ const SizedBox(height: 2), Text.rich( @@ -212,8 +210,7 @@ class _VideoIntroPanelState extends State ), const WidgetSpan(child: SizedBox(width: 2)), TextSpan( - text: - '${introController.videoDetail.value.argueInfo!.argueMsg}', + text: '${videoDetail.argueInfo!.argueMsg}', ) ], ), @@ -232,19 +229,17 @@ class _VideoIntroPanelState extends State children: [ const SizedBox(height: 8), GestureDetector( - onTap: () => Utils.copyText( - '${introController.videoDetail.value.bvid}'), + onTap: () => + Utils.copyText('${videoDetail.bvid}'), child: Text( - introController.videoDetail.value.bvid ?? '', + videoDetail.bvid ?? '', style: TextStyle( fontSize: 14, color: theme.colorScheme.secondary, ), ), ), - if (introController - .videoDetail.value.descV2?.isNotEmpty == - true) ...[ + if (videoDetail.descV2?.isNotEmpty == true) ...[ const SizedBox(height: 8), SelectableText.rich( style: const TextStyle( @@ -252,8 +247,7 @@ class _VideoIntroPanelState extends State ), TextSpan( children: [ - buildContent(theme, - introController.videoDetail.value), + buildContent(theme, videoDetail), ], ), ), diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 286b8226..983e5be2 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -1831,90 +1831,35 @@ class _VideoDetailPageVState extends State ); } - Widget get seasonPanel => Column( - children: [ - if ((videoIntroController.videoDetail.value.pages?.length ?? 0) > 1) - if (videoIntroController.videoDetail.value.ugcSeason != null) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 14), - child: PagesPanel( - heroTag: heroTag, - videoIntroController: videoIntroController, - bvid: videoIntroController.bvid, - showEpisodes: showEpisodes, - ), - ) - else - Expanded( - child: Obx( - () => EpisodePanel( - heroTag: heroTag, - enableSlide: false, - videoIntroController: videoIntroController, - type: EpisodeType.part, - list: [videoIntroController.videoDetail.value.pages!], - cover: videoDetailController.cover.value, - bvid: videoDetailController.bvid, - aid: IdUtils.bv2av(videoDetailController.bvid), - cid: videoDetailController.cid.value, - isReversed: - videoIntroController.videoDetail.value.isPageReversed, - changeFucCall: videoDetailController.videoType == - SearchType.media_bangumi - ? pgcIntroController.changeSeasonOrbangu - : videoIntroController.changeSeasonOrbangu, - showTitle: false, - isSupportReverse: videoDetailController.videoType != - SearchType.media_bangumi, - onReverse: () => onReversePlay( - bvid: videoDetailController.bvid, - aid: IdUtils.bv2av(videoDetailController.bvid), - isSeason: false, - ), - ), - ), - ), - if (videoIntroController.videoDetail.value.ugcSeason != null) ...[ - if ((videoIntroController.videoDetail.value.pages?.length ?? 0) > - 1) ...[ - const SizedBox(height: 8), - Divider( - height: 1, - color: themeData.colorScheme.outline.withValues(alpha: 0.1), - ), - ], + Widget get seasonPanel { + final videoDetail = videoIntroController.videoDetail.value; + return Column( + children: [ + if ((videoDetail.pages?.length ?? 0) > 1) + if (videoDetail.ugcSeason != null) Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: SeasonPanel( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: PagesPanel( heroTag: heroTag, - onTap: false, - changeFuc: videoIntroController.changeSeasonOrbangu, - showEpisodes: showEpisodes, videoIntroController: videoIntroController, + bvid: videoIntroController.bvid, + showEpisodes: showEpisodes, ), - ), + ) + else Expanded( child: Obx( () => EpisodePanel( heroTag: heroTag, enableSlide: false, videoIntroController: videoIntroController, - type: EpisodeType.season, - initialTabIndex: videoDetailController.seasonIndex.value, + type: EpisodeType.part, + list: [videoDetail.pages!], cover: videoDetailController.cover.value, - seasonId: - videoIntroController.videoDetail.value.ugcSeason!.id, - list: videoIntroController - .videoDetail.value.ugcSeason!.sections!, bvid: videoDetailController.bvid, aid: IdUtils.bv2av(videoDetailController.bvid), - cid: videoDetailController.seasonCid ?? 0, - isReversed: videoIntroController - .videoDetail - .value - .ugcSeason! - .sections![videoDetailController.seasonIndex.value] - .isReversed, + cid: videoDetailController.cid.value, + isReversed: videoDetail.isPageReversed, changeFucCall: videoDetailController.videoType == SearchType.media_bangumi ? pgcIntroController.changeSeasonOrbangu @@ -1925,14 +1870,68 @@ class _VideoDetailPageVState extends State onReverse: () => onReversePlay( bvid: videoDetailController.bvid, aid: IdUtils.bv2av(videoDetailController.bvid), - isSeason: true, + isSeason: false, ), ), ), ), + if (videoDetail.ugcSeason != null) ...[ + if ((videoDetail.pages?.length ?? 0) > 1) ...[ + const SizedBox(height: 8), + Divider( + height: 1, + color: themeData.colorScheme.outline.withValues(alpha: 0.1), + ), ], + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: SeasonPanel( + heroTag: heroTag, + onTap: false, + changeFuc: videoIntroController.changeSeasonOrbangu, + showEpisodes: showEpisodes, + videoIntroController: videoIntroController, + ), + ), + Expanded( + child: Obx( + () => EpisodePanel( + heroTag: heroTag, + enableSlide: false, + videoIntroController: videoIntroController, + type: EpisodeType.season, + initialTabIndex: videoDetailController.seasonIndex.value, + cover: videoDetailController.cover.value, + seasonId: videoDetail.ugcSeason!.id, + list: videoDetail.ugcSeason!.sections!, + bvid: videoDetailController.bvid, + aid: IdUtils.bv2av(videoDetailController.bvid), + cid: videoDetailController.seasonCid ?? 0, + isReversed: videoIntroController + .videoDetail + .value + .ugcSeason! + .sections![videoDetailController.seasonIndex.value] + .isReversed, + changeFucCall: + videoDetailController.videoType == SearchType.media_bangumi + ? pgcIntroController.changeSeasonOrbangu + : videoIntroController.changeSeasonOrbangu, + showTitle: false, + isSupportReverse: + videoDetailController.videoType != SearchType.media_bangumi, + onReverse: () => onReversePlay( + bvid: videoDetailController.bvid, + aid: IdUtils.bv2av(videoDetailController.bvid), + isSeason: true, + ), + ), + ), + ), ], - ); + ], + ); + } Widget videoReplyPanel([bool needCtr = true]) => Obx( () => VideoReplyPanel( @@ -2108,10 +2107,11 @@ class _VideoDetailPageVState extends State ); } + final videoDetail = videoIntroController.videoDetail.value; if (isSeason) { // reverse season - final item = videoIntroController.videoDetail.value.ugcSeason! - .sections![videoDetailController.seasonIndex.value]; + final item = videoDetail + .ugcSeason!.sections![videoDetailController.seasonIndex.value]; item ..isReversed = !item.isReversed ..episodes = item.episodes!.reversed.toList(); @@ -2136,16 +2136,15 @@ class _VideoDetailPageVState extends State } } else { // reverse part - final item = videoIntroController.videoDetail.value; - item - ..isPageReversed = !item.isPageReversed - ..pages = item.pages!.reversed.toList(); + videoDetail + ..isPageReversed = !videoDetail.isPageReversed + ..pages = videoDetail.pages!.reversed.toList(); if (!videoDetailController.plPlayerController.reverseFromFirst) { // keep current episode videoDetailController.cid.refresh(); } else { // switch to first episode - var episode = videoIntroController.videoDetail.value.pages!.first; + var episode = videoDetail.pages!.first; if (episode.cid != videoDetailController.cid.value) { changeEpisode(episode); } else { diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 4f2681a0..2a289f72 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -1876,20 +1876,16 @@ class HeaderControlState extends State { return Obx( () { String title; - - if (videoIntroController - .videoDetail.value.videos == - 1) { - title = videoIntroController - .videoDetail.value.title!; + final videoDetail = + videoIntroController.videoDetail.value; + if (videoDetail.videos == 1) { + title = videoDetail.title!; } else { - title = videoIntroController - .videoDetail.value.pages + title = videoDetail.pages ?.firstWhereOrNull((e) => e.cid == videoDetailCtr.cid.value) ?.pagePart ?? - videoIntroController - .videoDetail.value.title!; + videoDetail.title!; } final textPainter = TextPainter( text: TextSpan( diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index e5a187a6..fd539eae 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -246,9 +246,9 @@ class _PLVideoPlayerState extends State // 动态构建底部控制条 Widget buildBottomControl() { - bool isSeason = videoIntroController?.videoDetail.value.ugcSeason != null; - bool isPage = videoIntroController?.videoDetail.value.pages != null && - videoIntroController!.videoDetail.value.pages!.length > 1; + final videoDetail = videoIntroController?.videoDetail.value; + bool isSeason = videoDetail?.ugcSeason != null; + bool isPage = videoDetail?.pages != null && videoDetail!.pages!.length > 1; bool isPgc = pgcIntroController != null; bool anySeason = isSeason || isPage || isPgc; double widgetWidth = @@ -465,9 +465,10 @@ class _PLVideoPlayerState extends State int currentCid = plPlayerController.cid; String bvid = plPlayerController.bvid; List episodes = []; + final videoDetail = videoIntroController!.videoDetail.value; if (isSeason) { final List sections = - videoIntroController!.videoDetail.value.ugcSeason!.sections!; + videoDetail.ugcSeason!.sections!; for (int i = 0; i < sections.length; i++) { final List episodesList = sections[i].episodes!; for (int j = 0; j < episodesList.length; j++) { @@ -479,17 +480,14 @@ class _PLVideoPlayerState extends State } } } else if (isPage) { - final List pages = - videoIntroController!.videoDetail.value.pages!; + final List pages = videoDetail.pages!; episodes = pages; } else if (isPgc) { episodes = pgcIntroController!.pgcItem.episodes!; } widget.showEpisodes?.call( index, - isSeason - ? videoIntroController?.videoDetail.value.ugcSeason! - : null, + isSeason ? videoDetail.ugcSeason! : null, isSeason ? null : episodes, bvid, IdUtils.bv2av(bvid),