refactor: reply

This commit is contained in:
bggRGjQaUbCoE
2024-09-08 16:14:57 +08:00
parent 2bcddc1097
commit bb6aaaa480
20 changed files with 640 additions and 935 deletions

View File

@@ -1,31 +1,18 @@
import 'package:flutter/material.dart';
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/pages/common/reply_controller.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:PiliPalaX/http/html.dart';
import 'package:PiliPalaX/http/reply.dart';
import 'package:PiliPalaX/models/common/reply_sort_type.dart';
import 'package:PiliPalaX/models/video/reply/item.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import 'package:PiliPalaX/utils/storage.dart';
class HtmlRenderController extends GetxController {
class HtmlRenderController extends ReplyController {
late String id;
late String dynamicType;
late int type;
RxInt oid = (-1).obs;
late Map response;
int? floor;
String nextOffset = "";
bool isLoadingMore = false;
RxString noMore = ''.obs;
RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs;
RxInt acount = 0.obs;
final ScrollController scrollController = ScrollController();
late ReplySortType _sortType;
late RxString sortTypeTitle;
late RxString sortTypeLabel;
Box setting = GStorage.setting;
RxBool loaded = false.obs;
@override
void onInit() {
@@ -33,19 +20,12 @@ class HtmlRenderController extends GetxController {
id = Get.parameters['id']!;
dynamicType = Get.parameters['dynamicType']!;
type = dynamicType == 'picture' ? 11 : 12;
int defaultReplySortIndex =
setting.get(SettingBoxKey.replySortType, defaultValue: 1) as int;
if (defaultReplySortIndex == 2) {
setting.put(SettingBoxKey.replySortType, 0);
defaultReplySortIndex = 0;
}
_sortType = ReplySortType.values[defaultReplySortIndex];
sortTypeLabel = _sortType.labels.obs;
sortTypeTitle = _sortType.titles.obs;
reqHtml();
}
// 请求动态内容
Future reqHtml(id) async {
Future reqHtml() async {
late dynamic res;
if (dynamicType == 'opus' || dynamicType == 'picture') {
res = await HtmlHttp.reqHtml(id, dynamicType);
@@ -54,72 +34,17 @@ class HtmlRenderController extends GetxController {
}
response = res;
oid.value = res['commentId'];
queryReplyList(reqType: 'init');
return res;
queryData();
if (res['status'] == true) {
loaded.value = true;
}
}
// 请求评论
Future queryReplyList({reqType = 'init'}) async {
if (reqType == 'init') {
nextOffset = "";
noMore.value = "";
}
if (noMore.value == '没有更多了') return;
var res = await ReplyHttp.replyList(
oid: oid.value,
nextOffset: nextOffset,
type: type,
sort: _sortType.index,
);
if (res['status']) {
List<ReplyItemModel> replies = res['data'].replies;
acount.value = res['data'].cursor.allCount ?? 0;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) {
noMore.value = '加载中...';
if (res['data'].cursor.isEnd == true) {
noMore.value = '没有更多了';
}
} else {
noMore.value =
nextOffset == "" && reqType == 'init' ? '还没有评论' : '没有更多了';
}
if (reqType == 'init') {
// 添加置顶回复
if (res['data'].upper.top != null) {
bool flag = res['data']
.topReplies
.any((reply) => reply.rpid == res['data'].upper.top.rpid);
if (!flag) {
replies.insert(0, res['data'].upper.top);
}
}
replies.insertAll(0, res['data'].topReplies);
replyList.value = replies;
} else {
replyList.addAll(replies);
}
}
isLoadingMore = false;
return res;
}
// 排序搜索评论
queryBySort() {
feedBack();
switch (_sortType) {
case ReplySortType.time:
_sortType = ReplySortType.like;
break;
case ReplySortType.like:
_sortType = ReplySortType.time;
break;
default:
}
sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels;
nextOffset = "";
replyList.clear();
queryReplyList(reqType: 'init');
}
@override
Future<LoadingState> customGetData() => ReplyHttp.replyList(
oid: oid.value,
nextOffset: nextOffset,
type: type,
sort: sortType.index,
);
}

View File

@@ -1,5 +1,7 @@
import 'dart:math';
import 'package:PiliPalaX/common/widgets/http_error.dart';
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
@@ -34,8 +36,6 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
late String dynamicType;
late int type;
bool _isFabVisible = true;
late final Future _futureBuilderFuture;
late ScrollController scrollController;
late AnimationController fabAnimationCtr;
@override
@@ -46,31 +46,29 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
url = Get.parameters['url']!;
dynamicType = Get.parameters['dynamicType']!;
type = dynamicType == 'picture' ? 11 : 12;
_futureBuilderFuture = _htmlRenderCtr.reqHtml(id);
fabAnimationCtr = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
fabAnimationCtr.forward();
scrollListener();
}
@override
void dispose() {
fabAnimationCtr.dispose();
scrollController.removeListener(() {});
scrollController.dispose();
_htmlRenderCtr.scrollController.removeListener(() {});
super.dispose();
}
void scrollListener() {
scrollController = _htmlRenderCtr.scrollController;
scrollController.addListener(
_htmlRenderCtr.scrollController.addListener(
() {
// 分页加载
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
if (_htmlRenderCtr.scrollController.position.pixels >=
_htmlRenderCtr.scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
_htmlRenderCtr.queryReplyList(reqType: 'onLoad');
_htmlRenderCtr.onLoadMore();
});
}
@@ -85,7 +83,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
// fab按钮
final ScrollDirection direction =
scrollController.position.userScrollDirection;
_htmlRenderCtr.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
_showFab();
} else if (direction == ScrollDirection.reverse) {
@@ -161,7 +159,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
PopupMenuItem(
onTap: () => {
_htmlRenderCtr.reqHtml(id),
_htmlRenderCtr.reqHtml(),
},
child: const Row(
mainAxisSize: MainAxisSize.min,
@@ -225,23 +223,17 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
double padding = max(context.width / 2 - Grid.maxRowWidth, 0);
return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: SingleChildScrollView(
controller: orientation == Orientation.portrait
? scrollController
: ScrollController(),
child: Padding(
child: SingleChildScrollView(
controller: orientation == Orientation.portrait
? _htmlRenderCtr.scrollController
: ScrollController(),
child: Padding(
padding: orientation == Orientation.portrait
? EdgeInsets.symmetric(horizontal: padding)
: EdgeInsets.only(left: padding / 2),
child: FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
var data = snapshot.data;
// fabAnimationCtr.forward();
if (data != null && data['status']) {
return Column(
child: Obx(
() => _htmlRenderCtr.loaded.value
? Column(
children: [
Padding(
padding:
@@ -306,35 +298,38 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
.dividerColor
.withOpacity(0.05)),
replyHeader(),
replyList(),
Obx(
() => replyList(
_htmlRenderCtr.loadingState.value),
),
]
],
);
} else {
return const Text('error');
}
} else {
// 骨架屏
return const SizedBox();
}
},
)),
)),
)
: const SizedBox(),
),
),
),
),
if (orientation == Orientation.landscape) ...[
VerticalDivider(
thickness: 8,
color: Theme.of(context).dividerColor.withOpacity(0.05)),
Expanded(
child: SingleChildScrollView(
controller: scrollController,
child: Padding(
padding: EdgeInsets.only(right: padding / 2),
child: Column(
children: [
replyHeader(),
replyList(),
],
))))
child: SingleChildScrollView(
controller: _htmlRenderCtr.scrollController,
child: Padding(
padding: EdgeInsets.only(right: padding / 2),
child: Column(
children: [
replyHeader(),
Obx(
() => replyList(_htmlRenderCtr.loadingState.value),
),
],
),
),
),
),
]
]);
}),
@@ -365,13 +360,18 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
);
},
).then(
(value) => {
(value) {
// 完成评论,数据添加
if (value != null && value['data'] != null)
{
_htmlRenderCtr.replyList.insert(0, value['data']),
_htmlRenderCtr.acount.value++
}
if (value != null && value['data'] != null) {
_htmlRenderCtr.count.value++;
List list = _htmlRenderCtr.loadingState.value is Success
? (_htmlRenderCtr.loadingState.value as Success)
.response
: [];
list.insert(0, value['data']);
_htmlRenderCtr.loadingState.value =
LoadingState.success(list);
}
},
);
},
@@ -385,54 +385,59 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
);
}
Obx replyList() {
return Obx(
() => _htmlRenderCtr.replyList.isEmpty && _htmlRenderCtr.isLoadingMore
? ListView.builder(
itemCount: 5,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const VideoReplySkeleton();
},
)
: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _htmlRenderCtr.replyList.length + 1,
itemBuilder: (context, index) {
if (index == _htmlRenderCtr.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
height: MediaQuery.of(context).padding.bottom + 100,
child: Center(
child: Obx(
() => Text(
_htmlRenderCtr.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.outline,
),
Widget replyList(LoadingState loadingState) {
return loadingState is Success
? ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: loadingState.response.length + 1,
itemBuilder: (context, index) {
if (index == loadingState.response.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
height: MediaQuery.of(context).padding.bottom + 100,
child: Center(
child: Obx(
() => Text(
_htmlRenderCtr.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.outline,
),
),
),
);
} else {
return ReplyItem(
replyItem: _htmlRenderCtr.replyList[index],
showReplyRow: true,
replyLevel: '1',
replyReply: (replyItem) => replyReply(replyItem),
replyType: ReplyType.values[type],
addReply: (replyItem) {
_htmlRenderCtr.replyList[index].replies!.add(replyItem);
},
);
}
},
),
);
),
);
} else {
return ReplyItem(
replyItem: loadingState.response[index],
showReplyRow: true,
replyLevel: '1',
replyReply: (replyItem) => replyReply(replyItem),
replyType: ReplyType.values[type],
addReply: (replyItem) {
loadingState.response[index].replies!.add(replyItem);
},
);
}
},
)
: loadingState is Error
? HttpError(
errMsg: _htmlRenderCtr.loadingState.value is Error
? (_htmlRenderCtr.loadingState.value as Error).errMsg
: '没有相关数据',
fn: _htmlRenderCtr.onReload,
)
: ListView.builder(
itemCount: 5,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const VideoReplySkeleton();
},
);
}
Container replyHeader() {