Depended queries support added

This commit is contained in:
Kingkor Roy Tirtho
2022-06-22 17:56:24 +06:00
parent 8c53f1b0fe
commit 73711ee77d
10 changed files with 234 additions and 127 deletions
+71 -8
View File
@@ -2,7 +2,10 @@ import 'dart:async';
import 'package:fl_query/src/base_operation.dart';
import 'package:fl_query/src/models/query_job.dart';
import 'package:fl_query/src/query_bowl.dart';
import 'package:fl_query/src/utils.dart';
import 'package:flutter/widgets.dart';
import 'package:collection/collection.dart';
enum QueryStatus {
/// in times when an error occurs
@@ -23,7 +26,11 @@ enum QueryStatus {
refetching;
}
typedef QueryTaskFunction<T, Outside> = FutureOr<T> Function(String, Outside);
typedef QueryTaskFunction<T extends Object, Outside> = FutureOr<T> Function(
String queryKey,
Outside externalData,
Query<T, Outside> self,
);
typedef QueryListener<T> = FutureOr<void> Function(T);
@@ -64,6 +71,8 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
Timer? _refetchIntervalTimer;
final QueryBowl queryBowl;
Query({
required this.queryKey,
required this.task,
@@ -72,6 +81,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
required Outside externalData,
required super.retries,
required super.retryDelay,
required this.queryBowl,
this.refetchOnMount,
this.refetchInterval,
this.enabled = true,
@@ -95,6 +105,7 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
Query.fromOptions(
QueryJob<T, Outside> options, {
required this.queryBowl,
required Outside externalData,
QueryListener<T>? onData,
QueryListener<dynamic>? onError,
@@ -136,7 +147,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
Future<void> _execute() async {
try {
retryAttempts = 0;
data = await task(queryKey, _externalData);
data = await task(
queryKey,
_externalData,
this,
);
_prevUsedExternalData = _externalData;
updatedAt = DateTime.now();
status = QueryStatus.success;
@@ -157,7 +172,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
while (retryAttempts <= retries) {
await Future.delayed(retryDelay);
try {
data = await task(queryKey, _externalData);
data = await task(
queryKey,
_externalData,
this,
);
_prevUsedExternalData = _externalData;
status = QueryStatus.success;
for (final onData in onDataListeners) {
@@ -182,7 +201,9 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
}
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 (!enabled || isLoading || isRefetching) return null;
if (hasData) {
return data;
}
@@ -195,13 +216,14 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
}
Future<T?> refetch() async {
// cannot let run multiple refetch at the same time. It can cause
// race-condition
if (isRefetching) return null;
/// if isLoading/isRefetching is true that means its already fetching/
/// refetching. So [_execute] again can create a race condition
if (isRefetching || isLoading) return null;
status = QueryStatus.refetching;
refetchCount++;
// disabling the lazy query bound when query was actually called
if (!enabled) enabled = false;
if (!enabled) enabled = true;
if (enabled && !fetched) await fetch();
notifyListeners();
return await _execute().then((_) => data);
}
@@ -239,6 +261,11 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
fetched = false;
status = QueryStatus.idle;
retryAttempts = 0;
for (final queryEntry in _dependencyQueries.entries) {
queryEntry.value.unmount(queryEntry.key);
queryEntry.value.removeListener(refetch);
}
_dependencyQueries = {};
onDataListeners.clear();
onErrorListeners.clear();
mounts.clear();
@@ -268,6 +295,42 @@ class Query<T extends Object, Outside> extends BaseOperation<T, QueryStatus> {
notifyListeners();
}
Map<ValueKey<String>, Query> _dependencyQueries = {};
/// only usable inside any task method
Query<T, Outside> dependOnQuery<T extends Object, Outside>(
QueryJob<T, Outside> job, {
required Outside externalData,
}) {
final key = ValueKey(uuid.v4());
final query = queryBowl.addQuery(
Query<T, Outside>.fromOptions(
job,
externalData: externalData,
queryBowl: queryBowl,
),
key: key,
);
// removing listener if it was already hooked to it previously
query.removeListener(refetch);
query.addListener(refetch);
final uKey =
_dependencyQueries.keys.firstWhereOrNull((k) => k.value == key.value) ??
key;
_dependencyQueries[uKey] = query;
if (!query.fetched) query.fetch();
return query;
}
@override
void dispose() {
for (final queryEntry in _dependencyQueries.entries) {
queryEntry.value.unmount(queryEntry.key);
queryEntry.value.removeListener(refetch);
}
super.dispose();
}
bool get isStale {
/// when [_staleTime] is [Duration.zero], the query will always be
/// stale & will never refetch in the background. But can be inactive