feat: working query builder, remove timeout from retryConfig

This commit is contained in:
Kingkor Roy Tirtho
2023-02-16 12:37:35 +06:00
parent 18584a3c57
commit 091491966f
8 changed files with 86 additions and 38 deletions
+15 -8
View File
@@ -1,3 +1,5 @@
import 'dart:math';
import 'package:fl_query/fl_query.dart'; import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -16,21 +18,26 @@ class MainApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final value = Random().nextInt(200000);
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
body: QueryBuilder<String, void, String>( body: QueryBuilder<String, dynamic, String>(
const ValueKey('hello'), const ValueKey('hello'),
() { () {
return Future.delayed( return Future.delayed(
const Duration(seconds: 5), const Duration(seconds: 6), () => 'Hello World! $value');
() => 'Hello World!',
);
}, },
initial: 'Hello', initial: 'Hello',
// jsonConfig: JsonConfig( jsonConfig: JsonConfig(
// fromJson: (json) => json['data'], fromJson: (json) => json['data'],
// toJson: (data) => {'data': data}, toJson: (data) => {'data': data},
// ), ),
onData: (value) {
print('onData: $value');
},
onError: (error) {
print('onError: $error');
},
builder: (context, query) { builder: (context, query) {
if (query.isLoading) { if (query.isLoading) {
return const Center( return const Center(
@@ -5,12 +5,11 @@ abstract class DefaultConstants {
static const RetryConfig retryConfig = RetryConfig( static const RetryConfig retryConfig = RetryConfig(
maxRetries: 3, maxRetries: 3,
retryDelay: Duration(seconds: 1), retryDelay: Duration(seconds: 1),
timeout: Duration(seconds: 5),
); );
static const RefreshConfig refreshConfig = RefreshConfig( static const RefreshConfig refreshConfig = RefreshConfig(
staleDuration: Duration(seconds: 10), staleDuration: Duration(seconds: 10),
refreshInterval: Duration(seconds: 5), refreshInterval: Duration.zero,
refreshOnMount: true, refreshOnMount: true,
refreshOnQueryFnChange: false, refreshOnQueryFnChange: false,
); );
@@ -1,11 +1,6 @@
class RetryConfig { class RetryConfig {
final int maxRetries; final int maxRetries;
final Duration retryDelay; final Duration retryDelay;
final Duration timeout;
const RetryConfig({ const RetryConfig({required this.maxRetries, required this.retryDelay});
required this.maxRetries,
required this.retryDelay,
required this.timeout,
});
} }
@@ -131,7 +131,8 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
); );
} }
}); });
}
if (refreshConfig.refreshInterval > Duration.zero)
Timer.periodic(refreshConfig.refreshInterval, (_) async { Timer.periodic(refreshConfig.refreshInterval, (_) async {
await Future.wait( await Future.wait(
state.pages.map((page) async { state.pages.map((page) async {
@@ -141,7 +142,6 @@ class InfiniteQuery<DataType, ErrorType, KeyType, PageType>
}), }),
); );
}); });
}
} }
final _mutex = Mutex(); final _mutex = Mutex();
@@ -1,5 +1,6 @@
import 'package:fl_query/src/core/client.dart'; import 'package:fl_query/src/core/client.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
class QueryClientProvider extends InheritedWidget { class QueryClientProvider extends InheritedWidget {
final QueryClient client; final QueryClient client;
@@ -9,4 +10,10 @@ class QueryClientProvider extends InheritedWidget {
bool updateShouldNotify(covariant QueryClientProvider oldWidget) { bool updateShouldNotify(covariant QueryClientProvider oldWidget) {
return client != oldWidget.client; return client != oldWidget.client;
} }
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<QueryClient>('client', client));
}
} }
+18 -9
View File
@@ -51,21 +51,22 @@ class Query<DataType, ErrorType, KeyType>
extends StateNotifier<QueryState<DataType, ErrorType>> extends StateNotifier<QueryState<DataType, ErrorType>>
with Retryer<DataType, ErrorType> { with Retryer<DataType, ErrorType> {
final ValueKey<KeyType> key; final ValueKey<KeyType> key;
final DataType? initial;
final RefreshConfig refreshConfig; final RefreshConfig refreshConfig;
final RetryConfig retryConfig; final RetryConfig retryConfig;
final JsonConfig<DataType>? jsonConfig; final JsonConfig<DataType>? jsonConfig;
Query( Query(
this.key, this.key,
QueryFn<DataType> queryFn, { QueryFn<DataType> queryFn, {
this.initial, DataType? initial,
this.retryConfig = DefaultConstants.retryConfig, this.retryConfig = DefaultConstants.retryConfig,
this.refreshConfig = DefaultConstants.refreshConfig, this.refreshConfig = DefaultConstants.refreshConfig,
this.jsonConfig, this.jsonConfig,
}) : _box = Hive.lazyBox("cache"), }) : _box = Hive.lazyBox("cache"),
_dataController = StreamController<DataType>.broadcast(), _dataController = StreamController<DataType>.broadcast(),
_errorController = StreamController<ErrorType>.broadcast(), _errorController = StreamController<ErrorType>.broadcast(),
_initial = initial,
super(QueryState<DataType, ErrorType>( super(QueryState<DataType, ErrorType>(
updatedAt: DateTime.now(), updatedAt: DateTime.now(),
staleDuration: refreshConfig.staleDuration, staleDuration: refreshConfig.staleDuration,
@@ -76,28 +77,35 @@ class Query<DataType, ErrorType, KeyType>
_mutex.protect(() async { _mutex.protect(() async {
final json = await _box.get(key.toString()); final json = await _box.get(key.toString());
if (json != null) { if (json != null) {
state = state.copyWith( _initial = jsonConfig!.fromJson(
data: jsonConfig!.fromJson( Map.castFrom<dynamic, dynamic, String, dynamic>(json),
Map.castFrom<dynamic, dynamic, String, dynamic>(json),
),
); );
state = state.copyWith(data: _initial);
}
}).then((_) {
if (hasListeners) {
return fetch();
} }
}); });
} else {
_initial = initial;
}
if (refreshConfig.refreshInterval > Duration.zero)
Timer.periodic(refreshConfig.refreshInterval, (_) async { Timer.periodic(refreshConfig.refreshInterval, (_) async {
if (state.isStale) { if (state.isStale) {
await refresh(); await refresh();
} }
}); });
}
} }
DataType? _initial;
final LazyBox _box; final LazyBox _box;
final _mutex = Mutex(); final _mutex = Mutex();
final StreamController<DataType> _dataController; final StreamController<DataType> _dataController;
final StreamController<ErrorType> _errorController; final StreamController<ErrorType> _errorController;
bool get isInitial => state.data == initial; bool get isInitial => hasData && state.data == _initial;
bool get isLoading => isInitial ? _mutex.isLocked : !hasData && !hasError; bool get isLoading => isInitial ? _mutex.isLocked : !hasData && !hasError;
bool get isRefreshing => bool get isRefreshing =>
((!isInitial && hasData) || hasError) && _mutex.isLocked; ((!isInitial && hasData) || hasError) && _mutex.isLocked;
@@ -134,7 +142,8 @@ class Query<DataType, ErrorType, KeyType>
} }
Future<DataType?> fetch() async { Future<DataType?> fetch() async {
if (_mutex.isLocked || hasData || hasError) return state.data; if (_mutex.isLocked || (hasData && !isInitial) || hasError)
return state.data;
return _operate().then((_) => state.data); return _operate().then((_) => state.data);
} }
@@ -15,16 +15,6 @@ mixin Retryer<T, E> {
attempts == 0 ? Duration.zero : config.retryDelay, attempts == 0 ? Duration.zero : config.retryDelay,
operation, operation,
).then(completer.complete).catchError(completer.completeError); ).then(completer.complete).catchError(completer.completeError);
await Future.delayed(config.timeout, () {
if (!completer.isCompleted) {
completer.completeError(
TimeoutException(
'Operation timed out after ${config.timeout.inSeconds} seconds',
),
StackTrace.current,
);
}
});
try { try {
final result = await completer.future; final result = await completer.future;
onSuccessful(result); onSuccessful(result);
@@ -7,6 +7,7 @@ import 'package:fl_query/src/collections/retry_config.dart';
import 'package:fl_query/src/core/client.dart'; import 'package:fl_query/src/core/client.dart';
import 'package:fl_query/src/core/query.dart'; import 'package:fl_query/src/core/query.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
typedef QueryBuilderFn<DataType, ErrorType, KeyType> = Widget Function( typedef QueryBuilderFn<DataType, ErrorType, KeyType> = Widget Function(
BuildContext context, BuildContext context,
@@ -42,7 +43,10 @@ class QueryBuilder<DataType, ErrorType, KeyType> extends StatefulWidget {
this.onError, this.onError,
this.enabled = true, this.enabled = true,
super.key, super.key,
}); }) : assert(
enabled && jsonConfig != null,
'jsonConfig is only supported when enabled is true',
);
@override @override
State<QueryBuilder<DataType, ErrorType, KeyType>> createState() => State<QueryBuilder<DataType, ErrorType, KeyType>> createState() =>
@@ -132,4 +136,41 @@ class _QueryBuilderState<DataType, ErrorType, KeyType>
} }
return widget.builder(context, query!); return widget.builder(context, query!);
} }
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
DiagnosticsProperty<Query<DataType, ErrorType, KeyType>>('query', query),
);
properties.add(
DiagnosticsProperty<ValueKey<KeyType>>('queryKey', widget.queryKey),
);
properties.add(
DiagnosticsProperty<QueryBuilderFn>('builder', widget.builder),
);
properties.add(DiagnosticsProperty<DataType>('initial', widget.initial));
properties.add(
DiagnosticsProperty<RetryConfig>('retryConfig', widget.retryConfig),
);
properties.add(
DiagnosticsProperty<RefreshConfig>(
'refreshConfig',
widget.refreshConfig,
),
);
properties.add(
DiagnosticsProperty<JsonConfig<DataType>>(
'jsonConfig',
widget.jsonConfig,
),
);
properties.add(
DiagnosticsProperty<ValueChanged<DataType>>('onData', widget.onData),
);
properties.add(
DiagnosticsProperty<ValueChanged<ErrorType>>('onError', widget.onError),
);
properties.add(DiagnosticsProperty<bool>('enabled', widget.enabled));
}
} }