diff --git a/lib/http/api.dart b/lib/http/api.dart index 39482c648..77731b6d1 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -120,6 +120,10 @@ class Api { // https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/action.md static const String replyAdd = '/x/v2/reply/add'; + // 删除评论 + // https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/action.md + static const String replyDel = '/x/v2/reply/del'; + // 用户(被)关注数、投稿数 // https://api.bilibili.com/x/relation/stat?vmid=697166795 static const String userStat = '/x/relation/stat'; diff --git a/lib/http/video.dart b/lib/http/video.dart index c5f941cdf..abceee954 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -376,6 +376,25 @@ class VideoHttp { } } + static Future replyDel({ + required int type,//replyType + required int oid, + required int rpid, + }) async { + var res = await Request().post(Api.replyDel, queryParameters: { + 'type': type,//type.index + 'oid': oid, + 'rpid': rpid, + 'csrf': await Request.getCsrf(), + }); + log(res.toString()); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } + // 查询是否关注up static Future hasFollow({required int mid}) async { var res = await Request().get(Api.hasFollow, data: {'fid': mid}); @@ -584,14 +603,11 @@ class VideoHttp { } } if (subtitlesVtt.isNotEmpty) { - subtitlesVtt.insert(0, { - 'language': '', - 'title': '关闭字幕', - 'text': "" - }); + subtitlesVtt.insert(0, {'language': '', 'title': '关闭字幕', 'text': ""}); } return subtitlesVtt; } + // 视频排行 static Future getRankVideoList(int rid) async { try { diff --git a/lib/models/video/reply/data.dart b/lib/models/video/reply/data.dart index 4cb1cc9df..7103253dd 100644 --- a/lib/models/video/reply/data.dart +++ b/lib/models/video/reply/data.dart @@ -23,18 +23,16 @@ class ReplyData { page = ReplyPage.fromJson(json['page']); config = ReplyConfig.fromJson(json['config']); replies = json['replies'] != null - ? json['replies'] + ? List.from(json['replies'] .map( - (item) => ReplyItemModel.fromJson(item, json['upper']['mid'])) - .toList() - : []; + (item) => ReplyItemModel.fromJson(item, json['upper']['mid']))) + : []; topReplies = json['top_replies'] != null - ? json['top_replies'] + ? List.from(json['top_replies'] .map((item) => ReplyItemModel.fromJson( item, json['upper']['mid'], - isTopStatus: true)) - .toList() - : []; + isTopStatus: true))) + : []; upper = ReplyUpper.fromJson(json['upper']); } } diff --git a/lib/models/video/reply/item.dart b/lib/models/video/reply/item.dart index 3ae811ecb..f699536c2 100644 --- a/lib/models/video/reply/item.dart +++ b/lib/models/video/reply/item.dart @@ -53,7 +53,7 @@ class ReplyItemModel { int? action; ReplyMember? member; ReplyContent? content; - List? replies; + List? replies; int? assist; UpAction? upAction; bool? invisible; @@ -85,10 +85,9 @@ class ReplyItemModel { member = ReplyMember.fromJson(json['member']); content = ReplyContent.fromJson(json['content']); replies = json['replies'] != null - ? json['replies'] - .map((item) => ReplyItemModel.fromJson(item, upperMid)) - .toList() - : []; + ? List.from(json['replies'] + .map((item) => ReplyItemModel.fromJson(item, upperMid))) + : []; assist = json['assist']; upAction = UpAction.fromJson(json['up_action']); invisible = json['invisible']; diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index db3b87bff..8fa51d649 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/http/video.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -54,7 +55,7 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(item: replyItem); + return MorePanel(item: replyItem!); }, ); }, @@ -273,6 +274,7 @@ class ReplyItem extends StatelessWidget { onPressed: () { feedBack(); showModalBottomSheet( + isDismissible: false, context: context, isScrollControlled: true, builder: (builder) { @@ -347,7 +349,7 @@ class ReplyItemRow extends StatelessWidget { this.replyItem, this.replyReply, }); - final List? replies; + final List? replies; ReplyControl? replyControl; // int? f_rpid; ReplyItemModel? replyItem; @@ -392,9 +394,8 @@ class ReplyItemRow extends StatelessWidget { i == 0 && (extraRow == 1 || replies!.length > 1) ? 4 : 6, ), child: Semantics( - label: replies![i].member.uname + - ' ' + - replies![i].content.message, + label: + '${replies![i].member!.uname} ${replies![i].content!.message}', excludeSemantics: true, child: Text.rich( style: TextStyle( @@ -412,7 +413,7 @@ class ReplyItemRow extends StatelessWidget { TextSpan( children: [ TextSpan( - text: replies![i].member.uname + ' ', + text: '${replies![i].member!.uname} ', style: TextStyle( color: Theme.of(context) .colorScheme @@ -423,16 +424,16 @@ class ReplyItemRow extends StatelessWidget { ..onTap = () { feedBack(); final String heroTag = Utils.makeHeroTag( - replies![i].member.mid); + replies![i].member!.mid); Get.toNamed( - '/member?mid=${replies![i].member.mid}', + '/member?mid=${replies![i].member!.mid}', arguments: { - 'face': replies![i].member.avatar, + 'face': replies![i].member!.avatar, 'heroTag': heroTag }); }, ), - if (replies![i].isUp) + if (replies![i].isUp!) const WidgetSpan( alignment: PlaceholderAlignment.top, child: PBadge( @@ -971,11 +972,11 @@ InlineSpan buildContent( } class MorePanel extends StatelessWidget { - final dynamic item; + final ReplyItemModel item; const MorePanel({super.key, required this.item}); Future menuActionHandler(String type) async { - String message = item.content.message ?? item.content; + String message = item.content?.message ?? ''; switch (type) { case 'copyAll': await Clipboard.setData(ClipboardData(text: message)); @@ -1000,9 +1001,46 @@ class MorePanel extends StatelessWidget { // case 'report': // SmartDialog.showToast('举报'); // break; - // case 'delete': - // SmartDialog.showToast('删除'); - // break; + case 'delete': + //弹出确认提示: + 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('取消'), + ), + 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('删除成功,请刷新'); + // Get.back(); + } else { + SmartDialog.showToast('删除失败, ${result["msg"]}'); + } + break; default: } } @@ -1011,7 +1049,12 @@ class MorePanel extends StatelessWidget { Widget build(BuildContext context) { Color errorColor = Theme.of(context).colorScheme.error; return Container( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + padding: EdgeInsets.only( + bottom: MediaQueryData.fromView( + WidgetsBinding.instance.platformDispatcher.views.single) + .padding + .bottom + + 20), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -1031,6 +1074,18 @@ class MorePanel extends StatelessWidget { ), ), ), + // 已登录用户才显示删除 + if (GStrorage.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('copyAll'), minLeadingWidth: 0, @@ -1055,12 +1110,6 @@ class MorePanel extends StatelessWidget { // leading: Icon(Icons.report_outlined, color: errorColor), // title: Text('举报', style: TextStyle(color: errorColor)), // ), - // ListTile( - // onTap: () async => await menuActionHandler('del'), - // minLeadingWidth: 0, - // leading: Icon(Icons.delete_outline, color: errorColor), - // title: Text('删除', style: TextStyle(color: errorColor)), - // ), ], ), );