feat(infinite-query): add all the features of query in infinite query
Separated common logic of both `InfiniteQuery` and `Query` in a base class `BaseQuery` resulting in `InfiniteQuery` inheriting already having implemented features of `Query`
This commit is contained in:
@@ -1 +1 @@
|
|||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.2/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-16 21:50:29.137318","version":"3.0.5"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.5/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.2/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-24 21:03:29.720419","version":"3.0.5"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.6/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.6/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.3/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-21 19:29:44.415263","version":"3.0.5"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.6/","native_build":true,"dependencies":[]}],"android":[{"name":"connectivity_plus","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus-2.3.6/","native_build":true,"dependencies":[]}],"macos":[{"name":"connectivity_plus_macos","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_macos-1.2.4/","native_build":true,"dependencies":[]}],"linux":[{"name":"connectivity_plus_linux","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_linux-1.3.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"connectivity_plus_windows","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_windows-1.2.2/","native_build":true,"dependencies":[]}],"web":[{"name":"connectivity_plus_web","path":"/home/krtirtho/.pub-cache/hosted/pub.dartlang.org/connectivity_plus_web-1.2.3/","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":["connectivity_plus_linux","connectivity_plus_macos","connectivity_plus_web","connectivity_plus_windows"]},{"name":"connectivity_plus_linux","dependencies":[]},{"name":"connectivity_plus_macos","dependencies":[]},{"name":"connectivity_plus_web","dependencies":[]},{"name":"connectivity_plus_windows","dependencies":[]}],"date_created":"2022-08-24 21:03:52.762936","version":"3.0.5"}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fl_query/src/query_bowl.dart';
|
import 'package:fl_query/src/query_bowl.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
abstract class BaseOperation<Data> extends ChangeNotifier {
|
abstract class BaseOperation<Data, Error> extends ChangeNotifier {
|
||||||
/// The number of times the query should refetch in the time of error
|
/// The number of times the query should refetch in the time of error
|
||||||
/// before giving up
|
/// before giving up
|
||||||
final int retries;
|
final int retries;
|
||||||
@@ -13,7 +13,7 @@ abstract class BaseOperation<Data> extends ChangeNotifier {
|
|||||||
|
|
||||||
// all properties
|
// all properties
|
||||||
Data? data;
|
Data? data;
|
||||||
dynamic error;
|
Error? error;
|
||||||
|
|
||||||
/// total count of how many times the query retried to get a successful
|
/// total count of how many times the query retried to get a successful
|
||||||
/// result
|
/// result
|
||||||
|
|||||||
@@ -0,0 +1,318 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:fl_query/src/base_operation.dart';
|
||||||
|
import 'package:fl_query/src/mixins/autocast.dart';
|
||||||
|
import 'package:fl_query/src/query.dart';
|
||||||
|
import 'package:fl_query/src/utils.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
abstract class BaseQuery<T extends Object, Outside, Error>
|
||||||
|
extends BaseOperation<T, Error> with AutoCast {
|
||||||
|
// all params
|
||||||
|
final String queryKey;
|
||||||
|
bool? refetchOnMount;
|
||||||
|
bool? refetchOnReconnect;
|
||||||
|
|
||||||
|
final T? _initialData;
|
||||||
|
|
||||||
|
// got from global options
|
||||||
|
Duration _staleTime;
|
||||||
|
|
||||||
|
/// total count of how many times the query retried to get a successful
|
||||||
|
/// result
|
||||||
|
int refetchCount = 0;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
QueryStatus status;
|
||||||
|
|
||||||
|
final Set _onDataListeners = Set();
|
||||||
|
final Set _onErrorListeners = Set();
|
||||||
|
|
||||||
|
// externalData will always be passed to the task Callback
|
||||||
|
// it will change based on the presence of QueryBuilder
|
||||||
|
Outside _externalData;
|
||||||
|
|
||||||
|
Outside? _prevUsedExternalData;
|
||||||
|
|
||||||
|
Duration? refetchInterval;
|
||||||
|
|
||||||
|
Timer? _refetchIntervalTimer;
|
||||||
|
|
||||||
|
Connectivity _connectivity;
|
||||||
|
|
||||||
|
T? _previousData;
|
||||||
|
|
||||||
|
BaseQuery({
|
||||||
|
required this.queryKey,
|
||||||
|
required Duration staleTime,
|
||||||
|
required super.cacheTime,
|
||||||
|
required Outside externalData,
|
||||||
|
required super.retries,
|
||||||
|
required super.retryDelay,
|
||||||
|
required super.queryBowl,
|
||||||
|
required this.status,
|
||||||
|
this.refetchOnMount,
|
||||||
|
this.refetchOnReconnect,
|
||||||
|
this.refetchInterval,
|
||||||
|
this.enabled = true,
|
||||||
|
T? previousData,
|
||||||
|
Connectivity? connectivity,
|
||||||
|
T? initialData,
|
||||||
|
onData,
|
||||||
|
onError,
|
||||||
|
}) : _staleTime = staleTime,
|
||||||
|
_initialData = initialData,
|
||||||
|
_externalData = externalData,
|
||||||
|
_connectivity = connectivity ?? Connectivity(),
|
||||||
|
_previousData = previousData,
|
||||||
|
super(data: previousData ?? initialData) {
|
||||||
|
if (onData != null) _onDataListeners.add(onData);
|
||||||
|
if (onError != null) _onErrorListeners.add(onError);
|
||||||
|
|
||||||
|
if (refetchInterval != null && refetchInterval != Duration.zero) {
|
||||||
|
_refetchIntervalTimer = _createRefetchTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all getters & setters
|
||||||
|
|
||||||
|
Outside get externalData => _externalData;
|
||||||
|
Outside? get prevUsedExternalData => _prevUsedExternalData;
|
||||||
|
|
||||||
|
Timer _createRefetchTimer() {
|
||||||
|
return Timer.periodic(
|
||||||
|
refetchInterval!,
|
||||||
|
(_) async {
|
||||||
|
// only refetch if its connected to the internet or refetch will
|
||||||
|
// always result in error while there's no internet
|
||||||
|
if (isStale && await isInternetConnected()) await refetch();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the task function & doesn't check if there's already
|
||||||
|
/// cached data available
|
||||||
|
@protected
|
||||||
|
Future<void> execute() async {
|
||||||
|
try {
|
||||||
|
retryAttempts = 0;
|
||||||
|
await setData();
|
||||||
|
_prevUsedExternalData = _externalData;
|
||||||
|
updatedAt = DateTime.now();
|
||||||
|
status = QueryStatus.success;
|
||||||
|
for (final onData in _onDataListeners) {
|
||||||
|
onData(data!);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {
|
||||||
|
if (retries == 0) {
|
||||||
|
status = QueryStatus.error;
|
||||||
|
setError(e);
|
||||||
|
for (final onError in _onErrorListeners) {
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
// retrying for retry count if failed for the first time
|
||||||
|
while (retryAttempts <= retries) {
|
||||||
|
await Future.delayed(retryDelay);
|
||||||
|
try {
|
||||||
|
await setData();
|
||||||
|
_prevUsedExternalData = _externalData;
|
||||||
|
status = QueryStatus.success;
|
||||||
|
for (final onData in _onDataListeners) {
|
||||||
|
await onData(data!);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
if (retryAttempts == retries) {
|
||||||
|
status = QueryStatus.error;
|
||||||
|
setError(e);
|
||||||
|
for (final onError in _onErrorListeners) {
|
||||||
|
await onError(error);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retryAttempts++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDataListener(listener) {
|
||||||
|
_onDataListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addErrorListener(listener) {
|
||||||
|
_onErrorListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDataListener(listener) {
|
||||||
|
_onDataListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeErrorListener(listener) {
|
||||||
|
_onErrorListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fetches data or runs the provided task initially
|
||||||
|
///
|
||||||
|
/// Once [data] is available it won't run the [task] ever again
|
||||||
|
/// and will only return the available data
|
||||||
|
///
|
||||||
|
/// If a [fetch] is already running in the background it'll just return
|
||||||
|
/// the current available [data] (which can be nul if no [initialPage]
|
||||||
|
/// was provided) instead of running the task to prevent race conditions
|
||||||
|
Future<T?> fetch() async {
|
||||||
|
if (!enabled) return null;
|
||||||
|
|
||||||
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
|
/// refetching. So [_execute] again can create a race condition
|
||||||
|
if (isLoading || isRefetching || (hasData && !isPreviousData)) return data;
|
||||||
|
status = QueryStatus.loading;
|
||||||
|
notifyListeners();
|
||||||
|
return execute().then((_) {
|
||||||
|
fetched = true;
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// refetches a valid or invalid [Query]
|
||||||
|
///
|
||||||
|
/// When called before calling [fetch] in a [Query] it'll
|
||||||
|
/// automatically run [fetch]
|
||||||
|
///
|
||||||
|
/// But if it's used to fetch the first data of a non-enabled [Query]
|
||||||
|
/// aka `LazyQuery`, it'll execute the task & will set the status
|
||||||
|
/// `enabled=true`
|
||||||
|
///
|
||||||
|
/// If a [refetch] is already running in the background it'll just return
|
||||||
|
/// the current available [data] instead of running the task to prevent
|
||||||
|
/// race conditions
|
||||||
|
Future<T?> refetch() async {
|
||||||
|
/// if isLoading/isRefetching is true that means its already fetching/
|
||||||
|
/// refetching. So [_execute] again can create a race condition
|
||||||
|
if (isRefetching || isLoading) return data;
|
||||||
|
if (enabled && !fetched) return await fetch();
|
||||||
|
status = QueryStatus.refetching;
|
||||||
|
refetchCount++;
|
||||||
|
// disabling the lazy query bound when query was actually called
|
||||||
|
if (!enabled) enabled = true;
|
||||||
|
notifyListeners();
|
||||||
|
return await execute().then((_) => data);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> setData();
|
||||||
|
void setError(dynamic);
|
||||||
|
|
||||||
|
/// Sets the [externalData] from outside of the query
|
||||||
|
///
|
||||||
|
/// Remember, it's for the very instance of [Query]
|
||||||
|
/// So this won't persist through later UI/[Query] updates
|
||||||
|
void setExternalData(Outside externalData) {
|
||||||
|
_prevUsedExternalData = _externalData;
|
||||||
|
_externalData = externalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the query
|
||||||
|
///
|
||||||
|
/// The values of internal state of the query are reset to the
|
||||||
|
/// initial ones
|
||||||
|
void reset() {
|
||||||
|
refetchCount = 0;
|
||||||
|
data = _previousData ?? _initialData;
|
||||||
|
error = null;
|
||||||
|
fetched = false;
|
||||||
|
status = QueryStatus.idle;
|
||||||
|
retryAttempts = 0;
|
||||||
|
_onDataListeners.clear();
|
||||||
|
_onErrorListeners.clear();
|
||||||
|
mounts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update configurations of the query
|
||||||
|
/// after already creating the Query instance
|
||||||
|
///
|
||||||
|
/// Remember, it's just for the single query instance
|
||||||
|
/// In the next UI update/render the options will get reset
|
||||||
|
/// to the default ones defined in the [QueryJob] or [QueryBowlScope]
|
||||||
|
void updateDefaultOptions({
|
||||||
|
Duration? refetchInterval,
|
||||||
|
Duration? staleTime,
|
||||||
|
Duration? cacheTime,
|
||||||
|
bool? refetchOnMount,
|
||||||
|
bool? refetchOnReconnect,
|
||||||
|
}) {
|
||||||
|
if (this.refetchInterval == null &&
|
||||||
|
refetchInterval != null &&
|
||||||
|
refetchInterval != Duration.zero) {
|
||||||
|
this.refetchInterval = refetchInterval;
|
||||||
|
_refetchIntervalTimer?.cancel();
|
||||||
|
_refetchIntervalTimer = _createRefetchTimer();
|
||||||
|
}
|
||||||
|
if (this.cacheTime == Duration(minutes: 5) && cacheTime != null)
|
||||||
|
this.cacheTime = cacheTime;
|
||||||
|
if (this._staleTime == const Duration(milliseconds: 500) &&
|
||||||
|
staleTime != null) this._staleTime = staleTime;
|
||||||
|
if (this.refetchOnMount == null && refetchOnMount != null)
|
||||||
|
this.refetchOnMount = refetchOnMount;
|
||||||
|
if (this.refetchOnReconnect == null && refetchOnReconnect != null)
|
||||||
|
this.refetchOnReconnect = refetchOnReconnect;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// checks if the application is connected to internet in any mean
|
||||||
|
///
|
||||||
|
/// It's true when any one this is connected -
|
||||||
|
/// - ethernet
|
||||||
|
/// - mobile
|
||||||
|
/// - wifi
|
||||||
|
Future<bool> isInternetConnected() async {
|
||||||
|
return isConnectedToInternet(await _connectivity.checkConnectivity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// invalidates the query
|
||||||
|
///
|
||||||
|
/// Forcefully makes the query stale & expired which results in a refetch
|
||||||
|
/// when met conditions
|
||||||
|
void invalidate() {
|
||||||
|
/// subtracting [staleTime] from [updatedAt] as staleTime=Duration.zero
|
||||||
|
/// indicates the query must never become stale but subtracting the
|
||||||
|
/// [staleTime] will always revert the updatedAt time to the default
|
||||||
|
/// time whenever isStale is called
|
||||||
|
updatedAt = updatedAt.subtract(_staleTime);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isStale {
|
||||||
|
/// when [_staleTime] is [Duration.zero], the query will always be
|
||||||
|
/// stale & will never refetch in the background. But can be inactive
|
||||||
|
/// if [mounts.length] become zero
|
||||||
|
if (_staleTime == Duration.zero) return false;
|
||||||
|
|
||||||
|
// when [DateTime.now()] is after [update_at + stale_time] it means
|
||||||
|
// the data has become stale
|
||||||
|
return DateTime.now().isAfter(updatedAt.add(_staleTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isError => status == QueryStatus.error;
|
||||||
|
bool get isIdle => status == QueryStatus.idle;
|
||||||
|
bool get isLoading => status == QueryStatus.loading;
|
||||||
|
bool get isRefetching => status == QueryStatus.refetching;
|
||||||
|
bool get isSuccess => status == QueryStatus.success;
|
||||||
|
bool get isPreviousData {
|
||||||
|
return _previousData != null ? _previousData == data : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get debugLabel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return debugLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator ==(other);
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:fl_query/src/mixins/autocast.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:fl_query/src/base_query.dart';
|
||||||
import 'package:fl_query/src/models/infinite_query_job.dart';
|
import 'package:fl_query/src/models/infinite_query_job.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:fl_query/src/query.dart';
|
||||||
|
|
||||||
typedef InfiniteQueryTaskFunction<T extends Object, Outside,
|
typedef InfiniteQueryTaskFunction<T extends Object, Outside,
|
||||||
PageParam extends Object>
|
PageParam extends Object>
|
||||||
@@ -16,84 +17,94 @@ typedef InfiniteQueryPageParamFunction<T extends Object,
|
|||||||
= FutureOr<PageParam> Function(T lastPage, PageParam lastParam);
|
= FutureOr<PageParam> Function(T lastPage, PageParam lastParam);
|
||||||
|
|
||||||
class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
|
class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
|
||||||
with ChangeNotifier, AutoCast {
|
extends BaseQuery<Map<PageParam, T?>, Outside, Map<PageParam, dynamic>> {
|
||||||
String queryKey;
|
|
||||||
|
|
||||||
Map<PageParam, T?> _data;
|
|
||||||
Map<PageParam, dynamic> _error;
|
|
||||||
|
|
||||||
InfiniteQueryTaskFunction<T, Outside, PageParam> task;
|
InfiniteQueryTaskFunction<T, Outside, PageParam> task;
|
||||||
|
|
||||||
InfiniteQueryPageParamFunction<T, PageParam>? getNextPageParam;
|
InfiniteQueryPageParamFunction<T, PageParam>? getNextPageParam;
|
||||||
InfiniteQueryPageParamFunction<T, PageParam>? getPreviousPageParam;
|
InfiniteQueryPageParamFunction<T, PageParam>? getPreviousPageParam;
|
||||||
|
|
||||||
List<PageParam> get pageParams => _data.keys.toList();
|
|
||||||
List<dynamic> get errors => _error.values.toList();
|
|
||||||
List<T?> get pages => _data.values.toList();
|
|
||||||
|
|
||||||
PageParam currentParam;
|
|
||||||
|
|
||||||
Outside _externalData;
|
|
||||||
|
|
||||||
bool _hasNextPage = false;
|
bool _hasNextPage = false;
|
||||||
bool _hasPreviousPage = false;
|
bool _hasPreviousPage = false;
|
||||||
|
|
||||||
|
bool _isFetchingNextPage = false;
|
||||||
|
bool _isFetchingPreviousPage = false;
|
||||||
|
|
||||||
|
bool get isFetchingNextPage => _isFetchingNextPage;
|
||||||
|
bool get isFetchingPreviousPage => _isFetchingPreviousPage;
|
||||||
bool get hasNextPage => _hasNextPage;
|
bool get hasNextPage => _hasNextPage;
|
||||||
bool get hasPreviousPage => _hasPreviousPage;
|
bool get hasPreviousPage => _hasPreviousPage;
|
||||||
|
|
||||||
|
PageParam _currentParam;
|
||||||
|
|
||||||
InfiniteQuery({
|
InfiniteQuery({
|
||||||
required this.queryKey,
|
required super.queryKey,
|
||||||
required this.task,
|
required this.task,
|
||||||
|
required super.staleTime,
|
||||||
|
required super.cacheTime,
|
||||||
|
required super.externalData,
|
||||||
|
required super.retries,
|
||||||
|
required super.retryDelay,
|
||||||
|
required super.queryBowl,
|
||||||
|
required super.status,
|
||||||
required PageParam initialParam,
|
required PageParam initialParam,
|
||||||
required Outside externalData,
|
super.refetchOnMount,
|
||||||
|
super.refetchOnReconnect,
|
||||||
|
super.refetchInterval,
|
||||||
|
super.enabled,
|
||||||
|
super.previousData,
|
||||||
|
super.connectivity,
|
||||||
|
super.onData,
|
||||||
|
super.onError,
|
||||||
|
required T? initialPage,
|
||||||
this.getNextPageParam,
|
this.getNextPageParam,
|
||||||
this.getPreviousPageParam,
|
this.getPreviousPageParam,
|
||||||
T? initialPage,
|
}) : _currentParam = initialParam,
|
||||||
}) : currentParam = initialParam,
|
super(initialData: {initialParam: initialPage});
|
||||||
_externalData = externalData,
|
|
||||||
_error = {},
|
|
||||||
_data = {
|
|
||||||
if (initialPage != null) initialParam: initialPage,
|
|
||||||
};
|
|
||||||
|
|
||||||
InfiniteQuery.fromOptions(
|
InfiniteQuery.fromOptions(
|
||||||
InfiniteQueryJob<T, Outside, PageParam> options, {
|
InfiniteQueryJob<T, Outside, PageParam> options, {
|
||||||
|
required super.queryBowl,
|
||||||
required Outside externalData,
|
required Outside externalData,
|
||||||
}) : queryKey = options.queryKey,
|
QueryListener<T>? onData,
|
||||||
task = options.task,
|
QueryListener<dynamic>? onError,
|
||||||
currentParam = options.initialParam,
|
}) : task = options.task,
|
||||||
_externalData = externalData,
|
_currentParam = options.initialParam,
|
||||||
_error = {},
|
|
||||||
getNextPageParam = options.getNextPageParam,
|
getNextPageParam = options.getNextPageParam,
|
||||||
getPreviousPageParam = options.getPreviousPageParam,
|
getPreviousPageParam = options.getPreviousPageParam,
|
||||||
_data = {
|
super(
|
||||||
if (options.initialPage != null)
|
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
|
||||||
options.initialParam: options.initialPage,
|
retries: options.retries ?? 3,
|
||||||
};
|
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
|
||||||
|
externalData: externalData,
|
||||||
|
enabled: options.enabled ?? true,
|
||||||
|
staleTime: options.staleTime ?? const Duration(milliseconds: 500),
|
||||||
|
refetchInterval: options.refetchInterval,
|
||||||
|
refetchOnMount: options.refetchOnMount,
|
||||||
|
refetchOnReconnect: options.refetchOnReconnect,
|
||||||
|
status: QueryStatus.idle,
|
||||||
|
connectivity: options.connectivity ?? Connectivity(),
|
||||||
|
queryKey: options.queryKey,
|
||||||
|
initialData: {options.initialParam: options.initialPage},
|
||||||
|
);
|
||||||
|
|
||||||
Future<void> _execute() async {
|
List<PageParam> get pageParams => data?.keys.toList() ?? [];
|
||||||
final page = await task(
|
List<dynamic> get errors => error?.values.toList() ?? [];
|
||||||
queryKey,
|
List<T?> get pages => data?.values.toList() ?? [];
|
||||||
currentParam,
|
|
||||||
_externalData,
|
|
||||||
);
|
|
||||||
_data[currentParam] = page;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<T?>> fetch() async {
|
|
||||||
if (_data.isEmpty) await _execute();
|
|
||||||
return pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<T?> fetchNextPage([
|
Future<T?> fetchNextPage([
|
||||||
InfiniteQueryPageParamFunction<T, PageParam>? getNextPageParam,
|
InfiniteQueryPageParamFunction<T, PageParam>? getNextPageParam,
|
||||||
]) async {
|
]) async {
|
||||||
try {
|
try {
|
||||||
if (_data[currentParam] == null) await _execute();
|
if (isFetchingNextPage ||
|
||||||
|
isFetchingPreviousPage ||
|
||||||
|
isLoading ||
|
||||||
|
isRefetching) return null;
|
||||||
|
if (data == null || data?[_currentParam] == null) execute();
|
||||||
|
_isFetchingNextPage = true;
|
||||||
|
_isFetchingPreviousPage = false;
|
||||||
final nextParam = await (getNextPageParam ?? this.getNextPageParam)?.call(
|
final nextParam = await (getNextPageParam ?? this.getNextPageParam)?.call(
|
||||||
_data[currentParam]!,
|
data![_currentParam]!,
|
||||||
currentParam,
|
_currentParam,
|
||||||
);
|
);
|
||||||
if (nextParam == null) {
|
if (nextParam == null) {
|
||||||
_hasNextPage = false;
|
_hasNextPage = false;
|
||||||
@@ -101,11 +112,11 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
_hasNextPage = true;
|
_hasNextPage = true;
|
||||||
currentParam = nextParam;
|
_currentParam = nextParam;
|
||||||
return await _execute().then((_) => _data[currentParam]);
|
return await refetch().then((data) => data?[_currentParam]);
|
||||||
} catch (e) {
|
} finally {
|
||||||
print("[InfiniteQuery.fetchNextPage]: $e");
|
_isFetchingNextPage = false;
|
||||||
rethrow;
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,11 +124,18 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
|
|||||||
InfiniteQueryPageParamFunction<T, PageParam>? getPreviousPageParam,
|
InfiniteQueryPageParamFunction<T, PageParam>? getPreviousPageParam,
|
||||||
]) async {
|
]) async {
|
||||||
try {
|
try {
|
||||||
if (_data[currentParam] == null) await _execute();
|
if (isFetchingNextPage ||
|
||||||
|
isFetchingPreviousPage ||
|
||||||
|
isLoading ||
|
||||||
|
isRefetching) return null;
|
||||||
|
_isFetchingPreviousPage = true;
|
||||||
|
_isFetchingNextPage = false;
|
||||||
|
notifyListeners();
|
||||||
|
if (data?[_currentParam] == null) execute();
|
||||||
final prevParam =
|
final prevParam =
|
||||||
await (getNextPageParam ?? this.getPreviousPageParam)?.call(
|
await (getPreviousPageParam ?? this.getPreviousPageParam)?.call(
|
||||||
_data[currentParam]!,
|
data![_currentParam]!,
|
||||||
currentParam,
|
_currentParam,
|
||||||
);
|
);
|
||||||
if (prevParam == null) {
|
if (prevParam == null) {
|
||||||
_hasPreviousPage = false;
|
_hasPreviousPage = false;
|
||||||
@@ -125,11 +143,44 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
_hasPreviousPage = true;
|
_hasPreviousPage = true;
|
||||||
currentParam = prevParam;
|
_currentParam = prevParam;
|
||||||
return await _execute().then((_) => _data[currentParam]);
|
return await refetch().then((_) => data?[_currentParam]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("[InfiniteQuery.fetchPreviousPage]: $e");
|
print("[InfiniteQuery.fetchPreviousPage]: $e");
|
||||||
rethrow;
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
_isFetchingPreviousPage = false;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement debugLabel
|
||||||
|
String get debugLabel => "InfiniteQuery($queryKey)";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setData() async {
|
||||||
|
if (data == null) data = Map();
|
||||||
|
data?[_currentParam] = await task(
|
||||||
|
queryKey,
|
||||||
|
_currentParam,
|
||||||
|
externalData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setError(specError) {
|
||||||
|
if (error is! Map) error = Map();
|
||||||
|
error?[_currentParam] = specError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) {
|
||||||
|
return (other is InfiniteQuery<T, Outside, PageParam> &&
|
||||||
|
other.queryKey == queryKey) ||
|
||||||
|
identical(other, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get hasData => data?[_currentParam] != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,14 +45,15 @@ class _InfiniteQueryBuilderState<T extends Object, Outside,
|
|||||||
externalData: widget.externalData,
|
externalData: widget.externalData,
|
||||||
key: uKey,
|
key: uKey,
|
||||||
);
|
);
|
||||||
// final hasExternalDataChanged = infiniteQuery!.externalData != null &&
|
final hasExternalDataChanged = infiniteQuery!.externalData != null &&
|
||||||
// infiniteQuery!.prevUsedExternalData != null &&
|
infiniteQuery!.prevUsedExternalData != null &&
|
||||||
// !isShallowEqual(infiniteQuery!.externalData!, infiniteQuery!.prevUsedExternalData!);
|
!isShallowEqual(
|
||||||
// if (infiniteQuery!.fetched && hasExternalDataChanged) {
|
infiniteQuery!.externalData!, infiniteQuery!.prevUsedExternalData!);
|
||||||
// await infiniteQuery!.refetch();
|
if (infiniteQuery!.fetched && hasExternalDataChanged) {
|
||||||
// } else if (!infiniteQuery!.fetched) {
|
await infiniteQuery!.refetch();
|
||||||
await infiniteQuery!.fetch();
|
} else if (!infiniteQuery!.fetched) {
|
||||||
// }
|
await infiniteQuery!.fetch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ typedef MutationListener<T, V> = FutureOr<void> Function(
|
|||||||
typedef MutationTaskFunction<T, V> = FutureOr<T> Function(
|
typedef MutationTaskFunction<T, V> = FutureOr<T> Function(
|
||||||
String queryKey, V variables);
|
String queryKey, V variables);
|
||||||
|
|
||||||
class Mutation<T extends Object, V> extends BaseOperation<T> with AutoCast {
|
class Mutation<T extends Object, V> extends BaseOperation<T, dynamic>
|
||||||
|
with AutoCast {
|
||||||
// all params
|
// all params
|
||||||
final String mutationKey;
|
final String mutationKey;
|
||||||
MutationTaskFunction<T, V> task;
|
MutationTaskFunction<T, V> task;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:fl_query/src/base_operation.dart';
|
import 'package:fl_query/fl_query.dart';
|
||||||
import 'package:fl_query/src/mixins/autocast.dart';
|
import 'package:fl_query/src/base_query.dart';
|
||||||
import 'package:fl_query/src/models/query_job.dart';
|
import 'package:fl_query/src/models/query_job.dart';
|
||||||
import 'package:fl_query/src/utils.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
enum QueryStatus {
|
enum QueryStatus {
|
||||||
@@ -37,76 +36,29 @@ typedef ListenerUnsubscriber = void Function();
|
|||||||
|
|
||||||
typedef QueryUpdateFunction<T> = FutureOr<T> Function(T? oldData);
|
typedef QueryUpdateFunction<T> = FutureOr<T> Function(T? oldData);
|
||||||
|
|
||||||
class Query<T extends Object, Outside> extends BaseOperation<T> with AutoCast {
|
class Query<T extends Object, Outside> extends BaseQuery<T, Outside, dynamic> {
|
||||||
// all params
|
|
||||||
final String queryKey;
|
|
||||||
QueryTaskFunction<T, Outside> task;
|
QueryTaskFunction<T, Outside> task;
|
||||||
|
|
||||||
bool? refetchOnMount;
|
|
||||||
bool? refetchOnReconnect;
|
|
||||||
|
|
||||||
final T? _initialData;
|
|
||||||
|
|
||||||
// got from global options
|
|
||||||
Duration _staleTime;
|
|
||||||
|
|
||||||
/// total count of how many times the query retried to get a successful
|
|
||||||
/// result
|
|
||||||
int refetchCount = 0;
|
|
||||||
bool enabled;
|
|
||||||
|
|
||||||
QueryStatus status;
|
|
||||||
|
|
||||||
final Set<QueryListener<T>> _onDataListeners = Set<QueryListener<T>>();
|
|
||||||
final Set<QueryListener<dynamic>> _onErrorListeners =
|
|
||||||
Set<QueryListener<dynamic>>();
|
|
||||||
|
|
||||||
// externalData will always be passed to the task Callback
|
|
||||||
// it will change based on the presence of QueryBuilder
|
|
||||||
Outside _externalData;
|
|
||||||
|
|
||||||
Outside? _prevUsedExternalData;
|
|
||||||
|
|
||||||
Duration? refetchInterval;
|
|
||||||
|
|
||||||
Timer? _refetchIntervalTimer;
|
|
||||||
|
|
||||||
Connectivity _connectivity;
|
|
||||||
|
|
||||||
T? _previousData;
|
|
||||||
|
|
||||||
Query({
|
Query({
|
||||||
required this.queryKey,
|
required super.queryKey,
|
||||||
required this.task,
|
required this.task,
|
||||||
required Duration staleTime,
|
required super.staleTime,
|
||||||
required super.cacheTime,
|
required super.cacheTime,
|
||||||
required Outside externalData,
|
required super.externalData,
|
||||||
required super.retries,
|
required super.retries,
|
||||||
required super.retryDelay,
|
required super.retryDelay,
|
||||||
required super.queryBowl,
|
required super.queryBowl,
|
||||||
this.refetchOnMount,
|
required super.status,
|
||||||
this.refetchOnReconnect,
|
super.refetchOnMount,
|
||||||
this.refetchInterval,
|
super.refetchOnReconnect,
|
||||||
this.enabled = true,
|
super.refetchInterval,
|
||||||
T? previousData,
|
super.enabled,
|
||||||
Connectivity? connectivity,
|
super.previousData,
|
||||||
T? initialData,
|
super.connectivity,
|
||||||
QueryListener<T>? onData,
|
super.initialData,
|
||||||
QueryListener<dynamic>? onError,
|
super.onData,
|
||||||
}) : _staleTime = staleTime,
|
super.onError,
|
||||||
_initialData = initialData,
|
});
|
||||||
_externalData = externalData,
|
|
||||||
status = previousData == null ? QueryStatus.idle : QueryStatus.success,
|
|
||||||
_connectivity = connectivity ?? Connectivity(),
|
|
||||||
_previousData = previousData,
|
|
||||||
super(data: previousData ?? initialData) {
|
|
||||||
if (onData != null) _onDataListeners.add(onData);
|
|
||||||
if (onError != null) _onErrorListeners.add(onError);
|
|
||||||
|
|
||||||
if (refetchInterval != null && refetchInterval != Duration.zero) {
|
|
||||||
_refetchIntervalTimer = _createRefetchTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Query.fromOptions(
|
Query.fromOptions(
|
||||||
QueryJob<T, Outside> options, {
|
QueryJob<T, Outside> options, {
|
||||||
@@ -115,166 +67,23 @@ class Query<T extends Object, Outside> extends BaseOperation<T> with AutoCast {
|
|||||||
T? previousData,
|
T? previousData,
|
||||||
QueryListener<T>? onData,
|
QueryListener<T>? onData,
|
||||||
QueryListener<dynamic>? onError,
|
QueryListener<dynamic>? onError,
|
||||||
}) : queryKey = options.queryKey,
|
}) : task = options.task,
|
||||||
enabled = options.enabled ?? true,
|
|
||||||
task = options.task,
|
|
||||||
_staleTime = options.staleTime ?? const Duration(milliseconds: 500),
|
|
||||||
_initialData = options.initialData,
|
|
||||||
_externalData = externalData,
|
|
||||||
refetchInterval = options.refetchInterval,
|
|
||||||
refetchOnMount = options.refetchOnMount,
|
|
||||||
refetchOnReconnect = options.refetchOnReconnect,
|
|
||||||
status = previousData == null ? QueryStatus.idle : QueryStatus.success,
|
|
||||||
_connectivity = options.connectivity ?? Connectivity(),
|
|
||||||
_previousData = previousData,
|
|
||||||
super(
|
super(
|
||||||
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
|
cacheTime: options.cacheTime ?? const Duration(minutes: 5),
|
||||||
retries: options.retries ?? 3,
|
retries: options.retries ?? 3,
|
||||||
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
|
retryDelay: options.retryDelay ?? const Duration(milliseconds: 200),
|
||||||
data: previousData ?? options.initialData,
|
externalData: externalData,
|
||||||
) {
|
enabled: options.enabled ?? true,
|
||||||
if (onData != null) _onDataListeners.add(onData);
|
staleTime: options.staleTime ?? const Duration(milliseconds: 500),
|
||||||
if (onError != null) _onErrorListeners.add(onError);
|
initialData: options.initialData,
|
||||||
if (refetchInterval != null && refetchInterval != Duration.zero) {
|
refetchInterval: options.refetchInterval,
|
||||||
_refetchIntervalTimer = _createRefetchTimer();
|
refetchOnMount: options.refetchOnMount,
|
||||||
}
|
refetchOnReconnect: options.refetchOnReconnect,
|
||||||
}
|
status: previousData == null ? QueryStatus.idle : QueryStatus.success,
|
||||||
|
connectivity: options.connectivity ?? Connectivity(),
|
||||||
// all getters & setters
|
previousData: previousData,
|
||||||
|
queryKey: options.queryKey,
|
||||||
Outside get externalData => _externalData;
|
);
|
||||||
Outside? get prevUsedExternalData => _prevUsedExternalData;
|
|
||||||
|
|
||||||
Timer _createRefetchTimer() {
|
|
||||||
return Timer.periodic(
|
|
||||||
refetchInterval!,
|
|
||||||
(_) async {
|
|
||||||
// only refetch if its connected to the internet or refetch will
|
|
||||||
// always result in error while there's no internet
|
|
||||||
if (isStale && await isInternetConnected()) await refetch();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls the task function & doesn't check if there's already
|
|
||||||
/// cached data available
|
|
||||||
Future<void> _execute() async {
|
|
||||||
try {
|
|
||||||
retryAttempts = 0;
|
|
||||||
data = await task(
|
|
||||||
queryKey,
|
|
||||||
_externalData,
|
|
||||||
);
|
|
||||||
_prevUsedExternalData = _externalData;
|
|
||||||
updatedAt = DateTime.now();
|
|
||||||
status = QueryStatus.success;
|
|
||||||
for (final onData in _onDataListeners) {
|
|
||||||
onData(data!);
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
} catch (e) {
|
|
||||||
if (retries == 0) {
|
|
||||||
status = QueryStatus.error;
|
|
||||||
error = e;
|
|
||||||
for (final onError in _onErrorListeners) {
|
|
||||||
onError(error);
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
} else {
|
|
||||||
// retrying for retry count if failed for the first time
|
|
||||||
while (retryAttempts <= retries) {
|
|
||||||
await Future.delayed(retryDelay);
|
|
||||||
try {
|
|
||||||
data = await task(
|
|
||||||
queryKey,
|
|
||||||
_externalData,
|
|
||||||
);
|
|
||||||
_prevUsedExternalData = _externalData;
|
|
||||||
status = QueryStatus.success;
|
|
||||||
for (final onData in _onDataListeners) {
|
|
||||||
await onData(data!);
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
break;
|
|
||||||
} catch (e) {
|
|
||||||
if (retryAttempts == retries) {
|
|
||||||
status = QueryStatus.error;
|
|
||||||
error = e;
|
|
||||||
for (final onError in _onErrorListeners) {
|
|
||||||
await onError(error);
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
retryAttempts++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addDataListener(QueryListener<T> listener) {
|
|
||||||
_onDataListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addErrorListener(QueryListener<dynamic> listener) {
|
|
||||||
_onErrorListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeDataListener(QueryListener<T> listener) {
|
|
||||||
_onDataListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeErrorListener(QueryListener<dynamic> listener) {
|
|
||||||
_onErrorListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetches data or runs the provided task initially
|
|
||||||
///
|
|
||||||
/// Once [data] is available it won't run the [task] ever again
|
|
||||||
/// and will only return the available data
|
|
||||||
///
|
|
||||||
/// If a [fetch] is already running in the background it'll just return
|
|
||||||
/// the current available [data] (which can be nul if no [initialPage]
|
|
||||||
/// was provided) instead of running the task to prevent race conditions
|
|
||||||
Future<T?> fetch() async {
|
|
||||||
if (!enabled) return null;
|
|
||||||
|
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
|
||||||
/// refetching. So [_execute] again can create a race condition
|
|
||||||
if (isLoading || isRefetching || (hasData && !isPreviousData)) return data;
|
|
||||||
status = QueryStatus.loading;
|
|
||||||
notifyListeners();
|
|
||||||
return _execute().then((_) {
|
|
||||||
fetched = true;
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// refetches a valid or invalid [Query]
|
|
||||||
///
|
|
||||||
/// When called before calling [fetch] in a [Query] it'll
|
|
||||||
/// automatically run [fetch]
|
|
||||||
///
|
|
||||||
/// But if it's used to fetch the first data of a non-enabled [Query]
|
|
||||||
/// aka `LazyQuery`, it'll execute the task & will set the status
|
|
||||||
/// `enabled=true`
|
|
||||||
///
|
|
||||||
/// If a [refetch] is already running in the background it'll just return
|
|
||||||
/// the current available [data] instead of running the task to prevent
|
|
||||||
/// race conditions
|
|
||||||
Future<T?> refetch() async {
|
|
||||||
/// if isLoading/isRefetching is true that means its already fetching/
|
|
||||||
/// refetching. So [_execute] again can create a race condition
|
|
||||||
if (isRefetching || isLoading) return data;
|
|
||||||
if (enabled && !fetched) return await fetch();
|
|
||||||
status = QueryStatus.refetching;
|
|
||||||
refetchCount++;
|
|
||||||
// disabling the lazy query bound when query was actually called
|
|
||||||
if (!enabled) enabled = true;
|
|
||||||
notifyListeners();
|
|
||||||
return await _execute().then((_) => data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// can be used to update the data manually. Can be useful when used
|
/// can be used to update the data manually. Can be useful when used
|
||||||
/// together with mutations to perform optimistic updates or manual data
|
/// together with mutations to perform optimistic updates or manual data
|
||||||
@@ -293,105 +102,6 @@ class Query<T extends Object, Outside> extends BaseOperation<T> with AutoCast {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [externalData] from outside of the query
|
|
||||||
///
|
|
||||||
/// Remember, it's for the very instance of [Query]
|
|
||||||
/// So this won't persist through later UI/[Query] updates
|
|
||||||
void setExternalData(Outside externalData) {
|
|
||||||
_prevUsedExternalData = _externalData;
|
|
||||||
_externalData = externalData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets the query
|
|
||||||
///
|
|
||||||
/// The values of internal state of the query are reset to the
|
|
||||||
/// initial ones
|
|
||||||
void reset() {
|
|
||||||
refetchCount = 0;
|
|
||||||
data = _previousData ?? _initialData;
|
|
||||||
error = null;
|
|
||||||
fetched = false;
|
|
||||||
status = QueryStatus.idle;
|
|
||||||
retryAttempts = 0;
|
|
||||||
_onDataListeners.clear();
|
|
||||||
_onErrorListeners.clear();
|
|
||||||
mounts.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update configurations of the query
|
|
||||||
/// after already creating the Query instance
|
|
||||||
///
|
|
||||||
/// Remember, it's just for the single query instance
|
|
||||||
/// In the next UI update/render the options will get reset
|
|
||||||
/// to the default ones defined in the [QueryJob] or [QueryBowlScope]
|
|
||||||
void updateDefaultOptions({
|
|
||||||
Duration? refetchInterval,
|
|
||||||
Duration? staleTime,
|
|
||||||
Duration? cacheTime,
|
|
||||||
bool? refetchOnMount,
|
|
||||||
bool? refetchOnReconnect,
|
|
||||||
}) {
|
|
||||||
if (this.refetchInterval == null &&
|
|
||||||
refetchInterval != null &&
|
|
||||||
refetchInterval != Duration.zero) {
|
|
||||||
this.refetchInterval = refetchInterval;
|
|
||||||
_refetchIntervalTimer?.cancel();
|
|
||||||
_refetchIntervalTimer = _createRefetchTimer();
|
|
||||||
}
|
|
||||||
if (this.cacheTime == Duration(minutes: 5) && cacheTime != null)
|
|
||||||
this.cacheTime = cacheTime;
|
|
||||||
if (this._staleTime == const Duration(milliseconds: 500) &&
|
|
||||||
staleTime != null) this._staleTime = staleTime;
|
|
||||||
if (this.refetchOnMount == null && refetchOnMount != null)
|
|
||||||
this.refetchOnMount = refetchOnMount;
|
|
||||||
if (this.refetchOnReconnect == null && refetchOnReconnect != null)
|
|
||||||
this.refetchOnReconnect = refetchOnReconnect;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// checks if the application is connected to internet in any mean
|
|
||||||
///
|
|
||||||
/// It's true when any one this is connected -
|
|
||||||
/// - ethernet
|
|
||||||
/// - mobile
|
|
||||||
/// - wifi
|
|
||||||
Future<bool> isInternetConnected() async {
|
|
||||||
return isConnectedToInternet(await _connectivity.checkConnectivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// invalidates the query
|
|
||||||
///
|
|
||||||
/// Forcefully makes the query stale & expired which results in a refetch
|
|
||||||
/// when met conditions
|
|
||||||
void invalidate() {
|
|
||||||
/// subtracting [staleTime] from [updatedAt] as staleTime=Duration.zero
|
|
||||||
/// indicates the query must never become stale but subtracting the
|
|
||||||
/// [staleTime] will always revert the updatedAt time to the default
|
|
||||||
/// time whenever isStale is called
|
|
||||||
updatedAt = updatedAt.subtract(_staleTime);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isStale {
|
|
||||||
/// when [_staleTime] is [Duration.zero], the query will always be
|
|
||||||
/// stale & will never refetch in the background. But can be inactive
|
|
||||||
/// if [mounts.length] become zero
|
|
||||||
if (_staleTime == Duration.zero) return false;
|
|
||||||
|
|
||||||
// when [DateTime.now()] is after [update_at + stale_time] it means
|
|
||||||
// the data has become stale
|
|
||||||
return DateTime.now().isAfter(updatedAt.add(_staleTime));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isError => status == QueryStatus.error;
|
|
||||||
bool get isIdle => status == QueryStatus.idle;
|
|
||||||
bool get isLoading => status == QueryStatus.loading;
|
|
||||||
bool get isRefetching => status == QueryStatus.refetching;
|
|
||||||
bool get isSuccess => status == QueryStatus.success;
|
|
||||||
bool get isPreviousData {
|
|
||||||
return _previousData != null ? _previousData == data : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get debugLabel => "Query($queryKey)";
|
String get debugLabel => "Query($queryKey)";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -408,14 +118,19 @@ class Query<T extends Object, Outside> extends BaseOperation<T> with AutoCast {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return debugLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) {
|
bool operator ==(other) {
|
||||||
return (other is Query<T, Outside> && other.queryKey == queryKey) ||
|
return (other is Query<T, Outside> && other.queryKey == queryKey) ||
|
||||||
identical(other, this);
|
identical(other, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<void> setData() async {
|
||||||
|
data = await task(queryKey, externalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setError(e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -437,17 +437,17 @@ class QueryBowl extends InheritedWidget {
|
|||||||
if (prevInfiniteQuery is InfiniteQuery<T, Outside, PageParam>) {
|
if (prevInfiniteQuery is InfiniteQuery<T, Outside, PageParam>) {
|
||||||
// run the query if its still not called or if externalData has
|
// run the query if its still not called or if externalData has
|
||||||
// changed
|
// changed
|
||||||
// if (prevQuery.prevUsedExternalData != null &&
|
if (prevInfiniteQuery.prevUsedExternalData != null &&
|
||||||
// externalData != null &&
|
externalData != null &&
|
||||||
// !isShallowEqual(
|
!isShallowEqual(
|
||||||
// prevQuery.prevUsedExternalData!,
|
prevInfiniteQuery.prevUsedExternalData!,
|
||||||
// externalData,
|
externalData,
|
||||||
// )) {
|
)) {
|
||||||
// prevQuery.setExternalData(externalData);
|
prevInfiniteQuery.setExternalData(externalData);
|
||||||
// }
|
}
|
||||||
// prevQuery.mount(key);
|
prevInfiniteQuery.mount(key);
|
||||||
// if (onData != null) prevQuery.addDataListener(onData);
|
if (onData != null) prevInfiniteQuery.addDataListener(onData);
|
||||||
// if (onError != null) prevQuery.addErrorListener(onError);
|
if (onError != null) prevInfiniteQuery.addErrorListener(onError);
|
||||||
// mounting the widget that is using the query in the prevQuery
|
// mounting the widget that is using the query in the prevQuery
|
||||||
return prevInfiniteQuery;
|
return prevInfiniteQuery;
|
||||||
}
|
}
|
||||||
@@ -458,6 +458,7 @@ class QueryBowl extends InheritedWidget {
|
|||||||
final infiniteQuery = InfiniteQuery<T, Outside, PageParam>.fromOptions(
|
final infiniteQuery = InfiniteQuery<T, Outside, PageParam>.fromOptions(
|
||||||
infiniteQueryJob,
|
infiniteQueryJob,
|
||||||
externalData: externalData,
|
externalData: externalData,
|
||||||
|
queryBowl: this,
|
||||||
);
|
);
|
||||||
// if (onData != null) infiniteQuery.addDataListener(onData);
|
// if (onData != null) infiniteQuery.addDataListener(onData);
|
||||||
// if (onError != null) infiniteQuery.addErrorListener(onError);
|
// if (onError != null) infiniteQuery.addErrorListener(onError);
|
||||||
|
|||||||
Reference in New Issue
Block a user