mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-30 23:29:47 +08:00
@@ -81,8 +81,9 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
bool alwaysExapndIntroPanel = Pref.alwaysExapndIntroPanel;
|
||||
expandableCtr =
|
||||
ExpandableController(initialExpanded: alwaysExapndIntroPanel);
|
||||
expandableCtr = ExpandableController(
|
||||
initialExpanded: alwaysExapndIntroPanel,
|
||||
);
|
||||
if (!alwaysExapndIntroPanel && Pref.exapndIntroPanelH) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (Get.context!.orientation == Orientation.landscape &&
|
||||
@@ -103,8 +104,10 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
if (videoItem.title case String e) {
|
||||
videoDetail.value.title = e;
|
||||
} else if (videoItem.title case List list) {
|
||||
videoDetail.value.title =
|
||||
list.fold<String>('', (prev, val) => prev + val['text']);
|
||||
videoDetail.value.title = list.fold<String>(
|
||||
'',
|
||||
(prev, val) => prev + val['text'],
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
@@ -132,8 +135,9 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
}
|
||||
videoDetail.value = data;
|
||||
try {
|
||||
final videoDetailController =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
final videoDetailController = Get.find<VideoDetailController>(
|
||||
tag: heroTag,
|
||||
);
|
||||
if (videoDetailController.cover.value.isEmpty ||
|
||||
(videoDetailController.videoUrl.isNullOrEmpty &&
|
||||
!videoDetailController.isQuerying)) {
|
||||
@@ -141,8 +145,9 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
}
|
||||
if (videoDetailController.showReply) {
|
||||
try {
|
||||
final videoReplyController =
|
||||
Get.find<VideoReplyController>(tag: heroTag);
|
||||
final videoReplyController = Get.find<VideoReplyController>(
|
||||
tag: heroTag,
|
||||
);
|
||||
videoReplyController.count.value = data.stat?.reply ?? 0;
|
||||
} catch (_) {}
|
||||
}
|
||||
@@ -166,17 +171,19 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
// 获取up主粉丝数
|
||||
Future<void> queryUserStat(List<Staff>? staff) async {
|
||||
if (staff?.isNotEmpty == true) {
|
||||
Request().get(
|
||||
Api.relations,
|
||||
queryParameters: {'fids': staff!.map((item) => item.mid).join(',')},
|
||||
).then((res) {
|
||||
if (res.data['code'] == 0) {
|
||||
staffRelations.addAll({
|
||||
'status': true,
|
||||
if (res.data['data'] != null) ...res.data['data'],
|
||||
Request()
|
||||
.get(
|
||||
Api.relations,
|
||||
queryParameters: {'fids': staff!.map((item) => item.mid).join(',')},
|
||||
)
|
||||
.then((res) {
|
||||
if (res.data['code'] == 0) {
|
||||
staffRelations.addAll({
|
||||
'status': true,
|
||||
if (res.data['data'] != null) ...res.data['data'],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final mid = videoDetail.value.owner?.mid;
|
||||
if (mid == null) {
|
||||
@@ -258,8 +265,10 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
var result =
|
||||
await VideoHttp.dislikeVideo(bvid: bvid, type: !hasDislike.value);
|
||||
var result = await VideoHttp.dislikeVideo(
|
||||
bvid: bvid,
|
||||
type: !hasDislike.value,
|
||||
);
|
||||
if (result['status']) {
|
||||
if (!hasDislike.value) {
|
||||
SmartDialog.showToast('点踩成功');
|
||||
@@ -401,103 +410,106 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
// 分享视频
|
||||
void actionShareVideo(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
final videoDetail = this.videoDetail.value;
|
||||
String videoUrl = '${HttpString.baseUrl}/video/$bvid';
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'复制链接',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(videoUrl);
|
||||
},
|
||||
context: context,
|
||||
builder: (_) {
|
||||
final videoDetail = this.videoDetail.value;
|
||||
String videoUrl = '${HttpString.baseUrl}/video/$bvid';
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'复制链接',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'其它app打开',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
PageUtils.launchURL(videoUrl);
|
||||
},
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(videoUrl);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'其它app打开',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享视频',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.shareText('${videoDetail.title} '
|
||||
'UP主: ${videoDetail.owner!.name!}'
|
||||
' - $videoUrl');
|
||||
},
|
||||
onTap: () {
|
||||
Get.back();
|
||||
PageUtils.launchURL(videoUrl);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享视频',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享至动态',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (context) => RepostPanel(
|
||||
rid: videoDetail.aid,
|
||||
dynType: 8,
|
||||
pic: videoDetail.pic,
|
||||
title: videoDetail.title,
|
||||
uname: videoDetail.owner?.name,
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.shareText(
|
||||
'${videoDetail.title} '
|
||||
'UP主: ${videoDetail.owner!.name!}'
|
||||
' - $videoUrl',
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享至动态',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (context) => RepostPanel(
|
||||
rid: videoDetail.aid,
|
||||
dynType: 8,
|
||||
pic: videoDetail.pic,
|
||||
title: videoDetail.title,
|
||||
uname: videoDetail.owner?.name,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享至消息',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
try {
|
||||
PageUtils.pmShare(
|
||||
context,
|
||||
content: {
|
||||
"id": videoDetail.aid!.toString(),
|
||||
"title": videoDetail.title!,
|
||||
"headline": videoDetail.title!,
|
||||
"source": 5,
|
||||
"thumb": videoDetail.pic!,
|
||||
"author": videoDetail.owner!.name!,
|
||||
"author_id": videoDetail.owner!.mid!.toString(),
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'分享至消息',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
try {
|
||||
PageUtils.pmShare(
|
||||
context,
|
||||
content: {
|
||||
"id": videoDetail.aid!.toString(),
|
||||
"title": videoDetail.title!,
|
||||
"headline": videoDetail.title!,
|
||||
"source": 5,
|
||||
"thumb": videoDetail.pic!,
|
||||
"author": videoDetail.owner!.name!,
|
||||
"author_id": videoDetail.owner!.mid!.toString(),
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -695,13 +707,15 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
}
|
||||
}
|
||||
|
||||
final int currentIndex = episodes.indexWhere((e) =>
|
||||
e.cid ==
|
||||
(skipPages
|
||||
? videoDetail.isPageReversed == true
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid
|
||||
: lastPlayCid.value));
|
||||
final int currentIndex = episodes.indexWhere(
|
||||
(e) =>
|
||||
e.cid ==
|
||||
(skipPages
|
||||
? videoDetail.isPageReversed == true
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid
|
||||
: lastPlayCid.value),
|
||||
);
|
||||
int prevIndex = currentIndex - 1;
|
||||
final PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat;
|
||||
|
||||
@@ -759,13 +773,15 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int currentIndex = episodes.indexWhere((e) =>
|
||||
e.cid ==
|
||||
(skipPages
|
||||
? videoDetail.isPageReversed == true
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid
|
||||
: videoDetailCtr.cid.value));
|
||||
final int currentIndex = episodes.indexWhere(
|
||||
(e) =>
|
||||
e.cid ==
|
||||
(skipPages
|
||||
? videoDetail.isPageReversed == true
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid
|
||||
: videoDetailCtr.cid.value),
|
||||
);
|
||||
|
||||
int nextIndex = currentIndex + 1;
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
);
|
||||
return SliverLayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
bool isHorizontal = context.orientation == Orientation.landscape &&
|
||||
bool isHorizontal =
|
||||
context.orientation == Orientation.landscape &&
|
||||
constraints.crossAxisExtent >
|
||||
constraints.viewportMainAxisExtent * 1.25;
|
||||
return SliverPadding(
|
||||
@@ -145,7 +146,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: ReloadScrollPhysics(
|
||||
controller: introController),
|
||||
controller: introController,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 25,
|
||||
children: videoDetail.staff!
|
||||
@@ -157,9 +159,14 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
if (isHorizontal) ...[
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: actionGrid(context, isLoading,
|
||||
videoDetail, introController)),
|
||||
]
|
||||
child: actionGrid(
|
||||
context,
|
||||
isLoading,
|
||||
videoDetail,
|
||||
introController,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -181,8 +188,11 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
Feedback.forLongPress(context);
|
||||
Utils.copyText(videoDetail.title ?? '');
|
||||
},
|
||||
child: _buildVideoTitle(theme, videoDetail,
|
||||
isExpand: true),
|
||||
child: _buildVideoTitle(
|
||||
theme,
|
||||
videoDetail,
|
||||
isExpand: true,
|
||||
),
|
||||
),
|
||||
theme: expandTheme,
|
||||
),
|
||||
@@ -211,7 +221,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
const WidgetSpan(child: SizedBox(width: 2)),
|
||||
TextSpan(
|
||||
text: '${videoDetail.argueInfo!.argueMsg}',
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
@@ -258,7 +268,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return _buildTags(videoTags!);
|
||||
})
|
||||
}),
|
||||
],
|
||||
),
|
||||
theme: expandTheme,
|
||||
@@ -285,14 +295,19 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
if (!isHorizontal) ...[
|
||||
const SizedBox(height: 8),
|
||||
actionGrid(
|
||||
context, isLoading, videoDetail, introController),
|
||||
context,
|
||||
isLoading,
|
||||
videoDetail,
|
||||
introController,
|
||||
),
|
||||
],
|
||||
// 合集
|
||||
if (!isLoading &&
|
||||
videoDetail.ugcSeason != null &&
|
||||
(context.orientation != Orientation.landscape ||
|
||||
(context.orientation == Orientation.landscape &&
|
||||
!videoDetailCtr.plPlayerController
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel)))
|
||||
SeasonPanel(
|
||||
heroTag: widget.heroTag,
|
||||
@@ -305,7 +320,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
videoDetail.pages!.length > 1 &&
|
||||
(context.orientation != Orientation.landscape ||
|
||||
(context.orientation == Orientation.landscape &&
|
||||
!videoDetailCtr.plPlayerController
|
||||
!videoDetailCtr
|
||||
.plPlayerController
|
||||
.horizontalSeasonPanel))) ...[
|
||||
PagesPanel(
|
||||
heroTag: widget.heroTag,
|
||||
@@ -356,8 +372,11 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVideoTitle(ThemeData theme, VideoDetailData videoDetail,
|
||||
{bool isExpand = false}) {
|
||||
Widget _buildVideoTitle(
|
||||
ThemeData theme,
|
||||
VideoDetailData videoDetail, {
|
||||
bool isExpand = false,
|
||||
}) {
|
||||
late final isDark = theme.brightness == Brightness.dark;
|
||||
Widget child() {
|
||||
final videoLabel = videoDetailCtr.videoLabel.value;
|
||||
@@ -480,7 +499,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
4 || 6 => '已互关',
|
||||
128 => '已拉黑',
|
||||
-10 => '特别关注',
|
||||
_ => ' 关注 '
|
||||
_ => ' 关注 ',
|
||||
},
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
@@ -512,7 +531,8 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
? NumUtil.numFormat(videoDetail.stat!.like!)
|
||||
: null,
|
||||
needAnim: true,
|
||||
hasTriple: videoIntroController.hasLike.value &&
|
||||
hasTriple:
|
||||
videoIntroController.hasLike.value &&
|
||||
videoIntroController.hasCoin &&
|
||||
videoIntroController.hasFav.value,
|
||||
callBack: (start) {
|
||||
@@ -557,8 +577,10 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
selectIcon: const Icon(FontAwesomeIcons.solidStar),
|
||||
onTap: () => videoIntroController.showFavBottomSheet(context),
|
||||
onLongPress: () => videoIntroController
|
||||
.showFavBottomSheet(context, type: 'longPress'),
|
||||
onLongPress: () => videoIntroController.showFavBottomSheet(
|
||||
context,
|
||||
type: 'longPress',
|
||||
),
|
||||
selectStatus: videoIntroController.hasFav.value,
|
||||
semanticsLabel: '收藏',
|
||||
text: !isLoading
|
||||
@@ -582,8 +604,9 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
onTap: () => videoIntroController.actionShareVideo(context),
|
||||
selectStatus: false,
|
||||
semanticsLabel: '分享',
|
||||
text:
|
||||
!isLoading ? NumUtil.numFormat(videoDetail.stat!.share!) : null,
|
||||
text: !isLoading
|
||||
? NumUtil.numFormat(videoDetail.stat!.share!)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -726,38 +749,40 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: -6,
|
||||
child: Obx(() => introController.staffRelations['status'] ==
|
||||
true &&
|
||||
introController.staffRelations['${item.mid}'] == null
|
||||
? Material(
|
||||
type: MaterialType.transparency,
|
||||
shape: const CircleBorder(),
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () => RequestUtils.actionRelationMod(
|
||||
context: context,
|
||||
mid: item.mid,
|
||||
isFollow: false,
|
||||
callback: (val) {
|
||||
introController.staffRelations['${item.mid}'] =
|
||||
true;
|
||||
},
|
||||
),
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
child: Obx(
|
||||
() =>
|
||||
introController.staffRelations['status'] == true &&
|
||||
introController.staffRelations['${item.mid}'] == null
|
||||
? Material(
|
||||
type: MaterialType.transparency,
|
||||
shape: const CircleBorder(),
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () => RequestUtils.actionRelationMod(
|
||||
context: context,
|
||||
mid: item.mid,
|
||||
isFollow: false,
|
||||
callback: (val) {
|
||||
introController.staffRelations['${item.mid}'] =
|
||||
true;
|
||||
},
|
||||
),
|
||||
child: Icon(
|
||||
MdiIcons.plus,
|
||||
size: 16,
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
child: Ink(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
MdiIcons.plus,
|
||||
size: 16,
|
||||
color: theme.colorScheme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -791,124 +816,126 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAvatar(ThemeData theme, VoidCallback onPushMember) =>
|
||||
GestureDetector(
|
||||
onTap: onPushMember,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Obx(
|
||||
() {
|
||||
final userStat = introController.userStat.value;
|
||||
final isVip = (userStat.card?.vip?.status ?? 0) > 0;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
Widget _buildAvatar(
|
||||
ThemeData theme,
|
||||
VoidCallback onPushMember,
|
||||
) => GestureDetector(
|
||||
onTap: onPushMember,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Obx(
|
||||
() {
|
||||
final userStat = introController.userStat.value;
|
||||
final isVip = (userStat.card?.vip?.status ?? 0) > 0;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
PendantAvatar(
|
||||
avatar: userStat.card?.face,
|
||||
size: 35,
|
||||
badgeSize: 14,
|
||||
isVip: isVip,
|
||||
officialType: userStat.card?.officialVerify?.type,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
PendantAvatar(
|
||||
avatar: userStat.card?.face,
|
||||
size: 35,
|
||||
badgeSize: 14,
|
||||
isVip: isVip,
|
||||
officialType: userStat.card?.officialVerify?.type,
|
||||
Text(
|
||||
userStat.card?.name ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isVip && userStat.card?.vip?.type == 2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
userStat.card?.name ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isVip && userStat.card?.vip?.type == 2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 0),
|
||||
Text(
|
||||
'${NumUtil.numFormat(userStat.follower)}粉丝 ${'${NumUtil.numFormat(userStat.archiveCount)}视频'}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 0),
|
||||
Text(
|
||||
'${NumUtil.numFormat(userStat.follower)}粉丝 ${'${NumUtil.numFormat(userStat.archiveCount)}视频'}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildInfo(ThemeData theme, VideoDetailData videoDetail) => Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
StatWidget(
|
||||
type: StatType.play,
|
||||
value: videoDetail.stat?.view,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
StatWidget(
|
||||
type: StatType.danmaku,
|
||||
value: videoDetail.stat?.danmaku,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
Text(
|
||||
DateUtil.format(videoDetail.pubdate),
|
||||
spacing: 10,
|
||||
children: [
|
||||
StatWidget(
|
||||
type: StatType.play,
|
||||
value: videoDetail.stat?.view,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
StatWidget(
|
||||
type: StatType.danmaku,
|
||||
value: videoDetail.stat?.danmaku,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
Text(
|
||||
DateUtil.format(videoDetail.pubdate),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
if (MineController.anonymity.value)
|
||||
Icon(
|
||||
MdiIcons.incognito,
|
||||
size: 15,
|
||||
color: theme.colorScheme.outline,
|
||||
semanticLabel: '无痕',
|
||||
),
|
||||
if (introController.isShowOnlineTotal)
|
||||
Obx(
|
||||
() => Text(
|
||||
'${introController.total.value}人在看',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
if (MineController.anonymity.value)
|
||||
Icon(
|
||||
MdiIcons.incognito,
|
||||
size: 15,
|
||||
color: theme.colorScheme.outline,
|
||||
semanticLabel: '无痕',
|
||||
),
|
||||
if (introController.isShowOnlineTotal)
|
||||
Obx(
|
||||
() => Text(
|
||||
'${introController.total.value}人在看',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Widget get _aiBtn => Positioned(
|
||||
right: 10,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Center(
|
||||
child: Semantics(
|
||||
label: 'AI总结',
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
if (introController.aiConclusionResult == null) {
|
||||
await introController.aiConclusion();
|
||||
}
|
||||
if (introController.aiConclusionResult == null) {
|
||||
return;
|
||||
}
|
||||
if (introController.aiConclusionResult!.summary?.isNotEmpty ==
|
||||
true ||
|
||||
introController.aiConclusionResult!.outline?.isNotEmpty ==
|
||||
true) {
|
||||
widget.showAiBottomSheet();
|
||||
} else {
|
||||
SmartDialog.showToast("当前视频不支持AI视频总结");
|
||||
}
|
||||
},
|
||||
child: Image.asset('assets/images/ai.png', height: 22),
|
||||
),
|
||||
),
|
||||
right: 10,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Center(
|
||||
child: Semantics(
|
||||
label: 'AI总结',
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
if (introController.aiConclusionResult == null) {
|
||||
await introController.aiConclusion();
|
||||
}
|
||||
if (introController.aiConclusionResult == null) {
|
||||
return;
|
||||
}
|
||||
if (introController.aiConclusionResult!.summary?.isNotEmpty ==
|
||||
true ||
|
||||
introController.aiConclusionResult!.outline?.isNotEmpty ==
|
||||
true) {
|
||||
widget.showAiBottomSheet();
|
||||
} else {
|
||||
SmartDialog.showToast("当前视频不支持AI视频总结");
|
||||
}
|
||||
},
|
||||
child: Image.asset('assets/images/ai.png', height: 22),
|
||||
),
|
||||
);
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTags(List<VideoTagItem> tags) {
|
||||
return GestureDetector(
|
||||
|
||||
@@ -60,10 +60,11 @@ class ActionRowLineItem extends StatelessWidget {
|
||||
child: Text(
|
||||
text!,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: selectStatus
|
||||
? theme.colorScheme.onSecondaryContainer
|
||||
: theme.colorScheme.outline),
|
||||
fontSize: 13,
|
||||
color: selectStatus
|
||||
? theme.colorScheme.onSecondaryContainer
|
||||
: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -44,8 +44,9 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_videoDetailController =
|
||||
Get.find<VideoDetailController>(tag: widget.heroTag);
|
||||
_videoDetailController = Get.find<VideoDetailController>(
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
if (widget.list == null) {
|
||||
cid = widget.videoIntroController.lastPlayCid.value;
|
||||
pageIndex = pages.indexWhere((Part e) => e.cid == cid);
|
||||
@@ -68,8 +69,9 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
}
|
||||
const double itemWidth = 150;
|
||||
final double targetOffset = (pageIndex * itemWidth - itemWidth / 2).clamp(
|
||||
_scrollController.position.minScrollExtent,
|
||||
_scrollController.position.maxScrollExtent);
|
||||
_scrollController.position.minScrollExtent,
|
||||
_scrollController.position.maxScrollExtent,
|
||||
);
|
||||
_scrollController.animateTo(
|
||||
targetOffset,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
@@ -162,7 +164,10 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
widget.cover,
|
||||
);
|
||||
if (widget.list != null &&
|
||||
widget.videoIntroController.videoDetail.value
|
||||
widget
|
||||
.videoIntroController
|
||||
.videoDetail
|
||||
.value
|
||||
.ugcSeason !=
|
||||
null) {
|
||||
_videoDetailController.seasonCid = pages.first.cid;
|
||||
@@ -179,7 +184,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
height: 12,
|
||||
semanticLabel: "正在播放:",
|
||||
),
|
||||
const SizedBox(width: 6)
|
||||
const SizedBox(width: 6),
|
||||
],
|
||||
Expanded(
|
||||
child: Text(
|
||||
@@ -202,7 +207,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,19 +40,20 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_videoDetailController =
|
||||
Get.find<VideoDetailController>(tag: widget.heroTag);
|
||||
_videoDetailController = Get.find<VideoDetailController>(
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
_videoDetailController.seasonCid =
|
||||
videoIntroController.lastPlayCid.value != 0
|
||||
? (videoDetail.pages?.isNotEmpty == true
|
||||
? videoDetail.isPageReversed
|
||||
? (videoDetail.pages?.isNotEmpty == true
|
||||
? videoDetail.isPageReversed
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid
|
||||
: videoIntroController.lastPlayCid.value)
|
||||
: videoDetail.isPageReversed
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid;
|
||||
: videoIntroController.lastPlayCid.value)
|
||||
: videoDetail.isPageReversed
|
||||
? videoDetail.pages!.last.cid
|
||||
: videoDetail.pages!.first.cid;
|
||||
|
||||
/// 根据 cid 找到对应集,找到对应 episodes
|
||||
/// 有多个episodes时,只显示其中一个
|
||||
@@ -63,7 +64,8 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
|
||||
/// 取对应 season_id 的 episodes
|
||||
currentIndex.value = episodes.indexWhere(
|
||||
(EpisodeItem e) => e.cid == _videoDetailController.seasonCid);
|
||||
(EpisodeItem e) => e.cid == _videoDetailController.seasonCid,
|
||||
);
|
||||
_listener = _videoDetailController.cid.listen((int cid) {
|
||||
if (_videoDetailController.seasonCid != cid) {
|
||||
bool isPart =
|
||||
@@ -74,7 +76,8 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
}
|
||||
_findEpisode();
|
||||
currentIndex.value = episodes.indexWhere(
|
||||
(EpisodeItem e) => e.cid == _videoDetailController.seasonCid);
|
||||
(EpisodeItem e) => e.cid == _videoDetailController.seasonCid,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,13 +107,13 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
onTap: widget.onTap == false
|
||||
? null
|
||||
: () => widget.showEpisodes(
|
||||
_videoDetailController.seasonIndex.value,
|
||||
videoDetail.ugcSeason,
|
||||
null,
|
||||
_videoDetailController.bvid,
|
||||
null,
|
||||
_videoDetailController.seasonCid,
|
||||
),
|
||||
_videoDetailController.seasonIndex.value,
|
||||
videoDetail.ugcSeason,
|
||||
null,
|
||||
_videoDetailController.bvid,
|
||||
null,
|
||||
_videoDetailController.seasonCid,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
child: Row(
|
||||
@@ -143,7 +146,7 @@ class _SeasonPanelState extends State<SeasonPanel> {
|
||||
Icons.arrow_forward_ios_outlined,
|
||||
size: 13,
|
||||
semanticLabel: '查看',
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user