wip: bloc for files
This commit is contained in:
		| @@ -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) { | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()}'); | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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), | ||||
|   ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user