mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-16 14:20:13 +08:00
@@ -88,12 +88,13 @@ class _AiDetailState extends CommonCollapseSlidePageState<AiConclusionPanel> {
|
||||
sliver: SliverList.builder(
|
||||
itemCount: widget.item.outline!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.item.outline![index];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (index != 0) const SizedBox(height: 10),
|
||||
SelectableText(
|
||||
widget.item.outline![index].title!,
|
||||
item.title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -101,9 +102,8 @@ class _AiDetailState extends CommonCollapseSlidePageState<AiConclusionPanel> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
if (widget.item.outline![index].partOutline?.isNotEmpty ==
|
||||
true)
|
||||
...widget.item.outline![index].partOutline!.map(
|
||||
if (item.partOutline?.isNotEmpty == true)
|
||||
...item.partOutline!.map(
|
||||
(item) => Wrap(
|
||||
children: [
|
||||
SelectableText.rich(
|
||||
|
||||
@@ -384,17 +384,16 @@ class VideoDetailController extends GetxController
|
||||
: null,
|
||||
onDelete: sourceType == 'watchLater' ||
|
||||
(sourceType == 'fav' && Get.arguments?['isOwner'] == true)
|
||||
? (index) async {
|
||||
? (item, index) async {
|
||||
if (sourceType == 'watchLater') {
|
||||
var res = await UserHttp.toViewDel(
|
||||
aids: [mediaList[index].aid],
|
||||
aids: [item.aid],
|
||||
);
|
||||
if (res['status']) {
|
||||
mediaList.removeAt(index);
|
||||
}
|
||||
SmartDialog.showToast(res['msg']);
|
||||
} else {
|
||||
final item = mediaList[index];
|
||||
var res = await FavHttp.delFav(
|
||||
ids: ['${item.aid}:${item.type}'],
|
||||
delIds: '${Get.arguments?['mediaId']}',
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/data.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/staff.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_tag/data.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
@@ -292,73 +294,7 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: GestureDetector(
|
||||
onTap: onPushMember,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => PendantAvatar(
|
||||
avatar: videoIntroController
|
||||
.userStat['card']?['face'],
|
||||
size: 35,
|
||||
badgeSize: 14,
|
||||
isVip: (videoIntroController
|
||||
.userStat['card']
|
||||
?['vip']?['status'] ??
|
||||
-1) >
|
||||
0,
|
||||
officialType: videoIntroController
|
||||
.userStat['card']
|
||||
?['official_verify']?['type'],
|
||||
// garbPendantImage: videoIntroController.userStat.value['card']?['pendant']?['image'],
|
||||
)),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Obx(
|
||||
() => Text(
|
||||
videoIntroController
|
||||
.userStat['card']
|
||||
?['name'] ??
|
||||
"",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: (videoIntroController.userStat[
|
||||
'card']
|
||||
?['vip']
|
||||
?['status'] ??
|
||||
-1) >
|
||||
0 &&
|
||||
videoIntroController
|
||||
.userStat[
|
||||
'card']?[
|
||||
'vip']?['type'] ==
|
||||
2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 0),
|
||||
Obx(
|
||||
() => Text(
|
||||
'${NumUtil.numFormat(videoIntroController.userStat['follower'])}粉丝 ${videoIntroController.userStat['archive_count'] != null ? '${NumUtil.numFormat(videoIntroController.userStat['archive_count'])}视频' : ''}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: _buildAvatar(theme),
|
||||
),
|
||||
),
|
||||
followButton(context, theme),
|
||||
@@ -367,152 +303,10 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
child: SelfSizedHorizontalList(
|
||||
gapSize: 25,
|
||||
itemCount: videoItem['staff'].length,
|
||||
childBuilder: (index) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
int? ownerMid = !widget.isLoading
|
||||
? videoDetail.owner?.mid
|
||||
: videoItem['owner']?.mid;
|
||||
if (videoItem['staff'][index].mid ==
|
||||
ownerMid &&
|
||||
context.orientation ==
|
||||
Orientation.landscape &&
|
||||
_horizontalMemberPage) {
|
||||
widget.onShowMemberPage(ownerMid);
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/member?mid=${videoItem['staff'][index].mid}&from_view_aid=${videoDetailCtr.oid.value}',
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: ImageType.avatar,
|
||||
src: videoItem['staff'][index].face,
|
||||
width: 35,
|
||||
height: 35,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
if ((videoItem['staff'][index]
|
||||
.official
|
||||
?.type ??
|
||||
-1) !=
|
||||
-1)
|
||||
Positioned(
|
||||
right: -2,
|
||||
bottom: -2,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color:
|
||||
theme.colorScheme.surface,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.offline_bolt,
|
||||
color: videoItem['staff'][index]
|
||||
.official
|
||||
?.type ==
|
||||
0
|
||||
? const Color(0xFFFFCC00)
|
||||
: Colors.lightBlueAccent,
|
||||
size: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: -6,
|
||||
child: Obx(() => videoIntroController
|
||||
.staffRelations[
|
||||
'status'] ==
|
||||
true &&
|
||||
videoIntroController
|
||||
.staffRelations[
|
||||
'${videoItem['staff'][index].mid}'] ==
|
||||
null
|
||||
? Material(
|
||||
type:
|
||||
MaterialType.transparency,
|
||||
shape: const CircleBorder(),
|
||||
child: InkWell(
|
||||
customBorder:
|
||||
const CircleBorder(),
|
||||
onTap: () => RequestUtils
|
||||
.actionRelationMod(
|
||||
context: context,
|
||||
mid: videoItem['staff']
|
||||
[index]
|
||||
.mid,
|
||||
isFollow: false,
|
||||
callback: (val) {
|
||||
videoIntroController
|
||||
.staffRelations[
|
||||
'${videoItem['staff'][index].mid}'] = true;
|
||||
},
|
||||
),
|
||||
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(width: 8),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
videoItem['staff'][index].name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: videoItem['staff'][index]
|
||||
.vip
|
||||
.status >
|
||||
0 &&
|
||||
videoItem['staff'][index]
|
||||
.vip
|
||||
.type ==
|
||||
2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
videoItem['staff'][index].title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
childBuilder: (index) {
|
||||
return _buildStaff(
|
||||
theme, videoItem['staff'][index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (isHorizontal) ...[
|
||||
@@ -554,88 +348,8 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
StatWidget(
|
||||
type: StatType.play,
|
||||
value: !widget.isLoading
|
||||
? videoDetail.stat?.view
|
||||
: videoItem['stat']?.view,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
StatWidget(
|
||||
type: StatType.danmaku,
|
||||
value: !widget.isLoading
|
||||
? videoDetail.stat?.danmaku
|
||||
: videoItem['stat']?.danmu,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
Text(
|
||||
DateUtil.format(
|
||||
!widget.isLoading
|
||||
? videoDetail.pubdate
|
||||
: videoItem['pubdate'],
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
if (MineController.anonymity.value)
|
||||
Icon(
|
||||
MdiIcons.incognito,
|
||||
size: 15,
|
||||
color: theme.colorScheme.outline,
|
||||
semanticLabel: '无痕',
|
||||
),
|
||||
if (videoIntroController.isShowOnlineTotal)
|
||||
Obx(
|
||||
() => Text(
|
||||
'${videoIntroController.total.value}人在看',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (videoIntroController.enableAi)
|
||||
Positioned(
|
||||
right: 10,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Center(
|
||||
child: Semantics(
|
||||
label: 'AI总结',
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
if (videoIntroController.aiConclusionResult ==
|
||||
null) {
|
||||
await videoIntroController.aiConclusion();
|
||||
}
|
||||
if (videoIntroController.aiConclusionResult ==
|
||||
null) {
|
||||
return;
|
||||
}
|
||||
if (videoIntroController.aiConclusionResult!
|
||||
.summary?.isNotEmpty ==
|
||||
true ||
|
||||
videoIntroController.aiConclusionResult!
|
||||
.outline?.isNotEmpty ==
|
||||
true) {
|
||||
widget.showAiBottomSheet();
|
||||
} else {
|
||||
SmartDialog.showToast("当前视频不支持AI视频总结");
|
||||
}
|
||||
},
|
||||
child: Image.asset('assets/images/ai.png',
|
||||
height: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
_buildInfo(theme),
|
||||
if (videoIntroController.enableAi) _aiBtn,
|
||||
],
|
||||
),
|
||||
if (videoIntroController.videoDetail.value.argueInfo?.argueMsg
|
||||
@@ -704,31 +418,7 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
],
|
||||
if (videoIntroController.videoTags?.isNotEmpty ==
|
||||
true) ...[
|
||||
GestureDetector(
|
||||
onTap: () {},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: videoIntroController.videoTags!
|
||||
.map(
|
||||
(item) => SearchText(
|
||||
fontSize: 13,
|
||||
text: item.tagName!,
|
||||
onTap: (tagName) => Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': tagName},
|
||||
),
|
||||
onLongPress: Utils.copyText,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildTags(videoIntroController.videoTags!),
|
||||
],
|
||||
],
|
||||
),
|
||||
@@ -806,7 +496,7 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
);
|
||||
}
|
||||
|
||||
Obx followButton(BuildContext context, ThemeData t) {
|
||||
Widget followButton(BuildContext context, ThemeData t) {
|
||||
return Obx(
|
||||
() {
|
||||
int attr = videoIntroController.followStatus['attribute'] ?? 0;
|
||||
@@ -1029,4 +719,285 @@ class _VideoInfoState extends State<VideoInfo> {
|
||||
});
|
||||
return TextSpan(children: spanChildren);
|
||||
}
|
||||
|
||||
Widget _buildStaff(ThemeData theme, Staff item) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
int? ownerMid = !widget.isLoading
|
||||
? videoDetail.owner?.mid
|
||||
: videoItem['owner']?.mid;
|
||||
if (item.mid == ownerMid &&
|
||||
context.orientation == Orientation.landscape &&
|
||||
_horizontalMemberPage) {
|
||||
widget.onShowMemberPage(ownerMid);
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/member?mid=${item.mid}&from_view_aid=${videoDetailCtr.oid.value}',
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: ImageType.avatar,
|
||||
src: item.face,
|
||||
width: 35,
|
||||
height: 35,
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
),
|
||||
if ((item.official?.type ?? -1) != -1)
|
||||
Positioned(
|
||||
right: -2,
|
||||
bottom: -2,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.offline_bolt,
|
||||
color: item.official?.type == 0
|
||||
? const Color(0xFFFFCC00)
|
||||
: Colors.lightBlueAccent,
|
||||
size: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: -6,
|
||||
child: Obx(() =>
|
||||
videoIntroController.staffRelations['status'] == true &&
|
||||
videoIntroController
|
||||
.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) {
|
||||
videoIntroController
|
||||
.staffRelations['${item.mid}'] = true;
|
||||
},
|
||||
),
|
||||
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(width: 8),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: (item.vip?.status ?? 0) > 0 && item.vip?.type == 2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item.title!,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAvatar(ThemeData theme) => GestureDetector(
|
||||
onTap: onPushMember,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => PendantAvatar(
|
||||
avatar: videoIntroController.userStat['card']?['face'],
|
||||
size: 35,
|
||||
badgeSize: 14,
|
||||
isVip: (videoIntroController.userStat['card']?['vip']
|
||||
?['status'] ??
|
||||
-1) >
|
||||
0,
|
||||
officialType: videoIntroController.userStat['card']
|
||||
?['official_verify']?['type'],
|
||||
// garbPendantImage: videoIntroController.userStat.value['card']?['pendant']?['image'],
|
||||
)),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Obx(
|
||||
() => Text(
|
||||
videoIntroController.userStat['card']?['name'] ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: (videoIntroController.userStat['card']?['vip']
|
||||
?['status'] ??
|
||||
-1) >
|
||||
0 &&
|
||||
videoIntroController.userStat['card']?['vip']
|
||||
?['type'] ==
|
||||
2
|
||||
? context.vipColor
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 0),
|
||||
Obx(
|
||||
() => Text(
|
||||
'${NumUtil.numFormat(videoIntroController.userStat['follower'])}粉丝 ${videoIntroController.userStat['archive_count'] != null ? '${NumUtil.numFormat(videoIntroController.userStat['archive_count'])}视频' : ''}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildInfo(ThemeData theme) => Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
StatWidget(
|
||||
type: StatType.play,
|
||||
value: !widget.isLoading
|
||||
? videoDetail.stat?.view
|
||||
: videoItem['stat']?.view,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
StatWidget(
|
||||
type: StatType.danmaku,
|
||||
value: !widget.isLoading
|
||||
? videoDetail.stat?.danmaku
|
||||
: videoItem['stat']?.danmu,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
Text(
|
||||
DateUtil.format(
|
||||
!widget.isLoading ? videoDetail.pubdate : videoItem['pubdate'],
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
if (MineController.anonymity.value)
|
||||
Icon(
|
||||
MdiIcons.incognito,
|
||||
size: 15,
|
||||
color: theme.colorScheme.outline,
|
||||
semanticLabel: '无痕',
|
||||
),
|
||||
if (videoIntroController.isShowOnlineTotal)
|
||||
Obx(
|
||||
() => Text(
|
||||
'${videoIntroController.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 (videoIntroController.aiConclusionResult == null) {
|
||||
await videoIntroController.aiConclusion();
|
||||
}
|
||||
if (videoIntroController.aiConclusionResult == null) {
|
||||
return;
|
||||
}
|
||||
if (videoIntroController
|
||||
.aiConclusionResult!.summary?.isNotEmpty ==
|
||||
true ||
|
||||
videoIntroController
|
||||
.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(
|
||||
onTap: () {},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: tags
|
||||
.map(
|
||||
(item) => SearchText(
|
||||
fontSize: 13,
|
||||
text: item.tagName!,
|
||||
onTap: (tagName) => Get.toNamed(
|
||||
'/searchResult',
|
||||
parameters: {'keyword': tagName},
|
||||
),
|
||||
onLongPress: Utils.copyText,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
itemExtent: 150,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
bool isCurrentIndex = pageIndex == i;
|
||||
final item = pages[i];
|
||||
return Container(
|
||||
width: 150,
|
||||
margin: EdgeInsets.only(
|
||||
@@ -156,7 +157,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
widget.videoIntroController.changeSeasonOrbangu(
|
||||
null,
|
||||
widget.bvid,
|
||||
pages[i].cid,
|
||||
item.cid,
|
||||
IdUtils.bv2av(widget.bvid),
|
||||
widget.cover,
|
||||
);
|
||||
@@ -168,8 +169,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8, horizontal: 8),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
if (isCurrentIndex) ...<Widget>[
|
||||
@@ -182,17 +182,18 @@ class _PagesPanelState extends State<PagesPanel> {
|
||||
const SizedBox(width: 6)
|
||||
],
|
||||
Expanded(
|
||||
child: Text(
|
||||
pages[i].pagePart!,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isCurrentIndex
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurface,
|
||||
child: Text(
|
||||
item.pagePart!,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isCurrentIndex
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurface,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
))
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class MediaListPanel extends CommonCollapseSlidePage {
|
||||
final bool desc;
|
||||
final VoidCallback onReverse;
|
||||
final RefreshCallback? loadPrevious;
|
||||
final ValueChanged<int>? onDelete;
|
||||
final Function(dynamic item, int index)? onDelete;
|
||||
|
||||
@override
|
||||
State<MediaListPanel> createState() => _MediaListPanelState();
|
||||
@@ -298,9 +298,9 @@ class _MediaListPanelState
|
||||
onTap: () => showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定移除该视频?',
|
||||
onConfirm: () => widget.onDelete!(index),
|
||||
onConfirm: () => widget.onDelete!(item, index),
|
||||
),
|
||||
onLongPress: () => widget.onDelete!(index),
|
||||
onLongPress: () => widget.onDelete!(item, index),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(9),
|
||||
child: Icon(
|
||||
|
||||
@@ -106,302 +106,9 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
children: [
|
||||
...List.generate(
|
||||
list!.length,
|
||||
(index) => Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 5,
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (list![index].actionType !=
|
||||
ActionType.full) ...[
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: true,
|
||||
index: index,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (list![index].category !=
|
||||
SegmentType.poi_highlight)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: false,
|
||||
index: index,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('分类: '),
|
||||
PopupMenuButton<SegmentType>(
|
||||
initialValue:
|
||||
list![index].category,
|
||||
onSelected: (item) {
|
||||
list![index].category = item;
|
||||
List<ActionType>
|
||||
constraintList =
|
||||
item.toActionType;
|
||||
if (!constraintList.contains(
|
||||
list![index].actionType)) {
|
||||
list![index].actionType =
|
||||
constraintList.first;
|
||||
}
|
||||
switch (item) {
|
||||
case SegmentType
|
||||
.poi_highlight:
|
||||
updateSegment(
|
||||
isFirst: false,
|
||||
index: index,
|
||||
value: list![index]
|
||||
.segment
|
||||
.first,
|
||||
);
|
||||
break;
|
||||
case SegmentType
|
||||
.exclusive_access:
|
||||
updateSegment(
|
||||
isFirst: true,
|
||||
index: index,
|
||||
value: 0,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
(context as Element)
|
||||
.markNeedsBuild();
|
||||
},
|
||||
itemBuilder: (context) =>
|
||||
SegmentType.values
|
||||
.map((item) =>
|
||||
PopupMenuItem<
|
||||
SegmentType>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title),
|
||||
))
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
list![index].category.title,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
strutStyle:
|
||||
const StrutStyle(
|
||||
height: 1,
|
||||
leading: 0,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
MdiIcons
|
||||
.unfoldMoreHorizontal,
|
||||
size:
|
||||
MediaQuery.textScalerOf(
|
||||
context)
|
||||
.scale(14),
|
||||
color: theme
|
||||
.colorScheme.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('行为类别: '),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return PopupMenuButton<
|
||||
ActionType>(
|
||||
initialValue:
|
||||
list![index].actionType,
|
||||
onSelected: (item) {
|
||||
list![index].actionType =
|
||||
item;
|
||||
if (item ==
|
||||
ActionType.full) {
|
||||
updateSegment(
|
||||
isFirst: true,
|
||||
index: index,
|
||||
value: 0,
|
||||
);
|
||||
}
|
||||
(context as Element)
|
||||
.markNeedsBuild();
|
||||
},
|
||||
itemBuilder: (context) =>
|
||||
ActionType.values
|
||||
.map(
|
||||
(item) =>
|
||||
PopupMenuItem<
|
||||
ActionType>(
|
||||
enabled: list![
|
||||
index]
|
||||
.category
|
||||
.toActionType
|
||||
.contains(
|
||||
item),
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
list![index]
|
||||
.actionType
|
||||
.title,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: theme
|
||||
.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
strutStyle:
|
||||
const StrutStyle(
|
||||
height: 1,
|
||||
leading: 0,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
MdiIcons
|
||||
.unfoldMoreHorizontal,
|
||||
size: MediaQuery
|
||||
.textScalerOf(
|
||||
context)
|
||||
.scale(14),
|
||||
color: theme.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 4,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '移除',
|
||||
icon: Icons.clear,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
list!.removeAt(index);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 4,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '预览',
|
||||
icon: Icons.preview_outlined,
|
||||
onPressed: () async {
|
||||
if (widget.plPlayerController
|
||||
.videoPlayerController !=
|
||||
null) {
|
||||
int start = max(
|
||||
0,
|
||||
(list![index].segment.first * 1000)
|
||||
.round() -
|
||||
2000,
|
||||
);
|
||||
await widget
|
||||
.plPlayerController.videoPlayerController!
|
||||
.seek(
|
||||
Duration(milliseconds: start),
|
||||
);
|
||||
if (!widget.plPlayerController
|
||||
.videoPlayerController!.state.playing) {
|
||||
await widget.plPlayerController
|
||||
.videoPlayerController!
|
||||
.play();
|
||||
}
|
||||
if (start != 0) {
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 2));
|
||||
}
|
||||
widget
|
||||
.plPlayerController.videoPlayerController!
|
||||
.seek(
|
||||
Duration(
|
||||
milliseconds:
|
||||
(list![index].segment.second * 1000)
|
||||
.round(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
(index) {
|
||||
return _buildItem(theme, index, list![index]);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 88 + MediaQuery.paddingOf(context).bottom,
|
||||
@@ -448,28 +155,28 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
|
||||
void updateSegment({
|
||||
required bool isFirst,
|
||||
required int index,
|
||||
required PostSegmentModel item,
|
||||
required double value,
|
||||
}) {
|
||||
if (isFirst) {
|
||||
list![index].segment.first = value;
|
||||
item.segment.first = value;
|
||||
} else {
|
||||
list![index].segment.second = value;
|
||||
item.segment.second = value;
|
||||
}
|
||||
if (list![index].category == SegmentType.poi_highlight ||
|
||||
list![index].actionType == ActionType.full) {
|
||||
list![index].segment.second = value;
|
||||
if (item.category == SegmentType.poi_highlight ||
|
||||
item.actionType == ActionType.full) {
|
||||
item.segment.second = value;
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> segmentWidget(
|
||||
BuildContext context,
|
||||
ThemeData theme, {
|
||||
required int index,
|
||||
required PostSegmentModel item,
|
||||
required bool isFirst,
|
||||
}) {
|
||||
String value = DurationUtil.formatDuration(
|
||||
isFirst ? list![index].segment.first : list![index].segment.second);
|
||||
isFirst ? item.segment.first : item.segment.second);
|
||||
return [
|
||||
Text(
|
||||
'${isFirst ? '开始' : '结束'}: $value',
|
||||
@@ -483,7 +190,7 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
index: index,
|
||||
item: item,
|
||||
value: currentPos,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
@@ -498,7 +205,7 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
onPressed: () {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
index: index,
|
||||
item: item,
|
||||
value: isFirst ? 0 : videoDuration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
@@ -553,7 +260,7 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
if (duration <= videoDuration) {
|
||||
updateSegment(
|
||||
isFirst: isFirst,
|
||||
index: index,
|
||||
item: item,
|
||||
value: duration,
|
||||
);
|
||||
(context as Element).markNeedsBuild();
|
||||
@@ -629,4 +336,241 @@ class _PostPanelState extends CommonCollapseSlidePageState<PostPanel> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(ThemeData theme, int index, PostSegmentModel item) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 5,
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onInverseSurface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (item.actionType != ActionType.full) ...[
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: true,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (item.category != SegmentType.poi_highlight)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: segmentWidget(
|
||||
context,
|
||||
theme,
|
||||
isFirst: false,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('分类: '),
|
||||
PopupMenuButton<SegmentType>(
|
||||
initialValue: item.category,
|
||||
onSelected: (e) {
|
||||
item.category = e;
|
||||
List<ActionType> constraintList = e.toActionType;
|
||||
if (!constraintList.contains(item.actionType)) {
|
||||
item.actionType = constraintList.first;
|
||||
}
|
||||
switch (e) {
|
||||
case SegmentType.poi_highlight:
|
||||
updateSegment(
|
||||
isFirst: false,
|
||||
item: item,
|
||||
value: item.segment.first,
|
||||
);
|
||||
break;
|
||||
case SegmentType.exclusive_access:
|
||||
updateSegment(
|
||||
isFirst: true,
|
||||
item: item,
|
||||
value: 0,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
itemBuilder: (context) => SegmentType.values
|
||||
.map((e) => PopupMenuItem<SegmentType>(
|
||||
value: e,
|
||||
child: Text(e.title),
|
||||
))
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
item.category.title,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
strutStyle: const StrutStyle(
|
||||
height: 1,
|
||||
leading: 0,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
MdiIcons.unfoldMoreHorizontal,
|
||||
size: MediaQuery.textScalerOf(context)
|
||||
.scale(14),
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('行为类别: '),
|
||||
PopupMenuButton<ActionType>(
|
||||
initialValue: item.actionType,
|
||||
onSelected: (e) {
|
||||
item.actionType = e;
|
||||
if (e == ActionType.full) {
|
||||
updateSegment(
|
||||
isFirst: true,
|
||||
item: item,
|
||||
value: 0,
|
||||
);
|
||||
}
|
||||
(context as Element).markNeedsBuild();
|
||||
},
|
||||
itemBuilder: (context) => ActionType.values
|
||||
.map(
|
||||
(e) => PopupMenuItem<ActionType>(
|
||||
enabled:
|
||||
item.category.toActionType.contains(e),
|
||||
value: e,
|
||||
child: Text(e.title),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
item.actionType.title,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
strutStyle: const StrutStyle(
|
||||
height: 1,
|
||||
leading: 0,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
MdiIcons.unfoldMoreHorizontal,
|
||||
size: MediaQuery.textScalerOf(context)
|
||||
.scale(14),
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 4,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '移除',
|
||||
icon: Icons.clear,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
list!.removeAt(index);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 4,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
size: 26,
|
||||
tooltip: '预览',
|
||||
icon: Icons.preview_outlined,
|
||||
onPressed: () async {
|
||||
if (widget.plPlayerController.videoPlayerController != null) {
|
||||
int start = max(
|
||||
0,
|
||||
(item.segment.first * 1000).round() - 2000,
|
||||
);
|
||||
await widget.plPlayerController.videoPlayerController!.seek(
|
||||
Duration(milliseconds: start),
|
||||
);
|
||||
if (!widget.plPlayerController.videoPlayerController!.state
|
||||
.playing) {
|
||||
await widget.plPlayerController.videoPlayerController!
|
||||
.play();
|
||||
}
|
||||
if (start != 0) {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
widget.plPlayerController.videoPlayerController!.seek(
|
||||
Duration(
|
||||
milliseconds: (item.segment.second * 1000).round(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,10 +220,9 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
onReply: (replyItem) => _videoReplyController.onReply(
|
||||
context,
|
||||
replyItem: replyItem,
|
||||
index: index,
|
||||
),
|
||||
onDelete: (subIndex) =>
|
||||
_videoReplyController.onRemove(index, subIndex),
|
||||
onDelete: (item, subIndex) =>
|
||||
_videoReplyController.onRemove(index, item, subIndex),
|
||||
upMid: _videoReplyController.upMid,
|
||||
getTag: () => heroTag,
|
||||
onViewImage: widget.onViewImage,
|
||||
@@ -231,13 +230,11 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
callback: widget.callback,
|
||||
onCheckReply: (item) => _videoReplyController
|
||||
.onCheckReply(context, item, isManual: true),
|
||||
onToggleTop: (isUpTop, rpid) =>
|
||||
_videoReplyController.onToggleTop(
|
||||
onToggleTop: (item) => _videoReplyController.onToggleTop(
|
||||
item,
|
||||
index,
|
||||
_videoReplyController.aid,
|
||||
1,
|
||||
isUpTop,
|
||||
rpid,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
final Function(ReplyInfo replyItem, int? rpid)? replyReply;
|
||||
final bool needDivider;
|
||||
final ValueChanged<ReplyInfo>? onReply;
|
||||
final ValueChanged<int?>? onDelete;
|
||||
final Function(ReplyInfo replyItem, int? subIndex)? onDelete;
|
||||
final Int64? upMid;
|
||||
final VoidCallback? showDialogue;
|
||||
final Function? getTag;
|
||||
@@ -66,7 +66,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
final ValueChanged<int>? onDismissed;
|
||||
final Function(List<String>, int)? callback;
|
||||
final ValueChanged<ReplyInfo>? onCheckReply;
|
||||
final Function(bool isUpTop, int rpid)? onToggleTop;
|
||||
final ValueChanged<ReplyInfo>? onToggleTop;
|
||||
|
||||
static final _voteRegExp = RegExp(r"\{vote:\d+?\}");
|
||||
static final _timeRegExp = RegExp(r'^\b(?:\d+[::])?\d+[::]\d+\b$');
|
||||
@@ -96,7 +96,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
return morePanel(
|
||||
context: context,
|
||||
item: replyItem,
|
||||
onDelete: () => onDelete?.call(null),
|
||||
onDelete: () => onDelete?.call(replyItem, null),
|
||||
isSubReply: false,
|
||||
);
|
||||
},
|
||||
@@ -463,7 +463,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
return morePanel(
|
||||
context: context,
|
||||
item: childReply,
|
||||
onDelete: () => onDelete?.call(index),
|
||||
onDelete: () => onDelete?.call(replyItem, index),
|
||||
isSubReply: true,
|
||||
);
|
||||
},
|
||||
@@ -963,7 +963,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
Widget morePanel({
|
||||
required BuildContext context,
|
||||
required ReplyInfo item,
|
||||
required onDelete,
|
||||
required VoidCallback onDelete,
|
||||
required bool isSubReply,
|
||||
}) {
|
||||
final ownerMid = Int64(Accounts.main.mid);
|
||||
@@ -994,7 +994,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
onDelete?.call();
|
||||
onDelete();
|
||||
}
|
||||
return res.data as Map;
|
||||
},
|
||||
@@ -1074,7 +1074,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
SmartDialog.dismiss();
|
||||
if (result['status']) {
|
||||
SmartDialog.showToast('删除成功');
|
||||
onDelete?.call();
|
||||
onDelete();
|
||||
} else {
|
||||
SmartDialog.showToast('删除失败, ${result["msg"]}');
|
||||
}
|
||||
@@ -1085,7 +1085,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
break;
|
||||
case 'top':
|
||||
Get.back();
|
||||
onToggleTop?.call(item.replyControl.isUpTop, item.id.toInt());
|
||||
onToggleTop?.call(item);
|
||||
break;
|
||||
case 'saveReply':
|
||||
Get.back();
|
||||
@@ -1167,7 +1167,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
leading: const Icon(Icons.save_alt, size: 19),
|
||||
title: Text('保存评论', style: style),
|
||||
),
|
||||
if (kDebugMode || item.mid == ownerMid)
|
||||
if (item.mid == ownerMid)
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('checkReply'),
|
||||
minLeadingWidth: 0,
|
||||
|
||||
@@ -188,14 +188,6 @@ class _VideoReplyReplyPanelState
|
||||
callback: _getImageCallback,
|
||||
onCheckReply: (item) => _videoReplyReplyController
|
||||
.onCheckReply(context, item, isManual: true),
|
||||
onToggleTop: (isUpTop, rpid) =>
|
||||
_videoReplyReplyController.onToggleTop(
|
||||
index,
|
||||
_videoReplyReplyController.oid,
|
||||
_videoReplyReplyController.replyType,
|
||||
isUpTop,
|
||||
rpid,
|
||||
),
|
||||
);
|
||||
} else if (index == 1) {
|
||||
return Divider(
|
||||
@@ -431,8 +423,8 @@ class _VideoReplyReplyPanelState
|
||||
replyItem: replyItem,
|
||||
replyLevel: widget.isDialogue ? 3 : 2,
|
||||
onReply: (replyItem) => _onReply(replyItem, index),
|
||||
onDelete: (subIndex) {
|
||||
_videoReplyReplyController.onRemove(index, null);
|
||||
onDelete: (item, subIndex) {
|
||||
_videoReplyReplyController.onRemove(index, item, null);
|
||||
},
|
||||
upMid: _videoReplyReplyController.upMid,
|
||||
showDialogue: () => _key.currentState?.showBottomSheet(
|
||||
@@ -451,13 +443,6 @@ class _VideoReplyReplyPanelState
|
||||
callback: _getImageCallback,
|
||||
onCheckReply: (item) => _videoReplyReplyController
|
||||
.onCheckReply(context, item, isManual: true),
|
||||
onToggleTop: (isUpTop, rpid) => _videoReplyReplyController.onToggleTop(
|
||||
index,
|
||||
_videoReplyReplyController.oid,
|
||||
_videoReplyReplyController.replyType,
|
||||
isUpTop,
|
||||
rpid,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -593,15 +593,16 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
),
|
||||
),
|
||||
),
|
||||
for (int i = 0; i < totalQaSam; i++) ...[
|
||||
ListTile(
|
||||
...List.generate(totalQaSam, (index) {
|
||||
final item = videoFormat[index];
|
||||
return ListTile(
|
||||
dense: true,
|
||||
onTap: () async {
|
||||
if (currentVideoQa.code == videoFormat[i].quality) {
|
||||
if (currentVideoQa.code == item.quality) {
|
||||
return;
|
||||
}
|
||||
Get.back();
|
||||
final int quality = videoFormat[i].quality!;
|
||||
final int quality = item.quality!;
|
||||
videoDetailCtr
|
||||
..currentVideoQa = VideoQuality.fromCode(quality)
|
||||
..updatePlayer();
|
||||
@@ -631,20 +632,20 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
);
|
||||
},
|
||||
// 可能包含会员解锁画质
|
||||
enabled: i >= totalQaSam - userfulQaSam,
|
||||
enabled: index >= totalQaSam - userfulQaSam,
|
||||
contentPadding: const EdgeInsets.only(left: 20, right: 20),
|
||||
title: Text(videoFormat[i].newDesc!),
|
||||
trailing: currentVideoQa.code == videoFormat[i].quality
|
||||
title: Text(item.newDesc!),
|
||||
trailing: currentVideoQa.code == item.quality
|
||||
? Icon(
|
||||
Icons.done,
|
||||
color: theme.colorScheme.primary,
|
||||
)
|
||||
: Text(
|
||||
videoFormat[i].format!,
|
||||
item.format!,
|
||||
style: subTitleStyle,
|
||||
),
|
||||
),
|
||||
]
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user