🐛 Fixes in new pagination list

This commit is contained in:
2025-12-05 00:10:25 +08:00
parent 6aba84e506
commit c585522c35
4 changed files with 95 additions and 94 deletions

View File

@@ -45,11 +45,12 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
return await fetch();
});
state = newState;
fetchedCount = newState.value?.length ?? 0;
}
@override
Future<void> fetchFurther() async {
if (!fetchedAll) return;
if (fetchedAll) return;
state = AsyncLoading<List<T>>();
@@ -67,6 +68,7 @@ mixin AsyncPaginationFilter<F, T> on AsyncPaginationController<T>
implements PaginationFiltered<F> {
@override
Future<void> applyFilter(F filter) async {
if (currentFilter == filter) return;
// Reset the data
totalCount = null;
fetchedCount = 0;

View File

@@ -23,7 +23,11 @@ class ActivityListNotifier extends AsyncNotifier<List<SnTimelineEvent>>
final client = ref.read(apiClientProvider);
final cursor =
state.valueOrNull?.lastOrNull?.createdAt.toUtc().toIso8601String();
state.isLoading
? null
: state.valueOrNull?.lastOrNull?.createdAt
.toUtc()
.toIso8601String();
final queryParameters = {
if (cursor != null) 'cursor': cursor,

View File

@@ -72,8 +72,8 @@ class ExploreScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController(initialLength: 3);
final currentFilter = useState<String?>(null);
final notifier = ref.watch(activityListNotifierProvider.notifier);
useEffect(() {
// Set FAB type to chat
@@ -91,33 +91,10 @@ class ExploreScreen extends HookConsumerWidget {
};
}, []);
useEffect(() {
void listener() {
switch (tabController.index) {
case 0:
currentFilter.value = null;
break;
case 1:
currentFilter.value = 'subscriptions';
break;
case 2:
currentFilter.value = 'friends';
break;
void handleFilterChange(String? filter) {
currentFilter.value = filter;
notifier.applyFilter(filter);
}
}
tabController.addListener(listener);
return () => tabController.removeListener(listener);
}, [tabController]);
final notifier = ref.watch(activityListNotifierProvider.notifier);
useEffect(() {
Future(() {
notifier.applyFilter(currentFilter.value);
});
return null;
}, [currentFilter.value]);
// Listen for post creation events to refresh activities
useEffect(() {
@@ -149,35 +126,42 @@ class ExploreScreen extends HookConsumerWidget {
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Row(
children: [
Expanded(
child: TabBar(
controller: tabController,
tabAlignment: TabAlignment.start,
isScrollable: true,
dividerColor: Colors.transparent,
labelPadding: const EdgeInsets.symmetric(horizontal: 12),
tabs: [
Tab(
icon: Tooltip(
message: 'explore'.tr(),
child: Icon(Symbols.explore),
Row(
spacing: 4,
children: [
IconButton(
onPressed: () => handleFilterChange(null),
icon: Icon(Symbols.explore),
tooltip: 'explore'.tr(),
isSelected: currentFilter.value == null,
color:
currentFilter.value == null
? Theme.of(context).colorScheme.primary
: null,
),
IconButton(
onPressed: () => handleFilterChange('subscriptions'),
icon: Icon(Symbols.subscriptions),
tooltip: 'exploreFilterSubscriptions'.tr(),
isSelected: currentFilter.value == 'subscriptions',
color:
currentFilter.value == 'subscriptions'
? Theme.of(context).colorScheme.primary
: null,
),
Tab(
icon: Tooltip(
message: 'exploreFilterSubscriptions'.tr(),
child: Icon(Symbols.subscriptions),
),
),
Tab(
icon: Tooltip(
message: 'exploreFilterFriends'.tr(),
child: Icon(Symbols.people),
),
IconButton(
onPressed: () => handleFilterChange('friends'),
icon: Icon(Symbols.people),
tooltip: 'exploreFilterFriends'.tr(),
isSelected: currentFilter.value == 'friends',
color:
currentFilter.value == 'friends'
? Theme.of(context).colorScheme.primary
: null,
),
],
),
),
const Spacer(),
IconButton(
onPressed: () {
context.pushNamed('articles');
@@ -241,10 +225,13 @@ class ExploreScreen extends HookConsumerWidget {
tooltip: 'search'.tr(),
),
],
).padding(horizontal: 8),
).padding(horizontal: 8, vertical: 4),
);
final appBar = isWide ? null : _buildAppBar(tabController, context);
final appBar =
isWide
? null
: _buildAppBar(currentFilter.value, handleFilterChange, context);
final dragging = useState(false);
@@ -324,6 +311,7 @@ class ExploreScreen extends HookConsumerWidget {
notifier: activityListNotifierProvider.notifier,
// Sliver list cannot provide refresh handled by the pagination list
isRefreshable: false,
isSliver: true,
contentBuilder: (data) => _ActivityListView(data: data, isWide: isWide),
);
}
@@ -433,7 +421,8 @@ class ExploreScreen extends HookConsumerWidget {
}
PreferredSizeWidget _buildAppBar(
TabController tabController,
String? currentFilter,
void Function(String?) handleFilterChange,
BuildContext context,
) {
final foregroundColor = Theme.of(context).appBarTheme.foregroundColor;
@@ -449,38 +438,25 @@ class ExploreScreen extends HookConsumerWidget {
),
child: Row(
children: [
Expanded(
child: TabBar(
controller: tabController,
tabAlignment: TabAlignment.start,
isScrollable: true,
dividerColor: Colors.transparent,
labelPadding: const EdgeInsets.symmetric(horizontal: 12),
tabs: [
Tab(
icon: Tooltip(
message: 'explore'.tr(),
child: Icon(Symbols.explore, color: foregroundColor),
IconButton(
onPressed: () => handleFilterChange(null),
icon: Icon(Symbols.explore, color: foregroundColor),
tooltip: 'explore'.tr(),
isSelected: currentFilter == null,
),
IconButton(
onPressed: () => handleFilterChange('subscriptions'),
icon: Icon(Symbols.subscriptions, color: foregroundColor),
tooltip: 'exploreFilterSubscriptions'.tr(),
isSelected: currentFilter == 'subscriptions',
),
Tab(
icon: Tooltip(
message: 'exploreFilterSubscriptions'.tr(),
child: Icon(
Symbols.subscriptions,
color: foregroundColor,
),
),
),
Tab(
icon: Tooltip(
message: 'exploreFilterFriends'.tr(),
child: Icon(Symbols.people, color: foregroundColor),
),
),
],
),
IconButton(
onPressed: () => handleFilterChange('friends'),
icon: Icon(Symbols.people, color: foregroundColor),
tooltip: 'exploreFilterFriends'.tr(),
isSelected: currentFilter == 'friends',
),
const Spacer(),
IconButton(
onPressed: () {
context.pushNamed('articles');

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/paging.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
import 'package:island/widgets/response.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class PaginationList<T> extends HookConsumerWidget {
@@ -34,7 +35,7 @@ class PaginationList<T> extends HookConsumerWidget {
if (scrollInfo is ScrollEndNotification &&
scrollInfo.metrics.axisDirection == AxisDirection.down &&
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
if (!noti.fetchedAll) {
if (!noti.fetchedAll && !data.isLoading && !data.hasError) {
noti.fetchFurther();
}
}
@@ -54,24 +55,42 @@ class PaginationWidget<T> extends HookConsumerWidget {
final Refreshable<PaginationController<T>> notifier;
final Widget Function(List<T>) contentBuilder;
final bool isRefreshable;
final bool isSliver;
final bool showDefaultWidgets;
const PaginationWidget({
super.key,
required this.provider,
required this.notifier,
required this.contentBuilder,
this.isRefreshable = true,
this.isSliver = false,
this.showDefaultWidgets = true,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final data = ref.watch(provider);
final noti = ref.watch(notifier);
if (data.isLoading) {
final content = ResponseLoadingWidget();
return isSliver ? SliverFillRemaining(child: content) : content;
}
if (data.hasError) {
final content = ResponseErrorWidget(
error: data.error,
onRetry: noti.refresh,
);
return isSliver ? SliverFillRemaining(child: content) : content;
}
final content = NotificationListener(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo is ScrollEndNotification &&
scrollInfo.metrics.axisDirection == AxisDirection.down &&
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
if (!noti.fetchedAll) {
if (!noti.fetchedAll && !data.isLoading && !data.hasError) {
noti.fetchFurther();
}
}