From 5cc9c59c76cad312261840623ac85caa1cc57d4c Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Wed, 5 Mar 2025 07:42:04 +0000 Subject: [PATCH] report panel (#385) --- lib/common/widgets/report.dart | 200 ++++++++++++++++++ lib/pages/dynamics/widgets/author_panel.dart | 196 +++-------------- .../detail/reply/widgets/reply_item.dart | 31 ++- .../detail/reply/widgets/reply_item_grpc.dart | 32 ++- 4 files changed, 269 insertions(+), 190 deletions(-) create mode 100644 lib/common/widgets/report.dart diff --git a/lib/common/widgets/report.dart b/lib/common/widgets/report.dart new file mode 100644 index 000000000..b89b115c4 --- /dev/null +++ b/lib/common/widgets/report.dart @@ -0,0 +1,200 @@ +import 'package:PiliPlus/common/widgets/radio_widget.dart'; +import 'package:PiliPlus/utils/extension.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'; + +void autoWrapReportDialog( + BuildContext context, + Map> options, + Future Function(int, String?, bool) onSuccess, +) { + int? reasonType; + String? reasonDesc; + bool banUid = false; + late final key = GlobalKey(); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('举报'), + titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22), + contentPadding: const EdgeInsets.symmetric(vertical: 5), + actionsPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 10), + content: Builder( + builder: (context) { + return SingleChildScrollView( + child: AnimatedSize( + duration: const Duration(milliseconds: 200), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 22, + right: 22, + bottom: 5, + ), + child: const Text('请选择举报的理由:'), + ), + ...options.entries.map((title) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title.key.isNotEmpty) + Padding( + padding: const EdgeInsets.only(left: 22), + child: Text( + title.key, + style: Theme.of(context).textTheme.labelSmall, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Wrap( + children: title.value.entries.map((opt) { + return IntrinsicWidth( + child: radioWidget( + value: opt.key, + groupValue: reasonType, + title: opt.value, + onChanged: (value) { + reasonType = value; + if (context.mounted) { + (context as Element?)?.markNeedsBuild(); + } + }, + ), + ); + }).toList(), + ), + ), + ], + ); + }), + if (reasonType == 0) + Padding( + padding: const EdgeInsets.only( + left: 22, + top: 5, + right: 22, + ), + child: Form( + key: key, + child: TextFormField( + autofocus: true, + minLines: 4, + maxLines: 4, + initialValue: reasonDesc, + inputFormatters: [ + LengthLimitingTextInputFormatter(60), + ], + decoration: const InputDecoration( + labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息', + border: OutlineInputBorder(), + contentPadding: EdgeInsets.all(10), + ), + onChanged: (value) => reasonDesc = value, + validator: (value) { + if (value.isNullOrEmpty) { + return '理由不能为空'; + } + return null; + }, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 12, top: 12), + child: Row( + children: [ + Checkbox( + value: banUid, + onChanged: (v) { + banUid = v ?? false; + (context as Element?)?.markNeedsBuild(); + }, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + ), + const Text('拉黑该用户'), + ], + ), + ), + ], + ), + ), + ); + }, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + if (reasonType == null || + (reasonType == 0 && key.currentState?.validate() != true)) { + return; + } + SmartDialog.showLoading(); + try { + final data = await onSuccess(reasonType!, reasonDesc, banUid); + if (data['code'] == 0) { + Get.back(); + SmartDialog.showToast('举报成功'); + } else { + SmartDialog.showToast(data['message']); + } + } catch (e) { + SmartDialog.showToast('提交失败:$e'); + } finally { + SmartDialog.dismiss(); + } + }, + child: const Text('确定'), + ), + ], + ); + }, + ); +} + +class ReportOptions { + // from https://s1.hdslb.com/bfs/seed/jinkela/comment-h5/static/js/605.chunks.js + static const Map> commentReport = { + '违反法律法规': {9: '违法违规', 2: '色情', 10: '低俗', 12: '赌博诈骗', 23: '违法信息外链'}, + '谣言类不实信息': {19: '涉政谣言', 22: '虚假不实信息', 20: '涉社会事件谣言'}, + '侵犯个人权益': {7: '人身攻击', 15: '侵犯隐私'}, + '有害社区环境': { + 1: '垃圾广告', + 4: '引战', + 5: '剧透', + 3: '刷屏', + 8: '视频不相关', + 18: '违规抽奖', + 17: '青少年不良信息', + }, + '其他': {0: '其他'}, + }; + + static const Map> dynamicReport = { + '': { + 4: '垃圾广告', + 8: '引战', + 1: '色情', + 5: '人身攻击', + 3: '违法信息', + 9: '涉政谣言', + 10: '涉社会事件谣言', + 12: '虚假不实信息', + 13: '违法信息外链', + 0: '其他', + }, + }; +} diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index abfca822d..ca4ddf2a4 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -1,5 +1,6 @@ -import 'package:PiliPlus/common/widgets/radio_widget.dart'; +import 'package:PiliPlus/common/widgets/report.dart'; import 'package:PiliPlus/http/index.dart'; +import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -326,7 +327,30 @@ class AuthorPanel extends StatelessWidget { ), onTap: () { Get.back(); - _showReportDynDialog(context); + autoWrapReportDialog(context, ReportOptions.dynamicReport, + (reasonType, reasonDesc, banUid) async { + VideoHttp.relationMod( + mid: item.modules.moduleAuthor.mid, + act: 5, + reSrc: 11, + ); + final res = await Request().post( + '/x/dynamic/feed/dynamic_report/add', + queryParameters: { + 'csrf': await Request.getCsrf(), + }, + data: { + "accused_uid": item.modules.moduleAuthor.mid, + "dynamic_id": item.idStr, + "reason_type": reasonType, + "reason_desc": reasonType == 0 ? reasonDesc : null, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + return res.data as Map; + }); }, minLeadingWidth: 0, ), @@ -384,172 +408,4 @@ class AuthorPanel extends StatelessWidget { ), ); } - - void _showReportDynDialog(context) { - _ReportReasonType? reasonType; - String? reasonDesc; - late final key = GlobalKey(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('举报动态'), - titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22), - contentPadding: const EdgeInsets.only(top: 5), - actionsPadding: - const EdgeInsets.only(left: 16, right: 16, bottom: 10), - content: Builder(builder: (context) { - return SingleChildScrollView( - child: AnimatedSize( - duration: const Duration(milliseconds: 200), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only( - left: 22, - right: 22, - bottom: 5, - ), - child: const Text('请选择举报的理由:'), - ), - ...List.generate( - _ReportReasonType.values.length ~/ 2, - (index) => Row( - children: List.generate(2, (index2) { - return Expanded( - child: radioWidget<_ReportReasonType>( - paddingStart: index2 == 0 ? 10 : 0, - value: _ReportReasonType - .values[index * 2 + index2], - groupValue: reasonType, - title: _ReportReasonType - .values[index * 2 + index2].title, - onChanged: (value) { - reasonType = value; - if (context.mounted) { - (context as Element?)?.markNeedsBuild(); - } - }, - ), - ); - }), - ), - ), - if (reasonType == _ReportReasonType.s10) ...[ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: const Text('为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息'), - ), - Padding( - padding: const EdgeInsets.only( - left: 22, - top: 5, - right: 22, - ), - child: Form( - key: key, - child: TextFormField( - minLines: 4, - maxLines: 4, - initialValue: reasonDesc, - inputFormatters: [ - LengthLimitingTextInputFormatter(60), - ], - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.all(10), - ), - onChanged: (value) => reasonDesc = value, - validator: (value) { - if (value.isNullOrEmpty) { - return '理由不能为空'; - } - return null; - }, - ), - ), - ), - ], - ], - ), - ), - ); - }), - actions: [ - TextButton( - onPressed: Get.back, - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () async { - if (reasonType == null) { - return; - } - if (reasonType == _ReportReasonType.s10 && - key.currentState?.validate() != true) { - return; - } - try { - SmartDialog.showLoading(); - Request() - .post( - 'https://api.bilibili.com/x/dynamic/feed/dynamic_report/add', - queryParameters: { - 'csrf': await Request.getCsrf(), - }, - data: { - "accused_uid": item.modules.moduleAuthor.mid, - "dynamic_id": item.idStr, - "reason_type": reasonType!.code, - "reason_desc": reasonType == _ReportReasonType.s10 - ? reasonDesc - : null, - }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), - ) - .then((res) { - SmartDialog.dismiss(); - if (res.data['code'] == 0) { - Get.back(); - SmartDialog.showToast('举报成功'); - } else { - SmartDialog.showToast(res.data['message']); - } - }); - } catch (e) { - debugPrint('failed to report dyn: $e'); - } - }, - child: const Text('确定'), - ), - ], - ); - }); - } -} - -enum _ReportReasonType { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 } - -extension _ReportReasonTypeExt on _ReportReasonType { - String get title => [ - '垃圾广告', - '引战', - '色情', - '人身攻击', - '违法信息', - '涉政谣言', - '涉社会事件谣言', - '虚假不实信息', - '违法信息外链', - '其他', - ][index]; - int get code => [4, 8, 1, 5, 3, 9, 10, 12, 13, 0][index]; } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index c4de591b5..2b050b8d5 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -2,11 +2,14 @@ import 'dart:math'; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/imageview.dart'; +import 'package:PiliPlus/common/widgets/report.dart'; +import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dio/dio.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -1034,16 +1037,24 @@ class MorePanel extends StatelessWidget { switch (type) { case 'report': Get.back(); - dynamic result = await Get.toNamed( - '/webview', - parameters: { - 'url': - 'https://www.bilibili.com/h5/comment/report?mid=${item.mid}&oid=${item.oid}&pageType=1&rpid=${item.rpid}&platform=android', - }, - ); - if (result == true) { - onDelete?.call(item.rpid!); - } + autoWrapReportDialog(Get.context!, ReportOptions.commentReport, + (reasonType, reasonDesc, banUid) async { + final res = await Request().post('/x/v2/reply/report', + data: { + 'add_blacklist': banUid.toString(), + 'csrf': await Request.getCsrf(), + 'gaia_source': 'main_h5', + 'oid': item.oid.toString(), + 'platform': 'android', + 'reason': reasonType.toString(), + 'rpid': item.rpid.toString(), + 'scene': 'main', + 'type': '1', + if (reasonType == 0) 'content': reasonDesc! + }, + options: Options(contentType: Headers.formUrlEncodedContentType)); + return res.data as Map; + }); break; case 'copyAll': Get.back(); 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 7700bf9b1..37d29c2bb 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -3,13 +3,16 @@ import 'dart:math'; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/imageview.dart'; +import 'package:PiliPlus/common/widgets/report.dart'; import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart'; +import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/video/detail/reply/widgets/zan_grpc.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dio/dio.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -1074,16 +1077,25 @@ class ReplyItemGrpc extends StatelessWidget { switch (type) { case 'report': Get.back(); - dynamic result = await Get.toNamed( - '/webview', - 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?.call(item.id.toInt()); - } + autoWrapReportDialog(context, ReportOptions.commentReport, + (reasonType, reasonDesc, banUid) async { + final res = await Request().post('/x/v2/reply/report', + data: { + 'add_blacklist': banUid.toString(), + 'csrf': await Request.getCsrf(), + 'gaia_source': 'main_h5', + 'oid': item.oid.toString(), + 'platform': 'android', + 'reason': reasonType.toString(), + 'rpid': item.id.toString(), + 'scene': 'main', + 'type': '1', + if (reasonType == 0) 'content': reasonDesc! + }, + options: + Options(contentType: Headers.formUrlEncodedContentType)); + return res.data as Map; + }); break; case 'copyAll': Get.back();