Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-07-31 17:33:36 +08:00
parent edc9a1ca7b
commit f7d4db6aad
21 changed files with 220 additions and 458 deletions

View File

@@ -55,7 +55,7 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
final int quality; final int quality;
final ValueChanged? onClose; final ValueChanged<bool>? onClose;
final bool? setStatusBar; final bool? setStatusBar;

View File

@@ -141,7 +141,7 @@ class MyApp extends StatelessWidget {
// 强制设置高帧率 // 强制设置高帧率
if (Platform.isAndroid) { if (Platform.isAndroid) {
late List modes; late List<DisplayMode> modes;
FlutterDisplayMode.supported.then((value) { FlutterDisplayMode.supported.then((value) {
modes = value; modes = value;
var storageDisplay = GStorage.setting.get(SettingBoxKey.displayMode); var storageDisplay = GStorage.setting.get(SettingBoxKey.displayMode);

View File

@@ -239,14 +239,14 @@ class FormatItem {
String? format; String? format;
String? newDesc; String? newDesc;
String? displayDesc; String? displayDesc;
List? codecs; List<String>? codecs;
FormatItem.fromJson(Map<String, dynamic> json) { FormatItem.fromJson(Map<String, dynamic> json) {
quality = json['quality']; quality = json['quality'];
format = json['format']; format = json['format'];
newDesc = json['new_description']; newDesc = json['new_description'];
displayDesc = json['display_desc']; displayDesc = json['display_desc'];
codecs = json['codecs']; codecs = (json['codecs'] as List?)?.cast<String>();
} }
} }

View File

@@ -1,11 +0,0 @@
class Freya {
int? bubbleShowCnt;
int? iconShow;
Freya({this.bubbleShowCnt, this.iconShow});
factory Freya.fromJson(Map<String, dynamic> json) => Freya(
bubbleShowCnt: json['bubble_show_cnt'] as int?,
iconShow: json['icon_show'] as int?,
);
}

View File

@@ -1,26 +0,0 @@
class PayType {
int? allowDiscount;
int? allowPack;
int? allowTicket;
int? allowTimeLimit;
int? allowVipDiscount;
int? forbidBb;
PayType({
this.allowDiscount,
this.allowPack,
this.allowTicket,
this.allowTimeLimit,
this.allowVipDiscount,
this.forbidBb,
});
factory PayType.fromJson(Map<String, dynamic> json) => PayType(
allowDiscount: json['allow_discount'] as int?,
allowPack: json['allow_pack'] as int?,
allowTicket: json['allow_ticket'] as int?,
allowTimeLimit: json['allow_time_limit'] as int?,
allowVipDiscount: json['allow_vip_discount'] as int?,
forbidBb: json['forbid_bb'] as int?,
);
}

View File

@@ -1,42 +0,0 @@
import 'package:PiliPlus/models_new/pgc/pgc_info_model/pay_type.dart';
class Payment {
int? discount;
PayType? payType;
String? price;
String? promotion;
String? tip;
int? viewStartTime;
int? vipDiscount;
String? vipFirstPromotion;
String? vipPrice;
String? vipPromotion;
Payment({
this.discount,
this.payType,
this.price,
this.promotion,
this.tip,
this.viewStartTime,
this.vipDiscount,
this.vipFirstPromotion,
this.vipPrice,
this.vipPromotion,
});
factory Payment.fromJson(Map<String, dynamic> json) => Payment(
discount: json['discount'] as int?,
payType: json['pay_type'] == null
? null
: PayType.fromJson(json['pay_type'] as Map<String, dynamic>),
price: json['price'] as String?,
promotion: json['promotion'] as String?,
tip: json['tip'] as String?,
viewStartTime: json['view_start_time'] as int?,
vipDiscount: json['vip_discount'] as int?,
vipFirstPromotion: json['vip_first_promotion'] as String?,
vipPrice: json['vip_price'] as String?,
vipPromotion: json['vip_promotion'] as String?,
);
}

View File

@@ -1,9 +0,0 @@
class PlayStrategy {
List? strategies;
PlayStrategy({this.strategies});
factory PlayStrategy.fromJson(Map<String, dynamic> json) => PlayStrategy(
strategies: json['strategies'],
);
}

View File

@@ -1,11 +0,0 @@
class Positive {
int? id;
String? title;
Positive({this.id, this.title});
factory Positive.fromJson(Map<String, dynamic> json) => Positive(
id: json['id'] as int?,
title: json['title'] as String?,
);
}

View File

@@ -1,19 +1,14 @@
import 'package:PiliPlus/models_new/pgc/pgc_info_model/activity.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/activity.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/area.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/area.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/episode.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/freya.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/icon_font.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/icon_font.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/new_ep.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/new_ep.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/payment.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/play_strategy.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/positive.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/publish.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/publish.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/rating.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/rating.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/rights.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/rights.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/season.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/season.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/section.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/section.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/series.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/series.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/show.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/stat.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/stat.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/up_info.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/up_info.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/user_status.dart'; import 'package:PiliPlus/models_new/pgc/pgc_info_model/user_status.dart';
@@ -25,11 +20,9 @@ class PgcInfoModel {
List<Area>? areas; List<Area>? areas;
String? bkgCover; String? bkgCover;
String? cover; String? cover;
bool? deliveryFragmentVideo;
bool? enableVt; bool? enableVt;
List<EpisodeItem>? episodes; List<EpisodeItem>? episodes;
String? evaluate; String? evaluate;
Freya? freya;
int? hideEpVvVtDm; int? hideEpVvVtDm;
IconFont? iconFont; IconFont? iconFont;
String? jpTitle; String? jpTitle;
@@ -37,9 +30,6 @@ class PgcInfoModel {
int? mediaId; int? mediaId;
int? mode; int? mode;
NewEp? newEp; NewEp? newEp;
Payment? payment;
PlayStrategy? playStrategy;
Positive? positive;
Publish? publish; Publish? publish;
Rating? rating; Rating? rating;
String? record; String? record;
@@ -52,13 +42,11 @@ class PgcInfoModel {
String? shareCopy; String? shareCopy;
String? shareSubTitle; String? shareSubTitle;
String? shareUrl; String? shareUrl;
Show? show;
int? showSeasonType; int? showSeasonType;
String? squareCover; String? squareCover;
String? staff; String? staff;
PgcStat? stat; PgcStat? stat;
int? status; int? status;
List? styles;
String? subtitle; String? subtitle;
String? title; String? title;
int? total; int? total;
@@ -73,11 +61,9 @@ class PgcInfoModel {
this.areas, this.areas,
this.bkgCover, this.bkgCover,
this.cover, this.cover,
this.deliveryFragmentVideo,
this.enableVt, this.enableVt,
this.episodes, this.episodes,
this.evaluate, this.evaluate,
this.freya,
this.hideEpVvVtDm, this.hideEpVvVtDm,
this.iconFont, this.iconFont,
this.jpTitle, this.jpTitle,
@@ -85,9 +71,6 @@ class PgcInfoModel {
this.mediaId, this.mediaId,
this.mode, this.mode,
this.newEp, this.newEp,
this.payment,
this.playStrategy,
this.positive,
this.publish, this.publish,
this.rating, this.rating,
this.record, this.record,
@@ -100,13 +83,11 @@ class PgcInfoModel {
this.shareCopy, this.shareCopy,
this.shareSubTitle, this.shareSubTitle,
this.shareUrl, this.shareUrl,
this.show,
this.showSeasonType, this.showSeasonType,
this.squareCover, this.squareCover,
this.staff, this.staff,
this.stat, this.stat,
this.status, this.status,
this.styles,
this.subtitle, this.subtitle,
this.title, this.title,
this.total, this.total,
@@ -126,15 +107,11 @@ class PgcInfoModel {
.toList(), .toList(),
bkgCover: json['bkg_cover'] as String?, bkgCover: json['bkg_cover'] as String?,
cover: json['cover'] as String?, cover: json['cover'] as String?,
deliveryFragmentVideo: json['delivery_fragment_video'] as bool?,
enableVt: json['enable_vt'] as bool?, enableVt: json['enable_vt'] as bool?,
episodes: (json['episodes'] as List<dynamic>?) episodes: (json['episodes'] as List<dynamic>?)
?.map((e) => EpisodeItem.fromJson(e as Map<String, dynamic>)) ?.map((e) => EpisodeItem.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
evaluate: json['evaluate'] as String?, evaluate: json['evaluate'] as String?,
freya: json['freya'] == null
? null
: Freya.fromJson(json['freya'] as Map<String, dynamic>),
hideEpVvVtDm: json['hide_ep_vv_vt_dm'] as int?, hideEpVvVtDm: json['hide_ep_vv_vt_dm'] as int?,
iconFont: json['icon_font'] == null iconFont: json['icon_font'] == null
? null ? null
@@ -146,15 +123,6 @@ class PgcInfoModel {
newEp: json['new_ep'] == null newEp: json['new_ep'] == null
? null ? null
: NewEp.fromJson(json['new_ep'] as Map<String, dynamic>), : NewEp.fromJson(json['new_ep'] as Map<String, dynamic>),
payment: json['payment'] == null
? null
: Payment.fromJson(json['payment'] as Map<String, dynamic>),
playStrategy: json['play_strategy'] == null
? null
: PlayStrategy.fromJson(json['play_strategy'] as Map<String, dynamic>),
positive: json['positive'] == null
? null
: Positive.fromJson(json['positive'] as Map<String, dynamic>),
publish: json['publish'] == null publish: json['publish'] == null
? null ? null
: Publish.fromJson(json['publish'] as Map<String, dynamic>), : Publish.fromJson(json['publish'] as Map<String, dynamic>),
@@ -179,9 +147,6 @@ class PgcInfoModel {
shareCopy: json['share_copy'] as String?, shareCopy: json['share_copy'] as String?,
shareSubTitle: json['share_sub_title'] as String?, shareSubTitle: json['share_sub_title'] as String?,
shareUrl: json['share_url'] as String?, shareUrl: json['share_url'] as String?,
show: json['show'] == null
? null
: Show.fromJson(json['show'] as Map<String, dynamic>),
showSeasonType: json['show_season_type'] as int?, showSeasonType: json['show_season_type'] as int?,
squareCover: json['square_cover'] as String?, squareCover: json['square_cover'] as String?,
staff: json['staff'] as String?, staff: json['staff'] as String?,
@@ -189,7 +154,6 @@ class PgcInfoModel {
? null ? null
: PgcStat.fromJson(json['stat'] as Map<String, dynamic>), : PgcStat.fromJson(json['stat'] as Map<String, dynamic>),
status: json['status'] as int?, status: json['status'] as int?,
styles: json['styles'],
subtitle: json['subtitle'] as String?, subtitle: json['subtitle'] as String?,
title: json['title'] as String?, title: json['title'] as String?,
total: json['total'] as int?, total: json['total'] as int?,

View File

@@ -1,9 +0,0 @@
class Show {
int? wideScreen;
Show({this.wideScreen});
factory Show.fromJson(Map<String, dynamic> json) => Show(
wideScreen: json['wide_screen'] as int?,
);
}

View File

@@ -56,58 +56,18 @@ class _ArticlePageState extends State<ArticlePage>
tag: Utils.generateRandomString(8), tag: Utils.generateRandomString(8),
); );
bool _isFabVisible = true; bool _isFabVisible = true;
bool? _imageStatus;
late final AnimationController fabAnimationCtr; late final AnimationController fabAnimationCtr;
late final Animation<Offset> _anim; late final Animation<Offset> _anim;
late final List<double> _ratio = Pref.dynamicDetailRatio; late final List<double> _ratio = Pref.dynamicDetailRatio;
bool get _horizontalPreview => bool get _horizontalPreview =>
context.orientation == Orientation.landscape && _articleCtr.horizontalPreview &&
_articleCtr.horizontalPreview; context.orientation == Orientation.landscape;
late final _key = GlobalKey<ScaffoldState>(); late final _key = GlobalKey<ScaffoldState>();
Function(dynamic imgList, dynamic index)? get _getImageCallback => late Function(dynamic imgList, dynamic index)? _imageCallback;
_horizontalPreview
? (imgList, index) {
_imageStatus = true;
bool isFabVisible = _isFabVisible;
if (isFabVisible) {
_hideFab();
}
final ctr = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..forward();
PageUtils.onHorizontalPreview(
_key,
AnimationController(
vsync: this,
duration: Duration.zero,
),
ctr,
imgList,
index,
(value) async {
_imageStatus = null;
if (isFabVisible) {
isFabVisible = false;
_showFab();
}
if (value == false) {
await ctr.reverse();
}
try {
ctr.dispose();
} catch (_) {}
if (value == false) {
Get.back();
}
},
);
}
: null;
@override @override
void initState() { void initState() {
@@ -182,14 +142,21 @@ class _ArticlePageState extends State<ArticlePage>
EasyThrottle.throttle('replyReply', const Duration(milliseconds: 500), () { EasyThrottle.throttle('replyReply', const Duration(milliseconds: 500), () {
int oid = replyItem.oid.toInt(); int oid = replyItem.oid.toInt();
int rpid = replyItem.id.toInt(); int rpid = replyItem.id.toInt();
Widget replyReplyPage({ Widget replyReplyPage({bool showBackBtn = true}) => Scaffold(
bool automaticallyImplyLeading = true,
VoidCallback? onDispose,
}) => Scaffold(
appBar: AppBar( appBar: AppBar(
toolbarHeight: showBackBtn ? null : 45,
title: const Text('评论详情'), title: const Text('评论详情'),
titleSpacing: automaticallyImplyLeading ? null : 12, titleSpacing: showBackBtn ? null : 12,
automaticallyImplyLeading: automaticallyImplyLeading, automaticallyImplyLeading: showBackBtn,
actions: showBackBtn
? null
: [
IconButton(
tooltip: '关闭',
icon: const Icon(Icons.close, size: 20),
onPressed: Get.back,
),
],
), ),
body: SafeArea( body: SafeArea(
top: false, top: false,
@@ -202,7 +169,6 @@ class _ArticlePageState extends State<ArticlePage>
isVideoDetail: false, isVideoDetail: false,
replyType: _articleCtr.commentType, replyType: _articleCtr.commentType,
firstFloor: replyItem, firstFloor: replyItem,
onDispose: onDispose,
), ),
), ),
); );
@@ -226,14 +192,7 @@ class _ArticlePageState extends State<ArticlePage>
(context) => MediaQuery.removePadding( (context) => MediaQuery.removePadding(
context: context, context: context,
removeLeft: true, removeLeft: true,
child: replyReplyPage( child: replyReplyPage(showBackBtn: false),
automaticallyImplyLeading: false,
onDispose: () {
if (isFabVisible && _imageStatus != true) {
_showFab();
}
},
),
), ),
); );
} else { } else {
@@ -252,6 +211,17 @@ class _ArticlePageState extends State<ArticlePage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
_imageCallback = _horizontalPreview
? (imgList, index) {
_hideFab();
PageUtils.onHorizontalPreview(
_key,
this,
imgList,
index,
);
}
: null;
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: _buildAppBar, appBar: _buildAppBar,
@@ -386,7 +356,7 @@ class _ArticlePageState extends State<ArticlePage>
if (kDebugMode) debugPrint('json page'); if (kDebugMode) debugPrint('json page');
content = OpusContent( content = OpusContent(
opus: _articleCtr.opus!, opus: _articleCtr.opus!,
callback: _getImageCallback, callback: _imageCallback,
maxWidth: maxWidth, maxWidth: maxWidth,
); );
} else if (_articleCtr.opusData?.modules.moduleBlocked != null) { } else if (_articleCtr.opusData?.modules.moduleBlocked != null) {
@@ -408,7 +378,7 @@ class _ArticlePageState extends State<ArticlePage>
context: context, context: context,
html: _articleCtr.articleData!.content!, html: _articleCtr.articleData!.content!,
maxWidth: maxWidth, maxWidth: maxWidth,
callback: _getImageCallback, callback: _imageCallback,
), ),
); );
} else { } else {
@@ -419,7 +389,7 @@ class _ArticlePageState extends State<ArticlePage>
context: context, context: context,
element: res.body!.children[index], element: res.body!.children[index],
maxWidth: maxWidth, maxWidth: maxWidth,
callback: _getImageCallback, callback: _imageCallback,
); );
}, },
separatorBuilder: (context, index) => separatorBuilder: (context, index) =>
@@ -655,7 +625,7 @@ class _ArticlePageState extends State<ArticlePage>
onDelete: (item, subIndex) => onDelete: (item, subIndex) =>
_articleCtr.onRemove(index, item, subIndex), _articleCtr.onRemove(index, item, subIndex),
upMid: _articleCtr.upMid, upMid: _articleCtr.upMid,
callback: _getImageCallback, callback: _imageCallback,
onCheckReply: (item) => onCheckReply: (item) =>
_articleCtr.onCheckReply(item, isManual: true), _articleCtr.onCheckReply(item, isManual: true),
onToggleTop: (item) => _articleCtr.onToggleTop( onToggleTop: (item) => _articleCtr.onToggleTop(

View File

@@ -35,7 +35,7 @@ class DynamicsController extends GetxController
late int _upPage = 1; late int _upPage = 1;
late bool _upEnd = false; late bool _upEnd = false;
List<UpItem>? _cacheUpList; List<UpItem>? _cacheUpList;
late final showAllUp = Pref.dynamicsShowAllFollowedUp; late final _showAllUp = Pref.dynamicsShowAllFollowedUp;
late bool showLiveUp = Pref.expandDynLivePanel; late bool showLiveUp = Pref.expandDynLivePanel;
final upPanelPosition = Pref.upPanelPosition; final upPanelPosition = Pref.upPanelPosition;
@@ -55,7 +55,7 @@ class DynamicsController extends GetxController
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
if (showAllUp) { if (_showAllUp) {
scrollController.addListener(listener); scrollController.addListener(listener);
} }
queryFollowUp(); queryFollowUp();
@@ -64,15 +64,11 @@ class DynamicsController extends GetxController
void listener() { void listener() {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) { scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle( queryAllUp();
'following',
const Duration(seconds: 1),
queryFollowing2,
);
} }
} }
Future<void> queryFollowing2() async { Future<void> queryAllUp() async {
if (isQuerying) return; if (isQuerying) return;
isQuerying = true; isQuerying = true;
if (_upEnd) { if (_upEnd) {
@@ -116,7 +112,7 @@ class DynamicsController extends GetxController
final res = await Future.wait([ final res = await Future.wait([
DynamicsHttp.followUp(), DynamicsHttp.followUp(),
if (showAllUp) if (_showAllUp)
FollowHttp.followings( FollowHttp.followings(
vmid: accountService.mid, vmid: accountService.mid,
pn: _upPage, pn: _upPage,
@@ -126,11 +122,10 @@ class DynamicsController extends GetxController
]); ]);
final first = res.first; final first = res.first;
final second = res.getOrNull(1);
if (first.isSuccess) { if (first.isSuccess) {
FollowUpModel data = first.data as FollowUpModel; FollowUpModel data = first.data as FollowUpModel;
final second = res.getOrNull(1);
if (second != null && second.isSuccess) { if (second != null && second.isSuccess) {
_cacheUpList = List<UpItem>.from(data.upList);
FollowData data1 = second.data as FollowData; FollowData data1 = second.data as FollowData;
final list1 = data1.list; final list1 = data1.list;
@@ -140,6 +135,7 @@ class DynamicsController extends GetxController
} }
final list = data.upList; final list = data.upList;
_cacheUpList = List<UpItem>.from(list);
list.addAll(list1..removeWhere((e) => list.contains(e))); list.addAll(list1..removeWhere((e) => list.contains(e)));
} }
upState.value = Success(data); upState.value = Success(data);
@@ -166,7 +162,7 @@ class DynamicsController extends GetxController
@override @override
Future<void> onRefresh() async { Future<void> onRefresh() async {
if (showAllUp) { if (_showAllUp) {
_upPage = 1; _upPage = 1;
_cacheUpList = null; _cacheUpList = null;
} }

View File

@@ -110,7 +110,7 @@ class _UpPanelState extends State<UpPanel> {
), ),
), ),
), ),
if (upList.isNotEmpty == true) if (upList.isNotEmpty)
SliverList.builder( SliverList.builder(
itemCount: upList.length, itemCount: upList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {

View File

@@ -49,7 +49,6 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
final RxBool _visibleTitle = false.obs; final RxBool _visibleTitle = false.obs;
bool _isFabVisible = true; bool _isFabVisible = true;
bool? _imageStatus;
late final List<double> _ratio = Pref.dynamicDetailRatio; late final List<double> _ratio = Pref.dynamicDetailRatio;
@@ -59,46 +58,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
late final _key = GlobalKey<ScaffoldState>(); late final _key = GlobalKey<ScaffoldState>();
Function(dynamic imgList, dynamic index)? get _getImageCallback => late Function(List<String> imgList, int index)? _imageCallback;
_horizontalPreview
? (imgList, index) {
_imageStatus = true;
bool isFabVisible = _isFabVisible;
if (isFabVisible) {
_hideFab();
}
final ctr = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..forward();
PageUtils.onHorizontalPreview(
_key,
AnimationController(
vsync: this,
duration: Duration.zero,
),
ctr,
imgList,
index,
(value) async {
_imageStatus = null;
if (isFabVisible) {
isFabVisible = false;
_showFab();
}
if (value == false) {
await ctr.reverse();
}
try {
ctr.dispose();
} catch (_) {}
if (value == false) {
Get.back();
}
},
);
}
: null;
@override @override
void initState() { void initState() {
@@ -126,14 +86,21 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
EasyThrottle.throttle('replyReply', const Duration(milliseconds: 500), () { EasyThrottle.throttle('replyReply', const Duration(milliseconds: 500), () {
int oid = replyItem.oid.toInt(); int oid = replyItem.oid.toInt();
int rpid = replyItem.id.toInt(); int rpid = replyItem.id.toInt();
Widget replyReplyPage({ Widget replyReplyPage({bool showBackBtn = true}) => Scaffold(
bool automaticallyImplyLeading = true,
VoidCallback? onDispose,
}) => Scaffold(
appBar: AppBar( appBar: AppBar(
toolbarHeight: showBackBtn ? null : 45,
title: const Text('评论详情'), title: const Text('评论详情'),
titleSpacing: automaticallyImplyLeading ? null : 12, titleSpacing: showBackBtn ? null : 12,
automaticallyImplyLeading: automaticallyImplyLeading, automaticallyImplyLeading: showBackBtn,
actions: showBackBtn
? null
: [
IconButton(
tooltip: '关闭',
icon: const Icon(Icons.close, size: 20),
onPressed: Get.back,
),
],
), ),
body: SafeArea( body: SafeArea(
top: false, top: false,
@@ -146,7 +113,6 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
isVideoDetail: false, isVideoDetail: false,
replyType: _controller.replyType, replyType: _controller.replyType,
firstFloor: replyItem, firstFloor: replyItem,
onDispose: onDispose,
), ),
), ),
); );
@@ -170,14 +136,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
(context) => MediaQuery.removePadding( (context) => MediaQuery.removePadding(
context: context, context: context,
removeLeft: true, removeLeft: true,
child: replyReplyPage( child: replyReplyPage(showBackBtn: false),
automaticallyImplyLeading: false,
onDispose: () {
if (isFabVisible && _imageStatus != true) {
_showFab();
}
},
),
), ),
); );
} else { } else {
@@ -247,6 +206,17 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
_imageCallback = _horizontalPreview
? (imgList, index) {
_hideFab();
PageUtils.onHorizontalPreview(
_key,
this,
imgList,
index,
);
}
: null;
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
@@ -343,7 +313,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
child: DynamicPanel( child: DynamicPanel(
item: _controller.dynItem, item: _controller.dynItem,
isDetail: true, isDetail: true,
callback: _getImageCallback, callback: _imageCallback,
), ),
), ),
replyPersistentHeader(theme), replyPersistentHeader(theme),
@@ -378,7 +348,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
child: DynamicPanel( child: DynamicPanel(
item: _controller.dynItem, item: _controller.dynItem,
isDetail: true, isDetail: true,
callback: _getImageCallback, callback: _imageCallback,
), ),
), ),
), ),
@@ -788,7 +758,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
onDelete: (item, subIndex) => onDelete: (item, subIndex) =>
_controller.onRemove(index, item, subIndex), _controller.onRemove(index, item, subIndex),
upMid: _controller.upMid, upMid: _controller.upMid,
callback: _getImageCallback, callback: _imageCallback,
onCheckReply: (item) => onCheckReply: (item) =>
_controller.onCheckReply(item, isManual: true), _controller.onCheckReply(item, isManual: true),
onToggleTop: (item) => _controller.onToggleTop( onToggleTop: (item) => _controller.onToggleTop(

View File

@@ -96,7 +96,11 @@ class _PgcIndexPageState extends State<PgcIndexPage>
}; };
} }
Widget _buildSortWidget(ThemeData theme, count, data) => Column( Widget _buildSortWidget(
ThemeData theme,
int count,
PgcIndexConditionData data,
) => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -122,57 +126,55 @@ class _PgcIndexPageState extends State<PgcIndexPage>
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, horizontal: 12,
), ),
childBuilder: (childIndex) => Obx( childBuilder: (childIndex) {
() => SearchText( final e = item[childIndex];
bgColor: return Obx(
(item[childIndex] is PgcConditionOrder () => SearchText(
? _ctr.indexParams['order'] bgColor:
: _ctr.indexParams[data (e is PgcConditionOrder
.filter![data.order?.isNotEmpty == ? _ctr.indexParams['order']
true : _ctr.indexParams[data
? index - 1 .filter![data.order?.isNotEmpty ==
: index] true
.field]) == ? index - 1
(item[childIndex] is PgcConditionOrder : index]
? item[childIndex].field .field]) ==
: item[childIndex].keyword) (e is PgcConditionOrder ? e.field : e.keyword)
? theme.colorScheme.secondaryContainer ? theme.colorScheme.secondaryContainer
: Colors.transparent, : Colors.transparent,
textColor: textColor:
(item[childIndex] is PgcConditionOrder (e is PgcConditionOrder
? _ctr.indexParams['order'] ? _ctr.indexParams['order']
: _ctr.indexParams[data : _ctr.indexParams[data
.filter![data.order?.isNotEmpty == .filter![data.order?.isNotEmpty ==
true true
? index - 1 ? index - 1
: index] : index]
.field]) == .field]) ==
(item[childIndex] is PgcConditionOrder (e is PgcConditionOrder ? e.field : e.keyword)
? item[childIndex].field ? theme.colorScheme.onSecondaryContainer
: item[childIndex].keyword) : theme.colorScheme.onSurfaceVariant,
? theme.colorScheme.onSecondaryContainer text: e.name,
: theme.colorScheme.onSurfaceVariant, padding: const EdgeInsets.symmetric(
text: item[childIndex].name, horizontal: 6,
padding: const EdgeInsets.symmetric( vertical: 3,
horizontal: 6, ),
vertical: 3, onTap: (_) {
String name = e is PgcConditionOrder
? 'order'
: data
.filter![data.order?.isNotEmpty == true
? index - 1
: index]
.field!;
_ctr.indexParams[name] = (e is PgcConditionOrder
? e.field
: e.keyword);
_ctr.onReload();
},
), ),
onTap: (_) { );
String name = item[childIndex] is PgcConditionOrder },
? 'order'
: data
.filter![data.order?.isNotEmpty == true
? index - 1
: index]
.field!;
_ctr.indexParams[name] =
(item[childIndex] is PgcConditionOrder
? item[childIndex].field
: item[childIndex].keyword);
_ctr.onReload();
},
),
),
itemCount: item!.length, itemCount: item!.length,
), ),
) )

View File

@@ -1027,7 +1027,9 @@ class VideoDetailController extends GetxController
.where((i) => i.id == currentVideoQa.code) .where((i) => i.id == currentVideoQa.code)
.toList(); .toList();
final List supportDecodeFormats = videoList.map((e) => e.codecs!).toList(); final List<String> supportDecodeFormats = videoList
.map((e) => e.codecs!)
.toList();
VideoDecodeFormatType defaultDecodeFormats = VideoDecodeFormatType defaultDecodeFormats =
VideoDecodeFormatTypeExt.fromString(cacheDecode)!; VideoDecodeFormatTypeExt.fromString(cacheDecode)!;
VideoDecodeFormatType secondDecodeFormats = VideoDecodeFormatType secondDecodeFormats =
@@ -1264,7 +1266,7 @@ class VideoDetailController extends GetxController
/// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式 /// 优先顺序 设置中指定解码格式 -> 当前可选的首个解码格式
final List<FormatItem> supportFormats = data.supportFormats!; final List<FormatItem> supportFormats = data.supportFormats!;
// 根据画质选编码格式 // 根据画质选编码格式
final List supportDecodeFormats = supportFormats final List<String> supportDecodeFormats = supportFormats
.firstWhere( .firstWhere(
(e) => e.quality == resVideoQa, (e) => e.quality == resVideoQa,
orElse: () => supportFormats.first, orElse: () => supportFormats.first,

View File

@@ -329,7 +329,7 @@ class PgcIntroController extends CommonIntroController {
} }
bool prevPlay() { bool prevPlay() {
List episodes = pgcItem.episodes!; final episodes = pgcItem.episodes!;
VideoDetailController videoDetailCtr = Get.find<VideoDetailController>( VideoDetailController videoDetailCtr = Get.find<VideoDetailController>(
tag: Get.arguments['heroTag'], tag: Get.arguments['heroTag'],
); );
@@ -345,19 +345,21 @@ class PgcIntroController extends CommonIntroController {
return false; return false;
} }
} }
int epid = episodes[prevIndex].epId; final episode = episodes[prevIndex];
int cid = episodes[prevIndex].cid; changeSeasonOrbangu(
String bvid = episodes[prevIndex].bvid; episode.epId,
int aid = episodes[prevIndex].aid; episode.bvid,
dynamic cover = episodes[prevIndex].cover; episode.cid,
changeSeasonOrbangu(epid, bvid, cid, aid, cover); episode.aid,
episode.cover,
);
return true; return true;
} }
/// 列表循环或者顺序播放时,自动播放下一个;自动连播时,播放相关视频 /// 列表循环或者顺序播放时,自动播放下一个;自动连播时,播放相关视频
bool nextPlay() { bool nextPlay() {
try { try {
List episodes = pgcItem.episodes!; final episodes = pgcItem.episodes!;
VideoDetailController videoDetailCtr = Get.find<VideoDetailController>( VideoDetailController videoDetailCtr = Get.find<VideoDetailController>(
tag: Get.arguments['heroTag'], tag: Get.arguments['heroTag'],
); );
@@ -377,12 +379,14 @@ class PgcIntroController extends CommonIntroController {
return false; return false;
} }
} }
int epid = episodes[nextIndex].epId; final episode = episodes[nextIndex];
int cid = episodes[nextIndex].cid; changeSeasonOrbangu(
String bvid = episodes[nextIndex].bvid; episode.epId,
int aid = episodes[nextIndex].aid; episode.bvid,
dynamic cover = episodes[nextIndex].cover; episode.cid,
changeSeasonOrbangu(epid, bvid, cid, aid, cover); episode.aid,
episode.cover,
);
return true; return true;
} catch (_) { } catch (_) {
return false; return false;

View File

@@ -28,7 +28,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
this.isDialogue = false, this.isDialogue = false,
this.onViewImage, this.onViewImage,
this.onDismissed, this.onDismissed,
this.onDispose,
}); });
final int? id; final int? id;
final int oid; final int oid;
@@ -40,7 +39,6 @@ class VideoReplyReplyPanel extends CommonSlidePage {
final bool isDialogue; final bool isDialogue;
final VoidCallback? onViewImage; final VoidCallback? onViewImage;
final ValueChanged<int>? onDismissed; final ValueChanged<int>? onDismissed;
final VoidCallback? onDispose;
@override @override
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState(); State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
@@ -59,8 +57,9 @@ class _VideoReplyReplyPanelState
ReplyInfo? get firstFloor => widget.firstFloor ?? _controller.firstFloor; ReplyInfo? get firstFloor => widget.firstFloor ?? _controller.firstFloor;
bool get _horizontalPreview => bool get _horizontalPreview =>
context.orientation == Orientation.landscape && _controller.horizontalPreview &&
_controller.horizontalPreview; context.orientation == Orientation.landscape;
late Function(List<String> imgList, int index)? _imageCallback;
Animation<Color?>? colorAnimation; Animation<Color?>? colorAnimation;
@@ -83,36 +82,20 @@ class _VideoReplyReplyPanelState
@override @override
void dispose() { void dispose() {
widget.onDispose?.call();
Get.delete<VideoReplyReplyController>(tag: _tag); Get.delete<VideoReplyReplyController>(tag: _tag);
super.dispose(); super.dispose();
} }
Widget _header(ThemeData theme) => firstFloor == null
? _sortWidget(theme)
: ValueListenableBuilder<Iterable<ItemPosition>>(
valueListenable: itemPositionsListener.itemPositions,
builder: (context, positions, child) {
int min = -1;
if (positions.isNotEmpty) {
min = positions
.where(
(ItemPosition position) => position.itemTrailingEdge > 0,
)
.reduce(
(ItemPosition min, ItemPosition position) =>
position.itemTrailingEdge < min.itemTrailingEdge
? position
: min,
)
.index;
}
return min >= 2 ? _sortWidget(theme) : const SizedBox.shrink();
},
);
@override @override
Widget buildPage(ThemeData theme) { Widget buildPage(ThemeData theme) {
_imageCallback = _horizontalPreview
? (imgList, index) => PageUtils.onHorizontalPreview(
_key,
this,
imgList,
index,
)
: null;
return Scaffold( return Scaffold(
key: _key, key: _key,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
@@ -146,9 +129,7 @@ class _VideoReplyReplyPanelState
height: 1, height: 1,
color: theme.dividerColor.withValues(alpha: 0.1), color: theme.dividerColor.withValues(alpha: 0.1),
), ),
Expanded( Expanded(child: enableSlide ? slideList(theme) : buildList(theme)),
child: enableSlide ? slideList(theme) : buildList(theme),
),
], ],
), ),
); );
@@ -190,7 +171,7 @@ class _VideoReplyReplyPanelState
upMid: _controller.upMid, upMid: _controller.upMid,
onViewImage: widget.onViewImage, onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed, onDismissed: widget.onDismissed,
callback: _getImageCallback, callback: _imageCallback,
onCheckReply: (item) => onCheckReply: (item) =>
_controller.onCheckReply(item, isManual: true), _controller.onCheckReply(item, isManual: true),
); );
@@ -232,6 +213,29 @@ class _VideoReplyReplyPanelState
); );
} }
Widget _header(ThemeData theme) => firstFloor == null
? _sortWidget(theme)
: ValueListenableBuilder<Iterable<ItemPosition>>(
valueListenable: itemPositionsListener.itemPositions,
builder: (context, positions, child) {
int min = -1;
if (positions.isNotEmpty) {
min = positions
.where(
(ItemPosition position) => position.itemTrailingEdge > 0,
)
.reduce(
(ItemPosition min, ItemPosition position) =>
position.itemTrailingEdge < min.itemTrailingEdge
? position
: min,
)
.index;
}
return min >= 2 ? _sortWidget(theme) : const SizedBox.shrink();
},
);
Widget _sortWidget(ThemeData theme) => Container( Widget _sortWidget(ThemeData theme) => Container(
height: 40, height: 40,
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
@@ -261,7 +265,7 @@ class _VideoReplyReplyPanelState
SizedBox( SizedBox(
height: 35, height: 35,
child: TextButton.icon( child: TextButton.icon(
onPressed: () => _controller.queryBySort(), onPressed: _controller.queryBySort,
icon: Icon( icon: Icon(
Icons.sort, Icons.sort,
size: 16, size: 16,
@@ -282,36 +286,6 @@ class _VideoReplyReplyPanelState
), ),
); );
Function(List<String>, int)? get _getImageCallback => _horizontalPreview
? (imgList, index) {
final ctr = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..forward();
PageUtils.onHorizontalPreview(
_key,
AnimationController(
vsync: this,
duration: Duration.zero,
),
ctr,
imgList,
index,
(value) async {
if (value == false) {
await ctr.reverse();
}
try {
ctr.dispose();
} catch (_) {}
if (value == false) {
Get.back();
}
},
);
}
: null;
Widget _buildBody( Widget _buildBody(
ThemeData theme, ThemeData theme,
LoadingState<List<ReplyInfo>?> loadingState, LoadingState<List<ReplyInfo>?> loadingState,
@@ -381,9 +355,7 @@ class _VideoReplyReplyPanelState
replyLevel: widget.isDialogue ? 3 : 2, replyLevel: widget.isDialogue ? 3 : 2,
onReply: (replyItem) => onReply: (replyItem) =>
_controller.onReply(context, replyItem: replyItem, index: index), _controller.onReply(context, replyItem: replyItem, index: index),
onDelete: (item, subIndex) { onDelete: (item, subIndex) => _controller.onRemove(index, item, null),
_controller.onRemove(index, item, null);
},
upMid: _controller.upMid, upMid: _controller.upMid,
showDialogue: () => _key.currentState?.showBottomSheet( showDialogue: () => _key.currentState?.showBottomSheet(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -398,7 +370,7 @@ class _VideoReplyReplyPanelState
), ),
onViewImage: widget.onViewImage, onViewImage: widget.onViewImage,
onDismissed: widget.onDismissed, onDismissed: widget.onDismissed,
callback: _getImageCallback, callback: _imageCallback,
onCheckReply: (item) => _controller.onCheckReply(item, isManual: true), onCheckReply: (item) => _controller.onCheckReply(item, isManual: true),
); );
} }

View File

@@ -100,16 +100,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false; bool get isFullScreen => plPlayerController?.isFullScreen.value ?? false;
bool get _shouldShowSeasonPanel { bool get _shouldShowSeasonPanel {
final videoDetail = videoIntroController.videoDetail.value; late final videoDetail = videoIntroController.videoDetail.value;
return (videoDetail.ugcSeason != null || return videoDetailController.plPlayerController.horizontalSeasonPanel &&
(videoDetail.ugcSeason != null ||
((videoDetail.pages?.length ?? 0) > 1)) && ((videoDetail.pages?.length ?? 0) > 1)) &&
context.orientation == Orientation.landscape && context.orientation == Orientation.landscape;
videoDetailController.plPlayerController.horizontalSeasonPanel;
} }
bool get _horizontalPreview => bool get _horizontalPreview =>
context.orientation == Orientation.landscape && videoDetailController.plPlayerController.horizontalPreview &&
videoDetailController.plPlayerController.horizontalPreview; context.orientation == Orientation.landscape;
StreamSubscription? _listenerFS; StreamSubscription? _listenerFS;
@@ -2014,33 +2014,12 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
onViewImage: videoDetailController.onViewImage, onViewImage: videoDetailController.onViewImage,
onDismissed: videoDetailController.onDismissed, onDismissed: videoDetailController.onDismissed,
callback: _horizontalPreview callback: _horizontalPreview
? (imgList, index) { ? (imgList, index) => PageUtils.onHorizontalPreview(
final ctr = AnimationController( videoDetailController.childKey,
vsync: this, this,
duration: const Duration(milliseconds: 200), imgList,
)..forward(); index,
PageUtils.onHorizontalPreview( )
videoDetailController.childKey,
AnimationController(
vsync: this,
duration: Duration.zero,
),
ctr,
imgList,
index,
(value) async {
if (value == false) {
await ctr.reverse();
}
try {
ctr.dispose();
} catch (_) {}
if (value == false) {
Get.back();
}
},
);
}
: null, : null,
), ),
); );

View File

@@ -779,7 +779,7 @@ class HeaderControlState extends State<HeaderControl> {
final VideoItem firstVideo = videoDetailCtr.firstVideo; final VideoItem firstVideo = videoDetailCtr.firstVideo;
// 当前视频可用的解码格式 // 当前视频可用的解码格式
final List<FormatItem> videoFormat = videoInfo.supportFormats!; final List<FormatItem> videoFormat = videoInfo.supportFormats!;
final List? list = videoFormat final List<String>? list = videoFormat
.firstWhere((FormatItem e) => e.quality == firstVideo.quality.code) .firstWhere((FormatItem e) => e.quality == firstVideo.quality.code)
.codecs; .codecs;
if (list == null) { if (list == null) {
@@ -829,7 +829,7 @@ class HeaderControlState extends State<HeaderControl> {
VideoDecodeFormatTypeExt.fromString(i)!.description, VideoDecodeFormatTypeExt.fromString(i)!.description,
), ),
subtitle: Text( subtitle: Text(
i!, i,
style: subTitleStyle, style: subTitleStyle,
), ),
trailing: i.startsWith(currentDecodeFormats.code) trailing: i.startsWith(currentDecodeFormats.code)
@@ -2054,7 +2054,7 @@ class HeaderControlState extends State<HeaderControl> {
), ),
), ),
Obx( Obx(
() => videoDetailCtr.segmentList.isNotEmpty == true () => videoDetailCtr.segmentList.isNotEmpty
? SizedBox( ? SizedBox(
width: 42, width: 42,
height: 34, height: 34,

View File

@@ -515,12 +515,14 @@ class PageUtils {
static void onHorizontalPreview( static void onHorizontalPreview(
GlobalKey<ScaffoldState> key, GlobalKey<ScaffoldState> key,
transitionAnimationController, TickerProvider vsync,
ctr,
List<String> imgList, List<String> imgList,
index, int index,
onClose,
) { ) {
final ctr = AnimationController(
vsync: vsync,
duration: const Duration(milliseconds: 200),
)..forward();
key.currentState?.showBottomSheet( key.currentState?.showBottomSheet(
(context) { (context) {
return FadeTransition( return FadeTransition(
@@ -529,7 +531,17 @@ class PageUtils {
sources: imgList.map((url) => SourceModel(url: url)).toList(), sources: imgList.map((url) => SourceModel(url: url)).toList(),
initIndex: index, initIndex: index,
setStatusBar: false, setStatusBar: false,
onClose: onClose, onClose: (value) async {
if (value == false) {
await ctr.reverse();
}
try {
ctr.dispose();
} catch (_) {}
if (value == false) {
Get.back();
}
},
quality: GlobalData().imgQuality, quality: GlobalData().imgQuality,
), ),
); );
@@ -537,7 +549,6 @@ class PageUtils {
enableDrag: false, enableDrag: false,
elevation: 0, elevation: 0,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
transitionAnimationController: transitionAnimationController,
sheetAnimationStyle: const AnimationStyle(duration: Duration.zero), sheetAnimationStyle: const AnimationStyle(duration: Duration.zero),
); );
} }