utils tests created
TQueryFnData, TData & TQueryData now only can be Maps or subclass of Map
This commit is contained in:
@@ -29,7 +29,8 @@ void main() async {
|
|||||||
var key = QueryKey("TEST");
|
var key = QueryKey("TEST");
|
||||||
QueryClient queryClient = QueryClient();
|
QueryClient queryClient = QueryClient();
|
||||||
queryClient.mount();
|
queryClient.mount();
|
||||||
var data = await queryClient.fetchQuery<Map, dynamic, Map>(
|
var data = await queryClient
|
||||||
|
.fetchQuery<Map<String, dynamic>, dynamic, Map<String, dynamic>>(
|
||||||
queryKey: key,
|
queryKey: key,
|
||||||
queryFn: (context) {
|
queryFn: (context) {
|
||||||
return Future.value(todos.first);
|
return Future.value(todos.first);
|
||||||
@@ -39,7 +40,7 @@ void main() async {
|
|||||||
print(data);
|
print(data);
|
||||||
print("======CACHED DATA======");
|
print("======CACHED DATA======");
|
||||||
print(queryClient.getQueryData(key));
|
print(queryClient.getQueryData(key));
|
||||||
queryClient.setQueryData<Map>(key, (prevData) {
|
queryClient.setQueryData<Map<String, dynamic>>(key, (prevData) {
|
||||||
return {
|
return {
|
||||||
...(prevData) ?? {},
|
...(prevData) ?? {},
|
||||||
"title": "Yehi aloh heh",
|
"title": "Yehi aloh heh",
|
||||||
|
|||||||
@@ -9,16 +9,19 @@ typedef QueryKeyHashFunction = String Function(QueryKey queryKey);
|
|||||||
typedef QueryFunction<T, TPageParam> = FutureOr<T> Function(
|
typedef QueryFunction<T, TPageParam> = FutureOr<T> Function(
|
||||||
QueryFunctionContext<TPageParam> context,
|
QueryFunctionContext<TPageParam> context,
|
||||||
);
|
);
|
||||||
typedef GetPreviousPageParamFunction<TQueryFnData> = Function(
|
typedef GetPreviousPageParamFunction<TQueryFnData extends Map<String, dynamic>>
|
||||||
|
= Function(
|
||||||
TQueryFnData firstPage,
|
TQueryFnData firstPage,
|
||||||
List<TQueryFnData> allPages,
|
List<TQueryFnData> allPages,
|
||||||
);
|
);
|
||||||
typedef GetNextPageParamFunction<TQueryFnData> = Function(
|
typedef GetNextPageParamFunction<TQueryFnData extends Map<String, dynamic>>
|
||||||
|
= Function(
|
||||||
TQueryFnData firstPage,
|
TQueryFnData firstPage,
|
||||||
List<TQueryFnData> allPages,
|
List<TQueryFnData> allPages,
|
||||||
);
|
);
|
||||||
|
|
||||||
class QueryOptions<TQueryFnData, TError, TData> {
|
class QueryOptions<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>> {
|
||||||
ShouldRetryFunction<TError>? retry;
|
ShouldRetryFunction<TError>? retry;
|
||||||
RetryDelayFunction<TError>? retryDelay;
|
RetryDelayFunction<TError>? retryDelay;
|
||||||
Duration? cacheTime;
|
Duration? cacheTime;
|
||||||
@@ -259,7 +262,7 @@ enum QueryStatus {
|
|||||||
success,
|
success,
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryObserverResult<TData, TError> {
|
class QueryObserverResult<TData extends Map<String, dynamic>, TError> {
|
||||||
TData? data;
|
TData? data;
|
||||||
DateTime? dataUpdatedAt;
|
DateTime? dataUpdatedAt;
|
||||||
TError? error;
|
TError? error;
|
||||||
@@ -373,7 +376,11 @@ class QueryObserverResult<TData, TError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef RefetchIntervalFunction<TQueryFnData, TError, TQueryData, TData>
|
typedef RefetchIntervalFunction<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TQueryData extends Map<String, dynamic>,
|
||||||
|
TData extends Map<String, dynamic>>
|
||||||
= Duration? Function(
|
= Duration? Function(
|
||||||
TData? data,
|
TData? data,
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
@@ -391,7 +398,11 @@ enum RefetchOnMount {
|
|||||||
always,
|
always,
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
|
class QueryObserverOptions<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>
|
||||||
extends QueryOptions<TQueryFnData, TError, TQueryData> {
|
extends QueryOptions<TQueryFnData, TError, TQueryData> {
|
||||||
bool? enabled;
|
bool? enabled;
|
||||||
Duration? staleTime;
|
Duration? staleTime;
|
||||||
@@ -520,7 +531,8 @@ class DefaultOptions<TError> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchQueryOptions<TQueryFnData, TError, TData>
|
class FetchQueryOptions<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>>
|
||||||
extends QueryOptions<TQueryFnData, TError, TData> {
|
extends QueryOptions<TQueryFnData, TError, TData> {
|
||||||
/// The time after data is considered stale.
|
/// The time after data is considered stale.
|
||||||
/// If the data is fresh it will be returned from the cache.
|
/// If the data is fresh it will be returned from the cache.
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class FetchOptions {
|
|||||||
FetchOptions({this.cancelRefetch, this.meta});
|
FetchOptions({this.cancelRefetch, this.meta});
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchContext<TQueryFnData, TError, TData> {
|
class FetchContext<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>> {
|
||||||
FutureOr<TQueryFnData> Function() fetchFn;
|
FutureOr<TQueryFnData> Function() fetchFn;
|
||||||
FetchOptions? fetchOptions;
|
FetchOptions? fetchOptions;
|
||||||
QueryOptions<TQueryFnData, TError, TData> options;
|
QueryOptions<TQueryFnData, TError, TData> options;
|
||||||
@@ -35,12 +36,13 @@ class FetchContext<TQueryFnData, TError, TData> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryBehavior<TQueryFnData, TError, TData> {
|
class QueryBehavior<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>> {
|
||||||
void Function(FetchContext<TQueryFnData, TError, TData> context) onFetch;
|
void Function(FetchContext<TQueryFnData, TError, TData> context) onFetch;
|
||||||
QueryBehavior({required this.onFetch});
|
QueryBehavior({required this.onFetch});
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryState<TData, TError> {
|
class QueryState<TData extends Map<String, dynamic>, TError> {
|
||||||
TData? data;
|
TData? data;
|
||||||
TError? error;
|
TError? error;
|
||||||
QueryStatus status;
|
QueryStatus status;
|
||||||
@@ -120,7 +122,7 @@ class SetStateOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Action<TData, TError> {
|
class Action<TData extends Map<String, dynamic>, TError> {
|
||||||
ActionType type;
|
ActionType type;
|
||||||
Object? meta;
|
Object? meta;
|
||||||
TData? data;
|
TData? data;
|
||||||
@@ -160,7 +162,8 @@ class Action<TData, TError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Query<TQueryFnData, TError, TData> {
|
class Query<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>> {
|
||||||
QueryKey queryKey;
|
QueryKey queryKey;
|
||||||
String queryHash;
|
String queryHash;
|
||||||
late QueryOptions<TQueryFnData, TError, TData> options;
|
late QueryOptions<TQueryFnData, TError, TData> options;
|
||||||
@@ -277,7 +280,7 @@ class Query<TQueryFnData, TError, TData> {
|
|||||||
data = prevData as TData;
|
data = prevData as TData;
|
||||||
} else if (this.options.structuralSharing != false) {
|
} else if (this.options.structuralSharing != false) {
|
||||||
// Structurally share data between prev and new data if needed
|
// Structurally share data between prev and new data if needed
|
||||||
data = replaceEqualDeep<TData>(prevData ?? {} as TData, data);
|
data = replaceEqualDeep(prevData ?? {} as TData, data);
|
||||||
}
|
}
|
||||||
// Set data and mark it as cached
|
// Set data and mark it as cached
|
||||||
_dispatch(Action(
|
_dispatch(Action(
|
||||||
@@ -360,7 +363,7 @@ class Query<TQueryFnData, TError, TData> {
|
|||||||
var observer =
|
var observer =
|
||||||
_observers.firstWhereOrNull((x) => x.options.queryFn != null);
|
_observers.firstWhereOrNull((x) => x.options.queryFn != null);
|
||||||
if (observer != null) {
|
if (observer != null) {
|
||||||
_setOptions(QueryOptions(
|
_setOptions(QueryOptions<TQueryFnData, TError, TData>(
|
||||||
queryKey: observer.options.queryKey,
|
queryKey: observer.options.queryKey,
|
||||||
queryKeyHashFn: observer.options.queryKeyHashFn,
|
queryKeyHashFn: observer.options.queryKeyHashFn,
|
||||||
cacheTime: observer.options.cacheTime,
|
cacheTime: observer.options.cacheTime,
|
||||||
@@ -368,7 +371,7 @@ class Query<TQueryFnData, TError, TData> {
|
|||||||
queryFn:
|
queryFn:
|
||||||
observer.options.queryFn as QueryFunction<TQueryFnData, dynamic>,
|
observer.options.queryFn as QueryFunction<TQueryFnData, dynamic>,
|
||||||
queryHash: observer.options.queryHash,
|
queryHash: observer.options.queryHash,
|
||||||
initialData: observer.options.initialData,
|
initialData: observer.options.initialData as TData?,
|
||||||
initialDataUpdatedAt: observer.options.initialDataUpdatedAt,
|
initialDataUpdatedAt: observer.options.initialDataUpdatedAt,
|
||||||
meta: observer.options.meta,
|
meta: observer.options.meta,
|
||||||
structuralSharing: observer.options.structuralSharing,
|
structuralSharing: observer.options.structuralSharing,
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ class QueryCache extends Subscribable<QueryCacheListener> {
|
|||||||
_queriesMap = {},
|
_queriesMap = {},
|
||||||
super();
|
super();
|
||||||
|
|
||||||
Query<TQueryFnData, TError, TData> build<TQueryFnData, TError, TData>(
|
Query<TQueryFnData, TError, TData> build<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>>(
|
||||||
QueryClient client,
|
QueryClient client,
|
||||||
QueryOptions<TQueryFnData, TError, TData> options, [
|
QueryOptions<TQueryFnData, TError, TData> options, [
|
||||||
QueryState<TData, TError>? state,
|
QueryState<TData, TError>? state,
|
||||||
@@ -124,8 +127,10 @@ class QueryCache extends Subscribable<QueryCacheListener> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Query<TQueryFnData, TError, TData>? get<TQueryFnData, TError, TData>(
|
Query<TQueryFnData, TError, TData>? get<
|
||||||
String queryHash) {
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>>(String queryHash) {
|
||||||
return _queriesMap[queryHash] as Query<TQueryFnData, TError, TData>?;
|
return _queriesMap[queryHash] as Query<TQueryFnData, TError, TData>?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,13 +138,16 @@ class QueryCache extends Subscribable<QueryCacheListener> {
|
|||||||
return _queries;
|
return _queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
Query? find<TQueryFnData, TError, TData>(
|
Query<TQueryFnData, TError, TData>? find<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>>(
|
||||||
QueryKey queryKey,
|
QueryKey queryKey,
|
||||||
QueryFilters queryFilters,
|
QueryFilters queryFilters,
|
||||||
) {
|
) {
|
||||||
queryFilters.exact ??= true;
|
queryFilters.exact ??= true;
|
||||||
return _queries
|
return _queries.firstWhereOrNull((query) => matchQuery(queryFilters, query))
|
||||||
.firstWhereOrNull((query) => matchQuery(queryFilters, query));
|
as Query<TQueryFnData, TError, TData>?;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Query> findAll(QueryKey? queryKey, [QueryFilters? filters]) {
|
List<Query> findAll(QueryKey? queryKey, [QueryFilters? filters]) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class MutationDefaults {
|
|||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryData<TData> {
|
class QueryData<TData extends Map<String, dynamic>> {
|
||||||
QueryKey queryKey;
|
QueryKey queryKey;
|
||||||
TData data;
|
TData data;
|
||||||
QueryData({
|
QueryData({
|
||||||
@@ -85,17 +85,18 @@ class QueryClient {
|
|||||||
// return _mutationCache.findAll({ ...filters, fetching: true }).length
|
// return _mutationCache.findAll({ ...filters, fetching: true }).length
|
||||||
// }
|
// }
|
||||||
|
|
||||||
TData? getQueryData<TData>(
|
TData? getQueryData<TData extends Map<String, dynamic>>(
|
||||||
QueryKey queryKey, [
|
QueryKey queryKey, [
|
||||||
QueryFilters? filters,
|
QueryFilters? filters,
|
||||||
]) {
|
]) {
|
||||||
return _queryCache
|
return _queryCache
|
||||||
.find<TData, dynamic, dynamic>(queryKey, filters ?? QueryFilters())
|
.find<TData, dynamic, Map<String, dynamic>>(
|
||||||
|
queryKey, filters ?? QueryFilters())
|
||||||
?.state
|
?.state
|
||||||
.data;
|
.data as TData?;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<QueryData<TData>> getQueriesData<TData>({
|
List<QueryData<TData>> getQueriesData<TData extends Map<String, dynamic>>({
|
||||||
QueryKey? queryKey,
|
QueryKey? queryKey,
|
||||||
QueryFilters? filters,
|
QueryFilters? filters,
|
||||||
}) {
|
}) {
|
||||||
@@ -107,26 +108,27 @@ class QueryClient {
|
|||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
TData setQueryData<TData>(
|
TData setQueryData<TData extends Map<String, dynamic>>(
|
||||||
QueryKey queryKey,
|
QueryKey queryKey,
|
||||||
DataUpdateFunction<TData?, TData> updater, [
|
DataUpdateFunction<TData?, TData> updater, [
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
]) {
|
]) {
|
||||||
final QueryOptions<dynamic, dynamic, TData> defaultedOptions =
|
final QueryOptions<Map<String, dynamic>, dynamic, TData> defaultedOptions =
|
||||||
QueryOptions<dynamic, dynamic, TData>.fromJson(
|
QueryOptions<Map<String, dynamic>, dynamic, TData>.fromJson(
|
||||||
defaultQueryOptions<dynamic, dynamic, TData, dynamic>(
|
defaultQueryOptions<Map<String, dynamic>, dynamic, TData,
|
||||||
QueryObserverOptions<dynamic, dynamic, TData, dynamic>(
|
Map<String, dynamic>>(
|
||||||
queryKey: queryKey))
|
QueryObserverOptions<Map<String, dynamic>, dynamic, TData,
|
||||||
|
Map<String, dynamic>>(queryKey: queryKey))
|
||||||
.toJson());
|
.toJson());
|
||||||
return _queryCache
|
return _queryCache
|
||||||
.build<dynamic, dynamic, TData>(this, defaultedOptions)
|
.build<Map<String, dynamic>, dynamic, TData>(this, defaultedOptions)
|
||||||
.setData(
|
.setData(
|
||||||
updater,
|
updater,
|
||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<QueryData> setQueriesData<TData>({
|
List<QueryData> setQueriesData<TData extends Map<String, dynamic>>({
|
||||||
required DataUpdateFunction<TData?, TData> updater,
|
required DataUpdateFunction<TData?, TData> updater,
|
||||||
QueryKey? queryKey,
|
QueryKey? queryKey,
|
||||||
QueryFilters? filters,
|
QueryFilters? filters,
|
||||||
@@ -149,12 +151,13 @@ class QueryClient {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryState<TData, TError>? getQueryState<TData, TError>(
|
QueryState<TData, TError>?
|
||||||
|
getQueryState<TData extends Map<String, dynamic>, TError>(
|
||||||
QueryKey queryKey, [
|
QueryKey queryKey, [
|
||||||
QueryFilters? filters,
|
QueryFilters? filters,
|
||||||
]) {
|
]) {
|
||||||
return _queryCache
|
return _queryCache
|
||||||
.find<TData, TError, dynamic>(
|
.find<TData, TError, Map<String, dynamic>>(
|
||||||
queryKey,
|
queryKey,
|
||||||
filters ?? QueryFilters(),
|
filters ?? QueryFilters(),
|
||||||
)
|
)
|
||||||
@@ -257,13 +260,15 @@ class QueryClient {
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TData> fetchQuery<TQueryFnData, TError, TData>({
|
Future<TData> fetchQuery<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>>({
|
||||||
QueryKey? queryKey,
|
QueryKey? queryKey,
|
||||||
QueryFunction<TQueryFnData, dynamic>? queryFn,
|
QueryFunction<TQueryFnData, dynamic>? queryFn,
|
||||||
FetchQueryOptions<TQueryFnData, TError, TData>? options,
|
FetchQueryOptions<TQueryFnData, TError, TData>? options,
|
||||||
}) {
|
}) {
|
||||||
var defaultedOptions = this.defaultQueryOptions(
|
var defaultedOptions = this.defaultQueryOptions(
|
||||||
QueryObserverOptions(
|
QueryObserverOptions<Map<String, dynamic>, dynamic, Map<String, dynamic>,
|
||||||
|
TData>(
|
||||||
queryFn: queryFn,
|
queryFn: queryFn,
|
||||||
queryKey: queryKey,
|
queryKey: queryKey,
|
||||||
staleTime: options?.staleTime,
|
staleTime: options?.staleTime,
|
||||||
@@ -280,28 +285,33 @@ class QueryClient {
|
|||||||
);
|
);
|
||||||
// returning 0 indicates turing off retry
|
// returning 0 indicates turing off retry
|
||||||
defaultedOptions.retry ??= (_, __) => 0;
|
defaultedOptions.retry ??= (_, __) => 0;
|
||||||
var query = _queryCache.build(this, defaultedOptions);
|
var query = _queryCache.build<Map<String, dynamic>, dynamic, TData>(
|
||||||
|
this, defaultedOptions);
|
||||||
return query.isStaleByTime(defaultedOptions.staleTime)
|
return query.isStaleByTime(defaultedOptions.staleTime)
|
||||||
? query.fetch(defaultedOptions)
|
? query.fetch(defaultedOptions)
|
||||||
: Future.value(query.state.data as TData);
|
: Future.value(query.state.data as TData);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> prefetchQuery<TQueryFnData, TError, TData>({
|
Future<void> prefetchQuery<TQueryFnData extends Map<String, dynamic>, TError,
|
||||||
|
TData extends Map<String, dynamic>>({
|
||||||
QueryKey? queryKey,
|
QueryKey? queryKey,
|
||||||
QueryFunction<TQueryFnData, dynamic>? queryFn,
|
QueryFunction<TQueryFnData, dynamic>? queryFn,
|
||||||
FetchQueryOptions<TQueryFnData, TError, TData>? options,
|
FetchQueryOptions<TQueryFnData, TError, TData>? options,
|
||||||
}) {
|
}) {
|
||||||
return fetchQuery(
|
return fetchQuery<TQueryFnData, dynamic, TData>(
|
||||||
queryKey: queryKey,
|
queryKey: queryKey,
|
||||||
queryFn: queryFn,
|
queryFn: queryFn,
|
||||||
options: options,
|
options: options,
|
||||||
).then(noop).catchError(noop);
|
).then(noop).catchError(noop);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
|
QueryObserverOptions<TQueryFnData, TError, TData,
|
||||||
defaultQueryOptions<TQueryFnData, TError, TData, TQueryData>(
|
TQueryData> defaultQueryOptions<
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>?
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
options) {
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>? options) {
|
||||||
if (options?.defaulted == true) return options!;
|
if (options?.defaulted == true) return options!;
|
||||||
var defaultedOptions =
|
var defaultedOptions =
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>.fromJson({
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>.fromJson({
|
||||||
@@ -322,7 +332,11 @@ class QueryClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>
|
||||||
defaultQueryObserverOptions<TQueryFnData, TError, TData, TQueryData>([
|
defaultQueryObserverOptions<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>([
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>? options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>? options,
|
||||||
]) {
|
]) {
|
||||||
return this.defaultQueryOptions(options);
|
return this.defaultQueryOptions(options);
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import 'package:fl_query/src/core/subscribable.dart';
|
|||||||
import 'package:fl_query/src/core/utils.dart';
|
import 'package:fl_query/src/core/utils.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
typedef QueryObserverListener<TData, TError> = void Function(
|
typedef QueryObserverListener<TData extends Map<String, dynamic>, TError> = void
|
||||||
QueryObserverResult<TData, TError> result);
|
Function(QueryObserverResult<TData, TError> result);
|
||||||
|
|
||||||
class NotifyOptions {
|
class NotifyOptions {
|
||||||
bool? cache;
|
bool? cache;
|
||||||
@@ -52,13 +52,18 @@ class ObserverFetchOptions extends FetchOptions {
|
|||||||
}) : super(cancelRefetch: cancelRefetch, meta: meta);
|
}) : super(cancelRefetch: cancelRefetch, meta: meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelectQuery<TQueryData, TData> {
|
class SelectQuery<TQueryData extends Map<String, dynamic>,
|
||||||
|
TData extends Map<String, dynamic>> {
|
||||||
TData Function(TQueryData data) fn;
|
TData Function(TQueryData data) fn;
|
||||||
TData result;
|
TData result;
|
||||||
SelectQuery(this.fn, this.result);
|
SelectQuery(this.fn, this.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
class QueryObserver<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>
|
||||||
extends Subscribable<QueryObserverListener> {
|
extends Subscribable<QueryObserverListener> {
|
||||||
late QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options;
|
late QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options;
|
||||||
QueryClient _client;
|
QueryClient _client;
|
||||||
@@ -70,7 +75,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
_currentResultOptions;
|
_currentResultOptions;
|
||||||
QueryObserverResult<TData, TError>? _previousQueryResult;
|
QueryObserverResult<TData, TError>? _previousQueryResult;
|
||||||
Exception? _previousSelectError;
|
Exception? _previousSelectError;
|
||||||
SelectQuery? _previousSelect;
|
SelectQuery<TQueryData, TData>? _previousSelect;
|
||||||
Timer? _staleTimeout;
|
Timer? _staleTimeout;
|
||||||
Timer? _refetchInterval;
|
Timer? _refetchInterval;
|
||||||
Duration? _currentRefetchInterval;
|
Duration? _currentRefetchInterval;
|
||||||
@@ -119,8 +124,8 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>? options, [
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>? options, [
|
||||||
NotifyOptions? notifyOptions,
|
NotifyOptions? notifyOptions,
|
||||||
]) {
|
]) {
|
||||||
var prevOptions = this.options;
|
final prevOptions = this.options;
|
||||||
var prevQuery = _currentQuery;
|
final prevQuery = _currentQuery;
|
||||||
|
|
||||||
this.options = this._client.defaultQueryObserverOptions(options);
|
this.options = this._client.defaultQueryObserverOptions(options);
|
||||||
|
|
||||||
@@ -190,7 +195,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
Future<QueryObserverResult<TData, TError>> getNextResult(
|
Future<QueryObserverResult<TData, TError>> getNextResult(
|
||||||
bool? throwOnError,
|
bool? throwOnError,
|
||||||
) {
|
) {
|
||||||
var completer = Completer<QueryObserverResult<TData, TError>>();
|
final completer = Completer<QueryObserverResult<TData, TError>>();
|
||||||
var unsubscribe;
|
var unsubscribe;
|
||||||
unsubscribe = subscribe((result) {
|
unsubscribe = subscribe((result) {
|
||||||
if (!result.isFetching) {
|
if (!result.isFetching) {
|
||||||
@@ -215,8 +220,8 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
|
|
||||||
Future<QueryObserverResult<TData, TError>> fetchOptimistic(
|
Future<QueryObserverResult<TData, TError>> fetchOptimistic(
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options) {
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options) {
|
||||||
var defaultedOptions = _client.defaultQueryObserverOptions(options);
|
final defaultedOptions = _client.defaultQueryObserverOptions(options);
|
||||||
var query = _client.getQueryCache().build(_client, defaultedOptions);
|
final query = _client.getQueryCache().build(_client, defaultedOptions);
|
||||||
|
|
||||||
return query.fetch().then((val) {
|
return query.fetch().then((val) {
|
||||||
return createResult(query, defaultedOptions);
|
return createResult(query, defaultedOptions);
|
||||||
@@ -269,7 +274,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
Map<String, dynamic> prevResultMap = prevResult.toJson();
|
Map<String, dynamic> prevResultMap = prevResult.toJson();
|
||||||
|
|
||||||
return resultMap.keys.any((key) {
|
return resultMap.keys.any((key) {
|
||||||
var changed = resultMap[key] != prevResultMap[key];
|
final changed = resultMap[key] != prevResultMap[key];
|
||||||
bool? isIncluded = includedProps?.any((x) => x == key);
|
bool? isIncluded = includedProps?.any((x) => x == key);
|
||||||
bool isExcluded =
|
bool isExcluded =
|
||||||
options.notifyOnChangePropsExclusions?.any((x) => x == key) ?? false;
|
options.notifyOnChangePropsExclusions?.any((x) => x == key) ?? false;
|
||||||
@@ -280,7 +285,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateResult([NotifyOptions? notifyOptions]) {
|
void updateResult([NotifyOptions? notifyOptions]) {
|
||||||
QueryObserverResult<TData, TError>? prevResult = _currentResult;
|
final QueryObserverResult<TData, TError>? prevResult = _currentResult;
|
||||||
|
|
||||||
if (_currentQuery != null)
|
if (_currentQuery != null)
|
||||||
_currentResult = this.createResult(_currentQuery!, this.options);
|
_currentResult = this.createResult(_currentQuery!, this.options);
|
||||||
@@ -288,7 +293,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
_currentResultOptions = this.options;
|
_currentResultOptions = this.options;
|
||||||
|
|
||||||
// Only notify if something has changed
|
// Only notify if something has changed
|
||||||
if (shallowEqualMap(_currentResult.toJson(), prevResult.toJson())) {
|
if (shallowEqualMap(_currentResult.toJson(), prevResult?.toJson())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NotifyOptions defaultNotifyOptions = NotifyOptions(cache: true);
|
NotifyOptions defaultNotifyOptions = NotifyOptions(cache: true);
|
||||||
@@ -304,11 +309,12 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateQuery() {
|
void _updateQuery() {
|
||||||
var query = this._client.getQueryCache().build(this._client, this.options);
|
final query =
|
||||||
|
this._client.getQueryCache().build(this._client, this.options);
|
||||||
|
|
||||||
if (query == _currentQuery) return;
|
if (query == _currentQuery) return;
|
||||||
|
|
||||||
var prevQuery = _currentQuery;
|
final prevQuery = _currentQuery;
|
||||||
_currentQuery = query;
|
_currentQuery = query;
|
||||||
_currentQueryInitialState = query.state;
|
_currentQueryInitialState = query.state;
|
||||||
_previousQueryResult = _currentResult;
|
_previousQueryResult = _currentResult;
|
||||||
@@ -339,22 +345,22 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
var prevQuery = _currentQuery;
|
final prevQuery = _currentQuery;
|
||||||
var prevOptions = this.options;
|
final prevOptions = this.options;
|
||||||
var prevResult = _currentResult;
|
final prevResult = _currentResult;
|
||||||
var prevResultState = _currentResultState;
|
final prevResultState = _currentResultState;
|
||||||
var prevResultOptions = _currentResultOptions;
|
final prevResultOptions = _currentResultOptions;
|
||||||
bool queryChange = query != prevQuery;
|
final bool queryChange = query != prevQuery;
|
||||||
var queryInitialState =
|
final queryInitialState =
|
||||||
queryChange ? query.state : _currentQueryInitialState;
|
queryChange ? query.state : _currentQueryInitialState;
|
||||||
var prevQueryResult = queryChange ? _currentResult : _previousQueryResult;
|
final prevQueryResult = queryChange ? _currentResult : _previousQueryResult;
|
||||||
|
|
||||||
var state = query.state;
|
final state = query.state;
|
||||||
var dataUpdatedAt = state.dataUpdatedAt;
|
DateTime? dataUpdatedAt = state.dataUpdatedAt;
|
||||||
var error = state.error;
|
TError? error = state.error;
|
||||||
var errorUpdatedAt = state.errorUpdatedAt;
|
DateTime? errorUpdatedAt = state.errorUpdatedAt;
|
||||||
var isFetching = state.isFetching;
|
bool isFetching = state.isFetching;
|
||||||
var status = state.status;
|
QueryStatus status = state.status;
|
||||||
|
|
||||||
bool isPreviousData = false;
|
bool isPreviousData = false;
|
||||||
bool isPlaceholderData = false;
|
bool isPlaceholderData = false;
|
||||||
@@ -362,9 +368,9 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
|
|
||||||
// Optimistically set result in fetching state if needed
|
// Optimistically set result in fetching state if needed
|
||||||
if (options.optimisticResults == true) {
|
if (options.optimisticResults == true) {
|
||||||
var mounted = hasListeners();
|
final bool mounted = hasListeners();
|
||||||
|
|
||||||
var fetchOnMount = !mounted && shouldFetchOnMount(query, options);
|
final bool fetchOnMount = !mounted && shouldFetchOnMount(query, options);
|
||||||
|
|
||||||
bool fetchOptionally = mounted &&
|
bool fetchOptionally = mounted &&
|
||||||
prevQuery != null &&
|
prevQuery != null &&
|
||||||
@@ -401,7 +407,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
try {
|
try {
|
||||||
data = options.select?.call(state.data);
|
data = options.select?.call(state.data);
|
||||||
if (options.structuralSharing != false) {
|
if (options.structuralSharing != false) {
|
||||||
data = replaceEqualDeep(prevResult?.data, data);
|
data = replaceEqualDeep(prevResult.data, data);
|
||||||
}
|
}
|
||||||
if (options.select != null && data != null) {
|
if (options.select != null && data != null) {
|
||||||
_previousSelect = SelectQuery<TQueryData, TData>(
|
_previousSelect = SelectQuery<TQueryData, TData>(
|
||||||
@@ -429,7 +435,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
(status == QueryStatus.loading || status == QueryStatus.idle)) {
|
(status == QueryStatus.loading || status == QueryStatus.idle)) {
|
||||||
var placeholderData;
|
var placeholderData;
|
||||||
|
|
||||||
if (prevResult?.isPlaceholderData == true &&
|
if (prevResult.isPlaceholderData == true &&
|
||||||
options.placeholderData == prevResultOptions?.placeholderData) {
|
options.placeholderData == prevResultOptions?.placeholderData) {
|
||||||
placeholderData = prevResult.data;
|
placeholderData = prevResult.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -439,7 +445,7 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
placeholderData = options.select?.call(placeholderData);
|
placeholderData = options.select?.call(placeholderData);
|
||||||
if (options.structuralSharing != false) {
|
if (options.structuralSharing != false) {
|
||||||
placeholderData =
|
placeholderData =
|
||||||
replaceEqualDeep(prevResult?.data, placeholderData);
|
replaceEqualDeep(prevResult.data, placeholderData);
|
||||||
}
|
}
|
||||||
_previousSelectError = null;
|
_previousSelectError = null;
|
||||||
} catch (selectError) {
|
} catch (selectError) {
|
||||||
@@ -459,7 +465,8 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryObserverResult<TData, TError> result = QueryObserverResult(
|
final QueryObserverResult<TData, TError> result =
|
||||||
|
QueryObserverResult<TData, TError>(
|
||||||
status: status,
|
status: status,
|
||||||
dataUpdatedAt: dataUpdatedAt,
|
dataUpdatedAt: dataUpdatedAt,
|
||||||
isLoading: status == QueryStatus.loading,
|
isLoading: status == QueryStatus.loading,
|
||||||
@@ -602,7 +609,11 @@ class QueryObserver<TQueryFnData, TError, TData, TQueryData>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldLoadOnMount<TQueryFnData, TError, TData, TQueryData>(
|
bool shouldLoadOnMount<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
@@ -612,7 +623,11 @@ bool shouldLoadOnMount<TQueryFnData, TError, TData, TQueryData>(
|
|||||||
options.retryOnMount == false));
|
options.retryOnMount == false));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldRefetchOnMount<TQueryFnData, TError, TData, TQueryData>(
|
bool shouldRefetchOnMount<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
@@ -623,7 +638,11 @@ bool shouldRefetchOnMount<TQueryFnData, TError, TData, TQueryData>(
|
|||||||
isStale(query, options))));
|
isStale(query, options))));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldFetchOnMount<TQueryFnData, TError, TData, TQueryData>(
|
bool shouldFetchOnMount<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
@@ -631,17 +650,26 @@ bool shouldFetchOnMount<TQueryFnData, TError, TData, TQueryData>(
|
|||||||
shouldRefetchOnMount(query, options));
|
shouldRefetchOnMount(query, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldFetchOnReconnect<TQueryFnData, TError, TData, TQueryData>(
|
bool shouldFetchOnReconnect<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
return (options.enabled != false &&
|
return (options.enabled != false &&
|
||||||
(options.refetchOnReconnect == RefetchOnReconnect.always ||
|
(options.refetchOnReconnect == RefetchOnReconnect.always ||
|
||||||
(options.refetchOnReconnect != RefetchOnReconnect.off &&
|
(options.refetchOnReconnect != RefetchOnReconnect.off &&
|
||||||
isStale(query, options))));
|
isStale<TQueryFnData, TError, TData, TQueryData>(
|
||||||
|
query, options))));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldFetchOptionally<TQueryFnData, TError, TData, TQueryData>(
|
bool shouldFetchOptionally<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
Query<TQueryFnData, TError, TQueryData> prevQuery,
|
Query<TQueryFnData, TError, TQueryData> prevQuery,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
@@ -653,7 +681,11 @@ bool shouldFetchOptionally<TQueryFnData, TError, TData, TQueryData>(
|
|||||||
isStale(query, options));
|
isStale(query, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isStale<TQueryFnData, TError, TData, TQueryData>(
|
bool isStale<
|
||||||
|
TQueryFnData extends Map<String, dynamic>,
|
||||||
|
TError,
|
||||||
|
TData extends Map<String, dynamic>,
|
||||||
|
TQueryData extends Map<String, dynamic>>(
|
||||||
Query<TQueryFnData, TError, TQueryData> query,
|
Query<TQueryFnData, TError, TQueryData> query,
|
||||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> options,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ bool isCancelledError(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef OnError<TError> = void Function(TError error);
|
typedef OnError<TError> = void Function(TError error);
|
||||||
typedef OnData<TData> = void Function(TData data);
|
typedef OnData<TData extends Map<String, dynamic>> = void Function(TData data);
|
||||||
|
|
||||||
class Retryer<TData, TError> {
|
class Retryer<TData extends Map<String, dynamic>, TError> {
|
||||||
late void Function({bool? revert, bool? silent}) cancel;
|
late void Function({bool? revert, bool? silent}) cancel;
|
||||||
late void Function() cancelRetry;
|
late void Function() cancelRetry;
|
||||||
late void Function() continueRetry;
|
late void Function() continueRetry;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:fl_query/src/core/models.dart';
|
import 'package:fl_query/src/core/models.dart';
|
||||||
import 'package:fl_query/src/core/query.dart';
|
import 'package:fl_query/src/core/query.dart';
|
||||||
import 'package:fl_query/src/core/query_key.dart';
|
import 'package:fl_query/src/core/query_key.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
/// Default query keys hash function.
|
/// Default query keys hash function.
|
||||||
/// Dummy function just to fill the gaps for original react-query like
|
/// Dummy function just to fill the gaps for original react-query like
|
||||||
@@ -95,41 +96,61 @@ bool shallowEqualMap(Map? a, Map? b) {
|
|||||||
/// If not, it will replace any deeply equal children of `b` with those
|
/// If not, it will replace any deeply equal children of `b` with those
|
||||||
/// of `a`\
|
/// of `a`\
|
||||||
/// This can be used for structural sharing between JSON values for example.
|
/// This can be used for structural sharing between JSON values for example.
|
||||||
/// `a` & `b` can only be Type of [Iterable] or [Map]
|
/// `a` & `b` can only be Type of [List] or [Map]
|
||||||
T replaceEqualDeep<T>(T a, T b) {
|
replaceEqualDeep(a, b) {
|
||||||
if (a == b) {
|
if (a == b) {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isList = (a is Iterable && b is Iterable);
|
int aSize;
|
||||||
if (isList || (a is Map && b is Map)) {
|
List bItems;
|
||||||
int aSize = isList ? a.length : (a as Map).keys.length;
|
int bSize;
|
||||||
List bItems = (isList ? b : (b as Map).keys).toList();
|
int equalItems = 0;
|
||||||
int bSize = bItems.length;
|
onEqual() => equalItems++;
|
||||||
var copy;
|
var copy;
|
||||||
|
if (a is List && b is List) {
|
||||||
int equalItems = 0;
|
aSize = a.length;
|
||||||
|
bItems = b;
|
||||||
for (int i = 0; i < bSize; i++) {
|
bSize = bItems.length;
|
||||||
var key = isList ? i : bItems[i];
|
copy = replaceEqualDeepList(a, b, onEqual);
|
||||||
if (isList) {
|
} else if (a is Map && b is Map) {
|
||||||
copy ??= [];
|
aSize = a.keys.length;
|
||||||
copy[key] = replaceEqualDeep((a as List)[key], (b as List)[key]);
|
bItems = b.keys.toList();
|
||||||
if (copy[key] == a[key]) {
|
bSize = bItems.length;
|
||||||
equalItems++;
|
copy = replaceEqualDeepMap(a, b, onEqual);
|
||||||
}
|
} else {
|
||||||
} else {
|
return b;
|
||||||
copy ??= {};
|
|
||||||
copy[key] = replaceEqualDeep((a as Map)[key], (b as Map)[key]);
|
|
||||||
if (copy[key] == a[key]) {
|
|
||||||
equalItems++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return aSize == bSize && equalItems == aSize ? a : copy as T;
|
|
||||||
}
|
}
|
||||||
return b;
|
return aSize == bSize && equalItems == aSize ? a : copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map replaceEqualDeepMap(Map a, Map b, void Function() onEqual) {
|
||||||
|
final copy = Map.from(a);
|
||||||
|
copy.clear();
|
||||||
|
for (final bEntry in b.entries) {
|
||||||
|
final aItem = a[bEntry.key];
|
||||||
|
copy[bEntry.key] =
|
||||||
|
aItem != null ? replaceEqualDeep(aItem, bEntry.value) : bEntry.value;
|
||||||
|
if (copy[bEntry.key] == aItem) {
|
||||||
|
onEqual();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
List replaceEqualDeepList(List a, List b, void Function() onEqual) {
|
||||||
|
final copy = List.of(a, growable: true);
|
||||||
|
copy.clear();
|
||||||
|
for (final bEntry in b.asMap().entries) {
|
||||||
|
final aItem = a.firstWhereIndexedOrNull((i, _) => i == bEntry.key);
|
||||||
|
final result =
|
||||||
|
aItem != null ? replaceEqualDeep(aItem, bEntry.value) : bEntry.value;
|
||||||
|
copy.add(result);
|
||||||
|
if (copy.last == aItem) {
|
||||||
|
onEqual();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration timeUntilStale(DateTime updatedAt, [Duration? staleTime]) =>
|
Duration timeUntilStale(DateTime updatedAt, [Duration? staleTime]) =>
|
||||||
|
|||||||
@@ -0,0 +1,320 @@
|
|||||||
|
import 'package:fl_query/src/core/utils.dart';
|
||||||
|
import 'package:test/expect.dart';
|
||||||
|
import 'package:test/scaffolding.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('core/utils', () {
|
||||||
|
group('replaceEqualDeep', () {
|
||||||
|
test(
|
||||||
|
'Should return the previous value When the next value is an equal primitive',
|
||||||
|
() {
|
||||||
|
expect(replaceEqualDeep(1, 1), equals(1));
|
||||||
|
expect(replaceEqualDeep('1', '1'), equals('1'));
|
||||||
|
expect(replaceEqualDeep(true, true), true);
|
||||||
|
expect(replaceEqualDeep(false, false), false);
|
||||||
|
expect(replaceEqualDeep(null, null), null);
|
||||||
|
});
|
||||||
|
test(
|
||||||
|
'Should return the next value When the previous value is a different value',
|
||||||
|
() {
|
||||||
|
expect(replaceEqualDeep(1, 0), equals(0));
|
||||||
|
expect(replaceEqualDeep(1, 2), equals(2));
|
||||||
|
expect(replaceEqualDeep('1', '2'), equals('2'));
|
||||||
|
expect(replaceEqualDeep(true, false), equals(false));
|
||||||
|
expect(replaceEqualDeep(false, true), equals(true));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return the next value When the previous value is a different type',
|
||||||
|
() {
|
||||||
|
final array = [1];
|
||||||
|
final object = {"a": "a"};
|
||||||
|
expect(replaceEqualDeep(0, null), equals(null));
|
||||||
|
expect(replaceEqualDeep(null, 0), equals(0));
|
||||||
|
expect(replaceEqualDeep(2, null), equals(null));
|
||||||
|
expect(replaceEqualDeep(null, 2), equals(2));
|
||||||
|
expect(replaceEqualDeep({}, null), equals(null));
|
||||||
|
expect(replaceEqualDeep([], null), equals(null));
|
||||||
|
expect(replaceEqualDeep(array, object), equals(object));
|
||||||
|
expect(replaceEqualDeep(object, array), equals(array));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return the previous value When the next value is an equal array',
|
||||||
|
() {
|
||||||
|
final prev = [1, 2];
|
||||||
|
final next = [1, 2];
|
||||||
|
expect(replaceEqualDeep(prev, next), equals(prev));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return a copy When the previous value is a different array subset',
|
||||||
|
() {
|
||||||
|
final prev = [1, 2];
|
||||||
|
final next = [1, 2, 3];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result, isNot(equals(prev)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return the previous value When the next value is an equal empty array',
|
||||||
|
() {
|
||||||
|
final prev = [];
|
||||||
|
final next = [];
|
||||||
|
expect(replaceEqualDeep(prev, next), equals(prev));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return the previous value When the next value is an equal empty object',
|
||||||
|
() {
|
||||||
|
final prev = {};
|
||||||
|
final next = {};
|
||||||
|
expect(replaceEqualDeep(prev, next), equals(prev));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should return the previous value When the next value is an equal object',
|
||||||
|
() {
|
||||||
|
final prev = {"a": 'a'};
|
||||||
|
final next = {"a": 'a'};
|
||||||
|
expect(replaceEqualDeep(prev, next), equals(prev));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should replace different values in objects', () {
|
||||||
|
final prev = {
|
||||||
|
"a": {"b": 'b'},
|
||||||
|
"c": 'c'
|
||||||
|
};
|
||||||
|
final next = {
|
||||||
|
"a": {"b": 'b'},
|
||||||
|
"c": 'd'
|
||||||
|
};
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result, isNot(prev));
|
||||||
|
expect(result["a"], equals(prev["a"]));
|
||||||
|
expect(result["c"], equals(next["c"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should replace different values in arrays', () {
|
||||||
|
final prev = [
|
||||||
|
1,
|
||||||
|
{"a": 'a'},
|
||||||
|
{
|
||||||
|
"b": {"b": 'b'}
|
||||||
|
},
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
1,
|
||||||
|
{"a": 'a'},
|
||||||
|
{
|
||||||
|
"b": {"b": 'c'}
|
||||||
|
},
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result, isNot(prev));
|
||||||
|
expect(result[0], prev[0]);
|
||||||
|
expect(result[1], prev[1]);
|
||||||
|
expect((result[2] as Map)["b"]["b"], (next[2] as Map)["b"]["b"]);
|
||||||
|
expect(result[3], prev[3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should replace different values in arrays When the next value is a subset',
|
||||||
|
() {
|
||||||
|
final prev = [
|
||||||
|
{"a": 'a'},
|
||||||
|
{"b": 'b'},
|
||||||
|
{"c": 'c'}
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
{"a": 'a'},
|
||||||
|
{"b": 'b'}
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result, isNot(prev));
|
||||||
|
expect(result[0], prev[0]);
|
||||||
|
expect(result[1], prev[1]);
|
||||||
|
expect(() => result[2], throwsRangeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Should replace different values in arrays When the next value is a superset',
|
||||||
|
() {
|
||||||
|
final prev = [
|
||||||
|
{"a": 'a'},
|
||||||
|
{"b": 'b'}
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
{"a": 'a'},
|
||||||
|
{"b": 'b'},
|
||||||
|
{"c": 'c'}
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result[0], equals(prev[0]));
|
||||||
|
expect(result[1], equals(prev[1]));
|
||||||
|
expect(result[2], equals(next[2]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should copy objects which are not arrays or objects', () {
|
||||||
|
final prev = [
|
||||||
|
{"a": 'a'},
|
||||||
|
{"b": 'b'},
|
||||||
|
{"c": 'c'},
|
||||||
|
1
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
{"a": 'a'},
|
||||||
|
Map(),
|
||||||
|
{"c": 'c'},
|
||||||
|
2
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result[0], equals(prev[0]));
|
||||||
|
expect(result[1], equals(next[1]));
|
||||||
|
expect(result[2], equals(prev[2]));
|
||||||
|
expect(result[3], equals(next[3]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should support equal objects which are not arrays or objects', () {
|
||||||
|
final map = new Map();
|
||||||
|
final prev = [
|
||||||
|
map,
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
map,
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(prev));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should support non equal objects which are not arrays or objects',
|
||||||
|
() {
|
||||||
|
final map1 = new Map();
|
||||||
|
final map2 = new Map();
|
||||||
|
final prev = [
|
||||||
|
map1,
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final next = [
|
||||||
|
map2,
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result[0], equals(next[0]));
|
||||||
|
expect(result[1], equals(prev[1]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should replace all parent objects if some nested value changes',
|
||||||
|
() {
|
||||||
|
final prev = {
|
||||||
|
"todo": {
|
||||||
|
"id": '1',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": false},
|
||||||
|
},
|
||||||
|
"otherTodo": {
|
||||||
|
"id": '2',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
final next = {
|
||||||
|
"todo": {
|
||||||
|
"id": '1',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true},
|
||||||
|
},
|
||||||
|
"otherTodo": {
|
||||||
|
"id": '2',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result["todo"] == prev["todo"], isFalse);
|
||||||
|
expect(result["todo"] == next["todo"], isFalse);
|
||||||
|
expect(result["todo"]["id"], equals((next["todo"] as Map)["id"]));
|
||||||
|
expect(result["todo"]["meta"], equals((prev["todo"] as Map)["meta"]));
|
||||||
|
expect(
|
||||||
|
result["todo"]["state"],
|
||||||
|
equals((next["todo"] as Map)["state"]),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
result["todo"]["state"]["done"],
|
||||||
|
(next["todo"] as Map)["state"]["done"],
|
||||||
|
);
|
||||||
|
expect(result["otherTodo"], prev["otherTodo"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should replace all parent arrays if some nested value changes', () {
|
||||||
|
final Map<String, List<Map>> prev = {
|
||||||
|
"todos": [
|
||||||
|
{
|
||||||
|
"id": '1',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": false}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": '2',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
final Map<String, List<Map>> next = {
|
||||||
|
"todos": [
|
||||||
|
{
|
||||||
|
"id": '1',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": '2',
|
||||||
|
"meta": {"createdAt": 0},
|
||||||
|
"state": {"done": true}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
final result = replaceEqualDeep(prev, next);
|
||||||
|
expect(result, equals(next));
|
||||||
|
expect(result["todos"][0], isNot(equals(prev["todos"]?.first)));
|
||||||
|
expect(
|
||||||
|
result["todos"][0]?["id"],
|
||||||
|
equals(next["todos"]?.first["id"]),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
result["todos"][0]?["meta"],
|
||||||
|
equals(prev["todos"]?.first["meta"]),
|
||||||
|
);
|
||||||
|
expect(result["todos"][0]?["state"]["done"],
|
||||||
|
next["todos"]?.first["state"]["done"]);
|
||||||
|
expect(result["todos"][1], equals(prev["todos"]?[1]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group(
|
||||||
|
'matchMutation',
|
||||||
|
() => {
|
||||||
|
// test('should return false if mutationKey options is undefined', () => {
|
||||||
|
// const filters = { mutationKey: 'key1' };
|
||||||
|
// const queryClient = new QueryClient();
|
||||||
|
// const mutation = new Mutation({
|
||||||
|
// mutationId: 1,
|
||||||
|
// mutationCache: queryClient.getMutationCache(),
|
||||||
|
// options: {},
|
||||||
|
// })
|
||||||
|
// expect(matchMutation(filters, mutation)).toBeFalsy()
|
||||||
|
// })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user