♻️ Rebuilt fetching state machine
This commit is contained in:
@@ -2,6 +2,42 @@ import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class PaginationState<T> {
|
||||
final List<T> items;
|
||||
final bool isLoading;
|
||||
final bool isReloading;
|
||||
final int? totalCount;
|
||||
final bool hasMore;
|
||||
final String? cursor;
|
||||
|
||||
const PaginationState({
|
||||
required this.items,
|
||||
required this.isLoading,
|
||||
required this.isReloading,
|
||||
required this.totalCount,
|
||||
required this.hasMore,
|
||||
required this.cursor,
|
||||
});
|
||||
|
||||
PaginationState<T> copyWith({
|
||||
List<T>? items,
|
||||
bool? isLoading,
|
||||
bool? isReloading,
|
||||
int? totalCount,
|
||||
bool? hasMore,
|
||||
String? cursor,
|
||||
}) {
|
||||
return PaginationState<T>(
|
||||
items: items ?? this.items,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
isReloading: isReloading ?? this.isReloading,
|
||||
totalCount: totalCount ?? this.totalCount,
|
||||
hasMore: hasMore ?? this.hasMore,
|
||||
cursor: cursor ?? this.cursor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PaginationController<T> {
|
||||
int? get totalCount;
|
||||
int get fetchedCount;
|
||||
@@ -27,51 +63,84 @@ abstract class PaginationFiltered<F> {
|
||||
Future<void> applyFilter(F filter);
|
||||
}
|
||||
|
||||
mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
||||
mixin AsyncPaginationController<T> on AsyncNotifier<PaginationState<T>>
|
||||
implements PaginationController<T> {
|
||||
@override
|
||||
int? totalCount;
|
||||
|
||||
@override
|
||||
int get fetchedCount => isReloading ? 0 : state.value?.length ?? 0;
|
||||
int get fetchedCount =>
|
||||
state.value?.isReloading == true ? 0 : state.value?.items.length ?? 0;
|
||||
|
||||
@override
|
||||
bool get fetchedAll =>
|
||||
!hasMore || (totalCount != null && fetchedCount >= totalCount!);
|
||||
!(state.value?.hasMore ?? true) ||
|
||||
((state.value?.totalCount != null &&
|
||||
fetchedCount >= state.value!.totalCount!));
|
||||
|
||||
@override
|
||||
bool isLoading = false;
|
||||
bool get isLoading => state.value?.isLoading ?? false;
|
||||
|
||||
@override
|
||||
bool isReloading = false;
|
||||
bool get isReloading => state.value?.isReloading ?? false;
|
||||
|
||||
@override
|
||||
bool hasMore = true;
|
||||
bool get hasMore => state.value?.hasMore ?? true;
|
||||
|
||||
@override
|
||||
String? cursor;
|
||||
String? get cursor => state.value?.cursor;
|
||||
|
||||
@override
|
||||
FutureOr<List<T>> build() async {
|
||||
cursor = null;
|
||||
return fetch();
|
||||
set hasMore(bool value) {
|
||||
if (state is AsyncData) {
|
||||
state = AsyncData((state as AsyncData).value.copyWith(hasMore: value));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
set cursor(String? value) {
|
||||
if (state is AsyncData) {
|
||||
state = AsyncData((state as AsyncData).value.copyWith(cursor: value));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<PaginationState<T>> build() async {
|
||||
final items = await fetch();
|
||||
return PaginationState(
|
||||
items: items,
|
||||
isLoading: false,
|
||||
isReloading: false,
|
||||
totalCount: totalCount,
|
||||
hasMore: hasMore,
|
||||
cursor: cursor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> refresh() async {
|
||||
isLoading = true;
|
||||
isReloading = true;
|
||||
totalCount = null;
|
||||
hasMore = true;
|
||||
cursor = null;
|
||||
state = AsyncLoading<List<T>>();
|
||||
state = AsyncData(
|
||||
state.value!.copyWith(
|
||||
isLoading: true,
|
||||
isReloading: true,
|
||||
totalCount: null,
|
||||
hasMore: true,
|
||||
cursor: null,
|
||||
),
|
||||
);
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
return await fetch();
|
||||
});
|
||||
isReloading = false;
|
||||
isLoading = false;
|
||||
state = newState;
|
||||
final newItems = await fetch();
|
||||
|
||||
state = AsyncData(
|
||||
state.value!.copyWith(
|
||||
items: newItems,
|
||||
isLoading: false,
|
||||
isReloading: false,
|
||||
totalCount: totalCount,
|
||||
hasMore: hasMore,
|
||||
cursor: cursor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -79,16 +148,16 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
||||
if (fetchedAll) return;
|
||||
if (isLoading) return;
|
||||
|
||||
isLoading = true;
|
||||
state = AsyncLoading<List<T>>();
|
||||
state = AsyncData(state.value!.copyWith(isLoading: true));
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
final elements = await fetch();
|
||||
return [...?state.value, ...elements];
|
||||
});
|
||||
final newItems = await fetch();
|
||||
|
||||
isLoading = false;
|
||||
state = newState;
|
||||
state = AsyncData(
|
||||
state.value!.copyWith(
|
||||
items: [...state.value!.items, ...newItems],
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,20 +166,29 @@ mixin AsyncPaginationFilter<F, T> on AsyncPaginationController<T>
|
||||
@override
|
||||
Future<void> applyFilter(F filter) async {
|
||||
if (currentFilter == filter) return;
|
||||
// Reset the data
|
||||
isReloading = true;
|
||||
isLoading = true;
|
||||
totalCount = null;
|
||||
hasMore = true;
|
||||
cursor = null;
|
||||
state = AsyncLoading<List<T>>();
|
||||
|
||||
state = AsyncData(
|
||||
state.value!.copyWith(
|
||||
isReloading: true,
|
||||
isLoading: true,
|
||||
totalCount: null,
|
||||
hasMore: true,
|
||||
cursor: null,
|
||||
),
|
||||
);
|
||||
currentFilter = filter;
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
return await fetch();
|
||||
});
|
||||
isLoading = false;
|
||||
isReloading = false;
|
||||
state = newState;
|
||||
final newItems = await fetch();
|
||||
|
||||
state = AsyncData(
|
||||
state.value!.copyWith(
|
||||
items: newItems,
|
||||
isLoading: false,
|
||||
isReloading: false,
|
||||
totalCount: totalCount,
|
||||
hasMore: hasMore,
|
||||
cursor: cursor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user