diff --git a/lib/core/bloc/global_error_cubit.dart b/lib/core/bloc/global_error_cubit.dart deleted file mode 100644 index 9fa10f4..0000000 --- a/lib/core/bloc/global_error_cubit.dart +++ /dev/null @@ -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 { - 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; -} diff --git a/lib/core/bloc/label_cubit.dart b/lib/core/bloc/label_cubit.dart index cfa1d60..07c3580 100644 --- a/lib/core/bloc/label_cubit.dart +++ b/lib/core/bloc/label_cubit.dart @@ -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 extends Cubit> { final LabelRepository labelRepository; - final GlobalErrorCubit errorCubit; - LabelCubit(this.labelRepository, this.errorCubit) : super({}); + LabelCubit(this.labelRepository) : super({}); @protected void loadFrom(Iterable items) => emit(Map.fromIterable(items, key: (e) => (e as T).id!)); - Future add( - T item, { - bool propagateEventOnError = true, - }) async { + Future 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 replace( - T item, { - bool propagateEventOnError = true, - }) async { + Future 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 remove( - T item, { - bool propagateEventOnError = true, - }) async { + Future 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); } } diff --git a/lib/core/global/constants.dart b/lib/core/global/constants.dart new file mode 100644 index 0000000..652572f --- /dev/null +++ b/lib/core/global/constants.dart @@ -0,0 +1 @@ +const supportedFileExtensions = ['pdf', 'png', 'tiff', 'gif', 'jpg', 'jpeg']; diff --git a/lib/core/logic/error_code_localization_mapper.dart b/lib/core/logic/error_code_localization_mapper.dart index aadc175..9ad6c99 100644 --- a/lib/core/logic/error_code_localization_mapper.dart +++ b/lib/core/logic/error_code_localization_mapper.dart @@ -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; } } diff --git a/lib/core/model/error_message.dart b/lib/core/model/error_message.dart index b7d0c79..e49ac4d 100644 --- a/lib/core/model/error_message.dart +++ b/lib/core/model/error_message.dart @@ -48,5 +48,6 @@ enum ErrorCode { createSavedViewError, deleteSavedViewError, requestTimedOut, - unsupportedFileFormat; + unsupportedFileFormat, + missingClientCertificate; } diff --git a/lib/core/service/status.service.dart b/lib/core/service/status.service.dart index 0cc3f1b..d96274c 100644 --- a/lib/core/service/status.service.dart +++ b/lib/core/service/status.service.dart @@ -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; diff --git a/lib/core/util.dart b/lib/core/util.dart index d1f6b8f..9e26b0b 100644 --- a/lib/core/util.dart +++ b/lib/core/util.dart @@ -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 getSingleResult( 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> getCollection( if (body['count'] == 0) { return []; } else { - return body['results'] - .cast() - .map((result) => fromJson(result)) - .toList(); + return compute( + _collectionFromJson, + _CollectionFromJsonSerializationParams( + fromJson, (body['results'] as List).cast()), + ); } } } return Future.error(errorCode); } + +List _collectionFromJson( + _CollectionFromJsonSerializationParams params) { + return params.list.map((result) => params.fromJson(result)).toList(); +} + +class _CollectionFromJsonSerializationParams { + final T Function(JSON) fromJson; + final List list; + + _CollectionFromJsonSerializationParams(this.fromJson, this.list); +} diff --git a/lib/features/documents/bloc/documents_cubit.dart b/lib/features/documents/bloc/documents_cubit.dart index 4697984..016c5bc 100644 --- a/lib/features/documents/bloc/documents_cubit.dart +++ b/lib/features/documents/bloc/documents_cubit.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; 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/documents/bloc/documents_state.dart'; import 'package:paperless_mobile/features/documents/model/document.model.dart'; @@ -13,10 +12,8 @@ import 'package:injectable/injectable.dart'; @singleton class DocumentsCubit extends Cubit { final DocumentRepository documentRepository; - final GlobalErrorCubit errorCubit; - DocumentsCubit(this.documentRepository, this.errorCubit) - : super(DocumentsState.initial); + DocumentsCubit(this.documentRepository) : super(DocumentsState.initial); Future addDocument( Uint8List bytes, @@ -27,209 +24,99 @@ class DocumentsCubit extends Cubit { int? correspondent, List? tags, DateTime? createdAt, - bool propagateEventOnError = true, }) async { - try { - await documentRepository.create( - bytes, - fileName, - title: title, - documentType: documentType, - correspondent: correspondent, - tags: tags, - createdAt: createdAt, - ); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + await documentRepository.create( + bytes, + fileName, + title: title, + documentType: documentType, + correspondent: correspondent, + tags: tags, + createdAt: createdAt, + ); + // documentRepository // .waitForConsumptionFinished(fileName, title) // .then((value) => onConsumptionFinished(value)); } - Future removeDocument( - DocumentModel document, { - bool propagateEventOnError = true, - }) async { - try { - await documentRepository.delete(document); - return await reloadDocuments(); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future removeDocument(DocumentModel document) async { + await documentRepository.delete(document); + return await reloadDocuments(); } - Future bulkRemoveDocuments(List documents, - {bool propagateEventOnError = true}) async { - try { - await documentRepository.bulkDelete(documents); - return await reloadDocuments(); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future bulkRemoveDocuments(List documents) async { + await documentRepository.bulkDelete(documents); + return await reloadDocuments(); } - Future updateDocument( - DocumentModel document, { - bool propagateEventOnError = true, - }) async { - try { - await documentRepository.update(document); - await reloadDocuments(); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future updateDocument(DocumentModel document) async { + await documentRepository.update(document); + await reloadDocuments(); } - Future loadDocuments({ - bool propagateEventOnError = true, - }) async { - try { - final result = await documentRepository.find(state.filter); - emit(DocumentsState( - isLoaded: true, - value: [...state.value, result], - filter: state.filter, - )); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future loadDocuments() async { + final result = await documentRepository.find(state.filter); + emit(DocumentsState( + isLoaded: true, + value: [...state.value, result], + filter: state.filter, + )); } - Future reloadDocuments({ - bool propagateEventOnError = true, - }) async { + Future reloadDocuments() async { if (state.currentPageNumber >= 5) { return _bulkReloadDocuments(); } var newPages = []; - try { - for (final page in state.value) { - final result = await documentRepository - .find(state.filter.copyWith(page: page.pageKey)); - newPages.add(result); - } - emit(DocumentsState( - isLoaded: true, value: newPages, filter: state.filter)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } + for (final page in state.value) { + final result = await documentRepository + .find(state.filter.copyWith(page: page.pageKey)); + newPages.add(result); } + emit(DocumentsState(isLoaded: true, value: newPages, filter: state.filter)); } - Future _bulkReloadDocuments({ - bool propagateEventOnError = true, - }) async { - try { - final result = await documentRepository.find( - state.filter.copyWith(page: 1, pageSize: state.documents.length)); - emit(DocumentsState( - isLoaded: true, value: [result], filter: state.filter)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future _bulkReloadDocuments() async { + final result = await documentRepository + .find(state.filter.copyWith(page: 1, pageSize: state.documents.length)); + emit(DocumentsState(isLoaded: true, value: [result], filter: state.filter)); } - Future loadMore({ - bool propagateEventOnError = true, - }) async { + Future loadMore() async { if (state.isLastPageLoaded) { return; } final newFilter = state.filter.copyWith(page: state.filter.page + 1); - try { - final result = await documentRepository.find(newFilter); - emit(DocumentsState( - isLoaded: true, value: [...state.value, result], filter: newFilter)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + final result = await documentRepository.find(newFilter); + emit(DocumentsState( + isLoaded: true, value: [...state.value, result], filter: newFilter)); } - Future assignAsn( - DocumentModel document, { - bool propagateEventOnError = true, - }) async { - try { - if (document.archiveSerialNumber == null) { - final int asn = await documentRepository.findNextAsn(); - updateDocument(document.copyWith(archiveSerialNumber: asn)); - } - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } + Future assignAsn(DocumentModel document) async { + if (document.archiveSerialNumber == null) { + final int asn = await documentRepository.findNextAsn(); + updateDocument(document.copyWith(archiveSerialNumber: asn)); } } /// /// Update filter state and automatically reload documents. Always resets page to 1. /// Use [DocumentsCubit.loadMore] to load more data. - Future updateFilter( - {final DocumentFilter filter = DocumentFilter.initial, - bool propagateEventOnError = true}) async { - try { - final result = await documentRepository.find(filter.copyWith(page: 1)); - emit(DocumentsState(filter: filter, value: [result], isLoaded: true)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } else { - rethrow; - } - } + Future updateFilter({ + final DocumentFilter filter = DocumentFilter.initial, + }) async { + final result = await documentRepository.find(filter.copyWith(page: 1)); + emit(DocumentsState(filter: filter, value: [result], isLoaded: true)); } /// /// Convenience method which allows to directly use [DocumentFilter.copyWith] on the current filter. /// Future updateCurrentFilter( - final DocumentFilter Function(DocumentFilter) transformFn, { - bool propagateEventOnError = true, - }) async { - try { - return updateFilter(filter: transformFn(state.filter)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - return errorCubit.add(error); - } else { - rethrow; - } - } - } + final DocumentFilter Function(DocumentFilter) transformFn, + ) async => + updateFilter(filter: transformFn(state.filter)); void toggleDocumentSelection(DocumentModel model) { if (state.selection.contains(model)) { diff --git a/lib/features/documents/bloc/saved_view_cubit.dart b/lib/features/documents/bloc/saved_view_cubit.dart index e9c234c..9237be0 100644 --- a/lib/features/documents/bloc/saved_view_cubit.dart +++ b/lib/features/documents/bloc/saved_view_cubit.dart @@ -1,5 +1,4 @@ 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/di_initializer.dart'; import 'package:paperless_mobile/features/documents/bloc/saved_view_state.dart'; @@ -9,79 +8,42 @@ import 'package:injectable/injectable.dart'; @singleton class SavedViewCubit extends Cubit { - final GlobalErrorCubit errorCubit; - SavedViewCubit(this.errorCubit) : super(SavedViewState(value: {})); + SavedViewCubit() : super(SavedViewState(value: {})); - void selectView(SavedView? view, {bool propagateEventOnError = true}) { - try { - emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; - } + void selectView(SavedView? view) { + emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id)); } - Future add( - SavedView view, { - bool propagateEventOnError = true, - }) async { - try { - final savedView = await getIt().save(view); - emit( - SavedViewState( - value: {...state.value, savedView.id!: savedView}, - selectedSavedViewId: state.selectedSavedViewId, - ), - ); - return savedView; - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; - } + Future add(SavedView view) async { + final savedView = await getIt().save(view); + emit( + SavedViewState( + value: {...state.value, savedView.id!: savedView}, + selectedSavedViewId: state.selectedSavedViewId, + ), + ); + return savedView; } - Future remove( - SavedView view, { - bool propagateEventOnError = true, - }) async { - try { - final id = await getIt().delete(view); - final newValue = {...state.value}; - newValue.removeWhere((key, value) => key == id); - emit( - SavedViewState( - value: newValue, - selectedSavedViewId: view.id == state.selectedSavedViewId - ? null - : state.selectedSavedViewId, - ), - ); - return id; - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; - } + Future remove(SavedView view) async { + final id = await getIt().delete(view); + final newValue = {...state.value}; + newValue.removeWhere((key, value) => key == id); + emit( + SavedViewState( + value: newValue, + selectedSavedViewId: view.id == state.selectedSavedViewId + ? null + : state.selectedSavedViewId, + ), + ); + return id; } - Future initialize({ - bool propagateEventOnError = true, - }) async { - try { - final views = await getIt().getAll(); - final values = {for (var element in views) element.id!: element}; - emit(SavedViewState(value: values)); - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; - } + Future initialize() async { + final views = await getIt().getAll(); + final values = {for (var element in views) element.id!: element}; + emit(SavedViewState(value: values)); } void resetSelection() { diff --git a/lib/features/documents/model/paged_search_result.dart b/lib/features/documents/model/paged_search_result.dart index a80a56f..c95878a 100644 --- a/lib/features/documents/model/paged_search_result.dart +++ b/lib/features/documents/model/paged_search_result.dart @@ -4,6 +4,13 @@ import 'package:paperless_mobile/features/documents/model/document.model.dart'; const pageRegex = r".*page=(\d+).*"; +class PagedSearchResultJsonSerializer { + final JSON json; + final T Function(JSON) fromJson; + + PagedSearchResultJsonSerializer(this.json, this.fromJson); +} + class PagedSearchResult extends Equatable { /// Total number of available items final int count; @@ -46,12 +53,14 @@ class PagedSearchResult extends Equatable { }); factory PagedSearchResult.fromJson( - Map json, T Function(JSON) fromJson) { + PagedSearchResultJsonSerializer serializer) { return PagedSearchResult( - count: json['count'], - next: json['next'], - previous: json['previous'], - results: List.from(json['results']).map(fromJson).toList(), + count: serializer.json['count'], + next: serializer.json['next'], + previous: serializer.json['previous'], + results: List.from(serializer.json['results']) + .map(serializer.fromJson) + .toList(), ); } diff --git a/lib/features/documents/repository/document_repository_impl.dart b/lib/features/documents/repository/document_repository_impl.dart index cb7e6b9..06a2f32 100644 --- a/lib/features/documents/repository/document_repository_impl.dart +++ b/lib/features/documents/repository/document_repository_impl.dart @@ -138,8 +138,10 @@ class DocumentRepositoryImpl implements DocumentRepository { body: json.encode(doc.toJson()), headers: {"Content-Type": "application/json"}).timeout(requestTimeout); if (response.statusCode == 200) { - return DocumentModel.fromJson( - jsonDecode(utf8.decode(response.bodyBytes))); + return compute( + DocumentModel.fromJson, + jsonDecode(utf8.decode(response.bodyBytes)) as JSON, + ); } else { throw const ErrorMessage(ErrorCode.documentUpdateFailed); } @@ -152,11 +154,13 @@ class DocumentRepositoryImpl implements DocumentRepository { Uri.parse("/api/documents/?$filterParams"), ); if (response.statusCode == 200) { - final searchResult = PagedSearchResult.fromJson( - jsonDecode(utf8.decode(response.bodyBytes)), - DocumentModel.fromJson, + return compute( + PagedSearchResult.fromJson, + PagedSearchResultJsonSerializer( + jsonDecode(utf8.decode(response.bodyBytes)), + DocumentModel.fromJson, + ), ); - return searchResult; } else { throw const ErrorMessage(ErrorCode.documentLoadFailed); } @@ -261,8 +265,10 @@ class DocumentRepositoryImpl implements DocumentRepository { Future getMetaData(DocumentModel document) async { final response = await httpClient .get(Uri.parse("/api/documents/${document.id}/metadata/")); - return DocumentMetaData.fromJson( - jsonDecode(utf8.decode(response.bodyBytes))); + return compute( + DocumentMetaData.fromJson, + jsonDecode(utf8.decode(response.bodyBytes)) as JSON, + ); } @override @@ -280,10 +286,14 @@ class DocumentRepositoryImpl implements DocumentRepository { final response = await httpClient .get(Uri.parse("/api/documents/?more_like=$docId&pageSize=10")); if (response.statusCode == 200) { - return PagedSearchResult.fromJson( - jsonDecode(utf8.decode(response.bodyBytes)), - SimilarDocumentModel.fromJson, - ).results; + return (await compute( + PagedSearchResult.fromJson, + PagedSearchResultJsonSerializer( + jsonDecode(utf8.decode(response.bodyBytes)), + SimilarDocumentModel.fromJson, + ), + )) + .results; } throw const ErrorMessage(ErrorCode.similarQueryError); } diff --git a/lib/features/documents/view/pages/document_details_page.dart b/lib/features/documents/view/pages/document_details_page.dart index 085256a..9fa0370 100644 --- a/lib/features/documents/view/pages/document_details_page.dart +++ b/lib/features/documents/view/pages/document_details_page.dart @@ -198,10 +198,8 @@ class _DocumentDetailsPageState extends State { child: Text(S .of(context) .documentDetailsPageAssignAsnButtonLabel), - onPressed: widget.allowEdit - ? () => BlocProvider.of(context) - .assignAsn(document) - : null, + onPressed: + widget.allowEdit ? () => _assignAsn(document) : null, ), ), _separator(), @@ -233,6 +231,14 @@ class _DocumentDetailsPageState extends State { ); } + Future _assignAsn(DocumentModel document) async { + try { + await BlocProvider.of(context).assignAsn(document); + } on ErrorMessage catch (error) { + showError(context, error); + } + } + Widget _buildDocumentContentView(DocumentModel document, String? match) { return SingleChildScrollView( child: _DetailsItem( @@ -392,21 +398,23 @@ class _DocumentDetailsPageState extends State { ); } - Future _onDelete(DocumentModel document) async { - showDialog( - context: context, - builder: (context) => - DeleteDocumentConfirmationDialog(document: document)) - .then((delete) { - if (delete ?? false) { - BlocProvider.of(context) - .removeDocument(document) - .then((value) { - Navigator.pop(context); - showSnackBar(context, S.of(context).documentDeleteSuccessMessage); - }); + void _onDelete(DocumentModel document) async { + final delete = await showDialog( + context: context, + builder: (context) => + DeleteDocumentConfirmationDialog(document: document), + ) ?? + false; + if (delete) { + try { + await BlocProvider.of(context).removeDocument(document); + showSnackBar(context, S.of(context).documentDeleteSuccessMessage); + } on ErrorMessage catch (error) { + showError(context, error); + } finally { + Navigator.pop(context); } - }); + } } Future _onOpen(DocumentModel document) async { diff --git a/lib/features/documents/view/pages/document_edit_page.dart b/lib/features/documents/view/pages/document_edit_page.dart index 36d7b61..5ad6cc6 100644 --- a/lib/features/documents/view/pages/document_edit_page.dart +++ b/lib/features/documents/view/pages/document_edit_page.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; @@ -76,10 +77,14 @@ class _DocumentEditPageState extends State { setState(() { _isSubmitLoading = true; }); - await getIt().updateDocument(updatedDocument); - Navigator.pop(context); - showSnackBar( - context, "Document successfully updated."); //TODO: INTL + try { + await getIt().updateDocument(updatedDocument); + showSnackBar(context, S.of(context).documentUpdateErrorMessage); + } on ErrorMessage catch (error) { + showError(context, error); + } finally { + Navigator.pop(context); + } } }, icon: const Icon(Icons.save), diff --git a/lib/features/documents/view/pages/documents_page.dart b/lib/features/documents/view/pages/documents_page.dart index fb4f8f0..426a3af 100644 --- a/lib/features/documents/view/pages/documents_page.dart +++ b/lib/features/documents/view/pages/documents_page.dart @@ -44,13 +44,20 @@ class _DocumentsPageState extends State { @override void initState() { super.initState(); - final documentsCubit = BlocProvider.of(context); - if (!documentsCubit.state.isLoaded) { - documentsCubit.loadDocuments(); + if (!BlocProvider.of(context).state.isLoaded) { + _initDocuments(); } _pagingController.addPageRequestListener(_loadNewPage); } + Future _initDocuments() async { + try { + BlocProvider.of(context).loadDocuments(); + } on ErrorMessage catch (error) { + showError(context, error); + } + } + @override void dispose() { _pagingController.dispose(); @@ -64,17 +71,25 @@ class _DocumentsPageState extends State { if (pageCount <= pageKey + 1) { _pagingController.nextPageKey = null; } - documentsCubit.loadMore(); + try { + await documentsCubit.loadMore(); + } on ErrorMessage catch (error) { + showError(context, error); + } } void _onSelected(DocumentModel model) { BlocProvider.of(context).toggleDocumentSelection(model); } - Future _onRefresh() { - final documentsCubit = BlocProvider.of(context); - return documentsCubit.updateFilter( - filter: documentsCubit.state.filter.copyWith(page: 1)); + Future _onRefresh() async { + try { + await BlocProvider.of(context).updateCurrentFilter( + (filter) => filter.copyWith(page: 1), + ); + } on ErrorMessage catch (error) { + showError(context, error); + } } @override @@ -86,9 +101,9 @@ class _DocumentsPageState extends State { _panelController.close(); return false; } - final docBloc = BlocProvider.of(context); - if (docBloc.state.selection.isNotEmpty) { - docBloc.resetSelection(); + final documentsCubit = BlocProvider.of(context); + if (documentsCubit.state.selection.isNotEmpty) { + documentsCubit.resetSelection(); return false; } return true; diff --git a/lib/features/documents/view/widgets/search/document_filter_panel.dart b/lib/features/documents/view/widgets/search/document_filter_panel.dart index 603b4b6..31e8ad8 100644 --- a/lib/features/documents/view/widgets/search/document_filter_panel.dart +++ b/lib/features/documents/view/widgets/search/document_filter_panel.dart @@ -513,7 +513,7 @@ class _DocumentFilterPanelState extends State { ); } - void _onApplyFilter() { + void _onApplyFilter() async { if (_formKey.currentState?.saveAndValidate() ?? false) { final v = _formKey.currentState!.value; final docCubit = BlocProvider.of(context); @@ -530,13 +530,15 @@ class _DocumentFilterPanelState extends State { addedDateAfter: (v[fkAddedAt] as DateTimeRange?)?.start, queryType: v[QueryTypeFormField.fkQueryType] as QueryType, ); - BlocProvider.of(context) - .updateFilter(filter: newFilter) - .then((value) { + try { + await BlocProvider.of(context) + .updateFilter(filter: newFilter); BlocProvider.of(context).resetSelection(); FocusScope.of(context).unfocus(); widget.panelController.close(); - }); + } on ErrorMessage catch (error) { + showError(context, error); + } } } diff --git a/lib/features/documents/view/widgets/selection/documents_page_app_bar.dart b/lib/features/documents/view/widgets/selection/documents_page_app_bar.dart index 8926012..f38dfd8 100644 --- a/lib/features/documents/view/widgets/selection/documents_page_app_bar.dart +++ b/lib/features/documents/view/widgets/selection/documents_page_app_bar.dart @@ -74,14 +74,22 @@ class _DocumentsPageAppBarState extends State { void _onDelete(BuildContext context, DocumentsState documentsState) async { final shouldDelete = await showDialog( - context: context, - builder: (context) => BulkDeleteConfirmationDialog(state: documentsState), - ); - if (shouldDelete ?? false) { - BlocProvider.of(context) - .bulkRemoveDocuments(documentsState.selection) - .then((_) => showSnackBar( - context, S.of(context).documentsPageBulkDeleteSuccessfulText)); + context: context, + builder: (context) => + BulkDeleteConfirmationDialog(state: documentsState), + ) ?? + false; + if (shouldDelete) { + try { + await BlocProvider.of(context) + .bulkRemoveDocuments(documentsState.selection); + showSnackBar( + context, + S.of(context).documentsPageBulkDeleteSuccessfulText, + ); + } on ErrorMessage catch (error) { + showError(context, error); + } } } diff --git a/lib/features/documents/view/widgets/selection/saved_view_selection_widget.dart b/lib/features/documents/view/widgets/selection/saved_view_selection_widget.dart index 7ee8441..d31f8cc 100644 --- a/lib/features/documents/view/widgets/selection/saved_view_selection_widget.dart +++ b/lib/features/documents/view/widgets/selection/saved_view_selection_widget.dart @@ -78,22 +78,32 @@ class SavedViewSelectionWidget extends StatelessWidget { final newView = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => AddSavedViewPage( - currentFilter: getIt().state.filter), + currentFilter: getIt().state.filter, + ), ), ); if (newView != null) { - BlocProvider.of(context).add(newView); + try { + await BlocProvider.of(context).add(newView); + } on ErrorMessage catch (error) { + showError(context, error); + } } } - void _onSelected(bool isSelected, BuildContext context, SavedView view) { - if (isSelected) { - BlocProvider.of(context) - .updateFilter(filter: view.toDocumentFilter()); - BlocProvider.of(context).selectView(view); - } else { - BlocProvider.of(context).updateFilter(); - BlocProvider.of(context).selectView(null); + void _onSelected( + bool isSelected, BuildContext context, SavedView view) async { + try { + if (isSelected) { + BlocProvider.of(context) + .updateFilter(filter: view.toDocumentFilter()); + BlocProvider.of(context).selectView(view); + } else { + BlocProvider.of(context).updateFilter(); + BlocProvider.of(context).selectView(null); + } + } on ErrorMessage catch (error) { + showError(context, error); } } @@ -105,7 +115,11 @@ class SavedViewSelectionWidget extends StatelessWidget { ) ?? false; if (delete) { - BlocProvider.of(context).remove(view); + try { + BlocProvider.of(context).remove(view); + } on ErrorMessage catch (error) { + showError(context, error); + } } } } diff --git a/lib/features/documents/view/widgets/sort_documents_button.dart b/lib/features/documents/view/widgets/sort_documents_button.dart index cf3b66c..2c40e90 100644 --- a/lib/features/documents/view/widgets/sort_documents_button.dart +++ b/lib/features/documents/view/widgets/sort_documents_button.dart @@ -43,11 +43,18 @@ class _SortDocumentsButtonState extends State { ), onPressed: () async { setState(() => _isLoading = true); - BlocProvider.of(context) - .updateFilter( - filter: state.filter - .copyWith(sortOrder: state.filter.sortOrder.toggle())) - .whenComplete(() => setState(() => _isLoading = false)); + try { + await BlocProvider.of(context) + .updateCurrentFilter( + (filter) => filter.copyWith( + sortOrder: state.filter.sortOrder.toggle(), + ), + ); + } on ErrorMessage catch (error) { + showError(context, error); + } finally { + setState(() => _isLoading = false); + } }, ); } diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index df658c0..495e5f5 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; -import 'package:paperless_mobile/core/bloc/global_error_cubit.dart'; import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/widgets/offline_banner.dart'; import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; @@ -74,10 +74,14 @@ class _HomePageState extends State { } initializeLabelData(BuildContext context) { - BlocProvider.of(context).initialize(); - BlocProvider.of(context).initialize(); - BlocProvider.of(context).initialize(); - BlocProvider.of(context).initialize(); - BlocProvider.of(context).initialize(); + try { + BlocProvider.of(context).initialize(); + BlocProvider.of(context).initialize(); + BlocProvider.of(context).initialize(); + BlocProvider.of(context).initialize(); + BlocProvider.of(context).initialize(); + } on ErrorMessage catch (error) { + showError(context, error); + } } } diff --git a/lib/features/home/view/widget/info_drawer.dart b/lib/features/home/view/widget/info_drawer.dart index 91fd37e..d106cdd 100644 --- a/lib/features/home/view/widget/info_drawer.dart +++ b/lib/features/home/view/widget/info_drawer.dart @@ -1,8 +1,6 @@ -import 'dart:ui'; - import 'package:flutter/material.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/settings/bloc/application_settings_cubit.dart'; import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart'; @@ -129,14 +127,16 @@ class InfoDrawer extends StatelessWidget { leading: const Icon(Icons.logout), title: Text(S.of(context).appDrawerLogoutLabel), onTap: () { - // Clear all bloc data - BlocProvider.of(context).logout(); - getIt().reset(); - getIt().reset(); - getIt().reset(); - getIt().reset(); - getIt().reset(); - getIt().reset(); + try { + BlocProvider.of(context).logout(); + getIt().reset(); + getIt().reset(); + getIt().reset(); + getIt().reset(); + getIt().reset(); + } on ErrorMessage catch (error) { + showError(context, error); + } }, ), const Divider(), diff --git a/lib/features/labels/correspondent/bloc/correspondents_cubit.dart b/lib/features/labels/correspondent/bloc/correspondents_cubit.dart index 72d21f4..03c62a2 100644 --- a/lib/features/labels/correspondent/bloc/correspondents_cubit.dart +++ b/lib/features/labels/correspondent/bloc/correspondents_cubit.dart @@ -4,7 +4,7 @@ import 'package:injectable/injectable.dart'; @singleton class CorrespondentCubit extends LabelCubit { - CorrespondentCubit(super.metaDataService, super.errorCubit); + CorrespondentCubit(super.metaDataService); @override Future initialize() async { diff --git a/lib/features/labels/correspondent/view/pages/edit_correspondent_page.dart b/lib/features/labels/correspondent/view/pages/edit_correspondent_page.dart index b279783..cebf612 100644 --- a/lib/features/labels/correspondent/view/pages/edit_correspondent_page.dart +++ b/lib/features/labels/correspondent/view/pages/edit_correspondent_page.dart @@ -18,21 +18,28 @@ class EditCorrespondentPage extends StatelessWidget { return EditLabelPage( label: correspondent, onSubmit: BlocProvider.of(context).replace, - onDelete: (correspondent) => _onDelete(correspondent, context), + onDelete: (correspondent) => _onDelete(context, correspondent), fromJson: Correspondent.fromJson, ); } Future _onDelete( - Correspondent correspondent, BuildContext context) async { - await BlocProvider.of(context).remove(correspondent); - final cubit = BlocProvider.of(context); - if (cubit.state.filter.correspondent.id == correspondent.id) { - cubit.updateFilter( - filter: cubit.state.filter - .copyWith(correspondent: const CorrespondentQuery.unset()), - ); + BuildContext context, + Correspondent correspondent, + ) async { + try { + await BlocProvider.of(context).remove(correspondent); + final cubit = BlocProvider.of(context); + if (cubit.state.filter.correspondent.id == correspondent.id) { + await cubit.updateCurrentFilter( + (filter) => filter.copyWith( + correspondent: const CorrespondentQuery.unset(), + ), + ); + } + Navigator.pop(context); + } on ErrorMessage catch (error) { + showError(context, error); } - Navigator.pop(context); } } diff --git a/lib/features/labels/correspondent/view/widgets/correspondent_widget.dart b/lib/features/labels/correspondent/view/widgets/correspondent_widget.dart index c25ead9..395d780 100644 --- a/lib/features/labels/correspondent/view/widgets/correspondent_widget.dart +++ b/lib/features/labels/correspondent/view/widgets/correspondent_widget.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart'; import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart'; import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart'; +import 'package:paperless_mobile/util.dart'; class CorrespondentWidget extends StatelessWidget { final int? correspondentId; @@ -44,17 +46,21 @@ class CorrespondentWidget extends StatelessWidget { void _addCorrespondentToFilter(BuildContext context) { final cubit = BlocProvider.of(context); - if (cubit.state.filter.correspondent.id == correspondentId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(correspondent: const CorrespondentQuery.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - correspondent: CorrespondentQuery.fromId(correspondentId)), - ); + try { + if (cubit.state.filter.correspondent.id == correspondentId) { + cubit.updateCurrentFilter( + (filter) => + filter.copyWith(correspondent: const CorrespondentQuery.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith( + correspondent: CorrespondentQuery.fromId(correspondentId)), + ); + } + afterSelected?.call(); + } on ErrorMessage catch (error) { + showError(context, error); } - afterSelected?.call(); } } diff --git a/lib/features/labels/document_type/bloc/document_type_cubit.dart b/lib/features/labels/document_type/bloc/document_type_cubit.dart index 3a552b4..ca534cc 100644 --- a/lib/features/labels/document_type/bloc/document_type_cubit.dart +++ b/lib/features/labels/document_type/bloc/document_type_cubit.dart @@ -5,7 +5,7 @@ import 'package:injectable/injectable.dart'; @singleton class DocumentTypeCubit extends LabelCubit { - DocumentTypeCubit(super.metaDataService, super.errorCubit); + DocumentTypeCubit(super.metaDataService); @override Future initialize() async { diff --git a/lib/features/labels/document_type/view/widgets/document_type_widget.dart b/lib/features/labels/document_type/view/widgets/document_type_widget.dart index 0024d4d..6c91c89 100644 --- a/lib/features/labels/document_type/view/widgets/document_type_widget.dart +++ b/lib/features/labels/document_type/view/widgets/document_type_widget.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart'; import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart'; import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart'; +import 'package:paperless_mobile/util.dart'; class DocumentTypeWidget extends StatelessWidget { final int? documentTypeId; @@ -39,17 +41,21 @@ class DocumentTypeWidget extends StatelessWidget { void _addDocumentTypeToFilter(BuildContext context) { final cubit = BlocProvider.of(context); - if (cubit.state.filter.documentType.id == documentTypeId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(documentType: const DocumentTypeQuery.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - documentType: DocumentTypeQuery.fromId(documentTypeId)), - ); + try { + if (cubit.state.filter.documentType.id == documentTypeId) { + cubit.updateCurrentFilter( + (filter) => + filter.copyWith(documentType: const DocumentTypeQuery.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith( + documentType: DocumentTypeQuery.fromId(documentTypeId)), + ); + } + afterSelected?.call(); + } on ErrorMessage catch (error) { + showError(context, error); } - afterSelected?.call(); } } diff --git a/lib/features/labels/storage_path/bloc/storage_path_cubit.dart b/lib/features/labels/storage_path/bloc/storage_path_cubit.dart index d484673..efbd203 100644 --- a/lib/features/labels/storage_path/bloc/storage_path_cubit.dart +++ b/lib/features/labels/storage_path/bloc/storage_path_cubit.dart @@ -4,7 +4,7 @@ import 'package:paperless_mobile/features/labels/storage_path/model/storage_path @singleton class StoragePathCubit extends LabelCubit { - StoragePathCubit(super.metaDataService, super.errorCubit); + StoragePathCubit(super.metaDataService); @override Future initialize() async { diff --git a/lib/features/labels/storage_path/view/pages/edit_storage_path_page.dart b/lib/features/labels/storage_path/view/pages/edit_storage_path_page.dart index ec89fdb..b04c6ea 100644 --- a/lib/features/labels/storage_path/view/pages/edit_storage_path_page.dart +++ b/lib/features/labels/storage_path/view/pages/edit_storage_path_page.dart @@ -32,13 +32,19 @@ class EditStoragePathPage extends StatelessWidget { } Future _onDelete(StoragePath path, BuildContext context) async { - await BlocProvider.of(context).remove(path); - final cubit = BlocProvider.of(context); - if (cubit.state.filter.storagePath.id == path.id) { - cubit.updateFilter( - filter: cubit.state.filter - .copyWith(storagePath: const StoragePathQuery.unset())); + try { + await BlocProvider.of(context).remove(path); + final cubit = BlocProvider.of(context); + if (cubit.state.filter.storagePath.id == path.id) { + cubit.updateCurrentFilter( + (filter) => filter.copyWith( + storagePath: const StoragePathQuery.unset(), + ), + ); + } + Navigator.pop(context); + } on ErrorMessage catch (error) { + showError(context, error); } - Navigator.pop(context); } } diff --git a/lib/features/labels/storage_path/view/widgets/storage_path_widget.dart b/lib/features/labels/storage_path/view/widgets/storage_path_widget.dart index f02b946..10acece 100644 --- a/lib/features/labels/storage_path/view/widgets/storage_path_widget.dart +++ b/lib/features/labels/storage_path/view/widgets/storage_path_widget.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart'; import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart'; import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart'; +import 'package:paperless_mobile/util.dart'; class StoragePathWidget extends StatelessWidget { final int? pathId; @@ -43,17 +45,21 @@ class StoragePathWidget extends StatelessWidget { void _addStoragePathToFilter(BuildContext context) { final cubit = BlocProvider.of(context); - if (cubit.state.filter.correspondent.id == pathId) { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(storagePath: const StoragePathQuery.unset()), - ); - } else { - cubit.updateCurrentFilter( - (filter) => - filter.copyWith(storagePath: StoragePathQuery.fromId(pathId)), - ); + try { + if (cubit.state.filter.correspondent.id == pathId) { + cubit.updateCurrentFilter( + (filter) => + filter.copyWith(storagePath: const StoragePathQuery.unset()), + ); + } else { + cubit.updateCurrentFilter( + (filter) => + filter.copyWith(storagePath: StoragePathQuery.fromId(pathId)), + ); + } + afterSelected?.call(); + } on ErrorMessage catch (error) { + showError(context, error); } - afterSelected?.call(); } } diff --git a/lib/features/labels/tags/bloc/tags_cubit.dart b/lib/features/labels/tags/bloc/tags_cubit.dart index 3c5b886..19e2611 100644 --- a/lib/features/labels/tags/bloc/tags_cubit.dart +++ b/lib/features/labels/tags/bloc/tags_cubit.dart @@ -4,7 +4,7 @@ import 'package:injectable/injectable.dart'; @singleton class TagCubit extends LabelCubit { - TagCubit(super.metaDataService, super.errorCubit); + TagCubit(super.metaDataService); @override Future initialize() async { diff --git a/lib/features/labels/tags/view/pages/edit_tag_page.dart b/lib/features/labels/tags/view/pages/edit_tag_page.dart index e48995d..8dff9de 100644 --- a/lib/features/labels/tags/view/pages/edit_tag_page.dart +++ b/lib/features/labels/tags/view/pages/edit_tag_page.dart @@ -43,18 +43,22 @@ class EditTagPage extends StatelessWidget { } Future _onDelete(Tag tag, BuildContext context) async { - await BlocProvider.of(context).remove(tag); - final cubit = BlocProvider.of(context); - final currentFilter = cubit.state.filter; - late DocumentFilter updatedFilter = currentFilter; - if (currentFilter.tags.ids.contains(tag.id)) { - updatedFilter = currentFilter.copyWith( - tags: TagsQuery.fromIds( - currentFilter.tags.ids.where((tagId) => tagId != tag.id).toList(), - ), - ); + try { + await BlocProvider.of(context).remove(tag); + final cubit = BlocProvider.of(context); + final currentFilter = cubit.state.filter; + late DocumentFilter updatedFilter = currentFilter; + if (currentFilter.tags.ids.contains(tag.id)) { + updatedFilter = currentFilter.copyWith( + tags: TagsQuery.fromIds( + currentFilter.tags.ids.where((tagId) => tagId != tag.id).toList(), + ), + ); + } + cubit.updateFilter(filter: updatedFilter); + Navigator.pop(context); + } on ErrorMessage catch (error) { + showError(context, error); } - cubit.updateFilter(filter: updatedFilter); - Navigator.pop(context); } } diff --git a/lib/features/labels/tags/view/widgets/tag_widget.dart b/lib/features/labels/tags/view/widgets/tag_widget.dart index 96270a1..b077d79 100644 --- a/lib/features/labels/tags/view/widgets/tag_widget.dart +++ b/lib/features/labels/tags/view/widgets/tag_widget.dart @@ -38,22 +38,27 @@ class TagWidget extends StatelessWidget { void _addTagToFilter(BuildContext context) { final cubit = BlocProvider.of(context); - if (cubit.state.filter.tags.ids.contains(tag.id)) { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - tags: TagsQuery.fromIds( - cubit.state.filter.tags.ids.where((id) => id != tag.id).toList()), - ), - ); - } else { - cubit.updateCurrentFilter( - (filter) => filter.copyWith( - tags: TagsQuery.fromIds([...cubit.state.filter.tags.ids, tag.id!]), - ), - ); - } - if (afterTagTapped != null) { - afterTagTapped!(); + try { + if (cubit.state.filter.tags.ids.contains(tag.id)) { + cubit.updateCurrentFilter( + (filter) => filter.copyWith( + tags: TagsQuery.fromIds(cubit.state.filter.tags.ids + .where((id) => id != tag.id) + .toList()), + ), + ); + } else { + cubit.updateCurrentFilter( + (filter) => filter.copyWith( + tags: TagsQuery.fromIds([...cubit.state.filter.tags.ids, tag.id!]), + ), + ); + } + if (afterTagTapped != null) { + afterTagTapped!(); + } + } on ErrorMessage catch (error) { + showError(context, error); } } } diff --git a/lib/features/labels/view/pages/edit_label_page.dart b/lib/features/labels/view/pages/edit_label_page.dart index a970bac..f31bf27 100644 --- a/lib/features/labels/view/pages/edit_label_page.dart +++ b/lib/features/labels/view/pages/edit_label_page.dart @@ -3,11 +3,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/type/types.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart'; import 'package:paperless_mobile/features/labels/model/label.model.dart'; import 'package:paperless_mobile/generated/l10n.dart'; +import 'package:paperless_mobile/util.dart'; class EditLabelPage extends StatefulWidget { final T label; @@ -144,6 +146,8 @@ class _EditLabelPageState extends State> { Navigator.pop(context); } on PaperlessValidationErrors catch (errorMessages) { setState(() => _errors = errorMessages); + } on ErrorMessage catch (error) { + showError(context, error); } } } diff --git a/lib/features/labels/view/widgets/label_item.dart b/lib/features/labels/view/widgets/label_item.dart index a0b7cda..0f4e784 100644 --- a/lib/features/labels/view/widgets/label_item.dart +++ b/lib/features/labels/view/widgets/label_item.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:paperless_mobile/core/bloc/global_error_cubit.dart'; import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart'; import 'package:paperless_mobile/core/model/error_message.dart'; @@ -53,10 +52,9 @@ class LabelItem extends StatelessWidget { MaterialPageRoute( builder: (context) => LabelBlocProvider( child: BlocProvider( - create: (context) => DocumentsCubit( - getIt(), - getIt()) - ..updateFilter(filter: filter), + create: (context) => + DocumentsCubit(getIt()) + ..updateFilter(filter: filter), child: LinkedDocumentsPreview(filter: filter), ), ), diff --git a/lib/features/login/bloc/authentication_cubit.dart b/lib/features/login/bloc/authentication_cubit.dart index 5a2b216..7d50760 100644 --- a/lib/features/login/bloc/authentication_cubit.dart +++ b/lib/features/login/bloc/authentication_cubit.dart @@ -1,7 +1,7 @@ +import 'dart:developer'; import 'dart:io'; 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/core/store/local_vault.dart'; import 'package:paperless_mobile/di_initializer.dart'; @@ -17,13 +17,11 @@ const authenticationKey = "authentication"; @singleton class AuthenticationCubit extends Cubit { final LocalVault localStore; - final GlobalErrorCubit errorCubit; final AuthenticationService authenticationService; AuthenticationCubit( this.localStore, this.authenticationService, - this.errorCubit, ) : super(AuthenticationState.initial); Future initialize() { @@ -34,7 +32,6 @@ class AuthenticationCubit extends Cubit { required UserCredentials credentials, required String serverUrl, ClientCertificate? clientCertificate, - bool propagateEventOnError = true, }) async { assert(credentials.username != null && credentials.password != null); try { @@ -75,60 +72,37 @@ class AuthenticationCubit extends Cubit { } on TlsException catch (_) { const error = ErrorMessage(ErrorCode.invalidClientCertificateConfiguration); - if (propagateEventOnError) { - errorCubit.add(error); - } throw error; } on SocketException catch (err) { - late ErrorMessage error; if (err.message.contains("connection timed out")) { - error = const ErrorMessage(ErrorCode.requestTimedOut); + throw const ErrorMessage(ErrorCode.requestTimedOut); } else { - error = ErrorMessage.unknown(); + throw ErrorMessage.unknown(); } - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); - } - rethrow; } } - Future restoreSessionState({ - bool propagateEventOnError = true, - }) async { - try { - final storedAuth = await localStore.loadAuthenticationInformation(); - final appSettings = await localStore.loadApplicationSettings() ?? - ApplicationSettingsState.defaultSettings; + Future restoreSessionState() async { + final storedAuth = await localStore.loadAuthenticationInformation(); + final appSettings = await localStore.loadApplicationSettings() ?? + ApplicationSettingsState.defaultSettings; - if (storedAuth == null || !storedAuth.isValid) { + if (storedAuth == null || !storedAuth.isValid) { + emit(AuthenticationState(isAuthenticated: false, wasLoginStored: false)); + } else { + if (!appSettings.isLocalAuthenticationEnabled || + await authenticationService + .authenticateLocalUser("Authenticate to log back in")) { + registerSecurityContext(storedAuth.clientCertificate); emit( - AuthenticationState(isAuthenticated: false, wasLoginStored: false)); + AuthenticationState( + isAuthenticated: true, + wasLoginStored: true, + authentication: storedAuth, + ), + ); } else { - if (!appSettings.isLocalAuthenticationEnabled || - await authenticationService - .authenticateLocalUser("Authenticate to log back in")) { - registerSecurityContext(storedAuth.clientCertificate); - emit( - AuthenticationState( - isAuthenticated: true, - wasLoginStored: true, - authentication: storedAuth, - ), - ); - } else { - emit(AuthenticationState( - isAuthenticated: false, wasLoginStored: true)); - } - } - } on ErrorMessage catch (error) { - if (propagateEventOnError) { - errorCubit.add(error); + emit(AuthenticationState(isAuthenticated: false, wasLoginStored: true)); } } } diff --git a/lib/features/login/services/authentication.service.dart b/lib/features/login/services/authentication.service.dart index c4c02c0..52943fc 100644 --- a/lib/features/login/services/authentication.service.dart +++ b/lib/features/login/services/authentication.service.dart @@ -26,10 +26,19 @@ class AuthenticationService { required String password, required String serverUrl, }) async { - final response = await httpClient.post( - Uri.parse("/api/token/"), - body: {"username": username, "password": password}, - ); + late Response response; + try { + response = await httpClient.post( + Uri.parse("/api/token/"), + body: {"username": username, "password": password}, + ); + } on FormatException catch (e) { + final source = e.source; + if (source is String && + source.contains("400 No required SSL certificate was sent")) { + throw const ErrorMessage(ErrorCode.missingClientCertificate); + } + } if (response.statusCode == 200) { final data = jsonDecode(utf8.decode(response.bodyBytes)); return data['token']; diff --git a/lib/features/login/view/login_page.dart b/lib/features/login/view/login_page.dart index 0dfddee..809381a 100644 --- a/lib/features/login/view/login_page.dart +++ b/lib/features/login/view/login_page.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/view/widgets/client_certificate_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/server_address_form_field.dart'; import 'package:paperless_mobile/features/login/view/widgets/user_credentials_form_field.dart'; import 'package:paperless_mobile/generated/l10n.dart'; +import 'package:paperless_mobile/util.dart'; class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @@ -72,7 +74,8 @@ class _LoginPageState extends State { return ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStatePropertyAll( - Theme.of(context).colorScheme.primaryContainer), + Theme.of(context).colorScheme.primaryContainer, + ), elevation: const MaterialStatePropertyAll(0), ), onPressed: _login, @@ -82,19 +85,25 @@ class _LoginPageState extends State { ); } - void _login() { + void _login() async { FocusScope.of(context).unfocus(); if (_formKey.currentState?.saveAndValidate() ?? false) { setState(() => _isLoginLoading = true); - final form = _formKey.currentState?.value; - BlocProvider.of(context) - .login( - credentials: form?[UserCredentialsFormField.fkCredentials], - serverUrl: form?[ServerAddressFormField.fkServerAddress], - clientCertificate: - form?[ClientCertificateFormField.fkClientCertificate], - ) - .whenComplete(() => setState(() => _isLoginLoading = false)); + final form = _formKey.currentState!.value; + try { + await BlocProvider.of(context).login( + credentials: form[UserCredentialsFormField.fkCredentials], + serverUrl: form[ServerAddressFormField.fkServerAddress], + clientCertificate: + form[ClientCertificateFormField.fkClientCertificate], + ); + } on ErrorMessage catch (error) { + showError(context, error); + } catch (unknownError) { + showSnackBar(context, unknownError.toString()); + } finally { + setState(() => _isLoginLoading = false); + } } } } diff --git a/lib/features/scan/bloc/document_scanner_cubit.dart b/lib/features/scan/bloc/document_scanner_cubit.dart index 85b2166..92f263d 100644 --- a/lib/features/scan/bloc/document_scanner_cubit.dart +++ b/lib/features/scan/bloc/document_scanner_cubit.dart @@ -22,19 +22,22 @@ class DocumentScannerCubit extends Cubit> { scans.removeAt(fileIndex); emit(scans); } catch (_) { - addError(const ErrorMessage(ErrorCode.scanRemoveFailed)); + throw const ErrorMessage(ErrorCode.scanRemoveFailed); } } void reset() { - for (final doc in state) { - doc.deleteSync(); - if (kDebugMode) { - log('[ScannerCubit]: Removed ${doc.path}'); + try { + for (final doc in state) { + doc.deleteSync(); + if (kDebugMode) { + log('[ScannerCubit]: Removed ${doc.path}'); + } } + imageCache.clear(); + emit(initialState); + } catch (_) { + throw const ErrorMessage(ErrorCode.scanRemoveFailed); } - - imageCache.clear(); - emit(initialState); } } diff --git a/lib/features/scan/view/document_upload_page.dart b/lib/features/scan/view/document_upload_page.dart index f8dac98..cf51e16 100644 --- a/lib/features/scan/view/document_upload_page.dart +++ b/lib/features/scan/view/document_upload_page.dart @@ -185,51 +185,32 @@ class _DocumentUploadPageState extends State { } void _onSubmit() async { - _formKey.currentState?.save(); - if (_formKey.currentState?.validate() ?? false) { + if (_formKey.currentState?.saveAndValidate() ?? false) { try { - setState(() { - _isUploadLoading = true; - }); + setState(() => _isUploadLoading = true); + + final fv = _formKey.currentState!.value; + + final createdAt = fv[DocumentModel.createdKey] as DateTime?; + final title = fv[DocumentModel.titleKey] as String; + final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter; + final tags = fv[DocumentModel.tagsKey] as TagsQuery; + final correspondent = + fv[DocumentModel.correspondentKey] as IdQueryParameter; + await BlocProvider.of(context).addDocument( widget.fileBytes, _formKey.currentState?.value[fkFileName], - onConsumptionFinished: (document) { - ScaffoldMessenger.of(rootScaffoldKey.currentContext!).showSnackBar( - SnackBar( - action: SnackBarAction( - onPressed: () { - getIt().reloadDocuments(); - }, - label: S - .of(context) - .documentUploadProcessingSuccessfulReloadActionText, - ), - content: - Text(S.of(context).documentUploadProcessingSuccessfulText), - ), - ); - }, - title: _formKey.currentState?.value[DocumentModel.titleKey], - documentType: (_formKey.currentState - ?.value[DocumentModel.documentTypeKey] as IdQueryParameter) - .id, - correspondent: (_formKey.currentState - ?.value[DocumentModel.correspondentKey] as IdQueryParameter) - .id, - tags: - (_formKey.currentState?.value[DocumentModel.tagsKey] as TagsQuery) - .ids, - createdAt: (_formKey.currentState?.value[DocumentModel.createdKey] - as DateTime?), - propagateEventOnError: false, + onConsumptionFinished: _onConsumptionFinished, + title: title, + documentType: docType.id, + correspondent: correspondent.id, + tags: tags.ids, + createdAt: createdAt, ); - setState(() { - _isUploadLoading = false; - }); - getIt().reset(); - Navigator.pop(context); + getIt().reset(); //TODO: Access via provider showSnackBar(context, S.of(context).documentUploadSuccessText); + Navigator.pop(context); widget.afterUpload?.call(); } on ErrorMessage catch (error) { showError(context, error); @@ -239,9 +220,28 @@ class _DocumentUploadPageState extends State { showSnackBar(context, other.toString()); } finally { setState(() { - _isUploadLoading = true; + _isUploadLoading = false; }); } } } + + void _onConsumptionFinished(document) { + ScaffoldMessenger.of(rootScaffoldKey.currentContext!).showSnackBar( + SnackBar( + action: SnackBarAction( + onPressed: () async { + try { + getIt().reloadDocuments(); + } on ErrorMessage catch (error) { + showError(context, error); + } + }, + label: + S.of(context).documentUploadProcessingSuccessfulReloadActionText, + ), + content: Text(S.of(context).documentUploadProcessingSuccessfulText), + ), + ); + } } diff --git a/lib/features/scan/view/scanner_page.dart b/lib/features/scan/view/scanner_page.dart index 215db7d..0c994d5 100644 --- a/lib/features/scan/view/scanner_page.dart +++ b/lib/features/scan/view/scanner_page.dart @@ -8,8 +8,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:mime/mime.dart'; -import 'package:paperless_mobile/core/bloc/global_error_cubit.dart'; import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart'; +import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/di_initializer.dart'; @@ -19,6 +19,7 @@ import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart' import 'package:paperless_mobile/features/scan/view/document_upload_page.dart'; import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_widget.dart'; import 'package:paperless_mobile/generated/l10n.dart'; +import 'package:paperless_mobile/util.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:permission_handler/permission_handler.dart'; @@ -32,15 +33,6 @@ class ScannerPage extends StatefulWidget { class _ScannerPageState extends State with SingleTickerProviderStateMixin { - static const _supportedExtensions = [ - 'pdf', - 'png', - 'tiff', - 'gif', - 'jpg', - 'jpeg' - ]; - late final AnimationController _fabPulsingController; late final Animation _animation; @@ -205,8 +197,14 @@ class _ScannerPageState extends State itemBuilder: (context, index) { return GridImageItemWidget( file: scans[index], - onDelete: () => BlocProvider.of(context) - .removeScan(index), + onDelete: () async { + try { + BlocProvider.of(context) + .removeScan(index); + } on ErrorMessage catch (error) { + showError(context, error); + } + }, index: index, totalNumberOfFiles: scans.length, ); @@ -214,7 +212,11 @@ class _ScannerPageState extends State } void _reset(BuildContext context) { - BlocProvider.of(context).reset(); + try { + BlocProvider.of(context).reset(); + } on ErrorMessage catch (error) { + showError(context, error); + } } Future _requestCameraPermissions() async { @@ -227,15 +229,14 @@ class _ScannerPageState extends State void _onUploadFromFilesystem() async { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: _supportedExtensions, + allowedExtensions: supportedFileExtensions, withData: true, ); if (result?.files.single.path != null) { File file = File(result!.files.single.path!); - if (!_supportedExtensions.contains(file.path.split('.').last)) { - return getIt().add( - const ErrorMessage(ErrorCode.unsupportedFileFormat), - ); + if (!supportedFileExtensions.contains(file.path.split('.').last)) { + //TODO: Show error message; + return; } final mimeType = lookupMimeType(file.path) ?? ''; late Uint8List fileBytes; diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index b7a35c0..d195541 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -187,5 +187,7 @@ "editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen", "editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?", "settingsPageStorageSettingsLabel": "Storage", - "settingsPageStorageSettingsDescriptionText": "Manage files and storage space" + "settingsPageStorageSettingsDescriptionText": "Manage files and storage space", + "documentUpdateErrorMessage": "Document successfully updated.", + "errorMessageMissingClientCertificate": "Ein Client Zerfitikat wurde erwartet, aber nicht gesendet. Bitte konfiguriere ein gültiges Zertifikat." } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d3915ea..451f54b 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -188,5 +188,7 @@ "editLabelPageConfirmDeletionDialogTitle": "Confirm deletion", "editLabelPageDeletionDialogText": "This label contains references to other documents. By deleting this label, all references will be removed. Continue?", "settingsPageStorageSettingsLabel": "Storage", - "settingsPageStorageSettingsDescriptionText": "Manage files and storage space" + "settingsPageStorageSettingsDescriptionText": "Manage files and storage space", + "documentUpdateErrorMessage": "Document successfully updated.", + "errorMessageMissingClientCertificate": "A client certificate was expected but not sent. Please provide a valid client certificate." } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 9033ce4..b22adc7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,11 +9,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; -import 'package:paperless_mobile/core/bloc/global_error_cubit.dart'; import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/core/global/asset_images.dart'; +import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/http_self_signed_certificate_override.dart'; import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart'; +import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/core/util.dart'; import 'package:paperless_mobile/di_initializer.dart'; @@ -148,51 +149,46 @@ class AuthenticationWrapper extends StatefulWidget { } class _AuthenticationWrapperState extends State { + bool isFileTypeSupported(SharedMediaFile file) { + return supportedFileExtensions.contains(file.path.split('.').last); + } + + void handleReceivedFiles(List files) async { + if (files.isEmpty) { + return; + } + if (!isFileTypeSupported(files.first)) { + showError(context, const ErrorMessage(ErrorCode.unsupportedFileFormat)); + await Future.delayed( + const Duration(seconds: 2), + () => SystemNavigator.pop(), + ); + } + final bytes = File(files.first.path).readAsBytesSync(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => BlocProvider.value( + value: getIt(), + child: LabelBlocProvider( + child: DocumentUploadPage( + fileBytes: bytes, + afterUpload: () => SystemNavigator.pop(), + ), + ), + ), + ), + ); + } + @override void initState() { super.initState(); // For sharing files coming from outside the app while the app is still opened - ReceiveSharingIntent.getMediaStream().listen((List value) { - final bytes = File(value.first.path).readAsBytesSync(); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BlocProvider.value( - value: getIt(), - child: LabelBlocProvider( - child: DocumentUploadPage( - fileBytes: bytes, - afterUpload: () => SystemNavigator.pop(), - ), - ), - ), - ), - ); - }, onError: (err) { - log(err); - }); + ReceiveSharingIntent.getMediaStream().listen(handleReceivedFiles); // For sharing files coming from outside the app while the app is closed - ReceiveSharingIntent.getInitialMedia().then((List value) { - if (value.isEmpty) { - return; - } - final bytes = File(value.first.path).readAsBytesSync(); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BlocProvider.value( - value: getIt(), - child: LabelBlocProvider( - child: DocumentUploadPage( - fileBytes: bytes, - afterUpload: () => SystemNavigator.pop(), - ), - ), - ), - ), - ); - }); + ReceiveSharingIntent.getInitialMedia().then(handleReceivedFiles); } @override @@ -206,47 +202,37 @@ class _AuthenticationWrapperState extends State { @override Widget build(BuildContext context) { - return BlocProvider.value( - value: getIt(), - child: BlocListener( - listener: (context, state) { - if (state.hasError) { - showSnackBar(context, translateError(context, state.error!.code)); + return SafeArea( + top: true, + left: false, + right: false, + bottom: false, + child: BlocConsumer( + listener: (context, authState) { + final bool showIntroSlider = + authState.isAuthenticated && !authState.wasLoginStored; + if (showIntroSlider) { + for (final img in AssetImages.values) { + img.load(context); + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ApplicationIntroSlideshow(), + fullscreenDialog: true, + ), + ); + } + }, + builder: (context, authentication) { + if (authentication.isAuthenticated) { + return const LabelBlocProvider( + child: HomePage(), + ); + } else { + return const LoginPage(); } }, - child: SafeArea( - top: true, - left: false, - right: false, - bottom: false, - child: BlocConsumer( - listener: (context, authState) { - final bool showIntroSlider = - authState.isAuthenticated && !authState.wasLoginStored; - if (showIntroSlider) { - for (final img in AssetImages.values) { - img.load(context); - } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ApplicationIntroSlideshow(), - fullscreenDialog: true, - ), - ); - } - }, - builder: (context, authentication) { - if (authentication.isAuthenticated) { - return const LabelBlocProvider( - child: HomePage(), - ); - } else { - return const LoginPage(); - } - }, - ), - ), ), ); } diff --git a/test/src/bloc/document_cubit_test.dart b/test/src/bloc/document_cubit_test.dart index ad776c6..3a1ea64 100644 --- a/test/src/bloc/document_cubit_test.dart +++ b/test/src/bloc/document_cubit_test.dart @@ -1,5 +1,4 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:paperless_mobile/core/bloc/global_error_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_state.dart'; import 'package:paperless_mobile/features/documents/model/document.model.dart'; @@ -15,32 +14,32 @@ import 'package:mockito/mockito.dart'; import '../../utils.dart'; @GenerateNiceMocks([MockSpec()]) -@GenerateNiceMocks([MockSpec()]) import 'document_cubit_test.mocks.dart'; void main() async { TestWidgetsFlutterBinding.ensureInitialized(); final List documents = List.unmodifiable( - await loadCollection("test/fixtures/documents/documents.json", DocumentModel.fromJson), + await loadCollection( + "test/fixtures/documents/documents.json", DocumentModel.fromJson), ); final List tags = List.unmodifiable( await loadCollection("test/fixtures/tags/tags.json", Tag.fromJson), ); final List correspondents = List.unmodifiable( - await loadCollection( - "test/fixtures/correspondents/correspondents.json", Correspondent.fromJson), + await loadCollection("test/fixtures/correspondents/correspondents.json", + Correspondent.fromJson), ); final List documentTypes = List.unmodifiable( - await loadCollection("test/fixtures/document_types/document_types.json", DocumentType.fromJson), + await loadCollection("test/fixtures/document_types/document_types.json", + DocumentType.fromJson), ); final MockDocumentRepository documentRepository = MockDocumentRepository(); - final MockGlobalErrorCubit globalErrorCubit = MockGlobalErrorCubit(); group("Test DocumentsCubit reloadDocuments", () { test("Assert correct initial state", () { - expect(DocumentsCubit(documentRepository, globalErrorCubit).state, DocumentsState.initial); + expect(DocumentsCubit(documentRepository).state, DocumentsState.initial); }); blocTest( @@ -53,7 +52,7 @@ void main() async { results: documents, ), ), - build: () => DocumentsCubit(documentRepository, globalErrorCubit), + build: () => DocumentsCubit(documentRepository), seed: () => DocumentsState.initial, act: (bloc) => bloc.loadDocuments(), expect: () => [ @@ -82,7 +81,7 @@ void main() async { results: documents, ), ), - build: () => DocumentsCubit(documentRepository, globalErrorCubit), + build: () => DocumentsCubit(documentRepository), seed: () => DocumentsState.initial, act: (bloc) => bloc.loadDocuments(), expect: () => [