fix: 评论区优化

This commit is contained in:
guozhigq
2023-08-30 17:46:51 +08:00
parent f6c7143d2d
commit 1e1e2e5a77
4 changed files with 277 additions and 186 deletions

View File

@ -61,6 +61,12 @@ class _ExtraSettingState extends State<ExtraSetting> {
setKey: SettingBoxKey.enableQuickFav, setKey: SettingBoxKey.enableQuickFav,
defaultVal: false, defaultVal: false,
), ),
const SetSwitchItem(
title: '评论区搜索关键词',
subTitle: '展示评论区搜索关键词',
setKey: SettingBoxKey.enableWordRe,
defaultVal: false,
),
ListTile( ListTile(
dense: false, dense: false,
title: Text('评论展示', style: titleStyle), title: Text('评论展示', style: titleStyle),

View File

@ -69,7 +69,7 @@ class VideoReplyController extends GetxController {
noMore.value = '加载中...'; noMore.value = '加载中...';
/// 第一页回复数小于20 /// 第一页回复数小于20
if (currentPage == 0 && replies.length < 20) { if (currentPage == 0 && replies.length < 18) {
noMore.value = '没有更多了'; noMore.value = '没有更多了';
} }
currentPage++; currentPage++;

View File

@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/common/reply_type.dart';
@ -9,10 +10,13 @@ import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart'; import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'zan.dart'; import 'zan.dart';
Box setting = GStrorage.setting;
class ReplyItem extends StatelessWidget { class ReplyItem extends StatelessWidget {
const ReplyItem({ const ReplyItem({
this.replyItem, this.replyItem,
@ -41,63 +45,73 @@ class ReplyItem extends StatelessWidget {
replyReply!(replyItem); replyReply!(replyItem);
} }
}, },
child: Padding( child: Column(
padding: const EdgeInsets.fromLTRB(12, 4, 8, 2), children: [
child: content(context), Padding(
padding: const EdgeInsets.fromLTRB(12, 14, 8, 5),
child: content(context),
),
Divider(
indent: 55,
endIndent: 15,
height: 0.3,
color: Theme.of(context)
.colorScheme
.onInverseSurface
.withOpacity(0.5),
)
],
), ),
), ),
); );
} }
Widget lfAvtar(context, heroTag) { Widget lfAvtar(context, heroTag) {
return Container( return Stack(
margin: const EdgeInsets.only(top: 5), children: [
child: Stack( Hero(
children: [ tag: heroTag,
Hero( child: NetworkImgLayer(
tag: heroTag, src: replyItem!.member!.avatar,
child: NetworkImgLayer( width: 34,
src: replyItem!.member!.avatar, height: 34,
width: 34, type: 'avatar',
height: 34, ),
type: 'avatar', ),
if (replyItem!.member!.officialVerify != null &&
replyItem!.member!.officialVerify!['type'] == 0)
Positioned(
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7),
color: Theme.of(context).colorScheme.background,
),
child: Icon(
Icons.offline_bolt,
color: Theme.of(context).colorScheme.primary,
size: 16,
),
), ),
), ),
if (replyItem!.member!.officialVerify != null && if (replyItem!.member!.vip!['vipStatus'] > 0 &&
replyItem!.member!.officialVerify!['type'] == 0) replyItem!.member!.vip!['vipType'] == 2)
Positioned( Positioned(
right: 0, right: 0,
bottom: 0, bottom: 0,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7), borderRadius: BorderRadius.circular(7),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
), ),
child: Icon( child: Image.asset(
Icons.offline_bolt, 'assets/images/big-vip.png',
color: Theme.of(context).colorScheme.primary, height: 14,
size: 16,
),
), ),
), ),
if (replyItem!.member!.vip!['vipStatus'] > 0 && ),
replyItem!.member!.vip!['vipType'] == 2) ],
Positioned(
right: 0,
bottom: 0,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7),
color: Theme.of(context).colorScheme.background,
),
child: Image.asset(
'assets/images/big-vip.png',
height: 14,
),
),
),
],
),
); );
} }
@ -107,7 +121,100 @@ class ReplyItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// 头像、昵称 // 头像、昵称
// SizedBox(
// width: double.infinity,
// child: Stack(
// children: [
// GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {
// feedBack();
// Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
// 'face': replyItem!.member!.avatar!,
// 'heroTag': heroTag
// });
// },
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// lfAvtar(context, heroTag),
// const SizedBox(width: 12),
// Text(
// replyItem!.member!.uname!,
// style: TextStyle(
// color: replyItem!.member!.vip!['vipStatus'] > 0
// ? const Color.fromARGB(255, 251, 100, 163)
// : Theme.of(context).colorScheme.outline,
// fontSize: 13,
// ),
// ),
// const SizedBox(width: 6),
// Image.asset(
// 'assets/images/lv/lv${replyItem!.member!.level}.png',
// height: 11,
// ),
// const SizedBox(width: 6),
// if (replyItem!.isUp!)
// const PBadge(
// text: 'UP',
// size: 'small',
// stack: 'normal',
// fs: 9,
// ),
// ],
// ),
// ),
// Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: Container(
// width: double.infinity,
// height: 45,
// decoration: BoxDecoration(
// image: replyItem!.member!.userSailing!.cardbg != null
// ? DecorationImage(
// alignment: Alignment.centerRight,
// fit: BoxFit.fitHeight,
// image: NetworkImage(
// replyItem!.member!.userSailing!.cardbg!['image'],
// ),
// )
// : null,
// ),
// ),
// ),
// if (replyItem!.member!.userSailing!.cardbg != null &&
// replyItem!.member!.userSailing!.cardbg!['fan']['number'] > 0)
// Positioned(
// top: 10,
// left: Get.size.width / 7 * 5.8,
// child: DefaultTextStyle(
// style: TextStyle(
// fontFamily: 'fansCard',
// fontSize: 9,
// color: Theme.of(context).colorScheme.primary,
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// const Text('NO.'),
// Text(
// replyItem!.member!.userSailing!.cardbg!['fan']
// ['num_desc'],
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// ),
/// fix Stack内GestureDetector onTap无效
GestureDetector( GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
feedBack(); feedBack();
Get.toNamed('/member?mid=${replyItem!.mid}', arguments: { Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
@ -115,93 +222,72 @@ class ReplyItem extends StatelessWidget {
'heroTag': heroTag 'heroTag': heroTag
}); });
}, },
child: SizedBox( child: Row(
width: double.infinity, crossAxisAlignment: CrossAxisAlignment.center,
child: Stack( mainAxisSize: MainAxisSize.min,
children: [ children: <Widget>[
Row( lfAvtar(context, heroTag),
crossAxisAlignment: CrossAxisAlignment.center, const SizedBox(width: 12),
mainAxisSize: MainAxisSize.min, Column(
children: <Widget>[ crossAxisAlignment: CrossAxisAlignment.start,
lfAvtar(context, heroTag), children: [
const SizedBox(width: 12), Row(
Text( children: [
replyItem!.member!.uname!, Text(
style: TextStyle( replyItem!.member!.uname!,
color: replyItem!.member!.vip!['vipStatus'] > 0 style: TextStyle(
? const Color.fromARGB(255, 251, 100, 163) color: replyItem!.member!.vip!['vipStatus'] > 0
: Theme.of(context).colorScheme.outline, ? const Color.fromARGB(255, 251, 100, 163)
fontSize: 13, : Theme.of(context).colorScheme.outline,
fontSize: 13,
),
), ),
), const SizedBox(width: 6),
const SizedBox(width: 6), Image.asset(
Image.asset( 'assets/images/lv/lv${replyItem!.member!.level}.png',
'assets/images/lv/lv${replyItem!.member!.level}.png', height: 11,
height: 11,
),
const SizedBox(width: 6),
if (replyItem!.isUp!)
const PBadge(
text: 'UP',
size: 'small',
stack: 'normal',
fs: 9,
), ),
], const SizedBox(width: 6),
), if (replyItem!.isUp!)
Positioned( const PBadge(
top: 0, text: 'UP',
left: 0, size: 'small',
right: 0, stack: 'normal',
child: Container( fs: 9,
width: double.infinity, ),
height: 45, ],
decoration: BoxDecoration(
image: replyItem!.member!.userSailing!.cardbg != null
? DecorationImage(
alignment: Alignment.centerRight,
fit: BoxFit.fitHeight,
image: NetworkImage(
replyItem!
.member!.userSailing!.cardbg!['image'],
),
)
: null,
),
), ),
), Row(
if (replyItem!.member!.userSailing!.cardbg != null && children: [
replyItem!.member!.userSailing!.cardbg!['fan']['number'] > Text(
0) Utils.dateFormat(replyItem!.ctime),
Positioned( style: TextStyle(
top: 10, fontSize:
left: Get.size.width / 7 * 5.8, Theme.of(context).textTheme.labelSmall!.fontSize,
child: DefaultTextStyle( color: Theme.of(context).colorScheme.outline,
style: TextStyle( ),
fontFamily: 'fansCard',
fontSize: 9,
color: Theme.of(context).colorScheme.primary,
), ),
child: Column( if (replyItem!.replyControl != null &&
crossAxisAlignment: CrossAxisAlignment.start, replyItem!.replyControl!.location != '')
mainAxisAlignment: MainAxisAlignment.center, Text(
children: [ '${replyItem!.replyControl!.location!}',
const Text('NO.'), style: TextStyle(
Text( fontSize: Theme.of(context)
replyItem!.member!.userSailing!.cardbg!['fan'] .textTheme
['num_desc'], .labelSmall!
), .fontSize,
], color: Theme.of(context).colorScheme.outline),
), ),
), ],
), )
], ],
), ),
],
), ),
), ),
// title // title
Container( Container(
margin: const EdgeInsets.only(top: 0, left: 45, right: 6, bottom: 6), margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: SelectableRegion( child: SelectableRegion(
magnifierConfiguration: const TextMagnifierConfiguration(), magnifierConfiguration: const TextMagnifierConfiguration(),
focusNode: FocusNode(), focusNode: FocusNode(),
@ -255,52 +341,10 @@ class ReplyItem extends StatelessWidget {
Widget bottonAction(context, replyControl) { Widget bottonAction(context, replyControl) {
return Row( return Row(
children: [ children: [
const SizedBox(width: 48), const SizedBox(width: 32),
if (replyItem!.cardLabel!.isNotEmpty &&
replyItem!.cardLabel!.contains('热评'))
Text(
'热评 • ',
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.primary),
),
Text(
Utils.dateFormat(replyItem!.ctime),
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
if (replyItem!.replyControl != null &&
replyItem!.replyControl!.location != '')
Text(
'${replyItem!.replyControl!.location!}',
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline),
),
const Spacer(),
if (replyItem!.upAction!.like!)
Icon(Icons.favorite, color: Colors.red[400], size: 18),
SizedBox( SizedBox(
height: 28, height: 32,
width: 28, child: TextButton(
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
icon: Icon(
Icons.reply_outlined,
size: 18,
color: Theme.of(context).colorScheme.outline,
),
// child: Text('回复',
// style: Theme.of(context)
// .textTheme
// .labelMedium!
// .copyWith(color: Theme.of(context).colorScheme.outline)),
onPressed: () { onPressed: () {
feedBack(); feedBack();
showModalBottomSheet( showModalBottomSheet(
@ -324,8 +368,41 @@ class ReplyItem extends StatelessWidget {
} }
}); });
}, },
child: Row(children: [
Icon(Icons.reply,
size: 18,
color:
Theme.of(context).colorScheme.outline.withOpacity(0.8)),
const SizedBox(width: 3),
Text(
'回复',
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
]),
), ),
), ),
const SizedBox(width: 2),
if (replyItem!.upAction!.like!) ...[
Text(
'up主觉得很赞',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize),
),
const SizedBox(width: 2),
],
if (replyItem!.cardLabel!.isNotEmpty &&
replyItem!.cardLabel!.contains('热评'))
Text(
'热评',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize),
),
const Spacer(),
ZanButton(replyItem: replyItem, replyType: replyType), ZanButton(replyItem: replyItem, replyType: replyType),
const SizedBox(width: 5) const SizedBox(width: 5)
], ],
@ -595,16 +672,20 @@ InlineSpan buildContent(
RegExp(RegExp.escape(urlKeys.join("|"))), RegExp(RegExp.escape(urlKeys.join("|"))),
onMatch: (Match match) { onMatch: (Match match) {
String matchStr = match[0]!; String matchStr = match[0]!;
String appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
// 默认不显示关键词
bool enableWordRe =
setting.get(SettingBoxKey.enableWordRe, defaultValue: false);
spanChilds.add( spanChilds.add(
TextSpan( TextSpan(
text: content.jumpUrl[matchStr]['title'], text: content.jumpUrl[matchStr]['title'],
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.primary, color: enableWordRe
? Theme.of(context).colorScheme.primary
: null,
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
String appUrlSchema =
content.jumpUrl[matchStr]['app_url_schema'];
if (appUrlSchema == '') { if (appUrlSchema == '') {
Get.toNamed( Get.toNamed(
'/webview', '/webview',
@ -615,7 +696,8 @@ InlineSpan buildContent(
}, },
); );
} else { } else {
if (appUrlSchema.startsWith('bilibili://search')) { if (appUrlSchema.startsWith('bilibili://search') &&
enableWordRe) {
Get.toNamed('/searchResult', parameters: { Get.toNamed('/searchResult', parameters: {
'keyword': content.jumpUrl[matchStr]['title'] 'keyword': content.jumpUrl[matchStr]['title']
}); });
@ -624,16 +706,18 @@ InlineSpan buildContent(
}, },
), ),
); );
spanChilds.add( if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) {
WidgetSpan( spanChilds.add(
child: Icon( WidgetSpan(
FontAwesomeIcons.magnifyingGlass, child: Icon(
size: 9, FontAwesomeIcons.magnifyingGlass,
color: Theme.of(context).colorScheme.primary, size: 9,
color: Theme.of(context).colorScheme.primary,
),
alignment: PlaceholderAlignment.top,
), ),
alignment: PlaceholderAlignment.top, );
), }
);
return ''; return '';
}, },
onNonMatch: (String str) { onNonMatch: (String str) {
@ -647,7 +731,7 @@ InlineSpan buildContent(
); );
} }
str = matchUrl.splitMapJoin( str = matchUrl.splitMapJoin(
RegExp(r"\d{1,2}:\d{1,2}"), RegExp(r'\b\d{2}:\d{2}\b'),
onMatch: (Match match) { onMatch: (Match match) {
String matchStr = match[0]!; String matchStr = match[0]!;
spanChilds.add( spanChilds.add(

View File

@ -111,6 +111,7 @@ class SettingBoxKey {
static const String defaultDynamicType = 'defaultDynamicType'; static const String defaultDynamicType = 'defaultDynamicType';
static const String enableHotKey = 'enableHotKey'; static const String enableHotKey = 'enableHotKey';
static const String enableQuickFav = 'enableQuickFav'; static const String enableQuickFav = 'enableQuickFav';
static const String enableWordRe = 'enableWordRe';
/// 外观 /// 外观
static const String themeMode = 'themeMode'; static const String themeMode = 'themeMode';