fix: 评论区识别逻辑重构,修复含有关键词的评论重复出现的问题

This commit is contained in:
orz12
2024-01-27 14:28:15 +08:00
parent 791047753d
commit 0c4bad406e

View File

@ -1,5 +1,6 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -585,153 +586,88 @@ InlineSpan buildContent(
if (content.message.contains('&')) { if (content.message.contains('&')) {
content.message = content.message.replaceAll('&', '&'); content.message = content.message.replaceAll('&', '&');
} }
// 匹配表情 print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString());
// 构建正则表达式
final List<String> specialTokens = [
...content.emote.keys,
...content.atNameToMid.keys.map((e) => '@$e'),
...content.jumpUrl.keys.map((e) =>
e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')),
r'\b\d{2}:\d{2}\b'
];
final String patternStr = specialTokens.map(RegExp.escape).join('|');
final RegExp pattern = RegExp(patternStr);
List<String> matchedStrs = [];
// 分割文本并处理每个部分
content.message.splitMapJoin( content.message.splitMapJoin(
RegExp(r"\[.*?\]"), pattern,
onMatch: (Match match) { onMatch: (Match match) {
final String matchStr = match[0]!; String matchStr = match[0]!;
if (content.emote.isNotEmpty && if (content.emote.containsKey(matchStr)) {
matchStr.indexOf('[') == matchStr.lastIndexOf('[') && // 处理表情
matchStr.indexOf(']') == matchStr.lastIndexOf(']')) {
final int size = content.emote[matchStr]['meta']['size']; final int size = content.emote[matchStr]['meta']['size'];
if (content.emote.keys.contains(matchStr)) { spanChilds.add(WidgetSpan(
spanChilds.add(
WidgetSpan(
child: NetworkImgLayer( child: NetworkImgLayer(
src: content.emote[matchStr]['url'], src: content.emote[matchStr]['url'],
type: 'emote', type: 'emote',
width: size * 20, width: size * 20,
height: size * 20, height: size * 20,
), ),
), ));
); } else if (matchStr.startsWith("@") &&
} else { content.atNameToMid.containsKey(matchStr.substring(1))) {
spanChilds.add(TextSpan( // 处理@用户
text: matchStr, final String userName = matchStr.substring(1);
recognizer: TapGestureRecognizer() final int userId = content.atNameToMid[userName];
..onTap = () =>
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
return matchStr;
}
} else {
spanChilds.add(TextSpan(
text: matchStr,
recognizer: TapGestureRecognizer()
..onTap = () =>
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
return matchStr;
}
return '';
},
onNonMatch: (String str) {
// 匹配@用户
String matchMember = str;
if (content.atNameToMid.isNotEmpty) {
final List atNameToMidKeys = content.atNameToMid.keys.toList();
RegExp reg = RegExp(atNameToMidKeys.map((key) => key).join('|'));
// if (!content.message.contains(':')) {
// reg = RegExp(r"@.*( |:)");
// }
// 只@用户没有内容
if (!content.message.contains(':') ||
(content.atNameToMid.length == 1 &&
content.message == '@${content.members.first.uname}')) {
reg = RegExp(r"@.*( |:|$)");
}
matchMember = str.splitMapJoin(
reg,
onMatch: (Match match) {
if (match[0] != null) {
hasMatchMember = true;
content.atNameToMid.forEach((key, value) {
if (str.contains('回复')) {
spanChilds.add( spanChilds.add(
TextSpan( TextSpan(
text: '回复 ', text: matchStr,
style: TextStyle( style: TextStyle(
fontSize:
Theme.of(context).textTheme.titleSmall!.fontSize,
),
),
);
}
spanChilds.add(
TextSpan(
text: '@$key',
style: TextStyle(
fontSize:
Theme.of(context).textTheme.titleSmall!.fontSize,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
final String heroTag = Utils.makeHeroTag(value); final String heroTag = Utils.makeHeroTag(userId);
Get.toNamed( Get.toNamed(
'/member?mid=$value', '/member?mid=$userId',
arguments: {'face': '', 'heroTag': heroTag}, arguments: {'face': '', 'heroTag': heroTag},
); );
}, },
), ),
); );
}); } else if (matchStr.contains(':')) {
} spanChilds.add(
return ''; TextSpan(
}, text: ' $matchStr ',
onNonMatch: (String str) { style: TextStyle(
if (!str.contains('@')) { color: Theme.of(context).colorScheme.primary,
spanChilds.add(TextSpan(text: str)); ),
} recognizer: TapGestureRecognizer()
print(str); ..onTap = () {
return str; // 跳转到指定位置
try {
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
.plPlayerController
.seekTo(
Duration(seconds: Utils.duration(matchStr)),
);
} catch (_) {}
}, },
),
); );
} else { } else {
matchMember = str; print("matchStr=$matchStr");
}
// 匹配 jumpUrl
String matchUrl = matchMember;
if (content.jumpUrl.isNotEmpty) {
final List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
for (int index = 0; index < urlKeys.length; index++) {
var i = urlKeys[index];
if (i.contains('?')) {
urlKeys[index] = i.replaceAll('?', '\\?');
}
if (i.contains('+')) {
urlKeys[index] = i.replaceAll('+', '\\+');
}
if (i.contains('*')) {
urlKeys[index] = i.replaceAll('*', '\\*');
}
}
if (hasMatchMember) {
matchMember = matchMember.split('回复 @ :').length > 1
? matchMember.split('回复 @ :')[1]
: matchMember;
}
matchUrl = matchMember.splitMapJoin(
/// RegExp.escape() 转义特殊字符
RegExp(urlKeys.map((key) => key).join("|")),
// RegExp('What does the fox say\\?'),
onMatch: (Match match) {
final String matchStr = match[0]!;
String appUrlSchema = ''; String appUrlSchema = '';
if (content.jumpUrl[matchStr] != null) {
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
}
// 默认不显示关键词
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
defaultValue: false) as bool; defaultValue: false) as bool;
if (content.jumpUrl[matchStr] != null) { if (enableWordRe && content.jumpUrl[matchStr] != null && !matchedStrs.contains(matchStr)) {
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
spanChilds.add( spanChilds.add(
TextSpan( TextSpan(
text: content.jumpUrl[matchStr]['title'], text: content.jumpUrl[matchStr]['title'],
style: TextStyle( style: TextStyle(
color: enableWordRe color: Theme.of(context).colorScheme.primary,
? Theme.of(context).colorScheme.primary
: null,
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
@ -757,8 +693,7 @@ InlineSpan buildContent(
); );
} }
} else { } else {
if (appUrlSchema.startsWith('bilibili://search') && if (appUrlSchema.startsWith('bilibili://search')) {
enableWordRe) {
Get.toNamed('/searchResult', parameters: { Get.toNamed('/searchResult', parameters: {
'keyword': content.jumpUrl[matchStr]['title'] 'keyword': content.jumpUrl[matchStr]['title']
}); });
@ -767,8 +702,6 @@ InlineSpan buildContent(
}, },
), ),
); );
}
if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) { if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) {
spanChilds.add( spanChilds.add(
WidgetSpan( WidgetSpan(
@ -781,59 +714,25 @@ InlineSpan buildContent(
), ),
); );
} }
return ''; // 只显示一次
}, matchedStrs.add(matchStr);
onNonMatch: (String str) { } else {
spanChilds.add(TextSpan( spanChilds.add(TextSpan(
text: str, text: matchStr,
recognizer: TapGestureRecognizer()
..onTap = () => replyReply(
replyItem.root == 0 ? replyItem : fReplyItem)));
return str;
},
);
}
str = matchUrl.splitMapJoin(
RegExp(r'\b\d{2}:\d{2}\b'),
onMatch: (Match match) {
String matchStr = match[0]!;
spanChilds.add(
TextSpan(
text: ' $matchStr ',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// 跳转到指定位置
try {
Get.find<VideoDetailController>(
tag: Get.arguments['heroTag'])
.plPlayerController
.seekTo(
Duration(seconds: Utils.duration(matchStr)),
);
} catch (_) {}
},
),
);
return '';
},
onNonMatch: (str) {
return str;
},
);
if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) {
if (str != '') {
spanChilds.add(TextSpan(
text: str,
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () => ..onTap = () =>
replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
} }
} }
return str; return '';
},
onNonMatch: (String nonMatchStr) {
spanChilds.add(TextSpan(
text: nonMatchStr,
recognizer: TapGestureRecognizer()
..onTap = () =>
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
return nonMatchStr;
}, },
); );