mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 12:07:54 -06:00
Externalized API and models as own package
This commit is contained in:
@@ -62,7 +62,7 @@ class _BiometricAuthenticationIntroSlideState
|
||||
final settings =
|
||||
BlocProvider.of<ApplicationSettingsCubit>(context)
|
||||
.state;
|
||||
getIt<AuthenticationService>()
|
||||
getIt<LocalAuthenticationService>()
|
||||
.authenticateLocalUser(
|
||||
"Please authenticate to secure Paperless Mobile")
|
||||
.then((isEnabled) {
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
part 'document_details_state.dart';
|
||||
|
||||
class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
||||
final DocumentRepository _documentRepository;
|
||||
final PaperlessDocumentsApi _api;
|
||||
|
||||
DocumentDetailsCubit(this._documentRepository, DocumentModel initialDocument)
|
||||
DocumentDetailsCubit(this._api, DocumentModel initialDocument)
|
||||
: super(DocumentDetailsState(document: initialDocument));
|
||||
|
||||
Future<void> delete(DocumentModel document) async {
|
||||
await _documentRepository.delete(document);
|
||||
await _api.delete(document);
|
||||
emit(const DocumentDetailsState());
|
||||
}
|
||||
|
||||
Future<void> update(DocumentModel document) async {
|
||||
final updatedDocument = await _documentRepository.update(document);
|
||||
final updatedDocument = await _api.update(document);
|
||||
emit(DocumentDetailsState(document: updatedDocument));
|
||||
}
|
||||
|
||||
Future<void> assignAsn(DocumentModel document) async {
|
||||
if (document.archiveSerialNumber == null) {
|
||||
final int asn = await _documentRepository.findNextAsn();
|
||||
final int asn = await _api.findNextAsn();
|
||||
update(document.copyWith(archiveSerialNumber: asn));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,11 @@ 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/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_meta_data.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_edit_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart';
|
||||
@@ -235,7 +232,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
|
||||
Widget _buildDocumentMetaDataView(DocumentModel document) {
|
||||
return FutureBuilder<DocumentMetaData>(
|
||||
future: getIt<DocumentRepository>().getMetaData(document),
|
||||
future: getIt<PaperlessDocumentsApi>().getMetaData(document),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@@ -295,7 +292,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
Future<void> _assignAsn(DocumentModel document) async {
|
||||
try {
|
||||
await BlocProvider.of<DocumentDetailsCubit>(context).assignAsn(document);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -414,7 +411,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
return;
|
||||
}
|
||||
setState(() => _isDownloadPending = true);
|
||||
getIt<DocumentRepository>().download(document).then((bytes) async {
|
||||
getIt<PaperlessDocumentsApi>().download(document).then((bytes) async {
|
||||
final Directory dir = (await getExternalStorageDirectories(
|
||||
type: StorageDirectory.downloads))!
|
||||
.first;
|
||||
@@ -431,7 +428,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
///
|
||||
Future<void> _onShare(DocumentModel document) async {
|
||||
Uint8List documentBytes =
|
||||
await getIt<DocumentRepository>().download(document);
|
||||
await getIt<PaperlessDocumentsApi>().download(document);
|
||||
final dir = await getTemporaryDirectory();
|
||||
final String path = "${dir.path}/${document.originalFileName}";
|
||||
await File(path).writeAsBytes(documentBytes);
|
||||
@@ -459,7 +456,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
try {
|
||||
await BlocProvider.of<DocumentDetailsCubit>(context).delete(document);
|
||||
showSnackBar(context, S.of(context).documentDeleteSuccessMessage);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
// Document deleted => go back to primary route
|
||||
@@ -472,7 +469,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DocumentView(
|
||||
documentBytes: getIt<DocumentRepository>().getPreview(document.id),
|
||||
documentBytes: getIt<PaperlessDocumentsApi>().getPreview(document.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,21 +2,17 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
|
||||
@singleton
|
||||
class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
final DocumentRepository documentRepository;
|
||||
final PaperlessDocumentsApi _api;
|
||||
|
||||
DocumentsCubit(this.documentRepository) : super(DocumentsState.initial);
|
||||
DocumentsCubit(this._api) : super(DocumentsState.initial);
|
||||
|
||||
Future<void> bulkRemove(List<DocumentModel> documents) async {
|
||||
await documentRepository.bulkAction(
|
||||
await _api.bulkAction(
|
||||
BulkDeleteAction(documents.map((doc) => doc.id)),
|
||||
);
|
||||
await reload();
|
||||
@@ -27,7 +23,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
Iterable<int> addTags = const [],
|
||||
Iterable<int> removeTags = const [],
|
||||
}) async {
|
||||
await documentRepository.bulkAction(BulkModifyTagsAction(
|
||||
await _api.bulkAction(BulkModifyTagsAction(
|
||||
documents.map((doc) => doc.id),
|
||||
addTags: addTags,
|
||||
removeTags: removeTags,
|
||||
@@ -40,13 +36,13 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
bool updateRemote = true,
|
||||
]) async {
|
||||
if (updateRemote) {
|
||||
await documentRepository.update(document);
|
||||
await _api.update(document);
|
||||
}
|
||||
await reload();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final result = await documentRepository.find(state.filter);
|
||||
final result = await _api.find(state.filter);
|
||||
emit(DocumentsState(
|
||||
isLoaded: true,
|
||||
value: [...state.value, result],
|
||||
@@ -60,15 +56,14 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
}
|
||||
var newPages = <PagedSearchResult>[];
|
||||
for (final page in state.value) {
|
||||
final result = await documentRepository
|
||||
.find(state.filter.copyWith(page: page.pageKey));
|
||||
final result = await _api.find(state.filter.copyWith(page: page.pageKey));
|
||||
newPages.add(result);
|
||||
}
|
||||
emit(DocumentsState(isLoaded: true, value: newPages, filter: state.filter));
|
||||
}
|
||||
|
||||
Future<void> _bulkReloadDocuments() async {
|
||||
final result = await documentRepository
|
||||
final result = await _api
|
||||
.find(state.filter.copyWith(page: 1, pageSize: state.documents.length));
|
||||
emit(DocumentsState(isLoaded: true, value: [result], filter: state.filter));
|
||||
}
|
||||
@@ -78,7 +73,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
return;
|
||||
}
|
||||
final newFilter = state.filter.copyWith(page: state.filter.page + 1);
|
||||
final result = await documentRepository.find(newFilter);
|
||||
final result = await _api.find(newFilter);
|
||||
emit(
|
||||
DocumentsState(
|
||||
isLoaded: true, value: [...state.value, result], filter: newFilter),
|
||||
@@ -91,7 +86,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
||||
Future<void> updateFilter({
|
||||
final DocumentFilter filter = DocumentFilter.initial,
|
||||
}) async {
|
||||
final result = await documentRepository.find(filter.copyWith(page: 1));
|
||||
final result = await _api.find(filter.copyWith(page: 1));
|
||||
emit(DocumentsState(filter: filter, value: [result], isLoaded: true));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import 'package:equatable/equatable.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';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class DocumentsState extends Equatable {
|
||||
final bool isLoaded;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
|
||||
abstract class BulkAction {
|
||||
final Iterable<int> documentIds;
|
||||
|
||||
BulkAction(this.documentIds);
|
||||
|
||||
JSON toJson();
|
||||
}
|
||||
|
||||
class BulkDeleteAction extends BulkAction {
|
||||
BulkDeleteAction(super.documents);
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
return {
|
||||
'documents': documentIds.toList(),
|
||||
'method': 'delete',
|
||||
'parameters': {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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, Iterable<int> tags)
|
||||
: addTags = const [],
|
||||
removeTags = tags;
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
return {
|
||||
'documents': documentIds.toList(),
|
||||
'method': 'modify_tags',
|
||||
'parameters': {
|
||||
'add_tags': addTags.toList(),
|
||||
'remove_tags': removeTags.toList(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
|
||||
class DocumentModel extends Equatable {
|
||||
static const idKey = 'id';
|
||||
static const titleKey = 'title';
|
||||
static const contentKey = 'content';
|
||||
static const archivedFileNameKey = 'archived_file_name';
|
||||
static const asnKey = 'archive_serial_number';
|
||||
static const createdKey = 'created';
|
||||
static const modifiedKey = 'modified';
|
||||
static const addedKey = 'added';
|
||||
static const correspondentKey = 'correspondent';
|
||||
static const originalFileNameKey = 'original_file_name';
|
||||
static const documentTypeKey = 'document_type';
|
||||
static const tagsKey = 'tags';
|
||||
static const storagePathKey = 'storage_path';
|
||||
|
||||
final int id;
|
||||
final String title;
|
||||
final String? content;
|
||||
final Iterable<int> tags;
|
||||
final int? documentType;
|
||||
final int? correspondent;
|
||||
final int? storagePath;
|
||||
final DateTime created;
|
||||
final DateTime modified;
|
||||
final DateTime added;
|
||||
final int? archiveSerialNumber;
|
||||
final String originalFileName;
|
||||
final String? archivedFileName;
|
||||
|
||||
const DocumentModel({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.content,
|
||||
this.tags = const <int>[],
|
||||
required this.documentType,
|
||||
required this.correspondent,
|
||||
required this.created,
|
||||
required this.modified,
|
||||
required this.added,
|
||||
this.archiveSerialNumber,
|
||||
required this.originalFileName,
|
||||
this.archivedFileName,
|
||||
this.storagePath,
|
||||
});
|
||||
|
||||
DocumentModel.fromJson(JSON json)
|
||||
: id = json[idKey],
|
||||
title = json[titleKey],
|
||||
content = json[contentKey],
|
||||
created = DateTime.parse(json[createdKey]),
|
||||
modified = DateTime.parse(json[modifiedKey]),
|
||||
added = DateTime.parse(json[addedKey]),
|
||||
archiveSerialNumber = json[asnKey],
|
||||
originalFileName = json[originalFileNameKey],
|
||||
archivedFileName = json[archivedFileNameKey],
|
||||
tags = (json[tagsKey] as List<dynamic>).cast<int>(),
|
||||
correspondent = json[correspondentKey],
|
||||
documentType = json[documentTypeKey],
|
||||
storagePath = json[storagePathKey];
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
idKey: id,
|
||||
titleKey: title,
|
||||
asnKey: archiveSerialNumber,
|
||||
archivedFileNameKey: archivedFileName,
|
||||
contentKey: content,
|
||||
correspondentKey: correspondent,
|
||||
documentTypeKey: documentType,
|
||||
createdKey: created.toUtc().toIso8601String(),
|
||||
modifiedKey: modified.toUtc().toIso8601String(),
|
||||
addedKey: added.toUtc().toIso8601String(),
|
||||
originalFileNameKey: originalFileName,
|
||||
tagsKey: tags.toList(),
|
||||
storagePathKey: storagePath,
|
||||
};
|
||||
}
|
||||
|
||||
DocumentModel copyWith({
|
||||
String? title,
|
||||
String? content,
|
||||
bool overwriteTags = false,
|
||||
Iterable<int>? tags,
|
||||
bool overwriteDocumentType = false,
|
||||
int? documentType,
|
||||
bool overwriteCorrespondent = false,
|
||||
int? correspondent,
|
||||
bool overwriteStoragePath = false,
|
||||
int? storagePath,
|
||||
DateTime? created,
|
||||
DateTime? modified,
|
||||
DateTime? added,
|
||||
int? archiveSerialNumber,
|
||||
String? originalFileName,
|
||||
String? archivedFileName,
|
||||
}) {
|
||||
return DocumentModel(
|
||||
id: id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
documentType: overwriteDocumentType ? documentType : this.documentType,
|
||||
correspondent:
|
||||
overwriteCorrespondent ? correspondent : this.correspondent,
|
||||
storagePath: overwriteDocumentType ? storagePath : this.storagePath,
|
||||
tags: overwriteTags ? tags ?? [] : this.tags,
|
||||
created: created ?? this.created,
|
||||
modified: modified ?? this.modified,
|
||||
added: added ?? this.added,
|
||||
originalFileName: originalFileName ?? this.originalFileName,
|
||||
archiveSerialNumber: archiveSerialNumber ?? this.archiveSerialNumber,
|
||||
archivedFileName: archivedFileName ?? this.archivedFileName,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
title,
|
||||
content.hashCode,
|
||||
tags,
|
||||
documentType,
|
||||
storagePath,
|
||||
correspondent,
|
||||
created,
|
||||
modified,
|
||||
added,
|
||||
archiveSerialNumber,
|
||||
originalFileName,
|
||||
archivedFileName,
|
||||
storagePath
|
||||
];
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/asn_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentFilter with EquatableMixin {
|
||||
static const _oneDay = Duration(days: 1);
|
||||
static const DocumentFilter initial = DocumentFilter();
|
||||
|
||||
static const DocumentFilter latestDocument = DocumentFilter(
|
||||
sortField: SortField.added,
|
||||
sortOrder: SortOrder.descending,
|
||||
pageSize: 1,
|
||||
page: 1,
|
||||
);
|
||||
|
||||
final int pageSize;
|
||||
final int page;
|
||||
final DocumentTypeQuery documentType;
|
||||
final CorrespondentQuery correspondent;
|
||||
final StoragePathQuery storagePath;
|
||||
final AsnQuery asn;
|
||||
final TagsQuery tags;
|
||||
final SortField sortField;
|
||||
final SortOrder sortOrder;
|
||||
final DateTime? addedDateAfter;
|
||||
final DateTime? addedDateBefore;
|
||||
final DateTime? createdDateAfter;
|
||||
final DateTime? createdDateBefore;
|
||||
final QueryType queryType;
|
||||
final String? queryText;
|
||||
|
||||
const DocumentFilter({
|
||||
this.createdDateAfter,
|
||||
this.createdDateBefore,
|
||||
this.documentType = const DocumentTypeQuery.unset(),
|
||||
this.correspondent = const CorrespondentQuery.unset(),
|
||||
this.storagePath = const StoragePathQuery.unset(),
|
||||
this.asn = const AsnQuery.unset(),
|
||||
this.tags = const IdsTagsQuery(),
|
||||
this.sortField = SortField.created,
|
||||
this.sortOrder = SortOrder.descending,
|
||||
this.page = 1,
|
||||
this.pageSize = 25,
|
||||
this.addedDateAfter,
|
||||
this.addedDateBefore,
|
||||
this.queryType = QueryType.titleAndContent,
|
||||
this.queryText,
|
||||
});
|
||||
|
||||
String toQueryString() {
|
||||
final StringBuffer sb = StringBuffer("page=$page&page_size=$pageSize");
|
||||
sb.write(documentType.toQueryParameter());
|
||||
sb.write(correspondent.toQueryParameter());
|
||||
sb.write(tags.toQueryParameter());
|
||||
sb.write(storagePath.toQueryParameter());
|
||||
sb.write(asn.toQueryParameter());
|
||||
|
||||
if (queryText?.isNotEmpty ?? false) {
|
||||
sb.write("&${queryType.queryParam}=$queryText");
|
||||
}
|
||||
|
||||
sb.write("&ordering=${sortOrder.queryString}${sortField.queryString}");
|
||||
|
||||
// Add/subtract one day in the following because paperless uses gt/lt not gte/lte
|
||||
if (addedDateAfter != null) {
|
||||
sb.write(
|
||||
"&added__date__gt=${dateFormat.format(addedDateAfter!.subtract(_oneDay))}");
|
||||
}
|
||||
|
||||
if (addedDateBefore != null) {
|
||||
sb.write(
|
||||
"&added__date__lt=${dateFormat.format(addedDateBefore!.add(_oneDay))}");
|
||||
}
|
||||
|
||||
if (createdDateAfter != null) {
|
||||
sb.write(
|
||||
"&created__date__gt=${dateFormat.format(createdDateAfter!.subtract(_oneDay))}");
|
||||
}
|
||||
|
||||
if (createdDateBefore != null) {
|
||||
sb.write(
|
||||
"&created__date__lt=${dateFormat.format(createdDateBefore!.add(_oneDay))}");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toQueryString();
|
||||
}
|
||||
|
||||
DocumentFilter copyWith({
|
||||
int? pageSize,
|
||||
int? page,
|
||||
bool? onlyNoDocumentType,
|
||||
DocumentTypeQuery? documentType,
|
||||
CorrespondentQuery? correspondent,
|
||||
StoragePathQuery? storagePath,
|
||||
TagsQuery? tags,
|
||||
SortField? sortField,
|
||||
SortOrder? sortOrder,
|
||||
DateTime? addedDateAfter,
|
||||
DateTime? addedDateBefore,
|
||||
DateTime? createdDateBefore,
|
||||
DateTime? createdDateAfter,
|
||||
QueryType? queryType,
|
||||
String? queryText,
|
||||
}) {
|
||||
return DocumentFilter(
|
||||
pageSize: pageSize ?? this.pageSize,
|
||||
page: page ?? this.page,
|
||||
documentType: documentType ?? this.documentType,
|
||||
correspondent: correspondent ?? this.correspondent,
|
||||
storagePath: storagePath ?? this.storagePath,
|
||||
tags: tags ?? this.tags,
|
||||
sortField: sortField ?? this.sortField,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
addedDateAfter: addedDateAfter ?? this.addedDateAfter,
|
||||
addedDateBefore: addedDateBefore ?? this.addedDateBefore,
|
||||
queryType: queryType ?? this.queryType,
|
||||
queryText: queryText ?? this.queryText,
|
||||
createdDateBefore: createdDateBefore ?? this.createdDateBefore,
|
||||
createdDateAfter: createdDateAfter ?? this.createdDateAfter,
|
||||
);
|
||||
}
|
||||
|
||||
String? get titleOnlyMatchString {
|
||||
if (queryType == QueryType.title) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? get titleAndContentMatchString {
|
||||
if (queryType == QueryType.titleAndContent) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? get extendedMatchString {
|
||||
if (queryType == QueryType.extended) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
pageSize,
|
||||
page,
|
||||
documentType,
|
||||
correspondent,
|
||||
storagePath,
|
||||
asn,
|
||||
tags,
|
||||
sortField,
|
||||
sortOrder,
|
||||
addedDateAfter,
|
||||
addedDateBefore,
|
||||
createdDateAfter,
|
||||
createdDateBefore,
|
||||
queryType,
|
||||
queryText,
|
||||
];
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
class DocumentMetaData {
|
||||
String originalChecksum;
|
||||
int originalSize;
|
||||
String originalMimeType;
|
||||
String mediaFilename;
|
||||
bool hasArchiveVersion;
|
||||
String? archiveChecksum;
|
||||
int? archiveSize;
|
||||
|
||||
DocumentMetaData({
|
||||
required this.originalChecksum,
|
||||
required this.originalSize,
|
||||
required this.originalMimeType,
|
||||
required this.mediaFilename,
|
||||
required this.hasArchiveVersion,
|
||||
this.archiveChecksum,
|
||||
this.archiveSize,
|
||||
});
|
||||
|
||||
DocumentMetaData.fromJson(Map<String, dynamic> json)
|
||||
: originalChecksum = json['original_checksum'],
|
||||
originalSize = json['original_size'],
|
||||
originalMimeType = json['original_mime_type'],
|
||||
mediaFilename = json['media_filename'],
|
||||
hasArchiveVersion = json['has_archive_version'],
|
||||
archiveChecksum = json['archive_checksum'],
|
||||
archiveSize = json['archive_size'];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = <String, dynamic>{};
|
||||
data['original_checksum'] = originalChecksum;
|
||||
data['original_size'] = originalSize;
|
||||
data['original_mime_type'] = originalMimeType;
|
||||
data['media_filename'] = mediaFilename;
|
||||
data['has_archive_version'] = hasArchiveVersion;
|
||||
data['archive_checksum'] = archiveChecksum;
|
||||
data['archive_size'] = archiveSize;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class FilterRule with EquatableMixin {
|
||||
static const int titleRule = 0;
|
||||
static const int asnRule = 2;
|
||||
static const int correspondentRule = 3;
|
||||
static const int documentTypeRule = 4;
|
||||
static const int includeTagsRule = 6;
|
||||
static const int hasAnyTag = 7; // true = any tag, false = not assigned
|
||||
static const int createdBeforeRule = 8;
|
||||
static const int createdAfterRule = 9;
|
||||
static const int addedBeforeRule = 13;
|
||||
static const int addedAfterRule = 14;
|
||||
static const int excludeTagsRule = 17;
|
||||
static const int titleAndContentRule = 19;
|
||||
static const int extendedRule = 20;
|
||||
static const int storagePathRule = 25;
|
||||
// Currently unsupported view options:
|
||||
static const int _content = 1;
|
||||
static const int _isInInbox = 5;
|
||||
static const int _createdYearIs = 10;
|
||||
static const int _createdMonthIs = 11;
|
||||
static const int _createdDayIs = 12;
|
||||
static const int _modifiedBefore = 15;
|
||||
static const int _modifiedAfter = 16;
|
||||
static const int _doesNotHaveAsn = 18;
|
||||
static const int _moreLikeThis = 21;
|
||||
static const int _hasTagsIn = 22;
|
||||
static const int _asnGreaterThan = 23;
|
||||
static const int _asnLessThan = 24;
|
||||
|
||||
final int ruleType;
|
||||
final String? value;
|
||||
|
||||
FilterRule(this.ruleType, this.value);
|
||||
|
||||
FilterRule.fromJson(JSON json)
|
||||
: ruleType = json['rule_type'],
|
||||
value = json['value'];
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'rule_type': ruleType,
|
||||
'value': value,
|
||||
};
|
||||
}
|
||||
|
||||
DocumentFilter applyToFilter(final DocumentFilter filter) {
|
||||
//TODO: Check in profiling mode if this is inefficient enough to cause stutters...
|
||||
switch (ruleType) {
|
||||
case titleRule:
|
||||
return filter.copyWith(queryText: value, queryType: QueryType.title);
|
||||
case documentTypeRule:
|
||||
return filter.copyWith(
|
||||
documentType: value == null
|
||||
? const DocumentTypeQuery.notAssigned()
|
||||
: DocumentTypeQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case correspondentRule:
|
||||
return filter.copyWith(
|
||||
correspondent: value == null
|
||||
? const CorrespondentQuery.notAssigned()
|
||||
: CorrespondentQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case storagePathRule:
|
||||
return filter.copyWith(
|
||||
storagePath: value == null
|
||||
? const StoragePathQuery.notAssigned()
|
||||
: StoragePathQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case hasAnyTag:
|
||||
return filter.copyWith(
|
||||
tags: value == "true"
|
||||
? const AnyAssignedTagsQuery()
|
||||
: const OnlyNotAssignedTagsQuery(),
|
||||
);
|
||||
case includeTagsRule:
|
||||
assert(filter.tags is IdsTagsQuery);
|
||||
return filter.copyWith(
|
||||
tags: (filter.tags as IdsTagsQuery)
|
||||
.withIdQueriesAdded([IncludeTagIdQuery(int.parse(value!))]),
|
||||
);
|
||||
case excludeTagsRule:
|
||||
assert(filter.tags is IdsTagsQuery);
|
||||
return filter.copyWith(
|
||||
tags: (filter.tags as IdsTagsQuery)
|
||||
.withIdQueriesAdded([ExcludeTagIdQuery(int.parse(value!))]),
|
||||
);
|
||||
case createdBeforeRule:
|
||||
return filter.copyWith(
|
||||
createdDateBefore: value == null ? null : DateTime.parse(value!),
|
||||
);
|
||||
case createdAfterRule:
|
||||
return filter.copyWith(
|
||||
createdDateAfter: value == null ? null : DateTime.parse(value!),
|
||||
);
|
||||
case addedBeforeRule:
|
||||
return filter.copyWith(
|
||||
addedDateBefore: value == null ? null : DateTime.parse(value!),
|
||||
);
|
||||
case addedAfterRule:
|
||||
return filter.copyWith(
|
||||
addedDateAfter: value == null ? null : DateTime.parse(value!),
|
||||
);
|
||||
case titleAndContentRule:
|
||||
return filter.copyWith(
|
||||
queryText: value,
|
||||
queryType: QueryType.titleAndContent,
|
||||
);
|
||||
case extendedRule:
|
||||
return filter.copyWith(queryText: value, queryType: QueryType.extended);
|
||||
//TODO: Add currently unused rules
|
||||
default:
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a [DocumentFilter] to a list of [FilterRule]s.
|
||||
///
|
||||
static List<FilterRule> fromFilter(final DocumentFilter filter) {
|
||||
List<FilterRule> filterRules = [];
|
||||
if (filter.correspondent.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(correspondentRule, null));
|
||||
}
|
||||
if (filter.correspondent.isSet) {
|
||||
filterRules.add(
|
||||
FilterRule(correspondentRule, filter.correspondent.id!.toString()));
|
||||
}
|
||||
if (filter.documentType.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(documentTypeRule, null));
|
||||
}
|
||||
if (filter.documentType.isSet) {
|
||||
filterRules.add(
|
||||
FilterRule(documentTypeRule, filter.documentType.id!.toString()));
|
||||
}
|
||||
if (filter.storagePath.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(storagePathRule, null));
|
||||
}
|
||||
if (filter.storagePath.isSet) {
|
||||
filterRules
|
||||
.add(FilterRule(storagePathRule, filter.storagePath.id!.toString()));
|
||||
}
|
||||
if (filter.tags is OnlyNotAssignedTagsQuery) {
|
||||
filterRules.add(FilterRule(hasAnyTag, false.toString()));
|
||||
}
|
||||
if (filter.tags is AnyAssignedTagsQuery) {
|
||||
filterRules.add(FilterRule(hasAnyTag, true.toString()));
|
||||
}
|
||||
if (filter.tags is IdsTagsQuery) {
|
||||
filterRules.addAll((filter.tags as IdsTagsQuery)
|
||||
.includedIds
|
||||
.map((id) => FilterRule(includeTagsRule, id.toString())));
|
||||
filterRules.addAll((filter.tags as IdsTagsQuery)
|
||||
.excludedIds
|
||||
.map((id) => FilterRule(excludeTagsRule, id.toString())));
|
||||
}
|
||||
|
||||
if (filter.queryText != null) {
|
||||
switch (filter.queryType) {
|
||||
case QueryType.title:
|
||||
filterRules.add(FilterRule(titleRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.titleAndContent:
|
||||
filterRules.add(FilterRule(titleAndContentRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.extended:
|
||||
filterRules.add(FilterRule(extendedRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.asn:
|
||||
filterRules.add(FilterRule(asnRule, filter.queryText!));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filter.createdDateAfter != null) {
|
||||
filterRules.add(FilterRule(
|
||||
createdAfterRule, dateFormat.format(filter.createdDateAfter!)));
|
||||
}
|
||||
if (filter.createdDateBefore != null) {
|
||||
filterRules.add(FilterRule(
|
||||
createdBeforeRule, dateFormat.format(filter.createdDateBefore!)));
|
||||
}
|
||||
if (filter.addedDateAfter != null) {
|
||||
filterRules.add(FilterRule(
|
||||
addedAfterRule, dateFormat.format(filter.addedDateAfter!)));
|
||||
}
|
||||
if (filter.addedDateBefore != null) {
|
||||
filterRules.add(FilterRule(
|
||||
addedBeforeRule, dateFormat.format(filter.addedDateBefore!)));
|
||||
}
|
||||
return filterRules;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [ruleType, value];
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
|
||||
const pageRegex = r".*page=(\d+).*";
|
||||
|
||||
class PagedSearchResultJsonSerializer<T> {
|
||||
final JSON json;
|
||||
final T Function(JSON) fromJson;
|
||||
|
||||
PagedSearchResultJsonSerializer(this.json, this.fromJson);
|
||||
}
|
||||
|
||||
class PagedSearchResult<T> extends Equatable {
|
||||
/// Total number of available items
|
||||
final int count;
|
||||
|
||||
/// Link to next page
|
||||
final String? next;
|
||||
|
||||
/// Link to previous page
|
||||
final String? previous;
|
||||
|
||||
/// Actual items
|
||||
final List<T> results;
|
||||
|
||||
int get pageKey {
|
||||
if (next != null) {
|
||||
final matches = RegExp(pageRegex).allMatches(next!);
|
||||
final group = matches.first.group(1)!;
|
||||
final nextPageKey = int.parse(group);
|
||||
return nextPageKey - 1;
|
||||
}
|
||||
if (previous != null) {
|
||||
// This is only executed if it's the last page or there is no data.
|
||||
final matches = RegExp(pageRegex).allMatches(previous!);
|
||||
if (matches.isEmpty) {
|
||||
//In case there is a match but a page is not explicitly set, the page is 1 per default. Therefore, if the previous page is 1, this page is 1+1=2
|
||||
return 2;
|
||||
}
|
||||
final group = matches.first.group(1)!;
|
||||
final previousPageKey = int.parse(group);
|
||||
return previousPageKey + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const PagedSearchResult({
|
||||
required this.count,
|
||||
required this.next,
|
||||
required this.previous,
|
||||
required this.results,
|
||||
});
|
||||
|
||||
factory PagedSearchResult.fromJson(
|
||||
PagedSearchResultJsonSerializer<T> serializer) {
|
||||
return PagedSearchResult(
|
||||
count: serializer.json['count'],
|
||||
next: serializer.json['next'],
|
||||
previous: serializer.json['previous'],
|
||||
results: List<JSON>.from(serializer.json['results'])
|
||||
.map<T>(serializer.fromJson)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
PagedSearchResult copyWith({
|
||||
int? count,
|
||||
String? next,
|
||||
String? previous,
|
||||
List<DocumentModel>? results,
|
||||
}) {
|
||||
return PagedSearchResult(
|
||||
count: count ?? this.count,
|
||||
next: next ?? this.next,
|
||||
previous: previous ?? this.previous,
|
||||
results: results ?? this.results,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the number of pages based on the given [pageSize]. The last page
|
||||
/// might not exhaust its capacity.
|
||||
///
|
||||
int inferPageCount({required int pageSize}) {
|
||||
if (pageSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (count / pageSize).round() + 1;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [count, next, previous, results];
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class AsnQuery extends IdQueryParameter {
|
||||
const AsnQuery.fromId(super.id) : super.fromId();
|
||||
const AsnQuery.unset() : super.unset();
|
||||
const AsnQuery.notAssigned() : super.notAssigned();
|
||||
const AsnQuery.anyAssigned() : super.anyAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'archive_serial_number';
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class CorrespondentQuery extends IdQueryParameter {
|
||||
const CorrespondentQuery.fromId(super.id) : super.fromId();
|
||||
const CorrespondentQuery.unset() : super.unset();
|
||||
const CorrespondentQuery.notAssigned() : super.notAssigned();
|
||||
const CorrespondentQuery.anyAssigned() : super.anyAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'correspondent';
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class DocumentTypeQuery extends IdQueryParameter {
|
||||
const DocumentTypeQuery.fromId(super.id) : super.fromId();
|
||||
const DocumentTypeQuery.unset() : super.unset();
|
||||
const DocumentTypeQuery.notAssigned() : super.notAssigned();
|
||||
const DocumentTypeQuery.anyAssigned() : super.anyAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'document_type';
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
abstract class IdQueryParameter extends Equatable {
|
||||
final int? _assignmentStatus;
|
||||
final int? _id;
|
||||
|
||||
const IdQueryParameter.notAssigned()
|
||||
: _assignmentStatus = 1,
|
||||
_id = null;
|
||||
|
||||
const IdQueryParameter.anyAssigned()
|
||||
: _assignmentStatus = 0,
|
||||
_id = null;
|
||||
|
||||
const IdQueryParameter.fromId(int? id)
|
||||
: _assignmentStatus = null,
|
||||
_id = id;
|
||||
|
||||
const IdQueryParameter.unset() : this.fromId(null);
|
||||
|
||||
bool get isUnset => _id == null && _assignmentStatus == null;
|
||||
|
||||
bool get isSet => _id != null && _assignmentStatus == null;
|
||||
|
||||
bool get onlyNotAssigned => _assignmentStatus == 1;
|
||||
|
||||
bool get onlyAssigned => _assignmentStatus == 0;
|
||||
|
||||
int? get id => _id;
|
||||
|
||||
@protected
|
||||
String get queryParameterKey;
|
||||
|
||||
String toQueryParameter() {
|
||||
if (onlyNotAssigned || onlyAssigned) {
|
||||
return "&${queryParameterKey}__isnull=$_assignmentStatus";
|
||||
}
|
||||
if (isSet) {
|
||||
return "&${queryParameterKey}__id=$id";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [_assignmentStatus, _id];
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
enum QueryType {
|
||||
title('title__icontains'),
|
||||
titleAndContent('title_content'),
|
||||
extended('query'),
|
||||
asn('asn');
|
||||
|
||||
final String queryParam;
|
||||
const QueryType(this.queryParam);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
enum SortField {
|
||||
archiveSerialNumber("archive_serial_number"),
|
||||
correspondentName("correspondent__name"),
|
||||
title("title"),
|
||||
documentType("document_type__name"),
|
||||
created("created"),
|
||||
added("added"),
|
||||
modified("modified");
|
||||
|
||||
final String queryString;
|
||||
|
||||
const SortField(this.queryString);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
enum SortOrder {
|
||||
ascending(""),
|
||||
descending("-");
|
||||
|
||||
final String queryString;
|
||||
const SortOrder(this.queryString);
|
||||
|
||||
SortOrder toggle() {
|
||||
return this == ascending ? descending : ascending;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class StoragePathQuery extends IdQueryParameter {
|
||||
const StoragePathQuery.fromId(super.id) : super.fromId();
|
||||
const StoragePathQuery.unset() : super.unset();
|
||||
const StoragePathQuery.notAssigned() : super.notAssigned();
|
||||
const StoragePathQuery.anyAssigned() : super.anyAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'storage_path';
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class TagsQuery with EquatableMixin {
|
||||
const TagsQuery();
|
||||
String toQueryParameter();
|
||||
}
|
||||
|
||||
class OnlyNotAssignedTagsQuery extends TagsQuery {
|
||||
const OnlyNotAssignedTagsQuery();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
|
||||
@override
|
||||
String toQueryParameter() {
|
||||
return '&is_tagged=0';
|
||||
}
|
||||
}
|
||||
|
||||
class AnyAssignedTagsQuery extends TagsQuery {
|
||||
final Iterable<int> tagIds;
|
||||
|
||||
const AnyAssignedTagsQuery({
|
||||
this.tagIds = const [],
|
||||
});
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
|
||||
@override
|
||||
String toQueryParameter() {
|
||||
if (tagIds.isEmpty) {
|
||||
return '&is_tagged=1';
|
||||
}
|
||||
return '&tags__id__in=${tagIds.join(',')}';
|
||||
}
|
||||
}
|
||||
|
||||
class IdsTagsQuery extends TagsQuery {
|
||||
final Iterable<TagIdQuery> _idQueries;
|
||||
|
||||
const IdsTagsQuery([this._idQueries = const []]);
|
||||
|
||||
IdsTagsQuery.included(Iterable<int> ids)
|
||||
: _idQueries = ids.map((id) => IncludeTagIdQuery(id));
|
||||
|
||||
IdsTagsQuery.fromIds(Iterable<int> ids) : this.included(ids);
|
||||
|
||||
IdsTagsQuery.excluded(Iterable<int> ids)
|
||||
: _idQueries = ids.map((id) => ExcludeTagIdQuery(id));
|
||||
|
||||
IdsTagsQuery withIdQueriesAdded(Iterable<TagIdQuery> idQueries) {
|
||||
final intersection = idQueries
|
||||
.map((idQ) => idQ.id)
|
||||
.toSet()
|
||||
.intersection(_idQueries.map((idQ) => idQ.id).toSet());
|
||||
final res = IdsTagsQuery(
|
||||
[...withIdsRemoved(intersection).queries, ...idQueries],
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
IdsTagsQuery withIdsRemoved(Iterable<int> ids) {
|
||||
return IdsTagsQuery(
|
||||
_idQueries.where((idQuery) => !ids.contains(idQuery.id)),
|
||||
);
|
||||
}
|
||||
|
||||
Iterable<TagIdQuery> get queries => _idQueries;
|
||||
|
||||
Iterable<int> get includedIds {
|
||||
return _idQueries.whereType<IncludeTagIdQuery>().map((e) => e.id);
|
||||
}
|
||||
|
||||
Iterable<int> get excludedIds {
|
||||
return _idQueries.whereType<ExcludeTagIdQuery>().map((e) => e.id);
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a new instance with the type of the given [id] toggled.
|
||||
/// E.g. if the provided [id] is currently registered as a [IncludeTagIdQuery],
|
||||
/// then the new isntance will contain a [ExcludeTagIdQuery] with given id.
|
||||
///
|
||||
IdsTagsQuery withIdQueryToggled(int id) {
|
||||
return IdsTagsQuery(
|
||||
_idQueries.map((idQ) => idQ.id == id ? idQ.toggle() : idQ),
|
||||
);
|
||||
}
|
||||
|
||||
Iterable<int> get ids => [...includedIds, ...excludedIds];
|
||||
|
||||
@override
|
||||
String toQueryParameter() {
|
||||
final StringBuffer sb = StringBuffer("");
|
||||
if (includedIds.isNotEmpty) {
|
||||
sb.write('&tags__id__all=${includedIds.join(',')}');
|
||||
}
|
||||
if (excludedIds.isNotEmpty) {
|
||||
sb.write('&tags__id__none=${excludedIds.join(',')}');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [_idQueries];
|
||||
}
|
||||
|
||||
abstract class TagIdQuery with EquatableMixin {
|
||||
final int id;
|
||||
|
||||
TagIdQuery(this.id);
|
||||
|
||||
String get methodName;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, methodName];
|
||||
|
||||
TagIdQuery toggle();
|
||||
}
|
||||
|
||||
class IncludeTagIdQuery extends TagIdQuery {
|
||||
IncludeTagIdQuery(super.id);
|
||||
|
||||
@override
|
||||
String get methodName => 'include';
|
||||
|
||||
@override
|
||||
TagIdQuery toggle() {
|
||||
return ExcludeTagIdQuery(id);
|
||||
}
|
||||
}
|
||||
|
||||
class ExcludeTagIdQuery extends TagIdQuery {
|
||||
ExcludeTagIdQuery(super.id);
|
||||
|
||||
@override
|
||||
String get methodName => 'exclude';
|
||||
|
||||
@override
|
||||
TagIdQuery toggle() {
|
||||
return IncludeTagIdQuery(id);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/filter_rule.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
|
||||
class SavedView with EquatableMixin {
|
||||
final int? id;
|
||||
final String name;
|
||||
|
||||
final bool showOnDashboard;
|
||||
final bool showInSidebar;
|
||||
|
||||
final SortField sortField;
|
||||
final bool sortReverse;
|
||||
final List<FilterRule> filterRules;
|
||||
|
||||
SavedView({
|
||||
this.id,
|
||||
required this.name,
|
||||
required this.showOnDashboard,
|
||||
required this.showInSidebar,
|
||||
required this.sortField,
|
||||
required this.sortReverse,
|
||||
required this.filterRules,
|
||||
}) {
|
||||
filterRules.sort(
|
||||
(a, b) => (a.ruleType.compareTo(b.ruleType) != 0
|
||||
? a.ruleType.compareTo(b.ruleType)
|
||||
: a.value?.compareTo(b.value ?? "") ?? -1),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
name,
|
||||
showOnDashboard,
|
||||
showInSidebar,
|
||||
sortField,
|
||||
sortReverse,
|
||||
filterRules
|
||||
];
|
||||
|
||||
SavedView.fromJson(JSON json)
|
||||
: this(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
showOnDashboard: json['show_on_dashboard'],
|
||||
showInSidebar: json['show_in_sidebar'],
|
||||
sortField: SortField.values
|
||||
.where((order) => order.queryString == json['sort_field'])
|
||||
.first,
|
||||
sortReverse: json['sort_reverse'],
|
||||
filterRules: (json['filter_rules'] as List)
|
||||
.cast<JSON>()
|
||||
.map(FilterRule.fromJson)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
DocumentFilter toDocumentFilter() {
|
||||
return filterRules.fold(
|
||||
DocumentFilter(
|
||||
sortOrder: sortReverse ? SortOrder.descending : SortOrder.ascending,
|
||||
sortField: sortField,
|
||||
),
|
||||
(filter, filterRule) => filterRule.applyToFilter(filter),
|
||||
);
|
||||
}
|
||||
|
||||
SavedView.fromDocumentFilter(
|
||||
DocumentFilter filter, {
|
||||
required String name,
|
||||
required bool showInSidebar,
|
||||
required bool showOnDashboard,
|
||||
}) : this(
|
||||
id: null,
|
||||
name: name,
|
||||
filterRules: FilterRule.fromFilter(filter),
|
||||
sortField: filter.sortField,
|
||||
showInSidebar: showInSidebar,
|
||||
showOnDashboard: showOnDashboard,
|
||||
sortReverse: filter.sortOrder == SortOrder.descending,
|
||||
);
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'show_on_dashboard': showOnDashboard,
|
||||
'show_in_sidebar': showInSidebar,
|
||||
'sort_reverse': sortReverse,
|
||||
'sort_field': sortField.queryString,
|
||||
'filter_rules': filterRules.map((rule) => rule.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
|
||||
class SimilarDocumentModel extends DocumentModel {
|
||||
final SearchHit searchHit;
|
||||
|
||||
const SimilarDocumentModel({
|
||||
required super.id,
|
||||
required super.title,
|
||||
required super.documentType,
|
||||
required super.correspondent,
|
||||
required super.created,
|
||||
required super.modified,
|
||||
required super.added,
|
||||
required super.originalFileName,
|
||||
required this.searchHit,
|
||||
super.archiveSerialNumber,
|
||||
super.archivedFileName,
|
||||
super.content,
|
||||
super.storagePath,
|
||||
super.tags,
|
||||
});
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
final json = super.toJson();
|
||||
json['__search_hit__'] = searchHit.toJson();
|
||||
return json;
|
||||
}
|
||||
|
||||
SimilarDocumentModel.fromJson(JSON json)
|
||||
: searchHit = SearchHit.fromJson(json),
|
||||
super.fromJson(json);
|
||||
}
|
||||
|
||||
class SearchHit {
|
||||
final double? score;
|
||||
final String? highlights;
|
||||
final int? rank;
|
||||
|
||||
SearchHit({
|
||||
this.score,
|
||||
required this.highlights,
|
||||
required this.rank,
|
||||
});
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'score': score,
|
||||
'highlights': highlights,
|
||||
'rank': rank,
|
||||
};
|
||||
}
|
||||
|
||||
SearchHit.fromJson(JSON json)
|
||||
: score = json['score'],
|
||||
highlights = json['highlights'],
|
||||
rank = json['rank'];
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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';
|
||||
import 'package:paperless_mobile/features/documents/model/paged_search_result.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/similar_document.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
|
||||
abstract class DocumentRepository {
|
||||
Future<void> create(
|
||||
Uint8List documentBytes,
|
||||
String filename, {
|
||||
required String title,
|
||||
int? documentType,
|
||||
int? correspondent,
|
||||
Iterable<int> tags = const [],
|
||||
DateTime? createdAt,
|
||||
});
|
||||
Future<DocumentModel> update(DocumentModel doc);
|
||||
Future<int> findNextAsn();
|
||||
Future<PagedSearchResult<DocumentModel>> find(DocumentFilter filter);
|
||||
Future<List<SimilarDocumentModel>> findSimilar(int docId);
|
||||
Future<int> delete(DocumentModel doc);
|
||||
Future<DocumentMetaData> getMetaData(DocumentModel document);
|
||||
Future<Iterable<int>> bulkAction(BulkAction action);
|
||||
Future<Uint8List> getPreview(int docId);
|
||||
String getThumbnailUrl(int docId);
|
||||
Future<DocumentModel> waitForConsumptionFinished(
|
||||
String filename, String title);
|
||||
Future<Uint8List> download(DocumentModel document);
|
||||
|
||||
Future<List<String>> autocomplete(String query, [int limit = 10]);
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:http/src/boundary_characters.dart'; //TODO: remove once there is either a paperless API update or there is a better solution...
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/core/util.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.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/document_meta_data.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/paged_search_result.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/asn_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/similar_document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@Injectable(as: DocumentRepository)
|
||||
class DocumentRepositoryImpl implements DocumentRepository {
|
||||
////
|
||||
//final StatusService statusService;
|
||||
final LocalVault localStorage;
|
||||
final BaseClient httpClient;
|
||||
|
||||
DocumentRepositoryImpl(
|
||||
//this.statusService,
|
||||
this.localStorage,
|
||||
@Named("timeoutClient") this.httpClient,
|
||||
);
|
||||
@override
|
||||
Future<void> create(
|
||||
Uint8List documentBytes,
|
||||
String filename, {
|
||||
required String title,
|
||||
int? documentType,
|
||||
int? correspondent,
|
||||
Iterable<int> tags = const [],
|
||||
DateTime? createdAt,
|
||||
}) async {
|
||||
final auth = await localStorage.loadAuthenticationInformation();
|
||||
|
||||
if (auth == null) {
|
||||
throw const ErrorMessage(ErrorCode.notAuthenticated);
|
||||
}
|
||||
|
||||
// The multipart request has to be generated from scratch as the http library does
|
||||
// not allow the same key (tags) to be added multiple times. However, this is what the
|
||||
// paperless api expects, i.e. one block for each tag.
|
||||
final request = await getIt<HttpClient>().postUrl(
|
||||
Uri.parse("${auth.serverUrl}/api/documents/post_document/"),
|
||||
);
|
||||
|
||||
final boundary = _boundaryString();
|
||||
|
||||
StringBuffer bodyBuffer = StringBuffer();
|
||||
|
||||
var fields = <String, String>{};
|
||||
|
||||
fields.tryPutIfAbsent('title', () => title);
|
||||
fields.tryPutIfAbsent('created', () => formatDateNullable(createdAt));
|
||||
fields.tryPutIfAbsent('correspondent',
|
||||
() => correspondent == null ? null : json.encode(correspondent));
|
||||
fields.tryPutIfAbsent('document_type',
|
||||
() => documentType == null ? null : json.encode(documentType));
|
||||
|
||||
for (final key in fields.keys) {
|
||||
bodyBuffer.write(_buildMultipartField(key, fields[key]!, boundary));
|
||||
}
|
||||
|
||||
for (final tag in tags) {
|
||||
bodyBuffer.write(_buildMultipartField('tags', tag.toString(), boundary));
|
||||
}
|
||||
|
||||
bodyBuffer.write("--$boundary"
|
||||
'\r\nContent-Disposition: form-data; name="document"; filename="$filename"'
|
||||
"\r\nContent-type: application/octet-stream"
|
||||
"\r\n\r\n");
|
||||
|
||||
final closing = "\r\n--" + boundary + "--\r\n";
|
||||
|
||||
// Set headers
|
||||
request.headers.set(HttpHeaders.contentTypeHeader,
|
||||
"multipart/form-data; boundary=" + boundary);
|
||||
request.headers.set(HttpHeaders.contentLengthHeader,
|
||||
"${bodyBuffer.length + closing.length + documentBytes.lengthInBytes}");
|
||||
request.headers.set(HttpHeaders.authorizationHeader, "Token ${auth.token}");
|
||||
|
||||
//Write fields to request
|
||||
request.write(bodyBuffer.toString());
|
||||
//Stream file
|
||||
await request.addStream(Stream.fromIterable(documentBytes.map((e) => [e])));
|
||||
// Write closing boundary to request
|
||||
request.write(closing);
|
||||
|
||||
final response = await request.close();
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ErrorMessage(ErrorCode.documentUploadFailed,
|
||||
httpStatusCode: response.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
String _buildMultipartField(String fieldName, String value, String boundary) {
|
||||
return '--$boundary'
|
||||
'\r\nContent-Disposition: form-data; name="$fieldName"'
|
||||
'\r\nContent-type: text/plain'
|
||||
'\r\n\r\n' +
|
||||
value +
|
||||
'\r\n';
|
||||
}
|
||||
|
||||
String _boundaryString() {
|
||||
Random _random = Random();
|
||||
var prefix = 'dart-http-boundary-';
|
||||
var list = List<int>.generate(
|
||||
70 - prefix.length,
|
||||
(index) => boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
|
||||
growable: false,
|
||||
);
|
||||
return '$prefix${String.fromCharCodes(list)}';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentModel> update(DocumentModel doc) async {
|
||||
final response = await httpClient.put(
|
||||
Uri.parse("/api/documents/${doc.id}/"),
|
||||
body: json.encode(doc.toJson()),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return DocumentModel.fromJson(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)) as JSON,
|
||||
);
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.documentUpdateFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PagedSearchResult<DocumentModel>> find(DocumentFilter filter) async {
|
||||
final filterParams = filter.toQueryString();
|
||||
final response = await httpClient.get(
|
||||
Uri.parse("/api/documents/?$filterParams"),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return compute(
|
||||
PagedSearchResult.fromJson,
|
||||
PagedSearchResultJsonSerializer<DocumentModel>(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)),
|
||||
DocumentModel.fromJson,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.documentLoadFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> delete(DocumentModel doc) async {
|
||||
final response =
|
||||
await httpClient.delete(Uri.parse("/api/documents/${doc.id}/"));
|
||||
|
||||
if (response.statusCode == 204) {
|
||||
return Future.value(doc.id);
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.documentDeleteFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
String getThumbnailUrl(int documentId) {
|
||||
return "/api/documents/$documentId/thumb/";
|
||||
}
|
||||
|
||||
String getPreviewUrl(int documentId) {
|
||||
return "/api/documents/$documentId/preview/";
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> getPreview(int documentId) async {
|
||||
final response = await httpClient.get(Uri.parse(getPreviewUrl(documentId)));
|
||||
if (response.statusCode == 200) {
|
||||
return response.bodyBytes;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.documentPreviewFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> findNextAsn() async {
|
||||
const DocumentFilter asnQueryFilter = DocumentFilter(
|
||||
sortField: SortField.archiveSerialNumber,
|
||||
sortOrder: SortOrder.descending,
|
||||
asn: AsnQuery.anyAssigned(),
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
);
|
||||
try {
|
||||
final result = await find(asnQueryFilter);
|
||||
return result.results
|
||||
.map((e) => e.archiveSerialNumber)
|
||||
.firstWhere((asn) => asn != null, orElse: () => 0)! +
|
||||
1;
|
||||
} on ErrorMessage catch (_) {
|
||||
throw const ErrorMessage(ErrorCode.documentAsnQueryFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
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 action.documentIds;
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.documentBulkActionFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentModel> waitForConsumptionFinished(
|
||||
String fileName, String title) async {
|
||||
// Always wait 5 seconds, processing usually takes longer...
|
||||
//await Future.delayed(const Duration(seconds: 5));
|
||||
PagedSearchResult<DocumentModel> results =
|
||||
await find(DocumentFilter.latestDocument);
|
||||
|
||||
while ((results.results.isEmpty ||
|
||||
(results.results[0].originalFileName != fileName &&
|
||||
results.results[0].title != title))) {
|
||||
//TODO: maybe implement more intelligent retry logic or find workaround for websocket authentication...
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
results = await find(DocumentFilter.latestDocument);
|
||||
}
|
||||
try {
|
||||
return results.results.first;
|
||||
} on StateError {
|
||||
throw const ErrorMessage(ErrorCode.documentUploadFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> download(DocumentModel document) async {
|
||||
//TODO: Check if this works...
|
||||
final response = await httpClient
|
||||
.get(Uri.parse("/api/documents/${document.id}/download/"));
|
||||
return response.bodyBytes;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentMetaData> getMetaData(DocumentModel document) async {
|
||||
final response = await httpClient
|
||||
.get(Uri.parse("/api/documents/${document.id}/metadata/"));
|
||||
return compute(
|
||||
DocumentMetaData.fromJson,
|
||||
jsonDecode(utf8.decode(response.bodyBytes)) as JSON,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> autocomplete(String query, [int limit = 10]) async {
|
||||
final response = await httpClient
|
||||
.get(Uri.parse("/api/search/autocomplete/?query=$query&limit=$limit}"));
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(utf8.decode(response.bodyBytes)) as List<String>;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.autocompleteQueryError);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<SimilarDocumentModel>> findSimilar(int docId) async {
|
||||
final response = await httpClient
|
||||
.get(Uri.parse("/api/documents/?more_like=$docId&pageSize=10"));
|
||||
if (response.statusCode == 200) {
|
||||
return (await compute(
|
||||
PagedSearchResult<SimilarDocumentModel>.fromJson,
|
||||
PagedSearchResultJsonSerializer(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)),
|
||||
SimilarDocumentModel.fromJson,
|
||||
),
|
||||
))
|
||||
.results;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.similarQueryError);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/util.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/saved_view.model.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
abstract class SavedViewsRepository {
|
||||
Future<List<SavedView>> getAll();
|
||||
|
||||
Future<SavedView> save(SavedView view);
|
||||
Future<int> delete(SavedView view);
|
||||
}
|
||||
|
||||
@Injectable(as: SavedViewsRepository)
|
||||
class SavedViewRepositoryImpl implements SavedViewsRepository {
|
||||
final BaseClient httpClient;
|
||||
|
||||
SavedViewRepositoryImpl(@Named("timeoutClient") this.httpClient);
|
||||
|
||||
@override
|
||||
Future<List<SavedView>> getAll() {
|
||||
return getCollection(
|
||||
"/api/saved_views/",
|
||||
SavedView.fromJson,
|
||||
ErrorCode.loadSavedViewsError,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<SavedView> save(SavedView view) async {
|
||||
final response = await httpClient.post(
|
||||
Uri.parse("/api/saved_views/"),
|
||||
body: jsonEncode(view.toJson()),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
if (response.statusCode == 201) {
|
||||
return SavedView.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw ErrorMessage(ErrorCode.createSavedViewError,
|
||||
httpStatusCode: response.statusCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> delete(SavedView view) async {
|
||||
final response =
|
||||
await httpClient.delete(Uri.parse("/api/saved_views/${view.id}/"));
|
||||
if (response.statusCode == 204) {
|
||||
return view.id!;
|
||||
}
|
||||
throw ErrorMessage(ErrorCode.deleteSavedViewError,
|
||||
httpStatusCode: response.statusCode);
|
||||
}
|
||||
}
|
||||
@@ -6,26 +6,15 @@ 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_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.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/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.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/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/bloc/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';
|
||||
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';
|
||||
@@ -62,7 +51,8 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
documentBytes = getIt<DocumentRepository>().getPreview(widget.document.id);
|
||||
documentBytes =
|
||||
getIt<PaperlessDocumentsApi>().getPreview(widget.document.id);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -92,7 +82,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
try {
|
||||
await widget.onEdit(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:pdfx/pdfx.dart';
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.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/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
|
||||
@@ -48,7 +45,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
super.initState();
|
||||
try {
|
||||
BlocProvider.of<DocumentsCubit>(context).load();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
_pagingController.addPageRequestListener(_loadNewPage);
|
||||
@@ -69,7 +66,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
}
|
||||
try {
|
||||
await documentsCubit.loadMore();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -83,7 +80,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
await BlocProvider.of<DocumentsCubit>(context).updateCurrentFilter(
|
||||
(filter) => filter.copyWith(page: 1),
|
||||
);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -254,7 +251,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
value: BlocProvider.of<StoragePathCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: DocumentDetailsCubit(getIt<DocumentRepository>(), document),
|
||||
value:
|
||||
DocumentDetailsCubit(getIt<PaperlessDocumentsApi>(), document),
|
||||
),
|
||||
],
|
||||
child: const DocumentDetailsPage(),
|
||||
@@ -281,7 +279,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class DeleteDocumentConfirmationDialog extends StatelessWidget {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class DocumentPreview extends StatelessWidget {
|
||||
@@ -30,7 +30,7 @@ class DocumentPreview extends StatelessWidget {
|
||||
fit: fit,
|
||||
alignment: Alignment.topCenter,
|
||||
cacheKey: "thumb_$id",
|
||||
imageUrl: getIt<DocumentRepository>().getThumbnailUrl(id),
|
||||
imageUrl: getIt<PaperlessDocumentsApi>().getThumbnailUrl(id),
|
||||
errorWidget: (ctxt, msg, __) => Text(msg),
|
||||
placeholder: (context, value) => Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/empty_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_filter.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentsEmptyState extends StatelessWidget {
|
||||
final DocumentsState state;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.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/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid_item.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart';
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.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/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
|
||||
class DocumentListView extends StatelessWidget {
|
||||
final void Function(DocumentModel) onTap;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||
|
||||
class DocumentListItem extends StatelessWidget {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class OrderByDropdown extends StatefulWidget {
|
||||
static const fkOrderBy = "orderBy";
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.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/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.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/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/bloc/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';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
||||
@@ -468,7 +458,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
BlocProvider.of<SavedViewCubit>(context).resetSelection();
|
||||
FocusScope.of(context).unfocus();
|
||||
widget.panelController.close();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class QueryTypeFormField extends StatelessWidget {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
@@ -20,16 +17,6 @@ class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
|
||||
class _SortFieldSelectionBottomSheetState
|
||||
extends State<SortFieldSelectionBottomSheet> {
|
||||
static const _sortFields = [
|
||||
SortField.created,
|
||||
SortField.added,
|
||||
SortField.modified,
|
||||
SortField.title,
|
||||
SortField.correspondentName,
|
||||
SortField.documentType,
|
||||
SortField.archiveSerialNumber
|
||||
];
|
||||
|
||||
SortField? _selectedFieldLoading;
|
||||
SortOrder? _selectedOrderLoading;
|
||||
|
||||
@@ -49,7 +36,7 @@ class _SortFieldSelectionBottomSheetState
|
||||
).padded(
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16)),
|
||||
Column(
|
||||
children: _sortFields
|
||||
children: SortField.values
|
||||
.map(
|
||||
(e) => _buildSortOption(
|
||||
e,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.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/generated/l10n.dart';
|
||||
|
||||
class BulkDeleteConfirmationDialog extends StatelessWidget {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/saved_view.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class ConfirmDeleteSavedViewDialog extends StatelessWidget {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.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/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/saved_view_selection_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_selection_widget.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@@ -104,7 +102,7 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
context,
|
||||
S.of(context).documentsPageBulkDeleteSuccessfulText,
|
||||
);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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';
|
||||
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/query_parameters/sort_field.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class SortDocumentsButton extends StatefulWidget {
|
||||
const SortDocumentsButton({
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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/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';
|
||||
@@ -90,7 +89,7 @@ class _HomePageState extends State<HomePage> {
|
||||
getIt<StoragePathCubit>().initialize(),
|
||||
getIt<SavedViewCubit>().initialize(),
|
||||
]);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
return Future.error(error);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
@@ -67,8 +67,12 @@ class InfoDrawer extends StatelessWidget {
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: BlocBuilder<PaperlessServerInformationCubit,
|
||||
PaperlessServerInformation>(
|
||||
PaperlessServerInformationState>(
|
||||
builder: (context, state) {
|
||||
if (!state.isLoaded) {
|
||||
return Container();
|
||||
}
|
||||
final info = state.information!;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
@@ -77,7 +81,7 @@ class InfoDrawer extends StatelessWidget {
|
||||
dense: true,
|
||||
title: Text(
|
||||
S.of(context).appDrawerHeaderLoggedInAsText +
|
||||
(state.username ?? '?'),
|
||||
(info.username ?? '?'),
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.end,
|
||||
@@ -87,7 +91,7 @@ class InfoDrawer extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
state.host ?? '',
|
||||
state.information!.host ?? '',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyText2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -95,7 +99,7 @@ class InfoDrawer extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
),
|
||||
Text(
|
||||
'${S.of(context).serverInformationPaperlessVersionText} ${state.version} (API v${state.apiVersion})',
|
||||
'${S.of(context).serverInformationPaperlessVersionText} ${info.version} (API v${info.apiVersion})',
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.end,
|
||||
@@ -189,7 +193,7 @@ class InfoDrawer extends StatelessWidget {
|
||||
getIt<DocumentTypeCubit>().reset();
|
||||
getIt<TagCubit>().reset();
|
||||
getIt<DocumentScannerCubit>().reset();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
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_api/paperless_api.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;
|
||||
final PaperlessLabelsApi _labelApi;
|
||||
final PaperlessDocumentsApi _documentsApi;
|
||||
|
||||
InboxCubit(this._labelRepository, this._documentRepository)
|
||||
: super(const InboxState());
|
||||
InboxCubit(this._labelApi, this._documentsApi) : super(const InboxState());
|
||||
|
||||
///
|
||||
/// Fetches inbox tag ids and loads the inbox items (documents).
|
||||
///
|
||||
Future<void> loadInbox() async {
|
||||
final inboxTags = await _labelRepository.getTags().then(
|
||||
final inboxTags = await _labelApi.getTags().then(
|
||||
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
||||
);
|
||||
if (inboxTags.isEmpty) {
|
||||
@@ -33,7 +25,7 @@ class InboxCubit extends Cubit<InboxState> {
|
||||
inboxTags: [],
|
||||
));
|
||||
}
|
||||
final inboxDocuments = await _documentRepository
|
||||
final inboxDocuments = await _documentsApi
|
||||
.find(DocumentFilter(
|
||||
tags: AnyAssignedTagsQuery(tagIds: inboxTags),
|
||||
sortField: SortField.added,
|
||||
@@ -57,7 +49,7 @@ class InboxCubit extends Cubit<InboxState> {
|
||||
|
||||
final updatedTags = {...document.tags}..removeAll(tagsToRemove);
|
||||
|
||||
await _documentRepository.update(
|
||||
await _documentsApi.update(
|
||||
document.copyWith(
|
||||
tags: updatedTags,
|
||||
overwriteTags: true,
|
||||
@@ -85,7 +77,7 @@ class InboxCubit extends Cubit<InboxState> {
|
||||
tags: {...document.tags, ...removedTags},
|
||||
overwriteTags: true,
|
||||
);
|
||||
await _documentRepository.update(updatedDoc);
|
||||
await _documentsApi.update(updatedDoc);
|
||||
emit(
|
||||
InboxState(
|
||||
isLoaded: true,
|
||||
@@ -100,7 +92,7 @@ class InboxCubit extends Cubit<InboxState> {
|
||||
/// Removes inbox tags from all documents in the inbox.
|
||||
///
|
||||
Future<void> clearInbox() async {
|
||||
await _documentRepository.bulkAction(
|
||||
await _documentsApi.bulkAction(
|
||||
BulkModifyTagsAction.removeTags(
|
||||
state.inboxItems.map((e) => e.id),
|
||||
state.inboxTags,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class InboxState with EquatableMixin {
|
||||
final bool isLoaded;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import 'package:collection/collection.dart';
|
||||
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/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.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/inbox_item.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
|
||||
class InboxPage extends StatefulWidget {
|
||||
const InboxPage({super.key});
|
||||
@@ -230,13 +229,13 @@ class _InboxPageState extends State<InboxPage> {
|
||||
),
|
||||
);
|
||||
return true;
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
return false;
|
||||
} catch (error) {
|
||||
showErrorMessage(
|
||||
context,
|
||||
const ErrorMessage.unknown(),
|
||||
const PaperlessServerException.unknown(),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -249,7 +248,7 @@ class _InboxPageState extends State<InboxPage> {
|
||||
try {
|
||||
await BlocProvider.of<InboxCubit>(context)
|
||||
.undoRemove(document, removedTags);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class InboxEmptyWidget extends StatelessWidget {
|
||||
const InboxEmptyWidget({
|
||||
@@ -22,11 +23,11 @@ class InboxEmptyWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('You do not have unseen documents.'),
|
||||
Text(S.of(context).inboxPageNoNewDocumentsText),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
_emptyStateRefreshIndicatorKey.currentState?.show(),
|
||||
child: Text('Refresh'),
|
||||
child: Text(S.of(context).inboxPageNoNewDocumentsRefreshLabel),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.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/document_details/view/pages/document_details_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.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';
|
||||
@@ -54,7 +53,7 @@ class InboxItem extends StatelessWidget {
|
||||
additionalProviders: [
|
||||
BlocProvider<DocumentDetailsCubit>(
|
||||
create: (context) => DocumentDetailsCubit(
|
||||
getIt<DocumentRepository>(),
|
||||
getIt<PaperlessDocumentsApi>(),
|
||||
document,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
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';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
|
||||
abstract class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
||||
final LabelRepository labelRepository;
|
||||
final PaperlessLabelsApi labelsApi;
|
||||
|
||||
LabelCubit(this.labelRepository) : super(LabelState.initial());
|
||||
LabelCubit(this.labelsApi) : super(LabelState.initial());
|
||||
|
||||
@protected
|
||||
void loadFrom(Iterable<T> items) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class LabelState<T extends Label> {
|
||||
LabelState.initial() : this(isLoaded: false, labels: {});
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
|
||||
@singleton
|
||||
@@ -8,18 +8,17 @@ class CorrespondentCubit extends LabelCubit<Correspondent> {
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
return labelRepository.getCorrespondents().then(loadFrom);
|
||||
return labelsApi.getCorrespondents().then(loadFrom);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Correspondent> save(Correspondent item) =>
|
||||
labelRepository.saveCorrespondent(item);
|
||||
labelsApi.saveCorrespondent(item);
|
||||
|
||||
@override
|
||||
Future<Correspondent> update(Correspondent item) =>
|
||||
labelRepository.updateCorrespondent(item);
|
||||
labelsApi.updateCorrespondent(item);
|
||||
|
||||
@override
|
||||
Future<int> delete(Correspondent item) =>
|
||||
labelRepository.deleteCorrespondent(item);
|
||||
Future<int> delete(Correspondent item) => labelsApi.deleteCorrespondent(item);
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class Correspondent extends Label {
|
||||
static const lastCorrespondenceKey = 'last_correspondence';
|
||||
|
||||
late DateTime? lastCorrespondence;
|
||||
|
||||
Correspondent({
|
||||
required super.id,
|
||||
required super.name,
|
||||
super.slug,
|
||||
super.match,
|
||||
super.matchingAlgorithm,
|
||||
super.isInsensitive,
|
||||
super.documentCount,
|
||||
this.lastCorrespondence,
|
||||
});
|
||||
|
||||
Correspondent.fromJson(JSON json)
|
||||
: lastCorrespondence =
|
||||
DateTime.tryParse(json[lastCorrespondenceKey] ?? ''),
|
||||
super.fromJson(json);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@override
|
||||
void addSpecificFieldsToJson(JSON json) {
|
||||
json.tryPutIfAbsent(
|
||||
lastCorrespondenceKey,
|
||||
() => lastCorrespondence?.toIso8601String(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Correspondent copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
String? slug,
|
||||
String? match,
|
||||
MatchingAlgorithm? matchingAlgorithm,
|
||||
bool? isInsensitive,
|
||||
int? documentCount,
|
||||
DateTime? lastCorrespondence,
|
||||
}) {
|
||||
return Correspondent(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
documentCount: documentCount ?? documentCount,
|
||||
isInsensitive: isInsensitive ?? isInsensitive,
|
||||
lastCorrespondence: lastCorrespondence ?? this.lastCorrespondence,
|
||||
match: match ?? this.match,
|
||||
matchingAlgorithm: matchingAlgorithm ?? this.matchingAlgorithm,
|
||||
slug: slug ?? this.slug,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get queryEndpoint => 'correspondents';
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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/view/pages/add_label_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@@ -38,7 +35,7 @@ class EditCorrespondentPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class CorrespondentWidget extends StatelessWidget {
|
||||
@@ -60,7 +57,7 @@ class CorrespondentWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
|
||||
@singleton
|
||||
@@ -9,18 +8,17 @@ class DocumentTypeCubit extends LabelCubit<DocumentType> {
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
labelRepository.getDocumentTypes().then(loadFrom);
|
||||
labelsApi.getDocumentTypes().then(loadFrom);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentType> save(DocumentType item) =>
|
||||
labelRepository.saveDocumentType(item);
|
||||
labelsApi.saveDocumentType(item);
|
||||
|
||||
@override
|
||||
Future<DocumentType> update(DocumentType item) =>
|
||||
labelRepository.updateDocumentType(item);
|
||||
labelsApi.updateDocumentType(item);
|
||||
|
||||
@override
|
||||
Future<int> delete(DocumentType item) =>
|
||||
labelRepository.deleteDocumentType(item);
|
||||
Future<int> delete(DocumentType item) => labelsApi.deleteDocumentType(item);
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class DocumentType extends Label {
|
||||
DocumentType({
|
||||
required super.id,
|
||||
required super.name,
|
||||
super.slug,
|
||||
super.match,
|
||||
super.matchingAlgorithm,
|
||||
super.isInsensitive,
|
||||
super.documentCount,
|
||||
});
|
||||
|
||||
DocumentType.fromJson(JSON json) : super.fromJson(json);
|
||||
|
||||
@override
|
||||
void addSpecificFieldsToJson(JSON json) {}
|
||||
|
||||
@override
|
||||
String get queryEndpoint => 'document_types';
|
||||
|
||||
@override
|
||||
DocumentType copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
String? match,
|
||||
MatchingAlgorithm? matchingAlgorithm,
|
||||
bool? isInsensitive,
|
||||
int? documentCount,
|
||||
String? slug,
|
||||
}) {
|
||||
return DocumentType(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
match: match ?? this.match,
|
||||
matchingAlgorithm: matchingAlgorithm ?? this.matchingAlgorithm,
|
||||
isInsensitive: isInsensitive ?? this.isInsensitive,
|
||||
documentCount: documentCount ?? this.documentCount,
|
||||
slug: slug ?? this.slug,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
enum MatchingAlgorithm {
|
||||
anyWord(1, "Any: Match one of the following words"),
|
||||
allWords(2, "All: Match all of the following words"),
|
||||
exactMatch(3, "Exact: Match the following string"),
|
||||
regex(4, "Regex: Match the regular expression"),
|
||||
similarWord(5, "Similar: Match a similar word"),
|
||||
auto(6, "Auto: Learn automatic assignment");
|
||||
|
||||
final int value;
|
||||
final String name;
|
||||
|
||||
const MatchingAlgorithm(this.value, this.name);
|
||||
|
||||
static MatchingAlgorithm fromInt(int? value) {
|
||||
return MatchingAlgorithm.values
|
||||
.where((element) => element.value == value)
|
||||
.firstWhere(
|
||||
(element) => true,
|
||||
orElse: () => MatchingAlgorithm.anyWord,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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/view/pages/add_label_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@@ -33,8 +30,8 @@ class EditDocumentTypePage extends StatelessWidget {
|
||||
.copyWith(documentType: const DocumentTypeQuery.unset()),
|
||||
);
|
||||
}
|
||||
} on ErrorMessage catch (e) {
|
||||
showSnackBar(context, translateError(context, e.code));
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentTypeWidget extends StatelessWidget {
|
||||
@@ -55,7 +53,7 @@ class DocumentTypeWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
|
||||
abstract class Label with EquatableMixin implements Comparable {
|
||||
static const idKey = "id";
|
||||
static const nameKey = "name";
|
||||
static const slugKey = "slug";
|
||||
static const matchKey = "match";
|
||||
static const matchingAlgorithmKey = "matching_algorithm";
|
||||
static const isInsensitiveKey = "is_insensitive";
|
||||
static const documentCountKey = "document_count";
|
||||
|
||||
String get queryEndpoint;
|
||||
|
||||
final int? id;
|
||||
final String name;
|
||||
final String? slug;
|
||||
final String? match;
|
||||
final MatchingAlgorithm? matchingAlgorithm;
|
||||
final bool? isInsensitive;
|
||||
final int? documentCount;
|
||||
|
||||
const Label({
|
||||
required this.id,
|
||||
required this.name,
|
||||
this.match,
|
||||
this.matchingAlgorithm,
|
||||
this.isInsensitive,
|
||||
this.documentCount,
|
||||
this.slug,
|
||||
});
|
||||
|
||||
Label.fromJson(JSON json)
|
||||
: id = json[idKey],
|
||||
name = json[nameKey],
|
||||
slug = json[slugKey],
|
||||
match = json[matchKey],
|
||||
matchingAlgorithm =
|
||||
MatchingAlgorithm.fromInt(json[matchingAlgorithmKey]),
|
||||
isInsensitive = json[isInsensitiveKey],
|
||||
documentCount = json[documentCountKey];
|
||||
|
||||
JSON toJson() {
|
||||
JSON json = {};
|
||||
json.tryPutIfAbsent(idKey, () => id);
|
||||
json.tryPutIfAbsent(nameKey, () => name);
|
||||
json.tryPutIfAbsent(slugKey, () => slug);
|
||||
json.tryPutIfAbsent(matchKey, () => match);
|
||||
json.tryPutIfAbsent(matchingAlgorithmKey, () => matchingAlgorithm?.value);
|
||||
json.tryPutIfAbsent(isInsensitiveKey, () => isInsensitive);
|
||||
json.tryPutIfAbsent(documentCountKey, () => documentCount);
|
||||
addSpecificFieldsToJson(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
void addSpecificFieldsToJson(JSON json);
|
||||
|
||||
Label copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
String? match,
|
||||
MatchingAlgorithm? matchingAlgorithm,
|
||||
bool? isInsensitive,
|
||||
int? documentCount,
|
||||
String? slug,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@override
|
||||
int compareTo(dynamic other) {
|
||||
return toString().toLowerCase().compareTo(other.toString().toLowerCase());
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id];
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
|
||||
abstract class LabelRepository {
|
||||
Future<Correspondent?> getCorrespondent(int id);
|
||||
Future<List<Correspondent>> getCorrespondents();
|
||||
Future<Correspondent> saveCorrespondent(Correspondent correspondent);
|
||||
Future<Correspondent> updateCorrespondent(Correspondent correspondent);
|
||||
Future<int> deleteCorrespondent(Correspondent correspondent);
|
||||
|
||||
Future<Tag?> getTag(int id);
|
||||
Future<List<Tag>> getTags({List<int>? ids});
|
||||
Future<Tag> saveTag(Tag tag);
|
||||
Future<Tag> updateTag(Tag tag);
|
||||
Future<int> deleteTag(Tag tag);
|
||||
|
||||
Future<DocumentType?> getDocumentType(int id);
|
||||
Future<List<DocumentType>> getDocumentTypes();
|
||||
Future<DocumentType> saveDocumentType(DocumentType type);
|
||||
Future<DocumentType> updateDocumentType(DocumentType documentType);
|
||||
Future<int> deleteDocumentType(DocumentType documentType);
|
||||
|
||||
Future<StoragePath?> getStoragePath(int id);
|
||||
Future<List<StoragePath>> getStoragePaths();
|
||||
Future<StoragePath> saveStoragePath(StoragePath path);
|
||||
Future<StoragePath> updateStoragePath(StoragePath path);
|
||||
Future<int> deleteStoragePath(StoragePath path);
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/util.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
@Singleton(as: LabelRepository)
|
||||
class LabelRepositoryImpl implements LabelRepository {
|
||||
final BaseClient httpClient;
|
||||
|
||||
LabelRepositoryImpl(@Named("timeoutClient") this.httpClient);
|
||||
|
||||
@override
|
||||
Future<Correspondent?> getCorrespondent(int id) async {
|
||||
return getSingleResult(
|
||||
"/api/correspondents/$id/",
|
||||
Correspondent.fromJson,
|
||||
ErrorCode.correspondentLoadFailed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Tag?> getTag(int id) async {
|
||||
return getSingleResult(
|
||||
"/api/tags/$id/", Tag.fromJson, ErrorCode.tagLoadFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Tag>> getTags({List<int>? ids}) async {
|
||||
final results = await getCollection(
|
||||
"/api/tags/?page=1&page_size=100000",
|
||||
Tag.fromJson,
|
||||
ErrorCode.tagLoadFailed,
|
||||
minRequiredApiVersion: 2,
|
||||
);
|
||||
return results
|
||||
.where((element) => ids?.contains(element.id) ?? true)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentType?> getDocumentType(int id) async {
|
||||
return getSingleResult(
|
||||
"/api/document_types/$id/",
|
||||
DocumentType.fromJson,
|
||||
ErrorCode.documentTypeLoadFailed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Correspondent>> getCorrespondents() {
|
||||
return getCollection(
|
||||
"/api/correspondents/?page=1&page_size=100000",
|
||||
Correspondent.fromJson,
|
||||
ErrorCode.correspondentLoadFailed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DocumentType>> getDocumentTypes() {
|
||||
return getCollection(
|
||||
"/api/document_types/?page=1&page_size=100000",
|
||||
DocumentType.fromJson,
|
||||
ErrorCode.documentTypeLoadFailed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Correspondent> saveCorrespondent(Correspondent correspondent) async {
|
||||
final response = await httpClient.post(
|
||||
Uri.parse('/api/correspondents/'),
|
||||
body: json.encode(correspondent.toJson()),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 201) {
|
||||
return Correspondent.fromJson(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw ErrorMessage(ErrorCode.correspondentCreateFailed,
|
||||
httpStatusCode: response.statusCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentType> saveDocumentType(DocumentType type) async {
|
||||
final response = await httpClient.post(
|
||||
Uri.parse('/api/document_types/'),
|
||||
body: json.encode(type.toJson()),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 201) {
|
||||
return DocumentType.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.documentTypeCreateFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Tag> saveTag(Tag tag) async {
|
||||
final body = json.encode(tag.toJson());
|
||||
final response = await httpClient.post(
|
||||
Uri.parse('/api/tags/'),
|
||||
body: body,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json; version=2",
|
||||
},
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 201) {
|
||||
return Tag.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.tagCreateFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> deleteCorrespondent(Correspondent correspondent) async {
|
||||
assert(correspondent.id != null);
|
||||
final response = await httpClient
|
||||
.delete(Uri.parse('/api/correspondents/${correspondent.id}/'));
|
||||
if (response.statusCode == 204) {
|
||||
return correspondent.id!;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> deleteDocumentType(DocumentType documentType) async {
|
||||
assert(documentType.id != null);
|
||||
final response = await httpClient
|
||||
.delete(Uri.parse('/api/document_types/${documentType.id}/'));
|
||||
if (response.statusCode == 204) {
|
||||
return documentType.id!;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> deleteTag(Tag tag) async {
|
||||
assert(tag.id != null);
|
||||
final response = await httpClient.delete(Uri.parse('/api/tags/${tag.id}/'));
|
||||
if (response.statusCode == 204) {
|
||||
return tag.id!;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Correspondent> updateCorrespondent(Correspondent correspondent) async {
|
||||
assert(correspondent.id != null);
|
||||
final response = await httpClient.put(
|
||||
Uri.parse('/api/correspondents/${correspondent.id}/'),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: json.encode(correspondent.toJson()),
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return Correspondent.fromJson(
|
||||
jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentType> updateDocumentType(DocumentType documentType) async {
|
||||
assert(documentType.id != null);
|
||||
final response = await httpClient.put(
|
||||
Uri.parse('/api/document_types/${documentType.id}/'),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: json.encode(documentType.toJson()),
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return DocumentType.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Tag> updateTag(Tag tag) async {
|
||||
assert(tag.id != null);
|
||||
final response = await httpClient.put(
|
||||
Uri.parse('/api/tags/${tag.id}/'),
|
||||
headers: {
|
||||
"Accept": "application/json; version=2",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: json.encode(tag.toJson()),
|
||||
encoding: Encoding.getByName("utf-8"),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return Tag.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> deleteStoragePath(StoragePath path) async {
|
||||
assert(path.id != null);
|
||||
final response =
|
||||
await httpClient.delete(Uri.parse('/api/storage_paths/${path.id}/'));
|
||||
if (response.statusCode == 204) {
|
||||
return path.id!;
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StoragePath?> getStoragePath(int id) {
|
||||
return getSingleResult("/api/storage_paths/?page=1&page_size=100000",
|
||||
StoragePath.fromJson, ErrorCode.storagePathLoadFailed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<StoragePath>> getStoragePaths() {
|
||||
return getCollection(
|
||||
"/api/storage_paths/?page=1&page_size=100000",
|
||||
StoragePath.fromJson,
|
||||
ErrorCode.storagePathLoadFailed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StoragePath> saveStoragePath(StoragePath path) async {
|
||||
final response = await httpClient.post(
|
||||
Uri.parse('/api/storage_paths/'),
|
||||
body: json.encode(path.toJson()),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
);
|
||||
if (response.statusCode == 201) {
|
||||
return StoragePath.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw ErrorMessage(ErrorCode.storagePathCreateFailed,
|
||||
httpStatusCode: response.statusCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StoragePath> updateStoragePath(StoragePath path) async {
|
||||
assert(path.id != null);
|
||||
final response = await httpClient.put(
|
||||
Uri.parse('/api/storage_paths/${path.id}/'),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: json.encode(path.toJson()),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return StoragePath.fromJson(jsonDecode(utf8.decode(response.bodyBytes)));
|
||||
}
|
||||
throw const ErrorMessage(ErrorCode.unknown);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_api/paperless_api.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
|
||||
class StoragePathCubit extends LabelCubit<StoragePath> {
|
||||
@@ -8,18 +8,16 @@ class StoragePathCubit extends LabelCubit<StoragePath> {
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
return labelRepository.getStoragePaths().then(loadFrom);
|
||||
return labelsApi.getStoragePaths().then(loadFrom);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StoragePath> save(StoragePath item) =>
|
||||
labelRepository.saveStoragePath(item);
|
||||
Future<StoragePath> save(StoragePath item) => labelsApi.saveStoragePath(item);
|
||||
|
||||
@override
|
||||
Future<StoragePath> update(StoragePath item) =>
|
||||
labelRepository.updateStoragePath(item);
|
||||
labelsApi.updateStoragePath(item);
|
||||
|
||||
@override
|
||||
Future<int> delete(StoragePath item) =>
|
||||
labelRepository.deleteStoragePath(item);
|
||||
Future<int> delete(StoragePath item) => labelsApi.deleteStoragePath(item);
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class StoragePath extends Label {
|
||||
static const pathKey = 'path';
|
||||
|
||||
late String? path;
|
||||
|
||||
StoragePath({
|
||||
required super.id,
|
||||
required super.name,
|
||||
super.slug,
|
||||
super.match,
|
||||
super.matchingAlgorithm,
|
||||
super.isInsensitive,
|
||||
super.documentCount,
|
||||
required this.path,
|
||||
});
|
||||
|
||||
StoragePath.fromJson(JSON json)
|
||||
: path = json[pathKey],
|
||||
super.fromJson(json);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@override
|
||||
void addSpecificFieldsToJson(JSON json) {
|
||||
json.tryPutIfAbsent(
|
||||
pathKey,
|
||||
() => path,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
StoragePath copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
String? slug,
|
||||
String? match,
|
||||
MatchingAlgorithm? matchingAlgorithm,
|
||||
bool? isInsensitive,
|
||||
int? documentCount,
|
||||
String? path,
|
||||
}) {
|
||||
return StoragePath(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
documentCount: documentCount ?? documentCount,
|
||||
isInsensitive: isInsensitive ?? isInsensitive,
|
||||
path: path ?? this.path,
|
||||
match: match ?? this.match,
|
||||
matchingAlgorithm: matchingAlgorithm ?? this.matchingAlgorithm,
|
||||
slug: slug ?? this.slug,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get queryEndpoint => 'storage_paths';
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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/widgets/storage_path_autofill_form_builder_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
@@ -43,7 +40,7 @@ class EditStoragePathPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.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/bloc/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';
|
||||
|
||||
class StoragePathWidget extends StatelessWidget {
|
||||
@@ -59,7 +57,7 @@ class StoragePathWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
afterSelected?.call();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
|
||||
@singleton
|
||||
@@ -8,15 +8,15 @@ class TagCubit extends LabelCubit<Tag> {
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
return labelRepository.getTags().then(loadFrom);
|
||||
return labelsApi.getTags().then(loadFrom);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Tag> save(Tag item) => labelRepository.saveTag(item);
|
||||
Future<Tag> save(Tag item) => labelsApi.saveTag(item);
|
||||
|
||||
@override
|
||||
Future<Tag> update(Tag item) => labelRepository.updateTag(item);
|
||||
Future<Tag> update(Tag item) => labelsApi.updateTag(item);
|
||||
|
||||
@override
|
||||
Future<int> delete(Tag item) => labelRepository.deleteTag(item);
|
||||
Future<int> delete(Tag item) => labelsApi.deleteTag(item);
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
|
||||
class Tag extends Label {
|
||||
static const colorKey = 'color';
|
||||
static const isInboxTagKey = 'is_inbox_tag';
|
||||
static const textColorKey = 'text_color';
|
||||
static const legacyColourKey = 'colour';
|
||||
|
||||
final Color? color;
|
||||
final Color? textColor;
|
||||
final bool? isInboxTag;
|
||||
|
||||
Tag({
|
||||
required super.id,
|
||||
required super.name,
|
||||
super.documentCount,
|
||||
super.isInsensitive,
|
||||
super.match,
|
||||
super.matchingAlgorithm,
|
||||
super.slug,
|
||||
this.color,
|
||||
this.textColor,
|
||||
this.isInboxTag,
|
||||
});
|
||||
|
||||
Tag.fromJson(JSON json)
|
||||
: isInboxTag = json[isInboxTagKey],
|
||||
textColor = Color(_colorStringToInt(json[textColorKey]) ?? 0),
|
||||
color = _parseColorFromJson(json),
|
||||
super.fromJson(json);
|
||||
|
||||
///
|
||||
/// The `color` field of the json object can either be of type [Color] or a hex [String].
|
||||
/// Since API version 2, the old attribute `colour` has been replaced with `color`.
|
||||
///
|
||||
static Color _parseColorFromJson(JSON json) {
|
||||
if (json.containsKey(legacyColourKey)) {
|
||||
return Color(_colorStringToInt(json[legacyColourKey]) ?? 0);
|
||||
}
|
||||
if (json[colorKey] is Color) {
|
||||
return json[colorKey];
|
||||
}
|
||||
return Color(_colorStringToInt(json[colorKey]) ?? 0);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@override
|
||||
void addSpecificFieldsToJson(JSON json) {
|
||||
json.tryPutIfAbsent(colorKey, () => _toHex(color));
|
||||
json.tryPutIfAbsent(isInboxTagKey, () => isInboxTag);
|
||||
}
|
||||
|
||||
@override
|
||||
Tag copyWith({
|
||||
int? id,
|
||||
String? name,
|
||||
String? match,
|
||||
MatchingAlgorithm? matchingAlgorithm,
|
||||
bool? isInsensitive,
|
||||
int? documentCount,
|
||||
String? slug,
|
||||
Color? color,
|
||||
Color? textColor,
|
||||
bool? isInboxTag,
|
||||
}) {
|
||||
return Tag(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
match: match ?? this.match,
|
||||
matchingAlgorithm: matchingAlgorithm ?? this.matchingAlgorithm,
|
||||
isInsensitive: isInsensitive ?? this.isInsensitive,
|
||||
documentCount: documentCount ?? this.documentCount,
|
||||
slug: slug ?? this.slug,
|
||||
color: color ?? this.color,
|
||||
textColor: textColor ?? this.textColor,
|
||||
isInboxTag: isInboxTag ?? this.isInboxTag,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get queryEndpoint => 'tags';
|
||||
}
|
||||
|
||||
///
|
||||
/// Taken from [FormBuilderColorPicker].
|
||||
///
|
||||
String? _toHex(Color? color) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
String val =
|
||||
'#${(color.value & 0xFFFFFF).toRadixString(16).padLeft(6, '0').toLowerCase()}';
|
||||
log("Color in Tag#_toHex is $val");
|
||||
return val;
|
||||
}
|
||||
|
||||
int? _colorStringToInt(String? color) {
|
||||
if (color == null) return null;
|
||||
return int.tryParse(color.replaceAll("#", "ff"), radix: 16);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.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/view/pages/add_label_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
||||
|
||||
@@ -2,12 +2,9 @@ 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_extra_fields/form_builder_extra_fields.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.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/query_parameters/tags_query.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/view/pages/edit_label_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
@@ -61,7 +58,7 @@ class EditTagPage extends StatelessWidget {
|
||||
}
|
||||
cubit.updateFilter(filter: updatedFilter);
|
||||
Navigator.pop(context);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class TagWidget extends StatelessWidget {
|
||||
final Tag tag;
|
||||
|
||||
@@ -2,10 +2,9 @@ import 'package:flutter/material.dart';
|
||||
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_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/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';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/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';
|
||||
|
||||
class TagsWidget extends StatefulWidget {
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.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:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
class AddLabelPage<T extends Label> extends StatefulWidget {
|
||||
final String? initialName;
|
||||
@@ -107,8 +102,8 @@ class _AddLabelPageState<T extends Label> extends State<AddLabelPage<T>> {
|
||||
final label = await widget.cubit
|
||||
.add(widget.fromJson(_formKey.currentState!.value));
|
||||
Navigator.pop(context, label);
|
||||
} on ErrorMessage catch (e) {
|
||||
showSnackBar(context, translateError(context, e.code));
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} on PaperlessValidationErrors catch (json) {
|
||||
setState(() => _errors = json);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/matching_algorithm.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
|
||||
@@ -146,7 +144,7 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
||||
Navigator.pop(context);
|
||||
} on PaperlessValidationErrors catch (errorMessages) {
|
||||
setState(() => _errors = errorMessages);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
||||
import 'package:paperless_api/paperless_api.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';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.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/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/edit_correspondent_page.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/document_type/view/pages/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/edit_document_type_page.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';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/view/pages/edit_storage_path_page.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';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/pages/edit_tag_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/model/document_type.model.dart';
|
||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.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/model/error_message.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.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/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents_preview/view/pages/linked_documents_page.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_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/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_item.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
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_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
||||
|
||||
@injectable
|
||||
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
|
||||
final DocumentRepository _documentRepository;
|
||||
final PaperlessDocumentsApi _api;
|
||||
|
||||
LinkedDocumentsCubit(this._documentRepository)
|
||||
: super(LinkedDocumentsState());
|
||||
LinkedDocumentsCubit(this._api) : super(LinkedDocumentsState());
|
||||
|
||||
Future<void> initialize(DocumentFilter filter) async {
|
||||
final documents = await _documentRepository.find(
|
||||
final documents = await _api.find(
|
||||
filter.copyWith(
|
||||
pageSize: 100,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
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';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class LinkedDocumentsState {
|
||||
final bool isLoaded;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.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';
|
||||
@@ -69,7 +68,7 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
||||
additionalProviders: [
|
||||
BlocProvider<DocumentDetailsCubit>.value(
|
||||
value: DocumentDetailsCubit(
|
||||
getIt<DocumentRepository>(),
|
||||
getIt<PaperlessDocumentsApi>(),
|
||||
document,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
|
||||
@@ -10,18 +10,19 @@ import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
||||
import 'package:paperless_mobile/features/login/model/user_credentials.model.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication.service.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
const authenticationKey = "authentication";
|
||||
|
||||
@singleton
|
||||
class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
final LocalAuthenticationService _localAuthService;
|
||||
final PaperlessAuthenticationApi _authApi;
|
||||
final LocalVault localStore;
|
||||
final AuthenticationService authenticationService;
|
||||
|
||||
AuthenticationCubit(
|
||||
this.localStore,
|
||||
this.authenticationService,
|
||||
this._localAuthService,
|
||||
this._authApi,
|
||||
) : super(AuthenticationState.initial);
|
||||
|
||||
Future<void> initialize() {
|
||||
@@ -49,7 +50,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
),
|
||||
),
|
||||
);
|
||||
final token = await authenticationService.login(
|
||||
final token = await _authApi.login(
|
||||
username: credentials.username!,
|
||||
password: credentials.password!,
|
||||
serverUrl: serverUrl,
|
||||
@@ -70,14 +71,14 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
authentication: auth,
|
||||
));
|
||||
} on TlsException catch (_) {
|
||||
const error =
|
||||
ErrorMessage(ErrorCode.invalidClientCertificateConfiguration);
|
||||
const error = PaperlessServerException(
|
||||
ErrorCode.invalidClientCertificateConfiguration);
|
||||
throw error;
|
||||
} on SocketException catch (err) {
|
||||
if (err.message.contains("connection timed out")) {
|
||||
throw const ErrorMessage(ErrorCode.requestTimedOut);
|
||||
throw const PaperlessServerException(ErrorCode.requestTimedOut);
|
||||
} else {
|
||||
throw ErrorMessage.unknown();
|
||||
throw const PaperlessServerException.unknown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +96,7 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
||||
emit(AuthenticationState(isAuthenticated: false, wasLoginStored: false));
|
||||
} else {
|
||||
if (!appSettings.isLocalAuthenticationEnabled ||
|
||||
await authenticationService
|
||||
await _localAuthService
|
||||
.authenticateLocalUser("Authenticate to log back in")) {
|
||||
registerSecurityContext(storedAuth.clientCertificate);
|
||||
emit(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
|
||||
@@ -12,7 +12,8 @@ class LocalAuthenticationCubit extends Cubit<LocalAuthenticationState> {
|
||||
if (isAuthenticationSuccessful) {
|
||||
emit(LocalAuthenticationState(true));
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.biometricAuthenticationFailed);
|
||||
throw const PaperlessServerException(
|
||||
ErrorCode.biometricAuthenticationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,17 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||
|
||||
@singleton
|
||||
class AuthenticationService {
|
||||
final BaseClient httpClient;
|
||||
class LocalAuthenticationService {
|
||||
final LocalVault localStore;
|
||||
final LocalAuthentication localAuthentication;
|
||||
|
||||
AuthenticationService(
|
||||
LocalAuthenticationService(
|
||||
this.localStore,
|
||||
this.localAuthentication,
|
||||
@Named("timeoutClient") this.httpClient,
|
||||
);
|
||||
|
||||
///
|
||||
/// Returns the authentication token.
|
||||
///
|
||||
Future<String> login({
|
||||
required String username,
|
||||
required String password,
|
||||
required String serverUrl,
|
||||
}) async {
|
||||
late Response response;
|
||||
try {
|
||||
response = await httpClient.post(
|
||||
Uri.parse("/api/token/"),
|
||||
body: {"username": username, "password": password},
|
||||
);
|
||||
} on FormatException catch (e) {
|
||||
final source = e.source;
|
||||
if (source is String &&
|
||||
source.contains("400 No required SSL certificate was sent")) {
|
||||
throw const ErrorMessage(ErrorCode.missingClientCertificate);
|
||||
}
|
||||
}
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
return data['token'];
|
||||
} else if (response.statusCode == 400 &&
|
||||
response.body
|
||||
.toLowerCase()
|
||||
.contains("no required certificate was sent")) {
|
||||
throw const ErrorMessage(ErrorCode.invalidClientCertificateConfiguration);
|
||||
} else {
|
||||
throw const ErrorMessage(ErrorCode.authenticationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> authenticateLocalUser(String localizedReason) async {
|
||||
if (await localAuthentication.isDeviceSupported()) {
|
||||
return await localAuthentication.authenticate(
|
||||
|
||||
@@ -1,7 +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/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/view/widgets/client_certificate_form_field.dart';
|
||||
@@ -97,12 +97,12 @@ class _LoginPageState extends State<LoginPage> {
|
||||
clientCertificate:
|
||||
form[ClientCertificateFormField.fkClientCertificate],
|
||||
);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} on Map<String, dynamic> catch (error, stackTrace) {
|
||||
showGenericError(context, error.values.first, stackTrace);
|
||||
} catch (unknownError, stackTrace) {
|
||||
showErrorMessage(context, ErrorMessage.unknown(), stackTrace);
|
||||
showGenericError(context, unknownError.toString(), stackTrace);
|
||||
} finally {
|
||||
setState(() => _isLoginLoading = false);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/saved_view.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/saved_views_repository.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_state.dart';
|
||||
|
||||
@singleton
|
||||
class SavedViewCubit extends Cubit<SavedViewState> {
|
||||
SavedViewCubit() : super(SavedViewState(value: {}));
|
||||
final PaperlessSavedViewsApi _api;
|
||||
SavedViewCubit(this._api) : super(SavedViewState(value: {}));
|
||||
|
||||
void selectView(SavedView? view) {
|
||||
emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id));
|
||||
}
|
||||
|
||||
Future<SavedView> add(SavedView view) async {
|
||||
final savedView = await getIt<SavedViewsRepository>().save(view);
|
||||
final savedView = await _api.save(view);
|
||||
emit(
|
||||
SavedViewState(
|
||||
value: {...state.value, savedView.id!: savedView},
|
||||
@@ -25,7 +24,7 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
||||
}
|
||||
|
||||
Future<int> remove(SavedView view) async {
|
||||
final id = await getIt<SavedViewsRepository>().delete(view);
|
||||
final id = await _api.delete(view);
|
||||
final newValue = {...state.value};
|
||||
newValue.removeWhere((key, value) => key == id);
|
||||
emit(
|
||||
@@ -40,7 +39,7 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
||||
}
|
||||
|
||||
Future<void> initialize() async {
|
||||
final views = await getIt<SavedViewsRepository>().getAll();
|
||||
final views = await _api.getAll();
|
||||
final values = {for (var element in views) element.id!: element};
|
||||
emit(SavedViewState(value: values));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/saved_view.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class SavedViewState with EquatableMixin {
|
||||
final Map<int, SavedView> value;
|
||||
@@ -11,5 +11,8 @@ class SavedViewState with EquatableMixin {
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [value, selectedSavedViewId];
|
||||
List<Object?> get props => [
|
||||
value,
|
||||
selectedSavedViewId,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/saved_view.model.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.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/saved_view.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_state.dart';
|
||||
@@ -87,7 +86,7 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
if (newView != null) {
|
||||
try {
|
||||
await BlocProvider.of<SavedViewCubit>(context).add(newView);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -104,7 +103,7 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
BlocProvider.of<DocumentsCubit>(context).updateFilter();
|
||||
BlocProvider.of<SavedViewCubit>(context).selectView(null);
|
||||
}
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +118,7 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
||||
if (delete) {
|
||||
try {
|
||||
BlocProvider.of<SavedViewCubit>(context).remove(view);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,18 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.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/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
|
||||
@injectable
|
||||
class DocumentScannerCubit extends Cubit<List<File>> {
|
||||
final DocumentRepository documentRepository;
|
||||
final PaperlessDocumentsApi _api;
|
||||
|
||||
static List<File> initialState = [];
|
||||
|
||||
DocumentScannerCubit(this.documentRepository) : super(initialState);
|
||||
DocumentScannerCubit(this._api) : super(initialState);
|
||||
|
||||
void addScan(File file) => emit([...state, file]);
|
||||
|
||||
@@ -26,7 +26,7 @@ class DocumentScannerCubit extends Cubit<List<File>> {
|
||||
scans.removeAt(fileIndex);
|
||||
emit(scans);
|
||||
} catch (_) {
|
||||
throw const ErrorMessage(ErrorCode.scanRemoveFailed);
|
||||
throw const PaperlessServerException(ErrorCode.scanRemoveFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class DocumentScannerCubit extends Cubit<List<File>> {
|
||||
imageCache.clear();
|
||||
emit(initialState);
|
||||
} catch (_) {
|
||||
throw const ErrorMessage(ErrorCode.scanRemoveFailed);
|
||||
throw const PaperlessServerException(ErrorCode.scanRemoveFailed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,17 +55,23 @@ class DocumentScannerCubit extends Cubit<List<File>> {
|
||||
Iterable<int> tags = const [],
|
||||
DateTime? createdAt,
|
||||
}) async {
|
||||
await documentRepository.create(
|
||||
final auth = getIt<AuthenticationCubit>().state.authentication;
|
||||
if (auth == null) {
|
||||
throw const PaperlessServerException(ErrorCode.notAuthenticated);
|
||||
}
|
||||
await _api.create(
|
||||
bytes,
|
||||
fileName,
|
||||
filename: fileName,
|
||||
title: title,
|
||||
documentType: documentType,
|
||||
correspondent: correspondent,
|
||||
tags: tags,
|
||||
createdAt: createdAt,
|
||||
authToken: auth.token,
|
||||
serverUrl: auth.serverUrl,
|
||||
);
|
||||
if (onConsumptionFinished != null) {
|
||||
documentRepository
|
||||
_api
|
||||
.waitForConsumptionFinished(fileName, title)
|
||||
.then((value) => onConsumptionFinished(value));
|
||||
}
|
||||
|
||||
@@ -3,23 +3,15 @@ import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.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/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.dart';
|
||||
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/bloc/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';
|
||||
@@ -258,12 +250,13 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
showSnackBar(context, S.of(context).documentUploadSuccessText);
|
||||
Navigator.pop(context);
|
||||
widget.afterUpload?.call();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} on PaperlessValidationErrors catch (errorMessages) {
|
||||
setState(() => _errors = errorMessages);
|
||||
} on PaperlessValidationErrors catch (PaperlessServerExceptions) {
|
||||
setState(() => _errors = PaperlessServerExceptions);
|
||||
} catch (unknownError, stackTrace) {
|
||||
showErrorMessage(context, const ErrorMessage.unknown(), stackTrace);
|
||||
showErrorMessage(
|
||||
context, const PaperlessServerException.unknown(), stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isUploadLoading = false;
|
||||
|
||||
@@ -8,13 +8,10 @@ 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_api/paperless_api.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/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||
@@ -193,7 +190,7 @@ class _ScannerPageState extends State<ScannerPage>
|
||||
try {
|
||||
BlocProvider.of<DocumentScannerCubit>(context)
|
||||
.removeScan(index);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
@@ -206,7 +203,7 @@ class _ScannerPageState extends State<ScannerPage>
|
||||
void _reset(BuildContext context) {
|
||||
try {
|
||||
BlocProvider.of<DocumentScannerCubit>(context).reset();
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
@@ -231,7 +228,7 @@ class _ScannerPageState extends State<ScannerPage>
|
||||
)) {
|
||||
showErrorMessage(
|
||||
context,
|
||||
const ErrorMessage(ErrorCode.unsupportedFileFormat),
|
||||
const PaperlessServerException(ErrorCode.unsupportedFileFormat),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class BiometricAuthenticationSetting extends StatelessWidget {
|
||||
: S
|
||||
.of(context)
|
||||
.appSettingsDisableBiometricAuthenticationReasonText;
|
||||
final changeValue = await getIt<AuthenticationService>()
|
||||
final changeValue = await getIt<LocalAuthenticationService>()
|
||||
.authenticateLocalUser(localizedReason);
|
||||
if (changeValue) {
|
||||
settingsBloc.setIsBiometricAuthenticationEnabled(val);
|
||||
|
||||
@@ -14,13 +14,18 @@ class LanguageSelectionSetting extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
|
||||
static const _languageOptions = {
|
||||
'en': 'English',
|
||||
'de': 'Deutsch',
|
||||
'cs': 'Česky',
|
||||
};
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
builder: (context, settings) {
|
||||
return ListTile(
|
||||
title: Text(S.of(context).settingsPageLanguageSettingLabel),
|
||||
subtitle: Text(_mapSubtagToLanguage(settings.preferredLocaleSubtag)),
|
||||
subtitle: Text(_languageOptions[settings.preferredLocaleSubtag]!),
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => RadioSettingsDialog<String>(
|
||||
@@ -28,12 +33,16 @@ class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
|
||||
options: [
|
||||
RadioOption(
|
||||
value: 'en',
|
||||
label: _mapSubtagToLanguage('en'),
|
||||
label: _languageOptions['en']!,
|
||||
),
|
||||
RadioOption(
|
||||
value: 'de',
|
||||
label: _mapSubtagToLanguage('de'),
|
||||
label: _languageOptions['de']!,
|
||||
),
|
||||
RadioOption(
|
||||
value: 'cs',
|
||||
label: _languageOptions['cs']!,
|
||||
)
|
||||
],
|
||||
initialValue: BlocProvider.of<ApplicationSettingsCubit>(context)
|
||||
.state
|
||||
@@ -45,15 +54,4 @@ class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_mapSubtagToLanguage(String subtag) {
|
||||
switch (subtag) {
|
||||
case 'en':
|
||||
return "English";
|
||||
case 'de':
|
||||
return "Deutsch";
|
||||
default:
|
||||
return "English";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user