refactored data providers with centralized cache resolution, unified UI using custom dialogs and bottom sheets, and enhanced network error handling for Dio and TLS errors

This commit is contained in:
2026-05-08 20:01:45 +02:00
parent c62a14645a
commit 9e139b5704
37 changed files with 595 additions and 753 deletions
+30 -4
View File
@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:localstore/localstore.dart';
import 'api_response.dart';
import 'errors/parse_exception.dart';
abstract class RequestCache<T extends ApiResponse?> {
static const int cacheNothing = 0;
@@ -81,10 +82,8 @@ abstract class RequestCache<T extends ApiResponse?> {
}
/// Concrete [RequestCache] that delegates the two overrides to functions
/// passed in the constructor. Used to collapse the dozens of one-class-per-
/// endpoint cache files that all just forward to `<Endpoint>().run()` and
/// `<Response>.fromJson(jsonDecode(...))`.
/// Concrete [RequestCache] that takes the two overrides as constructor
/// callbacks instead of requiring a subclass per endpoint.
class SimpleCache<T extends ApiResponse?> extends RequestCache<T> {
final Future<T> Function() _loader;
final T Function(Map<String, dynamic> json) _fromJson;
@@ -115,3 +114,30 @@ class SimpleCache<T extends ApiResponse?> extends RequestCache<T> {
@override
T onLocalData(String json) => _fromJson(jsonDecode(json) as Map<String, dynamic>);
}
/// Captures the latest cache payload (cached or network) and rethrows the
/// captured network error if no payload arrived. Collapses the
/// `latest`/`capturedError`/`await ready` boilerplate that DataProviders
/// otherwise repeat per endpoint.
Future<T> resolveFromCache<T extends ApiResponse?>(
RequestCache<T> Function(void Function(T) onUpdate, void Function(Exception) onError) build, {
void Function(Object)? onError,
String? operationName,
}) async {
T? latest;
Object? capturedError;
final cache = build(
(data) => latest = data,
(e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest as T;
final err = capturedError;
if (err != null) throw err;
throw ParseException(
technicalDetails: operationName != null ? 'No data and no error from $operationName' : null,
);
}