feat: reply vote

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-07-04 18:04:46 +08:00
parent fd017e0581
commit 0036b58aab
10 changed files with 319 additions and 192 deletions

View File

@@ -2,10 +2,12 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart';
import 'package:PiliPlus/grpc/reply.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:PiliPlus/pages/video/reply/vote/reply_vote_mixin.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:get/get.dart';
abstract class CommonDynController extends ReplyController<MainListReply> {
abstract class CommonDynController extends ReplyController<MainListReply>
with ReplyVoteMixin {
int get oid;
int get replyType;

View File

@@ -10,6 +10,7 @@ import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/enum_with_label.dart';
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
import 'package:PiliPlus/pages/common/fab_mixin.dart';
import 'package:PiliPlus/pages/video/reply/vote/reply_vote_item.dart';
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
import 'package:PiliPlus/pages/video/reply_reply/view.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart';
@@ -120,62 +121,78 @@ mixin CommonDynPageMixin<T extends StatefulWidget>
}
Widget replyList(LoadingState<List<ReplyInfo>?> loadingState) {
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const VideoReplySkeleton(),
),
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(
itemCount: response.length + 1,
itemBuilder: (context, index) {
if (index == response.length) {
controller.onLoadMore();
return Container(
alignment: Alignment.center,
margin: EdgeInsets.only(bottom: padding.bottom),
height: 125,
child: Text(
controller.isEnd ? '没有更多了' : '加载中...',
style: TextStyle(
fontSize: 12,
color: theme.colorScheme.outline,
),
),
);
} else {
return ReplyItemGrpc(
replyItem: response[index],
replyLevel: 1,
replyReply: (replyItem, id) =>
replyReply(context, replyItem, id),
onReply: controller.onReply,
onDelete: (item, subIndex) =>
controller.onRemove(index, item, subIndex),
upMid: controller.upMid,
onViewImage: hideFab,
onCheckReply: (item) =>
controller.onCheckReply(item, isManual: true),
onToggleTop: (item) => controller.onToggleTop(
item,
index,
controller.oid,
controller.replyType,
),
);
}
},
)
: HttpError(
errMsg: '还没有评论',
onReload: controller.onReload,
),
Error(:final errMsg) => HttpError(
errMsg: errMsg,
onReload: controller.onReload,
),
};
switch (loadingState) {
case Loading():
return SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const VideoReplySkeleton(),
);
case Success(:final response):
if (response != null && response.isNotEmpty) {
var count = response.length + 1;
final voteCard = controller.voteCard;
final hasVote = voteCard != null;
if (hasVote) {
count++;
}
return SliverList.builder(
itemCount: count,
itemBuilder: (context, index) {
if (hasVote) {
if (index == 0) {
return buildVoteCard(context, theme.colorScheme, voteCard);
} else {
index--;
}
}
if (index == response.length) {
controller.onLoadMore();
return Container(
alignment: Alignment.center,
margin: EdgeInsets.only(bottom: padding.bottom),
height: 125,
child: Text(
controller.isEnd ? '没有更多了' : '加载中...',
style: TextStyle(
fontSize: 12,
color: theme.colorScheme.outline,
),
),
);
} else {
return ReplyItemGrpc(
replyItem: response[index],
replyLevel: 1,
replyReply: (replyItem, id) =>
replyReply(context, replyItem, id),
onReply: controller.onReply,
onDelete: (item, subIndex) =>
controller.onRemove(index, item, subIndex),
upMid: controller.upMid,
onViewImage: hideFab,
onCheckReply: (item) =>
controller.onCheckReply(item, isManual: true),
onToggleTop: (item) => controller.onToggleTop(
item,
index,
controller.oid,
controller.replyType,
),
);
}
},
);
}
return HttpError(
errMsg: '还没有评论',
onReload: controller.onReload,
);
case Error(:final errMsg):
return HttpError(
errMsg: errMsg,
onReload: controller.onReload,
);
}
}
void replyReply(BuildContext context, ReplyInfo replyItem, int? id) {

View File

@@ -57,16 +57,15 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
}
@override
bool customHandleResponse(bool isRefresh, Success response) {
MainListReply data = response.response;
bool customHandleResponse(bool isRefresh, Success<R> response) {
final data = response.response as MainListReply;
cursorNext = data.cursor.next;
paginationReply = data.paginationReply;
count.value = data.subjectControl.count.toInt();
if (isRefresh) {
subjectControl = data.subjectControl;
upMid ??= data.subjectControl.upMid;
hasUpTop = data.hasUpTop();
if (data.hasUpTop()) {
if (hasUpTop = data.hasUpTop()) {
data.replies.insert(0, data.upTop);
}
if (subjectControl?.title == ReplySortType.select.title) {

View File

@@ -566,9 +566,7 @@ class _DynamicDetailPageState
color: theme.colorScheme.surface,
border: Border(
top: BorderSide(
color: theme.colorScheme.outline.withValues(
alpha: 0.08,
),
color: theme.colorScheme.outline.withValues(alpha: 0.08),
),
),
),

View File

@@ -5,10 +5,12 @@ import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/video/video_type.dart';
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:PiliPlus/pages/video/controller.dart';
import 'package:PiliPlus/pages/video/reply/vote/reply_vote_mixin.dart';
import 'package:PiliPlus/utils/id_utils.dart';
import 'package:get/get.dart';
class VideoReplyController extends ReplyController<MainListReply> {
class VideoReplyController extends ReplyController<MainListReply>
with ReplyVoteMixin {
VideoReplyController({
required this.aid,
required this.videoType,

View File

@@ -8,6 +8,7 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/fab_mixin.dart';
import 'package:PiliPlus/pages/video/reply/controller.dart';
import 'package:PiliPlus/pages/video/reply/vote/reply_vote_item.dart';
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
import 'package:PiliPlus/pages/video/reply_reply/view.dart';
import 'package:PiliPlus/utils/feed_back.dart';
@@ -125,9 +126,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
}),
),
),
Obx(
() => _buildBody(_videoReplyController.loadingState.value),
),
Obx(() => _buildBody(_videoReplyController.loadingState.value)),
],
),
Positioned(
@@ -170,62 +169,75 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
}
Widget _buildBody(LoadingState<List<ReplyInfo>?> loadingState) {
return switch (loadingState) {
Loading() => SliverList.builder(
itemBuilder: (context, index) => const VideoReplySkeleton(),
itemCount: 5,
),
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(
itemBuilder: (context, index) {
if (index == response.length) {
_videoReplyController.onLoadMore();
return Container(
height: 125,
alignment: .center,
margin: .only(bottom: bottom),
child: Text(
_videoReplyController.isEnd ? '没有更多了' : '加载中...',
textAlign: .center,
style: TextStyle(
fontSize: 12,
color: colorScheme.outline,
),
),
);
} else {
return ReplyItemGrpc(
replyItem: response[index],
replyLevel: widget.replyLevel,
replyReply: replyReply,
onReply: _videoReplyController.onReply,
onDelete: (item, subIndex) =>
_videoReplyController.onRemove(index, item, subIndex),
upMid: _videoReplyController.upMid,
getTag: () => heroTag,
onCheckReply: (item) => _videoReplyController
.onCheckReply(item, isManual: true),
onToggleTop: (item) => _videoReplyController.onToggleTop(
item,
index,
_videoReplyController.aid,
_videoReplyController.videoType.replyType,
),
);
}
},
itemCount: response.length + 1,
)
: HttpError(
errMsg: '还没有评论',
onReload: _videoReplyController.onReload,
),
Error(:final errMsg) => HttpError(
errMsg: errMsg,
onReload: _videoReplyController.onReload,
),
};
switch (loadingState) {
case Loading():
return SliverList.builder(
itemBuilder: (context, index) => const VideoReplySkeleton(),
itemCount: 5,
);
case Success(:final response):
if (response != null && response.isNotEmpty) {
var count = response.length + 1;
final voteCard = _videoReplyController.voteCard;
final hasVote = voteCard != null;
if (hasVote) {
count++;
}
return SliverList.builder(
itemBuilder: (context, index) {
if (hasVote) {
if (index == 0) {
return buildVoteCard(context, colorScheme, voteCard);
} else {
index--;
}
}
if (index == response.length) {
_videoReplyController.onLoadMore();
return Container(
height: 125,
alignment: .center,
margin: .only(bottom: bottom),
child: Text(
_videoReplyController.isEnd ? '没有更多了' : '加载中...',
textAlign: .center,
style: TextStyle(fontSize: 12, color: colorScheme.outline),
),
);
} else {
return ReplyItemGrpc(
replyItem: response[index],
replyLevel: widget.replyLevel,
replyReply: replyReply,
onReply: _videoReplyController.onReply,
onDelete: (item, subIndex) =>
_videoReplyController.onRemove(index, item, subIndex),
upMid: _videoReplyController.upMid,
getTag: () => heroTag,
onCheckReply: (item) =>
_videoReplyController.onCheckReply(item, isManual: true),
onToggleTop: (item) => _videoReplyController.onToggleTop(
item,
index,
_videoReplyController.aid,
_videoReplyController.videoType.replyType,
),
);
}
},
itemCount: count,
);
}
return HttpError(
errMsg: '还没有评论',
onReload: _videoReplyController.onReload,
);
case Error(:final errMsg):
return HttpError(
errMsg: errMsg,
onReload: _videoReplyController.onReload,
);
}
}
// 展示二级回复

View File

@@ -0,0 +1,49 @@
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show VoteCard;
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:flutter/material.dart';
Widget buildVoteCard(
BuildContext context,
ColorScheme colorScheme,
VoteCard voteCard,
) {
return InkWell(
onTap: () => showVoteDialog(context, voteCard.voteId.toInt()),
child: Padding(
padding: const .symmetric(horizontal: 12, vertical: 6),
child: Row(
spacing: 10,
children: [
Container(
decoration: BoxDecoration(
color: colorScheme.onInverseSurface,
borderRadius: const .all(.circular(8)),
),
width: 60,
height: 42,
child: Icon(
Icons.bar_chart_rounded,
color: colorScheme.onSurfaceVariant,
),
),
Expanded(
child: Column(
crossAxisAlignment: .start,
children: [
Text(voteCard.title, maxLines: 1, overflow: .ellipsis),
Text(
'${NumUtils.numFormat(voteCard.count.toInt())}人参与',
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(fontSize: 13, color: colorScheme.outline),
),
],
),
),
],
),
),
);
}

View File

@@ -0,0 +1,19 @@
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show MainListReply, VoteCard, ReplyInfo;
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
mixin ReplyVoteMixin on CommonListController<MainListReply, ReplyInfo> {
VoteCard? voteCard;
@override
bool customHandleResponse(bool isRefresh, Success<MainListReply> response) {
if (isRefresh) {
final res = response.response;
if (res.hasVoteCard()) {
voteCard = res.voteCard;
}
}
return super.customHandleResponse(isRefresh, response);
}
}

View File

@@ -14,7 +14,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/image_grid/image_grid_view.dart';
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo, ReplyControl, Content, Url;
show ReplyInfo, ReplyControl, Content, Url, ReplyControl_VoteOption;
import 'package:PiliPlus/grpc/reply.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/reply.dart';
@@ -92,7 +92,7 @@ class ReplyItemGrpc extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final colorScheme = ColorScheme.of(context);
void showMore() => showModalBottomSheet(
context: context,
@@ -113,7 +113,7 @@ class ReplyItemGrpc extends StatelessWidget {
Widget child = Padding(
padding: const .fromLTRB(12, 14, 8, 5),
child: _buildContent(context, theme),
child: _buildContent(context, colorScheme),
);
if (needDivider) {
child = Column(
@@ -124,7 +124,7 @@ class ReplyItemGrpc extends StatelessWidget {
indent: 55,
endIndent: 15,
height: 0.3,
color: theme.colorScheme.outline.withValues(alpha: 0.08),
color: colorScheme.outline.withValues(alpha: 0.08),
),
],
);
@@ -140,7 +140,7 @@ class ReplyItemGrpc extends StatelessWidget {
);
}
Widget _buildHeader(BuildContext context, ThemeData theme) {
Widget _buildHeader(BuildContext context, ColorScheme colorScheme) {
final member = replyItem.member;
Widget header = GestureDetector(
onTap: () {
@@ -178,8 +178,8 @@ class ReplyItemGrpc extends StatelessWidget {
overflow: .ellipsis,
style: TextStyle(
color: (member.vipStatus > 0 && member.vipType == 2)
? theme.colorScheme.vipColor
: theme.colorScheme.outline,
? colorScheme.vipColor
: colorScheme.outline,
fontSize: 13,
),
),
@@ -227,16 +227,16 @@ class ReplyItemGrpc extends StatelessWidget {
replyItem.ctime.toInt(),
),
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
fontSize: 11,
color: colorScheme.outline,
),
),
if (replyItem.replyControl.hasLocation())
Text(
'${replyItem.replyControl.location}',
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
fontSize: 11,
color: colorScheme.outline,
),
),
],
@@ -293,23 +293,56 @@ class ReplyItemGrpc extends StatelessWidget {
return header;
}
Widget _buildContent(BuildContext context, ThemeData theme) {
Widget _buildVoteOption(
ColorScheme colorScheme,
ReplyControl_VoteOption voteOption,
) {
return Text.rich(
TextSpan(
children: [
switch (voteOption.labelKind) {
.RED => TextSpan(
text: '红队 ',
style: TextStyle(color: colorScheme.vipColor),
),
.BLUE => TextSpan(
text: '蓝队 ',
style: TextStyle(color: colorScheme.blue),
),
_ => TextSpan(
text: '投票 ',
style: TextStyle(color: colorScheme.outline),
),
},
TextSpan(text: voteOption.desc),
],
),
style: TextStyle(
fontSize: 12,
color: colorScheme.onSurfaceVariant,
),
);
}
Widget _buildContent(BuildContext context, ColorScheme colorScheme) {
final replyControl = replyItem.replyControl;
final padding = EdgeInsets.only(left: replyLevel == 0 ? 6 : 45, right: 6);
return Column(
mainAxisSize: .min,
crossAxisAlignment: .start,
children: [
_buildHeader(context, theme),
_buildHeader(context, colorScheme),
const SizedBox(height: 10),
if (replyControl.hasVoteOption())
Padding(
padding: padding,
child: _buildVoteOption(colorScheme, replyControl.voteOption),
),
Padding(
padding: padding,
child: custom_text.Text.rich(
primary: theme.colorScheme.primary,
style: TextStyle(
height: 1.75,
fontSize: theme.textTheme.bodyMedium!.fontSize,
),
primary: colorScheme.primary,
style: const TextStyle(height: 1.75, fontSize: 14),
maxLines: replyLevel == 1 ? replyLengthLimit : null,
TextSpan(
children: [
@@ -329,7 +362,7 @@ class ReplyItemGrpc extends StatelessWidget {
],
_buildMessage(
context,
theme,
colorScheme,
replyControl.showTranslation
? replyItem.translatedContent
: replyItem.content,
@@ -359,12 +392,12 @@ class ReplyItemGrpc extends StatelessWidget {
],
if (replyLevel != 0) ...[
const SizedBox(height: 4),
buttonAction(context, theme, replyControl),
buttonAction(context, colorScheme, replyControl),
],
if (replyLevel == 1 && replyItem.count > Int64.ZERO) ...[
Padding(
padding: const EdgeInsets.only(top: 5, bottom: 12),
child: replyItemRow(context, theme, replyItem.replies),
child: replyItemRow(context, colorScheme, replyItem.replies),
),
],
],
@@ -373,15 +406,15 @@ class ReplyItemGrpc extends StatelessWidget {
Widget _buildTranslateBtn(
BuildContext context,
ThemeData theme,
ColorScheme colorScheme,
ReplyControl replyControl,
TextStyle textStyle,
ButtonStyle buttonStyle,
) {
late bool isProcessing = false;
final color = replyControl.showTranslation
? theme.colorScheme.primary
: theme.colorScheme.outline.withValues(alpha: 0.8);
? colorScheme.primary
: colorScheme.outline.withValues(alpha: 0.8);
return SizedBox(
height: 32,
child: TextButton(
@@ -439,14 +472,14 @@ class ReplyItemGrpc extends StatelessWidget {
Widget buttonAction(
BuildContext context,
ThemeData theme,
ColorScheme colorScheme,
ReplyControl replyControl,
) {
final textStyle = TextStyle(
height: 1,
fontSize: 12,
fontWeight: .normal,
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelMedium!.fontSize,
color: colorScheme.outline,
);
const buttonStyle = ButtonStyle(
visualDensity: .compact,
@@ -492,7 +525,7 @@ class ReplyItemGrpc extends StatelessWidget {
Icon(
Icons.reply,
size: 18,
color: theme.colorScheme.outline.withValues(alpha: 0.8),
color: colorScheme.outline.withValues(alpha: 0.8),
),
Text('回复', style: textStyle),
],
@@ -504,7 +537,7 @@ class ReplyItemGrpc extends StatelessWidget {
.TRANSLATION_SWITCH_SHOW_TRANSLATION) ...[
_buildTranslateBtn(
context,
theme,
colorScheme,
replyControl,
textStyle,
buttonStyle,
@@ -515,7 +548,7 @@ class ReplyItemGrpc extends StatelessWidget {
dialogBtn != null
? replyControl.cardLabels.first.textContent
: replyControl.cardLabels.map((e) => e.textContent).join(' '),
style: textStyle.copyWith(color: theme.colorScheme.secondary),
style: textStyle.copyWith(color: colorScheme.secondary),
),
const SizedBox(width: 2),
],
@@ -529,7 +562,7 @@ class ReplyItemGrpc extends StatelessWidget {
Widget replyItemRow(
BuildContext context,
ThemeData theme,
ColorScheme colorScheme,
List<ReplyInfo> replies,
) {
final extraRow = replies.length < replyItem.count.toInt();
@@ -537,7 +570,7 @@ class ReplyItemGrpc extends StatelessWidget {
return Padding(
padding: const EdgeInsets.only(left: 42, right: 4),
child: Material(
color: theme.colorScheme.onInverseSurface,
color: colorScheme.onInverseSurface,
borderRadius: const BorderRadius.all(Radius.circular(6)),
clipBehavior: Clip.hardEdge,
animationDuration: Duration.zero,
@@ -584,11 +617,9 @@ class ReplyItemGrpc extends StatelessWidget {
padding: padding,
child: Text.rich(
style: TextStyle(
fontSize: theme.textTheme.bodyMedium!.fontSize,
color: theme.colorScheme.onSurface.withValues(
alpha: 0.85,
),
height: 1.6,
fontSize: 14,
color: colorScheme.onSurface.withValues(alpha: 0.85),
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
@@ -596,9 +627,7 @@ class ReplyItemGrpc extends StatelessWidget {
children: [
TextSpan(
text: childReply.member.name,
style: TextStyle(
color: theme.colorScheme.primary,
),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () {
feedBack();
@@ -630,7 +659,7 @@ class ReplyItemGrpc extends StatelessWidget {
),
_buildMessage(
context,
theme,
colorScheme,
childReply.content,
childReply.replyControl,
),
@@ -649,15 +678,13 @@ class ReplyItemGrpc extends StatelessWidget {
: const EdgeInsets.fromLTRB(8, 5, 8, 8),
child: Text.rich(
TextSpan(
style: TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
),
style: const TextStyle(fontSize: 12),
children: [
if (replyItem.replyControl.upReply)
TextSpan(
text: 'UP主等人 ',
style: TextStyle(
color: theme.colorScheme.onSurface.withValues(
color: colorScheme.onSurface.withValues(
alpha: 0.85,
),
),
@@ -665,7 +692,7 @@ class ReplyItemGrpc extends StatelessWidget {
TextSpan(
text: '${replyItem.count}条回复',
style: TextStyle(
color: theme.colorScheme.primary,
color: colorScheme.primary,
),
),
],
@@ -681,7 +708,7 @@ class ReplyItemGrpc extends StatelessWidget {
InlineSpan _buildMessage(
BuildContext context,
ThemeData theme,
ColorScheme colorScheme,
Content content,
ReplyControl replyControl,
) {
@@ -727,16 +754,14 @@ class ReplyItemGrpc extends StatelessWidget {
child: CachedNetworkImage(
height: 19,
memCacheHeight: 19.cacheSize(context),
color: theme.colorScheme.primary,
color: colorScheme.primary,
imageUrl: ImageUtils.thumbnailUrl(url.prefixIcon),
placeholder: (_, _) => const SizedBox.shrink(),
),
),
TextSpan(
text: isCv ? '[笔记] ' : url.title,
style: TextStyle(
color: theme.colorScheme.primary,
),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () {
if (url.appUrlSchema.isEmpty) {
@@ -815,7 +840,7 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(
TextSpan(
text: matchStr,
style: TextStyle(color: theme.colorScheme.primary),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () =>
Get.toNamed('/member?mid=${content.atNameToMid[name]}'),
@@ -825,7 +850,7 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(
TextSpan(
text: '投票: ${content.vote.title}',
style: TextStyle(color: theme.colorScheme.primary),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () =>
showVoteDialog(context, content.vote.id.toInt()),
@@ -847,9 +872,7 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(
TextSpan(
text: isValid ? ' $matchStr ' : matchStr,
style: isValid
? TextStyle(color: theme.colorScheme.primary)
: null,
style: isValid ? TextStyle(color: colorScheme.primary) : null,
recognizer: isValid
? (NoDeadlineTapGestureRecognizer()
..onTap = () {
@@ -881,7 +904,7 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(
TextSpan(
text: matchStr,
style: TextStyle(color: theme.colorScheme.primary),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () {
Get.toNamed(
@@ -895,7 +918,7 @@ class ReplyItemGrpc extends StatelessWidget {
spanChildren.add(
TextSpan(
text: matchStr,
style: TextStyle(color: theme.colorScheme.primary),
style: TextStyle(color: colorScheme.primary),
recognizer: NoDeadlineTapGestureRecognizer()
..onTap = () => PageUtils.handleWebview(matchStr),
),
@@ -929,7 +952,7 @@ class ReplyItemGrpc extends StatelessWidget {
final hasClickUrl = content.richText.note.hasClickUrl();
if (hasClickUrl || content.richText.opus.hasOpusId()) {
color = theme.colorScheme.primary;
color = colorScheme.primary;
recognizer = NoDeadlineTapGestureRecognizer()
..onTap = () => hasClickUrl
? PiliScheme.routePushFromUrl(content.richText.note.clickUrl)
@@ -941,7 +964,7 @@ class ReplyItemGrpc extends StatelessWidget {
},
);
} else {
color = theme.colorScheme.secondary;
color = colorScheme.secondary;
}
spanChildren.insert(
0,
@@ -965,11 +988,12 @@ class ReplyItemGrpc extends StatelessWidget {
late String message = item.content.message;
final ownerMid = Int64(Accounts.main.mid);
final theme = Theme.of(context);
final errorColor = theme.colorScheme.error;
final colorScheme = theme.colorScheme;
final errorColor = colorScheme.error;
final style = theme.textTheme.titleSmall!;
return Padding(
padding: EdgeInsets.only(
padding: .only(
bottom: MediaQuery.viewPaddingOf(context).bottom + 20,
),
child: Column(
@@ -985,7 +1009,7 @@ class ReplyItemGrpc extends StatelessWidget {
width: 32,
height: 3,
decoration: BoxDecoration(
color: theme.colorScheme.outline,
color: colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3)),
),
),
@@ -1007,7 +1031,7 @@ class ReplyItemGrpc extends StatelessWidget {
},
title: Text(
'save to local',
style: style.copyWith(color: theme.colorScheme.primary),
style: style.copyWith(color: colorScheme.primary),
),
),
ListTile(
@@ -1018,7 +1042,7 @@ class ReplyItemGrpc extends StatelessWidget {
},
title: Text(
'remove from local',
style: style.copyWith(color: theme.colorScheme.primary),
style: style.copyWith(color: colorScheme.primary),
),
),
ListTile(
@@ -1037,7 +1061,7 @@ class ReplyItemGrpc extends StatelessWidget {
},
title: Text(
'save to local (x1000)',
style: style.copyWith(color: theme.colorScheme.primary),
style: style.copyWith(color: colorScheme.primary),
),
),
],
@@ -1048,7 +1072,7 @@ class ReplyItemGrpc extends StatelessWidget {
bool? isDelete = await showDialog<bool>(
context: context,
builder: (context) {
final theme = Theme.of(context);
final colorScheme = ColorScheme.of(context);
return AlertDialog(
title: const Text('删除评论'),
content: Text.rich(
@@ -1059,7 +1083,7 @@ class ReplyItemGrpc extends StatelessWidget {
TextSpan(
text: '@${item.member.name}',
style: TextStyle(
color: theme.colorScheme.primary,
color: colorScheme.primary,
),
),
const TextSpan(text: ':\n'),
@@ -1074,7 +1098,7 @@ class ReplyItemGrpc extends StatelessWidget {
child: Text(
'取消',
style: TextStyle(
color: theme.colorScheme.outline,
color: colorScheme.outline,
),
),
),