From 098e2220cc95e16b7f5ffd56f4833ce0817dfac8 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Tue, 31 Dec 2024 18:16:08 +0800 Subject: [PATCH] feat: medialist: reverse play #70 Signed-off-by: bggRGjQaUbCoE --- lib/common/widgets/icon_button.dart | 19 +++++++ lib/common/widgets/list_sheet.dart | 34 +++--------- lib/models/video/later.dart | 3 -- lib/pages/fav_detail/controller.dart | 1 + lib/pages/later/controller.dart | 1 + .../content/video/member_video_ctr.dart | 16 ++++-- lib/pages/video/detail/controller.dart | 52 +++++++++++++------ .../detail/widgets/watch_later_list.dart | 38 ++++++++++---- 8 files changed, 105 insertions(+), 59 deletions(-) diff --git a/lib/common/widgets/icon_button.dart b/lib/common/widgets/icon_button.dart index 81af3504..6e2ea7ae 100644 --- a/lib/common/widgets/icon_button.dart +++ b/lib/common/widgets/icon_button.dart @@ -28,3 +28,22 @@ Widget iconButton({ ), ); } + +Widget mediumButton({ + String? tooltip, + IconData? icon, + VoidCallback? onPressed, +}) { + return SizedBox( + width: 34, + height: 34, + child: IconButton( + tooltip: tooltip, + icon: Icon(icon), + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: onPressed, + ), + ); +} diff --git a/lib/common/widgets/list_sheet.dart b/lib/common/widgets/list_sheet.dart index 44b34e69..67218924 100644 --- a/lib/common/widgets/list_sheet.dart +++ b/lib/common/widgets/list_sheet.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:PiliPalaX/common/constants.dart'; +import 'package:PiliPalaX/common/widgets/icon_button.dart'; import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/http/video.dart'; import 'package:PiliPalaX/models/bangumi/info.dart' as bangumi; @@ -291,7 +292,7 @@ class _ListSheetContentState extends State StreamBuilder( stream: _favStream?.stream, builder: (context, snapshot) => snapshot.hasData - ? _mediumButton( + ? mediumButton( tooltip: _seasonFav == 1 ? '取消订阅' : '订阅', icon: _seasonFav == 1 ? Icons.notifications_off_outlined @@ -313,7 +314,7 @@ class _ListSheetContentState extends State ) : const SizedBox.shrink(), ), - _mediumButton( + mediumButton( tooltip: '跳至顶部', icon: Icons.vertical_align_top, onPressed: () { @@ -331,7 +332,7 @@ class _ListSheetContentState extends State } catch (_) {} }, ), - _mediumButton( + mediumButton( tooltip: '跳至底部', icon: Icons.vertical_align_bottom, onPressed: () { @@ -349,7 +350,7 @@ class _ListSheetContentState extends State } catch (_) {} }, ), - _mediumButton( + mediumButton( tooltip: '跳至当前', icon: Icons.my_location, onPressed: () async { @@ -382,7 +383,7 @@ class _ListSheetContentState extends State StreamBuilder( stream: _indexStream?.stream, initialData: _index, - builder: (context, snapshot) => _mediumButton( + builder: (context, snapshot) => mediumButton( tooltip: reverse[snapshot.data] ? '顺序' : '倒序', icon: !reverse[snapshot.data] ? MdiIcons.sortNumericAscending @@ -395,7 +396,7 @@ class _ListSheetContentState extends State ), ), if (widget.onClose != null) - _mediumButton( + mediumButton( tooltip: '关闭', icon: Icons.close, onPressed: widget.onClose, @@ -437,7 +438,7 @@ class _ListSheetContentState extends State ); } - Widget get _reverseButton => _mediumButton( + Widget get _reverseButton => mediumButton( tooltip: widget.isReversed == true ? '正序播放' : '倒序播放', icon: widget.isReversed == true ? MdiIcons.sortDescending @@ -461,25 +462,6 @@ class _ListSheetContentState extends State }, ); - Widget _mediumButton({ - String? tooltip, - IconData? icon, - VoidCallback? onPressed, - }) { - return SizedBox( - width: 34, - height: 34, - child: IconButton( - tooltip: tooltip, - icon: Icon(icon), - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: onPressed, - ), - ); - } - Widget _buildBody(i, episodes) => Material( child: ScrollablePositionedList.separated( padding: EdgeInsets.only( diff --git a/lib/models/video/later.dart b/lib/models/video/later.dart index aee860e2..dd4f3154 100644 --- a/lib/models/video/later.dart +++ b/lib/models/video/later.dart @@ -31,7 +31,6 @@ class MediaVideoItemModel { this.forbidFav, this.moreType, this.businessOid, - this.isReversed = false, }); int? id; @@ -65,7 +64,6 @@ class MediaVideoItemModel { bool? forbidFav; int? moreType; int? businessOid; - bool isReversed; factory MediaVideoItemModel.fromJson(Map json) => MediaVideoItemModel( @@ -103,7 +101,6 @@ class MediaVideoItemModel { forbidFav: json["forbid_fav"], moreType: json["more_type"], businessOid: json["business_oid"], - isReversed: false, ); } diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 5c917fd8..5d0614b0 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -147,6 +147,7 @@ class FavDetailController extends MultiSelectController { 'oid': element.id, 'favTitle': item.value.title, 'count': item.value.mediaCount, + 'desc': true, }, ); break; diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 959b66ec..159b991e 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -187,6 +187,7 @@ class LaterController extends MultiSelectController { 'count': list.length, 'favTitle': '稍后再看', 'mediaId': GStorage.userInfo.get('userInfoCache')?.mid, + 'desc': false, }, ); break; diff --git a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart index c131748a..bb79af5e 100644 --- a/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart +++ b/lib/pages/member/new/content/member_contribute/content/video/member_video_ctr.dart @@ -6,6 +6,7 @@ import 'package:PiliPalaX/models/space_archive/item.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/member/new/content/member_contribute/member_contribute.dart' show ContributeType; +import 'package:PiliPalaX/utils/extension.dart'; import 'package:PiliPalaX/utils/id_utils.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -103,6 +104,13 @@ class MemberVideoCtr extends CommonController { SmartDialog.showToast('已跳过不支持播放的视频'); } final String heroTag = Utils.makeHeroTag(element.bvid); + bool desc = seasonId != null ? false : true; + desc = (seasonId != null || seriesId != null) && + (type == ContributeType.video + ? order.value == 'click' + : sort.value == 'asc') + ? desc.not + : desc; Get.toNamed( '/video?bvid=${element.bvid}&cid=${element.firstCid}', arguments: { @@ -118,9 +126,11 @@ class MemberVideoCtr extends CommonController { 'mediaType': RegExp(r'page_type=([\d]+)') .firstMatch('${episodicButton?.uri}') ?.group(1), - 'reverse': type == ContributeType.video - ? order.value == 'click' - : sort.value == 'asc', + 'desc': desc, + 'sortField': + type == ContributeType.video && order.value == 'click' + ? 2 + : 1, }, ); break; diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index b8ad7087..81b93dc1 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -244,6 +244,7 @@ class VideoDetailController extends GetxController // 页面来源 稍后再看 收藏夹 String sourceType = 'normal'; + late bool _mediaDesc = false; late RxList mediaList = [].obs; late String watchLaterTitle = ''; bool get isPlayAll => ['watchLater', 'fav', 'archive'].contains(sourceType); @@ -279,6 +280,7 @@ class VideoDetailController extends GetxController if (sourceType != 'normal') { watchLaterTitle = Get.arguments['favTitle']; + _mediaDesc = Get.arguments['desc']; getMediaList(); } @@ -327,31 +329,44 @@ class VideoDetailController extends GetxController } } - void getMediaList() async { - if (Get.arguments['count'] != null && + void getMediaList([bool isReverse = false]) async { + if (isReverse.not && + Get.arguments['count'] != null && mediaList.length >= Get.arguments['count']) { return; } - bool desc = - _mediaType == 2 || Get.arguments['mediaType'] == '8' ? false : true; var res = await UserHttp.getMediaList( type: Get.arguments['mediaType'] ?? _mediaType, bizId: Get.arguments['mediaId'] ?? -1, ps: 20, - oid: mediaList.isEmpty ? null : mediaList.last.id, - otype: mediaList.isEmpty ? null : mediaList.last.type, - desc: - Get.arguments['mediaType'] != null && Get.arguments['reverse'] == true - ? desc.not - : desc, - sortField: - Get.arguments['mediaType'] == null && Get.arguments['reverse'] == true - ? 2 - : 1, + oid: isReverse || mediaList.isEmpty ? null : mediaList.last.id, + otype: isReverse || mediaList.isEmpty ? null : mediaList.last.type, + desc: _mediaDesc, + sortField: Get.arguments['sortField'] ?? 1, ); if (res['status']) { if (res['data'].isNotEmpty) { - mediaList.addAll(res['data']); + if (isReverse) { + mediaList.value = res['data']; + try { + for (MediaVideoItemModel item in mediaList) { + if (item.cid == null) { + continue; + } else { + Get.find(tag: heroTag) + .changeSeasonOrbangu( + null, + mediaList.first.bvid, + mediaList.first.cid, + mediaList.first.aid, + mediaList.first.cover, + ); + } + } + } catch (_) {} + } else { + mediaList.addAll(res['data']); + } } } else { SmartDialog.showToast(res['msg']); @@ -366,9 +381,14 @@ class VideoDetailController extends GetxController mediaList: mediaList, changeMediaList: changeMediaList, panelTitle: watchLaterTitle, - bvid: bvid, + getBvId: () => bvid, count: Get.arguments['count'], loadMoreMedia: getMediaList, + desc: _mediaDesc, + onReverse: () { + _mediaDesc = !_mediaDesc; + getMediaList(true); + }, ), ); } diff --git a/lib/pages/video/detail/widgets/watch_later_list.dart b/lib/pages/video/detail/widgets/watch_later_list.dart index a40af0da..596542f1 100644 --- a/lib/pages/video/detail/widgets/watch_later_list.dart +++ b/lib/pages/video/detail/widgets/watch_later_list.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/common/widgets/icon_button.dart'; import 'package:PiliPalaX/common/widgets/stat/danmu.dart'; import 'package:PiliPalaX/common/widgets/stat/view.dart'; import 'package:flutter/material.dart'; @@ -9,6 +10,7 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; import 'package:PiliPalaX/http/search.dart'; import 'package:PiliPalaX/models/video/later.dart'; import 'package:PiliPalaX/utils/utils.dart'; +import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class MediaListPanel extends StatefulWidget { @@ -17,17 +19,21 @@ class MediaListPanel extends StatefulWidget { required this.mediaList, this.changeMediaList, this.panelTitle, - this.bvid, + required this.getBvId, required this.loadMoreMedia, required this.count, + required this.desc, + required this.onReverse, }); final List mediaList; final Function? changeMediaList; final String? panelTitle; - final String? bvid; + final Function getBvId; final VoidCallback loadMoreMedia; final int? count; + final bool? desc; + final VoidCallback onReverse; @override State createState() => _MediaListPanelState(); @@ -41,7 +47,7 @@ class _MediaListPanelState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { int index = - widget.mediaList.indexWhere((item) => item.bvid == widget.bvid); + widget.mediaList.indexWhere((item) => item.bvid == widget.getBvId()); if (index != -1 && index != 0) { try { _scrollController.jumpTo(index: index); @@ -64,8 +70,17 @@ class _MediaListPanelState extends State { titleSpacing: 16, title: Text(widget.panelTitle ?? '稍后再看'), actions: [ - IconButton( - icon: const Icon(Icons.close, size: 20), + if (widget.desc != null) + mediumButton( + tooltip: widget.desc == true ? '顺序播放' : '倒序播放', + icon: widget.desc == true + ? MdiIcons.sortAscending + : MdiIcons.sortDescending, + onPressed: widget.onReverse, + ), + mediumButton( + tooltip: '关闭', + icon: Icons.close, onPressed: Get.back, ), const SizedBox(width: 14), @@ -160,14 +175,15 @@ class _MediaListPanelState extends State { overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: - item.bvid == widget.bvid + item.bvid == widget.getBvId() ? FontWeight.bold : null, - color: item.bvid == widget.bvid - ? Theme.of(context) - .colorScheme - .primary - : null, + color: + item.bvid == widget.getBvId() + ? Theme.of(context) + .colorScheme + .primary + : null, ), ), const Spacer(),