Implemented extended query constraints

This commit is contained in:
Anton Stubenbord
2023-01-02 01:30:08 +01:00
parent 457d9f2684
commit 18ec5874a5
13 changed files with 323 additions and 235 deletions

View File

@@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/widgets/form_builder_fields/extended_date_range_form_field/form_builder_extended_date_range_picker.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.dart';
import 'package:paperless_mobile/features/documents/view/widgets/search/text_query_form_field.dart';
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
@@ -37,6 +36,13 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
static const fkAddedAt = DocumentModel.addedKey;
final _formKey = GlobalKey<FormBuilderState>();
late bool _allowOnlyExtendedQuery;
@override
void initState() {
super.initState();
_allowOnlyExtendedQuery = widget.initialFilter.forceExtendedQuery;
}
@override
Widget build(BuildContext context) {
@@ -108,14 +114,20 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
),
).padded(),
FormBuilderExtendedDateRangePicker(
name: DocumentModel.createdKey,
name: fkCreatedAt,
initialValue: widget.initialFilter.created,
labelText: S.of(context).documentCreatedPropertyLabel,
onChanged: (_) {
_checkQueryConstraints();
},
).padded(),
FormBuilderExtendedDateRangePicker(
name: DocumentModel.addedKey,
name: fkAddedAt,
initialValue: widget.initialFilter.added,
labelText: S.of(context).documentAddedPropertyLabel,
onChanged: (_) {
_checkQueryConstraints();
},
).padded(),
_buildCorrespondentFormField().padded(),
_buildDocumentTypeFormField().padded(),
@@ -154,11 +166,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
void _resetFilter() async {
FocusScope.of(context).unfocus();
Navigator.pop(
context,
DocumentFilter.initial.copyWith(
sortField: widget.initialFilter.sortField,
sortOrder: widget.initialFilter.sortOrder,
));
context,
DocumentFilter.initial.copyWith(
sortField: widget.initialFilter.sortField,
sortOrder: widget.initialFilter.sortOrder,
),
);
}
Widget _buildDocumentTypeFormField() {
@@ -207,52 +220,24 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
}
Widget _buildQueryFormField() {
final queryType =
_formKey.currentState?.getRawValue(QueryTypeFormField.fkQueryType) ??
QueryType.titleAndContent;
late String label;
switch (queryType) {
case QueryType.title:
label = S.of(context).documentFilterQueryOptionsTitleLabel;
break;
case QueryType.titleAndContent:
label = S.of(context).documentFilterQueryOptionsTitleAndContentLabel;
break;
case QueryType.extended:
label = S.of(context).documentFilterQueryOptionsExtendedLabel;
break;
}
return FormBuilderTextField(
return TextQueryFormField(
name: fkQuery,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search_outlined),
labelText: label,
suffixIcon: QueryTypeFormField(
initialValue: widget.initialFilter.queryType,
afterSelected: (queryType) => setState(() {}),
),
),
initialValue: widget.initialFilter.queryText,
onlyExtendedQueryAllowed: _allowOnlyExtendedQuery,
initialValue: widget.initialFilter.query,
);
}
void _onApplyFilter() async {
_formKey.currentState?.save();
if (_formKey.currentState?.validate() ?? false) {
final v = _formKey.currentState!.value;
DocumentFilter newFilter = _assembleFilter();
try {
FocusScope.of(context).unfocus();
Navigator.pop(context, newFilter);
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
FocusScope.of(context).unfocus();
Navigator.pop(context, newFilter);
}
}
DocumentFilter _assembleFilter() {
_formKey.currentState?.save();
final v = _formKey.currentState!.value;
return DocumentFilter(
correspondent: v[fkCorrespondent] as IdQueryParameter? ??
@@ -263,10 +248,9 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
DocumentFilter.initial.storagePath,
tags:
v[DocumentModel.tagsKey] as TagsQuery? ?? DocumentFilter.initial.tags,
queryText: v[fkQuery] as String?,
query: v[fkQuery] as TextQuery? ?? DocumentFilter.initial.query,
created: (v[fkCreatedAt] as DateRangeQuery),
added: (v[fkAddedAt] as DateRangeQuery),
queryType: v[QueryTypeFormField.fkQueryType] as QueryType,
asnQuery: widget.initialFilter.asnQuery,
page: 1,
pageSize: widget.initialFilter.pageSize,
@@ -274,16 +258,18 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
sortOrder: widget.initialFilter.sortOrder,
);
}
}
DateTimeRange? _dateTimeRangeOfNullable(DateTime? start, DateTime? end) {
if (start == null && end == null) {
return null;
void _checkQueryConstraints() {
final filter = _assembleFilter();
if (filter.forceExtendedQuery) {
setState(() => _allowOnlyExtendedQuery = true);
final queryField = _formKey.currentState?.fields[fkQuery];
queryField?.didChange(
(queryField.value as TextQuery?)
?.copyWith(queryType: QueryType.extended),
);
} else {
setState(() => _allowOnlyExtendedQuery = false);
}
}
if (start != null && end != null) {
return DateTimeRange(start: start, end: end);
}
assert(start != null || end != null);
final singleDate = (start ?? end)!;
return DateTimeRange(start: singleDate, end: singleDate);
}

View File

@@ -1,53 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/generated/l10n.dart';
class QueryTypeFormField extends StatelessWidget {
static const fkQueryType = 'queryType';
final QueryType? initialValue;
final void Function(QueryType)? afterSelected;
const QueryTypeFormField({
super.key,
this.initialValue,
this.afterSelected,
});
@override
Widget build(BuildContext context) {
return FormBuilderField<QueryType>(
builder: (field) => PopupMenuButton<QueryType>(
itemBuilder: (context) => [
PopupMenuItem(
child: ListTile(
title: Text(
S.of(context).documentFilterQueryOptionsTitleAndContentLabel),
),
value: QueryType.titleAndContent,
),
PopupMenuItem(
child: ListTile(
title: Text(S.of(context).documentFilterQueryOptionsTitleLabel),
),
value: QueryType.title,
),
PopupMenuItem(
child: ListTile(
title:
Text(S.of(context).documentFilterQueryOptionsExtendedLabel),
),
value: QueryType.extended,
),
//TODO: Add support for ASN queries
],
onSelected: (selection) {
field.didChange(selection);
afterSelected?.call(selection);
},
child: const Icon(Icons.more_vert),
),
initialValue: initialValue,
name: QueryTypeFormField.fkQueryType,
);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/generated/l10n.dart';
class TextQueryFormField extends StatelessWidget {
final String name;
final TextQuery? initialValue;
final bool onlyExtendedQueryAllowed;
const TextQueryFormField({
super.key,
required this.name,
this.initialValue,
required this.onlyExtendedQueryAllowed,
});
@override
Widget build(BuildContext context) {
return FormBuilderField<TextQuery>(
name: name,
initialValue: initialValue,
builder: (field) {
return TextFormField(
initialValue: initialValue?.queryText,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search_outlined),
labelText: _buildLabelText(context, field.value!.queryType),
suffixIcon: PopupMenuButton<QueryType>(
itemBuilder: (context) => [
if (!onlyExtendedQueryAllowed) ...[
PopupMenuItem(
child: ListTile(
title: Text(S
.of(context)
.documentFilterQueryOptionsTitleAndContentLabel),
),
value: QueryType.titleAndContent,
),
PopupMenuItem(
child: ListTile(
title: Text(
S.of(context).documentFilterQueryOptionsTitleLabel),
),
value: QueryType.title,
),
],
PopupMenuItem(
child: ListTile(
title: Text(
S.of(context).documentFilterQueryOptionsExtendedLabel),
),
value: QueryType.extended,
),
],
onSelected: (selection) {
field.didChange(field.value?.copyWith(queryType: selection));
},
child: const Icon(Icons.more_vert),
),
),
onChanged: (value) {
field.didChange(field.value?.copyWith(queryText: value));
},
);
},
);
}
String _buildLabelText(BuildContext context, QueryType queryType) {
switch (queryType) {
case QueryType.title:
return S.of(context).documentFilterQueryOptionsTitleLabel;
case QueryType.titleAndContent:
return S.of(context).documentFilterQueryOptionsTitleAndContentLabel;
case QueryType.extended:
return S.of(context).documentFilterQueryOptionsExtendedLabel;
default:
return '';
}
}
}