diff --git a/lib/pages/common/reply_controller.dart b/lib/pages/common/reply_controller.dart index 4c2dff1dd..2ae3cd487 100644 --- a/lib/pages/common/reply_controller.dart +++ b/lib/pages/common/reply_controller.dart @@ -313,6 +313,49 @@ abstract class ReplyController extends CommonController { } } + void onCheckReply(context, item) { + try { + if (item is ReplyInfo) { + checkReply( + context: context, + oid: item.oid.toInt(), + rpid: item.hasRoot() ? item.root.toInt() : null, + replyType: item.type.toInt(), + replyId: item.id.toInt(), + message: item.content.message, + // + root: item.root.toInt(), + parent: item.parent.toInt(), + ctime: item.ctime.toInt(), + pictures: + item.content.pictures.map((item) => item.toProto3Json()).toList(), + mid: item.mid.toInt(), + // + isManual: true, + ); + } else if (item is ReplyItemModel) { + checkReply( + context: context, + oid: item.oid, + rpid: item.root == 0 ? null : item.root, + replyType: item.type!, + replyId: item.rpid!, + message: item.content!.message!, + // + root: item.root, + parent: item.parent, + ctime: item.ctime, + pictures: item.content?.pictures, + mid: item.mid, + // + isManual: true, + ); + } + } catch (e) { + SmartDialog.showToast(e.toString()); + } + } + // ref https://github.com/freedom-introvert/biliSendCommAntifraud void checkReply({ required BuildContext context, @@ -324,8 +367,9 @@ abstract class ReplyController extends CommonController { dynamic root, dynamic parent, dynamic ctime, - dynamic pictures, + List? pictures, dynamic mid, + bool isManual = false, }) async { // biliSendCommAntifraud if (Platform.isAndroid && _biliSendCommAntifraud) { @@ -346,7 +390,7 @@ abstract class ReplyController extends CommonController { 'parent': parent, 'ctime': ctime, 'comment_text': message, - if (pictures.isNotEmpty == true) 'pictures': jsonEncode(pictures), + if (pictures?.isNotEmpty == true) 'pictures': jsonEncode(pictures), 'source_id': '$sourceId', 'uid': mid, 'cookies': [cookieString], @@ -359,7 +403,9 @@ abstract class ReplyController extends CommonController { } // CommAntifraud - await Future.delayed(const Duration(seconds: 5)); + if (isManual.not) { + await Future.delayed(const Duration(seconds: 5)); + } void showReplyCheckResult(String message) { showDialog( context: context, @@ -447,7 +493,9 @@ abstract class ReplyController extends CommonController { } else if (res2 is Success) { // found if (context.mounted) { - showReplyCheckResult(''' + showReplyCheckResult(isManual + ? '无账号状态下找到了你的评论,评论正常!\n\n你的评论:$message' + : ''' 你评论状态有点可疑,虽然无账号翻找评论区获取不到你的评论,但是无账号可通过 https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? replyId}&type=$replyType 获取你的评论,疑似评论区被戒严或者这是你的视频。 diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 6cae8ede7..09f3a14ac 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -772,6 +772,8 @@ class _DynamicDetailPageState extends State _dynamicDetailController.hasUpTop && index == 0, upMid: loadingState.response.subjectControl.upMid, callback: _getImageCallback, + onCheckReply: (item) => _dynamicDetailController + .onCheckReply(context, item), ) : ReplyItem( replyItem: loadingState.response.replies[index], @@ -789,6 +791,8 @@ class _DynamicDetailPageState extends State }, onDelete: _dynamicDetailController.onMDelete, callback: _getImageCallback, + onCheckReply: (item) => _dynamicDetailController + .onCheckReply(context, item), ); } }, diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 41897fcca..e8f6aaf8a 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -755,6 +755,8 @@ class _HtmlRenderPageState extends State isTop: _htmlRenderCtr.hasUpTop && index == 0, upMid: loadingState.response.subjectControl.upMid, callback: _getImageCallback, + onCheckReply: (item) => + _htmlRenderCtr.onCheckReply(context, item), ) : ReplyItem( replyItem: loadingState.response.replies[index], @@ -772,6 +774,8 @@ class _HtmlRenderPageState extends State }, onDelete: _htmlRenderCtr.onMDelete, callback: _getImageCallback, + onCheckReply: (item) => + _htmlRenderCtr.onCheckReply(context, item), ); } }, diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index fef004af1..d56450659 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -2037,10 +2037,7 @@ List get extraSettings => [ alignment: Alignment.center, children: [ const Icon(Icons.shield_outlined), - Icon( - Icons.reply, - size: 14, - ), + const Icon(Icons.reply, size: 14), ], ), setKey: SettingBoxKey.enableCommAntifraud, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index a8324cec3..408316783 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -261,6 +261,8 @@ class _VideoReplyPanelState extends State onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, callback: widget.callback, + onCheckReply: (item) => _videoReplyController + .onCheckReply(context, item), ) : ReplyItem( replyItem: loadingState.response.replies[index], @@ -280,6 +282,8 @@ class _VideoReplyPanelState extends State onDismissed: widget.onDismissed, getTag: () => heroTag, callback: widget.callback, + onCheckReply: (item) => _videoReplyController + .onCheckReply(context, item), ); } }, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index abff87a4c..1b6573260 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -41,6 +41,7 @@ class ReplyItem extends StatelessWidget { this.onDismissed, this.getTag, this.callback, + required this.onCheckReply, }); final ReplyItemModel replyItem; final String? replyLevel; @@ -54,6 +55,7 @@ class ReplyItem extends StatelessWidget { final ValueChanged? onDismissed; final Function? getTag; final Function(List, int)? callback; + final ValueChanged onCheckReply; @override Widget build(BuildContext context) { @@ -71,7 +73,8 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel( + return morePanel( + context: context, item: replyItem, onDelete: (rpid) { onDelete?.call(rpid, null); @@ -496,7 +499,8 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel( + return morePanel( + context: context, item: replies[i], onDelete: onDelete, ); @@ -1024,119 +1028,115 @@ class ReplyItem extends StatelessWidget { // spanChildren.add(TextSpan(text: matchMember)); return TextSpan(children: spanChildren); } -} -class MorePanel extends StatelessWidget { - final ReplyItemModel item; - final Function(dynamic rpid)? onDelete; - - const MorePanel({super.key, required this.item, this.onDelete}); - - Future menuActionHandler(String type) async { - String message = item.content?.message ?? ''; - switch (type) { - case 'report': - Get.back(); - autoWrapReportDialog( - Get.context!, - ReportOptions.commentReport, - (reasonType, reasonDesc, banUid) async { - final res = await Request().post( - '/x/v2/reply/report', - data: { - 'add_blacklist': banUid, - 'csrf': await Request.getCsrf(), - 'gaia_source': 'main_h5', - 'oid': item.oid, - 'platform': 'android', - 'reason': reasonType, - 'rpid': item.rpid, - 'scene': 'main', - 'type': 1, - if (reasonType == 0) 'content': reasonDesc! - }, - options: Options(contentType: Headers.formUrlEncodedContentType), - ); - if (res.data['code'] == 0) { - onDelete?.call(item.rpid); - } - return res.data as Map; - }, - ); - break; - case 'copyAll': - Get.back(); - Utils.copyText(message); - 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( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('删除评论(测试)'), - content: - Text('确定尝试删除这条评论吗?\n\n$message\n\n注:只能删除自己的评论,或自己管理的评论区下的评论'), - actions: [ - TextButton( - onPressed: () { - Get.back(result: false); - }, - child: const Text('取消'), + Widget morePanel({ + required BuildContext context, + required ReplyItemModel item, + required onDelete, + }) { + Future menuActionHandler(String type) async { + late String message = item.content?.message ?? ''; + switch (type) { + case 'report': + Get.back(); + autoWrapReportDialog( + context, + ReportOptions.commentReport, + (reasonType, reasonDesc, banUid) async { + final res = await Request().post( + '/x/v2/reply/report', + data: { + 'add_blacklist': banUid, + 'csrf': await Request.getCsrf(), + 'gaia_source': 'main_h5', + 'oid': item.oid, + 'platform': 'android', + 'reason': reasonType, + 'rpid': item.rpid, + 'scene': 'main', + 'type': 1, + if (reasonType == 0) 'content': reasonDesc! + }, + options: + Options(contentType: Headers.formUrlEncodedContentType), + ); + if (res.data['code'] == 0) { + onDelete?.call(item.rpid); + } + return res.data as Map; + }, + ); + break; + case 'copyAll': + Get.back(); + Utils.copyText(message); + break; + case 'copyFreedom': + Get.back(); + showDialog( + context: context, + builder: (context) { + return Dialog( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: SelectableText(message), ), - 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!, oid: item.oid!, rpid: item.rpid!); - SmartDialog.dismiss(); - if (result['status']) { - SmartDialog.showToast('删除成功'); - onDelete?.call(item.rpid!); - } else { - SmartDialog.showToast('删除失败, ${result["msg"]}'); - } - break; - default: + ); + }, + ); + break; + case 'delete': + Get.back(); + bool? isDelete = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('删除评论(测试)'), + content: Text( + '确定尝试删除这条评论吗?\n\n$message\n\n注:只能删除自己的评论,或自己管理的评论区下的评论'), + actions: [ + 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!, oid: item.oid!, rpid: item.rpid!); + SmartDialog.dismiss(); + if (result['status']) { + SmartDialog.showToast('删除成功'); + onDelete?.call(item.rpid!); + } else { + SmartDialog.showToast('删除失败, ${result["msg"]}'); + } + break; + case 'checkReply': + Get.back(); + onCheckReply(item); + break; + default: + } } - } - @override - Widget build(BuildContext context) { + dynamic ownerMid = GStorage.ownerMid; Color errorColor = Theme.of(context).colorScheme.error; + return Padding( padding: EdgeInsets.only( bottom: MediaQueryData.fromView( @@ -1148,7 +1148,7 @@ class MorePanel extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ InkWell( - onTap: () => Get.back(), + onTap: Get.back, borderRadius: const BorderRadius.only( topLeft: Radius.circular(28), topRight: Radius.circular(28), @@ -1167,10 +1167,9 @@ class MorePanel extends StatelessWidget { ), ), ), - // 已登录用户才显示删除 - if (GStorage.userInfo.get('userInfoCache') != null) ...[ + if (ownerMid != null) ...[ ListTile( - onTap: () async => await menuActionHandler('delete'), + onTap: () => menuActionHandler('delete'), minLeadingWidth: 0, leading: Icon(Icons.delete_outlined, color: errorColor, size: 19), title: Text('删除', @@ -1180,7 +1179,7 @@ class MorePanel extends StatelessWidget { .copyWith(color: errorColor)), ), ListTile( - onTap: () async => await menuActionHandler('report'), + onTap: () => menuActionHandler('report'), minLeadingWidth: 0, leading: Icon(Icons.error_outline, color: errorColor, size: 19), title: Text('举报', @@ -1191,30 +1190,31 @@ class MorePanel extends StatelessWidget { ), ], ListTile( - onTap: () async => await menuActionHandler('copyAll'), + onTap: () => 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'), + onTap: () => 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)), - // ), + if (item.mid == ownerMid) + ListTile( + onTap: () => menuActionHandler('checkReply'), + minLeadingWidth: 0, + leading: Stack( + alignment: Alignment.center, + children: [ + const Icon(Icons.shield_outlined, size: 19), + const Icon(Icons.reply, size: 12), + ], + ), + title: + Text('检查评论', style: Theme.of(context).textTheme.titleSmall), + ), ], ), ); diff --git a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart index a65ab05dc..d1dc75420 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -44,6 +44,7 @@ class ReplyItemGrpc extends StatelessWidget { this.onViewImage, this.onDismissed, this.callback, + required this.onCheckReply, }); final ReplyInfo replyItem; final String? replyLevel; @@ -60,6 +61,7 @@ class ReplyItemGrpc extends StatelessWidget { final VoidCallback? onViewImage; final ValueChanged? onDismissed; final Function(List, int)? callback; + final ValueChanged onCheckReply; @override Widget build(BuildContext context) { @@ -74,7 +76,7 @@ class ReplyItemGrpc extends StatelessWidget { onLongPress: () { feedBack(); // showDialog( - // context: Get.context!, + // context: context, // builder: (context) => AlertDialog( // content: SelectableText(jsonEncode(replyItem.toProto3Json())), // ), @@ -1073,7 +1075,7 @@ class ReplyItemGrpc extends StatelessWidget { required onDelete, }) { Future menuActionHandler(String type) async { - String message = item.content.message; + late String message = item.content.message; switch (type) { case 'report': Get.back(); @@ -1112,7 +1114,7 @@ class ReplyItemGrpc extends StatelessWidget { case 'copyFreedom': Get.back(); showDialog( - context: Get.context!, + context: context, builder: (context) { return Dialog( child: Padding( @@ -1124,17 +1126,10 @@ class ReplyItemGrpc extends StatelessWidget { }, ); break; - // case 'block': - // SmartDialog.showToast('加入黑名单'); - // break; - // case 'report': - // SmartDialog.showToast('举报'); - // break; case 'delete': - //弹出确认提示: Get.back(); bool? isDelete = await showDialog( - context: Get.context!, + context: context, builder: (context) { return AlertDialog( title: const Text('删除评论(测试)'), @@ -1174,11 +1169,17 @@ class ReplyItemGrpc extends StatelessWidget { SmartDialog.showToast('删除失败, ${result["msg"]}'); } break; + case 'checkReply': + Get.back(); + onCheckReply(item); + break; default: } } + dynamic ownerMid = GStorage.ownerMid; Color errorColor = Theme.of(context).colorScheme.error; + return Padding( padding: EdgeInsets.only( bottom: MediaQueryData.fromView( @@ -1190,7 +1191,7 @@ class ReplyItemGrpc extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ InkWell( - onTap: () => Get.back(), + onTap: Get.back, borderRadius: const BorderRadius.only( topLeft: Radius.circular(28), topRight: Radius.circular(28), @@ -1209,10 +1210,9 @@ class ReplyItemGrpc extends StatelessWidget { ), ), ), - // 已登录用户才显示删除 - if (GStorage.userInfo.get('userInfoCache') != null) ...[ + if (ownerMid != null) ...[ ListTile( - onTap: () async => await menuActionHandler('delete'), + onTap: () => menuActionHandler('delete'), minLeadingWidth: 0, leading: Icon(Icons.delete_outlined, color: errorColor, size: 19), title: Text('删除', @@ -1222,7 +1222,7 @@ class ReplyItemGrpc extends StatelessWidget { .copyWith(color: errorColor)), ), ListTile( - onTap: () async => await menuActionHandler('report'), + onTap: () => menuActionHandler('report'), minLeadingWidth: 0, leading: Icon(Icons.error_outline, color: errorColor, size: 19), title: Text('举报', @@ -1233,30 +1233,31 @@ class ReplyItemGrpc extends StatelessWidget { ), ], ListTile( - onTap: () async => await menuActionHandler('copyAll'), + onTap: () => 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'), + onTap: () => 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)), - // ), + if (item.mid.toInt() == ownerMid) + ListTile( + onTap: () => menuActionHandler('checkReply'), + minLeadingWidth: 0, + leading: Stack( + alignment: Alignment.center, + children: [ + const Icon(Icons.shield_outlined, size: 19), + const Icon(Icons.reply, size: 12), + ], + ), + title: + Text('检查评论', style: Theme.of(context).textTheme.titleSmall), + ), ], ), ); diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 8c48f432e..5eaa255a3 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -197,6 +197,9 @@ class _VideoReplyReplyPanelState onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, callback: _getImageCallback, + onCheckReply: (item) => + _videoReplyReplyController.onCheckReply( + context, item), ) : ReplyItem( replyItem: firstFloor, @@ -210,6 +213,9 @@ class _VideoReplyReplyPanelState onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, callback: _getImageCallback, + onCheckReply: (item) => + _videoReplyReplyController.onCheckReply( + context, item), ); } else if (index == 1) { return Divider( @@ -523,6 +529,8 @@ class _VideoReplyReplyPanelState onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, callback: _getImageCallback, + onCheckReply: (item) => + _videoReplyReplyController.onCheckReply(context, item), ) : ReplyItem( replyItem: replyItem, @@ -544,6 +552,8 @@ class _VideoReplyReplyPanelState onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, callback: _getImageCallback, + onCheckReply: (item) => + _videoReplyReplyController.onCheckReply(context, item), ); } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 344615248..dc190957b 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -35,7 +35,7 @@ class GStorage { static bool get isLogin => userInfo.get('userInfoCache') != null; - static get ownerMid => GStorage.userInfo.get('userInfoCache')?.mid; + static get ownerMid => userInfo.get('userInfoCache')?.mid; static List get speedList => List.from( video.get(