diff --git a/lib/pages/common/reply_controller.dart b/lib/pages/common/reply_controller.dart index 66a21a2ed..4a97e2b98 100644 --- a/lib/pages/common/reply_controller.dart +++ b/lib/pages/common/reply_controller.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/models/common/reply_type.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; @@ -124,15 +125,15 @@ abstract class ReplyController extends CommonController { dynamic replyItem, int index = 0, }) { - dynamic key = oid ?? replyItem.oid + replyItem.rpid; + dynamic key = oid ?? replyItem.oid + replyItem.id; Navigator.of(context) .push( GetDialogRoute( pageBuilder: (buildContext, animation, secondaryAnimation) { return ReplyPage( - oid: oid ?? replyItem.oid, - root: oid != null ? 0 : replyItem.rpid, - parent: oid != null ? 0 : replyItem.rpid, + oid: oid ?? replyItem.oid.toInt(), + root: oid != null ? 0 : replyItem.id.toInt(), + parent: oid != null ? 0 : replyItem.id.toInt(), replyType: ReplyType.video, replyItem: replyItem, savedReply: savedReplies[key], @@ -159,35 +160,37 @@ abstract class ReplyController extends CommonController { ) .then( (value) { - if (value != null && value['data'] != null) { + // TODO: data cast + if (value != null && value['data'] is ReplyInfo) { savedReplies[key] = null; - List list = loadingState.value is Success - ? (loadingState.value as Success).response - : []; + MainListReply response = + (loadingState.value as Success?)?.response ?? MainListReply(); if (oid != null) { - list.insert(0, value['data']); + response.replies.insert(0, value['data']); } else { - list[index].replies.add(value['data']); + response.replies[index].replies.add(value['data']); } - loadingState.value = LoadingState.success(list); + loadingState.value = LoadingState.success(response); } }, ); } onMDelete(rpid, frpid) { - List list = (loadingState.value as Success).response; - list = frpid == null - ? list.where((item) => item.rpid != rpid).toList() - : list.map((item) { - if (item.rpid == frpid) { - return item - ..replies = - item.replies?.where((reply) => reply.rpid != rpid).toList(); - } else { - return item; - } - }).toList(); - loadingState.value = LoadingState.success(list); + MainListReply response = (loadingState.value as Success).response; + if (frpid == null) { + response.replies.removeWhere((item) { + return item.id.toInt() == rpid; + }); + } else { + response.replies.map((item) { + if (item.id == frpid) { + return item..replies.removeWhere((reply) => reply.id.toInt() == rpid); + } else { + return item; + } + }).toList(); + } + loadingState.value = LoadingState.success(response); } } diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index f765de47a..2c98008b3 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -1,18 +1,11 @@ -import 'dart:convert'; -import 'dart:ffi'; - import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/models/common/reply_sort_type.dart'; import 'package:PiliPalaX/pages/common/reply_controller.dart'; import 'package:PiliPalaX/http/reply.dart'; -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( diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index a1529dd66..36f01f1c5 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -10,7 +10,6 @@ import 'package:PiliPalaX/models/common/reply_type.dart'; import 'package:PiliPalaX/utils/feed_back.dart'; import 'package:PiliPalaX/utils/id_utils.dart'; import 'controller.dart'; -import 'widgets/reply_item.dart'; class VideoReplyPanel extends StatefulWidget { final String? bvid; 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 6c5d15fdb..c1ae93f5d 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -1,10 +1,7 @@ -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'; @@ -261,8 +258,7 @@ class ReplyItemGrpc extends StatelessWidget { String text = replyItem.content.message; var textPainter = TextPainter( text: TextSpan(text: text), - maxLines: 6, - // replyItem.content!.isText! && replyLevel == '1' ? 6 : null, + maxLines: replyLevel == '1' ? 6 : null, textDirection: Directionality.of(context), )..layout(maxWidth: constraints.maxWidth); bool didExceedMaxLines = textPainter.didExceedMaxLines; @@ -322,6 +318,7 @@ class ReplyItemGrpc extends StatelessWidget { padding: const EdgeInsets.only(top: 5, bottom: 12), child: ReplyItemRow( upMid: upMid, + count: replyItem.count.toInt(), replies: replyItem.replies, replyControl: replyItem.replyControl, // f_rpid: replyItem.rpid, @@ -329,7 +326,7 @@ class ReplyItemGrpc extends StatelessWidget { replyReply: replyReply, onDelete: (rpid) { if (onDelete != null) { - onDelete!(rpid, replyItem.id); + onDelete!(rpid, replyItem.id.toInt()); } }, ), @@ -410,6 +407,7 @@ class ReplyItemGrpc extends StatelessWidget { class ReplyItemRow extends StatelessWidget { ReplyItemRow({ super.key, + required this.count, required this.replies, required this.replyControl, // this.f_rpid, @@ -418,6 +416,7 @@ class ReplyItemRow extends StatelessWidget { this.onDelete, this.upMid, }); + final int count; final List replies; ReplyControl replyControl; // int? f_rpid; @@ -428,7 +427,7 @@ class ReplyItemRow extends StatelessWidget { @override Widget build(BuildContext context) { - final bool extraRow = replyControl.subReplyEntryText.isNotEmpty; + final bool extraRow = replies.length < count; return Container( margin: const EdgeInsets.only(left: 42, right: 4, top: 0), child: Material( @@ -614,35 +613,35 @@ InlineSpan buildContent( message = message.substring(0, endOffset); } - return TextSpan(text: message); + // 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+?\}"), ""); - // } + if (content.hasVote()) { + 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('<', '<') @@ -653,22 +652,22 @@ InlineSpan buildContent( // 构建正则表达式 final List specialTokens = [ ...content.emote.keys, - // ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [], + ...content.topic.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(); + List jumpUrlKeysList = content.url.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('|')}'; - // } + if (jumpUrlKeysList.isNotEmpty) { + patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}'; + } final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { @@ -684,304 +683,302 @@ InlineSpan buildContent( } // 分割文本并处理每个部分 - // 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; - // }, - // ); + message.splitMapJoin( + pattern, + onMatch: (Match match) { + String matchStr = match[0]!; + if (content.emote.containsKey(matchStr)) { + // 处理表情 + final int size = content.emote[matchStr]!.size.toInt(); + 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]!.toInt(); + 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.url[matchStr] != null && !matchedStrs.contains(matchStr)) { + appUrlSchema = content.url[matchStr]!.appUrlSchema; + if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) { + addPlainTextSpan(matchStr); + return ""; + } + spanChildren.addAll( + [ + if (content.url[matchStr]?.hasPrefixIcon() == true) ...[ + WidgetSpan( + child: Image.network( + content.url[matchStr]!.prefixIcon, + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.url[matchStr]!.title, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + final String title = content.url[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.topic[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.url.keys.isNotEmpty) { + List unmatchedItems = content.url.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.url[patternStr]?.hasPrefixIcon() == true) ...[ + WidgetSpan( + child: Image.network( + content.url[patternStr]!.prefixIcon, + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.url[patternStr]!.title, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/webviewnew', + parameters: { + 'url': patternStr, + 'type': 'url', + 'pageTitle': content.url[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.pictures.isNotEmpty) { + spanChildren.add(const TextSpan(text: '\n')); + spanChildren.add( + WidgetSpan( + child: LayoutBuilder( + builder: (_, constraints) => image( + constraints.maxWidth, + content.pictures + .map( + (item) => ImageModel( + width: item.imgWidth, + height: item.imgHeight, + url: item.imgSrc, + ), + ) + .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': '笔记预览' - // }, - // ), - // ), - // ); - // } + if (content.hasRichText()) { + spanChildren.add( + TextSpan( + text: ' 笔记', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => Get.toNamed( + '/webviewnew', + parameters: { + 'url': content.richText.note.clickUrl, + 'type': 'note', + 'pageTitle': '笔记预览' + }, + ), + ), + ); + } // spanChildren.add(TextSpan(text: matchMember)); return TextSpan(children: spanChildren); } @@ -1005,7 +1002,7 @@ class MorePanel extends StatelessWidget { }, ); if (result == true && onDelete != null) { - onDelete!(item.id); + onDelete!(item.id.toInt()); } break; case 'copyAll': @@ -1015,18 +1012,18 @@ class MorePanel extends StatelessWidget { 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), - // ), - // ); - // }, - // ); + 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('加入黑名单'); @@ -1075,7 +1072,7 @@ class MorePanel extends StatelessWidget { SmartDialog.showToast('删除成功'); // Get.back(); if (onDelete != null) { - onDelete!(item.id); + onDelete!(item.id.toInt()); } } else { SmartDialog.showToast('删除失败, ${result["msg"]}'); diff --git a/lib/pages/video/detail/reply/widgets/zan_grpc.dart b/lib/pages/video/detail/reply/widgets/zan_grpc.dart index dbcd70703..0153355d5 100644 --- a/lib/pages/video/detail/reply/widgets/zan_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/zan_grpc.dart @@ -4,7 +4,6 @@ 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; diff --git a/lib/pages/video/detail/reply_new/reply_page.dart b/lib/pages/video/detail/reply_new/reply_page.dart index c242d2ab7..efa30b1ae 100644 --- a/lib/pages/video/detail/reply_new/reply_page.dart +++ b/lib/pages/video/detail/reply_new/reply_page.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart' + as reply; import 'package:PiliPalaX/http/msg.dart'; import 'package:chat_bottom_container/chat_bottom_container.dart'; import 'package:flutter/material.dart'; @@ -24,7 +26,7 @@ class ReplyPage extends StatefulWidget { final int? root; final int? parent; final ReplyType? replyType; - final ReplyItemModel? replyItem; + final reply.ReplyInfo? replyItem; final String? savedReply; final Function(String reply)? onSaveReply; @@ -492,7 +494,7 @@ class _ReplyPageState extends State root: widget.root!, parent: widget.parent!, message: widget.replyItem != null && widget.replyItem!.root != 0 - ? ' 回复 @${widget.replyItem!.member!.uname!} : $message' + ? ' 回复 @${widget.replyItem!.member.name} : $message' : message, pictures: pictures, ); diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 7d30238e7..3cc19ded9 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -4,7 +4,6 @@ import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/http/reply.dart'; import 'package:PiliPalaX/models/common/reply_type.dart'; -import 'package:PiliPalaX/models/video/reply/item.dart'; class VideoReplyReplyController extends CommonController { VideoReplyReplyController(this.aid, this.rpid, this.replyType); diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 23935d7cf..d5dfd349d 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -1,5 +1,4 @@ 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; @@ -11,8 +10,6 @@ import 'package:get/get.dart'; import 'package:PiliPalaX/common/skeleton/video_reply.dart'; import 'package:PiliPalaX/common/widgets/http_error.dart'; import 'package:PiliPalaX/models/common/reply_type.dart'; -import 'package:PiliPalaX/models/video/reply/item.dart'; -import 'package:PiliPalaX/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:get/get_navigation/src/dialog/dialog_route.dart'; import '../../../../utils/utils.dart'; @@ -122,7 +119,7 @@ class _VideoReplyReplyPanelState extends State { replyType: widget.replyType, needDivider: false, onReply: () { - // _onReply(widget.firstFloor!); + _onReply(widget.firstFloor!); }, ), ), @@ -186,10 +183,10 @@ class _VideoReplyReplyPanelState extends State { ); } - void _onReply(ReplyItemModel? item) { - dynamic oid = item?.oid; - dynamic root = item?.rpid; - dynamic parent = item?.rpid; + void _onReply(ReplyInfo? item) { + dynamic oid = item?.oid.toInt(); + dynamic root = item?.id.toInt(); + dynamic parent = item?.id.toInt(); dynamic key = oid + root + parent; Navigator.of(context) .push( @@ -224,13 +221,14 @@ class _VideoReplyReplyPanelState extends State { ), ) .then((value) { - // 完成评论,数据添加 - if (value != null && value['data'] != null) { + // 完成评论,数据添加 // TODO: data cast + if (value != null && value['data'] is ReplyInfo) { _savedReplies[key] = null; - List list = _videoReplyReplyController.loadingState.value is Success - ? (_videoReplyReplyController.loadingState.value as Success) - .response - : []; + List list = + _videoReplyReplyController.loadingState.value is Success + ? (_videoReplyReplyController.loadingState.value as Success) + .response + : []; list.add(value['data']); _videoReplyReplyController.loadingState.value = LoadingState.success(list); @@ -252,7 +250,7 @@ class _VideoReplyReplyPanelState extends State { replyType: widget.replyType, needDivider: false, onReply: () { - // _onReply(_videoReplyReplyController.root); + _onReply(_videoReplyReplyController.root); }, ), ), @@ -297,8 +295,7 @@ class _VideoReplyReplyPanelState extends State { List list = (_videoReplyReplyController .loadingState.value as Success) .response; - list = - list.where((item) => item.rpid != rpid).toList(); + list = list.where((item) => item.id != rpid).toList(); _videoReplyReplyController.loadingState.value = LoadingState.success(list); },