mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: grpc reply item
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ReplyInfo> 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<String> specialTokens = [
|
||||
...content.emote.keys,
|
||||
// ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [],
|
||||
...content.topic.keys.map((e) => '#$e#'),
|
||||
...content.atNameToMid.keys.map((e) => '@$e'),
|
||||
];
|
||||
// List<String> jumpUrlKeysList = content.jumpUrl.keys.map<String>((String e) {
|
||||
// return e.replaceAllMapped(
|
||||
// RegExp(r'[?+*]'), (match) => '\\${match.group(0)}');
|
||||
// }).toList();
|
||||
List<String> jumpUrlKeysList = content.url.keys.map<String>((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<String> 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<VideoDetailController>(
|
||||
// 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<VideoDetailController>(
|
||||
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<String> 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<String> 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"]}');
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<ReplyPage>
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<VideoReplyReplyPanel> {
|
||||
replyType: widget.replyType,
|
||||
needDivider: false,
|
||||
onReply: () {
|
||||
// _onReply(widget.firstFloor!);
|
||||
_onReply(widget.firstFloor!);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -186,10 +183,10 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
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<VideoReplyReplyPanel> {
|
||||
),
|
||||
)
|
||||
.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<ReplyInfo> list =
|
||||
_videoReplyReplyController.loadingState.value is Success
|
||||
? (_videoReplyReplyController.loadingState.value as Success)
|
||||
.response
|
||||
: <ReplyInfo>[];
|
||||
list.add(value['data']);
|
||||
_videoReplyReplyController.loadingState.value =
|
||||
LoadingState.success(list);
|
||||
@@ -252,7 +250,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
replyType: widget.replyType,
|
||||
needDivider: false,
|
||||
onReply: () {
|
||||
// _onReply(_videoReplyReplyController.root);
|
||||
_onReply(_videoReplyReplyController.root);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -297,8 +295,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
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);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user