mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 10:08:02 -06:00
Merge branch 'development'
This commit is contained in:
@@ -19,7 +19,7 @@ class DocumentProcessingStatus {
|
|||||||
final String taskId;
|
final String taskId;
|
||||||
final bool isApproximated;
|
final bool isApproximated;
|
||||||
|
|
||||||
static const String UNKNOWN_TASK_ID = "NO_TASK_ID";
|
static const String unknownTaskId = "NO_TASK_ID";
|
||||||
|
|
||||||
DocumentProcessingStatus({
|
DocumentProcessingStatus({
|
||||||
required this.currentProgress,
|
required this.currentProgress,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class LongPollingStatusService implements StatusService {
|
|||||||
maxProgress: 100,
|
maxProgress: 100,
|
||||||
message: ProcessingMessage.new_file,
|
message: ProcessingMessage.new_file,
|
||||||
status: ProcessingStatus.working,
|
status: ProcessingStatus.working,
|
||||||
taskId: DocumentProcessingStatus.UNKNOWN_TASK_ID,
|
taskId: DocumentProcessingStatus.unknownTaskId,
|
||||||
documentId: null,
|
documentId: null,
|
||||||
isApproximated: true,
|
isApproximated: true,
|
||||||
),
|
),
|
||||||
@@ -105,7 +105,7 @@ class LongPollingStatusService implements StatusService {
|
|||||||
maxProgress: 100,
|
maxProgress: 100,
|
||||||
message: ProcessingMessage.finished,
|
message: ProcessingMessage.finished,
|
||||||
status: ProcessingStatus.success,
|
status: ProcessingStatus.success,
|
||||||
taskId: DocumentProcessingStatus.UNKNOWN_TASK_ID,
|
taskId: DocumentProcessingStatus.unknownTaskId,
|
||||||
documentId: docId,
|
documentId: docId,
|
||||||
isApproximated: true,
|
isApproximated: true,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,3 +7,18 @@ extension NullableMapKey<K, V> on Map<K, V> {
|
|||||||
return putIfAbsent(key, () => value);
|
return putIfAbsent(key, () => value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Unique<E, Id> on List<E> {
|
||||||
|
List<E> unique([Id Function(E element)? id, bool inplace = true]) {
|
||||||
|
final ids = <Id>{};
|
||||||
|
var list = inplace ? this : List<E>.from(this);
|
||||||
|
list.retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DuplicateCheckable<T> on Iterable<T> {
|
||||||
|
bool hasDuplicates() {
|
||||||
|
return toSet().length != length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class DocumentsCubit extends Cubit<DocumentsState> {
|
|||||||
required void Function(DocumentModel document) onConsumptionFinished,
|
required void Function(DocumentModel document) onConsumptionFinished,
|
||||||
int? documentType,
|
int? documentType,
|
||||||
int? correspondent,
|
int? correspondent,
|
||||||
List<int>? tags,
|
Iterable<int> tags = const [],
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
}) async {
|
}) async {
|
||||||
await documentRepository.create(
|
await documentRepository.create(
|
||||||
|
|||||||
@@ -2,18 +2,23 @@ import 'package:paperless_mobile/core/type/types.dart';
|
|||||||
|
|
||||||
class BulkEditAction {
|
class BulkEditAction {
|
||||||
final List<int> documents;
|
final List<int> documents;
|
||||||
final String _method;
|
final BulkEditActionMethod _method;
|
||||||
final Map<String, dynamic> parameters;
|
final Map<String, dynamic> parameters;
|
||||||
|
|
||||||
BulkEditAction.delete(this.documents)
|
BulkEditAction.delete(this.documents)
|
||||||
: _method = 'delete',
|
: _method = BulkEditActionMethod.delete,
|
||||||
parameters = {};
|
parameters = {};
|
||||||
|
|
||||||
JSON toJson() {
|
JSON toJson() {
|
||||||
return {
|
return {
|
||||||
'documents': documents,
|
'documents': documents,
|
||||||
'method': _method,
|
'method': _method.name,
|
||||||
'parameters': parameters,
|
'parameters': parameters,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum BulkEditActionMethod {
|
||||||
|
delete,
|
||||||
|
edit;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class DocumentModel extends Equatable {
|
|||||||
final int id;
|
final int id;
|
||||||
final String title;
|
final String title;
|
||||||
final String? content;
|
final String? content;
|
||||||
final List<int> tags;
|
final Iterable<int> tags;
|
||||||
final int? documentType;
|
final int? documentType;
|
||||||
final int? correspondent;
|
final int? correspondent;
|
||||||
final int? storagePath;
|
final int? storagePath;
|
||||||
@@ -78,7 +78,7 @@ class DocumentModel extends Equatable {
|
|||||||
modifiedKey: modified.toUtc().toIso8601String(),
|
modifiedKey: modified.toUtc().toIso8601String(),
|
||||||
addedKey: added.toUtc().toIso8601String(),
|
addedKey: added.toUtc().toIso8601String(),
|
||||||
originalFileNameKey: originalFileName,
|
originalFileNameKey: originalFileName,
|
||||||
tagsKey: tags,
|
tagsKey: tags.toList(),
|
||||||
storagePathKey: storagePath,
|
storagePathKey: storagePath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -86,10 +86,14 @@ class DocumentModel extends Equatable {
|
|||||||
DocumentModel copyWith({
|
DocumentModel copyWith({
|
||||||
String? title,
|
String? title,
|
||||||
String? content,
|
String? content,
|
||||||
TagsQuery? tags,
|
bool overwriteTags = false,
|
||||||
IdQueryParameter? documentType,
|
Iterable<int>? tags,
|
||||||
IdQueryParameter? correspondent,
|
bool overwriteDocumentType = false,
|
||||||
IdQueryParameter? storagePath,
|
int? documentType,
|
||||||
|
bool overwriteCorrespondent = false,
|
||||||
|
int? correspondent,
|
||||||
|
bool overwriteStoragePath = false,
|
||||||
|
int? storagePath,
|
||||||
DateTime? created,
|
DateTime? created,
|
||||||
DateTime? modified,
|
DateTime? modified,
|
||||||
DateTime? added,
|
DateTime? added,
|
||||||
@@ -101,10 +105,11 @@ class DocumentModel extends Equatable {
|
|||||||
id: id,
|
id: id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
documentType: fromQuery(documentType, this.documentType),
|
documentType: overwriteDocumentType ? documentType : this.documentType,
|
||||||
correspondent: fromQuery(correspondent, this.correspondent),
|
correspondent:
|
||||||
storagePath: fromQuery(storagePath, this.storagePath),
|
overwriteCorrespondent ? correspondent : this.correspondent,
|
||||||
tags: fromListQuery(tags, this.tags),
|
storagePath: overwriteDocumentType ? storagePath : this.storagePath,
|
||||||
|
tags: overwriteTags ? tags ?? [] : this.tags,
|
||||||
created: created ?? this.created,
|
created: created ?? this.created,
|
||||||
modified: modified ?? this.modified,
|
modified: modified ?? this.modified,
|
||||||
added: added ?? this.added,
|
added: added ?? this.added,
|
||||||
@@ -114,20 +119,6 @@ class DocumentModel extends Equatable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int? fromQuery(IdQueryParameter? query, int? previous) {
|
|
||||||
if (query == null) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
return query.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<int> fromListQuery(TagsQuery? query, List<int> previous) {
|
|
||||||
if (query == null) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
return query.ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class DocumentFilter with EquatableMixin {
|
|||||||
this.correspondent = const CorrespondentQuery.unset(),
|
this.correspondent = const CorrespondentQuery.unset(),
|
||||||
this.storagePath = const StoragePathQuery.unset(),
|
this.storagePath = const StoragePathQuery.unset(),
|
||||||
this.asn = const AsnQuery.unset(),
|
this.asn = const AsnQuery.unset(),
|
||||||
this.tags = const TagsQuery.unset(),
|
this.tags = const IdsTagsQuery.unset(),
|
||||||
this.sortField = SortField.created,
|
this.sortField = SortField.created,
|
||||||
this.sortOrder = SortOrder.descending,
|
this.sortOrder = SortOrder.descending,
|
||||||
this.page = 1,
|
this.page = 1,
|
||||||
|
|||||||
@@ -13,24 +13,24 @@ class FilterRule with EquatableMixin {
|
|||||||
static const int asnRule = 2;
|
static const int asnRule = 2;
|
||||||
static const int correspondentRule = 3;
|
static const int correspondentRule = 3;
|
||||||
static const int documentTypeRule = 4;
|
static const int documentTypeRule = 4;
|
||||||
static const int tagRule = 6;
|
static const int includeTagsRule = 6;
|
||||||
|
static const int hasAnyTag = 7; // Corresponds to Not assigned
|
||||||
static const int createdBeforeRule = 8;
|
static const int createdBeforeRule = 8;
|
||||||
static const int createdAfterRule = 9;
|
static const int createdAfterRule = 9;
|
||||||
static const int addedBeforeRule = 13;
|
static const int addedBeforeRule = 13;
|
||||||
static const int addedAfterRule = 14;
|
static const int addedAfterRule = 14;
|
||||||
|
static const int excludeTagsRule = 17;
|
||||||
static const int titleAndContentRule = 19;
|
static const int titleAndContentRule = 19;
|
||||||
static const int extendedRule = 20;
|
static const int extendedRule = 20;
|
||||||
static const int storagePathRule = 25;
|
static const int storagePathRule = 25;
|
||||||
// Currently unsupported view optiosn:
|
// Currently unsupported view options:
|
||||||
static const int _content = 1;
|
static const int _content = 1;
|
||||||
static const int _isInInbox = 5;
|
static const int _isInInbox = 5;
|
||||||
static const int _hasAnyTag = 7;
|
|
||||||
static const int _createdYearIs = 10;
|
static const int _createdYearIs = 10;
|
||||||
static const int _createdMonthIs = 11;
|
static const int _createdMonthIs = 11;
|
||||||
static const int _createdDayIs = 12;
|
static const int _createdDayIs = 12;
|
||||||
static const int _modifiedBefore = 15;
|
static const int _modifiedBefore = 15;
|
||||||
static const int _modifiedAfter = 16;
|
static const int _modifiedAfter = 16;
|
||||||
static const int _doesNotHaveTag = 17;
|
|
||||||
static const int _doesNotHaveAsn = 18;
|
static const int _doesNotHaveAsn = 18;
|
||||||
static const int _moreLikeThis = 21;
|
static const int _moreLikeThis = 21;
|
||||||
static const int _hasTagsIn = 22;
|
static const int _hasTagsIn = 22;
|
||||||
@@ -76,11 +76,25 @@ class FilterRule with EquatableMixin {
|
|||||||
? const StoragePathQuery.notAssigned()
|
? const StoragePathQuery.notAssigned()
|
||||||
: StoragePathQuery.fromId(int.parse(value!)),
|
: StoragePathQuery.fromId(int.parse(value!)),
|
||||||
);
|
);
|
||||||
case tagRule:
|
case hasAnyTag:
|
||||||
return filter.copyWith(
|
return filter.copyWith(
|
||||||
tags: value == null
|
tags: value == "true"
|
||||||
? const TagsQuery.notAssigned()
|
? const AnyAssignedTagsQuery()
|
||||||
: TagsQuery.fromIds([...filter.tags.ids, int.parse(value!)]),
|
: 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:
|
case createdBeforeRule:
|
||||||
return filter.copyWith(
|
return filter.copyWith(
|
||||||
@@ -131,12 +145,19 @@ class FilterRule with EquatableMixin {
|
|||||||
filterRules
|
filterRules
|
||||||
.add(FilterRule(storagePathRule, filter.storagePath.id!.toString()));
|
.add(FilterRule(storagePathRule, filter.storagePath.id!.toString()));
|
||||||
}
|
}
|
||||||
if (filter.tags.onlyNotAssigned) {
|
if (filter.tags is OnlyNotAssignedTagsQuery) {
|
||||||
filterRules.add(FilterRule(tagRule, null));
|
filterRules.add(FilterRule(hasAnyTag, "false"));
|
||||||
}
|
}
|
||||||
if (filter.tags.isSet) {
|
if (filter.tags is AnyAssignedTagsQuery) {
|
||||||
filterRules.addAll(
|
filterRules.add(FilterRule(hasAnyTag, "true"));
|
||||||
filter.tags.ids.map((id) => FilterRule(tagRule, id.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) {
|
if (filter.queryText != null) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ enum SortField {
|
|||||||
archiveSerialNumber("archive_serial_number"),
|
archiveSerialNumber("archive_serial_number"),
|
||||||
correspondentName("correspondent__name"),
|
correspondentName("correspondent__name"),
|
||||||
title("title"),
|
title("title"),
|
||||||
documentType("documentType"),
|
documentType("document_type__name"),
|
||||||
created("created"),
|
created("created"),
|
||||||
added("added"),
|
added("added"),
|
||||||
modified("modified");
|
modified("modified");
|
||||||
|
|||||||
@@ -1,38 +1,136 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||||
|
|
||||||
class TagsQuery with EquatableMixin {
|
abstract class TagsQuery with EquatableMixin {
|
||||||
final List<int> _ids;
|
const TagsQuery();
|
||||||
final bool? _isTagged;
|
String toQueryParameter();
|
||||||
|
|
||||||
const TagsQuery.fromIds(List<int> ids)
|
|
||||||
: _isTagged = null,
|
|
||||||
_ids = ids;
|
|
||||||
|
|
||||||
const TagsQuery.anyAssigned()
|
|
||||||
: _isTagged = true,
|
|
||||||
_ids = const [];
|
|
||||||
|
|
||||||
const TagsQuery.notAssigned()
|
|
||||||
: _isTagged = false,
|
|
||||||
_ids = const [];
|
|
||||||
|
|
||||||
const TagsQuery.unset() : this.fromIds(const []);
|
|
||||||
|
|
||||||
bool get onlyNotAssigned => _isTagged == false;
|
|
||||||
bool get onlyAssigned => _isTagged == true;
|
|
||||||
|
|
||||||
bool get isUnset => _ids.isEmpty && _isTagged == null;
|
|
||||||
bool get isSet => _ids.isNotEmpty && _isTagged == null;
|
|
||||||
|
|
||||||
List<int> get ids => _ids;
|
|
||||||
|
|
||||||
String toQueryParameter() {
|
|
||||||
if (onlyNotAssigned || onlyAssigned) {
|
|
||||||
return '&is_tagged=$_isTagged';
|
|
||||||
}
|
}
|
||||||
return isUnset ? "" : '&tags__id__all=${ids.join(',')}';
|
|
||||||
|
class OnlyNotAssignedTagsQuery extends TagsQuery {
|
||||||
|
const OnlyNotAssignedTagsQuery();
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toQueryParameter() {
|
||||||
|
return '&is_tagged=0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnyAssignedTagsQuery extends TagsQuery {
|
||||||
|
const AnyAssignedTagsQuery();
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toQueryParameter() {
|
||||||
|
return '&is_tagged=1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdsTagsQuery extends TagsQuery {
|
||||||
|
final Iterable<TagIdQuery> _idQueries;
|
||||||
|
|
||||||
|
const IdsTagsQuery([this._idQueries = const []]);
|
||||||
|
|
||||||
|
const IdsTagsQuery.unset() : _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());
|
||||||
|
return IdsTagsQuery(
|
||||||
|
[...withIdsRemoved(intersection).queries, ...idQueries],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
List<Object?> get props => [_isTagged, _ids];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ abstract class DocumentRepository {
|
|||||||
required String title,
|
required String title,
|
||||||
int? documentType,
|
int? documentType,
|
||||||
int? correspondent,
|
int? correspondent,
|
||||||
List<int>? tags,
|
Iterable<int> tags = const [],
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
});
|
});
|
||||||
Future<DocumentModel> update(DocumentModel doc);
|
Future<DocumentModel> update(DocumentModel doc);
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import 'dart:math';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
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/model/error_message.dart';
|
||||||
import 'package:paperless_mobile/core/store/local_vault.dart';
|
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||||
import 'package:paperless_mobile/core/type/types.dart';
|
import 'package:paperless_mobile/core/type/types.dart';
|
||||||
@@ -22,9 +25,6 @@ import 'package:paperless_mobile/features/documents/model/query_parameters/sort_
|
|||||||
import 'package:paperless_mobile/features/documents/model/similar_document.model.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/features/documents/repository/document_repository.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.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';
|
|
||||||
|
|
||||||
@Injectable(as: DocumentRepository)
|
@Injectable(as: DocumentRepository)
|
||||||
class DocumentRepositoryImpl implements DocumentRepository {
|
class DocumentRepositoryImpl implements DocumentRepository {
|
||||||
@@ -45,7 +45,7 @@ class DocumentRepositoryImpl implements DocumentRepository {
|
|||||||
required String title,
|
required String title,
|
||||||
int? documentType,
|
int? documentType,
|
||||||
int? correspondent,
|
int? correspondent,
|
||||||
List<int>? tags,
|
Iterable<int> tags = const [],
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
}) async {
|
}) async {
|
||||||
final auth = await localStorage.loadAuthenticationInformation();
|
final auth = await localStorage.loadAuthenticationInformation();
|
||||||
@@ -78,7 +78,7 @@ class DocumentRepositoryImpl implements DocumentRepository {
|
|||||||
bodyBuffer.write(_buildMultipartField(key, fields[key]!, boundary));
|
bodyBuffer.write(_buildMultipartField(key, fields[key]!, boundary));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final tag in tags ?? <int>[]) {
|
for (final tag in tags) {
|
||||||
bodyBuffer.write(_buildMultipartField('tags', tag.toString(), boundary));
|
bodyBuffer.write(_buildMultipartField('tags', tag.toString(), boundary));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +125,9 @@ class DocumentRepositoryImpl implements DocumentRepository {
|
|||||||
var prefix = 'dart-http-boundary-';
|
var prefix = 'dart-http-boundary-';
|
||||||
var list = List<int>.generate(
|
var list = List<int>.generate(
|
||||||
70 - prefix.length,
|
70 - prefix.length,
|
||||||
(index) =>
|
(index) => boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
|
||||||
boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
|
growable: false,
|
||||||
growable: false);
|
);
|
||||||
return '$prefix${String.fromCharCodes(list)}';
|
return '$prefix${String.fromCharCodes(list)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -420,7 +420,9 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
Future<void> _onOpen(DocumentModel document) async {
|
Future<void> _onOpen(DocumentModel document) async {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => DocumentView(document: document),
|
builder: (context) => DocumentView(
|
||||||
|
documentBytes: getIt<DocumentRepository>().getPreview(document.id),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,9 +53,8 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
documentBytes = getIt<DocumentRepository>().getPreview(widget.document.id);
|
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
documentBytes = getIt<DocumentRepository>().getPreview(widget.document.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -69,10 +68,14 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
final updatedDocument = widget.document.copyWith(
|
final updatedDocument = widget.document.copyWith(
|
||||||
title: values[fkTitle],
|
title: values[fkTitle],
|
||||||
created: values[fkCreatedDate],
|
created: values[fkCreatedDate],
|
||||||
documentType: values[fkDocumentType] as IdQueryParameter,
|
overwriteDocumentType: true,
|
||||||
correspondent: values[fkCorrespondent] as IdQueryParameter,
|
documentType: (values[fkDocumentType] as IdQueryParameter).id,
|
||||||
storagePath: values[fkStoragePath] as IdQueryParameter,
|
overwriteCorrespondent: true,
|
||||||
tags: values[fkTags] as TagsQuery,
|
correspondent: (values[fkCorrespondent] as IdQueryParameter).id,
|
||||||
|
overwriteStoragePath: true,
|
||||||
|
storagePath: (values[fkStoragePath] as IdQueryParameter).id,
|
||||||
|
overwriteTags: true,
|
||||||
|
tags: (values[fkTags] as IdsTagsQuery).includedIds,
|
||||||
);
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSubmitLoading = true;
|
_isSubmitLoading = true;
|
||||||
@@ -181,7 +184,10 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
},
|
},
|
||||||
).padded(),
|
).padded(),
|
||||||
TagFormField(
|
TagFormField(
|
||||||
initialValue: TagsQuery.fromIds(widget.document.tags),
|
initialValue: IdsTagsQuery.included(widget.document.tags),
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
anyAssignedSelectable: false,
|
||||||
|
excludeAllowed: false,
|
||||||
name: fkTags,
|
name: fkTags,
|
||||||
).padded(),
|
).padded(),
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||||
@@ -6,11 +7,11 @@ import 'package:paperless_mobile/generated/l10n.dart';
|
|||||||
import 'package:pdfx/pdfx.dart';
|
import 'package:pdfx/pdfx.dart';
|
||||||
|
|
||||||
class DocumentView extends StatefulWidget {
|
class DocumentView extends StatefulWidget {
|
||||||
final DocumentModel document;
|
final Future<Uint8List> documentBytes;
|
||||||
|
|
||||||
const DocumentView({
|
const DocumentView({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.document,
|
required this.documentBytes,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -25,7 +26,7 @@ class _DocumentViewState extends State<DocumentView> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_pdfController = PdfController(
|
_pdfController = PdfController(
|
||||||
document: PdfDocument.openData(
|
document: PdfDocument.openData(
|
||||||
getIt<DocumentRepository>().getPreview(widget.document.id),
|
widget.documentBytes,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
|
||||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||||
import 'package:paperless_mobile/core/service/github_issue_service.dart';
|
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.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.model.dart';
|
||||||
@@ -19,14 +14,15 @@ import 'package:paperless_mobile/features/documents/view/widgets/search/document
|
|||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/documents_page_app_bar.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/documents_page_app_bar.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|
||||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
||||||
|
|
||||||
class DocumentsPage extends StatefulWidget {
|
class DocumentsPage extends StatefulWidget {
|
||||||
@@ -42,7 +38,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
firstPageKey: 1,
|
firstPageKey: 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
final PanelController _panelController = PanelController();
|
final PanelController _filterPanelController = PanelController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -99,9 +95,9 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
if (_panelController.isPanelOpen) {
|
if (_filterPanelController.isPanelOpen) {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
_panelController.close();
|
_filterPanelController.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final documentsCubit = BlocProvider.of<DocumentsCubit>(context);
|
final documentsCubit = BlocProvider.of<DocumentsCubit>(context);
|
||||||
@@ -129,7 +125,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
backdropEnabled: true,
|
backdropEnabled: true,
|
||||||
parallaxEnabled: true,
|
parallaxEnabled: true,
|
||||||
parallaxOffset: .5,
|
parallaxOffset: .5,
|
||||||
controller: _panelController,
|
controller: _filterPanelController,
|
||||||
defaultPanelState: PanelState.CLOSED,
|
defaultPanelState: PanelState.CLOSED,
|
||||||
minHeight: 48,
|
minHeight: 48,
|
||||||
maxHeight: (MediaQuery.of(context).size.height * 3) / 4,
|
maxHeight: (MediaQuery.of(context).size.height * 3) / 4,
|
||||||
@@ -140,7 +136,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
body: _buildBody(connectivityState),
|
body: _buildBody(connectivityState),
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
panelBuilder: (scrollController) => DocumentFilterPanel(
|
panelBuilder: (scrollController) => DocumentFilterPanel(
|
||||||
panelController: _panelController,
|
panelController: _filterPanelController,
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -194,7 +190,6 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: _onRefresh,
|
onRefresh: _onRefresh,
|
||||||
child: Container(
|
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
DocumentsPageAppBar(
|
DocumentsPageAppBar(
|
||||||
@@ -221,7 +216,6 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,8 +66,12 @@ class DocumentGridItem extends StatelessWidget {
|
|||||||
tagIds: document.tags,
|
tagIds: document.tags,
|
||||||
isMultiLine: false,
|
isMultiLine: false,
|
||||||
),
|
),
|
||||||
Text(DateFormat.yMMMd(Intl.getCurrentLocale())
|
const Spacer(),
|
||||||
.format(document.created)),
|
Text(
|
||||||
|
DateFormat.yMMMd(Intl.getCurrentLocale())
|
||||||
|
.format(document.created),
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ 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/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/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/query_type.dart';
|
||||||
import 'package:paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/model/query_parameters/storage_path_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/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/documents/view/widgets/search/query_type_form_field.dart';
|
||||||
@@ -52,16 +51,6 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
static const fkCreatedAt = DocumentModel.createdKey;
|
static const fkCreatedAt = DocumentModel.createdKey;
|
||||||
static const fkAddedAt = DocumentModel.addedKey;
|
static const fkAddedAt = DocumentModel.addedKey;
|
||||||
|
|
||||||
static const _sortFields = [
|
|
||||||
SortField.created,
|
|
||||||
SortField.added,
|
|
||||||
SortField.modified,
|
|
||||||
SortField.title,
|
|
||||||
SortField.correspondentName,
|
|
||||||
SortField.documentType,
|
|
||||||
SortField.archiveSerialNumber
|
|
||||||
];
|
|
||||||
|
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
|
||||||
late final DocumentsCubit _documentsCubit;
|
late final DocumentsCubit _documentsCubit;
|
||||||
@@ -99,8 +88,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return FormBuilder(
|
return FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(
|
child: Column(
|
||||||
controller: widget.scrollController,
|
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@@ -137,15 +125,25 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16.0,
|
height: 16.0,
|
||||||
),
|
),
|
||||||
_buildSortByChipsList(context, state),
|
Expanded(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16.0),
|
||||||
|
topRight: Radius.circular(16.0),
|
||||||
|
),
|
||||||
|
child: ListView(
|
||||||
|
controller: widget.scrollController,
|
||||||
|
children: [
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(S.of(context).documentsFilterPageSearchLabel),
|
child: Text(
|
||||||
|
S.of(context).documentsFilterPageSearchLabel),
|
||||||
).padded(const EdgeInsets.only(left: 8.0)),
|
).padded(const EdgeInsets.only(left: 8.0)),
|
||||||
_buildQueryFormField(state),
|
_buildQueryFormField(state),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(S.of(context).documentsFilterPageAdvancedLabel),
|
child: Text(
|
||||||
|
S.of(context).documentsFilterPageAdvancedLabel),
|
||||||
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
||||||
_buildCreatedDateRangePickerFormField(state).padded(),
|
_buildCreatedDateRangePickerFormField(state).padded(),
|
||||||
_buildAddedDateRangePickerFormField(state).padded(),
|
_buildAddedDateRangePickerFormField(state).padded(),
|
||||||
@@ -159,7 +157,11 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
).padded(),
|
).padded(),
|
||||||
// Required in order for the storage path field to be visible when typing
|
// Required in order for the storage path field to be visible when typing
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 200,
|
height: 150,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padded(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -385,6 +387,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4.0),
|
||||||
_buildDateRangePickerHelper(state, fkCreatedAt),
|
_buildDateRangePickerHelper(state, fkCreatedAt),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -432,6 +435,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4.0),
|
||||||
_buildDateRangePickerHelper(state, fkAddedAt),
|
_buildDateRangePickerHelper(state, fkAddedAt),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -448,71 +452,6 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSortByChipsList(BuildContext context, DocumentsState state) {
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
S.of(context).documentsPageOrderByLabel,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: kToolbarHeight,
|
|
||||||
child: ListView.separated(
|
|
||||||
itemCount: _sortFields.length,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
separatorBuilder: (context, index) => const SizedBox(
|
|
||||||
width: 8.0,
|
|
||||||
),
|
|
||||||
itemBuilder: (context, index) => _buildActionChip(
|
|
||||||
_sortFields[index], state.filter.sortField, context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padded();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildActionChip(SortField sortField,
|
|
||||||
SortField? currentlySelectedOrder, BuildContext context) {
|
|
||||||
String text;
|
|
||||||
switch (sortField) {
|
|
||||||
case SortField.archiveSerialNumber:
|
|
||||||
text = S.of(context).documentArchiveSerialNumberPropertyShortLabel;
|
|
||||||
break;
|
|
||||||
case SortField.correspondentName:
|
|
||||||
text = S.of(context).documentCorrespondentPropertyLabel;
|
|
||||||
break;
|
|
||||||
case SortField.title:
|
|
||||||
text = S.of(context).documentTitlePropertyLabel;
|
|
||||||
break;
|
|
||||||
case SortField.documentType:
|
|
||||||
text = S.of(context).documentDocumentTypePropertyLabel;
|
|
||||||
break;
|
|
||||||
case SortField.created:
|
|
||||||
text = S.of(context).documentCreatedPropertyLabel;
|
|
||||||
break;
|
|
||||||
case SortField.added:
|
|
||||||
text = S.of(context).documentAddedPropertyLabel;
|
|
||||||
break;
|
|
||||||
case SortField.modified:
|
|
||||||
text = S.of(context).documentModifiedPropertyLabel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final docBloc = BlocProvider.of<DocumentsCubit>(context);
|
|
||||||
return ActionChip(
|
|
||||||
label: Text(text),
|
|
||||||
avatar: currentlySelectedOrder == sortField
|
|
||||||
? const Icon(
|
|
||||||
Icons.done,
|
|
||||||
color: Colors.green,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
onPressed: () => docBloc.updateFilter(
|
|
||||||
filter: docBloc.state.filter.copyWith(sortField: sortField)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onApplyFilter() async {
|
void _onApplyFilter() async {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
final v = _formKey.currentState!.value;
|
final v = _formKey.currentState!.value;
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.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';
|
||||||
|
|
||||||
|
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||||
|
const SortFieldSelectionBottomSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SortFieldSelectionBottomSheet> createState() =>
|
||||||
|
_SortFieldSelectionBottomSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ClipRRect(
|
||||||
|
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
|
bloc: getIt<DocumentsCubit>(),
|
||||||
|
builder: (context, state) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
S.of(context).documentsPageOrderByLabel,
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
).padded(
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 16)),
|
||||||
|
Column(
|
||||||
|
children: _sortFields
|
||||||
|
.map(
|
||||||
|
(e) => _buildSortOption(
|
||||||
|
e,
|
||||||
|
state.filter.sortOrder,
|
||||||
|
state.filter.sortField == e,
|
||||||
|
_selectedFieldLoading == e,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSortOption(
|
||||||
|
SortField field,
|
||||||
|
SortOrder order,
|
||||||
|
bool isCurrentlySelected,
|
||||||
|
bool isNextSelected,
|
||||||
|
) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
title: Text(
|
||||||
|
_localizedSortField(field),
|
||||||
|
style: Theme.of(context).textTheme.bodyText2,
|
||||||
|
),
|
||||||
|
trailing: isNextSelected
|
||||||
|
? (_buildOrderIcon(_selectedOrderLoading!))
|
||||||
|
: (_selectedOrderLoading == null && isCurrentlySelected
|
||||||
|
? _buildOrderIcon(order)
|
||||||
|
: null),
|
||||||
|
onTap: () async {
|
||||||
|
setState(() {
|
||||||
|
_selectedFieldLoading = field;
|
||||||
|
_selectedOrderLoading =
|
||||||
|
isCurrentlySelected ? order.toggle() : SortOrder.descending;
|
||||||
|
});
|
||||||
|
BlocProvider.of<DocumentsCubit>(context)
|
||||||
|
.updateCurrentFilter((filter) => filter.copyWith(
|
||||||
|
sortOrder: isCurrentlySelected
|
||||||
|
? order.toggle()
|
||||||
|
: SortOrder.descending,
|
||||||
|
sortField: field,
|
||||||
|
))
|
||||||
|
.whenComplete(() {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_selectedFieldLoading = null;
|
||||||
|
_selectedOrderLoading = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOrderIcon(SortOrder order) {
|
||||||
|
if (order == SortOrder.ascending) {
|
||||||
|
return const Icon(Icons.arrow_upward);
|
||||||
|
}
|
||||||
|
return const Icon(Icons.arrow_downward);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _localizedSortField(SortField sortField) {
|
||||||
|
switch (sortField) {
|
||||||
|
case SortField.archiveSerialNumber:
|
||||||
|
return S.of(context).documentArchiveSerialNumberPropertyShortLabel;
|
||||||
|
case SortField.correspondentName:
|
||||||
|
return S.of(context).documentCorrespondentPropertyLabel;
|
||||||
|
case SortField.title:
|
||||||
|
return S.of(context).documentTitlePropertyLabel;
|
||||||
|
case SortField.documentType:
|
||||||
|
return S.of(context).documentDocumentTypePropertyLabel;
|
||||||
|
case SortField.created:
|
||||||
|
return S.of(context).documentCreatedPropertyLabel;
|
||||||
|
case SortField.added:
|
||||||
|
return S.of(context).documentAddedPropertyLabel;
|
||||||
|
case SortField.modified:
|
||||||
|
return S.of(context).documentModifiedPropertyLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,10 @@ class ConfirmDeleteSavedViewDialog extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(
|
title: Text(
|
||||||
"Delete view " + view.name + "?",
|
S.of(context).deleteViewDialogTitleText + view.name + "?",
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
),
|
),
|
||||||
content: Text("Do you really want to delete this view?"),
|
content: Text(S.of(context).deleteViewDialogContentText),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).genericActionCancelLabel),
|
child: Text(S.of(context).genericActionCancelLabel),
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|||||||
Widget _buildFlexibleArea(bool enabled) {
|
Widget _buildFlexibleArea(bool enabled) {
|
||||||
return FlexibleSpaceBar(
|
return FlexibleSpaceBar(
|
||||||
background: Padding(
|
background: Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||||
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/documents/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/features/documents/model/query_parameters/sort_order.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.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';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class SortDocumentsButton extends StatefulWidget {
|
class SortDocumentsButton extends StatefulWidget {
|
||||||
@@ -18,52 +24,32 @@ class SortDocumentsButton extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
||||||
bool _isLoading = false;
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<DocumentsCubit, DocumentsState>(
|
return IconButton(
|
||||||
builder: (context, state) {
|
icon: const Icon(Icons.sort),
|
||||||
Widget child;
|
onPressed: _onOpenSortBottomSheet,
|
||||||
if (_isLoading) {
|
);
|
||||||
child = const FittedBox(
|
}
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: RefreshProgressIndicator(
|
void _onOpenSortBottomSheet() {
|
||||||
strokeWidth: 4.0,
|
showModalBottomSheet(
|
||||||
backgroundColor: Colors.transparent,
|
elevation: 2,
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (context) => BlocProvider.value(
|
||||||
|
value: getIt<DocumentsCubit>(),
|
||||||
|
child: const FractionallySizedBox(
|
||||||
|
heightFactor: .6,
|
||||||
|
child: SortFieldSelectionBottomSheet(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
final bool isAscending =
|
|
||||||
state.filter.sortOrder == SortOrder.ascending;
|
|
||||||
child = IconButton(
|
|
||||||
icon: FaIcon(
|
|
||||||
isAscending
|
|
||||||
? FontAwesomeIcons.arrowDownAZ
|
|
||||||
: FontAwesomeIcons.arrowUpZA,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
try {
|
|
||||||
await BlocProvider.of<DocumentsCubit>(context)
|
|
||||||
.updateCurrentFilter(
|
|
||||||
(filter) => filter.copyWith(
|
|
||||||
sortOrder: state.filter.sortOrder.toggle(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} on ErrorMessage catch (error, stackTrace) {
|
|
||||||
showError(context, error, stackTrace);
|
|
||||||
} finally {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return SizedBox(
|
|
||||||
height: Theme.of(context).iconTheme.size,
|
|
||||||
width: Theme.of(context).iconTheme.size,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,16 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
const InfoDrawer({Key? key}) : super(key: key);
|
const InfoDrawer({Key? key}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Drawer(
|
return ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topRight: Radius.circular(16.0),
|
||||||
|
bottomRight: Radius.circular(16.0),
|
||||||
|
),
|
||||||
|
child: Drawer(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
padding: EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 8,
|
top: 8,
|
||||||
left: 8,
|
left: 8,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
@@ -78,7 +83,8 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
state.host ?? '',
|
state.host ?? '',
|
||||||
style: Theme.of(context).textTheme.bodyText2,
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyText2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@@ -192,6 +198,7 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
const Divider(),
|
const Divider(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,13 +48,15 @@ class EditTagPage extends StatelessWidget {
|
|||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
||||||
final currentFilter = cubit.state.filter;
|
final currentFilter = cubit.state.filter;
|
||||||
late DocumentFilter updatedFilter = currentFilter;
|
late DocumentFilter updatedFilter = currentFilter;
|
||||||
if (currentFilter.tags.ids.contains(tag.id)) {
|
if (currentFilter.tags is IdsTagsQuery) {
|
||||||
|
if ((currentFilter.tags as IdsTagsQuery).includedIds.contains(tag.id)) {
|
||||||
updatedFilter = currentFilter.copyWith(
|
updatedFilter = currentFilter.copyWith(
|
||||||
tags: TagsQuery.fromIds(
|
tags: (currentFilter.tags as IdsTagsQuery).withIdsRemoved(
|
||||||
currentFilter.tags.ids.where((tagId) => tagId != tag.id).toList(),
|
[tag.id!],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cubit.updateFilter(filter: updatedFilter);
|
cubit.updateFilter(filter: updatedFilter);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
} on ErrorMessage catch (error, stackTrace) {
|
} on ErrorMessage catch (error, stackTrace) {
|
||||||
|
|||||||
@@ -18,8 +18,13 @@ class TagWidget extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
final isIdsQuery = state.filter.tags is IdsTagsQuery;
|
||||||
return FilterChip(
|
return FilterChip(
|
||||||
selected: state.filter.tags.ids.contains(tag.id),
|
selected: isIdsQuery
|
||||||
|
? (state.filter.tags as IdsTagsQuery)
|
||||||
|
.includedIds
|
||||||
|
.contains(tag.id)
|
||||||
|
: false,
|
||||||
selectedColor: tag.color,
|
selectedColor: tag.color,
|
||||||
onSelected: (_) => _addTagToFilter(context),
|
onSelected: (_) => _addTagToFilter(context),
|
||||||
visualDensity: const VisualDensity(vertical: -2),
|
visualDensity: const VisualDensity(vertical: -2),
|
||||||
@@ -40,18 +45,19 @@ class TagWidget extends StatelessWidget {
|
|||||||
void _addTagToFilter(BuildContext context) {
|
void _addTagToFilter(BuildContext context) {
|
||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
||||||
try {
|
try {
|
||||||
if (cubit.state.filter.tags.ids.contains(tag.id)) {
|
final tagsQuery = cubit.state.filter.tags is IdsTagsQuery
|
||||||
|
? cubit.state.filter.tags as IdsTagsQuery
|
||||||
|
: const IdsTagsQuery();
|
||||||
|
if (tagsQuery.includedIds.contains(tag.id)) {
|
||||||
cubit.updateCurrentFilter(
|
cubit.updateCurrentFilter(
|
||||||
(filter) => filter.copyWith(
|
(filter) => filter.copyWith(
|
||||||
tags: TagsQuery.fromIds(cubit.state.filter.tags.ids
|
tags: tagsQuery.withIdsRemoved([tag.id!]),
|
||||||
.where((id) => id != tag.id)
|
|
||||||
.toList()),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
cubit.updateCurrentFilter(
|
cubit.updateCurrentFilter(
|
||||||
(filter) => filter.copyWith(
|
(filter) => filter.copyWith(
|
||||||
tags: TagsQuery.fromIds([...cubit.state.filter.tags.ids, tag.id!]),
|
tags: tagsQuery.withIdQueriesAdded([IncludeTagIdQuery(tag.id!)]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class TagFormField extends StatefulWidget {
|
|||||||
final String name;
|
final String name;
|
||||||
final bool allowCreation;
|
final bool allowCreation;
|
||||||
final bool notAssignedSelectable;
|
final bool notAssignedSelectable;
|
||||||
|
final bool anyAssignedSelectable;
|
||||||
|
final bool excludeAllowed;
|
||||||
|
|
||||||
const TagFormField({
|
const TagFormField({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -20,6 +22,8 @@ class TagFormField extends StatefulWidget {
|
|||||||
this.initialValue,
|
this.initialValue,
|
||||||
this.allowCreation = true,
|
this.allowCreation = true,
|
||||||
this.notAssignedSelectable = true,
|
this.notAssignedSelectable = true,
|
||||||
|
this.anyAssignedSelectable = true,
|
||||||
|
this.excludeAllowed = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -27,8 +31,11 @@ class TagFormField extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _TagFormFieldState extends State<TagFormField> {
|
class _TagFormFieldState extends State<TagFormField> {
|
||||||
|
static const _onlyNotAssignedId = -1;
|
||||||
|
static const _anyAssignedId = -2;
|
||||||
|
|
||||||
late final TextEditingController _textEditingController;
|
late final TextEditingController _textEditingController;
|
||||||
bool _showCreationSuffixIcon = false;
|
bool _showCreationSuffixIcon = true;
|
||||||
bool _showClearSuffixIcon = false;
|
bool _showClearSuffixIcon = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -44,7 +51,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
_textEditingController.text.toLowerCase(),
|
_textEditingController.text.toLowerCase(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.isEmpty;
|
.isEmpty ||
|
||||||
|
_textEditingController.text.isEmpty;
|
||||||
});
|
});
|
||||||
setState(() =>
|
setState(() =>
|
||||||
_showClearSuffixIcon = _textEditingController.text.isNotEmpty);
|
_showClearSuffixIcon = _textEditingController.text.isNotEmpty);
|
||||||
@@ -78,21 +86,32 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.startsWith(query.toLowerCase()))
|
.startsWith(query.toLowerCase()))
|
||||||
.map((e) => e.id!)
|
.map((e) => e.id!)
|
||||||
.toList()
|
.toList();
|
||||||
..removeWhere((element) =>
|
if (field.value is IdsTagsQuery) {
|
||||||
field.value?.ids.contains(element) ?? false);
|
suggestions.removeWhere((element) =>
|
||||||
if (widget.notAssignedSelectable) {
|
(field.value as IdsTagsQuery).ids.contains(element));
|
||||||
suggestions.insert(0, -1);
|
}
|
||||||
|
if (widget.notAssignedSelectable &&
|
||||||
|
field.value is! OnlyNotAssignedTagsQuery) {
|
||||||
|
suggestions.insert(0, _onlyNotAssignedId);
|
||||||
|
}
|
||||||
|
if (widget.anyAssignedSelectable &&
|
||||||
|
field.value is! AnyAssignedTagsQuery) {
|
||||||
|
suggestions.insert(0, _anyAssignedId);
|
||||||
}
|
}
|
||||||
return suggestions;
|
return suggestions;
|
||||||
},
|
},
|
||||||
getImmediateSuggestions: true,
|
getImmediateSuggestions: true,
|
||||||
animationStart: 1,
|
animationStart: 1,
|
||||||
itemBuilder: (context, data) {
|
itemBuilder: (context, data) {
|
||||||
if (data == -1) {
|
if (data == _onlyNotAssignedId) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(S.of(context).labelNotAssignedText),
|
title: Text(S.of(context).labelNotAssignedText),
|
||||||
);
|
);
|
||||||
|
} else if (data == _anyAssignedId) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(S.of(context).labelAnyAssignedText),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final tag = tagState[data]!;
|
final tag = tagState[data]!;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
@@ -108,33 +127,48 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onSuggestionSelected: (id) {
|
onSuggestionSelected: (id) {
|
||||||
if (id == -1) {
|
if (id == _onlyNotAssignedId) {
|
||||||
field.didChange(const TagsQuery.notAssigned());
|
//Not assigned tag
|
||||||
|
field.didChange(const OnlyNotAssignedTagsQuery());
|
||||||
return;
|
return;
|
||||||
|
} else if (id == _anyAssignedId) {
|
||||||
|
field.didChange(const AnyAssignedTagsQuery());
|
||||||
} else {
|
} else {
|
||||||
field.didChange(
|
final tagsQuery = field.value is IdsTagsQuery
|
||||||
TagsQuery.fromIds([...field.value?.ids ?? [], id]));
|
? field.value as IdsTagsQuery
|
||||||
|
: const IdsTagsQuery();
|
||||||
|
field.didChange(tagsQuery
|
||||||
|
.withIdQueriesAdded([IncludeTagIdQuery(id)]));
|
||||||
}
|
}
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
},
|
},
|
||||||
direction: AxisDirection.up,
|
direction: AxisDirection.up,
|
||||||
),
|
),
|
||||||
if (field.value?.onlyNotAssigned ?? false) ...[
|
if (field.value is OnlyNotAssignedTagsQuery) ...[
|
||||||
_buildNotAssignedTag(field)
|
_buildNotAssignedTag(field)
|
||||||
|
] else if (field.value is AnyAssignedTagsQuery) ...[
|
||||||
|
_buildAnyAssignedTag(field)
|
||||||
] else ...[
|
] else ...[
|
||||||
|
// field.value is IdsTagsQuery
|
||||||
Wrap(
|
Wrap(
|
||||||
alignment: WrapAlignment.start,
|
alignment: WrapAlignment.start,
|
||||||
runAlignment: WrapAlignment.start,
|
runAlignment: WrapAlignment.start,
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
children: (field.value?.ids ?? [])
|
children: ((field.value as IdsTagsQuery).queries)
|
||||||
.map((id) => _buildTag(field, tagState[id]!))
|
.map(
|
||||||
|
(query) => _buildTag(
|
||||||
|
field,
|
||||||
|
query,
|
||||||
|
tagState[query.id]!,
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
initialValue: widget.initialValue ?? const TagsQuery.unset(),
|
initialValue: widget.initialValue ?? const IdsTagsQuery(),
|
||||||
name: widget.name,
|
name: widget.name,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -172,8 +206,11 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
|
final tagsQuery = field.value is IdsTagsQuery
|
||||||
|
? field.value as IdsTagsQuery
|
||||||
|
: const IdsTagsQuery();
|
||||||
field.didChange(
|
field.didChange(
|
||||||
TagsQuery.fromIds([...field.value?.ids ?? [], tag.id!]),
|
tagsQuery.withIdQueriesAdded([IncludeTagIdQuery(tag.id!)]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
@@ -191,24 +228,43 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
),
|
),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
|
||||||
|
onDeleted: () => field.didChange(const IdsTagsQuery()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTag(
|
||||||
|
FormFieldState<TagsQuery> field,
|
||||||
|
TagIdQuery query,
|
||||||
|
Tag tag,
|
||||||
|
) {
|
||||||
|
final currentQuery = field.value as IdsTagsQuery;
|
||||||
|
final isIncludedTag = currentQuery.includedIds.contains(query.id);
|
||||||
|
|
||||||
|
return InputChip(
|
||||||
|
label: Text(
|
||||||
|
tag.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: tag.textColor,
|
||||||
|
decoration: !isIncludedTag ? TextDecoration.lineThrough : null,
|
||||||
|
decorationThickness: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: widget.excludeAllowed
|
||||||
|
? () => field.didChange(currentQuery.withIdQueryToggled(tag.id!))
|
||||||
|
: null,
|
||||||
|
backgroundColor: tag.color,
|
||||||
onDeleted: () => field.didChange(
|
onDeleted: () => field.didChange(
|
||||||
const TagsQuery.unset(),
|
(field.value as IdsTagsQuery).withIdsRemoved([tag.id!]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTag(FormFieldState<TagsQuery> field, Tag tag) {
|
Widget _buildAnyAssignedTag(FormFieldState<TagsQuery> field) {
|
||||||
return InputChip(
|
return InputChip(
|
||||||
label: Text(
|
label: Text(S.of(context).labelAnyAssignedText),
|
||||||
tag.name,
|
backgroundColor:
|
||||||
style: TextStyle(color: tag.textColor),
|
Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.12),
|
||||||
),
|
onDeleted: () => field.didChange(const IdsTagsQuery()),
|
||||||
backgroundColor: tag.color,
|
|
||||||
onDeleted: () => field.didChange(
|
|
||||||
TagsQuery.fromIds(
|
|
||||||
field.value?.ids.where((element) => element != tag.id).toList() ?? [],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:paperless_mobile/features/labels/tags/model/tag.model.dart';
|
|||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
||||||
|
|
||||||
class TagsWidget extends StatefulWidget {
|
class TagsWidget extends StatefulWidget {
|
||||||
final List<int> tagIds;
|
final Iterable<int> tagIds;
|
||||||
final bool isMultiLine;
|
final bool isMultiLine;
|
||||||
final void Function()? afterTagTapped;
|
final void Function()? afterTagTapped;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
|
|||||||
@@ -3,31 +3,29 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
import 'package:paperless_mobile/core/bloc/label_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/documents/model/document_filter.dart';
|
||||||
import 'package:paperless_mobile/features/documents/model/query_parameters/correspondent_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/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/storage_path_query.dart';
|
||||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.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/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/documents/model/document_filter.dart';
|
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/model/correspondent.model.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/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/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/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/document_type/view/pages/edit_document_type_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/model/label.model.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/model/storage_path.model.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/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/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/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/add_tag_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/pages/edit_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_item.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class LabelsPage extends StatefulWidget {
|
class LabelsPage extends StatefulWidget {
|
||||||
@@ -147,7 +145,7 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
LabelTabView<Tag>(
|
LabelTabView<Tag>(
|
||||||
cubit: BlocProvider.of<TagCubit>(context),
|
cubit: BlocProvider.of<TagCubit>(context),
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
tags: TagsQuery.fromIds([label.id!]),
|
tags: IdsTagsQuery.fromIds([label.id!]),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
),
|
),
|
||||||
onOpenEditPage: _openEditTagPage,
|
onOpenEditPage: _openEditTagPage,
|
||||||
|
|||||||
@@ -172,6 +172,9 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
|||||||
),
|
),
|
||||||
const TagFormField(
|
const TagFormField(
|
||||||
name: DocumentModel.tagsKey,
|
name: DocumentModel.tagsKey,
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
anyAssignedSelectable: false,
|
||||||
|
excludeAllowed: false,
|
||||||
//Label: "Tags" + " *",
|
//Label: "Tags" + " *",
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
@@ -194,10 +197,9 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
|||||||
final createdAt = fv[DocumentModel.createdKey] as DateTime?;
|
final createdAt = fv[DocumentModel.createdKey] as DateTime?;
|
||||||
final title = fv[DocumentModel.titleKey] as String;
|
final title = fv[DocumentModel.titleKey] as String;
|
||||||
final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter;
|
final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter;
|
||||||
final tags = fv[DocumentModel.tagsKey] as TagsQuery;
|
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery;
|
||||||
final correspondent =
|
final correspondent =
|
||||||
fv[DocumentModel.correspondentKey] as IdQueryParameter;
|
fv[DocumentModel.correspondentKey] as IdQueryParameter;
|
||||||
|
|
||||||
await BlocProvider.of<DocumentsCubit>(context).addDocument(
|
await BlocProvider.of<DocumentsCubit>(context).addDocument(
|
||||||
widget.fileBytes,
|
widget.fileBytes,
|
||||||
_formKey.currentState?.value[fkFileName],
|
_formKey.currentState?.value[fkFileName],
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import 'package:paperless_mobile/core/model/error_message.dart';
|
|||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/documents/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/home/view/widget/info_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
||||||
@@ -53,6 +55,24 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text(S.of(context).documentScannerPageTitle),
|
title: Text(S.of(context).documentScannerPageTitle),
|
||||||
actions: [
|
actions: [
|
||||||
|
BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: state.isNotEmpty
|
||||||
|
? () => Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DocumentView(
|
||||||
|
documentBytes:
|
||||||
|
_buildDocumentFromImageFiles(state).save(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.preview),
|
||||||
|
tooltip: S.of(context).documentScannerPageResetButtonTooltipText,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
BlocBuilder<DocumentScannerCubit, List<File>>(
|
BlocBuilder<DocumentScannerCubit, List<File>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
|||||||
@@ -192,5 +192,8 @@
|
|||||||
"errorMessageMissingClientCertificate": "Ein Client Zerfitikat wurde erwartet, aber nicht gesendet. Bitte konfiguriere ein gültiges Zertifikat.",
|
"errorMessageMissingClientCertificate": "Ein Client Zerfitikat wurde erwartet, aber nicht gesendet. Bitte konfiguriere ein gültiges Zertifikat.",
|
||||||
"serverInformationPaperlessVersionText": "Paperless Server-Version",
|
"serverInformationPaperlessVersionText": "Paperless Server-Version",
|
||||||
"errorReportLabel": "MELDEN",
|
"errorReportLabel": "MELDEN",
|
||||||
"appDrawerHeaderLoggedInAsText": "Eingeloggt als "
|
"appDrawerHeaderLoggedInAsText": "Eingeloggt als ",
|
||||||
|
"labelAnyAssignedText": "Beliebig zugewiesen",
|
||||||
|
"deleteViewDialogContentText": "Möchtest Du diese Ansicht wirklich löschen?",
|
||||||
|
"deleteViewDialogTitleText": "Lösche Ansicht "
|
||||||
}
|
}
|
||||||
@@ -193,5 +193,8 @@
|
|||||||
"errorMessageMissingClientCertificate": "A client certificate was expected but not sent. Please provide a valid client certificate.",
|
"errorMessageMissingClientCertificate": "A client certificate was expected but not sent. Please provide a valid client certificate.",
|
||||||
"serverInformationPaperlessVersionText": "Paperless server version",
|
"serverInformationPaperlessVersionText": "Paperless server version",
|
||||||
"errorReportLabel": "REPORT",
|
"errorReportLabel": "REPORT",
|
||||||
"appDrawerHeaderLoggedInAsText": "Logged in as "
|
"appDrawerHeaderLoggedInAsText": "Logged in as ",
|
||||||
|
"labelAnyAssignedText": "Any assigned",
|
||||||
|
"deleteViewDialogContentText": "Do you really want to delete this view?",
|
||||||
|
"deleteViewDialogTitleText": "Delete view "
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ void main() async {
|
|||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
Intl.systemLocale = await findSystemLocale();
|
Intl.systemLocale = await findSystemLocale();
|
||||||
|
|
||||||
// Required for client certificates
|
// Required for self signed client certificates
|
||||||
HttpOverrides.global = X509HttpOverrides();
|
HttpOverrides.global = X509HttpOverrides();
|
||||||
|
|
||||||
configureDependencies();
|
configureDependencies();
|
||||||
|
|||||||
@@ -31,15 +31,15 @@ void main() {
|
|||||||
'value': "69",
|
'value': "69",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'rule_type': FilterRule.tagRule,
|
'rule_type': FilterRule.includeTagsRule,
|
||||||
'value': "1",
|
'value': "1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'rule_type': FilterRule.tagRule,
|
'rule_type': FilterRule.includeTagsRule,
|
||||||
'value': "2",
|
'value': "2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'rule_type': FilterRule.tagRule,
|
'rule_type': FilterRule.includeTagsRule,
|
||||||
'value': "3",
|
'value': "3",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -73,7 +73,7 @@ void main() {
|
|||||||
correspondent: const CorrespondentQuery.fromId(42),
|
correspondent: const CorrespondentQuery.fromId(42),
|
||||||
documentType: const DocumentTypeQuery.fromId(69),
|
documentType: const DocumentTypeQuery.fromId(69),
|
||||||
storagePath: const StoragePathQuery.fromId(14),
|
storagePath: const StoragePathQuery.fromId(14),
|
||||||
tags: const TagsQuery.fromIds([1, 2, 3]),
|
tags: IdsTagsQuery.fromIds([1, 2, 3]),
|
||||||
createdDateBefore: DateTime.parse("2022-10-27"),
|
createdDateBefore: DateTime.parse("2022-10-27"),
|
||||||
createdDateAfter: DateTime.parse("2022-09-27"),
|
createdDateAfter: DateTime.parse("2022-09-27"),
|
||||||
addedDateBefore: DateTime.parse("2022-09-26"),
|
addedDateBefore: DateTime.parse("2022-09-26"),
|
||||||
@@ -121,7 +121,7 @@ void main() {
|
|||||||
'value': null,
|
'value': null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'rule_type': FilterRule.tagRule,
|
'rule_type': FilterRule.includeTagsRule,
|
||||||
'value': null,
|
'value': null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,7 +134,7 @@ void main() {
|
|||||||
correspondent: const CorrespondentQuery.notAssigned(),
|
correspondent: const CorrespondentQuery.notAssigned(),
|
||||||
documentType: const DocumentTypeQuery.notAssigned(),
|
documentType: const DocumentTypeQuery.notAssigned(),
|
||||||
storagePath: const StoragePathQuery.notAssigned(),
|
storagePath: const StoragePathQuery.notAssigned(),
|
||||||
tags: const TagsQuery.notAssigned(),
|
tags: const OnlyNotAssignedTagsQuery(),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -148,7 +148,7 @@ void main() {
|
|||||||
correspondent: const CorrespondentQuery.fromId(1),
|
correspondent: const CorrespondentQuery.fromId(1),
|
||||||
documentType: const DocumentTypeQuery.fromId(2),
|
documentType: const DocumentTypeQuery.fromId(2),
|
||||||
storagePath: const StoragePathQuery.fromId(3),
|
storagePath: const StoragePathQuery.fromId(3),
|
||||||
tags: const TagsQuery.fromIds([4, 5, 6]),
|
tags: IdsTagsQuery.fromIds([4, 5, 6]),
|
||||||
sortField: SortField.added,
|
sortField: SortField.added,
|
||||||
sortOrder: SortOrder.ascending,
|
sortOrder: SortOrder.ascending,
|
||||||
addedDateAfter: DateTime.parse("2020-01-01"),
|
addedDateAfter: DateTime.parse("2020-01-01"),
|
||||||
@@ -173,9 +173,9 @@ void main() {
|
|||||||
FilterRule(FilterRule.correspondentRule, "1"),
|
FilterRule(FilterRule.correspondentRule, "1"),
|
||||||
FilterRule(FilterRule.documentTypeRule, "2"),
|
FilterRule(FilterRule.documentTypeRule, "2"),
|
||||||
FilterRule(FilterRule.storagePathRule, "3"),
|
FilterRule(FilterRule.storagePathRule, "3"),
|
||||||
FilterRule(FilterRule.tagRule, "4"),
|
FilterRule(FilterRule.includeTagsRule, "4"),
|
||||||
FilterRule(FilterRule.tagRule, "5"),
|
FilterRule(FilterRule.includeTagsRule, "5"),
|
||||||
FilterRule(FilterRule.tagRule, "6"),
|
FilterRule(FilterRule.includeTagsRule, "6"),
|
||||||
FilterRule(FilterRule.addedAfterRule, "2020-01-01"),
|
FilterRule(FilterRule.addedAfterRule, "2020-01-01"),
|
||||||
FilterRule(FilterRule.addedBeforeRule, "2020-03-01"),
|
FilterRule(FilterRule.addedBeforeRule, "2020-03-01"),
|
||||||
FilterRule(FilterRule.createdAfterRule, "2020-02-01"),
|
FilterRule(FilterRule.createdAfterRule, "2020-02-01"),
|
||||||
@@ -194,7 +194,7 @@ void main() {
|
|||||||
correspondent: CorrespondentQuery.unset(),
|
correspondent: CorrespondentQuery.unset(),
|
||||||
documentType: DocumentTypeQuery.unset(),
|
documentType: DocumentTypeQuery.unset(),
|
||||||
storagePath: StoragePathQuery.unset(),
|
storagePath: StoragePathQuery.unset(),
|
||||||
tags: TagsQuery.unset(),
|
tags: IdsTagsQuery.unset(),
|
||||||
sortField: SortField.created,
|
sortField: SortField.created,
|
||||||
sortOrder: SortOrder.descending,
|
sortOrder: SortOrder.descending,
|
||||||
addedDateAfter: null,
|
addedDateAfter: null,
|
||||||
@@ -227,7 +227,7 @@ void main() {
|
|||||||
correspondent: CorrespondentQuery.notAssigned(),
|
correspondent: CorrespondentQuery.notAssigned(),
|
||||||
documentType: DocumentTypeQuery.notAssigned(),
|
documentType: DocumentTypeQuery.notAssigned(),
|
||||||
storagePath: StoragePathQuery.notAssigned(),
|
storagePath: StoragePathQuery.notAssigned(),
|
||||||
tags: TagsQuery.notAssigned(),
|
tags: OnlyNotAssignedTagsQuery(),
|
||||||
sortField: SortField.created,
|
sortField: SortField.created,
|
||||||
sortOrder: SortOrder.descending,
|
sortOrder: SortOrder.descending,
|
||||||
),
|
),
|
||||||
@@ -246,7 +246,7 @@ void main() {
|
|||||||
FilterRule(FilterRule.correspondentRule, null),
|
FilterRule(FilterRule.correspondentRule, null),
|
||||||
FilterRule(FilterRule.documentTypeRule, null),
|
FilterRule(FilterRule.documentTypeRule, null),
|
||||||
FilterRule(FilterRule.storagePathRule, null),
|
FilterRule(FilterRule.storagePathRule, null),
|
||||||
FilterRule(FilterRule.tagRule, null),
|
FilterRule(FilterRule.includeTagsRule, null),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user