feat: working query builder, remove timeout from retryConfig
This commit is contained in:
@@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user