wip: bloc for files

This commit is contained in:
2024-08-17 11:41:38 +02:00
parent ddeeaeaeac
commit 54b777237f
42 changed files with 880 additions and 148 deletions

View File

@ -2,26 +2,21 @@ import 'dart:convert';
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
abstract class DataLoader<TResult> {
abstract class HttpDataLoader<TResult> {
final Dio dio;
DataLoader(this.dio) {
HttpDataLoader(this.dio) {
dio.options.connectTimeout = const Duration(seconds: 10).inMilliseconds;
dio.options.sendTimeout = const Duration(seconds: 30).inMilliseconds;
dio.options.receiveTimeout = const Duration(seconds: 30).inMilliseconds;
}
Future<TResult> run() async {
var fetcher = fetch();
await Future.wait([
fetcher,
Future.delayed(const Duration(milliseconds: 500)) // TODO tune or remove
]);
var response = await fetcher;
var response = await fetch();
try {
return assemble(DataLoaderResult(
json: jsonDecode(response.data!),
json: await compute(jsonDecode, response.data!),
headers: response.headers.map.map((key, value) => MapEntry(key, value.join(';'))),
));
} catch(trace, e) {

View File

@ -166,27 +166,22 @@ class __$$LoadableStateImplCopyWithImpl<TState, $Res>
class _$LoadableStateImpl<TState> extends _LoadableState<TState> {
const _$LoadableStateImpl(
{this.isLoading = true,
this.data = null,
this.lastFetch = null,
this.reFetch = null,
this.error = null})
{required this.isLoading,
required this.data,
required this.lastFetch,
required this.reFetch,
required this.error})
: super._();
@override
@JsonKey()
final bool isLoading;
@override
@JsonKey()
final TState? data;
@override
@JsonKey()
final int? lastFetch;
@override
@JsonKey()
final void Function()? reFetch;
@override
@JsonKey()
final LoadingError? error;
@override
@ -222,11 +217,11 @@ class _$LoadableStateImpl<TState> extends _LoadableState<TState> {
abstract class _LoadableState<TState> extends LoadableState<TState> {
const factory _LoadableState(
{final bool isLoading,
final TState? data,
final int? lastFetch,
final void Function()? reFetch,
final LoadingError? error}) = _$LoadableStateImpl<TState>;
{required final bool isLoading,
required final TState? data,
required final int? lastFetch,
required final void Function()? reFetch,
required final LoadingError? error}) = _$LoadableStateImpl<TState>;
const _LoadableState._() : super._();
@override

View File

@ -30,16 +30,17 @@ abstract class LoadableHydratedBloc<
isLoading: state.isLoading,
data: event.state(innerState ?? fromNothing()),
lastFetch: state.lastFetch,
reFetch: retry,
reFetch: fetch,
error: state.error,
));
if(event.fetch) fetch();
});
on<DataGathered<TState>>((event, emit) => emit(LoadableState(
isLoading: false,
data: event.state(innerState ?? fromNothing()),
lastFetch: DateTime.now().millisecondsSinceEpoch,
reFetch: retry,
reFetch: fetch,
error: null,
)));
@ -55,7 +56,7 @@ abstract class LoadableHydratedBloc<
isLoading: false,
data: innerState,
lastFetch: state.lastFetch,
reFetch: retry,
reFetch: fetch,
error: event.error
)));
@ -66,19 +67,14 @@ abstract class LoadableHydratedBloc<
TState? get innerState => state.data;
TRepository get repo => _repository;
void retry() {
log('Fetch retry triggered for ${TState.toString()}');
add(RefetchStarted<TState>());
fetch();
}
void fetch() {
log('Fetching data for ${TState.toString()}');
add(RefetchStarted<TState>());
gatherData().catchError(
(e) {
log('Error while fetching ${TState.toString()}: ${e.toString()}');
add(Error(LoadingError(
message: e.message ?? e.toString(),
message: e.toString(),
allowRetry: true,
)));
},
@ -92,7 +88,7 @@ abstract class LoadableHydratedBloc<
var rawData = LoadableSaveContext.unwrap(json);
return LoadableState(
isLoading: true,
data: fromStorage(rawData.data),
data: fromStorage(rawData.data), // TODO fromStorage in isolate
lastFetch: rawData.meta.timestamp,
reFetch: null,
error: null,
@ -103,7 +99,7 @@ abstract class LoadableHydratedBloc<
Map<String, dynamic>? toJson(LoadableState<TState> state) {
Map<String, dynamic>? data;
try {
data = state.data == null ? null : toStorage(state.data);
data = state.data == null ? null : toStorage(state.data); // TODO toStorage in isolate
} catch(e) {
log('Failed to save state ${TState.toString()}: ${e.toString()}');
}

View File

@ -3,7 +3,8 @@ import '../../loadableState/loading_error.dart';
class LoadableHydratedBlocEvent<TState> {}
class Emit<TState> extends LoadableHydratedBlocEvent<TState> {
final TState Function(TState state) state;
Emit(this.state);
final bool fetch;
Emit(this.state, {this.fetch = false});
}
class DataGathered<TState> extends LoadableHydratedBlocEvent<TState> {
final TState Function(TState state) state;

View File

@ -9,7 +9,7 @@ part of 'loadable_save_context.dart';
_$LoadableSaveContextImpl _$$LoadableSaveContextImplFromJson(
Map<String, dynamic> json) =>
_$LoadableSaveContextImpl(
timestamp: json['timestamp'] as int,
timestamp: (json['timestamp'] as num).toInt(),
);
Map<String, dynamic> _$$LoadableSaveContextImplToJson(

View File

@ -0,0 +1,48 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'bloc_module.dart';
class SwappingBloc<TBloc> {
final TBloc initialBloc;
final StreamController<TBloc> updater = StreamController<TBloc>();
SwappingBloc(this.initialBloc);
void swap(TBloc bloc) {
updater.add(bloc);
}
}
class SwappingBlocModule<TBloc extends StateStreamableSource<TState>, TState> extends StatefulWidget {
final SwappingBloc<TBloc> bloc;
final Widget Function(BuildContext context, TBloc bloc, TState state) child;
const SwappingBlocModule({super.key, required this.bloc, required this.child});
@override
State<SwappingBlocModule<TBloc, TState>> createState() => _SwappingBlocModuleState<TBloc, TState>();
}
class _SwappingBlocModuleState<TBloc extends StateStreamableSource<TState>, TState> extends State<SwappingBlocModule<TBloc, TState>> {
late TBloc bloc;
@override
void initState() {
super.initState();
bloc = widget.bloc.initialBloc;
widget.bloc.updater.stream.listen((event) {
setState(() {
bloc = event;
});
});
}
@override
Widget build(BuildContext context) => BlocModule<TBloc, TState>(
autoRebuild: true,
create: (context) => bloc,
child: (context, bloc, state) => widget.child(context, bloc, state),
);
}