opt: reply message

This commit is contained in:
bggRGjQaUbCoE
2024-09-29 13:11:16 +08:00
parent 878e9d400c
commit f1e64752ae

View File

@@ -246,38 +246,70 @@ class ReplyItem extends StatelessWidget {
), ),
), ),
// title // title
Container( Padding(
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), padding:
child: Semantics( const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
label: replyItem?.content?.message ?? "", child: LayoutBuilder(
// excludeSemantics: true, builder: (BuildContext context, BoxConstraints constraints) {
child: Text.rich( String text = replyItem?.content?.message ?? '';
style: TextStyle( var textPainter = TextPainter(
height: 1.75, text: TextSpan(text: text),
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize),
maxLines: maxLines:
replyItem!.content!.isText! && replyLevel == '1' ? 6 : 999, replyItem!.content!.isText! && replyLevel == '1' ? 6 : 999,
overflow: TextOverflow.ellipsis, textDirection: Directionality.of(context),
TextSpan( )..layout(maxWidth: constraints.maxWidth);
children: [ bool didExceedMaxLines = textPainter.didExceedMaxLines;
if (replyItem!.isTop!) ...[ return Column(
const WidgetSpan( mainAxisSize: MainAxisSize.min,
alignment: PlaceholderAlignment.top, crossAxisAlignment: CrossAxisAlignment.start,
child: PBadge( children: [
text: 'TOP', Semantics(
size: 'small', label: text,
stack: 'normal', child: Text.rich(
type: 'line', style: TextStyle(
fs: 9, height: 1.75,
semanticsLabel: '置顶', fontSize: Theme.of(context)
.textTheme
.bodyMedium!
.fontSize),
TextSpan(
children: [
if (replyItem!.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)
Text(
'查看更多',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
), ),
const TextSpan(text: ' '), ),
], ],
buildContent(context, replyItem!, replyReply, null), );
], },
), ),
)),
), ),
// 操作区域 // 操作区域
buttonAction(context, replyItem!.replyControl), buttonAction(context, replyItem!.replyControl),
@@ -425,73 +457,76 @@ class ReplyItemRow extends StatelessWidget {
i == 0 && (extraRow == 1 || replies!.length > 1) ? 4 : 6, i == 0 && (extraRow == 1 || replies!.length > 1) ? 4 : 6,
), ),
child: Semantics( child: Semantics(
label: label:
'${replies![i].member!.uname} ${replies![i].content!.message}', '${replies![i].member!.uname} ${replies![i].content!.message}',
excludeSemantics: true, excludeSemantics: true,
child: Text.rich( child: Text.rich(
style: TextStyle( style: TextStyle(
fontSize: Theme.of(context) fontSize: Theme.of(context)
.textTheme .textTheme
.bodyMedium! .bodyMedium!
.fontSize, .fontSize,
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme
.onSurface .onSurface
.withOpacity(0.85), .withOpacity(0.85),
height: 1.6), height: 1.6),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
TextSpan( TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: '${replies![i].member!.uname}', text: '${replies![i].member!.uname}',
style: TextStyle( style: TextStyle(
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme
.primary .primary
.withOpacity(0.85), .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!.avatar,
'heroTag': heroTag
});
},
), ),
if (replies![i].isUp!) ...[ recognizer: TapGestureRecognizer()
const TextSpan(text: ' '), ..onTap = () {
const WidgetSpan( feedBack();
alignment: PlaceholderAlignment.top, final String heroTag = Utils.makeHeroTag(
child: PBadge( replies![i].member!.mid);
text: 'UP', Get.toNamed(
size: 'small', '/member?mid=${replies![i].member!.mid}',
stack: 'normal', arguments: {
fs: 9, 'face': replies![i].member!.avatar,
), 'heroTag': heroTag
});
},
),
if (replies![i].isUp!) ...[
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].isUp!
? ''
: ' '),
buildContent(
context,
replies![i],
replyReply,
replyItem,
), ),
const TextSpan(text: ' '),
], ],
), TextSpan(
)), text: replies![i].root == replies![i].parent
? ': '
: replies![i].isUp!
? ''
: ' '),
buildContent(
context,
replies![i],
replyReply,
replyItem,
null,
null,
),
],
),
),
),
), ),
) )
], ],
@@ -539,7 +574,13 @@ class ReplyItemRow extends StatelessWidget {
} }
InlineSpan buildContent( InlineSpan buildContent(
BuildContext context, replyItem, replyReply, fReplyItem) { BuildContext context,
replyItem,
replyReply,
fReplyItem,
textPainter,
didExceedMaxLines,
) {
final String routePath = Get.currentRoute; final String routePath = Get.currentRoute;
bool isVideoPage = routePath.startsWith('/video'); bool isVideoPage = routePath.startsWith('/video');
@@ -547,12 +588,24 @@ InlineSpan buildContent(
// replyReply 查看二楼回复(回复详情)回调 // replyReply 查看二楼回复(回复详情)回调
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
final content = replyItem.content; final content = replyItem.content;
String message = content.message ?? '';
final List<InlineSpan> spanChildren = <InlineSpan>[]; 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);
}
// 投票 // 投票
if (content.vote.isNotEmpty) { if (content.vote.isNotEmpty) {
content.message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) {
onMatch: (Match match) {
// String matchStr = match[0]!; // String matchStr = match[0]!;
spanChildren.add( spanChildren.add(
TextSpan( TextSpan(
@@ -575,9 +628,9 @@ InlineSpan buildContent(
}, onNonMatch: (String str) { }, onNonMatch: (String str) {
return str; return str;
}); });
content.message = content.message.replaceAll(RegExp(r"\{vote:\d+?\}"), ""); message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), "");
} }
content.message = content.message message = message
.replaceAll('&amp;', '&') .replaceAll('&amp;', '&')
.replaceAll('&lt;', '<') .replaceAll('&lt;', '<')
.replaceAll('&gt;', '>') .replaceAll('&gt;', '>')
@@ -618,7 +671,7 @@ InlineSpan buildContent(
} }
// 分割文本并处理每个部分 // 分割文本并处理每个部分
content.message.splitMapJoin( message.splitMapJoin(
pattern, pattern,
onMatch: (Match match) { onMatch: (Match match) {
String matchStr = match[0]!; String matchStr = match[0]!;
@@ -835,7 +888,7 @@ InlineSpan buildContent(
if (content.jumpUrl.keys.isNotEmpty) { if (content.jumpUrl.keys.isNotEmpty) {
List<String> unmatchedItems = content.jumpUrl.keys List<String> unmatchedItems = content.jumpUrl.keys
.toList() .toList()
.where((item) => !content.message.contains(item)) .where((item) => !message.contains(item))
.toList(); .toList();
if (unmatchedItems.isNotEmpty) { if (unmatchedItems.isNotEmpty) {
for (int i = 0; i < unmatchedItems.length; i++) { for (int i = 0; i < unmatchedItems.length; i++) {