mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 14:07:59 -06:00
WIP - more decoupling of blocs
This commit is contained in:
@@ -34,4 +34,10 @@ class AuthenticationInterceptor implements InterceptorContract {
|
|||||||
Future<BaseResponse> interceptResponse(
|
Future<BaseResponse> interceptResponse(
|
||||||
{required BaseResponse response}) async =>
|
{required BaseResponse response}) async =>
|
||||||
response;
|
response;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptRequest() async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptResponse() async => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,4 +25,10 @@ class BaseUrlInterceptor implements InterceptorContract {
|
|||||||
Future<BaseResponse> interceptResponse(
|
Future<BaseResponse> interceptResponse(
|
||||||
{required BaseResponse response}) async =>
|
{required BaseResponse response}) async =>
|
||||||
response;
|
response;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptRequest() async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptResponse() async => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,4 +25,10 @@ class LanguageHeaderInterceptor implements InterceptorContract {
|
|||||||
Future<BaseResponse> interceptResponse(
|
Future<BaseResponse> interceptResponse(
|
||||||
{required BaseResponse response}) async =>
|
{required BaseResponse response}) async =>
|
||||||
response;
|
response;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptRequest() async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptResponse() async => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:http/http.dart';
|
import 'package:http_interceptor/http_interceptor.dart';
|
||||||
import 'package:http_interceptor/http/http.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
const interceptedRoutes = ['thumb/'];
|
const interceptedRoutes = ['thumb/'];
|
||||||
@@ -33,4 +32,10 @@ class ResponseConversionInterceptor implements InterceptorContract {
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptRequest() async => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldInterceptResponse() async => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,4 +64,7 @@ class CorrespondentRepositoryImpl implements LabelRepository<Correspondent> {
|
|||||||
void clear() {
|
void clear() {
|
||||||
_subject.add(const {});
|
_subject.add(const {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get current => _subject.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,4 +63,7 @@ class DocumentTypeRepositoryImpl implements LabelRepository<DocumentType> {
|
|||||||
void clear() {
|
void clear() {
|
||||||
_subject.add(const {});
|
_subject.add(const {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get current => _subject.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,4 +63,7 @@ class StoragePathRepositoryImpl implements LabelRepository<StoragePath> {
|
|||||||
void clear() {
|
void clear() {
|
||||||
_subject.add(const {});
|
_subject.add(const {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get current => _subject.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,4 +62,7 @@ class TagRepositoryImpl implements LabelRepository<Tag> {
|
|||||||
void clear() {
|
void clear() {
|
||||||
_subject.add(const {});
|
_subject.add(const {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get current => _subject.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
abstract class LabelRepository<T extends Label> {
|
abstract class LabelRepository<T extends Label> {
|
||||||
Stream<Map<int, T>> get labels;
|
Stream<Map<int, T>> get labels;
|
||||||
|
|
||||||
|
Map<int, T> get current;
|
||||||
|
|
||||||
Future<T> create(T label);
|
Future<T> create(T label);
|
||||||
Future<T?> find(int id);
|
Future<T?> find(int id);
|
||||||
Future<Iterable<T>> findAll([Iterable<int>? ids]);
|
Future<Iterable<T>> findAll([Iterable<int>? ids]);
|
||||||
|
|||||||
@@ -71,8 +71,7 @@ Note: If you have the GitHub Android app installed, the descriptions will not be
|
|||||||
Text(
|
Text(
|
||||||
'Stack Trace',
|
'Stack Trace',
|
||||||
style: Theme.of(context).textTheme.subtitle1,
|
style: Theme.of(context).textTheme.subtitle1,
|
||||||
).padded(
|
).paddedOnly(top: 8.0, left: 8.0, right: 8.0),
|
||||||
const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0)),
|
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
label: const Text('Copy'),
|
label: const Text('Copy'),
|
||||||
icon: const Icon(Icons.copy),
|
icon: const Icon(Icons.copy),
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ import 'dart:io';
|
|||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
|
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:http_interceptor/http_interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/interceptor/authentication.interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/authentication.interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/interceptor/base_url_interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/base_url_interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/interceptor/response_conversion.interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/response_conversion.interceptor.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:http/io_client.dart';
|
import 'package:http/io_client.dart';
|
||||||
import 'package:http_interceptor/http/http.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,32 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
extension WidgetPadding on Widget {
|
extension WidgetPadding on Widget {
|
||||||
Widget padded([EdgeInsetsGeometry value = const EdgeInsets.all(8)]) {
|
Widget padded([double all = 8.0]) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: value,
|
padding: EdgeInsets.all(all),
|
||||||
|
child: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget paddedSymmetrically({double horizontal = 0.0, double vertical = 0.0}) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical),
|
||||||
|
child: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget paddedOnly(
|
||||||
|
{double top = 0.0,
|
||||||
|
double bottom = 0.0,
|
||||||
|
double left = 0.0,
|
||||||
|
double right = 0.0}) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: top,
|
||||||
|
bottom: bottom,
|
||||||
|
left: left,
|
||||||
|
right: right,
|
||||||
|
),
|
||||||
child: this,
|
child: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
|
|
||||||
Future<void> delete(DocumentModel document) async {
|
Future<void> delete(DocumentModel document) async {
|
||||||
await _api.delete(document);
|
await _api.delete(document);
|
||||||
emit(const DocumentDetailsState());
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> update(DocumentModel document) async {
|
|
||||||
final updatedDocument = await _api.update(document);
|
|
||||||
emit(DocumentDetailsState(document: updatedDocument));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> assignAsn(DocumentModel document) async {
|
Future<void> assignAsn(DocumentModel document) async {
|
||||||
if (document.archiveSerialNumber == null) {
|
if (document.archiveSerialNumber == null) {
|
||||||
final int asn = await _api.findNextAsn();
|
final int asn = await _api.findNextAsn();
|
||||||
update(document.copyWith(archiveSerialNumber: asn));
|
final updatedDocument =
|
||||||
|
await _api.update(document.copyWith(archiveSerialNumber: asn));
|
||||||
|
emit(DocumentDetailsState(document: updatedDocument));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replaceDocument(DocumentModel document) {
|
||||||
|
emit(DocumentDetailsState(document: document));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
part of 'document_details_cubit.dart';
|
part of 'document_details_cubit.dart';
|
||||||
|
|
||||||
class DocumentDetailsState with EquatableMixin {
|
class DocumentDetailsState with EquatableMixin {
|
||||||
final DocumentModel? document;
|
final DocumentModel document;
|
||||||
|
|
||||||
const DocumentDetailsState({
|
const DocumentDetailsState({
|
||||||
this.document,
|
required this.document,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import 'package:paperless_mobile/features/documents/view/pages/document_edit_pag
|
|||||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_document/cubit/edit_document_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
||||||
import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart';
|
import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart';
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart';
|
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart';
|
||||||
@@ -65,9 +66,13 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
||||||
floatingActionButton: widget.allowEdit
|
floatingActionButton: widget.allowEdit
|
||||||
? FloatingActionButton(
|
? BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return FloatingActionButton(
|
||||||
child: const Icon(Icons.edit),
|
child: const Icon(Icons.edit),
|
||||||
onPressed: _onEdit,
|
onPressed: () => _onEdit(state.document),
|
||||||
|
);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
bottomNavigationBar:
|
bottomNavigationBar:
|
||||||
@@ -79,24 +84,20 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
onPressed: widget.allowEdit && state.document != null
|
onPressed: widget.allowEdit
|
||||||
? () => _onDelete(state.document!)
|
? () => _onDelete(state.document)
|
||||||
: null,
|
: null,
|
||||||
).padded(const EdgeInsets.symmetric(horizontal: 4)),
|
).paddedSymmetrically(horizontal: 4),
|
||||||
DocumentDownloadButton(
|
DocumentDownloadButton(
|
||||||
document: state.document,
|
document: state.document,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.open_in_new),
|
icon: const Icon(Icons.open_in_new),
|
||||||
onPressed: state.document != null
|
onPressed: () => _onOpen(state.document),
|
||||||
? () => _onOpen(state.document!)
|
).paddedOnly(right: 4.0),
|
||||||
: null,
|
|
||||||
).padded(const EdgeInsets.only(right: 4)),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.share),
|
icon: const Icon(Icons.share),
|
||||||
onPressed: state.document != null
|
onPressed: () => _onShare(state.document),
|
||||||
? () => _onShare(state.document!)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -123,15 +124,10 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
expandedHeight: 200.0,
|
expandedHeight: 200.0,
|
||||||
flexibleSpace:
|
flexibleSpace:
|
||||||
BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) => DocumentPreview(
|
||||||
if (state.document == null) {
|
id: state.document.id,
|
||||||
return Container(height: 200);
|
|
||||||
}
|
|
||||||
return DocumentPreview(
|
|
||||||
id: state.document!.id,
|
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
bottom: ColoredTabBar(
|
bottom: ColoredTabBar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
@@ -172,27 +168,18 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
],
|
],
|
||||||
body: BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
body: BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.document == null) {
|
|
||||||
return TabBarView(
|
|
||||||
children: [
|
|
||||||
Container(),
|
|
||||||
Container(),
|
|
||||||
Container(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return TabBarView(
|
return TabBarView(
|
||||||
children: [
|
children: [
|
||||||
_buildDocumentOverview(
|
_buildDocumentOverview(
|
||||||
state.document!,
|
state.document,
|
||||||
widget.titleAndContentQueryString,
|
widget.titleAndContentQueryString,
|
||||||
),
|
),
|
||||||
_buildDocumentContentView(
|
_buildDocumentContentView(
|
||||||
state.document!,
|
state.document,
|
||||||
widget.titleAndContentQueryString,
|
widget.titleAndContentQueryString,
|
||||||
),
|
),
|
||||||
_buildDocumentMetaDataView(
|
_buildDocumentMetaDataView(
|
||||||
state.document!,
|
state.document,
|
||||||
),
|
),
|
||||||
].padded(),
|
].padded(),
|
||||||
);
|
);
|
||||||
@@ -204,47 +191,42 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onEdit() async {
|
Future<void> _onEdit(DocumentModel document) async {
|
||||||
{
|
{
|
||||||
final cubit = BlocProvider.of<DocumentDetailsCubit>(context);
|
final cubit = BlocProvider.of<DocumentDetailsCubit>(context);
|
||||||
if (cubit.state.document == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Navigator.push<bool>(
|
Navigator.push<bool>(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => MultiRepositoryProvider(
|
builder: (context) => BlocProvider(
|
||||||
providers: [
|
create: (context) => EditDocumentCubit(
|
||||||
RepositoryProvider.value(
|
document,
|
||||||
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
documentsApi: getIt<PaperlessDocumentsApi>(),
|
||||||
|
correspondentRepository:
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
documentTypeRepository:
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
storagePathRepository:
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
tagRepository: RepositoryProvider.of<LabelRepository<Tag>>(
|
||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
RepositoryProvider.value(
|
child: BlocListener<EditDocumentCubit, EditDocumentState>(
|
||||||
value: RepositoryProvider.of<LabelRepository<Tag>>(
|
listenWhen: (previous, current) =>
|
||||||
context,
|
previous.document != current.document,
|
||||||
),
|
listener: (context, state) {
|
||||||
),
|
cubit.replaceDocument(state.document);
|
||||||
RepositoryProvider.value(
|
|
||||||
value: RepositoryProvider.of<LabelRepository<StoragePath>>(
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
RepositoryProvider.value(
|
|
||||||
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: DocumentEditPage(
|
|
||||||
document: cubit.state.document!,
|
|
||||||
onEdit: (updatedDocument) {
|
|
||||||
return BlocProvider.of<DocumentDetailsCubit>(context)
|
|
||||||
.update(updatedDocument);
|
|
||||||
},
|
},
|
||||||
|
child: const DocumentEditPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
maintainState: false,
|
maintainState: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -33,7 +32,7 @@ class _DocumentDownloadButtonState extends State<DocumentDownloadButton> {
|
|||||||
onPressed: Platform.isAndroid && widget.document != null
|
onPressed: Platform.isAndroid && widget.document != null
|
||||||
? () => _onDownload(widget.document!)
|
? () => _onDownload(widget.document!)
|
||||||
: null,
|
: null,
|
||||||
).padded(const EdgeInsets.only(right: 4));
|
).paddedOnly(right: 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onDownload(DocumentModel document) async {
|
Future<void> _onDownload(DocumentModel document) async {
|
||||||
|
|||||||
@@ -200,11 +200,12 @@ class _DocumentUploadPreparationPageState
|
|||||||
CorrespondentQuery.notAssigned,
|
CorrespondentQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.person_outline),
|
prefixIcon: const Icon(Icons.person_outline),
|
||||||
),
|
),
|
||||||
const TagFormField(
|
TagFormField(
|
||||||
name: DocumentModel.tagsKey,
|
name: DocumentModel.tagsKey,
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
anyAssignedSelectable: false,
|
anyAssignedSelectable: false,
|
||||||
excludeAllowed: false,
|
excludeAllowed: false,
|
||||||
|
selectableOptions: state.tags,
|
||||||
//Label: "Tags" + " *",
|
//Label: "Tags" + " *",
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -8,27 +7,19 @@ import 'package:form_builder_validators/form_builder_validators.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_document/cubit/edit_document_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class DocumentEditPage extends StatefulWidget {
|
class DocumentEditPage extends StatefulWidget {
|
||||||
final DocumentModel document;
|
|
||||||
final FutureOr<void> Function(DocumentModel updatedDocument) onEdit;
|
|
||||||
|
|
||||||
const DocumentEditPage({
|
const DocumentEditPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.document,
|
|
||||||
required this.onEdit,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -43,25 +34,17 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
static const fkCreatedDate = "createdAtDate";
|
static const fkCreatedDate = "createdAtDate";
|
||||||
static const fkStoragePath = 'storagePath';
|
static const fkStoragePath = 'storagePath';
|
||||||
|
|
||||||
late Future<Uint8List> documentBytes;
|
|
||||||
|
|
||||||
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
||||||
bool _isSubmitLoading = false;
|
bool _isSubmitLoading = false;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
documentBytes =
|
|
||||||
getIt<PaperlessDocumentsApi>().getPreview(widget.document.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LabelsBlocProvider(
|
return BlocBuilder<EditDocumentCubit, EditDocumentState>(
|
||||||
child: Scaffold(
|
builder: (context, state) {
|
||||||
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: _onSubmit,
|
onPressed: () => _onSubmit(state.document),
|
||||||
icon: const Icon(Icons.save),
|
icon: const Icon(Icons.save),
|
||||||
label: Text(S.of(context).genericActionSaveLabel),
|
label: Text(S.of(context).genericActionSaveLabel),
|
||||||
),
|
),
|
||||||
@@ -85,85 +68,78 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
child: FormBuilder(
|
child: FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(children: [
|
child: ListView(children: [
|
||||||
_buildTitleFormField().padded(),
|
_buildTitleFormField(state.document.title).padded(),
|
||||||
_buildCreatedAtFormField().padded(),
|
_buildCreatedAtFormField(state.document.created).padded(),
|
||||||
_buildDocumentTypeFormField().padded(),
|
_buildDocumentTypeFormField(
|
||||||
_buildCorrespondentFormField().padded(),
|
state.document.documentType, state.documentTypes)
|
||||||
_buildStoragePathFormField().padded(),
|
.padded(),
|
||||||
|
_buildCorrespondentFormField(
|
||||||
|
state.document.correspondent, state.correspondents)
|
||||||
|
.padded(),
|
||||||
|
_buildStoragePathFormField(
|
||||||
|
state.document.storagePath, state.storagePaths)
|
||||||
|
.padded(),
|
||||||
TagFormField(
|
TagFormField(
|
||||||
initialValue: IdsTagsQuery.included(widget.document.tags),
|
initialValue: IdsTagsQuery.included(state.document.tags),
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
anyAssignedSelectable: false,
|
anyAssignedSelectable: false,
|
||||||
excludeAllowed: false,
|
excludeAllowed: false,
|
||||||
name: fkTags,
|
name: fkTags,
|
||||||
|
selectableOptions: state.tags,
|
||||||
).padded(),
|
).padded(),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
));
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>
|
Widget _buildStoragePathFormField(
|
||||||
_buildStoragePathFormField() {
|
int? initialId, Map<int, StoragePath> options) {
|
||||||
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider.value(
|
||||||
RepositoryProvider.value(
|
|
||||||
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
child: AddStoragePathPage(initalValue: initialValue),
|
child: AddStoragePathPage(initalValue: initialValue),
|
||||||
),
|
),
|
||||||
label: S.of(context).documentStoragePathPropertyLabel,
|
label: S.of(context).documentStoragePathPropertyLabel,
|
||||||
state: state.labels,
|
state: options,
|
||||||
initialValue: StoragePathQuery.fromId(widget.document.storagePath),
|
initialValue: StoragePathQuery.fromId(initialId),
|
||||||
name: fkStoragePath,
|
name: fkStoragePath,
|
||||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.folder_outlined),
|
prefixIcon: const Icon(Icons.folder_outlined),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>
|
Widget _buildCorrespondentFormField(
|
||||||
_buildCorrespondentFormField() {
|
int? initialId, Map<int, Correspondent> options) {
|
||||||
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider.value(
|
||||||
RepositoryProvider.value(
|
|
||||||
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
child: AddCorrespondentPage(initialName: initialValue),
|
child: AddCorrespondentPage(initialName: initialValue),
|
||||||
),
|
),
|
||||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||||
state: state.labels,
|
state: options,
|
||||||
initialValue:
|
initialValue: CorrespondentQuery.fromId(initialId),
|
||||||
CorrespondentQuery.fromId(widget.document.correspondent),
|
|
||||||
name: fkCorrespondent,
|
name: fkCorrespondent,
|
||||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.person_outlined),
|
prefixIcon: const Icon(Icons.person_outlined),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>
|
Widget _buildDocumentTypeFormField(
|
||||||
_buildDocumentTypeFormField() {
|
int? initialId, Map<int, DocumentType> options) {
|
||||||
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
labelCreationWidgetBuilder: (currentInput) =>
|
labelCreationWidgetBuilder: (currentInput) => RepositoryProvider.value(
|
||||||
RepositoryProvider.value(
|
|
||||||
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
context,
|
context,
|
||||||
),
|
),
|
||||||
@@ -172,21 +148,19 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||||
initialValue: DocumentTypeQuery.fromId(widget.document.documentType),
|
initialValue: DocumentTypeQuery.fromId(initialId),
|
||||||
state: state.labels,
|
state: options,
|
||||||
name: fkDocumentType,
|
name: fkDocumentType,
|
||||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.description_outlined),
|
prefixIcon: const Icon(Icons.description_outlined),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onSubmit() async {
|
Future<void> _onSubmit(DocumentModel document) async {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
final values = _formKey.currentState!.value;
|
final values = _formKey.currentState!.value;
|
||||||
var updatedDocument = widget.document.copyWith(
|
var mergedDocument = document.copyWith(
|
||||||
title: values[fkTitle],
|
title: values[fkTitle],
|
||||||
created: values[fkCreatedDate],
|
created: values[fkCreatedDate],
|
||||||
overwriteDocumentType: true,
|
overwriteDocumentType: true,
|
||||||
@@ -201,9 +175,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isSubmitLoading = true;
|
_isSubmitLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await widget.onEdit(updatedDocument);
|
await BlocProvider.of<EditDocumentCubit>(context)
|
||||||
|
.updateDocument(mergedDocument);
|
||||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
@@ -216,18 +190,18 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTitleFormField() {
|
Widget _buildTitleFormField(String? initialTitle) {
|
||||||
return FormBuilderTextField(
|
return FormBuilderTextField(
|
||||||
name: fkTitle,
|
name: fkTitle,
|
||||||
validator: FormBuilderValidators.required(),
|
validator: FormBuilderValidators.required(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text(S.of(context).documentTitlePropertyLabel),
|
label: Text(S.of(context).documentTitlePropertyLabel),
|
||||||
),
|
),
|
||||||
initialValue: widget.document.title,
|
initialValue: initialTitle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCreatedAtFormField() {
|
Widget _buildCreatedAtFormField(DateTime? initialCreatedAtDate) {
|
||||||
return FormBuilderDateTimePicker(
|
return FormBuilderDateTimePicker(
|
||||||
inputType: InputType.date,
|
inputType: InputType.date,
|
||||||
name: fkCreatedDate,
|
name: fkCreatedDate,
|
||||||
@@ -235,7 +209,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
prefixIcon: const Icon(Icons.calendar_month_outlined),
|
prefixIcon: const Icon(Icons.calendar_month_outlined),
|
||||||
label: Text(S.of(context).documentCreatedPropertyLabel),
|
label: Text(S.of(context).documentCreatedPropertyLabel),
|
||||||
),
|
),
|
||||||
initialValue: widget.document.created,
|
initialValue: initialCreatedAtDate,
|
||||||
format: DateFormat("dd. MMMM yyyy"), //TODO: Localized date format
|
format: DateFormat("dd. MMMM yyyy"), //TODO: Localized date format
|
||||||
initialEntryMode: DatePickerEntryMode.calendar,
|
initialEntryMode: DatePickerEntryMode.calendar,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import 'package:badges/badges.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
||||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||||
@@ -24,7 +24,6 @@ import 'package:paperless_mobile/features/settings/bloc/application_settings_cub
|
|||||||
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:sliding_up_panel/sliding_up_panel.dart';
|
|
||||||
|
|
||||||
class DocumentsPage extends StatefulWidget {
|
class DocumentsPage extends StatefulWidget {
|
||||||
const DocumentsPage({Key? key}) : super(key: key);
|
const DocumentsPage({Key? key}) : super(key: key);
|
||||||
@@ -35,16 +34,17 @@ class DocumentsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _DocumentsPageState extends State<DocumentsPage> {
|
class _DocumentsPageState extends State<DocumentsPage> {
|
||||||
late final DocumentsCubit _documentsCubit;
|
late final DocumentsCubit _documentsCubit;
|
||||||
|
late final SavedViewCubit _savedViewCubit;
|
||||||
|
|
||||||
final _pagingController = PagingController<int, DocumentModel>(
|
final _pagingController = PagingController<int, DocumentModel>(
|
||||||
firstPageKey: 1,
|
firstPageKey: 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
final _filterPanelController = PanelController();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_documentsCubit = BlocProvider.of<DocumentsCubit>(context);
|
_documentsCubit = BlocProvider.of<DocumentsCubit>(context);
|
||||||
|
_savedViewCubit = BlocProvider.of<SavedViewCubit>(context);
|
||||||
try {
|
try {
|
||||||
_documentsCubit.load();
|
_documentsCubit.load();
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
@@ -59,49 +59,9 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadNewPage(int pageKey) async {
|
|
||||||
final pageCount = _documentsCubit.state
|
|
||||||
.inferPageCount(pageSize: _documentsCubit.state.filter.pageSize);
|
|
||||||
if (pageCount <= pageKey + 1) {
|
|
||||||
_pagingController.nextPageKey = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await _documentsCubit.loadMore();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSelected(DocumentModel model) {
|
|
||||||
_documentsCubit.toggleDocumentSelection(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onRefresh() async {
|
|
||||||
try {
|
|
||||||
await _documentsCubit.updateCurrentFilter(
|
|
||||||
(filter) => filter.copyWith(page: 1),
|
|
||||||
);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return WillPopScope(
|
return BlocConsumer<ConnectivityCubit, ConnectivityState>(
|
||||||
onWillPop: () async {
|
|
||||||
if (_filterPanelController.isPanelOpen) {
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
_filterPanelController.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_documentsCubit.state.selection.isNotEmpty) {
|
|
||||||
_documentsCubit.resetSelection();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
child: BlocConsumer<ConnectivityCubit, ConnectivityState>(
|
|
||||||
listenWhen: (previous, current) =>
|
listenWhen: (previous, current) =>
|
||||||
previous != ConnectivityState.connected &&
|
previous != ConnectivityState.connected &&
|
||||||
current == ConnectivityState.connected,
|
current == ConnectivityState.connected,
|
||||||
@@ -116,40 +76,52 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
afterInboxClosed: () => _documentsCubit.reload(),
|
afterInboxClosed: () => _documentsCubit.reload(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
resizeToAvoidBottomInset: true,
|
floatingActionButton: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
body: SlidingUpPanel(
|
|
||||||
backdropEnabled: true,
|
|
||||||
parallaxEnabled: true,
|
|
||||||
parallaxOffset: .5,
|
|
||||||
controller: _filterPanelController,
|
|
||||||
defaultPanelState: PanelState.CLOSED,
|
|
||||||
minHeight: 48,
|
|
||||||
maxHeight: (MediaQuery.of(context).size.height * 3) / 4,
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(16),
|
|
||||||
topRight: Radius.circular(16),
|
|
||||||
),
|
|
||||||
body: _buildBody(connectivityState),
|
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
panelBuilder: (scrollController) =>
|
|
||||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return LabelsBlocProvider(
|
final appliedFiltersCount = state.filter.appliedFiltersCount;
|
||||||
|
return Badge(
|
||||||
|
toAnimate: false,
|
||||||
|
showBadge: appliedFiltersCount > 0,
|
||||||
|
badgeContent: appliedFiltersCount > 0
|
||||||
|
? Text(state.filter.appliedFiltersCount.toString())
|
||||||
|
: null,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
child: const Icon(Icons.filter_alt),
|
||||||
|
onPressed: _openDocumentFilter,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
body: _buildBody(connectivityState));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openDocumentFilter() async {
|
||||||
|
final filter = await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height - kToolbarHeight - 16,
|
||||||
|
child: LabelsBlocProvider(
|
||||||
child: DocumentFilterPanel(
|
child: DocumentFilterPanel(
|
||||||
panelController: _filterPanelController,
|
initialFilter: _documentsCubit.state.filter,
|
||||||
scrollController: scrollController,
|
|
||||||
initialFilter: state.filter,
|
|
||||||
onFilterChanged: (filter) =>
|
|
||||||
_documentsCubit.updateFilter(filter: filter),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
|
isDismissible: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16.0),
|
||||||
|
topRight: Radius.circular(16.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
if (filter != null) {
|
||||||
),
|
_documentsCubit.updateFilter(filter: filter);
|
||||||
);
|
_savedViewCubit.resetSelection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(ConnectivityState connectivityState) {
|
Widget _buildBody(ConnectivityState connectivityState) {
|
||||||
@@ -193,6 +165,10 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
child = SliverToBoxAdapter(
|
child = SliverToBoxAdapter(
|
||||||
child: DocumentsEmptyState(
|
child: DocumentsEmptyState(
|
||||||
state: state,
|
state: state,
|
||||||
|
onReset: () {
|
||||||
|
_documentsCubit.updateFilter();
|
||||||
|
_savedViewCubit.resetSelection();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -201,10 +177,10 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
onRefresh: _onRefresh,
|
onRefresh: _onRefresh,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
BlocProvider(
|
BlocListener<SavedViewCubit, SavedViewState>(
|
||||||
create: (context) => SavedViewCubit(
|
listenWhen: (previous, current) =>
|
||||||
RepositoryProvider.of<SavedViewRepository>(context)),
|
previous.selectedSavedViewId !=
|
||||||
child: BlocListener<SavedViewCubit, SavedViewState>(
|
current.selectedSavedViewId,
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
try {
|
try {
|
||||||
if (state.selectedSavedViewId == null) {
|
if (state.selectedSavedViewId == null) {
|
||||||
@@ -231,21 +207,15 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
: Icons.grid_view,
|
: Icons.grid_view,
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
BlocProvider.of<ApplicationSettingsCubit>(
|
BlocProvider.of<ApplicationSettingsCubit>(context)
|
||||||
context)
|
|
||||||
.setViewType(
|
.setViewType(
|
||||||
settings.preferredViewType.toggle()),
|
settings.preferredViewType.toggle(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
child,
|
child,
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: SizedBox(
|
|
||||||
height: MediaQuery.of(context).size.height / 4,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -296,4 +266,31 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _loadNewPage(int pageKey) async {
|
||||||
|
final pageCount = _documentsCubit.state
|
||||||
|
.inferPageCount(pageSize: _documentsCubit.state.filter.pageSize);
|
||||||
|
if (pageCount <= pageKey + 1) {
|
||||||
|
_pagingController.nextPageKey = null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await _documentsCubit.loadMore();
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSelected(DocumentModel model) {
|
||||||
|
_documentsCubit.toggleDocumentSelection(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onRefresh() async {
|
||||||
|
try {
|
||||||
|
await _documentsCubit.updateCurrentFilter(
|
||||||
|
(filter) => filter.copyWith(page: 1),
|
||||||
|
);
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import 'package:paperless_mobile/generated/l10n.dart';
|
|||||||
|
|
||||||
class DocumentsEmptyState extends StatelessWidget {
|
class DocumentsEmptyState extends StatelessWidget {
|
||||||
final DocumentsState state;
|
final DocumentsState state;
|
||||||
|
final VoidCallback onReset;
|
||||||
const DocumentsEmptyState({
|
const DocumentsEmptyState({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.state,
|
required this.state,
|
||||||
|
required this.onReset,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -22,10 +24,7 @@ class DocumentsEmptyState extends StatelessWidget {
|
|||||||
subtitle: S.of(context).documentsPageEmptyStateNothingHereText,
|
subtitle: S.of(context).documentsPageEmptyStateNothingHereText,
|
||||||
bottomChild: state.filter != DocumentFilter.initial
|
bottomChild: state.filter != DocumentFilter.initial
|
||||||
? TextButton(
|
? TextButton(
|
||||||
onPressed: () async {
|
onPressed: onReset,
|
||||||
await BlocProvider.of<DocumentsCubit>(context).updateFilter();
|
|
||||||
BlocProvider.of<SavedViewCubit>(context).resetSelection();
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).documentsFilterPageResetFilterLabel,
|
S.of(context).documentsFilterPageResetFilterLabel,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,34 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.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_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.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/bloc/label_state.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
|
||||||
|
|
||||||
enum DateRangeSelection { before, after }
|
enum DateRangeSelection { before, after }
|
||||||
|
|
||||||
class DocumentFilterPanel extends StatefulWidget {
|
class DocumentFilterPanel extends StatefulWidget {
|
||||||
final PanelController panelController;
|
|
||||||
final ScrollController scrollController;
|
|
||||||
|
|
||||||
final DocumentFilter initialFilter;
|
final DocumentFilter initialFilter;
|
||||||
|
|
||||||
final void Function(DocumentFilter filter) onFilterChanged;
|
|
||||||
const DocumentFilterPanel({
|
const DocumentFilterPanel({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.panelController,
|
|
||||||
required this.scrollController,
|
|
||||||
required this.onFilterChanged,
|
|
||||||
required this.initialFilter,
|
required this.initialFilter,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -60,33 +50,17 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
const radius = Radius.circular(16);
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: radius,
|
||||||
topRight: Radius.circular(16),
|
topRight: radius,
|
||||||
),
|
),
|
||||||
child: FormBuilder(
|
child: FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
_buildDraggableResetHeader(),
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
_buildDragLine(),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.refresh),
|
|
||||||
label:
|
|
||||||
Text(S.of(context).documentsFilterPageResetFilterLabel),
|
|
||||||
onPressed: () => _resetFilter(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 8.0,
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -101,9 +75,6 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padded(),
|
).padded(),
|
||||||
const SizedBox(
|
|
||||||
height: 16.0,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
@@ -111,34 +82,30 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
topRight: Radius.circular(16.0),
|
topRight: Radius.circular(16.0),
|
||||||
),
|
),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
controller: widget.scrollController,
|
|
||||||
children: [
|
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)),
|
).paddedOnly(left: 8.0),
|
||||||
_buildQueryFormField(),
|
_buildQueryFormField().padded(),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child:
|
child:
|
||||||
Text(S.of(context).documentsFilterPageAdvancedLabel),
|
Text(S.of(context).documentsFilterPageAdvancedLabel),
|
||||||
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
).padded(),
|
||||||
_buildCreatedDateRangePickerFormField().padded(),
|
_buildCreatedDateRangePickerFormField(),
|
||||||
_buildAddedDateRangePickerFormField().padded(),
|
_buildAddedDateRangePickerFormField(),
|
||||||
_buildCorrespondentFormField().padded(),
|
_buildCorrespondentFormField().padded(),
|
||||||
_buildDocumentTypeFormField().padded(),
|
_buildDocumentTypeFormField().padded(),
|
||||||
_buildStoragePathFormField().padded(),
|
_buildStoragePathFormField().padded(),
|
||||||
TagFormField(
|
_buildTagsFormField()
|
||||||
name: DocumentModel.tagsKey,
|
.paddedSymmetrically(horizontal: 8, vertical: 4.0),
|
||||||
initialValue: widget.initialFilter.tags,
|
|
||||||
allowCreation: false,
|
|
||||||
).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: 150,
|
height: 150,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padded(),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -147,13 +114,39 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlocBuilder<LabelCubit<Tag>, LabelState<Tag>> _buildTagsFormField() {
|
||||||
|
return BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return TagFormField(
|
||||||
|
name: DocumentModel.tagsKey,
|
||||||
|
initialValue: widget.initialFilter.tags,
|
||||||
|
allowCreation: false,
|
||||||
|
selectableOptions: state.labels,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack _buildDraggableResetHeader() {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
_buildDragLine(),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: TextButton.icon(
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
label: Text(S.of(context).documentsFilterPageResetFilterLabel),
|
||||||
|
onPressed: () => _resetFilter(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _resetFilter(BuildContext context) async {
|
void _resetFilter(BuildContext context) async {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
await BlocProvider.of<DocumentsCubit>(context).updateFilter();
|
Navigator.pop(context, DocumentFilter.initial);
|
||||||
BlocProvider.of<SavedViewCubit>(context).resetSelection();
|
|
||||||
if (!widget.panelController.isPanelClosed) {
|
|
||||||
widget.panelController.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Check if the blocs can be found in the context, otherwise just provide repository and create new bloc inside LabelFormField!
|
//TODO: Check if the blocs can be found in the context, otherwise just provide repository and create new bloc inside LabelFormField!
|
||||||
@@ -238,14 +231,17 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
initialValue: widget.initialFilter.queryText,
|
initialValue: widget.initialFilter.queryText,
|
||||||
).padded();
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDateRangePickerHelper(String formFieldKey) {
|
Widget _buildDateRangePickerHelper(String formFieldKey) {
|
||||||
return SingleChildScrollView(
|
const spacer = SizedBox(width: 8.0);
|
||||||
|
return SizedBox(
|
||||||
|
height: 64,
|
||||||
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
|
spacer,
|
||||||
ActionChip(
|
ActionChip(
|
||||||
label: Text(
|
label: Text(
|
||||||
S.of(context).documentsFilterPageDateRangeLastSevenDaysLabel,
|
S.of(context).documentsFilterPageDateRangeLastSevenDaysLabel,
|
||||||
@@ -258,7 +254,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padded(const EdgeInsets.only(right: 8.0)),
|
),
|
||||||
|
spacer,
|
||||||
ActionChip(
|
ActionChip(
|
||||||
label: Text(
|
label: Text(
|
||||||
S.of(context).documentsFilterPageDateRangeLastMonthLabel,
|
S.of(context).documentsFilterPageDateRangeLastMonthLabel,
|
||||||
@@ -275,7 +272,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padded(const EdgeInsets.only(right: 8.0)),
|
),
|
||||||
|
spacer,
|
||||||
ActionChip(
|
ActionChip(
|
||||||
label: Text(
|
label: Text(
|
||||||
S.of(context).documentsFilterPageDateRangeLastThreeMonthsLabel,
|
S.of(context).documentsFilterPageDateRangeLastThreeMonthsLabel,
|
||||||
@@ -295,7 +293,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padded(const EdgeInsets.only(right: 8.0)),
|
),
|
||||||
|
spacer,
|
||||||
ActionChip(
|
ActionChip(
|
||||||
label: Text(
|
label: Text(
|
||||||
S.of(context).documentsFilterPageDateRangeLastYearLabel,
|
S.of(context).documentsFilterPageDateRangeLastYearLabel,
|
||||||
@@ -316,6 +315,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
spacer,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -358,12 +358,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
labelText: S.of(context).documentCreatedPropertyLabel,
|
labelText: S.of(context).documentCreatedPropertyLabel,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () =>
|
onPressed: () {
|
||||||
_formKey.currentState?.fields[fkCreatedAt]?.didChange(null),
|
_formKey.currentState?.fields[fkCreatedAt]?.didChange(null);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
).paddedSymmetrically(horizontal: 8, vertical: 4.0),
|
||||||
const SizedBox(height: 4.0),
|
|
||||||
_buildDateRangePickerHelper(fkCreatedAt),
|
_buildDateRangePickerHelper(fkCreatedAt),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -393,7 +393,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
child: child!,
|
child: child!,
|
||||||
),
|
),
|
||||||
format: DateFormat.yMMMd(Localizations.localeOf(context).toString()),
|
format: DateFormat.yMMMd(),
|
||||||
fieldStartLabelText:
|
fieldStartLabelText:
|
||||||
S.of(context).documentsFilterPageDateRangeFieldStartLabel,
|
S.of(context).documentsFilterPageDateRangeFieldStartLabel,
|
||||||
fieldEndLabelText:
|
fieldEndLabelText:
|
||||||
@@ -406,11 +406,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
labelText: S.of(context).documentAddedPropertyLabel,
|
labelText: S.of(context).documentAddedPropertyLabel,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () =>
|
onPressed: () {
|
||||||
_formKey.currentState?.fields[fkAddedAt]?.didChange(null),
|
_formKey.currentState?.fields[fkAddedAt]?.didChange(null);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
).paddedSymmetrically(horizontal: 8),
|
||||||
const SizedBox(height: 4.0),
|
const SizedBox(height: 4.0),
|
||||||
_buildDateRangePickerHelper(fkAddedAt),
|
_buildDateRangePickerHelper(fkAddedAt),
|
||||||
],
|
],
|
||||||
@@ -429,28 +430,33 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onApplyFilter() async {
|
void _onApplyFilter() async {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
_formKey.currentState?.save();
|
||||||
|
if (_formKey.currentState?.validate() ?? false) {
|
||||||
final v = _formKey.currentState!.value;
|
final v = _formKey.currentState!.value;
|
||||||
final docCubit = BlocProvider.of<DocumentsCubit>(context);
|
DocumentFilter newFilter = DocumentFilter(
|
||||||
DocumentFilter newFilter = docCubit.state.filter.copyWith(
|
|
||||||
createdDateBefore: (v[fkCreatedAt] as DateTimeRange?)?.end,
|
createdDateBefore: (v[fkCreatedAt] as DateTimeRange?)?.end,
|
||||||
createdDateAfter: (v[fkCreatedAt] as DateTimeRange?)?.start,
|
createdDateAfter: (v[fkCreatedAt] as DateTimeRange?)?.start,
|
||||||
correspondent: v[fkCorrespondent] as CorrespondentQuery?,
|
correspondent: v[fkCorrespondent] as CorrespondentQuery? ??
|
||||||
documentType: v[fkDocumentType] as DocumentTypeQuery?,
|
DocumentFilter.initial.correspondent,
|
||||||
storagePath: v[fkStoragePath] as StoragePathQuery?,
|
documentType: v[fkDocumentType] as DocumentTypeQuery? ??
|
||||||
tags: v[DocumentModel.tagsKey] as TagsQuery?,
|
DocumentFilter.initial.documentType,
|
||||||
page: 1,
|
storagePath: v[fkStoragePath] as StoragePathQuery? ??
|
||||||
|
DocumentFilter.initial.storagePath,
|
||||||
|
tags: v[DocumentModel.tagsKey] as TagsQuery? ??
|
||||||
|
DocumentFilter.initial.tags,
|
||||||
queryText: v[fkQuery] as String?,
|
queryText: v[fkQuery] as String?,
|
||||||
addedDateBefore: (v[fkAddedAt] as DateTimeRange?)?.end,
|
addedDateBefore: (v[fkAddedAt] as DateTimeRange?)?.end,
|
||||||
addedDateAfter: (v[fkAddedAt] as DateTimeRange?)?.start,
|
addedDateAfter: (v[fkAddedAt] as DateTimeRange?)?.start,
|
||||||
queryType: v[QueryTypeFormField.fkQueryType] as QueryType,
|
queryType: v[QueryTypeFormField.fkQueryType] as QueryType,
|
||||||
|
asnQuery: widget.initialFilter.asnQuery,
|
||||||
|
page: 1,
|
||||||
|
pageSize: widget.initialFilter.pageSize,
|
||||||
|
sortField: widget.initialFilter.sortField,
|
||||||
|
sortOrder: widget.initialFilter.sortOrder,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await BlocProvider.of<DocumentsCubit>(context)
|
|
||||||
.updateFilter(filter: newFilter);
|
|
||||||
BlocProvider.of<SavedViewCubit>(context).resetSelection();
|
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
widget.panelController.close();
|
Navigator.pop(context, newFilter);
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.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/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||||
@@ -46,30 +49,58 @@ class _SortFieldSelectionBottomSheetState
|
|||||||
S.of(context).documentsPageOrderByLabel,
|
S.of(context).documentsPageOrderByLabel,
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
).padded(
|
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
child: Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
||||||
onPressed: () => widget.onSubmit(
|
onPressed: () {
|
||||||
|
widget.onSubmit(
|
||||||
_currentSortField,
|
_currentSortField,
|
||||||
_currentSortOrder,
|
_currentSortOrder,
|
||||||
),
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
).paddedSymmetrically(horizontal: 16, vertical: 8.0),
|
||||||
Column(
|
Column(
|
||||||
children: SortField.values.map(_buildSortOption).toList(),
|
children: [
|
||||||
|
_buildSortOption(SortField.archiveSerialNumber),
|
||||||
|
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return _buildSortOption(
|
||||||
|
SortField.correspondentName,
|
||||||
|
enabled: state.labels.values.fold<bool>(
|
||||||
|
false,
|
||||||
|
(previousValue, element) =>
|
||||||
|
previousValue || (element.documentCount ?? 0) > 0),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildSortOption(SortField.title),
|
||||||
|
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return _buildSortOption(
|
||||||
|
SortField.documentType,
|
||||||
|
enabled: state.labels.values.fold<bool>(
|
||||||
|
false,
|
||||||
|
(previousValue, element) =>
|
||||||
|
previousValue || (element.documentCount ?? 0) > 0),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildSortOption(SortField.created),
|
||||||
|
_buildSortOption(SortField.added),
|
||||||
|
_buildSortOption(SortField.modified),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSortOption(
|
Widget _buildSortOption(SortField field, {bool enabled = true}) {
|
||||||
SortField field,
|
|
||||||
) {
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
enabled: enabled,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
title: Text(
|
title: Text(
|
||||||
_localizedSortField(field),
|
_localizedSortField(field),
|
||||||
@@ -77,6 +108,14 @@ class _SortFieldSelectionBottomSheetState
|
|||||||
trailing: _currentSortField == field
|
trailing: _currentSortField == field
|
||||||
? _buildOrderIcon(_currentSortOrder)
|
? _buildOrderIcon(_currentSortOrder)
|
||||||
: null,
|
: null,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_currentSortOrder = (_currentSortField == field
|
||||||
|
? _currentSortOrder.toggle()
|
||||||
|
: SortOrder.descending);
|
||||||
|
_currentSortField = field;
|
||||||
|
});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +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:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/saved_view_repository.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/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_selection_widget.dart';
|
import 'package:paperless_mobile/features/saved_view/view/saved_view_selection_widget.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
@@ -35,7 +33,7 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|||||||
snap: true,
|
snap: true,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
flexibleSpace: _buildFlexibleArea(false),
|
flexibleSpace: _buildFlexibleArea(false, documentsState.filter),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
@@ -56,13 +54,12 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|||||||
snap: true,
|
snap: true,
|
||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
flexibleSpace: _buildFlexibleArea(true),
|
flexibleSpace: _buildFlexibleArea(
|
||||||
title: BlocBuilder<DocumentsCubit, DocumentsState>(
|
true,
|
||||||
builder: (context, state) {
|
documentsState.filter,
|
||||||
return Text(
|
),
|
||||||
'${S.of(context).documentsPageTitle} (${_formatDocumentCount(state.count)})',
|
title: Text(
|
||||||
);
|
'${S.of(context).documentsPageTitle} (${_formatDocumentCount(documentsState.count)})',
|
||||||
},
|
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
...widget.actions,
|
...widget.actions,
|
||||||
@@ -73,14 +70,18 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFlexibleArea(bool enabled) {
|
Widget _buildFlexibleArea(bool enabled, DocumentFilter filter) {
|
||||||
return FlexibleSpaceBar(
|
return FlexibleSpaceBar(
|
||||||
background: Padding(
|
background: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
SavedViewSelectionWidget(height: 48, enabled: enabled),
|
SavedViewSelectionWidget(
|
||||||
|
height: 48,
|
||||||
|
enabled: enabled,
|
||||||
|
currentFilter: filter,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
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_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.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/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
class SortDocumentsButton extends StatefulWidget {
|
class SortDocumentsButton extends StatelessWidget {
|
||||||
const SortDocumentsButton({
|
const SortDocumentsButton({super.key});
|
||||||
Key? key,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<SortDocumentsButton> createState() => _SortDocumentsButtonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: const Icon(Icons.sort),
|
icon: const Icon(Icons.sort),
|
||||||
onPressed: _onOpenSortBottomSheet,
|
onPressed: () => _onOpenSortBottomSheet(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenSortBottomSheet() {
|
void _onOpenSortBottomSheet(BuildContext context) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
context: context,
|
context: context,
|
||||||
@@ -32,21 +28,43 @@ class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context) => FractionallySizedBox(
|
builder: (_) => BlocProvider.value(
|
||||||
|
value: BlocProvider.of<DocumentsCubit>(context),
|
||||||
|
child: FractionallySizedBox(
|
||||||
heightFactor: .6,
|
heightFactor: .6,
|
||||||
|
child: MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => LabelCubit<Correspondent>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return SortFieldSelectionBottomSheet(
|
return SortFieldSelectionBottomSheet(
|
||||||
initialSortField: state.filter.sortField,
|
initialSortField: state.filter.sortField,
|
||||||
initialSortOrder: state.filter.sortOrder,
|
initialSortOrder: state.filter.sortOrder,
|
||||||
onSubmit: (field, order) =>
|
onSubmit: (field, order) =>
|
||||||
BlocProvider.of<DocumentsCubit>(context).updateCurrentFilter(
|
BlocProvider.of<DocumentsCubit>(context)
|
||||||
(filter) => filter.copyWith(sortField: field, sortOrder: order),
|
.updateCurrentFilter(
|
||||||
|
(filter) => filter.copyWith(
|
||||||
|
sortField: field,
|
||||||
|
sortOrder: order,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
91
lib/features/edit_document/cubit/edit_document_cubit.dart
Normal file
91
lib/features/edit_document/cubit/edit_document_cubit.dart
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
part 'edit_document_state.dart';
|
||||||
|
|
||||||
|
class EditDocumentCubit extends Cubit<EditDocumentState> {
|
||||||
|
final DocumentModel _initialDocument;
|
||||||
|
final PaperlessDocumentsApi _docsApi;
|
||||||
|
|
||||||
|
final LabelRepository<Correspondent> _correspondentRepository;
|
||||||
|
final LabelRepository<DocumentType> _documentTypeRepository;
|
||||||
|
final LabelRepository<StoragePath> _storagePathRepository;
|
||||||
|
final LabelRepository<Tag> _tagRepository;
|
||||||
|
|
||||||
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
|
EditDocumentCubit(
|
||||||
|
DocumentModel document, {
|
||||||
|
required PaperlessDocumentsApi documentsApi,
|
||||||
|
required LabelRepository<Correspondent> correspondentRepository,
|
||||||
|
required LabelRepository<DocumentType> documentTypeRepository,
|
||||||
|
required LabelRepository<StoragePath> storagePathRepository,
|
||||||
|
required LabelRepository<Tag> tagRepository,
|
||||||
|
}) : _initialDocument = document,
|
||||||
|
_docsApi = documentsApi,
|
||||||
|
_correspondentRepository = correspondentRepository,
|
||||||
|
_documentTypeRepository = documentTypeRepository,
|
||||||
|
_storagePathRepository = storagePathRepository,
|
||||||
|
_tagRepository = tagRepository,
|
||||||
|
super(
|
||||||
|
EditDocumentState(
|
||||||
|
document: document,
|
||||||
|
correspondents: correspondentRepository.current,
|
||||||
|
documentTypes: documentTypeRepository.current,
|
||||||
|
storagePaths: storagePathRepository.current,
|
||||||
|
tags: tagRepository.current,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
_subscriptions.add(
|
||||||
|
_correspondentRepository.labels
|
||||||
|
.listen((v) => emit(state.copyWith(correspondents: v))),
|
||||||
|
);
|
||||||
|
_subscriptions.add(
|
||||||
|
_documentTypeRepository.labels
|
||||||
|
.listen((v) => emit(state.copyWith(documentTypes: v))),
|
||||||
|
);
|
||||||
|
_subscriptions.add(
|
||||||
|
_storagePathRepository.labels
|
||||||
|
.listen((v) => emit(state.copyWith(storagePaths: v))),
|
||||||
|
);
|
||||||
|
_subscriptions.add(
|
||||||
|
_tagRepository.labels.listen(
|
||||||
|
(v) => emit(state.copyWith(tags: v)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateDocument(DocumentModel document) async {
|
||||||
|
final updated = await _docsApi.update(document);
|
||||||
|
// Reload changed labels (documentCount property changes with removal/add)
|
||||||
|
if (document.documentType != _initialDocument.documentType) {
|
||||||
|
_documentTypeRepository
|
||||||
|
.find((document.documentType ?? _initialDocument.documentType)!);
|
||||||
|
}
|
||||||
|
if (document.correspondent != _initialDocument.correspondent) {
|
||||||
|
_correspondentRepository
|
||||||
|
.find((document.correspondent ?? _initialDocument.correspondent)!);
|
||||||
|
}
|
||||||
|
if (document.storagePath != _initialDocument.storagePath) {
|
||||||
|
_storagePathRepository
|
||||||
|
.find((document.storagePath ?? _initialDocument.storagePath)!);
|
||||||
|
}
|
||||||
|
if (!const DeepCollectionEquality.unordered()
|
||||||
|
.equals(document.tags, _initialDocument.tags)) {
|
||||||
|
_tagRepository.findAll(document.tags);
|
||||||
|
}
|
||||||
|
emit(state.copyWith(document: updated));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
for (final sub in _subscriptions) {
|
||||||
|
sub.cancel();
|
||||||
|
}
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/features/edit_document/cubit/edit_document_state.dart
Normal file
43
lib/features/edit_document/cubit/edit_document_state.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
part of 'edit_document_cubit.dart';
|
||||||
|
|
||||||
|
class EditDocumentState extends Equatable {
|
||||||
|
final DocumentModel document;
|
||||||
|
|
||||||
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
final Map<int, StoragePath> storagePaths;
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
|
||||||
|
const EditDocumentState({
|
||||||
|
required this.correspondents,
|
||||||
|
required this.documentTypes,
|
||||||
|
required this.storagePaths,
|
||||||
|
required this.tags,
|
||||||
|
required this.document,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [
|
||||||
|
correspondents,
|
||||||
|
documentTypes,
|
||||||
|
storagePaths,
|
||||||
|
tags,
|
||||||
|
document,
|
||||||
|
];
|
||||||
|
|
||||||
|
EditDocumentState copyWith({
|
||||||
|
Map<int, Correspondent>? correspondents,
|
||||||
|
Map<int, DocumentType>? documentTypes,
|
||||||
|
Map<int, StoragePath>? storagePaths,
|
||||||
|
Map<int, Tag>? tags,
|
||||||
|
DocumentModel? document,
|
||||||
|
}) {
|
||||||
|
return EditDocumentState(
|
||||||
|
document: document ?? this.document,
|
||||||
|
correspondents: correspondents ?? this.correspondents,
|
||||||
|
documentTypes: documentTypes ?? this.documentTypes,
|
||||||
|
storagePaths: storagePaths ?? this.storagePaths,
|
||||||
|
tags: tags ?? this.tags,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,9 @@ class EditLabelCubit<T extends Label> extends Cubit<EditLabelState<T>> {
|
|||||||
.listen((labels) => emit(EditLabelState(labels: labels)));
|
.listen((labels) => emit(EditLabelState(labels: labels)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> create(T label) => _repository.create(label);
|
Future<T> create(T label) => _repository.create(label);
|
||||||
|
|
||||||
Future<void> update(T label) => _repository.update(label);
|
Future<T> update(T label) => _repository.update(label);
|
||||||
|
|
||||||
Future<void> delete(T label) => _repository.delete(label);
|
Future<void> delete(T label) => _repository.delete(label);
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class AddLabelPage<T extends Label> extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: AddLabelFormWidget(
|
child: AddLabelFormWidget(
|
||||||
pageTitle: pageTitle,
|
pageTitle: pageTitle,
|
||||||
label: fromJsonT({'name': initialName}),
|
label: initialName != null ? fromJsonT({'name': initialName}) : null,
|
||||||
additionalFields: additionalFields,
|
additionalFields: additionalFields,
|
||||||
fromJsonT: fromJsonT,
|
fromJsonT: fromJsonT,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -70,24 +70,24 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDelete(BuildContext context) {
|
void _onDelete(BuildContext context) async {
|
||||||
if ((label.documentCount ?? 0) > 0) {
|
if ((label.documentCount ?? 0) > 0) {
|
||||||
showDialog(
|
final shouldDelete = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text(S.of(context).editLabelPageConfirmDeletionDialogTitle),
|
title:
|
||||||
|
Text(S.of(context).editLabelPageConfirmDeletionDialogTitle),
|
||||||
content: Text(
|
content: Text(
|
||||||
S.of(context).editLabelPageDeletionDialogText,
|
S.of(context).editLabelPageDeletionDialogText,
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context, false),
|
||||||
child: Text(S.of(context).genericActionCancelLabel),
|
child: Text(S.of(context).genericActionCancelLabel),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
Navigator.pop(context, true);
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).genericActionDeleteLabel,
|
S.of(context).genericActionDeleteLabel,
|
||||||
@@ -96,7 +96,12 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
) ??
|
||||||
|
false;
|
||||||
|
if (shouldDelete) {
|
||||||
|
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
|
|
||||||
class EditDocumentTypePage extends StatelessWidget {
|
class EditDocumentTypePage extends StatelessWidget {
|
||||||
final DocumentType documentType;
|
final DocumentType documentType;
|
||||||
@@ -12,7 +12,7 @@ class EditDocumentTypePage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => LabelCubit<DocumentType>(
|
create: (context) => EditLabelCubit<DocumentType>(
|
||||||
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<DocumentType>(
|
child: EditLabelPage<DocumentType>(
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||||
|
|
||||||
class EditStoragePathPage extends StatelessWidget {
|
class EditStoragePathPage extends StatelessWidget {
|
||||||
@@ -13,7 +13,7 @@ class EditStoragePathPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => LabelCubit<StoragePath>(
|
create: (context) => EditLabelCubit<StoragePath>(
|
||||||
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<StoragePath>(
|
child: EditLabelPage<StoragePath>(
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|||||||
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class EditTagPage extends StatelessWidget {
|
class EditTagPage extends StatelessWidget {
|
||||||
@@ -16,7 +16,7 @@ class EditTagPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => LabelCubit<Tag>(
|
create: (context) => EditLabelCubit<Tag>(
|
||||||
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<Tag>(
|
child: EditLabelPage<Tag>(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import 'package:paperless_mobile/util.dart';
|
|||||||
class SubmitButtonConfig<T extends Label> {
|
class SubmitButtonConfig<T extends Label> {
|
||||||
final Widget icon;
|
final Widget icon;
|
||||||
final Widget label;
|
final Widget label;
|
||||||
final Future<void> Function(T) onSubmit;
|
final Future<T> Function(T) onSubmit;
|
||||||
|
|
||||||
SubmitButtonConfig({
|
SubmitButtonConfig({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
@@ -117,8 +117,9 @@ class _LabelFormState<T extends Label> extends State<LabelForm<T>> {
|
|||||||
...widget.initialValue?.toJson() ?? {},
|
...widget.initialValue?.toJson() ?? {},
|
||||||
..._formKey.currentState!.value
|
..._formKey.currentState!.value
|
||||||
};
|
};
|
||||||
await widget.submitButtonConfig.onSubmit(widget.fromJsonT(mergedJson));
|
final createdLabel = await widget.submitButtonConfig
|
||||||
Navigator.pop(context);
|
.onSubmit(widget.fromJsonT(mergedJson));
|
||||||
|
Navigator.pop(context, createdLabel);
|
||||||
} on PaperlessValidationErrors catch (errorMessages) {
|
} on PaperlessValidationErrors catch (errorMessages) {
|
||||||
setState(() => _errors = errorMessages);
|
setState(() => _errors = errorMessages);
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ class _HomePageState extends State<HomePage> {
|
|||||||
BlocProvider.value(
|
BlocProvider.value(
|
||||||
value: DocumentsCubit(getIt<PaperlessDocumentsApi>()),
|
value: DocumentsCubit(getIt<PaperlessDocumentsApi>()),
|
||||||
),
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => SavedViewCubit(
|
||||||
|
RepositoryProvider.of<SavedViewRepository>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const DocumentsPage(),
|
child: const DocumentsPage(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import 'package:paperless_mobile/core/repository/provider/label_repositories_pro
|
|||||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
|
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_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/view/settings_page.dart';
|
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
@@ -52,7 +50,7 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
height: 32,
|
height: 32,
|
||||||
width: 32,
|
width: 32,
|
||||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
).padded(const EdgeInsets.only(right: 8.0)),
|
).paddedOnly(right: 8.0),
|
||||||
Text(
|
Text(
|
||||||
S.of(context).appTitleText,
|
S.of(context).appTitleText,
|
||||||
style: Theme.of(context).textTheme.headline5?.copyWith(
|
style: Theme.of(context).textTheme.headline5?.copyWith(
|
||||||
@@ -215,7 +213,7 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
create: (context) => InboxCubit(
|
create: (context) => InboxCubit(
|
||||||
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
getIt<PaperlessDocumentsApi>(),
|
getIt<PaperlessDocumentsApi>(),
|
||||||
),
|
)..loadInbox(),
|
||||||
child: const InboxPage(),
|
child: const InboxPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
'${state.inboxItems.length} ${S.of(context).inboxPageUnseenText}',
|
'${state.inboxItems.length} ${S.of(context).inboxPageUnseenText}',
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
).padded(const EdgeInsets.symmetric(horizontal: 4.0)),
|
).paddedSymmetrically(horizontal: 4.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padded(const EdgeInsets.symmetric(horizontal: 8.0)),
|
).paddedSymmetrically(horizontal: 8.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: BlocBuilder<InboxCubit, InboxState>(
|
floatingActionButton: BlocBuilder<InboxCubit, InboxState>(
|
||||||
@@ -108,7 +108,7 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
).padded(),
|
).padded(),
|
||||||
),
|
),
|
||||||
).padded(const EdgeInsets.only(top: 8.0)),
|
).paddedOnly(top: 8.0),
|
||||||
),
|
),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
@@ -137,14 +137,7 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
S.of(context).inboxPageUsageHintText,
|
S.of(context).inboxPageUsageHintText,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
).padded(
|
).padded(),
|
||||||
const EdgeInsets.only(
|
|
||||||
top: 8.0,
|
|
||||||
left: 8.0,
|
|
||||||
right: 8.0,
|
|
||||||
bottom: 8.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
...slivers
|
...slivers
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
|||||||
|
|
||||||
late StreamSubscription _subscription;
|
late StreamSubscription _subscription;
|
||||||
|
|
||||||
LabelCubit(this._repository) : super(LabelState.initial()) {
|
LabelCubit(LabelRepository<T> repository)
|
||||||
|
: _repository = repository,
|
||||||
|
super(LabelState(labels: repository.current, isLoaded: true)) {
|
||||||
_subscription = _repository.labels.listen(
|
_subscription = _repository.labels.listen(
|
||||||
(update) => emit(LabelState(isLoaded: true, labels: update)),
|
(update) => emit(
|
||||||
|
LabelState(isLoaded: true, labels: update),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +32,10 @@ class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
|||||||
return addedItem;
|
return addedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> reload() {
|
||||||
|
return _repository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
Future<T> replace(T item) async {
|
Future<T> replace(T item) async {
|
||||||
assert(item.id != null);
|
assert(item.id != null);
|
||||||
final updatedItem = await _repository.update(item);
|
final updatedItem = await _repository.update(item);
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|||||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_tag_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_tag_page.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/bloc/providers/tag_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class TagFormField extends StatefulWidget {
|
class TagFormField extends StatefulWidget {
|
||||||
@@ -18,6 +14,7 @@ class TagFormField extends StatefulWidget {
|
|||||||
final bool notAssignedSelectable;
|
final bool notAssignedSelectable;
|
||||||
final bool anyAssignedSelectable;
|
final bool anyAssignedSelectable;
|
||||||
final bool excludeAllowed;
|
final bool excludeAllowed;
|
||||||
|
final Map<int, Tag> selectableOptions;
|
||||||
|
|
||||||
const TagFormField({
|
const TagFormField({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -27,6 +24,7 @@ class TagFormField extends StatefulWidget {
|
|||||||
this.notAssignedSelectable = true,
|
this.notAssignedSelectable = true,
|
||||||
this.anyAssignedSelectable = true,
|
this.anyAssignedSelectable = true,
|
||||||
this.excludeAllowed = true,
|
this.excludeAllowed = true,
|
||||||
|
required this.selectableOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -47,10 +45,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
_textEditingController = TextEditingController()
|
_textEditingController = TextEditingController()
|
||||||
..addListener(() {
|
..addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showCreationSuffixIcon = BlocProvider.of<LabelCubit<Tag>>(context)
|
_showCreationSuffixIcon = widget.selectableOptions.values
|
||||||
.state
|
|
||||||
.labels
|
|
||||||
.values
|
|
||||||
.where(
|
.where(
|
||||||
(item) => item.name.toLowerCase().startsWith(
|
(item) => item.name.toLowerCase().startsWith(
|
||||||
_textEditingController.text.toLowerCase(),
|
_textEditingController.text.toLowerCase(),
|
||||||
@@ -66,16 +61,21 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TagBlocProvider(
|
final isEnabled = widget.selectableOptions.values.fold<bool>(
|
||||||
child: BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
false,
|
||||||
builder: (context, tagState) {
|
(previousValue, element) =>
|
||||||
|
previousValue || (element.documentCount ?? 0) > 0) ||
|
||||||
|
widget.allowCreation;
|
||||||
|
|
||||||
return FormBuilderField<TagsQuery>(
|
return FormBuilderField<TagsQuery>(
|
||||||
|
enabled: isEnabled,
|
||||||
builder: (field) {
|
builder: (field) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
TypeAheadField<int>(
|
TypeAheadField<int>(
|
||||||
textFieldConfiguration: TextFieldConfiguration(
|
textFieldConfiguration: TextFieldConfiguration(
|
||||||
|
enabled: isEnabled,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(
|
prefixIcon: const Icon(
|
||||||
Icons.label_outline,
|
Icons.label_outline,
|
||||||
@@ -87,17 +87,20 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
controller: _textEditingController,
|
controller: _textEditingController,
|
||||||
),
|
),
|
||||||
suggestionsCallback: (query) {
|
suggestionsCallback: (query) {
|
||||||
final suggestions = tagState.labels.values
|
final suggestions = widget.selectableOptions.entries
|
||||||
.where((element) => element.name
|
.where(
|
||||||
|
(entry) => entry.value.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.startsWith(query.toLowerCase()))
|
.startsWith(query.toLowerCase()),
|
||||||
.map((e) => e.id!)
|
)
|
||||||
|
.where((entry) =>
|
||||||
|
widget.allowCreation ||
|
||||||
|
(entry.value.documentCount ?? 0) > 0)
|
||||||
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
if (field.value is IdsTagsQuery) {
|
if (field.value is IdsTagsQuery) {
|
||||||
suggestions.removeWhere((element) =>
|
suggestions.removeWhere((element) =>
|
||||||
(field.value as IdsTagsQuery)
|
(field.value as IdsTagsQuery).ids.contains(element));
|
||||||
.ids
|
|
||||||
.contains(element));
|
|
||||||
}
|
}
|
||||||
if (widget.notAssignedSelectable &&
|
if (widget.notAssignedSelectable &&
|
||||||
field.value is! OnlyNotAssignedTagsQuery) {
|
field.value is! OnlyNotAssignedTagsQuery) {
|
||||||
@@ -121,7 +124,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
title: Text(S.of(context).labelAnyAssignedText),
|
title: Text(S.of(context).labelAnyAssignedText),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final tag = tagState.getLabel(data)!;
|
final tag = widget.selectableOptions[data]!;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
Icons.circle,
|
Icons.circle,
|
||||||
@@ -130,8 +133,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
title: Text(
|
title: Text(
|
||||||
tag.name,
|
tag.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context).colorScheme.onBackground),
|
||||||
Theme.of(context).colorScheme.onBackground),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -146,8 +148,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
final tagsQuery = field.value is IdsTagsQuery
|
final tagsQuery = field.value is IdsTagsQuery
|
||||||
? field.value as IdsTagsQuery
|
? field.value as IdsTagsQuery
|
||||||
: const IdsTagsQuery();
|
: const IdsTagsQuery();
|
||||||
field.didChange(tagsQuery
|
field.didChange(
|
||||||
.withIdQueriesAdded([IncludeTagIdQuery(id)]));
|
tagsQuery.withIdQueriesAdded([IncludeTagIdQuery(id)]));
|
||||||
}
|
}
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
},
|
},
|
||||||
@@ -168,7 +170,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
(query) => _buildTag(
|
(query) => _buildTag(
|
||||||
field,
|
field,
|
||||||
query,
|
query,
|
||||||
tagState.getLabel(query.id),
|
widget.selectableOptions[query.id],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
@@ -180,9 +182,6 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
initialValue: widget.initialValue ?? const IdsTagsQuery(),
|
initialValue: widget.initialValue ?? const IdsTagsQuery(),
|
||||||
name: widget.name,
|
name: widget.name,
|
||||||
);
|
);
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? _buildSuffixIcon(
|
Widget? _buildSuffixIcon(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_tabController = TabController(length: 4, vsync: this)
|
_tabController = TabController(length: 4, vsync: this)
|
||||||
..addListener(() => setState(() => _currentIndex = _tabController.index));
|
..addListener(() => setState(() => _currentIndex = _tabController.index));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
@@ -55,13 +57,15 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
super.initState();
|
super.initState();
|
||||||
_showClearSuffixIcon = widget.state.containsKey(widget.initialValue?.id);
|
_showClearSuffixIcon = widget.state.containsKey(widget.initialValue?.id);
|
||||||
_textEditingController = TextEditingController(
|
_textEditingController = TextEditingController(
|
||||||
text: widget.state[widget.initialValue?.id]?.name ?? '')
|
text: widget.state[widget.initialValue?.id]?.name ?? '',
|
||||||
..addListener(() {
|
)..addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showCreationSuffixIcon = widget.state.values
|
_showCreationSuffixIcon = widget.state.values
|
||||||
.where((item) => item.name.toLowerCase().startsWith(
|
.where(
|
||||||
|
(item) => item.name.toLowerCase().startsWith(
|
||||||
_textEditingController.text.toLowerCase(),
|
_textEditingController.text.toLowerCase(),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
.isEmpty;
|
.isEmpty;
|
||||||
});
|
});
|
||||||
setState(() =>
|
setState(() =>
|
||||||
@@ -71,7 +75,13 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isEnabled = widget.state.values.fold<bool>(
|
||||||
|
false,
|
||||||
|
(previousValue, element) =>
|
||||||
|
previousValue || (element.documentCount ?? 0) > 0) ||
|
||||||
|
widget.labelCreationWidgetBuilder != null;
|
||||||
return FormBuilderTypeAhead<IdQueryParameter>(
|
return FormBuilderTypeAhead<IdQueryParameter>(
|
||||||
|
enabled: isEnabled,
|
||||||
noItemsFoundBuilder: (context) => Padding(
|
noItemsFoundBuilder: (context) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -88,13 +98,20 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
S.of(context).labelNotAssignedText),
|
S.of(context).labelNotAssignedText),
|
||||||
),
|
),
|
||||||
suggestionsCallback: (pattern) {
|
suggestionsCallback: (pattern) {
|
||||||
final List<IdQueryParameter> suggestions = widget.state.keys
|
final List<IdQueryParameter> suggestions = widget.state.entries
|
||||||
.where((item) =>
|
.where(
|
||||||
widget.state[item]!.name
|
(entry) =>
|
||||||
|
widget.state[entry.key]!.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.startsWith(pattern.toLowerCase()) ||
|
.contains(pattern.toLowerCase()) ||
|
||||||
pattern.isEmpty)
|
pattern.isEmpty,
|
||||||
.map((id) => widget.queryParameterIdBuilder(id))
|
)
|
||||||
|
.where(
|
||||||
|
(entry) =>
|
||||||
|
widget.labelCreationWidgetBuilder != null ||
|
||||||
|
(entry.value.documentCount ?? 0) > 0,
|
||||||
|
)
|
||||||
|
.map((entry) => widget.queryParameterIdBuilder(entry.key))
|
||||||
.toList();
|
.toList();
|
||||||
if (widget.notAssignedSelectable) {
|
if (widget.notAssignedSelectable) {
|
||||||
suggestions.insert(0, widget.queryParameterNotAssignedBuilder());
|
suggestions.insert(0, widget.queryParameterNotAssignedBuilder());
|
||||||
@@ -128,21 +145,23 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
Widget? _buildSuffixIcon(BuildContext context) {
|
Widget? _buildSuffixIcon(BuildContext context) {
|
||||||
if (_showCreationSuffixIcon && widget.labelCreationWidgetBuilder != null) {
|
if (_showCreationSuffixIcon && widget.labelCreationWidgetBuilder != null) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () => Navigator.of(context)
|
onPressed: () async {
|
||||||
.push<T>(MaterialPageRoute(
|
FocusScope.of(context).unfocus();
|
||||||
builder: (context) => widget
|
final createdLabel = await showDialog(
|
||||||
.labelCreationWidgetBuilder!(_textEditingController.text)))
|
context: context,
|
||||||
.then((value) {
|
builder: (context) => widget.labelCreationWidgetBuilder!(
|
||||||
if (value != null) {
|
_textEditingController.text,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (createdLabel != null) {
|
||||||
// If new label has been created, set form field value and text of this form field and unfocus keyboard (we assume user is done).
|
// If new label has been created, set form field value and text of this form field and unfocus keyboard (we assume user is done).
|
||||||
widget.formBuilderState?.fields[widget.name]
|
widget.formBuilderState?.fields[widget.name]
|
||||||
?.didChange(widget.queryParameterIdBuilder(value.id));
|
?.didChange(widget.queryParameterIdBuilder(createdLabel.id));
|
||||||
_textEditingController.text = value.name;
|
_textEditingController.text = createdLabel.name;
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
} else {
|
} else {
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.new_label,
|
Icons.new_label,
|
||||||
),
|
),
|
||||||
@@ -158,8 +177,9 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _reset() {
|
void _reset() {
|
||||||
widget.formBuilderState?.fields[widget.name]
|
widget.formBuilderState?.fields[widget.name]?.didChange(
|
||||||
?.didChange(widget.queryParameterIdBuilder(null));
|
widget.queryParameterIdBuilder(null), // equivalnt to IdQueryParam.unset()
|
||||||
|
);
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,9 +189,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
|
|||||||
} else if (T == DocumentType) {
|
} else if (T == DocumentType) {
|
||||||
return S.of(context).documentTypeFormFieldSearchHintText;
|
return S.of(context).documentTypeFormFieldSearchHintText;
|
||||||
} else {
|
} else {
|
||||||
return S
|
return S.of(context).tagFormFieldSearchHintText;
|
||||||
.of(context)
|
|
||||||
.tagFormFieldSearchHintText; //TODO: Update tag form field once there is multi selection support.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ListView(
|
return RefreshIndicator(
|
||||||
|
onRefresh: BlocProvider.of<LabelCubit<T>>(context).reload,
|
||||||
|
child: ListView(
|
||||||
children: labels
|
children: labels
|
||||||
.map((l) => LabelItem<T>(
|
.map((l) => LabelItem<T>(
|
||||||
name: l.name,
|
name: l.name,
|
||||||
@@ -74,6 +76,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
label: l,
|
label: l,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
appSettings = ApplicationSettingsState.defaultSettings;
|
appSettings = ApplicationSettingsState.defaultSettings;
|
||||||
}
|
}
|
||||||
if (storedAuth == null || !storedAuth.isValid) {
|
if (storedAuth == null || !storedAuth.isValid) {
|
||||||
emit(AuthenticationState(isAuthenticated: false, wasLoginStored: false));
|
return emit(
|
||||||
|
AuthenticationState(isAuthenticated: false, wasLoginStored: false));
|
||||||
} else {
|
} else {
|
||||||
if (appSettings.isLocalAuthenticationEnabled) {
|
if (appSettings.isLocalAuthenticationEnabled) {
|
||||||
final localAuthSuccess = await _localAuthService
|
final localAuthSuccess = await _localAuthService
|
||||||
@@ -103,8 +104,13 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
|
|||||||
wasLocalAuthenticationSuccessful: false,
|
wasLocalAuthenticationSuccessful: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return emit(AuthenticationState(
|
||||||
|
isAuthenticated: true,
|
||||||
|
authentication: storedAuth,
|
||||||
|
wasLoginStored: true,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
emit(AuthenticationState(isAuthenticated: false, wasLoginStored: true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
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_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||||
|
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class SavedViewSelectionWidget extends StatelessWidget {
|
class SavedViewSelectionWidget extends StatelessWidget {
|
||||||
|
final DocumentFilter currentFilter;
|
||||||
const SavedViewSelectionWidget({
|
const SavedViewSelectionWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.height,
|
required this.height,
|
||||||
required this.enabled,
|
required this.enabled,
|
||||||
|
required this.currentFilter,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final double height;
|
final double height;
|
||||||
@@ -64,10 +65,18 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
|||||||
S.of(context).savedViewsLabel,
|
S.of(context).savedViewsLabel,
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
|
buildWhen: (previous, current) =>
|
||||||
|
previous.filter != current.filter,
|
||||||
|
builder: (context, docState) {
|
||||||
|
return TextButton.icon(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: enabled ? () => _onCreatePressed(context) : null,
|
onPressed: enabled
|
||||||
|
? () => _onCreatePressed(context, docState.filter)
|
||||||
|
: null,
|
||||||
label: Text(S.of(context).savedViewCreateNewLabel),
|
label: Text(S.of(context).savedViewCreateNewLabel),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -75,11 +84,11 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCreatePressed(BuildContext context) async {
|
void _onCreatePressed(BuildContext context, DocumentFilter filter) async {
|
||||||
final newView = await Navigator.of(context).push<SavedView?>(
|
final newView = await Navigator.of(context).push<SavedView?>(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => AddSavedViewPage(
|
builder: (context) => AddSavedViewPage(
|
||||||
currentFilter: getIt<DocumentsCubit>().state.filter,
|
currentFilter: filter,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
dev.log('[ScannerPage] Created temporary file: ${file.path}');
|
dev.log('[ScannerPage] Created temporary file: ${file.path}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final success = await EdgeDetection.detectEdge(file.path);
|
final success = await EdgeDetection.detectEdge(file.path);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
|||||||
),
|
),
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 16.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
chipTheme: ChipThemeData(
|
chipTheme: ChipThemeData(
|
||||||
backgroundColor: Colors.lightGreen[50],
|
backgroundColor: Colors.lightGreen[50],
|
||||||
@@ -135,7 +139,10 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
|||||||
),
|
),
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 16.0,
|
||||||
|
)),
|
||||||
chipTheme: ChipThemeData(
|
chipTheme: ChipThemeData(
|
||||||
backgroundColor: Colors.green[900],
|
backgroundColor: Colors.green[900],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class DocumentFilter extends Equatable {
|
|||||||
final DocumentTypeQuery documentType;
|
final DocumentTypeQuery documentType;
|
||||||
final CorrespondentQuery correspondent;
|
final CorrespondentQuery correspondent;
|
||||||
final StoragePathQuery storagePath;
|
final StoragePathQuery storagePath;
|
||||||
final AsnQuery asn;
|
final AsnQuery asnQuery;
|
||||||
final TagsQuery tags;
|
final TagsQuery tags;
|
||||||
final SortField sortField;
|
final SortField sortField;
|
||||||
final SortOrder sortOrder;
|
final SortOrder sortOrder;
|
||||||
@@ -42,7 +42,7 @@ class DocumentFilter extends Equatable {
|
|||||||
this.documentType = const DocumentTypeQuery.unset(),
|
this.documentType = const DocumentTypeQuery.unset(),
|
||||||
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.asnQuery = const AsnQuery.unset(),
|
||||||
this.tags = const IdsTagsQuery(),
|
this.tags = const IdsTagsQuery(),
|
||||||
this.sortField = SortField.created,
|
this.sortField = SortField.created,
|
||||||
this.sortOrder = SortOrder.descending,
|
this.sortOrder = SortOrder.descending,
|
||||||
@@ -60,7 +60,7 @@ class DocumentFilter extends Equatable {
|
|||||||
sb.write(correspondent.toQueryParameter());
|
sb.write(correspondent.toQueryParameter());
|
||||||
sb.write(tags.toQueryParameter());
|
sb.write(tags.toQueryParameter());
|
||||||
sb.write(storagePath.toQueryParameter());
|
sb.write(storagePath.toQueryParameter());
|
||||||
sb.write(asn.toQueryParameter());
|
sb.write(asnQuery.toQueryParameter());
|
||||||
|
|
||||||
if (queryText?.isNotEmpty ?? false) {
|
if (queryText?.isNotEmpty ?? false) {
|
||||||
sb.write("&${queryType.queryParam}=$queryText");
|
sb.write("&${queryType.queryParam}=$queryText");
|
||||||
@@ -104,6 +104,7 @@ class DocumentFilter extends Equatable {
|
|||||||
DocumentTypeQuery? documentType,
|
DocumentTypeQuery? documentType,
|
||||||
CorrespondentQuery? correspondent,
|
CorrespondentQuery? correspondent,
|
||||||
StoragePathQuery? storagePath,
|
StoragePathQuery? storagePath,
|
||||||
|
AsnQuery? asnQuery,
|
||||||
TagsQuery? tags,
|
TagsQuery? tags,
|
||||||
SortField? sortField,
|
SortField? sortField,
|
||||||
SortOrder? sortOrder,
|
SortOrder? sortOrder,
|
||||||
@@ -129,6 +130,7 @@ class DocumentFilter extends Equatable {
|
|||||||
queryText: queryText ?? this.queryText,
|
queryText: queryText ?? this.queryText,
|
||||||
createdDateBefore: createdDateBefore ?? this.createdDateBefore,
|
createdDateBefore: createdDateBefore ?? this.createdDateBefore,
|
||||||
createdDateAfter: createdDateAfter ?? this.createdDateAfter,
|
createdDateAfter: createdDateAfter ?? this.createdDateAfter,
|
||||||
|
asnQuery: asnQuery ?? this.asnQuery,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +155,19 @@ class DocumentFilter extends Equatable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get appliedFiltersCount => [
|
||||||
|
documentType != initial.documentType,
|
||||||
|
correspondent != initial.correspondent,
|
||||||
|
storagePath != initial.storagePath,
|
||||||
|
tags != initial.tags,
|
||||||
|
(addedDateAfter != initial.addedDateAfter ||
|
||||||
|
addedDateBefore != initial.addedDateBefore),
|
||||||
|
(createdDateAfter != initial.createdDateAfter ||
|
||||||
|
createdDateBefore != initial.createdDateBefore),
|
||||||
|
asnQuery != initial.asnQuery,
|
||||||
|
(queryType != initial.queryType || queryText != initial.queryText),
|
||||||
|
].fold(0, (previousValue, element) => previousValue += element ? 1 : 0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
pageSize,
|
pageSize,
|
||||||
@@ -160,7 +175,7 @@ class DocumentFilter extends Equatable {
|
|||||||
documentType,
|
documentType,
|
||||||
correspondent,
|
correspondent,
|
||||||
storagePath,
|
storagePath,
|
||||||
asn,
|
asnQuery,
|
||||||
tags,
|
tags,
|
||||||
sortField,
|
sortField,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ class DocumentModel extends Equatable {
|
|||||||
: id = json[idKey],
|
: id = json[idKey],
|
||||||
title = json[titleKey],
|
title = json[titleKey],
|
||||||
content = json[contentKey],
|
content = json[contentKey],
|
||||||
created = DateTime.parse(json[createdKey]),
|
created = DateTime.parse(json[createdKey]).toLocal(),
|
||||||
modified = DateTime.parse(json[modifiedKey]),
|
modified = DateTime.parse(json[modifiedKey]).toLocal(),
|
||||||
added = DateTime.parse(json[addedKey]),
|
added = DateTime.parse(json[addedKey]).toLocal(),
|
||||||
archiveSerialNumber = json[asnKey],
|
archiveSerialNumber = json[asnKey],
|
||||||
originalFileName = json[originalFileNameKey],
|
originalFileName = json[originalFileNameKey],
|
||||||
archivedFileName = json[archivedFileNameKey],
|
archivedFileName = json[archivedFileNameKey],
|
||||||
@@ -71,9 +71,9 @@ class DocumentModel extends Equatable {
|
|||||||
contentKey: content,
|
contentKey: content,
|
||||||
correspondentKey: correspondent,
|
correspondentKey: correspondent,
|
||||||
documentTypeKey: documentType,
|
documentTypeKey: documentType,
|
||||||
createdKey: created.toUtc().toIso8601String(),
|
createdKey: created.toIso8601String(),
|
||||||
modifiedKey: modified.toUtc().toIso8601String(),
|
modifiedKey: modified.toIso8601String(),
|
||||||
addedKey: added.toUtc().toIso8601String(),
|
addedKey: added.toIso8601String(),
|
||||||
originalFileNameKey: originalFileName,
|
originalFileNameKey: originalFileName,
|
||||||
tagsKey: tags.toList(),
|
tagsKey: tags.toList(),
|
||||||
storagePathKey: storagePath,
|
storagePathKey: storagePath,
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
|
|||||||
const DocumentFilter asnQueryFilter = DocumentFilter(
|
const DocumentFilter asnQueryFilter = DocumentFilter(
|
||||||
sortField: SortField.archiveSerialNumber,
|
sortField: SortField.archiveSerialNumber,
|
||||||
sortOrder: SortOrder.descending,
|
sortOrder: SortOrder.descending,
|
||||||
asn: AsnQuery.anyAssigned(),
|
asnQuery: AsnQuery.anyAssigned(),
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 1,
|
pageSize: 1,
|
||||||
);
|
);
|
||||||
|
|||||||
171
pubspec.lock
171
pubspec.lock
@@ -35,7 +35,7 @@ packages:
|
|||||||
name: asn1lib
|
name: asn1lib
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.4.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -43,13 +43,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.0"
|
version: "2.9.0"
|
||||||
|
badges:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: badges
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
barcode:
|
barcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: barcode
|
name: barcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.3"
|
||||||
bloc:
|
bloc:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -77,14 +84,14 @@ packages:
|
|||||||
name: build
|
name: build
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.1.1"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -98,21 +105,21 @@ packages:
|
|||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.9"
|
version: "2.0.10"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.11"
|
version: "2.3.0"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.3"
|
version: "7.2.7"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -126,28 +133,28 @@ packages:
|
|||||||
name: built_value
|
name: built_value
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.3.2"
|
version: "8.4.2"
|
||||||
cached_network_image:
|
cached_network_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.3"
|
||||||
cached_network_image_platform_interface:
|
cached_network_image_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_platform_interface
|
name: cached_network_image_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "2.0.0"
|
||||||
cached_network_image_web:
|
cached_network_image_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_web
|
name: cached_network_image_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -210,7 +217,7 @@ packages:
|
|||||||
name: connectivity_plus_platform_interface
|
name: connectivity_plus_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.3"
|
||||||
connectivity_plus_web:
|
connectivity_plus_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -231,7 +238,7 @@ packages:
|
|||||||
name: convert
|
name: convert
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.1.1"
|
||||||
coverage:
|
coverage:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -266,7 +273,7 @@ packages:
|
|||||||
name: dart_style
|
name: dart_style
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.2.4"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -343,13 +350,13 @@ packages:
|
|||||||
name: dropdown_search
|
name: dropdown_search
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.3"
|
version: "5.0.5"
|
||||||
edge_detection:
|
edge_detection:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: master
|
ref: master
|
||||||
resolved-ref: "19fbebef99360e9cf0b59c6a90ff7cd26d4d6e7d"
|
resolved-ref: "2d417dd77e075cb12e82a390e50cc4554e877ec4"
|
||||||
url: "https://github.com/sawankumarbundelkhandi/edge_detection"
|
url: "https://github.com/sawankumarbundelkhandi/edge_detection"
|
||||||
source: git
|
source: git
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
@@ -366,7 +373,7 @@ packages:
|
|||||||
name: encrypted_shared_preferences
|
name: encrypted_shared_preferences
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.1"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -474,7 +481,7 @@ packages:
|
|||||||
name: flutter_form_builder
|
name: flutter_form_builder
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.5.0"
|
version: "7.7.0"
|
||||||
flutter_keyboard_visibility:
|
flutter_keyboard_visibility:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -535,14 +542,14 @@ packages:
|
|||||||
name: flutter_native_splash
|
name: flutter_native_splash
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.11"
|
version: "2.2.16"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.7"
|
||||||
flutter_rating_bar:
|
flutter_rating_bar:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -556,7 +563,7 @@ packages:
|
|||||||
name: flutter_svg
|
name: flutter_svg
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1+1"
|
version: "1.1.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -587,20 +594,20 @@ packages:
|
|||||||
name: fluttertoast
|
name: fluttertoast
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.1.1"
|
version: "8.1.2"
|
||||||
font_awesome_flutter:
|
font_awesome_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: font_awesome_flutter
|
name: font_awesome_flutter
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.1.0"
|
version: "10.3.0"
|
||||||
form_builder_extra_fields:
|
form_builder_extra_fields:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: main
|
ref: main
|
||||||
resolved-ref: "33ba0a4407086275ac4357badc631be550fb3bcc"
|
resolved-ref: b02de7dad9c00ece575ad4b8dfba73a3e1239e9c
|
||||||
url: "https://github.com/flutter-form-builder-ecosystem/form_builder_extra_fields.git"
|
url: "https://github.com/flutter-form-builder-ecosystem/form_builder_extra_fields.git"
|
||||||
source: git
|
source: git
|
||||||
version: "8.4.0"
|
version: "8.4.0"
|
||||||
@@ -636,14 +643,14 @@ packages:
|
|||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.0"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.2.0"
|
||||||
hive:
|
hive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -657,7 +664,7 @@ packages:
|
|||||||
name: html
|
name: html
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.0"
|
version: "0.15.1"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -671,28 +678,28 @@ packages:
|
|||||||
name: http_interceptor
|
name: http_interceptor
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0-beta.5"
|
version: "2.0.0-beta.6"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_multi_server
|
name: http_multi_server
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.1"
|
version: "4.0.2"
|
||||||
image:
|
image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.2"
|
||||||
infinite_scroll_pagination:
|
infinite_scroll_pagination:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -713,7 +720,7 @@ packages:
|
|||||||
name: injectable_generator
|
name: injectable_generator
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -739,7 +746,7 @@ packages:
|
|||||||
name: introduction_screen
|
name: introduction_screen
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.1.1"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -781,7 +788,7 @@ packages:
|
|||||||
name: local_auth_android
|
name: local_auth_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.13"
|
version: "1.0.15"
|
||||||
local_auth_ios:
|
local_auth_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -809,7 +816,7 @@ packages:
|
|||||||
name: logging
|
name: logging
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.1.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -837,7 +844,7 @@ packages:
|
|||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.3"
|
||||||
mockito:
|
mockito:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -886,7 +893,7 @@ packages:
|
|||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.0"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -921,7 +928,7 @@ packages:
|
|||||||
name: package_info_plus_web
|
name: package_info_plus_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
package_info_plus_windows:
|
package_info_plus_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -949,14 +956,14 @@ packages:
|
|||||||
name: path_drawing
|
name: path_drawing
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
path_parsing:
|
path_parsing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_parsing
|
name: path_parsing
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -970,14 +977,14 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.14"
|
version: "2.0.22"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.9"
|
version: "2.0.11"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -998,7 +1005,7 @@ packages:
|
|||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.5"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1012,7 +1019,7 @@ packages:
|
|||||||
name: pdf
|
name: pdf
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.1"
|
version: "3.8.4"
|
||||||
pdfx:
|
pdfx:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1047,28 +1054,28 @@ packages:
|
|||||||
name: permission_handler_apple
|
name: permission_handler_apple
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.4"
|
version: "9.0.7"
|
||||||
permission_handler_platform_interface:
|
permission_handler_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_platform_interface
|
name: permission_handler_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.0"
|
version: "3.9.0"
|
||||||
permission_handler_windows:
|
permission_handler_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: permission_handler_windows
|
name: permission_handler_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "0.1.2"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.1.0"
|
||||||
photo_view:
|
photo_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1089,21 +1096,21 @@ packages:
|
|||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.0"
|
version: "3.6.2"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pool
|
name: pool
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.1"
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1117,21 +1124,21 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.0.4"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.3"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
qr:
|
qr:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1139,6 +1146,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
recase:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: recase
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
receive_sharing_intent:
|
receive_sharing_intent:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1159,7 +1173,7 @@ packages:
|
|||||||
name: share_plus
|
name: share_plus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.0"
|
version: "6.3.0"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1180,7 +1194,7 @@ packages:
|
|||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.12"
|
version: "2.0.14"
|
||||||
shared_preferences_ios:
|
shared_preferences_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1208,7 +1222,7 @@ packages:
|
|||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.1.0"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1229,7 +1243,7 @@ packages:
|
|||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.0"
|
||||||
shelf_packages_handler:
|
shelf_packages_handler:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1250,7 +1264,7 @@ packages:
|
|||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.3"
|
||||||
shimmer:
|
shimmer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1264,47 +1278,40 @@ packages:
|
|||||||
name: signature
|
name: signature
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.1"
|
version: "5.3.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.99"
|
||||||
sliding_up_panel:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sliding_up_panel
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0+1"
|
|
||||||
sliver_tools:
|
sliver_tools:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sliver_tools
|
name: sliver_tools
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.7"
|
version: "0.2.8"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.6"
|
||||||
source_map_stack_trace:
|
source_map_stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_map_stack_trace
|
name: source_map_stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
source_maps:
|
source_maps:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_maps
|
name: source_maps
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.10.10"
|
version: "0.10.11"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1318,14 +1325,14 @@ packages:
|
|||||||
name: sqflite
|
name: sqflite
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2+1"
|
version: "2.2.2"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1+1"
|
version: "2.4.0+2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1346,7 +1353,7 @@ packages:
|
|||||||
name: stream_transform
|
name: stream_transform
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.1.0"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1367,7 +1374,7 @@ packages:
|
|||||||
name: synchronized
|
name: synchronized
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0+2"
|
version: "3.0.0+3"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1430,14 +1437,14 @@ packages:
|
|||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.7"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.17"
|
version: "6.0.22"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1486,7 +1493,7 @@ packages:
|
|||||||
name: uuid
|
name: uuid
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.7"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1507,7 +1514,7 @@ packages:
|
|||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1535,14 +1542,14 @@ packages:
|
|||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.1.2"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+1"
|
version: "0.2.0+2"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1559,4 +1566,4 @@ packages:
|
|||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.5 <3.0.0"
|
dart: ">=2.18.5 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.3.0"
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ dependencies:
|
|||||||
ref: main
|
ref: main
|
||||||
form_builder_validators: ^8.4.0
|
form_builder_validators: ^8.4.0
|
||||||
infinite_scroll_pagination: ^3.2.0
|
infinite_scroll_pagination: ^3.2.0
|
||||||
sliding_up_panel: ^2.0.0+1
|
|
||||||
package_info_plus: ^1.4.3+1
|
package_info_plus: ^1.4.3+1
|
||||||
font_awesome_flutter: ^10.1.0
|
font_awesome_flutter: ^10.1.0
|
||||||
local_auth: ^2.1.2
|
local_auth: ^2.1.2
|
||||||
@@ -82,6 +81,7 @@ dependencies:
|
|||||||
path: packages/paperless_api
|
path: packages/paperless_api
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
rxdart: ^0.27.7
|
rxdart: ^0.27.7
|
||||||
|
badges: ^2.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:bloc_test/bloc_test.dart';
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:mockito/annotations.dart';
|
import 'package:mockito/annotations.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user