Files
PiliPlus/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart
bggRGjQaUbCoE 1d1d277801 opt: reply item
2024-10-11 21:35:56 +08:00

1174 lines
41 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'package:PiliPalaX/common/widgets/badge.dart';
import 'package:PiliPalaX/common/widgets/imageview.dart';
import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart';
import 'package:PiliPalaX/http/video.dart';
import 'package:PiliPalaX/pages/video/detail/reply/widgets/zan.dart';
import 'package:PiliPalaX/pages/video/detail/reply/widgets/zan_grpc.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/models/common/reply_type.dart';
import 'package:PiliPalaX/pages/video/detail/index.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import 'package:PiliPalaX/utils/storage.dart';
import 'package:PiliPalaX/utils/url_utils.dart';
import 'package:PiliPalaX/utils/utils.dart';
import '../../../../../utils/app_scheme.dart';
Box setting = GStorage.setting;
class ReplyItemGrpc extends StatelessWidget {
const ReplyItemGrpc({
super.key,
required this.replyItem,
this.replyLevel,
this.showReplyRow = true,
this.replyReply,
this.replyType,
this.needDivider = true,
this.onReply,
this.onDelete,
this.upMid,
this.isTop = false,
});
final ReplyInfo replyItem;
final String? replyLevel;
final bool showReplyRow;
final Function? replyReply;
final ReplyType? replyType;
final bool needDivider;
final Function()? onReply;
final Function(dynamic rpid, dynamic frpid)? onDelete;
final dynamic upMid;
final bool isTop;
@override
Widget build(BuildContext context) {
return Material(
child: InkWell(
// 点击整个评论区 评论详情/回复
onTap: () {
feedBack();
if (replyReply != null) {
replyReply!(replyItem);
}
},
onLongPress: () {
feedBack();
// showDialog(
// context: Get.context!,
// builder: (_) => AlertDialog(
// content: SelectableText(
// jsonEncode(replyItem.replyControl.toProto3Json())),
// ),
// );
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(
item: replyItem,
onDelete: (rpid) {
if (onDelete != null) {
onDelete!(rpid, null);
}
},
);
},
);
},
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(12, 14, 8, 5),
child: content(context),
),
if (needDivider)
Divider(
indent: 55,
endIndent: 15,
height: 0.3,
color: Theme.of(context)
.colorScheme
.onInverseSurface
.withOpacity(0.5),
)
],
),
),
);
}
Widget lfAvtar(BuildContext context, String heroTag) {
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: replyItem.member.face,
width: 34,
height: 34,
type: 'avatar',
),
),
if (replyItem.member.vipType > 0)
Positioned(
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
//borderRadius: BorderRadius.circular(7),
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surface,
),
child: Image.asset(
'assets/images/big-vip.png',
height: 14,
semanticLabel: "大会员",
),
),
),
//https://www.bilibili.com/blackboard/activity-whPrHsYJ2.html
if (replyItem.member.officialVerifyType == 0)
Positioned(
left: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8),
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surface,
),
child: const Icon(
Icons.offline_bolt,
color: Colors.yellow,
size: 14,
semanticLabel: "认证个人",
),
),
)
else if (replyItem.member.officialVerifyType == 1)
Positioned(
left: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8),
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surface,
),
child: const Icon(
Icons.offline_bolt,
color: Colors.lightBlueAccent,
size: 14,
semanticLabel: "认证机构",
),
),
),
],
);
}
Widget content(BuildContext context) {
final String heroTag = Utils.makeHeroTag(replyItem.mid);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
/// fix Stack内GestureDetector onTap无效
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
feedBack();
Get.toNamed('/member?mid=${replyItem.mid}',
arguments: {'face': replyItem.member.face, 'heroTag': heroTag});
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
lfAvtar(context, heroTag),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
replyItem.member.name,
style: TextStyle(
color: (replyItem.member.vipType == 2)
? Utils.vipColor
: Theme.of(context).colorScheme.outline,
fontSize: 13,
),
),
const SizedBox(width: 6),
Image.asset(
'assets/images/lv/lv${replyItem.member.level}.png',
height: 11,
semanticLabel: "等级:${replyItem.member.level}",
),
const SizedBox(width: 6),
if (replyItem.mid == upMid)
const PBadge(
text: 'UP',
size: 'small',
stack: 'normal',
fs: 9,
),
],
),
Row(
children: <Widget>[
Text(
Utils.dateFormat(replyItem.ctime.toInt()),
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
if (replyItem.replyControl.location.isNotEmpty)
Text(
'${replyItem.replyControl.location}',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize,
color: Theme.of(context).colorScheme.outline),
),
],
)
],
),
],
),
),
// title
Padding(
padding:
const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
String text = replyItem.content.message;
var textPainter = TextPainter(
text: TextSpan(text: text),
maxLines: 6,
// replyItem.content!.isText! && replyLevel == '1' ? 6 : null,
textDirection: Directionality.of(context),
)..layout(maxWidth: constraints.maxWidth);
bool didExceedMaxLines = textPainter.didExceedMaxLines;
return Semantics(
label: text,
child: Text.rich(
style: TextStyle(
height: 1.75,
fontSize:
Theme.of(context).textTheme.bodyMedium!.fontSize),
TextSpan(
children: [
if (isTop) ...[
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
semanticsLabel: '置顶',
),
),
const TextSpan(text: ' '),
],
buildContent(
context,
replyItem,
replyReply,
null,
textPainter,
didExceedMaxLines,
),
if (didExceedMaxLines)
TextSpan(
text: '\n查看更多',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
);
},
),
),
// 操作区域
buttonAction(context, replyItem.replyControl),
// 一楼的评论
if (( //replyItem.replyControl!.isShow! ||
replyItem.replies.isNotEmpty ||
replyItem.replyControl.subReplyEntryText.isNotEmpty) &&
showReplyRow) ...[
Padding(
padding: const EdgeInsets.only(top: 5, bottom: 12),
child: ReplyItemRow(
upMid: upMid,
replies: replyItem.replies,
replyControl: replyItem.replyControl,
// f_rpid: replyItem.rpid,
replyItem: replyItem,
replyReply: replyReply,
onDelete: (rpid) {
if (onDelete != null) {
onDelete!(rpid, replyItem.id);
}
},
),
),
],
],
);
}
// 感谢、回复、复制
Widget buttonAction(BuildContext context, replyControl) {
return Row(
children: <Widget>[
const SizedBox(width: 32),
SizedBox(
height: 32,
child: TextButton(
onPressed: () {
feedBack();
if (onReply != null) {
onReply!();
return;
}
},
child: Row(children: [
Icon(Icons.reply,
size: 18,
color:
Theme.of(context).colorScheme.outline.withOpacity(0.8)),
const SizedBox(width: 3),
Text(
'回复',
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
]),
),
),
const SizedBox(width: 2),
if (replyItem.replyControl.upLike) ...[
SizedBox(
height: 32,
child: TextButton(
onPressed: null,
child: Text(
'UP主觉得很赞',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
fontWeight: FontWeight.normal,
),
),
),
),
const SizedBox(width: 2),
],
if (replyItem.replyControl.cardLabels
.map((item) => item.textContent)
.toList()
.contains('热评'))
Text(
'热评',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize),
),
const Spacer(),
ZanButtonGrpc(replyItem: replyItem, replyType: replyType),
const SizedBox(width: 5)
],
);
}
}
// ignore: must_be_immutable
class ReplyItemRow extends StatelessWidget {
ReplyItemRow({
super.key,
required this.replies,
required this.replyControl,
// this.f_rpid,
this.replyItem,
this.replyReply,
this.onDelete,
this.upMid,
});
final List<ReplyInfo> replies;
ReplyControl replyControl;
// int? f_rpid;
ReplyInfo? replyItem;
Function? replyReply;
final Function(dynamic rpid)? onDelete;
final dynamic upMid;
@override
Widget build(BuildContext context) {
final bool extraRow = replyControl.subReplyEntryText.isNotEmpty;
return Container(
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
child: Material(
color: Theme.of(context).colorScheme.onInverseSurface,
borderRadius: BorderRadius.circular(6),
clipBehavior: Clip.hardEdge,
animationDuration: Duration.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (replies.isNotEmpty)
for (int i = 0; i < replies.length; i++) ...[
InkWell(
// 一楼点击评论展开评论详情
onTap: () => replyReply!(replyItem),
onLongPress: () {
feedBack();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(
item: replies[i],
onDelete: onDelete,
);
},
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(
8,
i == 0 && (extraRow || replies.length > 1) ? 8 : 4,
8,
i == 0 && (extraRow || replies.length > 1) ? 4 : 6,
),
child: Semantics(
label:
'${replies[i].member.name} ${replies[i].content.message}',
excludeSemantics: true,
child: Text.rich(
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.bodyMedium!
.fontSize,
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.85),
height: 1.6),
overflow: TextOverflow.ellipsis,
maxLines: 2,
TextSpan(
children: [
TextSpan(
text: replies[i].member.name,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.85),
),
recognizer: TapGestureRecognizer()
..onTap = () {
feedBack();
final String heroTag =
Utils.makeHeroTag(replies[i].member.mid);
Get.toNamed(
'/member?mid=${replies[i].member.mid}',
arguments: {
'face': replies[i].member.face,
'heroTag': heroTag
});
},
),
if (replies[i].mid == upMid) ...[
const TextSpan(text: ' '),
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'UP',
size: 'small',
stack: 'normal',
fs: 9,
),
),
const TextSpan(text: ' '),
],
TextSpan(
text: replies[i].root == replies[i].parent
? ': '
: replies[i].mid == upMid
? ''
: ' ',
),
buildContent(
context,
replies[i],
replyReply,
replyItem,
null,
null,
),
],
),
),
),
),
)
],
if (extraRow)
InkWell(
// 一楼点击【共xx条回复】展开评论详情
onTap: () => replyReply!(replyItem),
child: Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(8, 5, 8, 8),
child: Text.rich(
TextSpan(
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
),
children: [
if (replyControl.upReply)
TextSpan(
text: 'UP主等人 ',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.85))),
TextSpan(
text: replyControl.subReplyEntryText,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.85),
),
)
],
),
),
),
)
],
),
),
);
}
}
InlineSpan buildContent(
BuildContext context,
replyItem,
replyReply,
fReplyItem,
textPainter,
didExceedMaxLines,
) {
final String routePath = Get.currentRoute;
bool isVideoPage = routePath.startsWith('/video');
// replyItem 当前回复内容
// replyReply 查看二楼回复(回复详情)回调
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
final Content content = replyItem.content;
String message = content.message;
final List<InlineSpan> spanChildren = <InlineSpan>[];
if (didExceedMaxLines == true) {
final textSize = textPainter.size;
var position = textPainter.getPositionForOffset(
Offset(
textSize.width,
textSize.height,
),
);
final endOffset = textPainter.getOffsetBefore(position.offset);
message = message.substring(0, endOffset);
}
return TextSpan(text: message);
// 投票
// if (content.vote.isNotEmpty) {
// message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) {
// // String matchStr = match[0]!;
// spanChildren.add(
// TextSpan(
// text: '投票: ${content.vote['title']}',
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () => Get.toNamed(
// '/webviewnew',
// parameters: {
// 'url': content.vote['url'],
// 'type': 'vote',
// 'pageTitle': content.vote['title'],
// },
// ),
// ),
// );
// return '';
// }, onNonMatch: (String str) {
// return str;
// });
// message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), "");
// }
message = message
.replaceAll('&amp;', '&')
.replaceAll('&lt;', '<')
.replaceAll('&gt;', '>')
.replaceAll('&quot;', '"')
.replaceAll('&apos;', "'")
.replaceAll('&nbsp;', ' ');
// 构建正则表达式
final List<String> specialTokens = [
...content.emote.keys,
// ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [],
...content.atNameToMid.keys.map((e) => '@$e'),
];
// List<String> jumpUrlKeysList = content.jumpUrl.keys.map<String>((String e) {
// return e.replaceAllMapped(
// RegExp(r'[?+*]'), (match) => '\\${match.group(0)}');
// }).toList();
specialTokens.sort((a, b) => b.length.compareTo(a.length));
String patternStr = specialTokens.map(RegExp.escape).join('|');
if (patternStr.isNotEmpty) {
patternStr += "|";
}
patternStr += r'(\b(?:\d+[:])?[0-5]?[0-9][:][0-5]?[0-9]\b)';
// if (jumpUrlKeysList.isNotEmpty) {
// patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}';
// }
final RegExp pattern = RegExp(patternStr);
List<String> matchedStrs = [];
void addPlainTextSpan(str) {
spanChildren.add(TextSpan(
text: str,
));
// TextSpan(
//
// text: str,
// recognizer: TapGestureRecognizer()
// ..onTap = () => replyReply
// ?.call(replyItem.root == 0 ? replyItem : fReplyItem)))));
}
// 分割文本并处理每个部分
// message.splitMapJoin(
// pattern,
// onMatch: (Match match) {
// String matchStr = match[0]!;
// if (content.emote.containsKey(matchStr)) {
// // 处理表情
// final int size = content.emote[matchStr]['meta']['size'];
// spanChildren.add(WidgetSpan(
// child: ExcludeSemantics(
// child: NetworkImgLayer(
// src: content.emote[matchStr]['url'],
// type: 'emote',
// width: size * 20,
// height: size * 20,
// semanticsLabel: matchStr,
// )),
// ));
// } else if (matchStr.startsWith("@") &&
// content.atNameToMid.containsKey(matchStr.substring(1))) {
// // 处理@用户
// final String userName = matchStr.substring(1);
// final int userId = content.atNameToMid[userName];
// spanChildren.add(
// TextSpan(
// text: matchStr,
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// final String heroTag = Utils.makeHeroTag(userId);
// Get.toNamed(
// '/member?mid=$userId',
// arguments: {'face': '', 'heroTag': heroTag},
// );
// },
// ),
// );
// } else if (RegExp(r'^\b(?:\d+[:])?[0-5]?[0-9][:][0-5]?[0-9]\b$')
// .hasMatch(matchStr)) {
// matchStr = matchStr.replaceAll('', ':');
// spanChildren.add(
// TextSpan(
// text: ' $matchStr ',
// style: isVideoPage
// ? TextStyle(
// color: Theme.of(context).colorScheme.primary,
// )
// : null,
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// // 跳转到指定位置
// if (isVideoPage) {
// try {
// SmartDialog.showToast('跳转至:$matchStr');
// Get.find<VideoDetailController>(
// tag: Get.arguments['heroTag'])
// .plPlayerController
// .seekTo(Duration(seconds: Utils.duration(matchStr)),
// type: 'slider');
// } catch (e) {
// SmartDialog.showToast('跳转失败: $e');
// }
// }
// },
// ),
// );
// } else {
// String appUrlSchema = '';
// final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
// defaultValue: false) as bool;
// if (content.jumpUrl[matchStr] != null &&
// !matchedStrs.contains(matchStr)) {
// appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
// if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) {
// addPlainTextSpan(matchStr);
// return "";
// }
// spanChildren.addAll(
// [
// if (content.jumpUrl[matchStr]?['prefix_icon'] != null) ...[
// WidgetSpan(
// child: Image.network(
// content.jumpUrl[matchStr]['prefix_icon'],
// height: 19,
// color: Theme.of(context).colorScheme.primary,
// ),
// )
// ],
// TextSpan(
// text: content.jumpUrl[matchStr]['title'],
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () async {
// final String title = content.jumpUrl[matchStr]['title'];
// if (appUrlSchema == '') {
// if (matchStr.startsWith('BV')) {
// UrlUtils.matchUrlPush(
// matchStr,
// title,
// '',
// );
// } else if (RegExp(r'^[Cc][Vv][0-9]+$')
// .hasMatch(matchStr)) {
// Get.toNamed('/htmlRender', parameters: {
// 'url': 'https://www.bilibili.com/read/$matchStr',
// 'title': title,
// 'id': matchStr,
// 'dynamicType': 'read'
// });
// } else {
// final String redirectUrl =
// await UrlUtils.parseRedirectUrl(matchStr);
// // if (redirectUrl == matchStr) {
// // Clipboard.setData(ClipboardData(text: matchStr));
// // SmartDialog.showToast('地址可能有误');
// // return;
// // }
// Uri uri = Uri.parse(redirectUrl);
// PiliScheme.routePush(uri);
// // final String pathSegment = Uri.parse(redirectUrl).path;
// // final String lastPathSegment =
// // pathSegment.split('/').last;
// // if (lastPathSegment.startsWith('BV')) {
// // UrlUtils.matchUrlPush(
// // lastPathSegment,
// // title,
// // redirectUrl,
// // );
// // } else {
// // Get.toNamed(
// // '/webviewnew',
// // parameters: {
// // 'url': redirectUrl,
// // 'type': 'url',
// // 'pageTitle': title
// // },
// // );
// // }
// }
// } else {
// if (appUrlSchema.startsWith('bilibili://search')) {
// Get.toNamed('/searchResult',
// parameters: {'keyword': title});
// } else if (matchStr.startsWith('https://b23.tv')) {
// final String redirectUrl =
// await UrlUtils.parseRedirectUrl(matchStr);
// final String pathSegment = Uri.parse(redirectUrl).path;
// final String lastPathSegment =
// pathSegment.split('/').last;
// if (lastPathSegment.startsWith('BV')) {
// UrlUtils.matchUrlPush(
// lastPathSegment,
// title,
// redirectUrl,
// );
// } else {
// Get.toNamed(
// '/webviewnew',
// parameters: {
// 'url': redirectUrl,
// 'type': 'url',
// 'pageTitle': title
// },
// );
// }
// } else {
// Get.toNamed(
// '/webviewnew',
// parameters: {
// 'url': matchStr,
// 'type': 'url',
// 'pageTitle': title
// },
// );
// }
// }
// },
// )
// ],
// );
// // 只显示一次
// matchedStrs.add(matchStr);
// } else if (matchStr.length > 1 &&
// content.topicsMeta[matchStr.substring(1, matchStr.length - 1)] !=
// null) {
// spanChildren.add(
// TextSpan(
// text: matchStr,
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// final String topic =
// matchStr.substring(1, matchStr.length - 1);
// Get.toNamed('/searchResult', parameters: {'keyword': topic});
// },
// ),
// );
// } else {
// addPlainTextSpan(matchStr);
// }
// }
// return '';
// },
// onNonMatch: (String nonMatchStr) {
// addPlainTextSpan(nonMatchStr);
// return nonMatchStr;
// },
// );
// if (content.jumpUrl.keys.isNotEmpty) {
// List<String> unmatchedItems = content.jumpUrl.keys
// .toList()
// .where((item) => !content.message.contains(item))
// .toList();
// if (unmatchedItems.isNotEmpty) {
// for (int i = 0; i < unmatchedItems.length; i++) {
// String patternStr = unmatchedItems[i];
// spanChildren.addAll(
// [
// if (content.jumpUrl[patternStr]?['prefix_icon'] != null) ...[
// WidgetSpan(
// child: Image.network(
// content.jumpUrl[patternStr]['prefix_icon'],
// height: 19,
// color: Theme.of(context).colorScheme.primary,
// ),
// )
// ],
// TextSpan(
// text: content.jumpUrl[patternStr]['title'],
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// Get.toNamed(
// '/webviewnew',
// parameters: {
// 'url': patternStr,
// 'type': 'url',
// 'pageTitle': content.jumpUrl[patternStr]['title']
// },
// );
// },
// )
// ],
// );
// }
// }
// }
// 图片渲染
// if (content.pictures.isNotEmpty) {
// spanChildren.add(const TextSpan(text: '\n'));
// spanChildren.add(
// WidgetSpan(
// child: LayoutBuilder(
// builder: (_, constraints) => image(
// constraints.maxWidth,
// (content.pictures as List)
// .map(
// (item) => ImageModel(
// width: item['img_width'],
// height: item['img_height'],
// url: item['img_src'],
// ),
// )
// .toList(),
// ),
// ),
// ),
// );
// }
// 笔记链接
// if (content.richText.isNotEmpty) {
// spanChildren.add(
// TextSpan(
// text: ' 笔记',
// style: TextStyle(
// color: Theme.of(context).colorScheme.primary,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () => Get.toNamed(
// '/webviewnew',
// parameters: {
// 'url': content.richText['note']['click_url'],
// 'type': 'note',
// 'pageTitle': '笔记预览'
// },
// ),
// ),
// );
// }
// spanChildren.add(TextSpan(text: matchMember));
return TextSpan(children: spanChildren);
}
class MorePanel extends StatelessWidget {
final ReplyInfo item;
final Function(dynamic rpid)? onDelete;
const MorePanel({super.key, required this.item, this.onDelete});
Future<dynamic> menuActionHandler(String type) async {
String message = item.content.message;
switch (type) {
case 'report':
Get.back();
dynamic result = await Get.toNamed(
'/webviewnew',
parameters: {
'url':
'https://www.bilibili.com/h5/comment/report?mid=${item.mid}&oid=${item.oid}&pageType=1&rpid=${item.id}&platform=android',
},
);
if (result == true && onDelete != null) {
onDelete!(item.id);
}
break;
case 'copyAll':
await Clipboard.setData(ClipboardData(text: message));
SmartDialog.showToast('已复制');
Get.back();
break;
case 'copyFreedom':
Get.back();
// showDialog(
// context: Get.context!,
// builder: (context) {
// return Dialog(
// child: Padding(
// padding:
// const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
// child: SelectableText(message),
// ),
// );
// },
// );
break;
// case 'block':
// SmartDialog.showToast('加入黑名单');
// break;
// case 'report':
// SmartDialog.showToast('举报');
// break;
case 'delete':
//弹出确认提示:
Get.back();
bool? isDelete = await showDialog<bool>(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('删除评论(测试)'),
content:
Text('确定尝试删除这条评论吗?\n\n$message\n\n注:只能删除自己的评论,或自己管理的评论区下的评论'),
actions: <Widget>[
TextButton(
onPressed: () {
Get.back(result: false);
},
child: const Text('取消'),
),
TextButton(
onPressed: () {
Get.back(result: true);
},
child: const Text('确定'),
),
],
);
},
);
if (isDelete == null || !isDelete) {
return;
}
SmartDialog.showLoading(msg: '删除中...');
var result = await VideoHttp.replyDel(
type: item.type.toInt(),
oid: item.oid.toInt(),
rpid: item.id.toInt(),
);
SmartDialog.dismiss();
if (result['status']) {
SmartDialog.showToast('删除成功');
// Get.back();
if (onDelete != null) {
onDelete!(item.id);
}
} else {
SmartDialog.showToast('删除失败, ${result["msg"]}');
}
break;
default:
}
}
@override
Widget build(BuildContext context) {
Color errorColor = Theme.of(context).colorScheme.error;
return Container(
padding: EdgeInsets.only(
bottom: MediaQueryData.fromView(
WidgetsBinding.instance.platformDispatcher.views.single)
.padding
.bottom +
20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => Get.back(),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28),
),
child: Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,
height: 3,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3))),
),
),
),
),
// 已登录用户才显示删除
if (GStorage.userInfo.get('userInfoCache') != null) ...[
ListTile(
onTap: () async => await menuActionHandler('delete'),
minLeadingWidth: 0,
leading: Icon(Icons.delete_outlined, color: errorColor, size: 19),
title: Text('删除',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: errorColor)),
),
ListTile(
onTap: () async => await menuActionHandler('report'),
minLeadingWidth: 0,
leading: Icon(Icons.error_outline, color: errorColor, size: 19),
title: Text('举报',
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(color: errorColor)),
),
],
ListTile(
onTap: () async => await menuActionHandler('copyAll'),
minLeadingWidth: 0,
leading: const Icon(Icons.copy_all_outlined, size: 19),
title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall),
),
ListTile(
onTap: () async => await menuActionHandler('copyFreedom'),
minLeadingWidth: 0,
leading: const Icon(Icons.copy_outlined, size: 19),
title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall),
),
// ListTile(
// onTap: () async => await menuActionHandler('block'),
// minLeadingWidth: 0,
// leading: Icon(Icons.block_outlined, color: errorColor),
// title: Text('加入黑名单', style: TextStyle(color: errorColor)),
// ),
// ListTile(
// onTap: () async => await menuActionHandler('report'),
// minLeadingWidth: 0,
// leading: Icon(Icons.report_outlined, color: errorColor),
// title: Text('举报', style: TextStyle(color: errorColor)),
// ),
],
),
);
}
}