feat: 评论+关注

This commit is contained in:
guozhigq
2023-05-21 16:19:11 +08:00
parent e384486ab6
commit 51c4a082ac
18 changed files with 897 additions and 258 deletions

View File

@ -10,11 +10,7 @@ import 'package:pilipala/models/video/reply/data.dart';
import 'package:pilipala/models/video/reply/item.dart';
class VideoReplyController extends GetxController {
VideoReplyController(
this.aid,
this.rpid,
this.level
);
VideoReplyController(this.aid, this.rpid, this.level);
final ScrollController scrollController = ScrollController();
// 视频aid 请求时使用的oid
String? aid;
@ -26,7 +22,7 @@ class VideoReplyController extends GetxController {
// 当前页
int currentPage = 0;
bool isLoadingMore = false;
RxBool noMore = false.obs;
RxString noMore = ''.obs;
RxBool autoFocus = false.obs;
// 当前回复的回复
ReplyItemModel? currentReplyItem;
@ -48,17 +44,18 @@ class VideoReplyController extends GetxController {
res['data'] = ReplyData.fromJson(res['data']);
if (res['data'].replies.isNotEmpty) {
currentPage = currentPage + 1;
noMore.value = false;
noMore.value = '加载中';
if(res['data'].page.count == res['data'].page.acount){
noMore.value = '没有更多了';
}
} else {
if (currentPage == 0) {
noMore.value = '还没有评论';
} else {
noMore.value = true;
noMore.value = '没有更多了';
return;
}
}
if (res['data'].replies.length >= res['data'].page.count) {
noMore.value = true;
}
if (type == 'init') {
List<ReplyItemModel> replies = res['data'].replies;
// 添加置顶回复
@ -96,21 +93,26 @@ class VideoReplyController extends GetxController {
// 发表评论
Future submitReplyAdd() async {
print('replyLevel: $replyLevel');
// print('rpid: $rpid');
// print('currentReplyItem!.rpid: ${currentReplyItem!.rpid}');
var result = await VideoHttp.replyAdd(
type: ReplyType.video,
oid: int.parse(aid!),
root: replyLevel == '0' ? 0 : replyLevel == '1' ? currentReplyItem!.rpid : rPid,
parent: replyLevel == '0' ? 0 : replyLevel == '1' ? currentReplyItem!.rpid : currentReplyItem!.rpid,
message: replyLevel == '2' ? ' 回复 @${currentReplyItem!.member!.uname!} : 2楼31' : '2楼31',
root: replyLevel == '0'
? 0
: replyLevel == '1'
? currentReplyItem!.rpid
: rPid,
parent: replyLevel == '0'
? 0
: replyLevel == '1'
? currentReplyItem!.rpid
: currentReplyItem!.rpid,
message: replyLevel == '2'
? ' 回复 @${currentReplyItem!.member!.uname!} : 2楼31'
: '2楼31',
);
if(result['status']){
if (result['status']) {
SmartDialog.showToast(result['data']['success_toast']);
}else{
} else {
SmartDialog.showToast(result['message']);
}
}

View File

@ -3,10 +3,10 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'controller.dart';
import 'widgets/reply_item.dart';
@ -14,6 +14,7 @@ class VideoReplyPanel extends StatefulWidget {
int oid;
int rpid;
String? level;
Key? key;
VideoReplyPanel({
this.oid = 0,
this.rpid = 0,
@ -29,7 +30,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
late VideoReplyController _videoReplyController;
late AnimationController fabAnimationCtr;
late AnimationController replyAnimationCtl;
// List<ReplyItemModel>? replyList;
Future? _futureBuilderFuture;
@ -55,16 +55,9 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
VideoReplyController(Get.parameters['aid']!, '', '1'),
tag: Get.arguments['heroTag']);
}
// if(replyLevel != ''){
// _videoReplyController.replyLevel = replyLevel;
// }
print(
'_videoReplyController.replyLevel: ${_videoReplyController.replyLevel}');
fabAnimationCtr = AnimationController(
vsync: this, duration: const Duration(milliseconds: 300));
replyAnimationCtl = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
_futureBuilderFuture = _videoReplyController.queryReplyList();
_videoReplyController.scrollController.addListener(
@ -86,6 +79,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
}
},
);
fabAnimationCtr.forward();
}
void _showFab() {
@ -112,14 +106,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_videoReplyController.replyLevel = '0';
}
replyAnimationCtl.forward();
await Future.delayed(const Duration(microseconds: 100));
_videoReplyController.wakeUpReply();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
fabAnimationCtr.dispose();
_videoReplyController.scrollController.dispose();
@ -164,9 +156,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
60,
child: Center(
child: Obx(() => Text(
_videoReplyController.noMore.value
? '没有更多了'
: '加载中')),
_videoReplyController.noMore.value)),
),
);
} else {
@ -211,9 +201,9 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
right: 14,
child: SlideTransition(
position: Tween<Offset>(
// begin: const Offset(0, 2),
begin: const Offset(0, 2),
// 评论内容为空/不足一屏
begin: const Offset(0, 0),
// begin: const Offset(0, 0),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: fabAnimationCtr,
@ -221,57 +211,25 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
)),
child: FloatingActionButton(
heroTag: null,
onPressed: () => _showReply('main'),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (builder) {
return VideoReplyNewDialog(
replyLevel: '0',
oid: int.parse(Get.parameters['aid']!),
root: 0,
parent: 0,
);
},
).then((value) => {print('close ModalBottomSheet')});
},
tooltip: '发表评论',
child: const Icon(Icons.reply),
),
),
),
Obx(
() => Positioned(
bottom: 0,
left: 0,
right: 0,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 2),
end: const Offset(0, 0),
).animate(CurvedAnimation(
parent: replyAnimationCtl,
curve: Curves.easeInOut,
)),
child: Container(
height: 100 + MediaQuery.of(context).padding.bottom,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
color: Theme.of(context).colorScheme.surfaceVariant,
child: Padding(
padding: const EdgeInsets.only(left: 14, right: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: _videoReplyController.autoFocus.value,
child: const TextField(
autofocus: true,
maxLines: null,
decoration: InputDecoration(
hintText: "友善评论", border: InputBorder.none),
),
),
TextButton(
onPressed: () =>
_videoReplyController.submitReplyAdd(),
child: const Text('发送'),
)
],
),
),
),
),
),
),
],
),
),

View File

@ -4,7 +4,9 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/reply/index.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'package:pilipala/utils/utils.dart';
class ReplyItem extends StatelessWidget {
@ -176,13 +178,14 @@ class ReplyItem extends StatelessWidget {
// 操作区域
bottonAction(context, replyItem!.replyControl),
const SizedBox(height: 3),
if (replyItem!.replies!.isNotEmpty) ...[
if (replyItem!.replies!.isNotEmpty && replyLevel != '2') ...[
Padding(
padding: const EdgeInsets.only(top: 2, bottom: 12),
child: ReplyItemRow(
replies: replyItem!.replies,
replyControl: replyItem!.replyControl,
f_rpid: replyItem!.rpid,
replyItem: replyItem,
),
),
],
@ -225,17 +228,32 @@ class ReplyItem extends StatelessWidget {
if (replyItem!.upAction!.like!)
Icon(Icons.favorite, color: Colors.red[400], size: 18),
SizedBox(
height: 28,
width: 42,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
child: Text('回复', style: Theme.of(context)
.textTheme
.labelMedium),
onPressed: () => weakUpReply!(replyItem, replyLevel),
)),
height: 28,
width: 42,
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
child: Text('回复', style: Theme.of(context).textTheme.labelMedium),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (builder) {
print('🌹: ${replyItem!.rpid}');
return VideoReplyNewDialog(
replyLevel: replyLevel,
oid: replyItem!.oid,
root: replyItem!.rpid,
parent: replyItem!.rpid,
);
},
).then((value) => {
print('showModalBottomSheet')
});
},
),
),
SizedBox(
height: 32,
child: TextButton(
@ -272,10 +290,12 @@ class ReplyItemRow extends StatelessWidget {
this.replies,
this.replyControl,
this.f_rpid,
this.replyItem
});
List? replies;
ReplyControl? replyControl;
int? f_rpid;
ReplyItemModel? replyItem;
@override
Widget build(BuildContext context) {
@ -297,7 +317,7 @@ class ReplyItemRow extends StatelessWidget {
if (extraRow == 1 && index == replies!.length) {
// 有楼中楼回复,在最后显示
return InkWell(
onTap: () => replyReply(context),
onTap: () => replyReply(replyItem),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
@ -323,7 +343,7 @@ class ReplyItemRow extends StatelessWidget {
);
} else {
return InkWell(
onTap: () {},
onTap: () => replyReply(replyItem),
child: Padding(
padding: EdgeInsets.fromLTRB(
8,
@ -338,10 +358,6 @@ class ReplyItemRow extends StatelessWidget {
: TextOverflow.visible,
maxLines: extraRow == 1 ? 2 : null,
TextSpan(
recognizer: TapGestureRecognizer()
..onTap = () {
replyReply(context);
},
children: [
TextSpan(
text: replies![index].member.uname + ' ',
@ -374,46 +390,15 @@ class ReplyItemRow extends StatelessWidget {
);
}
void replyReply(context) {
Get.bottomSheet(
barrierColor: Colors.transparent,
useRootNavigator: true,
isScrollControlled: true,
Container(
height: Get.size.height - Get.size.width * 9 / 16 - 45,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
AppBar(
automaticallyImplyLeading: false,
centerTitle: false,
elevation: 1,
title: Text(
'评论详情',
style: Theme.of(context).textTheme.titleMedium,
),
actions: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () async {
Get.back();
},
)
],
),
Expanded(
child: VideoReplyPanel(
oid: replies!.first.oid,
rpid: f_rpid!,
level: '2',
),
)
],
),
),
persistent: false,
backgroundColor: Theme.of(context).bottomSheetTheme.backgroundColor,
);
void replyReply(replyItem) {
// replyItem 楼主评论
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
.oid
.value = replies!.first.oid;
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
.fRpid
.value = f_rpid!;
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']).firstFloor = replyItem;
}
}
@ -423,11 +408,10 @@ InlineSpan buildContent(BuildContext context, content) {
content.jumpUrl.isEmpty &&
content.vote.isEmpty &&
content.pictures.isEmpty) {
return TextSpan(text: content.message,
recognizer: TapGestureRecognizer()
..onTap = ()=> {
print('点击')
},);
return TextSpan(
text: content.message,
// recognizer: TapGestureRecognizer()..onTap = () => {print('点击')},
);
}
List<InlineSpan> spanChilds = [];
// 匹配表情
@ -505,10 +489,10 @@ InlineSpan buildContent(BuildContext context, content) {
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () => {
print('Url 点击'),
},
// recognizer: TapGestureRecognizer()
// ..onTap = () => {
// print('Url 点击'),
// },
),
);
spanChilds.add(
@ -540,10 +524,10 @@ InlineSpan buildContent(BuildContext context, content) {
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () => {
print('time 点击'),
},
// recognizer: TapGestureRecognizer()
// ..onTap = () => {
// print('time 点击'),
// },
),
);
return '';