🐛 Fixes in new pagination list
This commit is contained in:
@@ -45,11 +45,12 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
|||||||
return await fetch();
|
return await fetch();
|
||||||
});
|
});
|
||||||
state = newState;
|
state = newState;
|
||||||
|
fetchedCount = newState.value?.length ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> fetchFurther() async {
|
Future<void> fetchFurther() async {
|
||||||
if (!fetchedAll) return;
|
if (fetchedAll) return;
|
||||||
|
|
||||||
state = AsyncLoading<List<T>>();
|
state = AsyncLoading<List<T>>();
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ mixin AsyncPaginationFilter<F, T> on AsyncPaginationController<T>
|
|||||||
implements PaginationFiltered<F> {
|
implements PaginationFiltered<F> {
|
||||||
@override
|
@override
|
||||||
Future<void> applyFilter(F filter) async {
|
Future<void> applyFilter(F filter) async {
|
||||||
|
if (currentFilter == filter) return;
|
||||||
// Reset the data
|
// Reset the data
|
||||||
totalCount = null;
|
totalCount = null;
|
||||||
fetchedCount = 0;
|
fetchedCount = 0;
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ class ActivityListNotifier extends AsyncNotifier<List<SnTimelineEvent>>
|
|||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
|
|
||||||
final cursor =
|
final cursor =
|
||||||
state.valueOrNull?.lastOrNull?.createdAt.toUtc().toIso8601String();
|
state.isLoading
|
||||||
|
? null
|
||||||
|
: state.valueOrNull?.lastOrNull?.createdAt
|
||||||
|
.toUtc()
|
||||||
|
.toIso8601String();
|
||||||
|
|
||||||
final queryParameters = {
|
final queryParameters = {
|
||||||
if (cursor != null) 'cursor': cursor,
|
if (cursor != null) 'cursor': cursor,
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final tabController = useTabController(initialLength: 3);
|
|
||||||
final currentFilter = useState<String?>(null);
|
final currentFilter = useState<String?>(null);
|
||||||
|
final notifier = ref.watch(activityListNotifierProvider.notifier);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
// Set FAB type to chat
|
// Set FAB type to chat
|
||||||
@@ -91,33 +91,10 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() {
|
void handleFilterChange(String? filter) {
|
||||||
void listener() {
|
currentFilter.value = filter;
|
||||||
switch (tabController.index) {
|
notifier.applyFilter(filter);
|
||||||
case 0:
|
|
||||||
currentFilter.value = null;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
currentFilter.value = 'subscriptions';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
currentFilter.value = 'friends';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Listen for post creation events to refresh activities
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@@ -149,35 +126,42 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Row(
|
||||||
child: TabBar(
|
spacing: 4,
|
||||||
controller: tabController,
|
children: [
|
||||||
tabAlignment: TabAlignment.start,
|
IconButton(
|
||||||
isScrollable: true,
|
onPressed: () => handleFilterChange(null),
|
||||||
dividerColor: Colors.transparent,
|
icon: Icon(Symbols.explore),
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 12),
|
tooltip: 'explore'.tr(),
|
||||||
tabs: [
|
isSelected: currentFilter.value == null,
|
||||||
Tab(
|
color:
|
||||||
icon: Tooltip(
|
currentFilter.value == null
|
||||||
message: 'explore'.tr(),
|
? Theme.of(context).colorScheme.primary
|
||||||
child: Icon(Symbols.explore),
|
: 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(
|
IconButton(
|
||||||
icon: Tooltip(
|
onPressed: () => handleFilterChange('friends'),
|
||||||
message: 'exploreFilterSubscriptions'.tr(),
|
icon: Icon(Symbols.people),
|
||||||
child: Icon(Symbols.subscriptions),
|
tooltip: 'exploreFilterFriends'.tr(),
|
||||||
),
|
isSelected: currentFilter.value == 'friends',
|
||||||
),
|
color:
|
||||||
Tab(
|
currentFilter.value == 'friends'
|
||||||
icon: Tooltip(
|
? Theme.of(context).colorScheme.primary
|
||||||
message: 'exploreFilterFriends'.tr(),
|
: null,
|
||||||
child: Icon(Symbols.people),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed('articles');
|
context.pushNamed('articles');
|
||||||
@@ -241,10 +225,13 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
tooltip: 'search'.tr(),
|
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);
|
final dragging = useState(false);
|
||||||
|
|
||||||
@@ -324,6 +311,7 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
notifier: activityListNotifierProvider.notifier,
|
notifier: activityListNotifierProvider.notifier,
|
||||||
// Sliver list cannot provide refresh handled by the pagination list
|
// Sliver list cannot provide refresh handled by the pagination list
|
||||||
isRefreshable: false,
|
isRefreshable: false,
|
||||||
|
isSliver: true,
|
||||||
contentBuilder: (data) => _ActivityListView(data: data, isWide: isWide),
|
contentBuilder: (data) => _ActivityListView(data: data, isWide: isWide),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -433,7 +421,8 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreferredSizeWidget _buildAppBar(
|
PreferredSizeWidget _buildAppBar(
|
||||||
TabController tabController,
|
String? currentFilter,
|
||||||
|
void Function(String?) handleFilterChange,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final foregroundColor = Theme.of(context).appBarTheme.foregroundColor;
|
final foregroundColor = Theme.of(context).appBarTheme.foregroundColor;
|
||||||
@@ -449,38 +438,25 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
IconButton(
|
||||||
child: TabBar(
|
onPressed: () => handleFilterChange(null),
|
||||||
controller: tabController,
|
icon: Icon(Symbols.explore, color: foregroundColor),
|
||||||
tabAlignment: TabAlignment.start,
|
tooltip: 'explore'.tr(),
|
||||||
isScrollable: true,
|
isSelected: currentFilter == null,
|
||||||
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('subscriptions'),
|
||||||
|
icon: Icon(Symbols.subscriptions, color: foregroundColor),
|
||||||
|
tooltip: 'exploreFilterSubscriptions'.tr(),
|
||||||
|
isSelected: currentFilter == 'subscriptions',
|
||||||
),
|
),
|
||||||
Tab(
|
IconButton(
|
||||||
icon: Tooltip(
|
onPressed: () => handleFilterChange('friends'),
|
||||||
message: 'exploreFilterSubscriptions'.tr(),
|
icon: Icon(Symbols.people, color: foregroundColor),
|
||||||
child: Icon(
|
tooltip: 'exploreFilterFriends'.tr(),
|
||||||
Symbols.subscriptions,
|
isSelected: currentFilter == 'friends',
|
||||||
color: foregroundColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tab(
|
|
||||||
icon: Tooltip(
|
|
||||||
message: 'exploreFilterFriends'.tr(),
|
|
||||||
child: Icon(Symbols.people, color: foregroundColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed('articles');
|
context.pushNamed('articles');
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/pods/paging.dart';
|
import 'package:island/pods/paging.dart';
|
||||||
import 'package:island/widgets/extended_refresh_indicator.dart';
|
import 'package:island/widgets/extended_refresh_indicator.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||||
|
|
||||||
class PaginationList<T> extends HookConsumerWidget {
|
class PaginationList<T> extends HookConsumerWidget {
|
||||||
@@ -34,7 +35,7 @@ class PaginationList<T> extends HookConsumerWidget {
|
|||||||
if (scrollInfo is ScrollEndNotification &&
|
if (scrollInfo is ScrollEndNotification &&
|
||||||
scrollInfo.metrics.axisDirection == AxisDirection.down &&
|
scrollInfo.metrics.axisDirection == AxisDirection.down &&
|
||||||
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
|
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
|
||||||
if (!noti.fetchedAll) {
|
if (!noti.fetchedAll && !data.isLoading && !data.hasError) {
|
||||||
noti.fetchFurther();
|
noti.fetchFurther();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,24 +55,42 @@ class PaginationWidget<T> extends HookConsumerWidget {
|
|||||||
final Refreshable<PaginationController<T>> notifier;
|
final Refreshable<PaginationController<T>> notifier;
|
||||||
final Widget Function(List<T>) contentBuilder;
|
final Widget Function(List<T>) contentBuilder;
|
||||||
final bool isRefreshable;
|
final bool isRefreshable;
|
||||||
|
final bool isSliver;
|
||||||
|
final bool showDefaultWidgets;
|
||||||
const PaginationWidget({
|
const PaginationWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.provider,
|
required this.provider,
|
||||||
required this.notifier,
|
required this.notifier,
|
||||||
required this.contentBuilder,
|
required this.contentBuilder,
|
||||||
this.isRefreshable = true,
|
this.isRefreshable = true,
|
||||||
|
this.isSliver = false,
|
||||||
|
this.showDefaultWidgets = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final data = ref.watch(provider);
|
final data = ref.watch(provider);
|
||||||
final noti = ref.watch(notifier);
|
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(
|
final content = NotificationListener(
|
||||||
onNotification: (ScrollNotification scrollInfo) {
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
if (scrollInfo is ScrollEndNotification &&
|
if (scrollInfo is ScrollEndNotification &&
|
||||||
scrollInfo.metrics.axisDirection == AxisDirection.down &&
|
scrollInfo.metrics.axisDirection == AxisDirection.down &&
|
||||||
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
|
scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent) {
|
||||||
if (!noti.fetchedAll) {
|
if (!noti.fetchedAll && !data.isLoading && !data.hasError) {
|
||||||
noti.fetchFurther();
|
noti.fetchFurther();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user