feat(infinite-query): onData and onError listener support

This commit is contained in:
Kingkor Roy Tirtho
2022-09-16 14:32:23 +06:00
parent 2a3ac29f64
commit f47ca98472
7 changed files with 165 additions and 108 deletions
@@ -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-09-14 13:12:02.969795","version":"3.3.0"}
{"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-09-15 12:05:12.417424","version":"3.3.0"}
+36 -22
View File
@@ -26,8 +26,10 @@ abstract class BaseQuery<T extends Object, Outside, Error>
QueryStatus status;
final Set _onDataListeners = Set();
final Set _onErrorListeners = Set();
@protected
final Set onDataListeners = Set();
@protected
final Set onErrorListeners = Set();
// externalData will always be passed to the task Callback
// it will change based on the presence of QueryBuilder
@@ -67,8 +69,8 @@ abstract class BaseQuery<T extends Object, Outside, Error>
_connectivity = connectivity ?? Connectivity(),
_previousData = previousData,
super(data: previousData ?? initialData) {
if (onData != null) _onDataListeners.add(onData);
if (onError != null) _onErrorListeners.add(onError);
if (onData != null) onDataListeners.add(onData);
if (onError != null) onErrorListeners.add(onError);
if (refetchInterval != null && refetchInterval != Duration.zero) {
_refetchIntervalTimer = createRefetchTimer();
@@ -92,17 +94,13 @@ abstract class BaseQuery<T extends Object, Outside, Error>
_prevUsedExternalData = _externalData;
updatedAt = DateTime.now();
status = QueryStatus.success;
for (final onData in _onDataListeners) {
onData(data!);
}
await notifyDataListeners();
notifyListeners();
} catch (e) {
if (retries == 0) {
status = QueryStatus.error;
setError(e);
for (final onError in _onErrorListeners) {
onError(error);
}
await notifyErrorListeners();
notifyListeners();
} else {
// retrying for retry count if failed for the first time
@@ -112,18 +110,14 @@ abstract class BaseQuery<T extends Object, Outside, Error>
await setData();
_prevUsedExternalData = _externalData;
status = QueryStatus.success;
for (final onData in _onDataListeners) {
await onData(data!);
}
await notifyDataListeners();
notifyListeners();
break;
} catch (e) {
if (retryAttempts == retries) {
status = QueryStatus.error;
setError(e);
for (final onError in _onErrorListeners) {
await onError(error);
}
await notifyErrorListeners();
notifyListeners();
break;
}
@@ -135,19 +129,19 @@ abstract class BaseQuery<T extends Object, Outside, Error>
}
void addDataListener(listener) {
_onDataListeners.add(listener);
onDataListeners.add(listener);
}
void addErrorListener(listener) {
_onErrorListeners.add(listener);
onErrorListeners.add(listener);
}
void removeDataListener(listener) {
_onDataListeners.remove(listener);
onDataListeners.remove(listener);
}
void removeErrorListener(listener) {
_onErrorListeners.remove(listener);
onErrorListeners.remove(listener);
}
/// fetches data or runs the provided task initially
@@ -185,6 +179,13 @@ abstract class BaseQuery<T extends Object, Outside, Error>
/// the current available [data] instead of running the task to prevent
/// race conditions
Future<T?> refetch() async {
if (queryKey == "category-playlists#user-featured-playlists") {
print(
"Refetch Count of ${queryKey}-> ${refetchCount}",
);
print(StackTrace.current);
}
/// if isLoading/isRefetching is true that means its already fetching/
/// refetching. So [_execute] again can create a race condition
if (isRefetching || isLoading) return data;
@@ -201,6 +202,19 @@ abstract class BaseQuery<T extends Object, Outside, Error>
FutureOr<void> setData();
@protected
void setError(dynamic);
@protected
FutureOr<void> notifyDataListeners() async {
for (final onData in onDataListeners) {
await onData(error);
}
}
@protected
FutureOr<void> notifyErrorListeners() async {
for (final onError in onErrorListeners) {
await onError(error);
}
}
/// Sets the [externalData] from outside of the query
///
@@ -222,8 +236,8 @@ abstract class BaseQuery<T extends Object, Outside, Error>
fetched = false;
status = QueryStatus.idle;
retryAttempts = 0;
_onDataListeners.clear();
_onErrorListeners.clear();
onDataListeners.clear();
onErrorListeners.clear();
mounts.clear();
}
+47 -17
View File
@@ -14,6 +14,10 @@ typedef InfiniteQueryTaskFunction<T extends Object, Outside,
PageParam pageParam,
Outside externalData,
);
typedef InfiniteQueryListeners<T, PageParam extends Object> = FutureOr<void>
Function(T page, PageParam pageParam, List<T?> pages);
typedef InfiniteQueryPageParamFunction<T extends Object,
PageParam extends Object>
= FutureOr<PageParam> Function(T lastPage, PageParam lastParam);
@@ -38,6 +42,10 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
PageParam _currentParam;
final Set<InfiniteQueryListeners<T, PageParam>> onDataListeners = Set();
final Set<InfiniteQueryListeners<dynamic, PageParam>> onErrorListeners =
Set();
InfiniteQuery({
required super.queryKey,
required this.task,
@@ -55,8 +63,8 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
super.enabled,
super.previousData,
super.connectivity,
super.onData,
super.onError,
InfiniteQueryListeners<T, PageParam>? super.onData,
InfiniteQueryListeners<dynamic, PageParam>? super.onError,
required T? initialPage,
this.getNextPageParam,
this.getPreviousPageParam,
@@ -67,8 +75,8 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
InfiniteQueryJob<T, Outside, PageParam> options, {
required super.queryBowl,
required Outside externalData,
QueryListener<T>? onData,
QueryListener<dynamic>? onError,
InfiniteQueryListeners<T, PageParam>? onData,
InfiniteQueryListeners<dynamic, PageParam>? onError,
}) : task = options.task,
_currentParam = options.initialParam,
getNextPageParam = options.getNextPageParam,
@@ -114,13 +122,16 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
isFetchingPreviousPage ||
isLoading ||
isRefetching) return null;
if (data == null || data?[_currentParam] == null) execute();
final page = data?[_currentParam];
if (data == null || page == null) await execute();
_isFetchingNextPage = true;
_isFetchingPreviousPage = false;
final nextParam = await (getNextPageParam ?? this.getNextPageParam)?.call(
data![_currentParam]!,
_currentParam,
);
final nextParam = page != null
? await (getNextPageParam ?? this.getNextPageParam)?.call(
page,
_currentParam,
)
: null;
if (nextParam == null) {
_hasNextPage = false;
notifyListeners();
@@ -128,7 +139,7 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
} else {
_hasNextPage = true;
_currentParam = nextParam;
return await refetch().then((data) => data?[_currentParam]);
return await fetch().then((_) => data?[_currentParam]);
}
} finally {
_isFetchingNextPage = false;
@@ -147,12 +158,14 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
_isFetchingPreviousPage = true;
_isFetchingNextPage = false;
notifyListeners();
if (data?[_currentParam] == null) execute();
final prevParam =
await (getPreviousPageParam ?? this.getPreviousPageParam)?.call(
data![_currentParam]!,
_currentParam,
);
final page = data?[_currentParam];
if (page == null) await execute();
final prevParam = page != null
? await (getPreviousPageParam ?? this.getPreviousPageParam)?.call(
page,
_currentParam,
)
: null;
if (prevParam == null) {
_hasPreviousPage = false;
notifyListeners();
@@ -160,7 +173,7 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
}
_hasPreviousPage = true;
_currentParam = prevParam;
return await refetch().then((_) => data?[_currentParam]);
return await fetch().then((_) => data?[_currentParam]);
} catch (e) {
print("[InfiniteQuery.fetchPreviousPage]: $e");
rethrow;
@@ -233,6 +246,23 @@ class InfiniteQuery<T extends Object, Outside, PageParam extends Object>
error?[_currentParam] = specError;
}
@override
@protected
FutureOr<void> notifyDataListeners() async {
for (var onData in onDataListeners) {
if (data?[_currentParam] == null) continue;
onData.call(data![_currentParam]!, _currentParam, pages);
}
}
@override
@protected
FutureOr<void> notifyErrorListeners() async {
for (var onError in onErrorListeners) {
onError.call(error?[_currentParam], _currentParam, errors);
}
}
@override
bool operator ==(other) {
return (other is InfiniteQuery<T, Outside, PageParam> &&
@@ -14,10 +14,15 @@ class InfiniteQueryBuilder<T extends Object, Outside, PageParam extends Object>
) builder;
final InfiniteQueryJob<T, Outside, PageParam> job;
final Outside externalData;
final InfiniteQueryListeners<T, PageParam>? onData;
final InfiniteQueryListeners<dynamic, PageParam>? onError;
InfiniteQueryBuilder({
required this.job,
required this.builder,
required this.externalData,
this.onData,
this.onError,
Key? key,
}) : super(key: key);
@@ -63,10 +68,10 @@ class _InfiniteQueryBuilderState<T extends Object, Outside,
@override
void didUpdateWidget(covariant oldWidget) {
// final hasOnErrorChanged = false;
// oldWidget.onError != widget.onError && oldWidget.onError != null;
// final hasOnDataChanged = false;
// oldWidget.onData != widget.onData && oldWidget.onData != null;
final hasOnErrorChanged =
oldWidget.onError != widget.onError && oldWidget.onError != null;
final hasOnDataChanged =
oldWidget.onData != widget.onData && oldWidget.onData != null;
// re-init the query-builder when new queryJob is appended
if (oldWidget.job.queryKey != widget.job.queryKey) {
@@ -92,33 +97,39 @@ class _InfiniteQueryBuilderState<T extends Object, Outside,
widget.job,
externalData: widget.externalData,
key: uKey,
// onData: widget.onData,
// onError: widget.onError,
onData: widget.onData,
onError: widget.onError,
)..refetchPages();
} else {
QueryBowl.of(context)
.getQuery(widget.job.queryKey)
?.setExternalData(widget.externalData);
}
// if (hasOnDataChanged) query?.removeDataListener(oldWidget.onData!);
// if (hasOnErrorChanged) query?.removeErrorListener(oldWidget.onError!);
if (hasOnDataChanged)
infiniteQuery?.removeDataListener(oldWidget.onData!);
if (hasOnErrorChanged)
infiniteQuery?.removeErrorListener(oldWidget.onError!);
} else {
// if (hasOnDataChanged) {
// query?.removeDataListener(oldWidget.onData!);
// if (widget.onData != null) query?.addDataListener(widget.onData!);
// }
// if (hasOnErrorChanged) {
// query?.removeErrorListener(oldWidget.onError!);
// if (widget.onError != null) query?.addErrorListener(widget.onError!);
// }
if (hasOnDataChanged) {
infiniteQuery?.removeDataListener(oldWidget.onData!);
if (widget.onData != null)
infiniteQuery?.addDataListener(widget.onData!);
}
if (hasOnErrorChanged) {
infiniteQuery?.removeErrorListener(oldWidget.onError!);
if (widget.onError != null)
infiniteQuery?.addErrorListener(widget.onError!);
}
}
super.didUpdateWidget(oldWidget);
}
_infiniteQueryDispose() {
infiniteQuery?.unmount(uKey);
// if (widget.onData != null) query?.removeDataListener(widget.onData!);
// if (widget.onError != null) query?.removeErrorListener(widget.onError!);
if (widget.onData != null)
infiniteQuery?.removeDataListener(widget.onData!);
if (widget.onError != null)
infiniteQuery?.removeErrorListener(widget.onError!);
}
@override
+5 -2
View File
@@ -39,6 +39,9 @@ typedef QueryUpdateFunction<T> = FutureOr<T> Function(T? oldData);
class Query<T extends Object, Outside> extends BaseQuery<T, Outside, dynamic> {
QueryTaskFunction<T, Outside> task;
final Set<QueryListener<T>> onDataListeners = Set();
final Set<QueryListener<dynamic>> onErrorListeners = Set();
Query({
required super.queryKey,
required this.task,
@@ -56,8 +59,8 @@ class Query<T extends Object, Outside> extends BaseQuery<T, Outside, dynamic> {
super.previousData,
super.connectivity,
super.initialData,
super.onData,
super.onError,
QueryListener<T>? super.onData,
QueryListener<dynamic>? super.onError,
});
Query.fromOptions(
+2 -2
View File
@@ -434,8 +434,8 @@ class QueryBowl extends InheritedWidget {
InfiniteQueryJob<T, Outside, PageParam> infiniteQueryJob, {
required Outside externalData,
required ValueKey<String> key,
final QueryListener<T>? onData,
final QueryListener<dynamic>? onError,
final InfiniteQueryListeners<T, PageParam>? onData,
final InfiniteQueryListeners<dynamic, PageParam>? onError,
}) {
final prevInfiniteQuery = _infiniteQueries.firstWhereOrNull(
(q) => q.queryKey == infiniteQueryJob.queryKey,
@@ -10,18 +10,18 @@ InfiniteQuery<T, Outside, PageParam>
required InfiniteQueryJob<T, Outside, PageParam> job,
required Outside externalData,
// /// Called when the query returns new data, on query
// /// refetch or query gets expired
// QueryListener<T>? onData,
/// Called when the query returns new data, on query
/// refetch or query gets expired
final InfiniteQueryListeners<T, PageParam>? onData,
// /// Called when the query returns error
// QueryListener<dynamic>? onError,
/// Called when the query returns error
final InfiniteQueryListeners<dynamic, PageParam>? onError,
List<Object?>? keys,
}) {
final context = useContext();
final QueryBowl queryBowl = QueryBowl.of(context);
final ValueKey<String> uKey = useMemoized(() => ValueKey(uuid.v4()), []);
final query = useRef(
final infiniteQuery = useRef(
InfiniteQuery.fromOptions(
job,
externalData: externalData,
@@ -31,46 +31,45 @@ InfiniteQuery<T, Outside, PageParam>
final oldJob = usePrevious(job);
final oldExternalData = usePrevious(externalData);
// final oldOnData = usePrevious(onData);
// final oldOnError = usePrevious(onError);
final oldOnData = usePrevious(onData);
final oldOnError = usePrevious(onError);
final init = useCallback(([T? previousData]) {
query.value = queryBowl.addInfiniteQuery<T, Outside, PageParam>(
infiniteQuery.value = queryBowl.addInfiniteQuery<T, Outside, PageParam>(
job,
externalData: externalData,
// previousData: previousData,
key: uKey,
// onData: onData,
// onError: onError,
onData: onData,
onError: onError,
);
final hasExternalDataChanged = query.value.externalData != null &&
query.value.prevUsedExternalData != null &&
!isShallowEqual(
query.value.externalData!, query.value.prevUsedExternalData!);
if (query.value.fetched && hasExternalDataChanged) {
query.value.refetch();
} else if (!query.value.fetched) {
query.value.fetch();
final hasExternalDataChanged = infiniteQuery.value.externalData != null &&
infiniteQuery.value.prevUsedExternalData != null &&
!isShallowEqual(infiniteQuery.value.externalData!,
infiniteQuery.value.prevUsedExternalData!);
if (infiniteQuery.value.fetched && hasExternalDataChanged) {
infiniteQuery.value.refetchPages();
} else if (!infiniteQuery.value.fetched) {
infiniteQuery.value.fetch();
}
}, [
queryBowl,
query.value,
infiniteQuery.value,
uKey,
job,
externalData,
// onData,
// onError,
onData,
onError,
]);
final disposeQuery = useCallback(() {
query.value.unmount(uKey);
// if (onData != null) query.value.removeDataListener(onData);
// if (onError != null) query.value.removeErrorListener(onError);
infiniteQuery.value.unmount(uKey);
if (onData != null) infiniteQuery.value.removeDataListener(onData);
if (onError != null) infiniteQuery.value.removeErrorListener(onError);
}, [
query.value,
infiniteQuery.value,
uKey,
// onData,
// onError,
onData,
onError,
]);
useEffect(() {
@@ -79,8 +78,8 @@ InfiniteQuery<T, Outside, PageParam>
}, []);
useEffect(() {
// final hasOnErrorChanged = oldOnError != onError && oldOnError != null;
// final hasOnDataChanged = oldOnData != onData && oldOnData != null;
final hasOnErrorChanged = oldOnError != onError && oldOnError != null;
final hasOnDataChanged = oldOnData != onData && oldOnData != null;
if (oldJob != null && oldJob.queryKey != job.queryKey) {
disposeQuery();
init();
@@ -93,8 +92,8 @@ InfiniteQuery<T, Outside, PageParam>
job,
externalData: externalData,
key: uKey,
// onData: onData,
// onError: onError,
onData: onData,
onError: onError,
)..refetchPages();
} else {
QueryBowl.of(context)
@@ -102,22 +101,22 @@ InfiniteQuery<T, Outside, PageParam>
?.setExternalData(externalData);
}
// if (hasOnDataChanged) query.value.removeDataListener(oldOnData);
// if (hasOnErrorChanged) query.value.removeErrorListener(oldOnError);
if (hasOnDataChanged) infiniteQuery.value.removeDataListener(oldOnData);
if (hasOnErrorChanged)
infiniteQuery.value.removeErrorListener(oldOnError);
} else {
if (hasOnDataChanged) {
infiniteQuery.value.removeDataListener(oldOnData);
if (onData != null) infiniteQuery.value.addDataListener(onData);
}
if (hasOnErrorChanged) {
infiniteQuery.value.removeErrorListener(oldOnError);
if (onError != null) infiniteQuery.value.addErrorListener(onError);
}
}
// else {
// if (hasOnDataChanged) {
// query.value.removeDataListener(oldOnData);
// if (onData != null) query.value.addDataListener(onData);
// }
// if (hasOnErrorChanged) {
// query.value.removeErrorListener(oldOnError);
// if (onError != null) query.value.addErrorListener(onError);
// }
// }
return null;
});
return queryBowl.getInfiniteQuery<T, Outside, PageParam>(job.queryKey) ??
query.value;
infiniteQuery.value;
}