|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:isekai_wiki/api/mw/list.dart';
|
|
|
|
import 'package:isekai_wiki/api/mw/mw_api.dart';
|
|
|
|
import 'package:isekai_wiki/api/response/page_info.dart';
|
|
|
|
import 'package:isekai_wiki/components/page_card.dart';
|
|
|
|
import 'package:isekai_wiki/models/favorite_list.dart';
|
|
|
|
import 'package:isekai_wiki/models/user.dart';
|
|
|
|
import 'package:isekai_wiki/pages/home.dart';
|
|
|
|
|
|
|
|
class RecentPageListController extends GetxController {
|
|
|
|
Function? _handleReload;
|
|
|
|
ScrollController? scrollController;
|
|
|
|
UserController? uc;
|
|
|
|
FavoriteListController? flc;
|
|
|
|
|
|
|
|
var shouldRefresh = true;
|
|
|
|
|
|
|
|
var pageList = RxList<PageInfo>();
|
|
|
|
var continueInfo = RxMap<String, String>();
|
|
|
|
var errorList = RxList<MWError>();
|
|
|
|
|
|
|
|
var hasNextPage = true.obs;
|
|
|
|
var isLoading = false.obs;
|
|
|
|
|
|
|
|
RecentPageListController({this.scrollController});
|
|
|
|
|
|
|
|
@override
|
|
|
|
void onInit() {
|
|
|
|
scrollController?.addListener(() {
|
|
|
|
if (scrollController!.position.pixels > scrollController!.position.maxScrollExtent - 10) {
|
|
|
|
// 滚动到底部
|
|
|
|
if (hasNextPage.value && !isLoading.value) {
|
|
|
|
loadNextPages();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
super.onInit();
|
|
|
|
|
|
|
|
uc = Get.find<UserController>();
|
|
|
|
flc = Get.find<FavoriteListController>();
|
|
|
|
|
|
|
|
loadNextPages();
|
|
|
|
|
|
|
|
var homeController = Get.find<HomeController>();
|
|
|
|
homeController.onRefresh = refreshPages;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
var homeController = Get.find<HomeController>();
|
|
|
|
homeController.onRefresh = refreshPages;
|
|
|
|
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> refreshPages() async {
|
|
|
|
continueInfo.clear();
|
|
|
|
errorList.clear();
|
|
|
|
hasNextPage.value = true;
|
|
|
|
shouldRefresh = true;
|
|
|
|
|
|
|
|
await loadNextPages();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> loadNextPages() async {
|
|
|
|
// 如果没有下一页,则阻止加载
|
|
|
|
if (!hasNextPage.value) return;
|
|
|
|
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
|
|
var rcListRes = await MWApiList.getMixedRecentChanges(
|
|
|
|
limit: 10, continueInfo: continueInfo.isNotEmpty ? continueInfo : null);
|
|
|
|
if (!rcListRes.ok) {
|
|
|
|
// 发生错误,展示错误列表
|
|
|
|
errorList.value = rcListRes.errorList ?? [];
|
|
|
|
isLoading.value = false;
|
|
|
|
hasNextPage.value = false;
|
|
|
|
if (shouldRefresh) {
|
|
|
|
pageList.clear();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var pageIds = rcListRes.data!.map((rcInfo) => rcInfo.pageid).toList();
|
|
|
|
var pageListRes = await MWApiList.getPageInfoList(
|
|
|
|
pageids: pageIds, extractChars: 515, getInWatchlist: uc!.isLoggedIn);
|
|
|
|
if (!pageListRes.ok) {
|
|
|
|
// 发生错误,展示错误列表
|
|
|
|
errorList.value = pageListRes.errorList ?? [];
|
|
|
|
isLoading.value = false;
|
|
|
|
hasNextPage.value = false;
|
|
|
|
if (shouldRefresh) {
|
|
|
|
pageList.clear();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uc!.isLoggedIn) {
|
|
|
|
// 更新收藏列表信息
|
|
|
|
flc!.updateFromPageList(pageListRes.data!);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 加载完成,设置信息
|
|
|
|
if (shouldRefresh) {
|
|
|
|
// 初次加载,替换全部数据
|
|
|
|
pageList.value = pageListRes.data!;
|
|
|
|
shouldRefresh = false;
|
|
|
|
} else {
|
|
|
|
pageList.addAll(pageListRes.data!);
|
|
|
|
}
|
|
|
|
hasNextPage.value = rcListRes.continueInfo != null;
|
|
|
|
continueInfo.value = rcListRes.continueInfo ?? {};
|
|
|
|
isLoading.value = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> reload() async {
|
|
|
|
if (_handleReload != null) {
|
|
|
|
await _handleReload!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setReloadHandler(Function? reloadHandler) {
|
|
|
|
_handleReload = reloadHandler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RecentPageList extends StatelessWidget {
|
|
|
|
final ScrollController? scrollController;
|
|
|
|
|
|
|
|
const RecentPageList({
|
|
|
|
super.key,
|
|
|
|
this.scrollController,
|
|
|
|
});
|
|
|
|
|
|
|
|
Widget _buildSkeletonList() {
|
|
|
|
return Column(
|
|
|
|
key: key,
|
|
|
|
children: [
|
|
|
|
for (var i = 0; i < 6; i++) PageCard(key: ValueKey("rpl-card-$i"), isLoading: true),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildPageCard(
|
|
|
|
int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController fc) {
|
|
|
|
return PageCard(
|
|
|
|
key: ValueKey("rpl-card-$index"),
|
|
|
|
pageInfo: c.pageList[index],
|
|
|
|
isFavorite: fc.isFavorite(c.pageList[index]),
|
|
|
|
onSetFavorite: fc.setFavorite,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
SliverChildBuilderDelegate _buildPageListDelegate() {
|
|
|
|
var c = Get.find<RecentPageListController>();
|
|
|
|
var flc = Get.find<FavoriteListController>();
|
|
|
|
|
|
|
|
return SliverChildBuilderDelegate(
|
|
|
|
childCount: c.pageList.length + 1,
|
|
|
|
(context, index) {
|
|
|
|
if (index == 0) {
|
|
|
|
// 第一页数据,直接加载
|
|
|
|
if (c.pageList.isEmpty) {
|
|
|
|
return _buildSkeletonList();
|
|
|
|
} else {
|
|
|
|
return _buildPageCard(index, c.pageList[index], c, flc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 后续页数据
|
|
|
|
if (index < c.pageList.length) {
|
|
|
|
return _buildPageCard(index, c.pageList[index], c, flc);
|
|
|
|
} else if (index == c.pageList.length) {
|
|
|
|
// 显示加载动画
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
|
|
child: Center(
|
|
|
|
child: c.isLoading.value
|
|
|
|
? const CupertinoActivityIndicator(
|
|
|
|
radius: 14,
|
|
|
|
)
|
|
|
|
: const SizedBox(
|
|
|
|
width: 28,
|
|
|
|
height: 28,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Get.put(RecentPageListController(scrollController: scrollController));
|
|
|
|
|
|
|
|
return Obx(
|
|
|
|
() => SliverList(
|
|
|
|
delegate: _buildPageListDelegate(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|