diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index c77d56bb3..9bd4a4076 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -32,8 +32,9 @@ class OverlayPop extends StatelessWidget { NetworkImgLayer( width: imgWidth, height: imgWidth / StyleString.aspectRatio, - src: (videoItem as card.Card?)?.smallCoverV5.base.cover ?? - videoItem.pic, + src: videoItem is card.Card + ? (videoItem as card.Card).smallCoverV5.base.cover + : videoItem.pic, quality: 100, ), Positioned( @@ -68,8 +69,9 @@ class OverlayPop extends StatelessWidget { children: [ Expanded( child: Text( - (videoItem as card.Card?)?.smallCoverV5.base.title ?? - videoItem.title, + videoItem is card.Card + ? (videoItem as card.Card).smallCoverV5.base.title + : videoItem.title, ), ), const SizedBox(width: 4), @@ -78,8 +80,9 @@ class OverlayPop extends StatelessWidget { onPressed: () async { await DownloadUtils.downloadImg( context, - (videoItem as card.Card?)?.smallCoverV5.base.cover ?? - (videoItem.pic != null + videoItem is card.Card + ? (videoItem as card.Card).smallCoverV5.base.cover + : (videoItem.pic != null ? videoItem.pic as String : videoItem.cover as String), ); diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index f571f4b69..0ac58afaa 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:ffi'; import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; @@ -9,6 +10,9 @@ import 'package:PiliPalaX/models/common/reply_type.dart'; import 'package:PiliPalaX/utils/feed_back.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; class VideoReplyController extends ReplyController { VideoReplyController( @@ -59,7 +63,16 @@ class VideoReplyController extends ReplyController { @override bool customHandleResponse(Success response) { MainListReply replies = response.response; + if (cursor == null) { + count.value = replies.subjectControl.count.toInt(); + } cursor = replies.cursor; + // replies.replies.clear(); + // showDialog( + // context: Get.context!, + // builder: (_) => AlertDialog( + // content: SelectableText(jsonEncode(replies.toProto3Json())), + // )); if (replies.replies.isNotEmpty) { noMore.value = '加载中...'; if (replies.cursor.isEnd) { @@ -75,7 +88,7 @@ class VideoReplyController extends ReplyController { : []; replies.replies.insertAll(0, list); } - loadingState.value = LoadingState.success(replies.replies); + loadingState.value = LoadingState.success(replies); return true; } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 55698b3c0..6b9c6f750 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -1,5 +1,6 @@ import 'package:PiliPalaX/common/widgets/http_error.dart'; import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/video/detail/reply/widgets/reply_item_grpc.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -128,7 +129,7 @@ class _VideoReplyPanelState extends State CustomScrollView( controller: _videoReplyController.scrollController, physics: const AlwaysScrollableScrollPhysics(), - key: const PageStorageKey('评论'), + // key: const PageStorageKey('评论'), slivers: [ SliverPersistentHeader( pinned: false, @@ -204,7 +205,7 @@ class _VideoReplyPanelState extends State delegate: SliverChildBuilderDelegate( (BuildContext context, index) { double bottom = MediaQuery.of(context).padding.bottom; - if (index == loadingState.response.length) { + if (index == loadingState.response.replies.length) { return Container( padding: EdgeInsets.only(bottom: bottom), height: bottom + 100, @@ -221,27 +222,25 @@ class _VideoReplyPanelState extends State ), ); } else { - return ListTile( - title: Text(loadingState.response[index].content.message), + return ReplyItemGrpc( + replyItem: loadingState.response.replies[index], + showReplyRow: true, + replyLevel: replyLevel, + replyReply: widget.replyReply, + replyType: ReplyType.video, + onReply: () { + _videoReplyController.onReply( + context, + replyItem: loadingState.response.replies[index], + index: index, + ); + }, + onDelete: _videoReplyController.onMDelete, + upMid: loadingState.response.subjectControl.upMid, ); - // return ReplyItem( - // replyItem: loadingState.response[index], - // showReplyRow: true, - // replyLevel: replyLevel, - // replyReply: widget.replyReply, - // replyType: ReplyType.video, - // onReply: () { - // _videoReplyController.onReply( - // context, - // replyItem: loadingState.response[index], - // index: index, - // ); - // }, - // onDelete: _videoReplyController.onMDelete, - // ); } }, - childCount: loadingState.response.length + 1, + childCount: loadingState.response.replies.length + 1, ), ) : loadingState is Error diff --git a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart new file mode 100644 index 000000000..611ca2ce4 --- /dev/null +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -0,0 +1,1170 @@ +import 'dart:convert'; + +import 'package:PiliPalaX/common/widgets/badge.dart'; +import 'package:PiliPalaX/common/widgets/imageview.dart'; +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; +import 'package:PiliPalaX/http/video.dart'; +import 'package:PiliPalaX/pages/video/detail/reply/widgets/zan.dart'; +import 'package:PiliPalaX/pages/video/detail/reply/widgets/zan_grpc.dart'; +import 'package:flutter/gestures.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'; +import 'package:hive/hive.dart'; +import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; +import 'package:PiliPalaX/models/common/reply_type.dart'; +import 'package:PiliPalaX/pages/video/detail/index.dart'; +import 'package:PiliPalaX/utils/feed_back.dart'; +import 'package:PiliPalaX/utils/storage.dart'; +import 'package:PiliPalaX/utils/url_utils.dart'; +import 'package:PiliPalaX/utils/utils.dart'; +import '../../../../../utils/app_scheme.dart'; + +Box setting = GStorage.setting; + +class ReplyItemGrpc extends StatelessWidget { + const ReplyItemGrpc({ + super.key, + required this.replyItem, + this.replyLevel, + this.showReplyRow = true, + this.replyReply, + this.replyType, + this.needDivider = true, + this.onReply, + this.onDelete, + this.upMid, + }); + final ReplyInfo replyItem; + final String? replyLevel; + final bool showReplyRow; + final Function? replyReply; + final ReplyType? replyType; + final bool needDivider; + final Function()? onReply; + final Function(dynamic rpid, dynamic frpid)? onDelete; + final dynamic upMid; + + @override + Widget build(BuildContext context) { + return Material( + child: InkWell( + // 点击整个评论区 评论详情/回复 + onTap: () { + feedBack(); + if (replyReply != null) { + replyReply!(replyItem); + } + }, + onLongPress: () { + feedBack(); + // showDialog( + // context: Get.context!, + // builder: (_) => AlertDialog( + // content: + // SelectableText(jsonEncode(replyItem.toProto3Json())), + // )); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel( + item: replyItem, + onDelete: (rpid) { + if (onDelete != null) { + onDelete!(rpid, null); + } + }, + ); + }, + ); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 14, 8, 5), + child: content(context), + ), + if (needDivider) + Divider( + indent: 55, + endIndent: 15, + height: 0.3, + color: Theme.of(context) + .colorScheme + .onInverseSurface + .withOpacity(0.5), + ) + ], + ), + ), + ); + } + + Widget lfAvtar(BuildContext context, String heroTag) { + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: replyItem.member.face, + width: 34, + height: 34, + type: 'avatar', + ), + ), + if (replyItem.member.vipType > 0) + Positioned( + right: 0, + bottom: 0, + child: Container( + decoration: BoxDecoration( + //borderRadius: BorderRadius.circular(7), + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surface, + ), + child: Image.asset( + 'assets/images/big-vip.png', + height: 14, + semanticLabel: "大会员", + ), + ), + ), + //https://www.bilibili.com/blackboard/activity-whPrHsYJ2.html + if (replyItem.member.officialVerifyType == 0) + Positioned( + left: 0, + bottom: 0, + child: Container( + decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(8), + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surface, + ), + child: const Icon( + Icons.offline_bolt, + color: Colors.yellow, + size: 14, + semanticLabel: "认证个人", + ), + ), + ) + else if (replyItem.member.officialVerifyType == 1) + Positioned( + left: 0, + bottom: 0, + child: Container( + decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(8), + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surface, + ), + child: const Icon( + Icons.offline_bolt, + color: Colors.lightBlueAccent, + size: 14, + semanticLabel: "认证机构", + ), + ), + ), + ], + ); + } + + Widget content(BuildContext context) { + final String heroTag = Utils.makeHeroTag(replyItem.mid); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// fix Stack内GestureDetector onTap无效 + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + feedBack(); + Get.toNamed('/member?mid=${replyItem.mid}', + arguments: {'face': replyItem.member.face, 'heroTag': heroTag}); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + lfAvtar(context, heroTag), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + replyItem.member.name, + style: TextStyle( + color: (replyItem.member.vipType == 2) + ? Utils.vipColor + : Theme.of(context).colorScheme.outline, + fontSize: 13, + ), + ), + const SizedBox(width: 6), + Image.asset( + 'assets/images/lv/lv${replyItem.member.level}.png', + height: 11, + semanticLabel: "等级:${replyItem.member.level}", + ), + const SizedBox(width: 6), + if (replyItem.mid == upMid) + const PBadge( + text: 'UP', + size: 'small', + stack: 'normal', + fs: 9, + ), + ], + ), + Row( + children: [ + Text( + Utils.dateFormat(replyItem.ctime.toInt()), + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + if (replyItem.replyControl.location.isNotEmpty) + Text( + ' • ${replyItem.replyControl.location}', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), + ), + ], + ) + ], + ), + ], + ), + ), + // title + Padding( + padding: + const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + String text = replyItem.content.message; + var textPainter = TextPainter( + text: TextSpan(text: text), + maxLines: 6, + // replyItem.content!.isText! && replyLevel == '1' ? 6 : 999, + textDirection: Directionality.of(context), + )..layout(maxWidth: constraints.maxWidth); + bool didExceedMaxLines = textPainter.didExceedMaxLines; + return Semantics( + label: text, + child: Text.rich( + style: TextStyle( + height: 1.75, + 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) + TextSpan( + text: '\n查看更多', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + ), + ); + }, + ), + ), + // 操作区域 + buttonAction(context, replyItem.replyControl), + // 一楼的评论 + if (( //replyItem.replyControl!.isShow! || + replyItem.replies.isNotEmpty || + replyItem.replyControl.subReplyEntryText.isNotEmpty) && + showReplyRow) ...[ + Padding( + padding: const EdgeInsets.only(top: 5, bottom: 12), + child: ReplyItemRow( + replies: replyItem.replies, + replyControl: replyItem.replyControl, + // f_rpid: replyItem.rpid, + replyItem: replyItem, + replyReply: replyReply, + onDelete: (rpid) { + if (onDelete != null) { + onDelete!(rpid, replyItem.id); + } + }, + ), + ), + ], + ], + ); + } + + // 感谢、回复、复制 + Widget buttonAction(BuildContext context, replyControl) { + return Row( + children: [ + const SizedBox(width: 32), + SizedBox( + height: 32, + child: TextButton( + onPressed: () { + feedBack(); + if (onReply != null) { + onReply!(); + return; + } + }, + child: Row(children: [ + Icon(Icons.reply, + size: 18, + color: + Theme.of(context).colorScheme.outline.withOpacity(0.8)), + const SizedBox(width: 3), + Text( + '回复', + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ]), + ), + ), + const SizedBox(width: 2), + if (replyItem.replyControl.upLike) ...[ + SizedBox( + height: 32, + child: TextButton( + onPressed: null, + child: Text( + 'UP主觉得很赞', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + fontWeight: FontWeight.normal, + ), + ), + ), + ), + const SizedBox(width: 2), + ], + if (replyItem.replyControl.cardLabels + .map((item) => item.textContent) + .toList() + .contains('热评')) + Text( + '热评', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize), + ), + const Spacer(), + ZanButtonGrpc(replyItem: replyItem, replyType: replyType), + const SizedBox(width: 5) + ], + ); + } +} + +// ignore: must_be_immutable +class ReplyItemRow extends StatelessWidget { + ReplyItemRow({ + super.key, + required this.replies, + required this.replyControl, + // this.f_rpid, + this.replyItem, + this.replyReply, + this.onDelete, + this.upMid, + }); + final List replies; + ReplyControl replyControl; + // int? f_rpid; + ReplyInfo? replyItem; + Function? replyReply; + final Function(dynamic rpid)? onDelete; + final dynamic upMid; + + @override + Widget build(BuildContext context) { + final int extraRow = // replyControl?.isShow == true || + (replyControl.subReplyEntryText.isNotEmpty && replies.isEmpty) ? 1 : 0; + return Container( + margin: const EdgeInsets.only(left: 42, right: 4, top: 0), + child: Material( + color: Theme.of(context).colorScheme.onInverseSurface, + borderRadius: BorderRadius.circular(6), + clipBehavior: Clip.hardEdge, + animationDuration: Duration.zero, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (replies.isNotEmpty) + for (int i = 0; i < replies.length; i++) ...[ + InkWell( + // 一楼点击评论展开评论详情 + onTap: () => replyReply!(replyItem), + onLongPress: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel( + item: replies[i], + onDelete: onDelete, + ); + }, + ); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.fromLTRB( + 8, + i == 0 && (extraRow == 1 || replies.length > 1) ? 8 : 4, + 8, + i == 0 && (extraRow == 1 || replies.length > 1) ? 4 : 6, + ), + child: Semantics( + label: + '${replies[i].member.name} ${replies[i].content.message}', + excludeSemantics: true, + child: Text.rich( + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .bodyMedium! + .fontSize, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.85), + height: 1.6), + overflow: TextOverflow.ellipsis, + maxLines: 2, + TextSpan( + children: [ + TextSpan( + text: replies[i].member.name, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary + .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.face, + 'heroTag': heroTag + }); + }, + ), + if (replies[i].mid == upMid) ...[ + 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].mid == upMid + ? '' + : ' ', + ), + buildContent( + context, + replies[i], + replyReply, + replyItem, + null, + null, + ), + ], + ), + ), + ), + ), + ) + ], + if (extraRow == 1) + InkWell( + // 一楼点击【共xx条回复】展开评论详情 + onTap: () => replyReply!(replyItem), + child: Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), + child: Text.rich( + TextSpan( + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + children: [ + if (replyControl.upReply) + TextSpan( + text: 'UP主等人 ', + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.85))), + TextSpan( + text: replyControl.subReplyEntryText, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary + .withOpacity(0.85), + ), + ) + ], + ), + ), + ), + ) + ], + ), + ), + ); + } +} + +InlineSpan buildContent( + BuildContext context, + replyItem, + replyReply, + fReplyItem, + textPainter, + didExceedMaxLines, +) { + final String routePath = Get.currentRoute; + bool isVideoPage = routePath.startsWith('/video'); + + // replyItem 当前回复内容 + // replyReply 查看二楼回复(回复详情)回调 + // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 + final Content content = replyItem.content; + String message = content.message; + final List spanChildren = []; + + 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); + } + + return TextSpan(text: message); + + // 投票 + // if (content.vote.isNotEmpty) { + // message.splitMapJoin(RegExp(r"\{vote:\d+?\}"), onMatch: (Match match) { + // // String matchStr = match[0]!; + // spanChildren.add( + // TextSpan( + // text: '投票: ${content.vote['title']}', + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () => Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': content.vote['url'], + // 'type': 'vote', + // 'pageTitle': content.vote['title'], + // }, + // ), + // ), + // ); + // return ''; + // }, onNonMatch: (String str) { + // return str; + // }); + // message = message.replaceAll(RegExp(r"\{vote:\d+?\}"), ""); + // } + message = message + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll(' ', ' '); + // 构建正则表达式 + final List specialTokens = [ + ...content.emote.keys, + // ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [], + ...content.atNameToMid.keys.map((e) => '@$e'), + ]; + // List jumpUrlKeysList = content.jumpUrl.keys.map((String e) { + // return e.replaceAllMapped( + // RegExp(r'[?+*]'), (match) => '\\${match.group(0)}'); + // }).toList(); + specialTokens.sort((a, b) => b.length.compareTo(a.length)); + String patternStr = specialTokens.map(RegExp.escape).join('|'); + if (patternStr.isNotEmpty) { + patternStr += "|"; + } + patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)'; + // if (jumpUrlKeysList.isNotEmpty) { + // patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}'; + // } + final RegExp pattern = RegExp(patternStr); + List matchedStrs = []; + void addPlainTextSpan(str) { + spanChildren.add(TextSpan( + text: str, + )); + // TextSpan( + // + // text: str, + // recognizer: TapGestureRecognizer() + // ..onTap = () => replyReply + // ?.call(replyItem.root == 0 ? replyItem : fReplyItem))))); + } + + // 分割文本并处理每个部分 + // message.splitMapJoin( + // pattern, + // onMatch: (Match match) { + // String matchStr = match[0]!; + // if (content.emote.containsKey(matchStr)) { + // // 处理表情 + // final int size = content.emote[matchStr]['meta']['size']; + // spanChildren.add(WidgetSpan( + // child: ExcludeSemantics( + // child: NetworkImgLayer( + // src: content.emote[matchStr]['url'], + // type: 'emote', + // width: size * 20, + // height: size * 20, + // semanticsLabel: matchStr, + // )), + // )); + // } else if (matchStr.startsWith("@") && + // content.atNameToMid.containsKey(matchStr.substring(1))) { + // // 处理@用户 + // final String userName = matchStr.substring(1); + // final int userId = content.atNameToMid[userName]; + // spanChildren.add( + // TextSpan( + // text: matchStr, + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () { + // final String heroTag = Utils.makeHeroTag(userId); + // Get.toNamed( + // '/member?mid=$userId', + // arguments: {'face': '', 'heroTag': heroTag}, + // ); + // }, + // ), + // ); + // } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$') + // .hasMatch(matchStr)) { + // matchStr = matchStr.replaceAll(':', ':'); + // spanChildren.add( + // TextSpan( + // text: ' $matchStr ', + // style: isVideoPage + // ? TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ) + // : null, + // recognizer: TapGestureRecognizer() + // ..onTap = () { + // // 跳转到指定位置 + // if (isVideoPage) { + // try { + // SmartDialog.showToast('跳转至:$matchStr'); + // Get.find( + // tag: Get.arguments['heroTag']) + // .plPlayerController + // .seekTo(Duration(seconds: Utils.duration(matchStr)), + // type: 'slider'); + // } catch (e) { + // SmartDialog.showToast('跳转失败: $e'); + // } + // } + // }, + // ), + // ); + // } else { + // String appUrlSchema = ''; + // final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, + // defaultValue: false) as bool; + // if (content.jumpUrl[matchStr] != null && + // !matchedStrs.contains(matchStr)) { + // appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; + // if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) { + // addPlainTextSpan(matchStr); + // return ""; + // } + // spanChildren.addAll( + // [ + // if (content.jumpUrl[matchStr]?['prefix_icon'] != null) ...[ + // WidgetSpan( + // child: Image.network( + // content.jumpUrl[matchStr]['prefix_icon'], + // height: 19, + // color: Theme.of(context).colorScheme.primary, + // ), + // ) + // ], + // TextSpan( + // text: content.jumpUrl[matchStr]['title'], + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () async { + // final String title = content.jumpUrl[matchStr]['title']; + // if (appUrlSchema == '') { + // if (matchStr.startsWith('BV')) { + // UrlUtils.matchUrlPush( + // matchStr, + // title, + // '', + // ); + // } else if (RegExp(r'^[Cc][Vv][0-9]+$') + // .hasMatch(matchStr)) { + // Get.toNamed('/htmlRender', parameters: { + // 'url': 'https://www.bilibili.com/read/$matchStr', + // 'title': title, + // 'id': matchStr, + // 'dynamicType': 'read' + // }); + // } else { + // final String redirectUrl = + // await UrlUtils.parseRedirectUrl(matchStr); + // // if (redirectUrl == matchStr) { + // // Clipboard.setData(ClipboardData(text: matchStr)); + // // SmartDialog.showToast('地址可能有误'); + // // return; + // // } + // Uri uri = Uri.parse(redirectUrl); + // PiliScheme.routePush(uri); + // // final String pathSegment = Uri.parse(redirectUrl).path; + // // final String lastPathSegment = + // // pathSegment.split('/').last; + // // if (lastPathSegment.startsWith('BV')) { + // // UrlUtils.matchUrlPush( + // // lastPathSegment, + // // title, + // // redirectUrl, + // // ); + // // } else { + // // Get.toNamed( + // // '/webviewnew', + // // parameters: { + // // 'url': redirectUrl, + // // 'type': 'url', + // // 'pageTitle': title + // // }, + // // ); + // // } + // } + // } else { + // if (appUrlSchema.startsWith('bilibili://search')) { + // Get.toNamed('/searchResult', + // parameters: {'keyword': title}); + // } else if (matchStr.startsWith('https://b23.tv')) { + // final String redirectUrl = + // await UrlUtils.parseRedirectUrl(matchStr); + // final String pathSegment = Uri.parse(redirectUrl).path; + // final String lastPathSegment = + // pathSegment.split('/').last; + // if (lastPathSegment.startsWith('BV')) { + // UrlUtils.matchUrlPush( + // lastPathSegment, + // title, + // redirectUrl, + // ); + // } else { + // Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': redirectUrl, + // 'type': 'url', + // 'pageTitle': title + // }, + // ); + // } + // } else { + // Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': matchStr, + // 'type': 'url', + // 'pageTitle': title + // }, + // ); + // } + // } + // }, + // ) + // ], + // ); + // // 只显示一次 + // matchedStrs.add(matchStr); + // } else if (matchStr.length > 1 && + // content.topicsMeta[matchStr.substring(1, matchStr.length - 1)] != + // null) { + // spanChildren.add( + // TextSpan( + // text: matchStr, + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () { + // final String topic = + // matchStr.substring(1, matchStr.length - 1); + // Get.toNamed('/searchResult', parameters: {'keyword': topic}); + // }, + // ), + // ); + // } else { + // addPlainTextSpan(matchStr); + // } + // } + // return ''; + // }, + // onNonMatch: (String nonMatchStr) { + // addPlainTextSpan(nonMatchStr); + // return nonMatchStr; + // }, + // ); + + // if (content.jumpUrl.keys.isNotEmpty) { + // List unmatchedItems = content.jumpUrl.keys + // .toList() + // .where((item) => !content.message.contains(item)) + // .toList(); + // if (unmatchedItems.isNotEmpty) { + // for (int i = 0; i < unmatchedItems.length; i++) { + // String patternStr = unmatchedItems[i]; + // spanChildren.addAll( + // [ + // if (content.jumpUrl[patternStr]?['prefix_icon'] != null) ...[ + // WidgetSpan( + // child: Image.network( + // content.jumpUrl[patternStr]['prefix_icon'], + // height: 19, + // color: Theme.of(context).colorScheme.primary, + // ), + // ) + // ], + // TextSpan( + // text: content.jumpUrl[patternStr]['title'], + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () { + // Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': patternStr, + // 'type': 'url', + // 'pageTitle': content.jumpUrl[patternStr]['title'] + // }, + // ); + // }, + // ) + // ], + // ); + // } + // } + // } + // 图片渲染 + // if (content.pictures.isNotEmpty) { + // spanChildren.add(const TextSpan(text: '\n')); + // spanChildren.add( + // WidgetSpan( + // child: LayoutBuilder( + // builder: (_, constraints) => image( + // constraints.maxWidth, + // (content.pictures as List) + // .map( + // (item) => ImageModel( + // width: item['img_width'], + // height: item['img_height'], + // url: item['img_src'], + // ), + // ) + // .toList(), + // ), + // ), + // ), + // ); + // } + + // 笔记链接 + // if (content.richText.isNotEmpty) { + // spanChildren.add( + // TextSpan( + // text: ' 笔记', + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // ), + // recognizer: TapGestureRecognizer() + // ..onTap = () => Get.toNamed( + // '/webviewnew', + // parameters: { + // 'url': content.richText['note']['click_url'], + // 'type': 'note', + // 'pageTitle': '笔记预览' + // }, + // ), + // ), + // ); + // } + // spanChildren.add(TextSpan(text: matchMember)); + return TextSpan(children: spanChildren); +} + +class MorePanel extends StatelessWidget { + final ReplyInfo 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(); + dynamic result = await Get.toNamed( + '/webviewnew', + 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 != null) { + onDelete!(item.id); + } + break; + case 'copyAll': + await Clipboard.setData(ClipboardData(text: message)); + SmartDialog.showToast('已复制'); + Get.back(); + 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('取消'), + ), + 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.toInt(), + oid: item.oid.toInt(), + rpid: item.id.toInt(), + ); + SmartDialog.dismiss(); + if (result['status']) { + SmartDialog.showToast('删除成功'); + // Get.back(); + if (onDelete != null) { + onDelete!(item.id); + } + } else { + SmartDialog.showToast('删除失败, ${result["msg"]}'); + } + break; + default: + } + } + + @override + Widget build(BuildContext context) { + Color errorColor = Theme.of(context).colorScheme.error; + return Container( + padding: EdgeInsets.only( + bottom: MediaQueryData.fromView( + WidgetsBinding.instance.platformDispatcher.views.single) + .padding + .bottom + + 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + ), + // 已登录用户才显示删除 + if (GStorage.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('report'), + minLeadingWidth: 0, + leading: Icon(Icons.error_outline, color: errorColor, size: 19), + title: Text('举报', + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(color: errorColor)), + ), + ], + ListTile( + onTap: () async => await 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'), + 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)), + // ), + ], + ), + ); + } +} diff --git a/lib/pages/video/detail/reply/widgets/zan_grpc.dart b/lib/pages/video/detail/reply/widgets/zan_grpc.dart new file mode 100644 index 000000000..dbcd70703 --- /dev/null +++ b/lib/pages/video/detail/reply/widgets/zan_grpc.dart @@ -0,0 +1,175 @@ +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.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:PiliPalaX/http/reply.dart'; +import 'package:PiliPalaX/models/common/reply_type.dart'; +import 'package:PiliPalaX/models/video/reply/item.dart'; +import 'package:PiliPalaX/utils/feed_back.dart'; +import 'package:fixnum/fixnum.dart' as $fixnum; + +class ZanButtonGrpc extends StatefulWidget { + const ZanButtonGrpc({ + super.key, + required this.replyItem, + this.replyType, + }); + + final ReplyInfo replyItem; + final ReplyType? replyType; + + @override + State createState() => _ZanButtonGrpcState(); +} + +class _ZanButtonGrpcState extends State { + Future onHateReply() async { + feedBack(); + // SmartDialog.showLoading(msg: 'pilipala ...'); + final int oid = widget.replyItem.oid.toInt(); + final int rpid = widget.replyItem.id.toInt(); + // 1 已点赞 2 不喜欢 0 未操作 + final int action = + widget.replyItem.replyControl.action.toInt() != 2 ? 2 : 0; + final res = await ReplyHttp.hateReply( + type: widget.replyType!.index, + action: action == 2 ? 1 : 0, + oid: oid, + rpid: rpid, + ); + // SmartDialog.dismiss(); + if (res['status']) { + SmartDialog.showToast( + widget.replyItem.replyControl.action.toInt() != 2 ? '点踩成功' : '取消踩'); + if (action == 2) { + if (widget.replyItem.replyControl.action.toInt() == 1) { + widget.replyItem.like = + $fixnum.Int64(widget.replyItem.like.toInt() - 1); + } + widget.replyItem.replyControl.action = $fixnum.Int64(2); + } else { + // replyItem.like = replyItem.like! - 1; + widget.replyItem.replyControl.action = $fixnum.Int64(0); + } + setState(() {}); + } else { + SmartDialog.showToast(res['msg']); + } + } + + // 评论点赞 + Future onLikeReply() async { + feedBack(); + // SmartDialog.showLoading(msg: 'pilipala ...'); + final int oid = widget.replyItem.oid.toInt(); + final int rpid = widget.replyItem.id.toInt(); + // 1 已点赞 2 不喜欢 0 未操作 + final int action = + widget.replyItem.replyControl.action.toInt() != 1 ? 1 : 0; + final res = await ReplyHttp.likeReply( + type: widget.replyType!.index, + oid: oid, + rpid: rpid, + action: action, + ); + // SmartDialog.dismiss(); + if (res['status']) { + SmartDialog.showToast( + widget.replyItem.replyControl.action.toInt() != 1 ? '点赞成功' : '取消赞'); + if (action == 1) { + widget.replyItem.like = + $fixnum.Int64(widget.replyItem.like.toInt() + 1); + widget.replyItem.replyControl.action = $fixnum.Int64(1); + } else { + widget.replyItem.like = + $fixnum.Int64(widget.replyItem.like.toInt() - 1); + widget.replyItem.replyControl.action = $fixnum.Int64(0); + } + setState(() {}); + } else { + SmartDialog.showToast(res['msg']); + } + } + + bool isProcessing = false; + void Function()? handleState(Future Function() action) { + return isProcessing + ? null + : () async { + setState(() => isProcessing = true); + await action(); + setState(() => isProcessing = false); + }; + } + + @override + Widget build(BuildContext context) { + final ThemeData t = Theme.of(context); + final Color color = t.colorScheme.outline; + final Color primary = t.colorScheme.primary; + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 32, + child: TextButton( + onPressed: handleState(onHateReply), + child: Icon( + widget.replyItem.replyControl.action.toInt() == 2 + ? FontAwesomeIcons.solidThumbsDown + : FontAwesomeIcons.thumbsDown, + size: 16, + color: widget.replyItem.replyControl.action.toInt() == 2 + ? primary + : color, + semanticLabel: widget.replyItem.replyControl.action.toInt() == 2 + ? '已踩' + : '点踩', + ), + ), + ), + SizedBox( + height: 32, + child: TextButton( + onPressed: handleState(onLikeReply), + child: Row( + children: [ + Icon( + widget.replyItem.replyControl.action.toInt() == 1 + ? FontAwesomeIcons.solidThumbsUp + : FontAwesomeIcons.thumbsUp, + size: 16, + color: widget.replyItem.replyControl.action.toInt() == 1 + ? primary + : color, + semanticLabel: + widget.replyItem.replyControl.action.toInt() == 1 + ? '已赞' + : '点赞', + ), + const SizedBox(width: 4), + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + widget.replyItem.like.toString(), + // key: ValueKey(widget.replyItem!.like!), + style: TextStyle( + color: widget.replyItem.replyControl.action.toInt() == 1 + ? primary + : color, + fontSize: t.textTheme.labelSmall!.fontSize, + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index d9fd11054..7d30238e7 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -20,6 +20,8 @@ class VideoReplyReplyController extends CommonController { ReplyInfo? root; CursorReply? cursor; + Rx mode = Mode.MAIN_LIST_HOT.obs; + RxInt count = (-1).obs; @override void onInit() { @@ -79,6 +81,9 @@ class VideoReplyReplyController extends CommonController { bool customHandleResponse(Success response) { DetailListReply replies = response.response; root = replies.root; + if (cursor == null) { + count.value = replies.root.count.toInt(); + } cursor = replies.cursor; if (replies.root.replies.isNotEmpty) { noMore.value = '加载中...'; @@ -105,7 +110,15 @@ class VideoReplyReplyController extends CommonController { root: int.parse(rpid!), cursor: CursorReq( next: cursor?.next, - mode: Mode.MAIN_LIST_HOT, // Mode.MAIN_LIST_TIME // Mode.MAIN_LIST_HOT + mode: mode.value, ), ); + + queryBySort() { + mode.value = mode.value == Mode.MAIN_LIST_HOT + ? Mode.MAIN_LIST_TIME + : Mode.MAIN_LIST_HOT; + loadingState.value = LoadingState.loading(); + onRefresh(); + } } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index aee88ba8c..23935d7cf 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -1,4 +1,9 @@ +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pbenum.dart'; import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/video/detail/reply/view.dart' + show MySliverPersistentHeaderDelegate; +import 'package:PiliPalaX/pages/video/detail/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPalaX/pages/video/detail/reply_new/reply_page.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -15,7 +20,7 @@ import 'controller.dart'; class VideoReplyReplyPanel extends StatefulWidget { const VideoReplyReplyPanel({ - this.rcount, + // this.rcount, this.oid, this.rpid, this.firstFloor, @@ -23,10 +28,10 @@ class VideoReplyReplyPanel extends StatefulWidget { this.replyType, super.key, }); - final dynamic rcount; + // final dynamic rcount; final int? oid; final int? rpid; - final ReplyItemModel? firstFloor; + final ReplyInfo? firstFloor; final String? source; final ReplyType? replyType; @@ -85,7 +90,7 @@ class _VideoReplyReplyPanelState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('评论详情${widget.rcount > 0 ? '(${widget.rcount})' : ''}'), + Text('评论详情'), IconButton( tooltip: '关闭', icon: const Icon(Icons.close, size: 20), @@ -110,14 +115,14 @@ class _VideoReplyReplyPanelState extends State { if (widget.firstFloor != null) ...[ // const SliverToBoxAdapter(child: SizedBox(height: 10)), SliverToBoxAdapter( - child: ReplyItem( - replyItem: widget.firstFloor, + child: ReplyItemGrpc( + replyItem: widget.firstFloor!, replyLevel: '2', showReplyRow: false, replyType: widget.replyType, needDivider: false, onReply: () { - _onReply(widget.firstFloor); + // _onReply(widget.firstFloor!); }, ), ), @@ -129,6 +134,47 @@ class _VideoReplyReplyPanelState extends State { ), ), ], + SliverPersistentHeader( + pinned: false, + floating: true, + delegate: MySliverPersistentHeaderDelegate( + child: Container( + height: 40, + padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), + color: Theme.of(context).colorScheme.surface, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx( + () => _videoReplyReplyController.count.value != -1 + ? Text( + '相关回复共${_videoReplyReplyController.count.value}条', + style: const TextStyle(fontSize: 13), + ) + : const SizedBox.shrink(), + ), + SizedBox( + height: 35, + child: TextButton.icon( + onPressed: () => + _videoReplyReplyController.queryBySort(), + icon: const Icon(Icons.sort, size: 16), + label: Obx( + () => Text( + _videoReplyReplyController.mode.value == + Mode.MAIN_LIST_HOT + ? '按热度' + : '按时间', + style: const TextStyle(fontSize: 13), + ), + ), + ), + ) + ], + ), + ), + ), + ), Obx(() => _buildBody( _videoReplyReplyController.loadingState.value)), ], @@ -199,20 +245,16 @@ class _VideoReplyReplyPanelState extends State { if (widget.firstFloor == null && _videoReplyReplyController.root != null) ...[ SliverToBoxAdapter( - child: ListTile( - title: - Text(_videoReplyReplyController.root!.content.message), + child: ReplyItemGrpc( + replyItem: _videoReplyReplyController.root!, + replyLevel: '2', + showReplyRow: false, + replyType: widget.replyType, + needDivider: false, + onReply: () { + // _onReply(_videoReplyReplyController.root); + }, ), - // child: ReplyItem( - // replyItem: _videoReplyReplyController.root, - // replyLevel: '2', - // showReplyRow: false, - // replyType: widget.replyType, - // needDivider: false, - // onReply: () { - // _onReply(_videoReplyReplyController.root); - // }, - // ), ), SliverToBoxAdapter( child: Divider( @@ -243,28 +285,24 @@ class _VideoReplyReplyPanelState extends State { ), ); } else { - return ListTile( - title: - Text(loadingState.response[index].content.message), + return ReplyItemGrpc( + replyItem: loadingState.response[index], + replyLevel: '2', + showReplyRow: false, + replyType: widget.replyType, + onReply: () { + _onReply(loadingState.response[index]); + }, + onDelete: (rpid, frpid) { + List list = (_videoReplyReplyController + .loadingState.value as Success) + .response; + list = + list.where((item) => item.rpid != rpid).toList(); + _videoReplyReplyController.loadingState.value = + LoadingState.success(list); + }, ); - // return ReplyItem( - // replyItem: loadingState.response[index], - // replyLevel: '2', - // showReplyRow: false, - // replyType: widget.replyType, - // onReply: () { - // _onReply(loadingState.response[index]); - // }, - // onDelete: (rpid, frpid) { - // List list = (_videoReplyReplyController - // .loadingState.value as Success) - // .response; - // list = - // list.where((item) => item.rpid != rpid).toList(); - // _videoReplyReplyController.loadingState.value = - // LoadingState.success(list); - // }, - // ); } }, childCount: loadingState.response.length + 1, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 6a992346f..b445ce41b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1290,9 +1290,9 @@ class _VideoDetailPageState extends State void replyReply(replyItem) { scaffoldKey.currentState?.showBottomSheet( (context) => VideoReplyReplyPanel( - rcount: replyItem.rcount, - oid: replyItem.oid, - rpid: replyItem.rpid, + // rcount: replyItem.rcount, + oid: replyItem.oid.toInt(), + rpid: replyItem.id.toInt(), firstFloor: replyItem, replyType: ReplyType.video, source: 'videoDetail',