mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 10:07:51 -06:00
Implemented inbox (still WIP)
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
|
||||
|
||||
abstract class LabelCubit<T extends Label> extends Cubit<Map<int, T>> {
|
||||
final LabelRepository labelRepository;
|
||||
|
||||
LabelCubit(this.labelRepository) : super({});
|
||||
|
||||
@protected
|
||||
void loadFrom(Iterable<T> items) =>
|
||||
emit(Map.fromIterable(items, key: (e) => (e as T).id!));
|
||||
|
||||
Future<T> add(T item) async {
|
||||
assert(item.id == null);
|
||||
final addedItem = await save(item);
|
||||
final newState = {...state};
|
||||
newState.putIfAbsent(addedItem.id!, () => addedItem);
|
||||
emit(newState);
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
Future<T> replace(T item) async {
|
||||
assert(item.id != null);
|
||||
final updatedItem = await update(item);
|
||||
final newState = {...state};
|
||||
newState[item.id!] = updatedItem;
|
||||
emit(newState);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
Future<void> remove(T item) async {
|
||||
assert(item.id != null);
|
||||
if (state.containsKey(item.id)) {
|
||||
final deletedId = await delete(item);
|
||||
final newState = {...state};
|
||||
newState.remove(deletedId);
|
||||
emit(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() => emit({});
|
||||
|
||||
Future<void> initialize();
|
||||
|
||||
@protected
|
||||
Future<T> save(T item);
|
||||
|
||||
@protected
|
||||
Future<T> update(T item);
|
||||
|
||||
@protected
|
||||
Future<int> delete(T item);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ class PaperlessServerInformationCubit
|
||||
PaperlessServerInformationCubit(this.service)
|
||||
: super(PaperlessServerInformation());
|
||||
|
||||
Future<void> updateStatus() async {
|
||||
Future<void> updateInformtion() async {
|
||||
emit(await service.getInformation());
|
||||
}
|
||||
}
|
||||
|
||||
62
lib/core/bloc/paperless_statistics_cubit.dart
Normal file
62
lib/core/bloc/paperless_statistics_cubit.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/model/paperless_statistics.dart';
|
||||
import 'package:paperless_mobile/core/model/paperless_statistics_state.dart';
|
||||
import 'package:paperless_mobile/core/service/paperless_statistics_service.dart';
|
||||
|
||||
@singleton
|
||||
class PaperlessStatisticsCubit extends Cubit<PaperlessStatisticsState> {
|
||||
final PaperlessStatisticsService statisticsService;
|
||||
|
||||
PaperlessStatisticsCubit(this.statisticsService)
|
||||
: super(PaperlessStatisticsState(isLoaded: false));
|
||||
|
||||
Future<void> updateStatistics() async {
|
||||
final stats = await statisticsService.getStatistics();
|
||||
emit(PaperlessStatisticsState(isLoaded: true, statistics: stats));
|
||||
}
|
||||
|
||||
void decrementInboxCount() {
|
||||
if (state.isLoaded) {
|
||||
emit(
|
||||
PaperlessStatisticsState(
|
||||
isLoaded: true,
|
||||
statistics: PaperlessStatistics(
|
||||
documentsInInbox: max(0, state.statistics!.documentsInInbox - 1),
|
||||
documentsTotal: state.statistics!.documentsTotal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void incrementInboxCount() {
|
||||
if (state.isLoaded) {
|
||||
emit(
|
||||
PaperlessStatisticsState(
|
||||
isLoaded: true,
|
||||
statistics: PaperlessStatistics(
|
||||
documentsInInbox: state.statistics!.documentsInInbox + 1,
|
||||
documentsTotal: state.statistics!.documentsTotal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void resetInboxCount() {
|
||||
if (state.isLoaded) {
|
||||
emit(
|
||||
PaperlessStatisticsState(
|
||||
isLoaded: true,
|
||||
statistics: PaperlessStatistics(
|
||||
documentsInInbox: 0,
|
||||
documentsTotal: state.statistics!.documentsTotal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ String translateError(BuildContext context, ErrorCode code) {
|
||||
return S.of(context).errorMessageScanRemoveFailed;
|
||||
case ErrorCode.invalidClientCertificateConfiguration:
|
||||
return S.of(context).errorMessageInvalidClientCertificateConfiguration;
|
||||
case ErrorCode.documentBulkDeleteFailed:
|
||||
return S.of(context).errorMessageBulkDeleteDocumentsFailed;
|
||||
case ErrorCode.documentBulkActionFailed:
|
||||
return S.of(context).errorMessageBulkActionFailed;
|
||||
case ErrorCode.biometricsNotSupported:
|
||||
return S.of(context).errorMessageBiotmetricsNotSupported;
|
||||
case ErrorCode.biometricAuthenticationFailed:
|
||||
|
||||
@@ -27,7 +27,7 @@ enum ErrorCode {
|
||||
documentUpdateFailed,
|
||||
documentLoadFailed,
|
||||
documentDeleteFailed,
|
||||
documentBulkDeleteFailed,
|
||||
documentBulkActionFailed,
|
||||
documentPreviewFailed,
|
||||
documentAsnQueryFailed,
|
||||
tagCreateFailed,
|
||||
|
||||
11
lib/core/model/paperless_statistics_state.dart
Normal file
11
lib/core/model/paperless_statistics_state.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:paperless_mobile/core/model/paperless_statistics.dart';
|
||||
|
||||
class PaperlessStatisticsState {
|
||||
final bool isLoaded;
|
||||
final PaperlessStatistics? statistics;
|
||||
|
||||
PaperlessStatisticsState({
|
||||
required this.isLoaded,
|
||||
this.statistics,
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:typed_data';
|
||||
import 'package:flutter_bloc/flutter_bloc.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/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/paged_search_result.dart';
|
||||
@@ -36,9 +37,9 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
createdAt: createdAt,
|
||||
);
|
||||
|
||||
// documentRepository
|
||||
// .waitForConsumptionFinished(fileName, title)
|
||||
// .then((value) => onConsumptionFinished(value));
|
||||
documentRepository
|
||||
.waitForConsumptionFinished(fileName, title)
|
||||
.then((value) => onConsumptionFinished(value));
|
||||
}
|
||||
|
||||
Future<void> removeDocument(DocumentModel document) async {
|
||||
@@ -47,8 +48,23 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
}
|
||||
|
||||
Future<void> bulkRemoveDocuments(List<DocumentModel> documents) async {
|
||||
await documentRepository.bulkDelete(documents);
|
||||
return await reloadDocuments();
|
||||
await documentRepository.bulkAction(
|
||||
BulkDeleteAction(documents.map((doc) => doc.id)),
|
||||
);
|
||||
await reloadDocuments();
|
||||
}
|
||||
|
||||
Future<void> bulkEditTags(
|
||||
List<DocumentModel> documents, {
|
||||
Iterable<int> addTags = const [],
|
||||
Iterable<int> removeTags = const [],
|
||||
}) async {
|
||||
await documentRepository.bulkAction(BulkModifyTagsAction(
|
||||
documents.map((doc) => doc.id),
|
||||
addTags: addTags,
|
||||
removeTags: removeTags,
|
||||
));
|
||||
await reloadDocuments();
|
||||
}
|
||||
|
||||
Future<void> updateDocument(DocumentModel document) async {
|
||||
@@ -135,15 +151,20 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeInboxTags(
|
||||
///
|
||||
/// 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 updatedTags = document.tags.where((id) => !inboxTags.contains(id));
|
||||
return updateDocument(
|
||||
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() {
|
||||
|
||||
@@ -1,24 +1,50 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
|
||||
class BulkEditAction {
|
||||
final List<int> documents;
|
||||
final BulkEditActionMethod _method;
|
||||
final Map<String, dynamic> parameters;
|
||||
abstract class BulkAction {
|
||||
final Iterable<int> documentIds;
|
||||
|
||||
BulkEditAction.delete(this.documents)
|
||||
: _method = BulkEditActionMethod.delete,
|
||||
parameters = {};
|
||||
BulkAction(this.documentIds);
|
||||
|
||||
JSON toJson();
|
||||
}
|
||||
|
||||
class BulkDeleteAction extends BulkAction {
|
||||
BulkDeleteAction(super.documents);
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
return {
|
||||
'documents': documents,
|
||||
'method': _method.name,
|
||||
'parameters': parameters,
|
||||
'documents': documentIds.toList(),
|
||||
'method': 'delete',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum BulkEditActionMethod {
|
||||
delete,
|
||||
edit;
|
||||
class BulkModifyTagsAction extends BulkAction {
|
||||
final Iterable<int> removeTags;
|
||||
final Iterable<int> addTags;
|
||||
|
||||
BulkModifyTagsAction(
|
||||
super.documents, {
|
||||
this.removeTags = const [],
|
||||
this.addTags = const [],
|
||||
});
|
||||
|
||||
BulkModifyTagsAction.addTags(super.documents, this.addTags)
|
||||
: removeTags = const [];
|
||||
|
||||
BulkModifyTagsAction.removeTags(super.documents, this.removeTags)
|
||||
: addTags = const [];
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
return {
|
||||
'documents': documentIds.toList(),
|
||||
'method': 'modify_tags',
|
||||
'parameters': {
|
||||
'add_tags': addTags.toList(),
|
||||
'remove_tags': removeTags.toList(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,19 @@ class OnlyNotAssignedTagsQuery extends TagsQuery {
|
||||
}
|
||||
|
||||
class AnyAssignedTagsQuery extends TagsQuery {
|
||||
const AnyAssignedTagsQuery();
|
||||
final Iterable<int> tagIds;
|
||||
const AnyAssignedTagsQuery({
|
||||
this.tagIds = const [],
|
||||
});
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
|
||||
@override
|
||||
String toQueryParameter() {
|
||||
return '&is_tagged=1';
|
||||
if (tagIds.isEmpty) {
|
||||
return '&is_tagged=1';
|
||||
}
|
||||
return '&tags__id__in=${tagIds.join(',')}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
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/document_meta_data.model.dart';
|
||||
@@ -23,7 +24,7 @@ abstract class DocumentRepository {
|
||||
Future<List<SimilarDocumentModel>> findSimilar(int docId);
|
||||
Future<int> delete(DocumentModel doc);
|
||||
Future<DocumentMetaData> getMetaData(DocumentModel document);
|
||||
Future<List<int>> bulkDelete(List<DocumentModel> models);
|
||||
Future<Iterable<int>> bulkAction(BulkAction action);
|
||||
Future<Uint8List> getPreview(int docId);
|
||||
String getThumbnailUrl(int docId);
|
||||
Future<DocumentModel> waitForConsumptionFinished(
|
||||
|
||||
@@ -216,18 +216,16 @@ class DocumentRepositoryImpl implements DocumentRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> bulkDelete(List<DocumentModel> documentModels) async {
|
||||
final List<int> ids = documentModels.map((e) => e.id).toList();
|
||||
final action = BulkEditAction.delete(ids);
|
||||
Future<Iterable<int>> bulkAction(BulkAction action) async {
|
||||
final response = await httpClient.post(
|
||||
Uri.parse("/api/documents/bulk_edit/"),
|
||||
body: json.encode(action.toJson()),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return ids;
|
||||
return action.documentIds;
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.documentBulkDeleteFailed);
|
||||
throw const ErrorMessage(ErrorCode.documentBulkActionFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import 'dart:math';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.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/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
|
||||
@@ -345,16 +346,20 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
return const SizedBox(height: 32.0);
|
||||
}
|
||||
|
||||
void _onEdit(DocumentModel document) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => LabelBlocProvider(
|
||||
child: DocumentEditPage(document: document),
|
||||
),
|
||||
maintainState: true,
|
||||
),
|
||||
);
|
||||
void _onEdit(DocumentModel document) async {
|
||||
final wasUpdated = await Navigator.push<bool>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => LabelBlocProvider(
|
||||
child: DocumentEditPage(document: document),
|
||||
),
|
||||
maintainState: true,
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (wasUpdated) {
|
||||
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDownload(DocumentModel document) async {
|
||||
|
||||
@@ -3,6 +3,8 @@ 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:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:intl/intl.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';
|
||||
@@ -20,6 +22,7 @@ import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_co
|
||||
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/features/labels/document_type/view/pages/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.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/features/labels/storage_path/view/pages/add_storage_path_page.dart';
|
||||
@@ -27,8 +30,6 @@ import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_fie
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DocumentEditPage extends StatefulWidget {
|
||||
final DocumentModel document;
|
||||
@@ -80,13 +81,15 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
setState(() {
|
||||
_isSubmitLoading = true;
|
||||
});
|
||||
bool wasUpdated = false;
|
||||
try {
|
||||
await getIt<DocumentsCubit>().updateDocument(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateErrorMessage);
|
||||
wasUpdated = true;
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context, wasUpdated);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -115,7 +118,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
child: ListView(children: [
|
||||
_buildTitleFormField().padded(),
|
||||
_buildCreatedAtFormField().padded(),
|
||||
BlocBuilder<DocumentTypeCubit, Map<int, DocumentType>>(
|
||||
BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
notAssignedSelectable: false,
|
||||
@@ -130,7 +133,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
initialValue:
|
||||
DocumentTypeQuery.fromId(widget.document.documentType),
|
||||
state: state,
|
||||
state: state.labels,
|
||||
name: fkDocumentType,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
@@ -139,7 +142,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
);
|
||||
},
|
||||
).padded(),
|
||||
BlocBuilder<CorrespondentCubit, Map<int, Correspondent>>(
|
||||
BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
notAssignedSelectable: false,
|
||||
@@ -150,7 +153,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
child: AddCorrespondentPage(initalValue: initialValue),
|
||||
),
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
initialValue:
|
||||
CorrespondentQuery.fromId(widget.document.correspondent),
|
||||
name: fkCorrespondent,
|
||||
@@ -161,7 +164,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
);
|
||||
},
|
||||
).padded(),
|
||||
BlocBuilder<StoragePathCubit, Map<int, StoragePath>>(
|
||||
BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||
notAssignedSelectable: false,
|
||||
@@ -172,7 +175,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
child: AddStoragePathPage(initalValue: initialValue),
|
||||
),
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
initialValue:
|
||||
StoragePathQuery.fromId(widget.document.storagePath),
|
||||
name: fkStoragePath,
|
||||
|
||||
@@ -2,6 +2,7 @@ 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/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.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/bloc/documents_state.dart';
|
||||
@@ -237,6 +238,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
BlocProvider.value(value: BlocProvider.of<TagCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<StoragePathCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<PaperlessStatisticsCubit>(context)),
|
||||
],
|
||||
child: DocumentDetailsPage(
|
||||
documentId: model.id,
|
||||
|
||||
@@ -43,7 +43,6 @@ class DocumentListItem extends StatelessWidget {
|
||||
child: CorrespondentWidget(
|
||||
isClickable: isLabelClickable,
|
||||
correspondentId: document.correspondent,
|
||||
afterSelected: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -18,6 +18,7 @@ import 'package:paperless_mobile/features/labels/correspondent/bloc/corresponden
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.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/features/labels/model/label_state.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/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
@@ -53,14 +54,6 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
late final DocumentsCubit _documentsCubit;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_documentsCubit = BlocProvider.of<DocumentsCubit>(context);
|
||||
}
|
||||
|
||||
DateTimeRange? _dateTimeRangeOfNullable(DateTime? start, DateTime? end) {
|
||||
if (start == null && end == null) {
|
||||
return null;
|
||||
@@ -181,12 +174,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
}
|
||||
|
||||
Widget _buildDocumentTypeFormField(DocumentsState docState) {
|
||||
return BlocBuilder<DocumentTypeCubit, Map<int, DocumentType>>(
|
||||
return BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkDocumentType,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
initialValue: docState.filter.documentType,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
@@ -198,12 +191,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
}
|
||||
|
||||
Widget _buildCorrespondentFormField(DocumentsState docState) {
|
||||
return BlocBuilder<CorrespondentCubit, Map<int, Correspondent>>(
|
||||
return BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkCorrespondent,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
initialValue: docState.filter.correspondent,
|
||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||
@@ -215,12 +208,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
}
|
||||
|
||||
Widget _buildStoragePathFormField(DocumentsState docState) {
|
||||
return BlocBuilder<StoragePathCubit, Map<int, StoragePath>>(
|
||||
return BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkStoragePath,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
initialValue: docState.filter.storagePath,
|
||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.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/paperless_server_information_cubit.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/offline_banner.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/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository_impl.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/info_drawer.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/inbox_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_bloc_provider.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/storage_path/bloc/storage_path_cubit.dart';
|
||||
@@ -20,6 +26,7 @@ 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/scan/bloc/document_scanner_cubit.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';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@@ -35,7 +42,30 @@ class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeData(context);
|
||||
_initializeData(context).then(
|
||||
(_) 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
|
||||
@@ -59,10 +89,6 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
drawer: const InfoDrawer(),
|
||||
body: [
|
||||
BlocProvider.value(
|
||||
value: DocumentsCubit(getIt<DocumentRepository>()),
|
||||
child: const InboxPage(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: getIt<DocumentsCubit>(),
|
||||
child: const DocumentsPage(),
|
||||
@@ -78,17 +104,21 @@ class _HomePageState extends State<HomePage> {
|
||||
);
|
||||
}
|
||||
|
||||
_initializeData(BuildContext context) async {
|
||||
Future<void> _initializeData(BuildContext context) {
|
||||
try {
|
||||
await BlocProvider.of<PaperlessServerInformationCubit>(context)
|
||||
.updateStatus();
|
||||
BlocProvider.of<DocumentTypeCubit>(context).initialize();
|
||||
BlocProvider.of<CorrespondentCubit>(context).initialize();
|
||||
BlocProvider.of<TagCubit>(context).initialize();
|
||||
BlocProvider.of<StoragePathCubit>(context).initialize();
|
||||
BlocProvider.of<SavedViewCubit>(context).initialize();
|
||||
return Future.wait([
|
||||
BlocProvider.of<PaperlessServerInformationCubit>(context)
|
||||
.updateInformtion(),
|
||||
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics(),
|
||||
BlocProvider.of<DocumentTypeCubit>(context).initialize(),
|
||||
BlocProvider.of<CorrespondentCubit>(context).initialize(),
|
||||
BlocProvider.of<TagCubit>(context).initialize(),
|
||||
BlocProvider.of<StoragePathCubit>(context).initialize(),
|
||||
BlocProvider.of<SavedViewCubit>(context).initialize(),
|
||||
]);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
return Future.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,6 @@ class BottomNavBar extends StatelessWidget {
|
||||
onDestinationSelected: onNavigationChanged,
|
||||
selectedIndex: selectedIndex,
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.inbox_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.inbox,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
label: S.of(context).bottomNavInboxPageLabel,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.description_outlined),
|
||||
selectedIcon: Icon(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.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/features/labels/bloc/label_bloc_provider.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/paperless_server_information.dart';
|
||||
@@ -57,7 +59,7 @@ class InfoDrawer extends StatelessWidget {
|
||||
).padded(const EdgeInsets.only(right: 8.0)),
|
||||
Text(
|
||||
S.of(context).appTitleText,
|
||||
style: Theme.of(context).textTheme.headline5!.copyWith(
|
||||
style: Theme.of(context).textTheme.headline5?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
@@ -104,18 +106,6 @@ class InfoDrawer extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
// title: RichText(
|
||||
|
||||
// text: TextSpan(
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text:
|
||||
// style:
|
||||
// Theme.of(context).textTheme.bodyText2,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
isThreeLine: true,
|
||||
),
|
||||
],
|
||||
@@ -129,27 +119,32 @@ class InfoDrawer extends StatelessWidget {
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
),
|
||||
),
|
||||
FutureBuilder<PaperlessStatistics>(
|
||||
future: getIt<PaperlessStatisticsService>().getStatistics(),
|
||||
builder: (context, snapshot) {
|
||||
BlocBuilder<PaperlessStatisticsCubit, PaperlessStatisticsState>(
|
||||
builder: (context, state) {
|
||||
return ListTile(
|
||||
title: Text("Inbox"),
|
||||
title: Text(S.of(context).bottomNavInboxPageLabel),
|
||||
leading: const Icon(Icons.inbox),
|
||||
trailing: snapshot.hasData
|
||||
? Text(
|
||||
snapshot.data!.documentsInInbox.toString(),
|
||||
)
|
||||
trailing: state.isLoaded
|
||||
? Text(state.statistics!.documentsInInbox.toString())
|
||||
: null,
|
||||
onTap: () => Navigator.push(
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LabelBlocProvider(
|
||||
child: BlocProvider.value(
|
||||
value: DocumentsCubit(getIt<DocumentRepository>()),
|
||||
child: const InboxPage(),
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: getIt<PaperlessStatisticsCubit>(),
|
||||
child: LabelBlocProvider(
|
||||
child: BlocProvider.value(
|
||||
value:
|
||||
DocumentsCubit(getIt<DocumentRepository>()),
|
||||
child: const InboxPage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
);
|
||||
getIt<DocumentsCubit>().reloadDocuments();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.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/home/view/widget/info_drawer.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/extensions/flutter_extensions.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});
|
||||
@@ -22,6 +29,9 @@ class InboxPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _InboxPageState extends State<InboxPage> {
|
||||
static const _a4AspectRatio = 1 / 1.4142;
|
||||
|
||||
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
|
||||
Iterable<int> _inboxTags = [];
|
||||
@override
|
||||
void initState() {
|
||||
@@ -31,9 +41,12 @@ class _InboxPageState extends State<InboxPage> {
|
||||
}
|
||||
|
||||
Future<void> _initInbox() async {
|
||||
final tags = BlocProvider.of<TagCubit>(context).state.values;
|
||||
_inboxTags = tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!);
|
||||
final filter = DocumentFilter(tags: IdsTagsQuery.included(_inboxTags));
|
||||
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,
|
||||
);
|
||||
@@ -41,71 +54,210 @@ class _InboxPageState extends State<InboxPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Inbox"),
|
||||
),
|
||||
drawer: const InfoDrawer(),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
label: Text("Mark all as read"),
|
||||
icon: const Icon(FontAwesomeIcons.checkDouble),
|
||||
onPressed: () {},
|
||||
),
|
||||
body: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
if (!state.isLoaded) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (state.documents.isEmpty) {
|
||||
return Text("You do not have new documents in your inbox.")
|
||||
.padded();
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
"You have ${state.documents.length} documents in your inbox.",
|
||||
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,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: state.documents
|
||||
.map(
|
||||
(doc) => Dismissible(
|
||||
direction: DismissDirection.endToStart,
|
||||
onDismissed: (_) {
|
||||
BlocProvider.of<DocumentsCubit>(context)
|
||||
.removeInboxTags(doc, _inboxTags);
|
||||
},
|
||||
key: ObjectKey(doc.id),
|
||||
child: ListTile(
|
||||
title: Text(doc.title),
|
||||
isThreeLine: true,
|
||||
leading: DocumentPreview(id: doc.id),
|
||||
subtitle: Text(DateFormat().format(doc.added)),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => LabelBlocProvider(
|
||||
child: BlocProvider.value(
|
||||
value:
|
||||
BlocProvider.of<DocumentsCubit>(context),
|
||||
child: DocumentDetailsPage(
|
||||
documentId: doc.id,
|
||||
allowEdit: false,
|
||||
isLabelClickable: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
lib/features/labels/bloc/label_cubit.dart
Normal file
75
lib/features/labels/bloc/label_cubit.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
|
||||
|
||||
abstract class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
||||
final LabelRepository labelRepository;
|
||||
|
||||
LabelCubit(this.labelRepository) : super(LabelState.initial());
|
||||
|
||||
@protected
|
||||
void loadFrom(Iterable<T> items) {
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: true,
|
||||
labels: Map.fromIterable(items, key: (e) => (e as T).id!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<T> add(T item) async {
|
||||
assert(item.id == null);
|
||||
final addedItem = await save(item);
|
||||
final newValues = {...state.labels};
|
||||
newValues.putIfAbsent(addedItem.id!, () => addedItem);
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: true,
|
||||
labels: newValues,
|
||||
),
|
||||
);
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
Future<T> replace(T item) async {
|
||||
assert(item.id != null);
|
||||
final updatedItem = await update(item);
|
||||
final updatedValues = {...state.labels};
|
||||
updatedValues[item.id!] = updatedItem;
|
||||
emit(
|
||||
LabelState(
|
||||
isLoaded: state.isLoaded,
|
||||
labels: updatedValues,
|
||||
),
|
||||
);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
Future<void> remove(T item) async {
|
||||
assert(item.id != null);
|
||||
if (state.labels.containsKey(item.id)) {
|
||||
final deletedId = await delete(item);
|
||||
final updatedValues = {...state.labels}..remove(deletedId);
|
||||
emit(
|
||||
LabelState(isLoaded: true, labels: updatedValues),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
emit(LabelState(isLoaded: false, labels: {}));
|
||||
}
|
||||
|
||||
Future<void> initialize();
|
||||
|
||||
@protected
|
||||
Future<T> save(T item);
|
||||
|
||||
@protected
|
||||
Future<T> update(T item);
|
||||
|
||||
@protected
|
||||
Future<int> delete(T item);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ 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/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class CorrespondentWidget extends StatelessWidget {
|
||||
@@ -16,7 +17,7 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
|
||||
const CorrespondentWidget({
|
||||
Key? key,
|
||||
required this.correspondentId,
|
||||
this.correspondentId,
|
||||
this.afterSelected,
|
||||
this.textColor,
|
||||
this.isClickable = true,
|
||||
@@ -26,12 +27,12 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return AbsorbPointer(
|
||||
absorbing: !isClickable,
|
||||
child: BlocBuilder<CorrespondentCubit, Map<int, Correspondent>>(
|
||||
child: BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () => _addCorrespondentToFilter(context),
|
||||
child: Text(
|
||||
(state[correspondentId]?.name) ?? "-",
|
||||
(state.getLabel(correspondentId)?.name) ?? "-",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ 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/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentTypeWidget extends StatelessWidget {
|
||||
@@ -24,10 +25,10 @@ class DocumentTypeWidget extends StatelessWidget {
|
||||
absorbing: !isClickable,
|
||||
child: GestureDetector(
|
||||
onTap: () => _addDocumentTypeToFilter(context),
|
||||
child: BlocBuilder<DocumentTypeCubit, Map<int, DocumentType>>(
|
||||
child: BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return Text(
|
||||
state[documentTypeId]?.toString() ?? "-",
|
||||
state.labels[documentTypeId]?.toString() ?? "-",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText2!
|
||||
|
||||
19
lib/features/labels/model/label_state.dart
Normal file
19
lib/features/labels/model/label_state.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class LabelState<T extends Label> {
|
||||
LabelState.initial() : this(isLoaded: false, labels: {});
|
||||
final bool isLoaded;
|
||||
final Map<int, T> labels;
|
||||
|
||||
LabelState({
|
||||
required this.isLoaded,
|
||||
required this.labels,
|
||||
});
|
||||
|
||||
T? getLabel(int? key) {
|
||||
if (isLoaded) {
|
||||
return labels[key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
|
||||
@singleton
|
||||
|
||||
@@ -3,6 +3,7 @@ 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/model/label_state.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';
|
||||
@@ -15,7 +16,7 @@ class StoragePathWidget extends StatelessWidget {
|
||||
|
||||
const StoragePathWidget({
|
||||
Key? key,
|
||||
required this.pathId,
|
||||
this.pathId,
|
||||
this.afterSelected,
|
||||
this.textColor,
|
||||
this.isClickable = true,
|
||||
@@ -25,12 +26,12 @@ class StoragePathWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return AbsorbPointer(
|
||||
absorbing: !isClickable,
|
||||
child: BlocBuilder<StoragePathCubit, Map<int, StoragePath>>(
|
||||
child: BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () => _addStoragePathToFilter(context),
|
||||
child: Text(
|
||||
(state[pathId]?.name) ?? "-",
|
||||
state.getLabel(pathId)?.name ?? "-",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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/bloc/paperless_statistics_cubit.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/document_filter.dart';
|
||||
@@ -21,7 +22,11 @@ class EditTagPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return EditLabelPage<Tag>(
|
||||
label: tag,
|
||||
onSubmit: BlocProvider.of<TagCubit>(context).replace,
|
||||
onSubmit: (tag) async {
|
||||
await BlocProvider.of<TagCubit>(context).replace(tag);
|
||||
//If inbox property was added/removed from tag, the number of documetns in inbox may increase/decrease.
|
||||
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics();
|
||||
},
|
||||
onDelete: (tag) => _onDelete(tag, context),
|
||||
fromJson: Tag.fromJson,
|
||||
additionalFields: [
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/pages/add_tag_page.dart';
|
||||
@@ -45,7 +46,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
_textEditingController = TextEditingController()
|
||||
..addListener(() {
|
||||
setState(() {
|
||||
_showCreationSuffixIcon = state.values
|
||||
_showCreationSuffixIcon = state.labels.values
|
||||
.where(
|
||||
(item) => item.name.toLowerCase().startsWith(
|
||||
_textEditingController.text.toLowerCase(),
|
||||
@@ -61,7 +62,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TagCubit, Map<int, Tag>>(
|
||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
||||
builder: (context, tagState) {
|
||||
return FormBuilderField<TagsQuery>(
|
||||
builder: (field) {
|
||||
@@ -81,7 +82,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
controller: _textEditingController,
|
||||
),
|
||||
suggestionsCallback: (query) {
|
||||
final suggestions = tagState.values
|
||||
final suggestions = tagState.labels.values
|
||||
.where((element) => element.name
|
||||
.toLowerCase()
|
||||
.startsWith(query.toLowerCase()))
|
||||
@@ -113,7 +114,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
title: Text(S.of(context).labelAnyAssignedText),
|
||||
);
|
||||
}
|
||||
final tag = tagState[data]!;
|
||||
final tag = tagState.getLabel(data)!;
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
Icons.circle,
|
||||
@@ -159,7 +160,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
||||
(query) => _buildTag(
|
||||
field,
|
||||
query,
|
||||
tagState[query.id]!,
|
||||
tagState.getLabel(query.id)!,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
||||
@@ -27,13 +28,13 @@ class TagsWidget extends StatefulWidget {
|
||||
class _TagsWidgetState extends State<TagsWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TagCubit, Map<int, Tag>>(
|
||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
||||
builder: (context, state) {
|
||||
final children = widget.tagIds
|
||||
.where((id) => state.containsKey(id))
|
||||
.where((id) => state.labels.containsKey(id))
|
||||
.map(
|
||||
(id) => TagWidget(
|
||||
tag: state[id]!,
|
||||
tag: state.getLabel(id)!,
|
||||
afterTagTapped: widget.afterTagTapped,
|
||||
isClickable: widget.isClickable,
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_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/type/types.dart';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.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/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
@@ -227,6 +228,7 @@ class _LabelsPageState extends State<LabelsPage>
|
||||
providers: [
|
||||
BlocProvider.value(value: getIt<DocumentsCubit>()),
|
||||
BlocProvider.value(value: BlocProvider.of<TagCubit>(context)),
|
||||
BlocProvider.value(value: getIt<PaperlessStatisticsCubit>()),
|
||||
],
|
||||
child: EditTagPage(tag: tag),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/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';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
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/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_item.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
@@ -45,10 +46,10 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
||||
}
|
||||
return RefreshIndicator(
|
||||
onRefresh: cubit.initialize,
|
||||
child: BlocBuilder<Cubit<Map<int, T>>, Map<int, T>>(
|
||||
child: BlocBuilder<Cubit<LabelState<T>>, LabelState<T>>(
|
||||
bloc: cubit,
|
||||
builder: (context, state) {
|
||||
final labels = state.values.toList()..sort();
|
||||
final labels = state.labels.values.toList()..sort();
|
||||
if (labels.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.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';
|
||||
|
||||
@@ -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/bloc/paperless_statistics_cubit.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';
|
||||
@@ -19,6 +20,7 @@ import 'package:paperless_mobile/features/labels/correspondent/model/corresponde
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||
@@ -165,7 +167,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
labelText: S.of(context).documentCreatedPropertyLabel + " *",
|
||||
),
|
||||
),
|
||||
BlocBuilder<DocumentTypeCubit, Map<int, DocumentType>>(
|
||||
BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
bloc: getIt<DocumentTypeCubit>(), //TODO: Use provider
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
@@ -178,7 +180,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
),
|
||||
label: S.of(context).documentDocumentTypePropertyLabel + " *",
|
||||
name: DocumentModel.documentTypeKey,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
DocumentTypeQuery.notAssigned,
|
||||
@@ -186,7 +188,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocBuilder<CorrespondentCubit, Map<int, Correspondent>>(
|
||||
BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
bloc: getIt<CorrespondentCubit>(), //TODO: Use provider
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
@@ -200,7 +202,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
label:
|
||||
S.of(context).documentCorrespondentPropertyLabel + " *",
|
||||
name: DocumentModel.correspondentKey,
|
||||
state: state,
|
||||
state: state.labels,
|
||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
CorrespondentQuery.notAssigned,
|
||||
@@ -257,7 +259,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
} on PaperlessValidationErrors catch (errorMessages) {
|
||||
setState(() => _errors = errorMessages);
|
||||
} catch (unknownError, stackTrace) {
|
||||
showErrorMessage(context, ErrorMessage.unknown(), stackTrace);
|
||||
showErrorMessage(context, const ErrorMessage.unknown(), stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isUploadLoading = false;
|
||||
@@ -274,22 +276,23 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
return source.replaceAll(RegExp(r"[\W_]"), "_");
|
||||
}
|
||||
|
||||
void _onConsumptionFinished(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),
|
||||
),
|
||||
);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ 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/label_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/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';
|
||||
|
||||
@@ -14,23 +14,27 @@ class ApplicationSettingsState {
|
||||
preferredLocaleSubtag: Platform.localeName.split('_').first,
|
||||
preferredThemeMode: ThemeMode.system,
|
||||
preferredViewType: ViewType.list,
|
||||
showInboxOnStartup: true,
|
||||
);
|
||||
|
||||
static const isLocalAuthenticationEnabledKey = "isLocalAuthenticationEnabled";
|
||||
static const preferredLocaleSubtagKey = "localeSubtag";
|
||||
static const preferredThemeModeKey = "preferredThemeModeKey";
|
||||
static const preferredViewTypeKey = 'preferredViewType';
|
||||
static const showInboxOnStartupKey = 'showinboxOnStartup';
|
||||
|
||||
final bool isLocalAuthenticationEnabled;
|
||||
final String preferredLocaleSubtag;
|
||||
final ThemeMode preferredThemeMode;
|
||||
final ViewType preferredViewType;
|
||||
final bool showInboxOnStartup;
|
||||
|
||||
ApplicationSettingsState({
|
||||
required this.preferredLocaleSubtag,
|
||||
required this.preferredThemeMode,
|
||||
required this.isLocalAuthenticationEnabled,
|
||||
required this.preferredViewType,
|
||||
required this.showInboxOnStartup,
|
||||
});
|
||||
|
||||
JSON toJson() {
|
||||
@@ -43,17 +47,25 @@ class ApplicationSettingsState {
|
||||
}
|
||||
|
||||
ApplicationSettingsState.fromJson(JSON json)
|
||||
: isLocalAuthenticationEnabled = json[isLocalAuthenticationEnabledKey],
|
||||
preferredLocaleSubtag = json[preferredLocaleSubtagKey],
|
||||
preferredThemeMode =
|
||||
ThemeMode.values.byName(json[preferredThemeModeKey]),
|
||||
preferredViewType = ViewType.values.byName(json[preferredViewTypeKey]);
|
||||
: isLocalAuthenticationEnabled = json[isLocalAuthenticationEnabledKey] ??
|
||||
defaultSettings.isLocalAuthenticationEnabled,
|
||||
preferredLocaleSubtag = json[preferredLocaleSubtagKey] ??
|
||||
defaultSettings.preferredLocaleSubtag,
|
||||
preferredThemeMode = json.containsKey(preferredThemeModeKey)
|
||||
? ThemeMode.values.byName(json[preferredThemeModeKey])
|
||||
: defaultSettings.preferredThemeMode,
|
||||
preferredViewType = json.containsKey(preferredViewTypeKey)
|
||||
? ViewType.values.byName(json[preferredViewTypeKey])
|
||||
: defaultSettings.preferredViewType,
|
||||
showInboxOnStartup =
|
||||
json[showInboxOnStartupKey] ?? defaultSettings.showInboxOnStartup;
|
||||
|
||||
ApplicationSettingsState copyWith({
|
||||
bool? isLocalAuthenticationEnabled,
|
||||
String? preferredLocaleSubtag,
|
||||
ThemeMode? preferredThemeMode,
|
||||
ViewType? preferredViewType,
|
||||
bool? showInboxOnStartup,
|
||||
}) {
|
||||
return ApplicationSettingsState(
|
||||
isLocalAuthenticationEnabled:
|
||||
@@ -62,6 +74,7 @@ class ApplicationSettingsState {
|
||||
preferredLocaleSubtag ?? this.preferredLocaleSubtag,
|
||||
preferredThemeMode: preferredThemeMode ?? this.preferredThemeMode,
|
||||
preferredViewType: preferredViewType ?? this.preferredViewType,
|
||||
showInboxOnStartup: showInboxOnStartup ?? this.showInboxOnStartup,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"appSettingsBiometricAuthenticationLabel": "Biometrische Authentifizierung aktivieren",
|
||||
"appSettingsEnableBiometricAuthenticationReasonText": "Authentifizieren, um die biometrische Authentifizierung zu aktivieren.",
|
||||
"appSettingsDisableBiometricAuthenticationReasonText": "Authentifizieren, um die biometrische Authentifizierung zu deaktivieren.",
|
||||
"errorMessageBulkDeleteDocumentsFailed": "Es ist ein Fehler beim massenhaften Löschen der Dokumente aufgetreten.",
|
||||
"errorMessageBulkActionFailed": "Es ist ein Fehler beim massenhaften bearbeiten der Dokumente aufgetreten.",
|
||||
"errorMessageBiotmetricsNotSupported": "Biometrische Authentifizierung wird von diesem Gerät nicht unterstützt.",
|
||||
"errorMessageBiometricAuthenticationFailed": "Biometrische Authentifizierung fehlgeschlagen.",
|
||||
"errorMessageDeviceOffline": "Daten konnten nicht geladen werden: Eine Verbindung zum Internet konnte nicht hergestellt werden.",
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"documentPreviewPageTitle": "Preview",
|
||||
"appSettingsEnableBiometricAuthenticationReasonText": "Authenticate to enable biometric authentication",
|
||||
"appSettingsDisableBiometricAuthenticationReasonText": "Authenticate to disable biometric authentication",
|
||||
"errorMessageBulkDeleteDocumentsFailed": "Could not bulk delete documents.",
|
||||
"errorMessageBulkActionFailed": "Could not bulk edit documents.",
|
||||
"errorMessageBiotmetricsNotSupported": "Biometric authentication not supported on this device.",
|
||||
"errorMessageBiometricAuthenticationFailed": "Biometric authentication failed.",
|
||||
"errorMessageDeviceOffline": "Could not fetch data: You are not connected to the internet.",
|
||||
|
||||
@@ -11,7 +11,8 @@ import 'package:intl/intl.dart';
|
||||
import 'package:intl/intl_standalone.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/label_bloc_provider.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/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/global/asset_images.dart';
|
||||
import 'package:paperless_mobile/core/global/constants.dart';
|
||||
@@ -194,15 +195,6 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
ReceiveSharingIntent.getInitialMedia().then(handleReceivedFiles);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
FlutterNativeSplash.remove();
|
||||
for (var element in AssetImages.values) {
|
||||
element.load(context);
|
||||
}
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
@@ -215,9 +207,6 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
final bool showIntroSlider =
|
||||
authState.isAuthenticated && !authState.wasLoginStored;
|
||||
if (showIntroSlider) {
|
||||
for (final img in AssetImages.values) {
|
||||
img.load(context);
|
||||
}
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@@ -229,10 +218,14 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
||||
},
|
||||
builder: (context, authentication) {
|
||||
if (authentication.isAuthenticated) {
|
||||
return const LabelBlocProvider(
|
||||
child: HomePage(),
|
||||
return BlocProvider.value(
|
||||
value: getIt<PaperlessStatisticsCubit>(),
|
||||
child: const LabelBlocProvider(
|
||||
child: HomePage(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
FlutterNativeSplash.remove();
|
||||
return const LoginPage();
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user