mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 18:07:59 -06:00
Improved error handling, added multithreading for fromJson calls, made receive sharing intent more robust
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
|
||||
///
|
||||
/// Class for handling generic errors which usually only require to inform the user via a Snackbar
|
||||
/// or similar that an error has occurred.
|
||||
///
|
||||
@singleton
|
||||
class GlobalErrorCubit extends Cubit<GlobalErrorState> {
|
||||
static const _waitBeforeNextErrorDuration = Duration(seconds: 5);
|
||||
|
||||
GlobalErrorCubit() : super(GlobalErrorState.initial);
|
||||
|
||||
///
|
||||
/// Adds a new error to this bloc. If the new error is equal to the current error, the new error
|
||||
/// will not be published unless the previous error occured over 5 seconds ago.
|
||||
///
|
||||
void add(ErrorMessage error) {
|
||||
final now = DateTime.now();
|
||||
if (error != state.error || (error == state.error && _canEmitNewError())) {
|
||||
emit(GlobalErrorState(error: error, errorTimestamp: now));
|
||||
}
|
||||
}
|
||||
|
||||
bool _canEmitNewError() {
|
||||
if (state.errorTimestamp != null) {
|
||||
return DateTime.now().difference(state.errorTimestamp!) >=
|
||||
_waitBeforeNextErrorDuration;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
emit(GlobalErrorState.initial);
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalErrorState {
|
||||
static const GlobalErrorState initial = GlobalErrorState();
|
||||
final ErrorMessage? error;
|
||||
final DateTime? errorTimestamp;
|
||||
|
||||
const GlobalErrorState({this.error, this.errorTimestamp});
|
||||
|
||||
bool get hasError => error != null;
|
||||
}
|
||||
@@ -1,75 +1,42 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/global_error_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
|
||||
|
||||
abstract class LabelCubit<T extends Label> extends Cubit<Map<int, T>> {
|
||||
final LabelRepository labelRepository;
|
||||
final GlobalErrorCubit errorCubit;
|
||||
|
||||
LabelCubit(this.labelRepository, this.errorCubit) : super({});
|
||||
LabelCubit(this.labelRepository) : super({});
|
||||
|
||||
@protected
|
||||
void loadFrom(Iterable<T> items) =>
|
||||
emit(Map.fromIterable(items, key: (e) => (e as T).id!));
|
||||
|
||||
Future<T> add(
|
||||
T item, {
|
||||
bool propagateEventOnError = true,
|
||||
}) async {
|
||||
Future<T> add(T item) async {
|
||||
assert(item.id == null);
|
||||
try {
|
||||
final addedItem = await save(item);
|
||||
final newState = {...state};
|
||||
newState.putIfAbsent(addedItem.id!, () => addedItem);
|
||||
emit(newState);
|
||||
return addedItem;
|
||||
} on ErrorMessage catch (error) {
|
||||
if (propagateEventOnError) {
|
||||
errorCubit.add(error);
|
||||
}
|
||||
return Future.error(error);
|
||||
}
|
||||
final addedItem = await save(item);
|
||||
final newState = {...state};
|
||||
newState.putIfAbsent(addedItem.id!, () => addedItem);
|
||||
emit(newState);
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
Future<T> replace(
|
||||
T item, {
|
||||
bool propagateEventOnError = true,
|
||||
}) async {
|
||||
Future<T> replace(T item) async {
|
||||
assert(item.id != null);
|
||||
try {
|
||||
final updatedItem = await update(item);
|
||||
final newState = {...state};
|
||||
newState[item.id!] = updatedItem;
|
||||
emit(newState);
|
||||
return updatedItem;
|
||||
} on ErrorMessage catch (error) {
|
||||
if (propagateEventOnError) {
|
||||
errorCubit.add(error);
|
||||
}
|
||||
return Future.error(error);
|
||||
}
|
||||
final updatedItem = await update(item);
|
||||
final newState = {...state};
|
||||
newState[item.id!] = updatedItem;
|
||||
emit(newState);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
Future<void> remove(
|
||||
T item, {
|
||||
bool propagateEventOnError = true,
|
||||
}) async {
|
||||
Future<void> remove(T item) async {
|
||||
assert(item.id != null);
|
||||
if (state.containsKey(item.id)) {
|
||||
try {
|
||||
final deletedId = await delete(item);
|
||||
final newState = {...state};
|
||||
newState.remove(deletedId);
|
||||
emit(newState);
|
||||
} on ErrorMessage catch (error) {
|
||||
if (propagateEventOnError) {
|
||||
errorCubit.add(error);
|
||||
}
|
||||
return Future.error(error);
|
||||
}
|
||||
final deletedId = await delete(item);
|
||||
final newState = {...state};
|
||||
newState.remove(deletedId);
|
||||
emit(newState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
lib/core/global/constants.dart
Normal file
1
lib/core/global/constants.dart
Normal file
@@ -0,0 +1 @@
|
||||
const supportedFileExtensions = ['pdf', 'png', 'tiff', 'gif', 'jpg', 'jpeg'];
|
||||
@@ -66,5 +66,7 @@ String translateError(BuildContext context, ErrorCode code) {
|
||||
return S.of(context).errorMessageRequestTimedOut;
|
||||
case ErrorCode.unsupportedFileFormat:
|
||||
return S.of(context).errorMessageUnsupportedFileFormat;
|
||||
case ErrorCode.missingClientCertificate:
|
||||
return S.of(context).errorMessageMissingClientCertificate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,5 +48,6 @@ enum ErrorCode {
|
||||
createSavedViewError,
|
||||
deleteSavedViewError,
|
||||
requestTimedOut,
|
||||
unsupportedFileFormat;
|
||||
unsupportedFileFormat,
|
||||
missingClientCertificate;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:paperless_mobile/core/bloc/document_status_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/document_processing_status.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
@@ -89,8 +90,11 @@ class LongPollingStatusService implements StatusService {
|
||||
Uri.parse(
|
||||
'$httpUrl/api/documents/?query=$documentFileName added:${formatDate(today)}'),
|
||||
);
|
||||
final data = PagedSearchResult.fromJson(
|
||||
jsonDecode(response.body), DocumentModel.fromJson);
|
||||
final data = await compute(
|
||||
PagedSearchResult.fromJson,
|
||||
PagedSearchResultJsonSerializer(
|
||||
jsonDecode(response.body), DocumentModel.fromJson),
|
||||
);
|
||||
if (data.count > 0) {
|
||||
consumptionFinished = true;
|
||||
final docId = data.results[0].id;
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:paperless_mobile/core/logic/timeout_client.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
const requestTimeout = Duration(seconds: 5);
|
||||
|
||||
@@ -23,7 +19,10 @@ Future<T> getSingleResult<T>(
|
||||
headers: {'accept': 'application/json; version=$minRequiredApiVersion'},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return fromJson(jsonDecode(utf8.decode(response.bodyBytes)) as JSON);
|
||||
return compute(
|
||||
fromJson,
|
||||
jsonDecode(utf8.decode(response.bodyBytes)) as JSON,
|
||||
);
|
||||
}
|
||||
return Future.error(errorCode);
|
||||
}
|
||||
@@ -45,12 +44,25 @@ Future<List<T>> getCollection<T>(
|
||||
if (body['count'] == 0) {
|
||||
return <T>[];
|
||||
} else {
|
||||
return body['results']
|
||||
.cast<JSON>()
|
||||
.map<T>((result) => fromJson(result))
|
||||
.toList();
|
||||
return compute(
|
||||
_collectionFromJson,
|
||||
_CollectionFromJsonSerializationParams(
|
||||
fromJson, (body['results'] as List).cast<JSON>()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Future.error(errorCode);
|
||||
}
|
||||
|
||||
List<T> _collectionFromJson<T>(
|
||||
_CollectionFromJsonSerializationParams<T> params) {
|
||||
return params.list.map<T>((result) => params.fromJson(result)).toList();
|
||||
}
|
||||
|
||||
class _CollectionFromJsonSerializationParams<T> {
|
||||
final T Function(JSON) fromJson;
|
||||
final List<JSON> list;
|
||||
|
||||
_CollectionFromJsonSerializationParams(this.fromJson, this.list);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user