From f242eec1450c6e65a5eca3ddcca4d8efea92142a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 9 Nov 2024 17:56:13 +0800 Subject: [PATCH 1/4] mod: season change error --- .../video/detail/introduction/controller.dart | 22 ++++++++-------- lib/pages/video/detail/introduction/view.dart | 17 +++---------- .../introduction/widgets/page_panel.dart | 25 +++++++++++-------- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index e78d8121..2671f4d8 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -63,6 +63,7 @@ class VideoIntroController extends GetxController { PersistentBottomSheetController? bottomSheetController; late bool enableRelatedVideo; UgcSeason? ugcSeason; + RxList pages = [].obs; @override void onInit() { @@ -84,18 +85,20 @@ class VideoIntroController extends GetxController { } // 获取视频简介&分p - Future queryVideoIntro() async { + Future queryVideoIntro({cover}) async { var result = await VideoHttp.videoIntro(bvid: bvid); if (result['status']) { videoDetail.value = result['data']!; ugcSeason = result['data']!.ugcSeason; - if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) { - lastPlayCid.value = videoDetail.value.pages!.first.cid!; + pages.value = result['data']!.pages!; + lastPlayCid.value = videoDetail.value.cid!; + if (pages.isNotEmpty) { + lastPlayCid.value = pages.first.cid!; } final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}']; - videoDetailCtr.cover.value = result['data'].pic ?? ''; + videoDetailCtr.cover.value = cover ?? result['data'].pic ?? ''; // 获取到粉丝数再返回 await queryUserStat(); } @@ -470,8 +473,7 @@ class VideoIntroController extends GetxController { videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} this.bvid = bvid; - lastPlayCid.value = cid; - await queryVideoIntro(); + await queryVideoIntro(cover: cover); } void startTimer() { @@ -521,9 +523,8 @@ class VideoIntroController extends GetxController { final List episodesList = sections[i].episodes!; episodes.addAll(episodesList); } - } else if (videoDetail.value.pages != null) { + } else if (pages.isNotEmpty) { isPages = true; - final List pages = videoDetail.value.pages!; episodes.addAll(pages); } @@ -621,10 +622,9 @@ class VideoIntroController extends GetxController { } } } - if (videoDetail.value.pages != null && - videoDetail.value.pages!.length > 1) { + if (pages.length > 1) { dataType = VideoEpidoesType.videoPart; - episodes = videoDetail.value.pages!; + episodes = pages; } DrawerUtils.showRightDialog( diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 80176a7b..a02ed530 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -404,27 +404,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx( () => SeasonPanel( ugcSeason: widget.videoDetail!.ugcSeason!, - cid: videoIntroController.lastPlayCid.value != 0 - ? videoIntroController.lastPlayCid.value - : widget.videoDetail!.pages!.first.cid, + cid: videoIntroController.lastPlayCid.value, sheetHeight: videoDetailCtr.sheetHeight.value, - changeFuc: (bvid, cid, aid, cover) => - videoIntroController.changeSeasonOrbangu( - bvid, - cid, - aid, - cover, - ), + changeFuc: videoIntroController.changeSeasonOrbangu, videoIntroCtr: videoIntroController, ), ) ], // 合集 videoEpisode - if (widget.videoDetail!.pages != null && - widget.videoDetail!.pages!.length > 1) ...[ + if (videoIntroController.pages.length > 1) ...[ Obx( () => PagesPanel( - pages: widget.videoDetail!.pages!, + pages: videoIntroController.pages, cid: videoIntroController.lastPlayCid.value, sheetHeight: videoDetailCtr.sheetHeight.value, changeFuc: (cid, cover) => diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 81a22176..96786804 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; -import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; import '../../../../../common/pages_bottom_sheet.dart'; import '../../../../../models/common/video_episode_type.dart'; @@ -32,25 +31,26 @@ class _PagesPanelState extends State { late int cid; late RxInt currentIndex = (-1).obs; final String heroTag = Get.arguments['heroTag']; - late VideoDetailController _videoDetailController; final ScrollController listViewScrollCtr = ScrollController(); - late PersistentBottomSheetController? _bottomSheetController; + PersistentBottomSheetController? _bottomSheetController; @override void initState() { super.initState(); cid = widget.cid; episodes = widget.pages; - _videoDetailController = Get.find(tag: heroTag); - currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid); - scrollToIndex(); - _videoDetailController.cid.listen((int p0) { + updateCurrentIndexAndScroll(); + widget.videoIntroCtr.lastPlayCid.listen((int p0) { cid = p0; - currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid); - scrollToIndex(); + updateCurrentIndexAndScroll(); }); } + void updateCurrentIndexAndScroll() { + currentIndex.value = widget.pages.indexWhere((Part e) => e.cid == cid); + scrollToIndex(); + } + @override void dispose() { listViewScrollCtr.dispose(); @@ -60,7 +60,10 @@ class _PagesPanelState extends State { void changeFucCall(item, i) async { widget.changeFuc?.call(item.cid, item.cover); currentIndex.value = i; - _bottomSheetController?.close(); + cid = item.cid; + if (_bottomSheetController != null) { + _bottomSheetController?.close(); + } scrollToIndex(); } @@ -112,7 +115,7 @@ class _PagesPanelState extends State { widget.videoIntroCtr.bottomSheetController = _bottomSheetController = EpisodeBottomSheet( currentCid: cid, - episodes: episodes, + episodes: widget.pages, changeFucCall: changeFucCall, sheetHeight: widget.sheetHeight, dataType: VideoEpidoesType.videoPart, From 9c801129793913c8ae133f23802114bb3b09d3a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 9 Nov 2024 23:12:30 +0800 Subject: [PATCH 2/4] fix: sections --- lib/common/pages_bottom_sheet.dart | 218 ++++++++++++++---- .../introduction/widgets/season_panel.dart | 5 +- 2 files changed, 175 insertions(+), 48 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index d7872f13..85f7d33b 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -20,6 +20,7 @@ class EpisodeBottomSheet { final double? sheetHeight; bool isFullScreen = false; final UgcSeason? ugcSeason; + final int? currentEpisodeIndex; EpisodeBottomSheet({ required this.episodes, @@ -30,6 +31,7 @@ class EpisodeBottomSheet { this.sheetHeight, this.isFullScreen = false, this.ugcSeason, + this.currentEpisodeIndex, }); Widget buildShowContent() { @@ -42,6 +44,7 @@ class EpisodeBottomSheet { sheetHeight: sheetHeight, isFullScreen: isFullScreen, ugcSeason: ugcSeason, + currentEpisodeIndex: currentEpisodeIndex, ); } @@ -67,6 +70,7 @@ class PagesBottomSheet extends StatefulWidget { this.sheetHeight, this.isFullScreen = false, this.ugcSeason, + this.currentEpisodeIndex, }); final List episodes; @@ -77,41 +81,29 @@ class PagesBottomSheet extends StatefulWidget { final double? sheetHeight; final bool isFullScreen; final UgcSeason? ugcSeason; + final int? currentEpisodeIndex; @override State createState() => _PagesBottomSheetState(); } -class _PagesBottomSheetState extends State { +class _PagesBottomSheetState extends State + with TickerProviderStateMixin { final ScrollController _listScrollController = ScrollController(); late ListObserverController _listObserverController; final ScrollController _scrollController = ScrollController(); late int currentIndex; + TabController? tabController; + List? _listObserverControllerList; + List? _listScrollControllerList; @override void initState() { super.initState(); currentIndex = widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid); - _listObserverController = - ListObserverController(controller: _listScrollController); - if (widget.dataType == VideoEpidoesType.videoEpisode) { - _listObserverController.initialIndexModel = ObserverIndexPositionModel( - index: currentIndex, - isFixedHeight: true, - ); - } - - WidgetsBinding.instance.addPostFrameCallback((_) { - if (widget.dataType != VideoEpidoesType.videoEpisode) { - double itemHeight = (widget.isFullScreen - ? 400 - : Get.size.width - 3 * StyleString.safeSpace) / - 5.2; - double offset = ((currentIndex - 1) / 2).ceil() * itemHeight; - _scrollController.jumpTo(offset); - } - }); + _scrollToInit(); + _scrollPositionInit(); } String prefix() { @@ -126,9 +118,84 @@ class _PagesBottomSheetState extends State { return '选集'; } + // 滚动器初始化 + void _scrollToInit() { + /// 单个 + _listObserverController = + ListObserverController(controller: _listScrollController); + + if (widget.dataType == VideoEpidoesType.videoEpisode && + widget.ugcSeason?.sections != null && + widget.ugcSeason!.sections!.length > 1) { + tabController = TabController( + length: widget.ugcSeason!.sections!.length, + vsync: this, + initialIndex: widget.currentEpisodeIndex ?? 0, + ); + + /// 多tab + _listScrollControllerList = List.generate( + widget.ugcSeason!.sections!.length, + (index) { + return ScrollController(); + }, + ); + _listObserverControllerList = List.generate( + widget.ugcSeason!.sections!.length, + (index) { + return ListObserverController( + controller: _listScrollControllerList![index], + ); + }, + ); + } + } + + // 滚动器位置初始化 + void _scrollPositionInit() { + if (widget.dataType == VideoEpidoesType.videoEpisode) { + // 单个 多tab + if (widget.ugcSeason?.sections != null) { + if (widget.ugcSeason!.sections!.length == 1) { + _listObserverController.initialIndexModel = + ObserverIndexPositionModel( + index: currentIndex, + isFixedHeight: true, + ); + } else { + _listObserverControllerList![widget.currentEpisodeIndex!] + .initialIndexModel = ObserverIndexPositionModel( + index: currentIndex, + isFixedHeight: true, + ); + } + } + } + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (widget.dataType != VideoEpidoesType.videoEpisode) { + double itemHeight = (widget.isFullScreen + ? 400 + : Get.size.width - 3 * StyleString.safeSpace) / + 5.2; + double offset = ((currentIndex - 1) / 2).ceil() * itemHeight; + _scrollController.jumpTo(offset); + } + }); + } + @override void dispose() { - _listObserverController.controller?.dispose(); + try { + _listObserverController.controller?.dispose(); + _listScrollController.dispose(); + for (var element in _listObserverControllerList!) { + element.controller?.dispose(); + } + for (var element in _listScrollControllerList!) { + element.dispose(); + } + } catch (_) {} super.dispose(); } @@ -150,31 +217,35 @@ class _PagesBottomSheetState extends State { Expanded( child: Material( child: widget.dataType == VideoEpidoesType.videoEpisode - ? ListViewObserver( - controller: _listObserverController, - child: ListView.builder( - controller: _listScrollController, - itemCount: widget.episodes.length + 1, - itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == widget.episodes.length; - bool isCurrentIndex = currentIndex == index; - return isLastItem - ? SizedBox( - height: - MediaQuery.of(context).padding.bottom + + ? (widget.ugcSeason!.sections!.length == 1 + ? ListViewObserver( + controller: _listObserverController, + child: ListView.builder( + controller: _listScrollController, + itemCount: widget.episodes.length + 1, + itemBuilder: (BuildContext context, int index) { + bool isLastItem = + index == widget.episodes.length; + bool isCurrentIndex = currentIndex == index; + return isLastItem + ? SizedBox( + height: MediaQuery.of(context) + .padding + .bottom + 20, - ) - : EpisodeListItem( - episode: widget.episodes[index], - index: index, - isCurrentIndex: isCurrentIndex, - dataType: widget.dataType, - changeFucCall: widget.changeFucCall, - isFullScreen: widget.isFullScreen, - ); - }, - ), - ) + ) + : EpisodeListItem( + episode: widget.episodes[index], + index: index, + isCurrentIndex: isCurrentIndex, + dataType: widget.dataType, + changeFucCall: widget.changeFucCall, + isFullScreen: widget.isFullScreen, + ); + }, + ), + ) + : buildTabBar()) : Padding( padding: const EdgeInsets.symmetric( horizontal: 12.0), // 设置左右间距为12 @@ -206,6 +277,61 @@ class _PagesBottomSheetState extends State { ); }); } + + Widget buildTabBar() { + return Column( + children: [ + TabBar( + controller: tabController, + isScrollable: true, + indicatorSize: TabBarIndicatorSize.label, + tabAlignment: TabAlignment.start, + splashBorderRadius: BorderRadius.circular(4), + tabs: [ + ...widget.ugcSeason!.sections!.map((SectionItem section) { + return Tab( + text: section.title, + ); + }).toList() + ], + ), + Expanded( + child: TabBarView( + controller: tabController, + children: [ + ...widget.ugcSeason!.sections!.map((SectionItem section) { + final int fIndex = widget.ugcSeason!.sections!.indexOf(section); + return ListViewObserver( + controller: _listObserverControllerList![fIndex], + child: ListView.builder( + controller: _listScrollControllerList![fIndex], + itemCount: section.episodes!.length + 1, + itemBuilder: (BuildContext context, int index) { + final bool isLastItem = index == section.episodes!.length; + return isLastItem + ? SizedBox( + height: + MediaQuery.of(context).padding.bottom + 20, + ) + : EpisodeListItem( + episode: section.episodes![index], // 调整索引 + index: index, // 调整索引 + isCurrentIndex: widget.currentCid == + section.episodes![index].cid, + dataType: widget.dataType, + changeFucCall: widget.changeFucCall, + isFullScreen: widget.isFullScreen, + ); + }, + ), + ); + }).toList() + ], + ), + ), + ], + ); + } } class TitleBar extends StatelessWidget { @@ -516,7 +642,7 @@ class UgcSeasonBuild extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.fromLTRB(12, 0, 12, 8), + padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), color: Theme.of(context).colorScheme.surface, child: Column( mainAxisAlignment: MainAxisAlignment.start, diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index f26885a7..9d9f33d8 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -33,6 +33,7 @@ class _SeasonPanelState extends State { final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; late PersistentBottomSheetController? _bottomSheetController; + int currentEpisodeIndex = 0; @override void initState() { @@ -41,13 +42,12 @@ class _SeasonPanelState extends State { _videoDetailController = Get.find(tag: heroTag); /// 根据 cid 找到对应集,找到对应 episodes - /// 有多个episodes时,只显示其中一个 - /// TODO 同时显示多个合集 final List sections = widget.ugcSeason.sections!; for (int i = 0; i < sections.length; i++) { final List episodesList = sections[i].episodes!; for (int j = 0; j < episodesList.length; j++) { if (episodesList[j].cid == cid) { + currentEpisodeIndex = i; episodes = episodesList; continue; } @@ -125,6 +125,7 @@ class _SeasonPanelState extends State { sheetHeight: widget.sheetHeight, dataType: VideoEpidoesType.videoEpisode, ugcSeason: widget.ugcSeason, + currentEpisodeIndex: currentEpisodeIndex, ).show(context); }, child: Padding( From 43d866bfe1e46407dbcb7a0eb5b55c71d85c9a30 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 9 Nov 2024 23:22:19 +0800 Subject: [PATCH 3/4] fix: season currentIndex --- lib/common/pages_bottom_sheet.dart | 7 +++++- lib/models/video_detail_res.dart | 18 ++++++++++++++ .../introduction/widgets/season_panel.dart | 24 ++++++++++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 85f7d33b..3b7bd8b7 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -21,6 +21,7 @@ class EpisodeBottomSheet { bool isFullScreen = false; final UgcSeason? ugcSeason; final int? currentEpisodeIndex; + final int? currentIndex; EpisodeBottomSheet({ required this.episodes, @@ -32,6 +33,7 @@ class EpisodeBottomSheet { this.isFullScreen = false, this.ugcSeason, this.currentEpisodeIndex, + this.currentIndex, }); Widget buildShowContent() { @@ -45,6 +47,7 @@ class EpisodeBottomSheet { isFullScreen: isFullScreen, ugcSeason: ugcSeason, currentEpisodeIndex: currentEpisodeIndex, + currentIndex: currentIndex, ); } @@ -71,6 +74,7 @@ class PagesBottomSheet extends StatefulWidget { this.isFullScreen = false, this.ugcSeason, this.currentEpisodeIndex, + this.currentIndex, }); final List episodes; @@ -82,6 +86,7 @@ class PagesBottomSheet extends StatefulWidget { final bool isFullScreen; final UgcSeason? ugcSeason; final int? currentEpisodeIndex; + final int? currentIndex; @override State createState() => _PagesBottomSheetState(); @@ -100,7 +105,7 @@ class _PagesBottomSheetState extends State @override void initState() { super.initState(); - currentIndex = + currentIndex = widget.currentIndex ?? widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid); _scrollToInit(); _scrollPositionInit(); diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index 3401d809..ad52a840 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -641,6 +641,7 @@ class EpisodeItem { this.page, this.bvid, this.cover, + this.pages, }); int? seasonId; int? sectionId; @@ -655,6 +656,7 @@ class EpisodeItem { int? pubdate; int? duration; Stat? stat; + List? pages; EpisodeItem.fromJson(Map json) { seasonId = json['season_id']; @@ -670,6 +672,7 @@ class EpisodeItem { pubdate = json['arc']['pubdate']; duration = json['arc']['duration']; stat = Stat.fromJson(json['arc']['stat']); + pages = json['pages'].map((e) => Page.fromJson(e)).toList(); } } @@ -712,3 +715,18 @@ class Vip { status = json['status']; } } + +class Page { + Page({ + this.cid, + this.page, + }); + + int? cid; + int? page; + + Page.fromJson(Map json) { + cid = json['cid']; + page = json['page']; + } +} diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index 9d9f33d8..e5a9ea84 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -33,7 +33,7 @@ class _SeasonPanelState extends State { final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; late PersistentBottomSheetController? _bottomSheetController; - int currentEpisodeIndex = 0; + int currentEpisodeIndex = -1; @override void initState() { @@ -55,10 +55,10 @@ class _SeasonPanelState extends State { } /// 取对应 season_id 的 episodes - currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid); + getCurrentIndex(); _videoDetailController.cid.listen((int p0) { cid = p0; - currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid); + getCurrentIndex(); }); } @@ -73,6 +73,23 @@ class _SeasonPanelState extends State { _bottomSheetController?.close(); } + // 获取currentIndex + void getCurrentIndex() { + currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid); + final List sections = widget.ugcSeason.sections!; + if (sections.length == 1 && sections.first.type == 1) { + final List episodesList = sections.first.episodes!; + for (int i = 0; i < episodesList.length; i++) { + for (int j = 0; j < episodesList[i].pages!.length; j++) { + if (episodesList[i].pages![j].cid == cid) { + currentIndex.value = i; + continue; + } + } + } + } + } + Widget buildEpisodeListItem( EpisodeItem episode, int index, @@ -126,6 +143,7 @@ class _SeasonPanelState extends State { dataType: VideoEpidoesType.videoEpisode, ugcSeason: widget.ugcSeason, currentEpisodeIndex: currentEpisodeIndex, + currentIndex: currentIndex.value, ).show(context); }, child: Padding( From 4c139c3a26d974d1c2f13d98b9b0dbd9e149be2c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Nov 2024 00:52:16 +0800 Subject: [PATCH 4/4] feat: season fav --- lib/common/pages_bottom_sheet.dart | 82 +++++++++++++++++++++++++----- lib/http/api.dart | 6 +++ lib/http/video.dart | 47 +++++++++++++++++ 3 files changed, 123 insertions(+), 12 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 3b7bd8b7..1e88e863 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -3,7 +3,9 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import '../models/common/video_episode_type.dart'; @@ -101,6 +103,9 @@ class _PagesBottomSheetState extends State TabController? tabController; List? _listObserverControllerList; List? _listScrollControllerList; + final String heroTag = Get.arguments['heroTag']; + VideoDetailController? _videoDetailController; + late RxInt isSubscribe = (-1).obs; @override void initState() { @@ -109,6 +114,10 @@ class _PagesBottomSheetState extends State widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid); _scrollToInit(); _scrollPositionInit(); + if (widget.dataType == VideoEpidoesType.videoEpisode) { + _videoDetailController = Get.find(tag: heroTag); + _getSubscribeStatus(); + } } String prefix() { @@ -189,6 +198,32 @@ class _PagesBottomSheetState extends State }); } + // 获取订阅状态 + void _getSubscribeStatus() async { + var res = + await VideoHttp.getSubscribeStatus(bvid: _videoDetailController!.bvid); + if (res['status']) { + isSubscribe.value = res['data']['season_fav'] ? 1 : 0; + } + } + + // 更改订阅状态 + void _changeSubscribeStatus() async { + if (isSubscribe.value == -1) { + return; + } + dynamic result = await VideoHttp.seasonFav( + isFav: isSubscribe.value == 1, + seasonId: widget.ugcSeason!.id, + ); + if (result['status']) { + SmartDialog.showToast(isSubscribe.value == 1 ? '取消订阅成功' : '订阅成功'); + isSubscribe.value = isSubscribe.value == 1 ? 0 : 1; + } else { + SmartDialog.showToast(result['msg']); + } + } + @override void dispose() { try { @@ -217,7 +252,11 @@ class _PagesBottomSheetState extends State isFullScreen: widget.isFullScreen, ), if (widget.ugcSeason != null) ...[ - UgcSeasonBuild(ugcSeason: widget.ugcSeason!), + UgcSeasonBuild( + ugcSeason: widget.ugcSeason!, + isSubscribe: isSubscribe, + changeFucCall: _changeSubscribeStatus, + ), ], Expanded( child: Material( @@ -638,14 +677,20 @@ class EpisodeGridItem extends StatelessWidget { class UgcSeasonBuild extends StatelessWidget { final UgcSeason ugcSeason; + final RxInt isSubscribe; + final Function changeFucCall; const UgcSeasonBuild({ Key? key, required this.ugcSeason, + required this.isSubscribe, + required this.changeFucCall, }) : super(key: key); @override Widget build(BuildContext context) { + final ThemeData t = Theme.of(context); + final Color outline = t.colorScheme.outline; return Container( padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), color: Theme.of(context).colorScheme.surface, @@ -673,17 +718,30 @@ class UgcSeasonBuild extends StatelessWidget { style: TextStyle( color: Theme.of(context).colorScheme.outline)), ), - // SizedBox( - // height: 32, - // child: FilledButton.tonal( - // onPressed: () {}, - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // child: const Text('订阅'), - // ), - // ), - // const SizedBox(width: 6), + Obx( + () => isSubscribe.value == -1 + ? const SizedBox(height: 32) + : SizedBox( + height: 32, + child: FilledButton.tonal( + onPressed: () => changeFucCall.call(), + style: TextButton.styleFrom( + padding: const EdgeInsets.only( + left: 8, + right: 8, + ), + foregroundColor: isSubscribe.value == 1 + ? outline + : t.colorScheme.onPrimary, + backgroundColor: isSubscribe.value == 1 + ? t.colorScheme.onInverseSurface + : t.colorScheme.primary, // 设置按钮背景色 + ), + child: Text(isSubscribe.value == 1 ? '已订阅' : '订阅'), + ), + ), + ), + const SizedBox(width: 6), ], ), ], diff --git a/lib/http/api.dart b/lib/http/api.dart index 13fb19c8..e4500710 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -609,4 +609,10 @@ class Api { /// @我的 static const String messageAtAPi = '/x/msgfeed/at?'; + + /// 订阅 + static const String confirmSub = '/x/v3/fav/season/fav'; + + /// 订阅状态 + static const String videoRelation = '/x/web-interface/archive/relation'; } diff --git a/lib/http/video.dart b/lib/http/video.dart index 5a9f4aa1..cb84e676 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:developer'; import 'package:dio/dio.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/utils/id_utils.dart'; import '../common/constants.dart'; import '../models/common/reply_type.dart'; import '../models/home/rcmd/result.dart'; @@ -560,4 +561,50 @@ class VideoHttp { final List body = res.data['body']; return {'content': content, 'body': body}; } + + static Future> getSubscribeStatus( + {required dynamic bvid}) async { + var res = await Request().get( + Api.videoRelation, + data: { + 'aid': IdUtils.bv2av(bvid), + 'bvid': bvid, + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } + + static Future seasonFav({ + required bool isFav, + required dynamic seasonId, + }) async { + var res = await Request().post( + isFav ? Api.cancelSub : Api.confirmSub, + data: { + 'platform': 'web', + 'season_id': seasonId, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } }