Merge branch 'main' into feature-m3Design
This commit is contained in:
@ -12,7 +12,7 @@ class ReplyHttp {
|
|||||||
'oid': oid,
|
'oid': oid,
|
||||||
'pn': pageNum,
|
'pn': pageNum,
|
||||||
'type': type,
|
'type': type,
|
||||||
'sort': 1,
|
'sort': sort,
|
||||||
});
|
});
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -306,7 +306,7 @@ class VideoHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': []};
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
lib/models/common/reply_sort_type.dart
Normal file
6
lib/models/common/reply_sort_type.dart
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
enum ReplySortType { time, like, reply }
|
||||||
|
|
||||||
|
extension ReplySortTypeExtension on ReplySortType {
|
||||||
|
String get titles => ['最新评论', '最热评论', '回复最多'][index];
|
||||||
|
String get labels => ['最新', '最热', '最多回复'][index];
|
||||||
|
}
|
||||||
@ -28,7 +28,8 @@ enum ReplyType {
|
|||||||
ticket,
|
ticket,
|
||||||
// 音频
|
// 音频
|
||||||
audio,
|
audio,
|
||||||
|
// 风纪委员会
|
||||||
|
unset3,
|
||||||
// 点评
|
// 点评
|
||||||
comment,
|
comment,
|
||||||
// 动态
|
// 动态
|
||||||
|
|||||||
@ -33,10 +33,7 @@ class ReplyContent {
|
|||||||
pictures = json['pictures'] ?? [];
|
pictures = json['pictures'] ?? [];
|
||||||
vote = json['vote'] ?? {};
|
vote = json['vote'] ?? {};
|
||||||
richText = json['rich_text'] ?? {};
|
richText = json['rich_text'] ?? {};
|
||||||
isText = emote!.isEmpty &&
|
// 不包含@ 笔记 图片的时候,文字可折叠
|
||||||
atNameToMid!.isEmpty &&
|
isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty;
|
||||||
jumpUrl!.isEmpty &&
|
|
||||||
vote!.isEmpty &&
|
|
||||||
pictures!.isEmpty;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/reply.dart';
|
import 'package:pilipala/http/reply.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||||
import 'package:pilipala/models/video/reply/data.dart';
|
import 'package:pilipala/models/video/reply/data.dart';
|
||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
|
|
||||||
@ -16,6 +16,10 @@ class DynamicDetailController extends GetxController {
|
|||||||
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
||||||
RxInt acount = 0.obs;
|
RxInt acount = 0.obs;
|
||||||
|
|
||||||
|
ReplySortType sortType = ReplySortType.time;
|
||||||
|
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
||||||
|
RxString sortTypeLabel = ReplySortType.time.labels.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@ -35,6 +39,7 @@ class DynamicDetailController extends GetxController {
|
|||||||
oid: oid!,
|
oid: oid!,
|
||||||
pageNum: currentPage + 1,
|
pageNum: currentPage + 1,
|
||||||
type: type!,
|
type: type!,
|
||||||
|
sort: sortType.index,
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
res['data'] = ReplyData.fromJson(res['data']);
|
res['data'] = ReplyData.fromJson(res['data']);
|
||||||
@ -42,7 +47,7 @@ class DynamicDetailController extends GetxController {
|
|||||||
if (res['data'].replies.isNotEmpty) {
|
if (res['data'].replies.isNotEmpty) {
|
||||||
currentPage = currentPage + 1;
|
currentPage = currentPage + 1;
|
||||||
noMore.value = '加载中...';
|
noMore.value = '加载中...';
|
||||||
if (replyList.isEmpty) {
|
if (res['data'].replies.isEmpty) {
|
||||||
noMore.value = '没有更多了';
|
noMore.value = '没有更多了';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -80,4 +85,24 @@ class DynamicDetailController extends GetxController {
|
|||||||
isLoadingMore = false;
|
isLoadingMore = false;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 排序搜索评论
|
||||||
|
queryBySort() {
|
||||||
|
switch (sortType) {
|
||||||
|
case ReplySortType.time:
|
||||||
|
sortType = ReplySortType.like;
|
||||||
|
break;
|
||||||
|
case ReplySortType.like:
|
||||||
|
sortType = ReplySortType.reply;
|
||||||
|
break;
|
||||||
|
case ReplySortType.reply:
|
||||||
|
sortType = ReplySortType.time;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
sortTypeTitle.value = sortType.titles;
|
||||||
|
sortTypeLabel.value = sortType.labels;
|
||||||
|
replyList.clear();
|
||||||
|
queryReplyList(reqType: 'init');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,7 +72,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void replyReply(replyItem) {
|
void replyReply(replyItem) {
|
||||||
int oid = replyItem.replies!.first.oid;
|
int oid = replyItem.oid;
|
||||||
int rpid = replyItem.rpid!;
|
int rpid = replyItem.rpid!;
|
||||||
Get.to(
|
Get.to(
|
||||||
() => Scaffold(
|
() => Scaffold(
|
||||||
@ -85,6 +85,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
|||||||
rpid: rpid,
|
rpid: rpid,
|
||||||
source: 'dynamic',
|
source: 'dynamic',
|
||||||
replyType: ReplyType.values[type],
|
replyType: ReplyType.values[type],
|
||||||
|
firstFloor: replyItem,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -158,27 +159,16 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
|||||||
),
|
),
|
||||||
const Text('条回复'),
|
const Text('条回复'),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
// TextButton.icon(
|
SizedBox(
|
||||||
// onPressed: () {},
|
height: 35,
|
||||||
// icon: const Icon(
|
child: TextButton.icon(
|
||||||
// Icons.subject_rounded,
|
onPressed: () =>
|
||||||
// size: 15,
|
_dynamicDetailController!.queryBySort(),
|
||||||
// ),
|
icon: const Icon(Icons.sort, size: 17),
|
||||||
// style: TextButton.styleFrom(
|
label: Obx(() => Text(
|
||||||
// padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
|
_dynamicDetailController!.sortTypeLabel.value)),
|
||||||
// foregroundColor:
|
),
|
||||||
// Theme.of(context).colorScheme.outline,
|
)
|
||||||
// ),
|
|
||||||
// label: Text(
|
|
||||||
// '按时间',
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: Theme.of(context)
|
|
||||||
// .textTheme
|
|
||||||
// .titleSmall!
|
|
||||||
// .fontSize,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -193,36 +183,49 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
|||||||
if (snapshot.data['status']) {
|
if (snapshot.data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverList(
|
() => _dynamicDetailController!.replyList.isEmpty
|
||||||
|
? SliverList(
|
||||||
|
delegate:
|
||||||
|
SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 8),
|
||||||
|
)
|
||||||
|
: SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index ==
|
if (index ==
|
||||||
_dynamicDetailController!.replyList.length) {
|
_dynamicDetailController!
|
||||||
|
.replyList.length) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom:
|
bottom: MediaQuery.of(context)
|
||||||
MediaQuery.of(context).padding.bottom),
|
.padding
|
||||||
height:
|
.bottom),
|
||||||
MediaQuery.of(context).padding.bottom + 100,
|
height: MediaQuery.of(context)
|
||||||
|
.padding
|
||||||
|
.bottom +
|
||||||
|
100,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Obx(() => Text(
|
child: Obx(() => Text(
|
||||||
_dynamicDetailController!.noMore.value)),
|
_dynamicDetailController!
|
||||||
|
.noMore.value)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return ReplyItem(
|
return ReplyItem(
|
||||||
replyItem:
|
replyItem: _dynamicDetailController!
|
||||||
_dynamicDetailController!.replyList[index],
|
.replyList[index],
|
||||||
showReplyRow: true,
|
showReplyRow: true,
|
||||||
replyLevel: '1',
|
replyLevel: '1',
|
||||||
replyReply: (replyItem) =>
|
replyReply: (replyItem) =>
|
||||||
replyReply(replyItem),
|
replyReply(replyItem),
|
||||||
replyType: ReplyType.album,
|
replyType: ReplyType.values[type],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
childCount:
|
childCount:
|
||||||
_dynamicDetailController!.replyList.length + 1,
|
_dynamicDetailController!.replyList.length +
|
||||||
|
1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/reply.dart';
|
import 'package:pilipala/http/reply.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
|
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||||
import 'package:pilipala/models/common/reply_type.dart';
|
import 'package:pilipala/models/common/reply_type.dart';
|
||||||
import 'package:pilipala/models/video/reply/data.dart';
|
import 'package:pilipala/models/video/reply/data.dart';
|
||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
@ -27,7 +28,6 @@ class VideoReplyController extends GetxController {
|
|||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
bool isLoadingMore = false;
|
bool isLoadingMore = false;
|
||||||
RxString noMore = ''.obs;
|
RxString noMore = ''.obs;
|
||||||
RxBool autoFocus = false.obs;
|
|
||||||
// 当前回复的回复
|
// 当前回复的回复
|
||||||
ReplyItemModel? currentReplyItem;
|
ReplyItemModel? currentReplyItem;
|
||||||
// 回复来源
|
// 回复来源
|
||||||
@ -37,11 +37,19 @@ class VideoReplyController extends GetxController {
|
|||||||
// 默认回复主楼
|
// 默认回复主楼
|
||||||
String replyLevel = '0';
|
String replyLevel = '0';
|
||||||
|
|
||||||
|
ReplySortType sortType = ReplySortType.time;
|
||||||
|
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
||||||
|
RxString sortTypeLabel = ReplySortType.time.labels.obs;
|
||||||
|
|
||||||
Future queryReplyList({type = 'init'}) async {
|
Future queryReplyList({type = 'init'}) async {
|
||||||
isLoadingMore = true;
|
isLoadingMore = true;
|
||||||
var res = level == '1'
|
var res = level == '1'
|
||||||
? await ReplyHttp.replyList(
|
? await ReplyHttp.replyList(
|
||||||
oid: aid!, pageNum: currentPage + 1, type: 1)
|
oid: aid!,
|
||||||
|
pageNum: currentPage + 1,
|
||||||
|
type: ReplyType.video.index,
|
||||||
|
sort: sortType.index,
|
||||||
|
)
|
||||||
: await ReplyHttp.replyReplyList(
|
: await ReplyHttp.replyReplyList(
|
||||||
oid: aid!, root: rpid!, pageNum: currentPage + 1, type: 1);
|
oid: aid!, root: rpid!, pageNum: currentPage + 1, type: 1);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
@ -91,33 +99,24 @@ class VideoReplyController extends GetxController {
|
|||||||
queryReplyList(type: 'onLoad');
|
queryReplyList(type: 'onLoad');
|
||||||
}
|
}
|
||||||
|
|
||||||
wakeUpReply() {
|
// 排序搜索评论
|
||||||
autoFocus.value = true;
|
queryBySort() {
|
||||||
}
|
switch (sortType) {
|
||||||
|
case ReplySortType.time:
|
||||||
// 发表评论
|
sortType = ReplySortType.like;
|
||||||
Future submitReplyAdd() async {
|
break;
|
||||||
var result = await VideoHttp.replyAdd(
|
case ReplySortType.like:
|
||||||
type: ReplyType.video,
|
sortType = ReplySortType.reply;
|
||||||
oid: aid!,
|
break;
|
||||||
root: replyLevel == '0'
|
case ReplySortType.reply:
|
||||||
? 0
|
sortType = ReplySortType.time;
|
||||||
: replyLevel == '1'
|
break;
|
||||||
? currentReplyItem!.rpid
|
default:
|
||||||
: rPid,
|
|
||||||
parent: replyLevel == '0'
|
|
||||||
? 0
|
|
||||||
: replyLevel == '1'
|
|
||||||
? currentReplyItem!.rpid
|
|
||||||
: currentReplyItem!.rpid,
|
|
||||||
message: replyLevel == '2'
|
|
||||||
? ' 回复 @${currentReplyItem!.member!.uname!} : 2楼31'
|
|
||||||
: '2楼31',
|
|
||||||
);
|
|
||||||
if (result['status']) {
|
|
||||||
SmartDialog.showToast(result['data']['success_toast']);
|
|
||||||
} else {
|
|
||||||
SmartDialog.showToast(result['message']);
|
|
||||||
}
|
}
|
||||||
|
sortTypeTitle.value = sortType.titles;
|
||||||
|
sortTypeLabel.value = sortType.labels;
|
||||||
|
currentPage = 0;
|
||||||
|
replyList.clear();
|
||||||
|
queryReplyList(type: 'init');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
late VideoReplyController _videoReplyController;
|
late VideoReplyController _videoReplyController;
|
||||||
late AnimationController fabAnimationCtr;
|
late AnimationController fabAnimationCtr;
|
||||||
|
|
||||||
// List<ReplyItemModel>? replyList;
|
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
bool _isFabVisible = true;
|
bool _isFabVisible = true;
|
||||||
String replyLevel = '1';
|
String replyLevel = '1';
|
||||||
@ -112,15 +111,15 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
_videoReplyController.replyLevel = '0';
|
_videoReplyController.replyLevel = '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.delayed(const Duration(microseconds: 100));
|
// await Future.delayed(const Duration(microseconds: 100));
|
||||||
_videoReplyController.wakeUpReply();
|
// _videoReplyController.wakeUpReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 展示二级回复
|
// 展示二级回复
|
||||||
void replyReply(replyItem) {
|
void replyReply(replyItem) {
|
||||||
VideoDetailController videoDetailCtr =
|
VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
videoDetailCtr.oid = replyItem.replies!.first.oid;
|
videoDetailCtr.oid = replyItem.oid;
|
||||||
videoDetailCtr.fRpid = replyItem.rpid!;
|
videoDetailCtr.fRpid = replyItem.rpid!;
|
||||||
videoDetailCtr.firstFloor = replyItem;
|
videoDetailCtr.firstFloor = replyItem;
|
||||||
videoDetailCtr.showReplyReplyPanel();
|
videoDetailCtr.showReplyReplyPanel();
|
||||||
@ -147,7 +146,46 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
controller: _videoReplyController.scrollController,
|
controller: _videoReplyController.scrollController,
|
||||||
key: const PageStorageKey<String>('评论'),
|
key: const PageStorageKey<String>('评论'),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
const SliverToBoxAdapter(child: SizedBox(height: 12)),
|
SliverPersistentHeader(
|
||||||
|
pinned: false,
|
||||||
|
floating: true,
|
||||||
|
delegate: _MySliverPersistentHeaderDelegate(
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 6, 10, 6),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Obx(
|
||||||
|
() => AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
transitionBuilder:
|
||||||
|
(Widget child, Animation<double> animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation, child: child);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
_videoReplyController.sortTypeTitle.value,
|
||||||
|
key: ValueKey<String>(
|
||||||
|
_videoReplyController.sortTypeTitle.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 35,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () =>
|
||||||
|
_videoReplyController.queryBySort(),
|
||||||
|
icon: const Icon(Icons.sort, size: 17),
|
||||||
|
label: Obx(() => Text(
|
||||||
|
_videoReplyController.sortTypeLabel.value)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -156,28 +194,38 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverList(
|
() => _videoReplyController.replyList.isEmpty
|
||||||
|
? SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 5),
|
||||||
|
)
|
||||||
|
: SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(context, index) {
|
(context, index) {
|
||||||
if (index ==
|
if (index ==
|
||||||
_videoReplyController.replyList.length) {
|
_videoReplyController
|
||||||
|
.replyList.length) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.of(context)
|
bottom: MediaQuery.of(context)
|
||||||
.padding
|
.padding
|
||||||
.bottom),
|
.bottom),
|
||||||
height:
|
height: MediaQuery.of(context)
|
||||||
MediaQuery.of(context).padding.bottom +
|
.padding
|
||||||
|
.bottom +
|
||||||
100,
|
100,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Obx(() => Text(
|
child: Obx(() => Text(
|
||||||
_videoReplyController.noMore.value)),
|
_videoReplyController
|
||||||
|
.noMore.value)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return ReplyItem(
|
return ReplyItem(
|
||||||
replyItem:
|
replyItem: _videoReplyController
|
||||||
_videoReplyController.replyList[index],
|
.replyList[index],
|
||||||
showReplyRow: true,
|
showReplyRow: true,
|
||||||
replyLevel: replyLevel,
|
replyLevel: replyLevel,
|
||||||
replyReply: (replyItem) =>
|
replyReply: (replyItem) =>
|
||||||
@ -187,7 +235,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
childCount:
|
childCount:
|
||||||
_videoReplyController.replyList.length + 1,
|
_videoReplyController.replyList.length +
|
||||||
|
1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -231,7 +280,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return VideoReplyNewDialog(
|
return VideoReplyNewDialog(
|
||||||
replyLevel: '0',
|
|
||||||
oid: IdUtils.bv2av(Get.parameters['bvid']!),
|
oid: IdUtils.bv2av(Get.parameters['bvid']!),
|
||||||
root: 0,
|
root: 0,
|
||||||
parent: 0,
|
parent: 0,
|
||||||
@ -256,3 +304,33 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
final double _minExtent = 45;
|
||||||
|
final double _maxExtent = 45;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
_MySliverPersistentHeaderDelegate({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(
|
||||||
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
|
//创建child子组件
|
||||||
|
//shrinkOffset:child偏移值minExtent~maxExtent
|
||||||
|
//overlapsContent:SliverPersistentHeader覆盖其他子组件返回true,否则返回false
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
//SliverPersistentHeader最大高度
|
||||||
|
@override
|
||||||
|
double get maxExtent => _maxExtent;
|
||||||
|
|
||||||
|
//SliverPersistentHeader最小高度
|
||||||
|
@override
|
||||||
|
double get minExtent => _minExtent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,17 +1,12 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.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: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';
|
||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
import 'package:pilipala/pages/video/detail/controller.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/pages/video/detail/replyNew/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class ReplyItem extends StatelessWidget {
|
class ReplyItem extends StatelessWidget {
|
||||||
@ -33,21 +28,14 @@ class ReplyItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return Material(
|
||||||
onTap: () {},
|
child: InkWell(
|
||||||
child: Column(
|
// 点击整个评论区 评论详情/回复
|
||||||
children: [
|
onTap: () => replyReply!(replyItem),
|
||||||
Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 4, 8, 2),
|
padding: const EdgeInsets.fromLTRB(12, 4, 8, 2),
|
||||||
child: content(context),
|
child: content(context),
|
||||||
),
|
),
|
||||||
// Divider(
|
|
||||||
// height: 1,
|
|
||||||
// indent: 52,
|
|
||||||
// endIndent: 10,
|
|
||||||
// color: Theme.of(context).dividerColor.withOpacity(0.08),
|
|
||||||
// )
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -84,14 +72,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
// child:
|
|
||||||
// NetworkImgLayer(
|
|
||||||
// src: replyItem!.member!.avatar,
|
|
||||||
// width: 30,
|
|
||||||
// height: 30,
|
|
||||||
// type: 'avatar',
|
|
||||||
// ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +103,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
replyItem!.member!.uname!,
|
replyItem!.member!.uname!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: replyItem!.isUp! ||
|
color: replyItem!.isUp! ||
|
||||||
replyItem!.member!.vip!['vipType'] > 0
|
replyItem!.member!.vip!['vipStatus'] > 0
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.outline,
|
: Theme.of(context).colorScheme.outline,
|
||||||
fontSize:
|
fontSize:
|
||||||
@ -196,15 +177,15 @@ class ReplyItem extends StatelessWidget {
|
|||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
style: const TextStyle(height: 1.65),
|
style: const TextStyle(height: 1.75),
|
||||||
maxLines:
|
maxLines:
|
||||||
replyItem!.content!.isText! && replyLevel == '1' ? 6 : 999,
|
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
if (replyItem!.isTop!)
|
if (replyItem!.isTop!)
|
||||||
WidgetSpan(child: UpTag(tagText: 'TOP')),
|
WidgetSpan(child: UpTag(tagText: 'TOP')),
|
||||||
buildContent(context, replyItem!, replyReply),
|
buildContent(context, replyItem!, replyReply, null),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -212,10 +193,12 @@ class ReplyItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
// 操作区域
|
// 操作区域
|
||||||
bottonAction(context, replyItem!.replyControl),
|
bottonAction(context, replyItem!.replyControl),
|
||||||
const SizedBox(height: 3),
|
// 一楼的评论
|
||||||
if (replyItem!.replies!.isNotEmpty && showReplyRow!) ...[
|
if ((replyItem!.replyControl!.isShow! ||
|
||||||
|
replyItem!.replies!.isNotEmpty) &&
|
||||||
|
showReplyRow!) ...[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 2, bottom: 12),
|
padding: const EdgeInsets.only(top: 5, bottom: 12),
|
||||||
child: ReplyItemRow(
|
child: ReplyItemRow(
|
||||||
replies: replyItem!.replies,
|
replies: replyItem!.replies,
|
||||||
replyControl: replyItem!.replyControl,
|
replyControl: replyItem!.replyControl,
|
||||||
@ -281,16 +264,16 @@ class ReplyItem extends StatelessWidget {
|
|||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (builder) {
|
builder: (builder) {
|
||||||
return VideoReplyNewDialog(
|
return VideoReplyNewDialog(
|
||||||
replyLevel: replyLevel,
|
|
||||||
oid: replyItem!.oid,
|
oid: replyItem!.oid,
|
||||||
root: replyItem!.rpid,
|
root: replyItem!.rpid,
|
||||||
parent: replyItem!.rpid,
|
parent: replyItem!.rpid,
|
||||||
replyType: replyType,
|
replyType: replyType,
|
||||||
|
replyItem: replyItem,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).then((value) => {
|
).then((value) => {
|
||||||
// 完成评论,数据添加
|
// 完成评论,数据添加
|
||||||
if (value['data'] != null)
|
if (value != null && value['data'] != null)
|
||||||
{
|
{
|
||||||
addReply!(value['data'])
|
addReply!(value['data'])
|
||||||
// replyControl.replies.add(value['data']),
|
// replyControl.replies.add(value['data']),
|
||||||
@ -358,8 +341,10 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
if (replies!.isNotEmpty)
|
||||||
for (var i = 0; i < replies!.length; i++) ...[
|
for (var i = 0; i < replies!.length; i++) ...[
|
||||||
InkWell(
|
InkWell(
|
||||||
|
// 一楼点击评论展开评论详情
|
||||||
onTap: () => replyReply!(replyItem),
|
onTap: () => replyReply!(replyItem),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -370,10 +355,8 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
i == 0 && (extraRow == 1 || replies!.length > 1) ? 5 : 6,
|
i == 0 && (extraRow == 1 || replies!.length > 1) ? 5 : 6,
|
||||||
),
|
),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
overflow: extraRow == 1
|
overflow: TextOverflow.ellipsis,
|
||||||
? TextOverflow.ellipsis
|
maxLines: 2,
|
||||||
: TextOverflow.visible,
|
|
||||||
maxLines: extraRow == 1 ? 2 : null,
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
@ -401,7 +384,8 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: UpTag(),
|
child: UpTag(),
|
||||||
),
|
),
|
||||||
buildContent(context, replies![i], replyReply),
|
buildContent(
|
||||||
|
context, replies![i], replyReply, replyItem),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -410,6 +394,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
if (extraRow == 1)
|
if (extraRow == 1)
|
||||||
InkWell(
|
InkWell(
|
||||||
|
// 一楼点击【共xx条回复】展开评论详情
|
||||||
onTap: () => replyReply!(replyItem),
|
onTap: () => replyReply!(replyItem),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -441,7 +426,11 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineSpan buildContent(BuildContext context, replyItem, replyReply) {
|
InlineSpan buildContent(
|
||||||
|
BuildContext context, replyItem, replyReply, fReplyItem) {
|
||||||
|
// replyItem 当前回复内容
|
||||||
|
// replyReply 查看二楼回复(回复详情)回调
|
||||||
|
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
||||||
var content = replyItem.content;
|
var content = replyItem.content;
|
||||||
if (content.emote.isEmpty &&
|
if (content.emote.isEmpty &&
|
||||||
content.atNameToMid.isEmpty &&
|
content.atNameToMid.isEmpty &&
|
||||||
@ -450,7 +439,9 @@ InlineSpan buildContent(BuildContext context, replyItem, replyReply) {
|
|||||||
content.pictures.isEmpty) {
|
content.pictures.isEmpty) {
|
||||||
return TextSpan(
|
return TextSpan(
|
||||||
text: content.message,
|
text: content.message,
|
||||||
recognizer: TapGestureRecognizer()..onTap = () => replyReply(replyItem),
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap =
|
||||||
|
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
List<InlineSpan> spanChilds = [];
|
List<InlineSpan> spanChilds = [];
|
||||||
@ -510,11 +501,19 @@ InlineSpan buildContent(BuildContext context, replyItem, replyReply) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
spanChilds.add(TextSpan(text: matchStr));
|
spanChilds.add(TextSpan(
|
||||||
|
text: matchStr,
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () =>
|
||||||
|
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
return matchStr;
|
return matchStr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
spanChilds.add(TextSpan(text: matchStr));
|
spanChilds.add(TextSpan(
|
||||||
|
text: matchStr,
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () =>
|
||||||
|
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
return matchStr;
|
return matchStr;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@ -595,7 +594,11 @@ InlineSpan buildContent(BuildContext context, replyItem, replyReply) {
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
onNonMatch: (String str) {
|
onNonMatch: (String str) {
|
||||||
spanChilds.add(TextSpan(text: str));
|
spanChilds.add(TextSpan(
|
||||||
|
text: str,
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () => replyReply(
|
||||||
|
replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -628,7 +631,11 @@ InlineSpan buildContent(BuildContext context, replyItem, replyReply) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) {
|
if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) {
|
||||||
spanChilds.add(TextSpan(text: str));
|
spanChilds.add(TextSpan(
|
||||||
|
text: str,
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () =>
|
||||||
|
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -12,16 +12,16 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
class VideoReplyNewDialog extends StatefulWidget {
|
class VideoReplyNewDialog extends StatefulWidget {
|
||||||
int? oid;
|
int? oid;
|
||||||
int? root;
|
int? root;
|
||||||
String? replyLevel;
|
|
||||||
int? parent;
|
int? parent;
|
||||||
ReplyType? replyType;
|
ReplyType? replyType;
|
||||||
|
ReplyItemModel? replyItem;
|
||||||
|
|
||||||
VideoReplyNewDialog({
|
VideoReplyNewDialog({
|
||||||
this.oid,
|
this.oid,
|
||||||
this.root,
|
this.root,
|
||||||
this.replyLevel,
|
|
||||||
this.parent,
|
this.parent,
|
||||||
this.replyType,
|
this.replyType,
|
||||||
|
this.replyItem,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -36,14 +36,12 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
double _keyboardHeight = 0.0; // 键盘高度
|
double _keyboardHeight = 0.0; // 键盘高度
|
||||||
final _debouncer = Debouncer(milliseconds: 100); // 设置延迟时间
|
final _debouncer = Debouncer(milliseconds: 100); // 设置延迟时间
|
||||||
bool ableClean = false;
|
bool ableClean = false;
|
||||||
bool autoFocus = false;
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
Box localCache = GStrorage.localCache;
|
Box localCache = GStrorage.localCache;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
// TODO: implement initState
|
|
||||||
super.initState();
|
super.initState();
|
||||||
// 监听输入框聚焦
|
// 监听输入框聚焦
|
||||||
// replyContentFocusNode.addListener(_onFocus);
|
// replyContentFocusNode.addListener(_onFocus);
|
||||||
@ -74,14 +72,18 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
oid: widget.oid!,
|
oid: widget.oid!,
|
||||||
root: widget.root!,
|
root: widget.root!,
|
||||||
parent: widget.parent!,
|
parent: widget.parent!,
|
||||||
message: message,
|
message: widget.replyItem != null && widget.replyItem!.root != 0
|
||||||
|
? ' 回复 @${widget.replyItem!.member!.uname!} : $message'
|
||||||
|
: message,
|
||||||
);
|
);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
SmartDialog.showToast(result['data']['success_toast']);
|
SmartDialog.showToast(result['data']['success_toast']);
|
||||||
Get.back(result: {
|
Get.back(result: {
|
||||||
'data': ReplyItemModel.fromJson(result['data']['reply'], ''),
|
'data': ReplyItemModel.fromJson(result['data']['reply'], ''),
|
||||||
});
|
});
|
||||||
} else {}
|
} else {
|
||||||
|
SmartDialog.showToast(result['msg']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -103,6 +105,12 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_replyContentController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@ -123,7 +123,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
addReply: (replyItem) {
|
addReply: (replyItem) {
|
||||||
_videoReplyReplyController.replyList.add(replyItem);
|
_videoReplyReplyController.replyList.add(replyItem);
|
||||||
},
|
},
|
||||||
replyType: ReplyType.video,
|
replyType: widget.replyType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
@ -167,8 +167,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Material(
|
return ReplyItem(
|
||||||
child: ReplyItem(
|
|
||||||
replyItem: _videoReplyReplyController
|
replyItem: _videoReplyReplyController
|
||||||
.replyList[index],
|
.replyList[index],
|
||||||
replyLevel: '2',
|
replyLevel: '2',
|
||||||
@ -176,7 +175,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
addReply: (replyItem) {
|
addReply: (replyItem) {
|
||||||
_videoReplyReplyController.replyList
|
_videoReplyReplyController.replyList
|
||||||
.add(replyItem);
|
.add(replyItem);
|
||||||
}),
|
},
|
||||||
|
replyType: widget.replyType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -122,9 +122,9 @@ class Utils {
|
|||||||
return timeStr;
|
return timeStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formatType == 'list' && int.parse(DD) > DateTime.now().day - 2) {
|
// if (formatType == 'list' && int.parse(DD) > DateTime.now().day - 2) {
|
||||||
return '昨天';
|
// return '昨天';
|
||||||
}
|
// }
|
||||||
|
|
||||||
date = date
|
date = date
|
||||||
.replaceAll('YY', YY)
|
.replaceAll('YY', YY)
|
||||||
@ -133,8 +133,12 @@ class Utils {
|
|||||||
.replaceAll('hh', hh)
|
.replaceAll('hh', hh)
|
||||||
.replaceAll('mm', mm)
|
.replaceAll('mm', mm)
|
||||||
.replaceAll('ss', ss);
|
.replaceAll('ss', ss);
|
||||||
if (int.parse(DD) < DateTime.now().day) {
|
if (int.parse(YY) == DateTime.now().year &&
|
||||||
return date.split(' ')[0];
|
int.parse(MM) == DateTime.now().month) {
|
||||||
|
// 当天
|
||||||
|
if (int.parse(DD) == DateTime.now().day) {
|
||||||
|
return date.split(' ')[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user