Started removing tight coupling

This commit is contained in:
Anton Stubenbord
2022-11-24 22:51:42 +01:00
parent eb5025e8ca
commit 5edbdabf26
27 changed files with 845 additions and 693 deletions

View File

@@ -1,15 +1,13 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:injectable/injectable.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
import 'package:paperless_mobile/features/documents/model/bulk_edit.model.dart'; import 'package:paperless_mobile/features/documents/model/bulk_edit.model.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart'; import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart'; import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/model/paged_search_result.dart'; import 'package:paperless_mobile/features/documents/model/paged_search_result.dart';
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart'; import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:injectable/injectable.dart';
@singleton @singleton
class DocumentsCubit extends Cubit<DocumentsState> { class DocumentsCubit extends Cubit<DocumentsState> {
@@ -17,45 +15,20 @@ class DocumentsCubit extends Cubit<DocumentsState> {
DocumentsCubit(this.documentRepository) : super(DocumentsState.initial); DocumentsCubit(this.documentRepository) : super(DocumentsState.initial);
Future<void> addDocument( Future<void> remove(DocumentModel document) async {
Uint8List bytes,
String fileName, {
required String title,
required void Function(DocumentModel document) onConsumptionFinished,
int? documentType,
int? correspondent,
Iterable<int> tags = const [],
DateTime? createdAt,
}) async {
await documentRepository.create(
bytes,
fileName,
title: title,
documentType: documentType,
correspondent: correspondent,
tags: tags,
createdAt: createdAt,
);
documentRepository
.waitForConsumptionFinished(fileName, title)
.then((value) => onConsumptionFinished(value));
}
Future<void> removeDocument(DocumentModel document) async {
await documentRepository.delete(document); await documentRepository.delete(document);
return await reloadDocuments(); await reload();
} }
Future<void> bulkRemoveDocuments(List<DocumentModel> documents) async { Future<void> bulkRemove(List<DocumentModel> documents) async {
await documentRepository.bulkAction( await documentRepository.bulkAction(
BulkDeleteAction(documents.map((doc) => doc.id)), BulkDeleteAction(documents.map((doc) => doc.id)),
); );
await reloadDocuments(); await reload();
} }
Future<void> bulkEditTags( Future<void> bulkEditTags(
List<DocumentModel> documents, { Iterable<DocumentModel> documents, {
Iterable<int> addTags = const [], Iterable<int> addTags = const [],
Iterable<int> removeTags = const [], Iterable<int> removeTags = const [],
}) async { }) async {
@@ -64,15 +37,15 @@ class DocumentsCubit extends Cubit<DocumentsState> {
addTags: addTags, addTags: addTags,
removeTags: removeTags, removeTags: removeTags,
)); ));
await reloadDocuments(); await reload();
} }
Future<void> updateDocument(DocumentModel document) async { Future<void> update(DocumentModel document) async {
await documentRepository.update(document); await documentRepository.update(document);
await reloadDocuments(); await reload();
} }
Future<void> loadDocuments() async { Future<void> load() async {
final result = await documentRepository.find(state.filter); final result = await documentRepository.find(state.filter);
emit(DocumentsState( emit(DocumentsState(
isLoaded: true, isLoaded: true,
@@ -81,7 +54,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
)); ));
} }
Future<void> reloadDocuments() async { Future<void> reload() async {
if (state.currentPageNumber >= 5) { if (state.currentPageNumber >= 5) {
return _bulkReloadDocuments(); return _bulkReloadDocuments();
} }
@@ -113,7 +86,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
Future<void> assignAsn(DocumentModel document) async { Future<void> assignAsn(DocumentModel document) async {
if (document.archiveSerialNumber == null) { if (document.archiveSerialNumber == null) {
final int asn = await documentRepository.findNextAsn(); final int asn = await documentRepository.findNextAsn();
updateDocument(document.copyWith(archiveSerialNumber: asn)); update(document.copyWith(archiveSerialNumber: asn));
} }
} }
@@ -151,22 +124,6 @@ class DocumentsCubit extends Cubit<DocumentsState> {
} }
} }
///
/// Updates the given document with the inbox tags removed and returns the remoed inbox tags.
///
Future<Iterable<int>> removeInboxTags(
DocumentModel document, final Iterable<int> inboxTags) async {
final tagsToRemove = document.tags.toSet().intersection(inboxTags.toSet());
final updatedTags = {...document.tags}..removeAll(tagsToRemove);
await updateDocument(
document.copyWith(
tags: updatedTags,
overwriteTags: true,
),
);
return tagsToRemove;
}
void resetSelection() { void resetSelection() {
emit(state.copyWith(selection: [])); emit(state.copyWith(selection: []));
} }

View File

@@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.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/model/error_message.dart';
import 'package:paperless_mobile/core/widgets/highlighted_text.dart'; import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
@@ -52,7 +52,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
DateFormat("MMM d, yyyy HH:mm:ss"); DateFormat("MMM d, yyyy HH:mm:ss");
bool _isDownloadPending = false; bool _isDownloadPending = false;
bool _isAssignAsnPending = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -350,7 +349,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
final wasUpdated = await Navigator.push<bool>( final wasUpdated = await Navigator.push<bool>(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => LabelBlocProvider( builder: (_) => GlobalStateBlocProvider(
child: DocumentEditPage(document: document), child: DocumentEditPage(document: document),
), ),
maintainState: true, maintainState: true,
@@ -412,7 +411,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
false; false;
if (delete) { if (delete) {
try { try {
await BlocProvider.of<DocumentsCubit>(context).removeDocument(document); await BlocProvider.of<DocumentsCubit>(context).remove(document);
showSnackBar(context, S.of(context).documentDeleteSuccessMessage); showSnackBar(context, S.of(context).documentDeleteSuccessMessage);
} on ErrorMessage catch (error, stackTrace) { } on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);

View File

@@ -83,7 +83,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
}); });
bool wasUpdated = false; bool wasUpdated = false;
try { try {
await getIt<DocumentsCubit>().updateDocument(updatedDocument); await getIt<DocumentsCubit>().update(updatedDocument);
showSnackBar(context, S.of(context).documentUpdateErrorMessage); showSnackBar(context, S.of(context).documentUpdateErrorMessage);
wasUpdated = true; wasUpdated = true;
} on ErrorMessage catch (error, stackTrace) { } on ErrorMessage catch (error, stackTrace) {

View File

@@ -44,15 +44,13 @@ class _DocumentsPageState extends State<DocumentsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (!BlocProvider.of<DocumentsCubit>(context).state.isLoaded) {
_initDocuments(); _initDocuments();
}
_pagingController.addPageRequestListener(_loadNewPage); _pagingController.addPageRequestListener(_loadNewPage);
} }
Future<void> _initDocuments() async { Future<void> _initDocuments() async {
try { try {
BlocProvider.of<DocumentsCubit>(context).loadDocuments(); BlocProvider.of<DocumentsCubit>(context).load();
} on ErrorMessage catch (error, stackTrace) { } on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);
} }
@@ -113,7 +111,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
previous != ConnectivityState.connected && previous != ConnectivityState.connected &&
current == ConnectivityState.connected, current == ConnectivityState.connected,
listener: (context, state) { listener: (context, state) {
BlocProvider.of<DocumentsCubit>(context).loadDocuments(); BlocProvider.of<DocumentsCubit>(context).load();
}, },
builder: (context, connectivityState) { builder: (context, connectivityState) {
return Scaffold( return Scaffold(
@@ -241,9 +239,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
BlocProvider.value( BlocProvider.value(
value: BlocProvider.of<PaperlessStatisticsCubit>(context)), value: BlocProvider.of<PaperlessStatisticsCubit>(context)),
], ],
child: DocumentDetailsPage( child: DocumentDetailsPage(documentId: model.id),
documentId: model.id,
),
), ),
), ),
); );

View File

@@ -99,7 +99,7 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
if (shouldDelete) { if (shouldDelete) {
try { try {
await BlocProvider.of<DocumentsCubit>(context) await BlocProvider.of<DocumentsCubit>(context)
.bulkRemoveDocuments(documentsState.selection); .bulkRemove(documentsState.selection);
showSnackBar( showSnackBar(
context, context,
S.of(context).documentsPageBulkDeleteSuccessfulText, S.of(context).documentsPageBulkDeleteSuccessfulText,

View File

@@ -1,10 +1,6 @@
import 'dart:developer';
import 'dart:isolate';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.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/connectivity_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
@@ -13,12 +9,12 @@ import 'package:paperless_mobile/core/widgets/offline_banner.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/bloc/saved_view_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/saved_view_cubit.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
import 'package:paperless_mobile/features/home/view/widget/bottom_navigation_bar.dart'; import 'package:paperless_mobile/features/home/view/widget/bottom_navigation_bar.dart';
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart'; import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
import 'package:paperless_mobile/features/inbox/view/inbox_page.dart'; import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart'; import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart'; import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart'; import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
@@ -26,7 +22,6 @@ import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart'; import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart';
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart'; import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
import 'package:paperless_mobile/features/scan/view/scanner_page.dart'; import 'package:paperless_mobile/features/scan/view/scanner_page.dart';
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
import 'package:paperless_mobile/util.dart'; import 'package:paperless_mobile/util.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
@@ -42,30 +37,7 @@ class _HomePageState extends State<HomePage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_initializeData(context).then( _initializeData(context);
(_) async {
FlutterNativeSplash.remove();
if (BlocProvider.of<ApplicationSettingsCubit>(context)
.state
.showInboxOnStartup) {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: getIt<PaperlessStatisticsCubit>(),
child: LabelBlocProvider(
child: BlocProvider.value(
value: DocumentsCubit(getIt<DocumentRepository>()),
child: const InboxPage(),
),
),
),
),
);
getIt<DocumentsCubit>().reloadDocuments();
}
},
);
} }
@override @override
@@ -89,15 +61,22 @@ class _HomePageState extends State<HomePage> {
), ),
drawer: const InfoDrawer(), drawer: const InfoDrawer(),
body: [ body: [
MultiBlocProvider(
providers: [
BlocProvider.value( BlocProvider.value(
value: getIt<DocumentsCubit>(), value: getIt<DocumentsCubit>(),
),
],
child: const DocumentsPage(), child: const DocumentsPage(),
), ),
BlocProvider.value( BlocProvider.value(
value: getIt<DocumentScannerCubit>(), value: getIt<DocumentScannerCubit>(),
child: const ScannerPage(), child: const ScannerPage(),
), ),
const LabelsPage(), BlocProvider.value(
value: getIt<DocumentsCubit>(),
child: const LabelsPage(),
),
][_currentIndex], ][_currentIndex],
); );
}, },
@@ -109,12 +88,12 @@ class _HomePageState extends State<HomePage> {
return Future.wait([ return Future.wait([
BlocProvider.of<PaperlessServerInformationCubit>(context) BlocProvider.of<PaperlessServerInformationCubit>(context)
.updateInformtion(), .updateInformtion(),
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics(), getIt<PaperlessStatisticsCubit>().updateStatistics(),
BlocProvider.of<DocumentTypeCubit>(context).initialize(), getIt<DocumentTypeCubit>().initialize(),
BlocProvider.of<CorrespondentCubit>(context).initialize(), getIt<CorrespondentCubit>().initialize(),
BlocProvider.of<TagCubit>(context).initialize(), getIt<TagCubit>().initialize(),
BlocProvider.of<StoragePathCubit>(context).initialize(), getIt<StoragePathCubit>().initialize(),
BlocProvider.of<SavedViewCubit>(context).initialize(), getIt<SavedViewCubit>().initialize(),
]); ]);
} on ErrorMessage catch (error, stackTrace) { } on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace); showErrorMessage(context, error, stackTrace);

View File

@@ -3,14 +3,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/core/model/paperless_statistics_state.dart'; import 'package:paperless_mobile/core/model/paperless_statistics_state.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/core/model/paperless_server_information.dart'; import 'package:paperless_mobile/core/model/paperless_server_information.dart';
import 'package:paperless_mobile/core/model/paperless_statistics.dart'; import 'package:paperless_mobile/core/model/paperless_statistics.dart';
import 'package:paperless_mobile/core/service/paperless_statistics_service.dart'; import 'package:paperless_mobile/core/service/paperless_statistics_service.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart'; import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:paperless_mobile/features/inbox/view/inbox_page.dart'; import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart'; import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart'; import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
@@ -127,24 +128,7 @@ class InfoDrawer extends StatelessWidget {
trailing: state.isLoaded trailing: state.isLoaded
? Text(state.statistics!.documentsInInbox.toString()) ? Text(state.statistics!.documentsInInbox.toString())
: null, : null,
onTap: () async { onTap: () => _onOpenInbox(context),
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: getIt<PaperlessStatisticsCubit>(),
child: LabelBlocProvider(
child: BlocProvider.value(
value:
DocumentsCubit(getIt<DocumentRepository>()),
child: const InboxPage(),
),
),
),
),
);
getIt<DocumentsCubit>().reloadDocuments();
},
); );
}, },
), ),
@@ -228,6 +212,27 @@ class InfoDrawer extends StatelessWidget {
); );
} }
Future<dynamic> _onOpenInbox(BuildContext context) {
return Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => GlobalStateBlocProvider(
additionalProviders: [
BlocProvider<PaperlessStatisticsCubit>.value(
value: BlocProvider.of<PaperlessStatisticsCubit>(context),
),
BlocProvider<InboxCubit>.value(
value: getIt<InboxCubit>()..initialize(),
),
BlocProvider<DocumentsCubit>.value(
value: getIt<DocumentsCubit>(),
),
],
child: const InboxPage(),
),
),
);
}
Link _buildOnboardingImageCredits() { Link _buildOnboardingImageCredits() {
return Link( return Link(
uri: Uri.parse( uri: Uri.parse(

View File

@@ -0,0 +1,115 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import 'package:paperless_mobile/features/documents/model/bulk_edit.model.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
@injectable
class InboxCubit extends Cubit<InboxState> {
final LabelRepository _labelRepository;
final DocumentRepository _documentRepository;
InboxCubit(this._labelRepository, this._documentRepository)
: super(const InboxState());
///
/// Fetches inbox tag ids and loads the inbox items (documents).
///
Future<void> initialize() async {
final inboxTags = await _labelRepository.getTags().then(
(value) => value.where((t) => t.isInboxTag ?? false).map((t) => t.id!));
final inboxDocuments = await _documentRepository
.find(DocumentFilter(
tags: AnyAssignedTagsQuery(tagIds: inboxTags),
sortField: SortField.added,
))
.then((psr) => psr.results);
final newState = InboxState(
isLoaded: true,
inboxItems: inboxDocuments,
inboxTags: inboxTags,
);
emit(newState);
}
Future<void> reloadInbox() async {
if (!state.isLoaded) {
throw "State has not yet loaded. Ensure the state is loaded when calling this method!";
}
final inboxDocuments = await _documentRepository
.find(DocumentFilter(
tags: AnyAssignedTagsQuery(tagIds: state.inboxTags),
sortField: SortField.added,
))
.then((psr) => psr.results);
emit(InboxState(
isLoaded: true,
inboxItems: inboxDocuments,
inboxTags: state.inboxTags,
));
}
///
/// Updates the document with all inbox tags removed and removes the document
/// from the currently loaded inbox documents.
///
Future<Iterable<int>> remove(DocumentModel document) async {
if (!state.isLoaded) {
throw "State has not yet loaded. Ensure the state is loaded when calling this method!";
}
final tagsToRemove =
document.tags.toSet().intersection(state.inboxTags.toSet());
final updatedTags = {...document.tags}..removeAll(tagsToRemove);
await _documentRepository.update(
document.copyWith(
tags: updatedTags,
overwriteTags: true,
),
);
emit(
InboxState(
isLoaded: true,
inboxTags: state.inboxTags,
inboxItems: state.inboxItems.where((doc) => doc.id != document.id),
),
);
return tagsToRemove;
}
Future<void> undoRemove(
DocumentModel document, Iterable<int> removedTags) async {
final updatedDoc = document.copyWith(
tags: {...document.tags, ...removedTags},
overwriteTags: true,
);
await _documentRepository.update(updatedDoc);
emit(InboxState(
isLoaded: true,
inboxItems: [...state.inboxItems, updatedDoc]
..sort((d1, d2) => d1.added.compareTo(d2.added)),
inboxTags: state.inboxTags,
));
}
///
/// Removes inbox tags from all documents in the inbox.
///
Future<void> clearInbox() async {
await _documentRepository.bulkAction(BulkModifyTagsAction.removeTags(
state.inboxItems.map((e) => e.id), state.inboxTags));
emit(
InboxState(
isLoaded: true,
inboxTags: state.inboxTags,
),
);
}
}

View File

@@ -0,0 +1,17 @@
import 'package:equatable/equatable.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart';
class InboxState with EquatableMixin {
final bool isLoaded;
final Iterable<int> inboxTags;
final Iterable<DocumentModel> inboxItems;
const InboxState({
this.isLoaded = false,
this.inboxTags = const [],
this.inboxItems = const [],
});
@override
List<Object?> get props => [isLoaded, inboxTags, inboxItems];
}

View File

@@ -1,263 +0,0 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/core/model/paperless_statistics_state.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.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';
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
import 'package:paperless_mobile/features/documents/view/pages/document_details_page.dart';
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart';
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/util.dart';
class InboxPage extends StatefulWidget {
const InboxPage({super.key});
@override
State<InboxPage> createState() => _InboxPageState();
}
class _InboxPageState extends State<InboxPage> {
static const _a4AspectRatio = 1 / 1.4142;
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
Iterable<int> _inboxTags = [];
@override
void initState() {
super.initState();
initializeDateFormatting();
_initInbox();
}
Future<void> _initInbox() async {
final tags = BlocProvider.of<TagCubit>(context).state.labels;
log("Loading documents with tags...${tags.values.join(",")}");
_inboxTags =
tags.values.where((t) => t.isInboxTag ?? false).map((t) => t.id!);
final filter =
DocumentFilter(tags: AnyAssignedTagsQuery(tagIds: _inboxTags));
return BlocProvider.of<DocumentsCubit>(context).updateFilter(
filter: filter,
);
}
@override
Widget build(BuildContext context) {
return BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, documentState) {
return Scaffold(
appBar: AppBar(
title:
BlocBuilder<PaperlessStatisticsCubit, PaperlessStatisticsState>(
builder: (context, state) {
return Text(
S.of(context).bottomNavInboxPageLabel +
(state.isLoaded
? ' (${state.statistics!.documentsInInbox})'
: ''),
);
},
),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
),
floatingActionButton: documentState.documents.isNotEmpty
? FloatingActionButton.extended(
label: Text("Mark all as seen"),
icon: const Icon(Icons.done_all),
onPressed: () =>
_onMarkAllAsSeen(documentState.documents, _inboxTags),
)
: null,
body: Builder(
builder: (context) {
if (!documentState.isLoaded) {
return const Center(child: CircularProgressIndicator());
}
if (documentState.documents.isEmpty) {
return Text(
"You do not have new documents in your inbox.",
textAlign: TextAlign.center,
) // TODO: INTL
.padded();
}
return Column(
children: [
Text(
'Hint: Swipe left to mark a document as read. This will remove all inbox tags from the document.', //TODO: INTL
style: Theme.of(context).textTheme.caption,
).padded(
const EdgeInsets.only(
top: 4.0,
left: 8.0,
right: 8.0,
bottom: 8.0,
),
),
Expanded(
child: AnimatedList(
key: _listKey,
initialItemCount: documentState.documents.length,
itemBuilder: (context, index, animation) {
final doc = documentState.documents[index];
return _buildListItem(context, doc);
},
),
),
],
);
},
),
);
},
);
}
Widget _buildListItem(BuildContext context, DocumentModel doc) {
return Dismissible(
direction: DismissDirection.endToStart,
background: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.done,
color: Theme.of(context).colorScheme.primary,
).padded(),
Text(
'Mark as read', //TODO: INTL
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
],
).padded(),
confirmDismiss: (_) => _onItemDismissed(doc),
key: ObjectKey(doc.id),
child: ListTile(
title: Text(doc.title),
isThreeLine: true,
leading: AspectRatio(
aspectRatio: _a4AspectRatio,
child: DocumentPreview(
id: doc.id,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(DateFormat().format(doc.added)),
TagsWidget(tagIds: doc.tags.where((id) => _inboxTags.contains(id)))
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => LabelBlocProvider(
child: BlocProvider.value(
value: BlocProvider.of<DocumentsCubit>(context),
child: DocumentDetailsPage(
documentId: doc.id,
allowEdit: false,
isLabelClickable: false,
),
),
),
),
),
),
);
}
Widget _buildSlideAnimation(
BuildContext context,
animation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
Future<void> _onMarkAllAsSeen(
List<DocumentModel> documents,
Iterable<int> inboxTags,
) async {
for (int i = documents.length - 1; i >= 0; i--) {
final doc = documents[i];
_listKey.currentState?.removeItem(
0,
(context, animation) => _buildSlideAnimation(
context,
animation,
_buildListItem(context, doc),
),
);
await Future.delayed(const Duration(milliseconds: 75));
}
await BlocProvider.of<DocumentsCubit>(context)
.bulkEditTags(documents, removeTags: inboxTags);
BlocProvider.of<PaperlessStatisticsCubit>(context).resetInboxCount();
}
Future<bool> _onItemDismissed(DocumentModel doc) async {
try {
final removedTags = await BlocProvider.of<DocumentsCubit>(context)
.removeInboxTags(doc, _inboxTags);
BlocProvider.of<PaperlessStatisticsCubit>(context).decrementInboxCount();
showSnackBar(
context,
'Document removed from inbox.', //TODO: INTL
action: SnackBarAction(
label: 'UNDO', //TODO: INTL
textColor: Theme.of(context).colorScheme.primary,
onPressed: () => _onUndoMarkAsSeen(doc, removedTags),
),
);
return true;
} on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
return false;
} catch (error) {
showErrorMessage(
context,
const ErrorMessage.unknown(),
);
return false;
}
}
Future<void> _onUndoMarkAsSeen(
DocumentModel doc, Iterable<int> removedTags) async {
try {
await BlocProvider.of<DocumentsCubit>(context).updateDocument(
doc.copyWith(
tags: {...doc.tags, ...removedTags},
overwriteTags: true,
),
);
BlocProvider.of<PaperlessStatisticsCubit>(context).incrementInboxCount();
BlocProvider.of<DocumentsCubit>(context).reloadDocuments();
} on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
}

View File

@@ -0,0 +1,202 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
import 'package:paperless_mobile/features/inbox/view/widgets/document_inbox_item.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/util.dart';
class InboxPage extends StatefulWidget {
const InboxPage({super.key});
@override
State<InboxPage> createState() => _InboxPageState();
}
class _InboxPageState extends State<InboxPage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
@override
void initState() {
super.initState();
initializeDateFormatting();
}
@override
Widget build(BuildContext context) {
final bloc = BlocProvider.of<InboxCubit>(context);
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).bottomNavInboxPageLabel),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
),
floatingActionButton: BlocBuilder<InboxCubit, InboxState>(
builder: (context, state) {
return FloatingActionButton.extended(
label: Text("Mark all as seen"),
icon: const Icon(Icons.done_all),
onPressed: state.isLoaded && state.inboxItems.isNotEmpty
? () => _onMarkAllAsSeen(
bloc.state.inboxItems,
bloc.state.inboxTags,
)
: null,
);
},
),
body: BlocBuilder<InboxCubit, InboxState>(
builder: (context, state) {
if (!state.isLoaded) {
return const DocumentsListLoadingWidget();
}
if (state.inboxItems.isEmpty) {
return Text(
"You do not have new documents in your inbox.",
textAlign: TextAlign.center,
).padded();
}
return RefreshIndicator(
onRefresh: () => BlocProvider.of<InboxCubit>(context).reloadInbox(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Hint: Swipe left to mark a document as seen. This will remove all inbox tags from the document.', //TODO: INTL
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.caption,
).padded(
const EdgeInsets.only(
top: 4.0,
left: 8.0,
right: 8.0,
bottom: 8.0,
),
),
Expanded(
child: AnimatedList(
key: _listKey,
initialItemCount: state.inboxItems.length,
itemBuilder: (context, index, animation) {
final doc = state.inboxItems.elementAt(index);
return _buildListItem(context, doc);
},
),
),
],
),
);
},
),
);
}
Widget _buildListItem(BuildContext context, DocumentModel doc) {
return Dismissible(
direction: DismissDirection.endToStart,
background: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.done,
color: Theme.of(context).colorScheme.primary,
).padded(),
Text(
'Mark as read', //TODO: INTL
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
],
).padded(),
confirmDismiss: (_) => _onItemDismissed(doc),
key: ObjectKey(doc.id),
child: DocumentInboxItem(document: doc),
);
}
Widget _buildSlideAnimation(
BuildContext context,
animation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(animation),
child: child,
);
}
Future<void> _onMarkAllAsSeen(
Iterable<DocumentModel> documents,
Iterable<int> inboxTags,
) async {
for (int i = documents.length - 1; i >= 0; i--) {
final doc = documents.elementAt(i);
_listKey.currentState?.removeItem(
0,
(context, animation) => _buildSlideAnimation(
context,
animation,
_buildListItem(context, doc),
),
);
await Future.delayed(const Duration(milliseconds: 75));
}
await BlocProvider.of<DocumentsCubit>(context)
.bulkEditTags(documents, removeTags: inboxTags);
BlocProvider.of<PaperlessStatisticsCubit>(context).resetInboxCount();
}
Future<bool> _onItemDismissed(DocumentModel doc) async {
try {
final removedTags =
await BlocProvider.of<InboxCubit>(context).remove(doc);
BlocProvider.of<PaperlessStatisticsCubit>(context).decrementInboxCount();
showSnackBar(
context,
'Document removed from inbox.', //TODO: INTL
action: SnackBarAction(
label: 'UNDO', //TODO: INTL
textColor: Theme.of(context).colorScheme.primary,
onPressed: () => _onUndoMarkAsSeen(doc, removedTags),
),
);
return true;
} on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
return false;
} catch (error) {
showErrorMessage(
context,
const ErrorMessage.unknown(),
);
return false;
}
}
Future<void> _onUndoMarkAsSeen(
DocumentModel document,
Iterable<int> removedTags,
) async {
try {
await BlocProvider.of<InboxCubit>(context)
.undoRemove(document, removedTags);
BlocProvider.of<PaperlessStatisticsCubit>(context).incrementInboxCount();
} on ErrorMessage catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/documents/view/pages/document_details_page.dart';
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
class DocumentInboxItem extends StatelessWidget {
final DocumentModel document;
const DocumentInboxItem({
super.key,
required this.document,
});
static const _a4AspectRatio = 1 / 1.4142;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(document.title),
isThreeLine: true,
leading: AspectRatio(
aspectRatio: _a4AspectRatio,
child: DocumentPreview(
id: document.id,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(DateFormat().format(document.added)),
TagsWidget(
tagIds: document.tags,
isMultiLine: false,
isClickable: false,
),
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => GlobalStateBlocProvider(
additionalProviders: [
BlocProvider.value(
value: BlocProvider.of<DocumentsCubit>(context)),
],
child: DocumentDetailsPage(
documentId: document.id,
allowEdit: false,
isLabelClickable: false,
),
),
),
),
);
}
}

View File

@@ -7,9 +7,14 @@ import 'package:paperless_mobile/features/labels/document_type/bloc/document_typ
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart'; import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart'; import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
class LabelBlocProvider extends StatelessWidget { class GlobalStateBlocProvider extends StatelessWidget {
final List<BlocProvider> additionalProviders;
final Widget child; final Widget child;
const LabelBlocProvider({super.key, required this.child}); const GlobalStateBlocProvider({
super.key,
this.additionalProviders = const [],
required this.child,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -20,6 +25,7 @@ class LabelBlocProvider extends StatelessWidget {
BlocProvider.value(value: getIt<TagCubit>()), BlocProvider.value(value: getIt<TagCubit>()),
BlocProvider.value(value: getIt<StoragePathCubit>()), BlocProvider.value(value: getIt<StoragePathCubit>()),
BlocProvider.value(value: getIt<SavedViewCubit>()), BlocProvider.value(value: getIt<SavedViewCubit>()),
...additionalProviders,
], ],
child: child, child: child,
); );

View File

@@ -24,7 +24,7 @@ class EditTagPage extends StatelessWidget {
label: tag, label: tag,
onSubmit: (tag) async { onSubmit: (tag) async {
await BlocProvider.of<TagCubit>(context).replace(tag); await BlocProvider.of<TagCubit>(context).replace(tag);
//If inbox property was added/removed from tag, the number of documetns in inbox may increase/decrease. //If inbox property was added/removed from tag, the number of documents in inbox may increase/decrease.
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics(); BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics();
}, },
onDelete: (tag) => _onDelete(tag, context), onDelete: (tag) => _onDelete(tag, context),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart'; import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart'; import 'package:paperless_mobile/features/documents/model/document_filter.dart';
@@ -54,9 +54,7 @@ class _LabelsPageState extends State<LabelsPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider.value( return DefaultTabController(
value: getIt<DocumentsCubit>(),
child: DefaultTabController(
length: 3, length: 3,
child: Scaffold( child: Scaffold(
drawer: const InfoDrawer(), drawer: const InfoDrawer(),
@@ -138,9 +136,8 @@ class _LabelsPageState extends State<LabelsPage>
onOpenEditPage: _openEditDocumentTypePage, onOpenEditPage: _openEditDocumentTypePage,
emptyStateActionButtonLabel: emptyStateActionButtonLabel:
S.of(context).labelsPageDocumentTypeEmptyStateAddNewLabel, S.of(context).labelsPageDocumentTypeEmptyStateAddNewLabel,
emptyStateDescription: S emptyStateDescription:
.of(context) S.of(context).labelsPageDocumentTypeEmptyStateDescriptionText,
.labelsPageDocumentTypeEmptyStateDescriptionText,
onOpenAddNewPage: _onAddPressed, onOpenAddNewPage: _onAddPressed,
), ),
LabelTabView<Tag>( LabelTabView<Tag>(
@@ -176,15 +173,13 @@ class _LabelsPageState extends State<LabelsPage>
contentBuilder: (path) => Text(path.path ?? ""), contentBuilder: (path) => Text(path.path ?? ""),
emptyStateActionButtonLabel: emptyStateActionButtonLabel:
S.of(context).labelsPageStoragePathEmptyStateAddNewLabel, S.of(context).labelsPageStoragePathEmptyStateAddNewLabel,
emptyStateDescription: S emptyStateDescription:
.of(context) S.of(context).labelsPageStoragePathEmptyStateDescriptionText,
.labelsPageStoragePathEmptyStateDescriptionText,
onOpenAddNewPage: _onAddPressed, onOpenAddNewPage: _onAddPressed,
), ),
], ],
), ),
), ),
),
); );
} }
@@ -192,11 +187,9 @@ class _LabelsPageState extends State<LabelsPage>
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => MultiBlocProvider( builder: (_) => GlobalStateBlocProvider(
providers: [ additionalProviders: [
BlocProvider.value(value: getIt<DocumentsCubit>()), BlocProvider.value(value: BlocProvider.of<DocumentsCubit>(context)),
BlocProvider.value(
value: BlocProvider.of<CorrespondentCubit>(context)),
], ],
child: EditCorrespondentPage(correspondent: correspondent), child: EditCorrespondentPage(correspondent: correspondent),
), ),
@@ -208,11 +201,9 @@ class _LabelsPageState extends State<LabelsPage>
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => MultiBlocProvider( builder: (_) => GlobalStateBlocProvider(
providers: [ additionalProviders: [
BlocProvider.value(value: getIt<DocumentsCubit>()), BlocProvider.value(value: BlocProvider.of<DocumentsCubit>(context)),
BlocProvider.value(
value: BlocProvider.of<DocumentTypeCubit>(context)),
], ],
child: EditDocumentTypePage(documentType: docType), child: EditDocumentTypePage(documentType: docType),
), ),
@@ -224,10 +215,9 @@ class _LabelsPageState extends State<LabelsPage>
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => MultiBlocProvider( builder: (_) => GlobalStateBlocProvider(
providers: [ additionalProviders: [
BlocProvider.value(value: getIt<DocumentsCubit>()), BlocProvider.value(value: BlocProvider.of<DocumentsCubit>(context)),
BlocProvider.value(value: BlocProvider.of<TagCubit>(context)),
BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()), BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()),
], ],
child: EditTagPage(tag: tag), child: EditTagPage(tag: tag),
@@ -240,11 +230,9 @@ class _LabelsPageState extends State<LabelsPage>
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => MultiBlocProvider( builder: (_) => GlobalStateBlocProvider(
providers: [ additionalProviders: [
BlocProvider.value(value: getIt<DocumentsCubit>()), BlocProvider.value(value: getIt<DocumentsCubit>()),
BlocProvider.value(
value: BlocProvider.of<StoragePathCubit>(context)),
], ],
child: EditStoragePathPage(storagePath: path), child: EditStoragePathPage(storagePath: path),
), ),
@@ -269,7 +257,7 @@ class _LabelsPageState extends State<LabelsPage>
case 3: case 3:
page = const AddStoragePathPage(); page = const AddStoragePathPage();
} }
return LabelBlocProvider(child: page); return GlobalStateBlocProvider(child: page);
}, },
)); ));
} }

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.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/model/error_message.dart';
import 'package:paperless_mobile/di_initializer.dart'; import 'package:paperless_mobile/di_initializer.dart';
@@ -8,7 +8,8 @@ import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart'; import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart'; import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:paperless_mobile/features/labels/model/label.model.dart'; import 'package:paperless_mobile/features/labels/model/label.model.dart';
import 'package:paperless_mobile/features/labels/view/widgets/linked_documents_preview.dart'; import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents_preview/view/pages/linked_documents_page.dart';
class LabelItem<T extends Label> extends StatelessWidget { class LabelItem<T extends Label> extends StatelessWidget {
final T label; final T label;
@@ -50,13 +51,13 @@ class LabelItem<T extends Label> extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LabelBlocProvider( builder: (context) => GlobalStateBlocProvider(
child: BlocProvider( additionalProviders: [
create: (context) => BlocProvider.value(
DocumentsCubit(getIt<DocumentRepository>()) value: getIt<LinkedDocumentsCubit>()
..updateFilter(filter: filter), ..initialize(filter)),
child: LinkedDocumentsPreview(filter: filter), ],
), child: const LinkedDocumentsPage(),
), ),
), ),
); );

View File

@@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.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';
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/view/pages/document_details_page.dart';
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
class LinkedDocumentsPreview extends StatefulWidget {
final DocumentFilter filter;
const LinkedDocumentsPreview({super.key, required this.filter});
@override
State<LinkedDocumentsPreview> createState() => _LinkedDocumentsPreviewState();
}
class _LinkedDocumentsPreviewState extends State<LinkedDocumentsPreview> {
final _pagingController =
PagingController<int, DocumentModel>(firstPageKey: 1);
@override
void initState() {
super.initState();
_pagingController.nextPageKey = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).linkedDocumentsPageTitle),
),
body: BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, state) {
_pagingController.itemList = state.documents;
return Column(
children: [
Text(
S.of(context).referencedDocumentsReadOnlyHintText,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.caption,
),
Expanded(
child: CustomScrollView(
slivers: [
DocumentListView(
isLabelClickable: false,
onTap: (doc) {
Navigator.push(
context,
MaterialPageRoute(
builder: (ctxt) => LabelBlocProvider(
child: BlocProvider.value(
value: BlocProvider.of<DocumentsCubit>(context),
child: DocumentDetailsPage(
documentId: doc.id,
allowEdit: false,
isLabelClickable: false,
),
),
),
),
);
},
pagingController: _pagingController,
state: state,
onSelected: BlocProvider.of<DocumentsCubit>(context)
.toggleDocumentSelection,
hasInternetConnection: true,
),
],
),
),
],
);
},
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
@injectable
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
final DocumentRepository _documentRepository;
LinkedDocumentsCubit(this._documentRepository)
: super(LinkedDocumentsState());
Future<void> initialize(DocumentFilter filter) async {
final documents = await _documentRepository.find(
filter.copyWith(
pageSize: 100,
),
);
emit(LinkedDocumentsState(
isLoaded: true,
documents: documents,
filter: filter,
));
}
}

View File

@@ -0,0 +1,15 @@
import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
import 'package:paperless_mobile/features/documents/model/paged_search_result.dart';
class LinkedDocumentsState {
final bool isLoaded;
final PagedSearchResult<DocumentModel>? documents;
final DocumentFilter? filter;
LinkedDocumentsState({
this.filter,
this.isLoaded = false,
this.documents,
});
}

View File

@@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.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';
import 'package:paperless_mobile/features/documents/view/pages/document_details_page.dart';
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
import 'package:paperless_mobile/generated/l10n.dart';
class LinkedDocumentsPage extends StatefulWidget {
const LinkedDocumentsPage({super.key});
@override
State<LinkedDocumentsPage> createState() => _LinkedDocumentsPageState();
}
class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
final _pagingController =
PagingController<int, DocumentModel>(firstPageKey: 1);
@override
void initState() {
super.initState();
_pagingController.nextPageKey = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).linkedDocumentsPageTitle),
),
body: BlocBuilder<LinkedDocumentsCubit, LinkedDocumentsState>(
builder: (context, state) {
if (!state.isLoaded) {
return const DocumentsListLoadingWidget();
}
_pagingController.itemList = state.documents!.results;
return Column(
children: [
Text(
S.of(context).referencedDocumentsReadOnlyHintText,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.caption,
),
Expanded(
child: CustomScrollView(
slivers: [
PagedSliverList<int, DocumentModel>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate(
animateTransitions: true,
itemBuilder: (context, document, index) {
return DocumentListItem(
isLabelClickable: false,
document: document,
onTap: (doc) {
Navigator.push(
context,
MaterialPageRoute(
builder: (ctxt) => GlobalStateBlocProvider(
additionalProviders: [
BlocProvider.value(
value: BlocProvider.of<DocumentsCubit>(
context,
),
),
],
child: DocumentDetailsPage(
documentId: doc.id,
allowEdit: false,
isLabelClickable: false,
),
),
),
);
},
isSelected: false,
isAtLeastOneSelected: false,
);
},
),
),
],
),
),
],
);
},
),
);
}
}

View File

@@ -6,12 +6,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:paperless_mobile/features/documents/model/document.model.dart';
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
@singleton @injectable
class DocumentScannerCubit extends Cubit<List<File>> { class DocumentScannerCubit extends Cubit<List<File>> {
final DocumentRepository documentRepository;
static List<File> initialState = []; static List<File> initialState = [];
DocumentScannerCubit() : super(initialState); DocumentScannerCubit(this.documentRepository) : super(initialState);
void addScan(File file) => emit([...state, file]); void addScan(File file) => emit([...state, file]);
@@ -40,4 +44,30 @@ class DocumentScannerCubit extends Cubit<List<File>> {
throw const ErrorMessage(ErrorCode.scanRemoveFailed); throw const ErrorMessage(ErrorCode.scanRemoveFailed);
} }
} }
Future<void> uploadDocument(
Uint8List bytes,
String fileName, {
required String title,
required void Function(DocumentModel document)? onConsumptionFinished,
int? documentType,
int? correspondent,
Iterable<int> tags = const [],
DateTime? createdAt,
}) async {
await documentRepository.create(
bytes,
fileName,
title: title,
documentType: documentType,
correspondent: correspondent,
tags: tags,
createdAt: createdAt,
);
if (onConsumptionFinished != null) {
documentRepository
.waitForConsumptionFinished(fileName, title)
.then((value) => onConsumptionFinished(value));
}
}
} }

View File

@@ -35,6 +35,7 @@ class DocumentUploadPage extends StatefulWidget {
final String? title; final String? title;
final String? filename; final String? filename;
final void Function()? afterUpload; final void Function()? afterUpload;
final void Function(DocumentModel)? onSuccessfullyConsumed;
const DocumentUploadPage({ const DocumentUploadPage({
Key? key, Key? key,
@@ -42,6 +43,7 @@ class DocumentUploadPage extends StatefulWidget {
this.afterUpload, this.afterUpload,
this.title, this.title,
this.filename, this.filename,
this.onSuccessfullyConsumed,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -229,6 +231,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
void _onSubmit() async { void _onSubmit() async {
if (_formKey.currentState?.saveAndValidate() ?? false) { if (_formKey.currentState?.saveAndValidate() ?? false) {
final cubit = BlocProvider.of<DocumentScannerCubit>(context);
try { try {
setState(() => _isUploadLoading = true); setState(() => _isUploadLoading = true);
@@ -240,17 +243,19 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery; final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery;
final correspondent = final correspondent =
fv[DocumentModel.correspondentKey] as IdQueryParameter; fv[DocumentModel.correspondentKey] as IdQueryParameter;
await BlocProvider.of<DocumentsCubit>(context).addDocument(
await cubit.uploadDocument(
widget.fileBytes, widget.fileBytes,
_padWithPdfExtension(_formKey.currentState?.value[fkFileName]), _padWithPdfExtension(_formKey.currentState?.value[fkFileName]),
onConsumptionFinished: _onConsumptionFinished, onConsumptionFinished: widget.onSuccessfullyConsumed,
title: title, title: title,
documentType: docType.id, documentType: docType.id,
correspondent: correspondent.id, correspondent: correspondent.id,
tags: tags.ids, tags: tags.ids,
createdAt: createdAt, createdAt: createdAt,
); );
getIt<DocumentScannerCubit>().reset(); //TODO: Access via provider
cubit.reset(); //TODO: Access via provider
showSnackBar(context, S.of(context).documentUploadSuccessText); showSnackBar(context, S.of(context).documentUploadSuccessText);
Navigator.pop(context); Navigator.pop(context);
widget.afterUpload?.call(); widget.afterUpload?.call();
@@ -275,24 +280,4 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
String _formatFilename(String source) { String _formatFilename(String source) {
return source.replaceAll(RegExp(r"[\W_]"), "_"); return source.replaceAll(RegExp(r"[\W_]"), "_");
} }
void _onConsumptionFinished(DocumentModel document) {
// ScaffoldMessenger.of(rootScaffoldKey.currentContext!).showSnackBar(
// SnackBar(
// action: SnackBarAction(
// onPressed: () async {
// try {
// getIt<DocumentsCubit>().reloadDocuments();
// } on ErrorMessage catch (error, stackTrace) {
// showErrorMessage(context, error, stackTrace);
// }
// },
// label:
// S.of(context).documentUploadProcessingSuccessfulReloadActionText,
// ),
// content: Text(S.of(context).documentUploadProcessingSuccessfulText),
// ),
// );
getIt<PaperlessStatisticsCubit>().incrementInboxCount();
}
} }

View File

@@ -8,7 +8,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/constants.dart';
import 'package:paperless_mobile/core/model/error_message.dart'; import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/core/service/file_service.dart';
@@ -127,12 +128,17 @@ class _ScannerPageState extends State<ScannerPage>
final bytes = await doc.save(); final bytes = await doc.save();
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BlocProvider.value( builder: (_) => GlobalStateBlocProvider(
value: getIt<DocumentsCubit>(), additionalProviders: [
child: LabelBlocProvider( BlocProvider<DocumentScannerCubit>.value(
value: BlocProvider.of<DocumentScannerCubit>(context),
),
],
child: DocumentUploadPage( child: DocumentUploadPage(
fileBytes: bytes, fileBytes: bytes,
), onSuccessfullyConsumed: (_) =>
BlocProvider.of<PaperlessStatisticsCubit>(context)
.updateStatistics(),
), ),
), ),
), ),
@@ -242,17 +248,20 @@ class _ScannerPageState extends State<ScannerPage>
// pdf // pdf
fileBytes = file.readAsBytesSync(); fileBytes = file.readAsBytesSync();
} }
Navigator.of(context).push(
Navigator.push(
context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BlocProvider.value( builder: (_) => GlobalStateBlocProvider(
value: getIt<DocumentsCubit>(), additionalProviders: [
child: LabelBlocProvider( BlocProvider<DocumentScannerCubit>.value(
value: BlocProvider.of<DocumentScannerCubit>(context),
),
],
child: DocumentUploadPage( child: DocumentUploadPage(
filename: filename, filename: filename,
fileBytes: fileBytes, fileBytes: fileBytes,
), onSuccessfullyConsumed: (_) =>
BlocProvider.of<PaperlessStatisticsCubit>(context)
.updateStatistics(),
), ),
), ),
), ),

View File

@@ -183,7 +183,7 @@
"labelsPageTagsEmptyStateDescriptionText": "Es wurden noch keine Tags angelegt.", "labelsPageTagsEmptyStateDescriptionText": "Es wurden noch keine Tags angelegt.",
"labelsPageStoragePathEmptyStateAddNewLabel": "Erstelle neuen Speicherpfad", "labelsPageStoragePathEmptyStateAddNewLabel": "Erstelle neuen Speicherpfad",
"labelsPageStoragePathEmptyStateDescriptionText": "Es wurden noch keine Speicherpfade angelegt.", "labelsPageStoragePathEmptyStateDescriptionText": "Es wurden noch keine Speicherpfade angelegt.",
"referencedDocumentsReadOnlyHintText": "Dies ist eine schreibgeschützte Ansicht! Dokumente können nicht bearbeitet oder entfernt werden.", "referencedDocumentsReadOnlyHintText": "Dies ist eine schreibgeschützte Ansicht! Dokumente können nicht bearbeitet oder entfernt werden. Es werden maximal 100 referenzierte Dokumente geladen.",
"editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen", "editLabelPageConfirmDeletionDialogTitle": "Löschen bestätigen",
"editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?", "editLabelPageDeletionDialogText": "Dieser Kennzeichner wird von Dokumenten referenziert. Durch das Löschen dieses Kennzeichners werden alle Referenzen entfernt. Fortfahren?",
"settingsPageStorageSettingsLabel": "Speicher", "settingsPageStorageSettingsLabel": "Speicher",

View File

@@ -184,7 +184,7 @@
"labelsPageTagsEmptyStateDescriptionText": "You don't seem to have any tags set up.", "labelsPageTagsEmptyStateDescriptionText": "You don't seem to have any tags set up.",
"labelsPageStoragePathEmptyStateAddNewLabel": "Add new storage path", "labelsPageStoragePathEmptyStateAddNewLabel": "Add new storage path",
"labelsPageStoragePathEmptyStateDescriptionText": "You don't seem to have any storage paths set up.", "labelsPageStoragePathEmptyStateDescriptionText": "You don't seem to have any storage paths set up.",
"referencedDocumentsReadOnlyHintText": "This is a read-only view! You cannot edit or remove documents.", "referencedDocumentsReadOnlyHintText": "This is a read-only view! You cannot edit or remove documents. A maximum of 100 referenced documents will be loaded.",
"editLabelPageConfirmDeletionDialogTitle": "Confirm deletion", "editLabelPageConfirmDeletionDialogTitle": "Confirm deletion",
"editLabelPageDeletionDialogText": "This label contains references to other documents. By deleting this label, all references will be removed. Continue?", "editLabelPageDeletionDialogText": "This label contains references to other documents. By deleting this label, all references will be removed. Continue?",
"settingsPageStorageSettingsLabel": "Storage", "settingsPageStorageSettingsLabel": "Storage",

View File

@@ -12,7 +12,7 @@ import 'package:intl/intl_standalone.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.dart'; import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart'; import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
import 'package:paperless_mobile/core/global/asset_images.dart'; import 'package:paperless_mobile/core/global/asset_images.dart';
import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/constants.dart';
@@ -26,6 +26,7 @@ import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
import 'package:paperless_mobile/features/home/view/home_page.dart'; import 'package:paperless_mobile/features/home/view/home_page.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart'; import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/view/login_page.dart'; import 'package:paperless_mobile/features/login/view/login_page.dart';
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/document_upload_page.dart';
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart'; import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart'; import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
@@ -50,17 +51,18 @@ void main() async {
await getIt<ApplicationSettingsCubit>().initialize(); await getIt<ApplicationSettingsCubit>().initialize();
await getIt<AuthenticationCubit>().initialize(); await getIt<AuthenticationCubit>().initialize();
runApp(const MyApp()); runApp(const PaperlessMobileEntrypoint());
} }
class MyApp extends StatefulWidget { class PaperlessMobileEntrypoint extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const PaperlessMobileEntrypoint({Key? key}) : super(key: key);
@override @override
State<MyApp> createState() => _MyAppState(); State<PaperlessMobileEntrypoint> createState() =>
_PaperlessMobileEntrypointState();
} }
class _MyAppState extends State<MyApp> { class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
@@ -68,6 +70,7 @@ class _MyAppState extends State<MyApp> {
BlocProvider.value(value: getIt<ConnectivityCubit>()), BlocProvider.value(value: getIt<ConnectivityCubit>()),
BlocProvider.value(value: getIt<AuthenticationCubit>()), BlocProvider.value(value: getIt<AuthenticationCubit>()),
BlocProvider.value(value: getIt<PaperlessServerInformationCubit>()), BlocProvider.value(value: getIt<PaperlessServerInformationCubit>()),
BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()),
BlocProvider.value(value: getIt<ApplicationSettingsCubit>()), BlocProvider.value(value: getIt<ApplicationSettingsCubit>()),
], ],
child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>( child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
@@ -172,20 +175,26 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BlocProvider.value( builder: (context) => GlobalStateBlocProvider(
value: getIt<DocumentsCubit>(), additionalProviders: [
child: LabelBlocProvider( BlocProvider.value(value: getIt<DocumentScannerCubit>()),
],
child: DocumentUploadPage( child: DocumentUploadPage(
fileBytes: bytes, fileBytes: bytes,
afterUpload: () => SystemNavigator.pop(), afterUpload: SystemNavigator.pop,
filename: filename, filename: filename,
), ),
), ),
), ),
),
); );
} }
@override
void didChangeDependencies() {
FlutterNativeSplash.remove();
super.didChangeDependencies();
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -218,14 +227,14 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
}, },
builder: (context, authentication) { builder: (context, authentication) {
if (authentication.isAuthenticated) { if (authentication.isAuthenticated) {
return BlocProvider.value( return GlobalStateBlocProvider(
value: getIt<PaperlessStatisticsCubit>(), additionalProviders: [
child: const LabelBlocProvider( BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()),
child: HomePage(), BlocProvider.value(value: getIt<DocumentsCubit>()),
), ],
child: const HomePage(),
); );
} else { } else {
FlutterNativeSplash.remove();
return const LoginPage(); return const LoginPage();
} }
}, },

View File

@@ -54,7 +54,7 @@ void main() async {
), ),
build: () => DocumentsCubit(documentRepository), build: () => DocumentsCubit(documentRepository),
seed: () => DocumentsState.initial, seed: () => DocumentsState.initial,
act: (bloc) => bloc.loadDocuments(), act: (bloc) => bloc.load(),
expect: () => [ expect: () => [
DocumentsState( DocumentsState(
isLoaded: true, isLoaded: true,
@@ -83,7 +83,7 @@ void main() async {
), ),
build: () => DocumentsCubit(documentRepository), build: () => DocumentsCubit(documentRepository),
seed: () => DocumentsState.initial, seed: () => DocumentsState.initial,
act: (bloc) => bloc.loadDocuments(), act: (bloc) => bloc.load(),
expect: () => [ expect: () => [
DocumentsState( DocumentsState(
isLoaded: true, isLoaded: true,