Tidy up error handling in blocs

This commit is contained in:
Ming Ming 2023-05-20 19:07:07 +08:00
parent 66c5b6608b
commit 360c74ce95
15 changed files with 215 additions and 5 deletions

View file

@ -75,6 +75,19 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
return super.close();
}
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
bool isCollectionCapabilityPermitted(CollectionCapability capability) {
return CollectionAdapter.of(_c, account, state.collection)
.isPermitted(capability);
@ -470,6 +483,7 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
StreamSubscription? _collectionControllerSubscription;
StreamSubscription? _itemsControllerSubscription;
var _isHandlingError = false;
}
class _TransformResult {

View file

@ -9,12 +9,26 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
on<_LoadCollections>(_onLoad);
on<_TransformItems>(_onTransformItems);
on<_SelectCollection>(_onSelectCollection);
on<_SetError>(_onSetError);
}
@override
String get tag => _log.fullName;
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
Future<void> _onLoad(_LoadCollections ev, Emitter<_State> emit) async {
_log.info(ev);
return emit.forEach<CollectionStreamEvent>(
@ -60,4 +74,6 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
final Account account;
final CollectionsController controller;
var _isHandlingError = false;
}

View file

@ -111,3 +111,10 @@ extension _$_SubmitFormToString on _SubmitForm {
return "_SubmitForm {}";
}
}
extension _$_SetErrorToString on _SetError {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_SetError {error: $error, stackTrace: $stackTrace}";
}
}

View file

@ -9,6 +9,21 @@ class _Bloc extends Bloc<_Event, _State> {
required this.items,
}) : super(_State.init()) {
on<_FormEvent>(_onFormEvent);
on<_SetError>(_onSetError);
}
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
Future<void> _onFormEvent(_FormEvent ev, Emitter<_State> emit) async {
@ -49,15 +64,20 @@ class _Bloc extends Bloc<_Event, _State> {
break;
}
emit(state.copyWith(result: result));
} catch (e, stackTrace) {
_log.severe("[_onSubmitForm] Failed while exporting", e, stackTrace);
} finally {
emit(state.copyWith(isExporting: false));
}
}
void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
}
final Account account;
final CollectionsController collectionsController;
final Collection collection;
final List<CollectionItem> items;
var _isHandlingError = false;
}

View file

@ -69,3 +69,14 @@ class _SubmitForm extends _FormEvent {
@override
String toString() => _$toString();
}
@toString
class _SetError implements _Event {
const _SetError(this.error, [this.stackTrace]);
@override
String toString() => _$toString();
final Object error;
final StackTrace? stackTrace;
}

View file

@ -210,3 +210,10 @@ extension _$_SetPasswordLinkDetailsToString on _SetPasswordLinkDetails {
return "_SetPasswordLinkDetails {albumName: $albumName, password: $password}";
}
}
extension _$_SetErrorToString on _SetError {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_SetError {error: $error, stackTrace: $stackTrace}";
}
}

View file

@ -12,11 +12,26 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
on<_SetResult>(_onSetResult);
on<_SetPublicLinkDetails>(_onSetPublicLinkDetails);
on<_SetPasswordLinkDetails>(_onSetPasswordLinkDetails);
on<_SetError>(_onSetError);
}
@override
String get tag => _log.fullName;
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
Future<void> _onSetMethod(_SetMethod ev, Emitter<_State> emit) async {
_log.info("$ev");
emit(state.copyWith(method: ev.method));
@ -49,6 +64,11 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
return _doShareLink(emit, albumName: ev.albumName, password: ev.password);
}
void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
}
Future<void> _doShareFile(Emitter<_State> emit) async {
assert(platform_k.isAndroid);
emit(state.copyWith(
@ -206,4 +226,6 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
final DiContainer _c;
final Account account;
final List<FileDescriptor> files;
var _isHandlingError = false;
}

View file

@ -135,3 +135,14 @@ class _SetPasswordLinkDetails implements _Event {
final String? albumName;
final String password;
}
@toString
class _SetError implements _Event {
const _SetError(this.error, [this.stackTrace]);
@override
String toString() => _$toString();
final Object error;
final StackTrace? stackTrace;
}

View file

@ -148,3 +148,10 @@ extension _$_SetCollectionSortToString on _SetCollectionSort {
return "_SetCollectionSort {sort: ${sort.name}}";
}
}
extension _$_SetErrorToString on _SetError {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_SetError {error: $error, stackTrace: $stackTrace}";
}
}

View file

@ -17,6 +17,8 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
on<_UpdateCollectionSort>(_onUpdateCollectionSort);
on<_SetCollectionSort>(_onSetCollectionSort);
on<_SetError>(_onSetError);
_homeAlbumsSortSubscription =
prefController.homeAlbumsSort.distinct().listen((event) {
add(_UpdateCollectionSort(collection_util.CollectionSort.values[event]));
@ -32,6 +34,19 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
@override
String get tag => _log.fullName;
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
Future<void> _onLoad(_LoadCollections ev, Emitter<_State> emit) async {
_log.info("[_onLoad] $ev");
return emit.forEach<CollectionStreamEvent>(
@ -90,6 +105,11 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
prefController.setHomeAlbumsSort(ev.sort.index);
}
void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
}
List<_Item> _transformCollections(
List<Collection> collections,
collection_util.CollectionSort sort,
@ -103,4 +123,5 @@ class _Bloc extends Bloc<_Event, _State> implements BlocTag {
final PrefController prefController;
StreamSubscription<int>? _homeAlbumsSortSubscription;
var _isHandlingError = false;
}

View file

@ -111,3 +111,14 @@ class _SetCollectionSort implements _Event {
final collection_util.CollectionSort sort;
}
@toString
class _SetError implements _Event {
const _SetError(this.error, [this.stackTrace]);
@override
String toString() => _$toString();
final Object error;
final StackTrace? stackTrace;
}

View file

@ -18,6 +18,7 @@ import 'package:nc_photos/entity/collection/content_provider/nc_album.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/nc_album.dart';
import 'package:nc_photos/entity/tag.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/object_extension.dart';
@ -88,6 +89,17 @@ class _WrappedNewCollectionDialogState
previous.result != current.result && current.result != null,
listener: _onResult,
),
BlocListener<_Bloc, _State>(
listenWhen: (previous, current) => previous.error != current.error,
listener: (context, state) {
if (state.error != null) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(state.error!.error)),
duration: k.snackBarDurationNormal,
));
}
},
),
],
child: BlocBuilder<_Bloc, _State>(
buildWhen: (previous, current) =>

View file

@ -43,7 +43,11 @@ extension $_FormValueCopyWith on _FormValue {
}
abstract class $_StateCopyWithWorker {
_State call({_FormValue? formValue, Collection? result, bool? showDialog});
_State call(
{_FormValue? formValue,
Collection? result,
bool? showDialog,
ExceptionEvent? error});
}
class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
@ -51,12 +55,16 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
@override
_State call(
{dynamic formValue, dynamic result = copyWithNull, dynamic showDialog}) {
{dynamic formValue,
dynamic result = copyWithNull,
dynamic showDialog,
dynamic error = copyWithNull}) {
return _State(
supportedProviders: that.supportedProviders,
formValue: formValue as _FormValue? ?? that.formValue,
result: result == copyWithNull ? that.result : result as Collection?,
showDialog: showDialog as bool? ?? that.showDialog);
showDialog: showDialog as bool? ?? that.showDialog,
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
}
final _State that;
@ -132,3 +140,10 @@ extension _$_HideDialogToString on _HideDialog {
return "_HideDialog {}";
}
}
extension _$_SetErrorToString on _SetError {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_SetError {error: $error, stackTrace: $stackTrace}";
}
}

View file

@ -11,6 +11,21 @@ class _Bloc extends Bloc<_Event, _State> {
)) {
on<_FormEvent>(_onFormEvent);
on<_HideDialog>(_onHideDialog);
on<_SetError>(_onSetError);
}
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
if (!isClosed && !_isHandlingError) {
_isHandlingError = true;
try {
add(_SetError(error, stackTrace));
} catch (_) {}
_isHandlingError = false;
}
super.onError(error, stackTrace);
}
void _onFormEvent(_FormEvent ev, Emitter<_State> emit) {
@ -68,6 +83,11 @@ class _Bloc extends Bloc<_Event, _State> {
.setLastNewCollectionType(state.formValue.provider.index));
}
void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
}
CollectionContentProvider _buildProvider() {
switch (state.formValue.provider) {
case _ProviderOption.appAlbum:
@ -115,4 +135,6 @@ class _Bloc extends Bloc<_Event, _State> {
}
final Account account;
var _isHandlingError = false;
}

View file

@ -22,6 +22,7 @@ class _State {
required this.formValue,
this.result,
required this.showDialog,
this.error,
});
factory _State.init({
@ -55,6 +56,8 @@ class _State {
final _FormValue formValue;
final Collection? result;
final bool showDialog;
final ExceptionEvent? error;
}
abstract class _Event {
@ -120,3 +123,14 @@ class _HideDialog extends _Event {
@override
String toString() => _$toString();
}
@toString
class _SetError implements _Event {
const _SetError(this.error, [this.stackTrace]);
@override
String toString() => _$toString();
final Object error;
final StackTrace? stackTrace;
}