mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-24 19:16:44 +08:00
opt show more text
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -495,10 +495,11 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
|
||||
|
||||
void onChanged(String value) {
|
||||
bool isEmpty = value.trim().isEmpty;
|
||||
if (!isEmpty && !enablePublish.value) {
|
||||
enablePublish.value = true;
|
||||
} else if (isEmpty && enablePublish.value) {
|
||||
if (isEmpty) {
|
||||
enablePublish.value = false;
|
||||
mentions?.clear();
|
||||
} else {
|
||||
enablePublish.value = true;
|
||||
}
|
||||
widget.onSave?.call((text: value, mentions: mentions));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
@@ -243,10 +244,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: Get.back,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(18),
|
||||
topRight: Radius.circular(18),
|
||||
),
|
||||
borderRadius: StyleString.bottomSheetRadius,
|
||||
child: Container(
|
||||
height: 35,
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 内容
|
||||
import 'package:PiliPlus/common/widgets/custom_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_view.dart';
|
||||
import 'package:PiliPlus/common/widgets/text/text.dart' as custom_text;
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -68,13 +69,12 @@ Widget content(
|
||||
? const TextStyle(fontSize: 15)
|
||||
: const TextStyle(fontSize: 16),
|
||||
)
|
||||
: Text.rich(
|
||||
: custom_text.Text.rich(
|
||||
style: floor == 1
|
||||
? const TextStyle(fontSize: 15)
|
||||
: const TextStyle(fontSize: 14),
|
||||
richNodes,
|
||||
maxLines: isSave ? null : 6,
|
||||
overflow: isSave ? null : TextOverflow.ellipsis,
|
||||
),
|
||||
if (item.modules.moduleDynamic?.major?.opus?.pics?.isNotEmpty == true)
|
||||
LayoutBuilder(
|
||||
|
||||
@@ -205,11 +205,12 @@ List<SettingsModel> get extraSettings => [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
int length = int.tryParse(replyLengthLimit) ?? 6;
|
||||
ReplyItemGrpc.replyLengthLimit =
|
||||
int.tryParse(replyLengthLimit) ?? 6;
|
||||
length == 0 ? null : length;
|
||||
await GStorage.setting.put(
|
||||
SettingBoxKey.replyLengthLimit,
|
||||
ReplyItemGrpc.replyLengthLimit,
|
||||
length,
|
||||
);
|
||||
setState();
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_view.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
|
||||
import 'package:PiliPlus/common/widgets/text/text.dart' as custom_text;
|
||||
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
show ReplyInfo, ReplyControl, Content;
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
@@ -13,7 +14,6 @@ import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
|
||||
import 'package:PiliPlus/pages/save_panel/view.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/reply/widgets/zan_grpc.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
@@ -68,10 +68,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
final ValueChanged<ReplyInfo>? onCheckReply;
|
||||
final ValueChanged<ReplyInfo>? onToggleTop;
|
||||
|
||||
static final _voteRegExp = RegExp(r"\{vote:\d+?\}");
|
||||
static final _voteRegExp = RegExp(r"^\{vote:\d+?\}$");
|
||||
static final _timeRegExp = RegExp(r'^\b(?:\d+[::])?\d+[::]\d+\b$');
|
||||
static bool enableWordRe = Pref.enableWordRe;
|
||||
static int replyLengthLimit = Pref.replyLengthLimit;
|
||||
static int? replyLengthLimit = Pref.replyLengthLimit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -181,6 +181,10 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
);
|
||||
|
||||
Widget content(BuildContext context, ThemeData theme) {
|
||||
final padding = EdgeInsets.only(
|
||||
left: replyLevel == 0 ? 6 : 45,
|
||||
right: 6,
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -218,7 +222,6 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${replyItem.member.isSeniorMember == 1 ? '6_s' : replyItem.member.level}.png',
|
||||
height: 11,
|
||||
semanticLabel: "等级:${replyItem.member.level}",
|
||||
),
|
||||
if (replyItem.mid == upMid)
|
||||
const PBadge(
|
||||
@@ -255,69 +258,69 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
// title
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 10,
|
||||
left: replyLevel == 0 ? 6 : 45,
|
||||
right: 6,
|
||||
bottom: 4,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
String text = replyItem.content.message;
|
||||
TextStyle style = TextStyle(
|
||||
height: 1.75,
|
||||
fontSize: theme.textTheme.bodyMedium!.fontSize,
|
||||
);
|
||||
TextPainter? textPainter;
|
||||
bool? didExceedMaxLines;
|
||||
if (replyLevel == 1 && replyLengthLimit != 0) {
|
||||
textPainter = TextPainter(
|
||||
text: TextSpan(text: text, style: style),
|
||||
maxLines: replyLengthLimit,
|
||||
textDirection: Directionality.of(context),
|
||||
)..layout(maxWidth: constraints.maxWidth);
|
||||
didExceedMaxLines = textPainter.didExceedMaxLines;
|
||||
}
|
||||
return Semantics(
|
||||
label: text,
|
||||
child: Text.rich(
|
||||
style: style,
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replyItem.replyControl.isUpTop) ...[
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.top,
|
||||
child: PBadge(
|
||||
text: 'TOP',
|
||||
size: PBadgeSize.small,
|
||||
isStack: false,
|
||||
type: PBadgeType.line_primary,
|
||||
fontSize: 9,
|
||||
textScaleFactor: 1,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
],
|
||||
buildContent(
|
||||
context,
|
||||
theme,
|
||||
replyItem,
|
||||
null,
|
||||
textPainter,
|
||||
didExceedMaxLines,
|
||||
),
|
||||
],
|
||||
padding: padding,
|
||||
child: custom_text.Text.rich(
|
||||
style: TextStyle(
|
||||
height: 1.75,
|
||||
fontSize: theme.textTheme.bodyMedium!.fontSize,
|
||||
),
|
||||
maxLines: replyLevel == 1 ? replyLengthLimit : null,
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replyItem.replyControl.isUpTop) ...[
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.top,
|
||||
child: PBadge(
|
||||
text: 'TOP',
|
||||
size: PBadgeSize.small,
|
||||
isStack: false,
|
||||
type: PBadgeType.line_primary,
|
||||
fontSize: 9,
|
||||
textScaleFactor: 1,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
],
|
||||
buildContent(
|
||||
context,
|
||||
theme,
|
||||
replyItem,
|
||||
null,
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (replyItem.content.pictures.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Padding(
|
||||
padding: padding,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => imageView(
|
||||
constraints.maxWidth,
|
||||
replyItem.content.pictures
|
||||
.map(
|
||||
(item) => ImageModel(
|
||||
width: item.imgWidth,
|
||||
height: item.imgHeight,
|
||||
url: item.imgSrc,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onViewImage: onViewImage,
|
||||
onDismissed: onDismissed,
|
||||
callback: callback,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// 操作区域
|
||||
if (replyLevel != 0)
|
||||
if (replyLevel != 0) ...[
|
||||
const SizedBox(height: 4),
|
||||
buttonAction(context, theme, replyItem.replyControl),
|
||||
],
|
||||
// 一楼的评论
|
||||
if (replyLevel == 1 && replyItem.count > Int64.ZERO) ...[
|
||||
Padding(
|
||||
@@ -472,64 +475,57 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: padding,
|
||||
child: Semantics(
|
||||
label:
|
||||
'${childReply.member.name} ${childReply.content.message}',
|
||||
excludeSemantics: true,
|
||||
child: Text.rich(
|
||||
style: TextStyle(
|
||||
fontSize: theme.textTheme.bodyMedium!.fontSize,
|
||||
color: theme.colorScheme.onSurface
|
||||
.withValues(alpha: 0.85),
|
||||
height: 1.6),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: childReply.member.name,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
child: Text.rich(
|
||||
style: TextStyle(
|
||||
fontSize: theme.textTheme.bodyMedium!.fontSize,
|
||||
color: theme.colorScheme.onSurface
|
||||
.withValues(alpha: 0.85),
|
||||
height: 1.6),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: childReply.member.name,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
feedBack();
|
||||
Get.toNamed(
|
||||
'/member?mid=${childReply.member.mid}',
|
||||
);
|
||||
},
|
||||
),
|
||||
if (childReply.mid == upMid) ...[
|
||||
const TextSpan(text: ' '),
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: PBadge(
|
||||
text: 'UP',
|
||||
size: PBadgeSize.small,
|
||||
isStack: false,
|
||||
fontSize: 9,
|
||||
textScaleFactor: 1,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
feedBack();
|
||||
Get.toNamed(
|
||||
'/member?mid=${childReply.member.mid}',
|
||||
);
|
||||
},
|
||||
),
|
||||
if (childReply.mid == upMid) ...[
|
||||
const TextSpan(text: ' '),
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: PBadge(
|
||||
text: 'UP',
|
||||
size: PBadgeSize.small,
|
||||
isStack: false,
|
||||
fontSize: 9,
|
||||
textScaleFactor: 1,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
],
|
||||
TextSpan(
|
||||
text: childReply.root == childReply.parent
|
||||
? ': '
|
||||
: childReply.mid == upMid
|
||||
? ''
|
||||
: ' ',
|
||||
),
|
||||
buildContent(
|
||||
context,
|
||||
theme,
|
||||
childReply,
|
||||
replyItem,
|
||||
null,
|
||||
null,
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
],
|
||||
),
|
||||
TextSpan(
|
||||
text: childReply.root == childReply.parent
|
||||
? ': '
|
||||
: childReply.mid == upMid
|
||||
? ''
|
||||
: ' ',
|
||||
),
|
||||
buildContent(
|
||||
context,
|
||||
theme,
|
||||
childReply,
|
||||
replyItem,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -580,91 +576,68 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
ThemeData theme,
|
||||
ReplyInfo replyItem,
|
||||
ReplyInfo? fReplyItem,
|
||||
TextPainter? textPainter,
|
||||
bool? 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<InlineSpan> spanChildren = <InlineSpan>[];
|
||||
|
||||
if (didExceedMaxLines == true) {
|
||||
final textSize = textPainter!.size;
|
||||
final double maxHeight = textPainter.preferredLineHeight * 6;
|
||||
var position = textPainter.getPositionForOffset(
|
||||
Offset(textSize.width, maxHeight),
|
||||
if (content.hasRichText()) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: '[笔记] ',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap =
|
||||
() => PageUtils.handleWebview(content.richText.note.clickUrl),
|
||||
),
|
||||
);
|
||||
message = message.substring(0, position.offset);
|
||||
}
|
||||
|
||||
// 投票
|
||||
if (content.hasVote()) {
|
||||
message = message.replaceAllMapped(_voteRegExp, (Match match) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: '投票: ${content.vote.title}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => showVoteDialog(context, content.vote.id.toInt()),
|
||||
),
|
||||
);
|
||||
return '';
|
||||
});
|
||||
}
|
||||
// 构建正则表达式
|
||||
final List<String> specialTokens = [
|
||||
...content.emotes.keys,
|
||||
...content.topics.keys.map((e) => '#$e#'),
|
||||
...content.atNameToMid.keys.map((e) => '@$e'),
|
||||
...content.urls.keys,
|
||||
];
|
||||
List<String> jumpUrlKeysList = content.urls.keys.map<String>((String e) {
|
||||
return e;
|
||||
}).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+[::])?\d+[::]\d+\b)';
|
||||
if (jumpUrlKeysList.isNotEmpty) {
|
||||
patternStr += '|${jumpUrlKeysList.map(RegExp.escape).join('|')}';
|
||||
}
|
||||
patternStr += '|${Constants.urlPattern}';
|
||||
String patternStr = [
|
||||
...specialTokens.map(RegExp.escape),
|
||||
r'(\b(?:\d+[::])?\d+[::]\d+\b)',
|
||||
r'(\{vote:\d+?\})',
|
||||
Constants.urlPattern,
|
||||
].join('|');
|
||||
final RegExp pattern = RegExp(patternStr);
|
||||
List<String> matchedStrs = [];
|
||||
|
||||
late List<String> matchedStrs = [];
|
||||
|
||||
void addPlainTextSpan(str) {
|
||||
spanChildren.add(TextSpan(
|
||||
text: str,
|
||||
));
|
||||
spanChildren.add(TextSpan(text: str));
|
||||
}
|
||||
|
||||
// 分割文本并处理每个部分
|
||||
message.splitMapJoin(
|
||||
content.message.splitMapJoin(
|
||||
pattern,
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
if (content.emotes.containsKey(matchStr)) {
|
||||
// 处理表情
|
||||
final int size = content.emotes[matchStr]!.size.toInt();
|
||||
spanChildren.add(WidgetSpan(
|
||||
child: ExcludeSemantics(
|
||||
child: NetworkImgLayer(
|
||||
src: content.emotes[matchStr]?.hasGifUrl() == true
|
||||
? content.emotes[matchStr]?.gifUrl
|
||||
: content.emotes[matchStr]?.url,
|
||||
type: ImageType.emote,
|
||||
width: size * 20,
|
||||
height: size * 20,
|
||||
semanticsLabel: matchStr,
|
||||
)),
|
||||
));
|
||||
spanChildren.add(
|
||||
WidgetSpan(
|
||||
child: NetworkImgLayer(
|
||||
src: content.emotes[matchStr]?.hasGifUrl() == true
|
||||
? content.emotes[matchStr]?.gifUrl
|
||||
: content.emotes[matchStr]?.url,
|
||||
type: ImageType.emote,
|
||||
width: size * 20,
|
||||
height: size * 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (matchStr.startsWith("@") &&
|
||||
content.atNameToMid.containsKey(matchStr.substring(1))) {
|
||||
// 处理@用户
|
||||
@@ -680,6 +653,18 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
..onTap = () => Get.toNamed('/member?mid=$userId'),
|
||||
),
|
||||
);
|
||||
} else if (_voteRegExp.hasMatch(matchStr)) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: '投票: ${content.vote.title}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap =
|
||||
() => showVoteDialog(context, content.vote.id.toInt()),
|
||||
),
|
||||
);
|
||||
} else if (_timeRegExp.hasMatch(matchStr)) {
|
||||
matchStr = matchStr.replaceAll(':', ':');
|
||||
bool isValid = false;
|
||||
@@ -702,6 +687,7 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
} catch (e) {
|
||||
if (kDebugMode) debugPrint('failed to validate: $e');
|
||||
}
|
||||
bool isVideoPage = Get.currentRoute.startsWith('/video');
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: isValid ? ' $matchStr ' : matchStr,
|
||||
@@ -905,58 +891,6 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
if (didExceedMaxLines == true) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: '\n查看更多',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 图片渲染
|
||||
if (content.pictures.isNotEmpty) {
|
||||
spanChildren
|
||||
..add(const TextSpan(text: '\n'))
|
||||
..add(
|
||||
WidgetSpan(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => imageView(
|
||||
constraints.maxWidth,
|
||||
content.pictures
|
||||
.map(
|
||||
(item) => ImageModel(
|
||||
width: item.imgWidth,
|
||||
height: item.imgHeight,
|
||||
url: item.imgSrc,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onViewImage: onViewImage,
|
||||
onDismissed: onDismissed,
|
||||
callback: callback,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 笔记链接
|
||||
if (content.hasRichText()) {
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: ' 笔记',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap =
|
||||
() => PageUtils.handleWebview(content.richText.note.clickUrl),
|
||||
),
|
||||
);
|
||||
}
|
||||
return TextSpan(children: spanChildren);
|
||||
}
|
||||
|
||||
@@ -966,151 +900,22 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
required VoidCallback onDelete,
|
||||
required bool isSubReply,
|
||||
}) {
|
||||
late String message = item.content.message;
|
||||
final ownerMid = Int64(Accounts.main.mid);
|
||||
Future<void> menuActionHandler(String type) async {
|
||||
late String message = item.content.message;
|
||||
switch (type) {
|
||||
case 'report':
|
||||
Get.back();
|
||||
autoWrapReportDialog(
|
||||
context,
|
||||
ReportOptions.commentReport,
|
||||
(reasonType, reasonDesc, banUid) async {
|
||||
final res = await Request().post(
|
||||
'/x/v2/reply/report',
|
||||
data: {
|
||||
'add_blacklist': banUid,
|
||||
'csrf': Accounts.main.csrf,
|
||||
'gaia_source': 'main_h5',
|
||||
'oid': item.oid,
|
||||
'platform': 'android',
|
||||
'reason': reasonType,
|
||||
'rpid': item.id,
|
||||
'scene': 'main',
|
||||
'type': 1,
|
||||
if (reasonType == 0) 'content': reasonDesc!
|
||||
},
|
||||
options:
|
||||
Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
onDelete();
|
||||
}
|
||||
return res.data as Map;
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'copyAll':
|
||||
Get.back();
|
||||
Utils.copyText(message);
|
||||
break;
|
||||
case 'copyFreedom':
|
||||
Get.back();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
child: SelectableText(message),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'delete':
|
||||
Get.back();
|
||||
bool? isDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final theme = Theme.of(context);
|
||||
return AlertDialog(
|
||||
title: const Text('删除评论'),
|
||||
content: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: '确定删除这条评论吗?\n\n'),
|
||||
if (ownerMid != item.member.mid.toInt()) ...[
|
||||
TextSpan(
|
||||
text: '@${item.member.name}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ':\n'),
|
||||
],
|
||||
TextSpan(text: message),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: false),
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
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('删除成功');
|
||||
onDelete();
|
||||
} else {
|
||||
SmartDialog.showToast('删除失败, ${result["msg"]}');
|
||||
}
|
||||
break;
|
||||
case 'checkReply':
|
||||
Get.back();
|
||||
onCheckReply?.call(item);
|
||||
break;
|
||||
case 'top':
|
||||
Get.back();
|
||||
onToggleTop?.call(item);
|
||||
break;
|
||||
case 'saveReply':
|
||||
Get.back();
|
||||
SavePanel.toSavePanel(upMid: upMid, item: item);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final errorColor = theme.colorScheme.error;
|
||||
final style = theme.textTheme.titleSmall;
|
||||
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 20),
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 20,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: Get.back,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(18),
|
||||
topRight: Radius.circular(18),
|
||||
),
|
||||
borderRadius: StyleString.bottomSheetRadius,
|
||||
child: Container(
|
||||
height: 35,
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
@@ -1119,29 +924,120 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
width: 32,
|
||||
height: 3,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.outline,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||
color: theme.colorScheme.outline,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(3)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (ownerMid == upMid || ownerMid == item.member.mid)
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('delete'),
|
||||
onTap: () async {
|
||||
bool? isDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final theme = Theme.of(context);
|
||||
return AlertDialog(
|
||||
title: const Text('删除评论'),
|
||||
content: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: '确定删除这条评论吗?\n\n'),
|
||||
if (ownerMid != item.member.mid.toInt()) ...[
|
||||
TextSpan(
|
||||
text: '@${item.member.name}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ':\n'),
|
||||
],
|
||||
TextSpan(text: message),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: false),
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
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('删除成功');
|
||||
onDelete();
|
||||
} else {
|
||||
SmartDialog.showToast('删除失败, ${result["msg"]}');
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: Icon(Icons.delete_outlined, color: errorColor, size: 19),
|
||||
title: Text('删除', style: style!.copyWith(color: errorColor)),
|
||||
),
|
||||
if (ownerMid != Int64.ZERO)
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('report'),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
autoWrapReportDialog(
|
||||
context,
|
||||
ReportOptions.commentReport,
|
||||
(reasonType, reasonDesc, banUid) async {
|
||||
final res = await Request().post(
|
||||
'/x/v2/reply/report',
|
||||
data: {
|
||||
'add_blacklist': banUid,
|
||||
'csrf': Accounts.main.csrf,
|
||||
'gaia_source': 'main_h5',
|
||||
'oid': item.oid,
|
||||
'platform': 'android',
|
||||
'reason': reasonType,
|
||||
'rpid': item.id,
|
||||
'scene': 'main',
|
||||
'type': 1,
|
||||
if (reasonType == 0) 'content': reasonDesc!
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
onDelete();
|
||||
}
|
||||
return res.data as Map;
|
||||
},
|
||||
);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: Icon(Icons.error_outline, color: errorColor, size: 19),
|
||||
title: Text('举报', style: style!.copyWith(color: errorColor)),
|
||||
),
|
||||
if (replyLevel == 1 && !isSubReply && ownerMid == upMid)
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('top'),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onToggleTop?.call(item);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.vertical_align_top, size: 19),
|
||||
title: Text(
|
||||
@@ -1150,26 +1046,104 @@ class ReplyItemGrpc extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('copyAll'),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(message);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.copy_all_outlined, size: 19),
|
||||
title: Text('复制全部', style: style),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('copyFreedom'),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 16),
|
||||
child: SelectableText(message),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.copy_outlined, size: 19),
|
||||
title: Text('自由复制', style: style),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('saveReply'),
|
||||
onTap: () async {
|
||||
Get.back();
|
||||
bool? isDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final theme = Theme.of(context);
|
||||
return AlertDialog(
|
||||
title: const Text('删除评论'),
|
||||
content: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(text: '确定删除这条评论吗?\n\n'),
|
||||
if (ownerMid != item.member.mid.toInt()) ...[
|
||||
TextSpan(
|
||||
text: '@${item.member.name}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ':\n'),
|
||||
],
|
||||
TextSpan(text: message),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: false),
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: true),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
if (isDelete == true) {
|
||||
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('删除成功');
|
||||
onDelete();
|
||||
} else {
|
||||
SmartDialog.showToast('删除失败, ${result["msg"]}');
|
||||
}
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Icon(Icons.save_alt, size: 19),
|
||||
title: Text('保存评论', style: style),
|
||||
),
|
||||
if (item.mid == ownerMid)
|
||||
ListTile(
|
||||
onTap: () => menuActionHandler('checkReply'),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
onCheckReply?.call(item);
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
leading: const Stack(
|
||||
clipBehavior: Clip.none,
|
||||
|
||||
Reference in New Issue
Block a user