added timestamp to bloc cache, showing age in offline mode

This commit is contained in:
2024-05-12 02:39:35 +02:00
parent 3281b134e0
commit ebbb70dc96
13 changed files with 302 additions and 40 deletions

View File

@ -6,6 +6,7 @@ import '../../loadableState/loading_error.dart';
import '../../repository/repository.dart';
import 'loadable_hydrated_bloc_event.dart';
import '../../loadableState/loadable_state.dart';
import 'loadable_save_context.dart';
abstract class LoadableHydratedBloc<
TEvent extends LoadableHydratedBlocEvent<TState>,
@ -17,10 +18,29 @@ abstract class LoadableHydratedBloc<
> {
late TRepository _repository;
LoadableHydratedBloc() : super(const LoadableState()) {
on<Emit<TState>>((event, emit) => emit(LoadableState(isLoading: event.loading, data: event.state(innerState ?? fromNothing()), reFetch: retry)));
on<RefetchStarted<TState>>((event, emit) => emit(LoadableState(isLoading: true, data: innerState)));
on<Emit<TState>>((event, emit) => emit(LoadableState(
isLoading: event.loading,
data: event.state(innerState ?? fromNothing()),
lastFetch: DateTime.now().millisecondsSinceEpoch,
reFetch: retry
)));
on<RefetchStarted<TState>>((event, emit) => emit(LoadableState(
isLoading: true,
data: innerState,
lastFetch: state.lastFetch
)));
on<ClearState<TState>>((event, emit) => emit(const LoadableState()));
on<Error<TState>>((event, emit) => emit(LoadableState(isLoading: false, data: innerState, reFetch: retry, error: event.error)));
on<Error<TState>>((event, emit) => emit(LoadableState(
isLoading: false,
data: innerState,
lastFetch: state.lastFetch,
reFetch: retry,
error: event.error
)));
_repository = repository();
fetch();
@ -41,19 +61,26 @@ abstract class LoadableHydratedBloc<
(e) {
log('Error while fetching ${TState.toString()}: ${e.toString()}');
add(Error(LoadingError(
message: e.toString(),
message: e.message,
allowRetry: true,
)));
}
},
).then((value) {
log('Fetch for ${TState.toString()} completed!');
});
}
@override
fromJson(Map<String, dynamic> json) => LoadableState(isLoading: true, data: fromStorage(json));
fromJson(Map<String, dynamic> json) {
var rawData = LoadableSaveContext.unwrap(json);
return LoadableState(isLoading: true, lastFetch: rawData.meta.timestamp, data: fromStorage(rawData.data));
}
@override
Map<String, dynamic>? toJson(LoadableState<TState> state) => state.data == null ? {} : state.data.toJson();
Map<String, dynamic>? toJson(LoadableState<TState> state) => LoadableSaveContext.wrap(
toStorage(state.data),
state.lastFetch ?? DateTime.now().millisecondsSinceEpoch
);
Future<void> gatherData();
TRepository repository();

View File

@ -0,0 +1,24 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'loadable_save_context.freezed.dart';
part 'loadable_save_context.g.dart';
@freezed
class LoadableSaveContext with _$LoadableSaveContext {
const LoadableSaveContext._();
const factory LoadableSaveContext({
required int timestamp,
}) = _LoadableSaveContext;
factory LoadableSaveContext.fromJson(Map<String, dynamic> json) => _$LoadableSaveContextFromJson(json);
static String dataKey = 'data';
static String metaKey = 'meta';
static Map<String, dynamic> wrap(Map<String, dynamic>? data, int lastFetch) =>
{dataKey: data, metaKey: LoadableSaveContext(timestamp: lastFetch).toJson()};
static ({Map<String, dynamic> data, LoadableSaveContext meta}) unwrap(Map<String, dynamic> data) =>
(data: data[dataKey] as Map<String, dynamic>, meta: LoadableSaveContext.fromJson(data[metaKey]));
}

View File

@ -0,0 +1,155 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'loadable_save_context.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
LoadableSaveContext _$LoadableSaveContextFromJson(Map<String, dynamic> json) {
return _LoadableSaveContext.fromJson(json);
}
/// @nodoc
mixin _$LoadableSaveContext {
int get timestamp => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LoadableSaveContextCopyWith<LoadableSaveContext> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoadableSaveContextCopyWith<$Res> {
factory $LoadableSaveContextCopyWith(
LoadableSaveContext value, $Res Function(LoadableSaveContext) then) =
_$LoadableSaveContextCopyWithImpl<$Res, LoadableSaveContext>;
@useResult
$Res call({int timestamp});
}
/// @nodoc
class _$LoadableSaveContextCopyWithImpl<$Res, $Val extends LoadableSaveContext>
implements $LoadableSaveContextCopyWith<$Res> {
_$LoadableSaveContextCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? timestamp = null,
}) {
return _then(_value.copyWith(
timestamp: null == timestamp
? _value.timestamp
: timestamp // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$LoadableSaveContextImplCopyWith<$Res>
implements $LoadableSaveContextCopyWith<$Res> {
factory _$$LoadableSaveContextImplCopyWith(_$LoadableSaveContextImpl value,
$Res Function(_$LoadableSaveContextImpl) then) =
__$$LoadableSaveContextImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int timestamp});
}
/// @nodoc
class __$$LoadableSaveContextImplCopyWithImpl<$Res>
extends _$LoadableSaveContextCopyWithImpl<$Res, _$LoadableSaveContextImpl>
implements _$$LoadableSaveContextImplCopyWith<$Res> {
__$$LoadableSaveContextImplCopyWithImpl(_$LoadableSaveContextImpl _value,
$Res Function(_$LoadableSaveContextImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? timestamp = null,
}) {
return _then(_$LoadableSaveContextImpl(
timestamp: null == timestamp
? _value.timestamp
: timestamp // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LoadableSaveContextImpl extends _LoadableSaveContext {
const _$LoadableSaveContextImpl({required this.timestamp}) : super._();
factory _$LoadableSaveContextImpl.fromJson(Map<String, dynamic> json) =>
_$$LoadableSaveContextImplFromJson(json);
@override
final int timestamp;
@override
String toString() {
return 'LoadableSaveContext(timestamp: $timestamp)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadableSaveContextImpl &&
(identical(other.timestamp, timestamp) ||
other.timestamp == timestamp));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, timestamp);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$LoadableSaveContextImplCopyWith<_$LoadableSaveContextImpl> get copyWith =>
__$$LoadableSaveContextImplCopyWithImpl<_$LoadableSaveContextImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LoadableSaveContextImplToJson(
this,
);
}
}
abstract class _LoadableSaveContext extends LoadableSaveContext {
const factory _LoadableSaveContext({required final int timestamp}) =
_$LoadableSaveContextImpl;
const _LoadableSaveContext._() : super._();
factory _LoadableSaveContext.fromJson(Map<String, dynamic> json) =
_$LoadableSaveContextImpl.fromJson;
@override
int get timestamp;
@override
@JsonKey(ignore: true)
_$$LoadableSaveContextImplCopyWith<_$LoadableSaveContextImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'loadable_save_context.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$LoadableSaveContextImpl _$$LoadableSaveContextImplFromJson(
Map<String, dynamic> json) =>
_$LoadableSaveContextImpl(
timestamp: json['timestamp'] as int,
);
Map<String, dynamic> _$$LoadableSaveContextImplToJson(
_$LoadableSaveContextImpl instance) =>
<String, dynamic>{
'timestamp': instance.timestamp,
};