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; double insetBottom = 0; var shouldRefresh = true; var pageList = RxList(); var continueInfo = RxMap(); var errorList = RxList(); 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(); flc = Get.find(); loadNextPages(); var homeController = Get.find(); homeController.onRefresh = refreshPages; } @override void dispose() { var homeController = Get.find(); homeController.onRefresh = refreshPages; super.dispose(); } Future refreshPages() async { continueInfo.clear(); errorList.clear(); hasNextPage.value = true; shouldRefresh = true; await loadNextPages(); } Future loadNextPages() async { // 如果没有下一页,则阻止加载 if (!hasNextPage.value) return; isLoading.value = true; try { var rcListRes = await MWApiList.getMixedRecentChanges( limit: 10, continueInfo: continueInfo.isNotEmpty ? continueInfo : null); var pageIds = rcListRes.data.map((rcInfo) => rcInfo.pageid).toList(); var pageListRes = await MWApiList.getPageInfoList( pageids: pageIds, extractChars: 515, getInWatchlist: uc!.isLoggedIn); 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 ?? {}; } catch (err) { hasNextPage.value = false; if (shouldRefresh) { pageList.clear(); } } finally { isLoading.value = false; } } Future reload() async { if (_handleReload != null) { await _handleReload!(); } } void setReloadHandler(Function? reloadHandler) { _handleReload = reloadHandler; } } class RecentPageList extends StatelessWidget { final ScrollController? scrollController; final double insetBottom; const RecentPageList( {super.key, this.scrollController, this.insetBottom = 0}); 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 flc) { return PageCard( key: ValueKey("rpl-card-$index"), pageInfo: c.pageList[index], isFavorite: flc.isFavorite(c.pageList[index]), onSetFavorite: flc.setFavoriteImmediate, ); } SliverChildBuilderDelegate _buildPageListDelegate() { var c = Get.find(); var flc = Get.find(); return SliverChildBuilderDelegate( childCount: c.pageList.length + 1, (context, index) { if (index == 0) { // 第一页数据,直接加载 return Obx(() { if (c.pageList.isEmpty) { return _buildSkeletonList(); } else { return _buildPageCard(index, c.pageList[index], c, flc); } }); } // 后续页数据 if (index < c.pageList.length) { return Obx(() => _buildPageCard(index, c.pageList[index], c, flc)); } else if (index == c.pageList.length) { // 显示加载动画 return Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Center( child: Obx( () => c.isLoading.value ? Padding( padding: EdgeInsets.only(bottom: insetBottom), child: const CupertinoActivityIndicator( radius: 14, ), ) : SizedBox( width: 28, height: 28 + insetBottom, ), ), ), ); } return null; }, ); } @override Widget build(BuildContext context) { Get.put(RecentPageListController(scrollController: scrollController)); return Obx( () => SliverList( delegate: _buildPageListDelegate(), ), ); } }