chore: infinite query comments

This commit is contained in:
Kingkor Roy Tirtho
2023-06-10 11:45:17 +06:00
parent bc52de5303
commit c6e484637a
@@ -5,6 +5,7 @@ import 'package:collection/collection.dart';
import 'package:fl_query/fl_query.dart';
import 'package:fl_query/src/core/mixins/retryer.dart';
import 'package:fl_query/src/core/mixins/validation.dart';
import 'package:flutter/material.dart' hide Listener;
import 'package:hive_flutter/adapters.dart';
import 'package:mutex/mutex.dart';
import 'package:state_notifier/state_notifier.dart';
@@ -16,6 +17,7 @@ typedef InfiniteQueryNextPage<DataType, PageType> = PageType? Function(
DataType lastPageData,
);
/// A page holding all the data for a given page
class InfiniteQueryPage<DataType, ErrorType, PageType> with Invalidation {
final PageType page;
final DataType? data;
@@ -74,6 +76,8 @@ class InfiniteQueryState<DataType, ErrorType, PageType> {
}
}
/// Event fired with data and error by the [InfiniteQuery] fetchPage operation
@immutable
class PageEvent<T, P> {
final P page;
final T data;
@@ -86,6 +90,12 @@ class PageEvent<T, P> {
}
}
/// A specialized Query that can retrieve + hold paginated/segmented data
///
/// - [nextPage] provides the next page param for fetching
/// - [initialParam] provides the initial page param for fetching
///
/// Use the [InfiniteQueryBuilder] create and use an [InfiniteQuery]
class InfiniteQuery<DataType, ErrorType, PageType>
extends StateNotifier<InfiniteQueryState<DataType, ErrorType, PageType>>
with Retryer<DataType, ErrorType> {
@@ -169,16 +179,31 @@ class InfiniteQuery<DataType, ErrorType, PageType>
CancelableOperation<void>? _operation;
/// All the pages that has been successfully fetched
List<DataType> get pages =>
state.pages.map((e) => e.data).whereType<DataType>().toList();
/// All the errors of pages that has failed to fetch
List<ErrorType> get errors =>
state.pages.map((e) => e.error).whereType<ErrorType>().toList();
/// The last page that has been fetched
PageType get lastPage => state.lastPage;
/// Stream of data events
///
/// Subscribe to it to get notified when a page data is
/// fetched/refreshed/retried
Stream<PageEvent<DataType, PageType>> get dataStream =>
_dataController.stream;
/// Stream of error events
///
/// Subscribe to it to get notified when a page has failed
Stream<PageEvent<ErrorType, PageType>> get errorStream =>
_errorController.stream;
/// The next page param that will be used to fetch the next page
PageType? get getNextPage {
final lastPageData = state.pages
.firstWhereOrNull((e) => e.data is DataType && e.page == lastPage)
@@ -262,6 +287,9 @@ class InfiniteQuery<DataType, ErrorType, PageType>
});
}
/// Fetch current non-fetched page
///
/// If page is already done fetching. It'll simply returns the old data
Future<DataType?> fetch() async {
final lastPage = state.lastPage;
if (_mutex.isLocked || hasPageData || hasPageError)
@@ -269,6 +297,9 @@ class InfiniteQuery<DataType, ErrorType, PageType>
return await _operate(lastPage).then((_) => state.pages.last.data);
}
/// Refresh a page that has or has not been fetched
///
/// - [page] The page to refresh. If null, it'll refresh the last page
Future<DataType?> refresh([PageType? page]) async {
page ??= lastPage;
if (_mutex.isLocked)
@@ -278,6 +309,7 @@ class InfiniteQuery<DataType, ErrorType, PageType>
});
}
/// Refresh all the pages that has been fetched
Future<List<DataType>?> refreshAll() async {
if (_mutex.isLocked) return pages;
return await Future.wait(
@@ -285,6 +317,9 @@ class InfiniteQuery<DataType, ErrorType, PageType>
).then((_) => pages);
}
/// Fetch the next page
///
/// If there's no next page, it'll simply return the last page data
Future<DataType?> fetchNext() async {
final nextPage = getNextPage;
if (_mutex.isLocked || nextPage == null) {
@@ -295,6 +330,10 @@ class InfiniteQuery<DataType, ErrorType, PageType>
});
}
/// Replace the currently [queryFn] with new [queryFn]
///
/// This is internally used to update queryFn when external data
/// has changed. Used by [InfiniteQueryBuilder] and [QueryClient]
void updateQueryFn(InfiniteQueryFn<DataType, PageType> queryFn) {
if (_queryFn == queryFn) return;
_queryFn = queryFn;
@@ -311,11 +350,18 @@ class InfiniteQuery<DataType, ErrorType, PageType>
}
}
/// Replace the currently [nextPage] with new [nextPage]
void updateNextPageFn(InfiniteQueryNextPage<DataType, PageType> nextPage) {
if (_nextPage == nextPage) return;
_nextPage = nextPage;
}
/// Manually set the data of a page
///
/// If there's no page with the given [page], it'll create a new page
/// and set the data as the given [data]
///
/// The new page will be added to the end of the list so it becomes [lastPage]
void setPageData(PageType page, DataType data) {
final newPage = state.pages
.firstWhere(
@@ -336,6 +382,10 @@ class InfiniteQuery<DataType, ErrorType, PageType>
);
}
/// Reset all data, pages, error, events of this query
///
/// This will also remove every data of this [InfiniteQuery] from
/// persistent cache
Future<void> reset() async {
await _operation?.cancel();
state = state.copyWith(pages: {
@@ -353,10 +403,11 @@ class InfiniteQuery<DataType, ErrorType, PageType>
Listener<InfiniteQueryState<DataType, ErrorType, PageType>> listener, {
bool fireImmediately = true,
}) {
Future.microtask(() async {
if (refreshConfig.refreshOnMount) {
refreshAll();
await refreshAll();
} else {
Future.wait(
await Future.wait(
state.pages.map((page) async {
if (page.isStale) {
return await refresh(page.page);
@@ -364,6 +415,7 @@ class InfiniteQuery<DataType, ErrorType, PageType>
}),
);
}
});
return super.addListener(listener, fireImmediately: fireImmediately);
}