mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-07 07:15:47 -06:00
WIP - More decoupling of data layer from ui layer
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
import 'dart:ui';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
|
||||||
|
|
||||||
class BlocChangesObserver extends BlocObserver {
|
class BlocChangesObserver extends BlocObserver {
|
||||||
@override
|
@override
|
||||||
|
|||||||
67
lib/core/repository/impl/correspondent_repository_impl.dart
Normal file
67
lib/core/repository/impl/correspondent_repository_impl.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart' show BehaviorSubject;
|
||||||
|
|
||||||
|
class CorrespondentRepositoryImpl implements LabelRepository<Correspondent> {
|
||||||
|
final PaperlessLabelsApi _api;
|
||||||
|
|
||||||
|
final _subject = BehaviorSubject<Map<int, Correspondent>>.seeded(const {});
|
||||||
|
|
||||||
|
CorrespondentRepositoryImpl(this._api);
|
||||||
|
@override
|
||||||
|
Stream<Map<int, Correspondent>> get labels =>
|
||||||
|
_subject.stream.asBroadcastStream();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Correspondent> create(Correspondent correspondent) async {
|
||||||
|
final created = await _api.saveCorrespondent(correspondent);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> delete(Correspondent correspondent) async {
|
||||||
|
await _api.deleteCorrespondent(correspondent);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..removeWhere((k, v) => k == correspondent.id);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Correspondent?> find(int id) async {
|
||||||
|
final correspondent = await _api.getCorrespondent(id);
|
||||||
|
if (correspondent != null) {
|
||||||
|
final updatedState = {..._subject.value}..[id] = correspondent;
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return correspondent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Iterable<Correspondent>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final correspondents = await _api.getCorrespondents(ids);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..addEntries(correspondents.map((e) => MapEntry(e.id!, e)));
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return correspondents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Correspondent> update(Correspondent correspondent) async {
|
||||||
|
final updated = await _api.updateCorrespondent(correspondent);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
_subject.add(const {});
|
||||||
|
}
|
||||||
|
}
|
||||||
66
lib/core/repository/impl/document_type_repository_impl.dart
Normal file
66
lib/core/repository/impl/document_type_repository_impl.dart
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart' show BehaviorSubject;
|
||||||
|
|
||||||
|
class DocumentTypeRepositoryImpl implements LabelRepository<DocumentType> {
|
||||||
|
final PaperlessLabelsApi _api;
|
||||||
|
|
||||||
|
final _subject = BehaviorSubject<Map<int, DocumentType>>.seeded(const {});
|
||||||
|
|
||||||
|
DocumentTypeRepositoryImpl(this._api);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Map<int, DocumentType>> get labels =>
|
||||||
|
_subject.stream.asBroadcastStream();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<DocumentType> create(DocumentType documentType) async {
|
||||||
|
final created = await _api.saveDocumentType(documentType);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> delete(DocumentType documentType) async {
|
||||||
|
await _api.deleteDocumentType(documentType);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..removeWhere((k, v) => k == documentType.id);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<DocumentType?> find(int id) async {
|
||||||
|
final documentType = await _api.getDocumentType(id);
|
||||||
|
if (documentType != null) {
|
||||||
|
final updatedState = {..._subject.value}..[id] = documentType;
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return documentType;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Iterable<DocumentType>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final documentTypes = await _api.getDocumentTypes(ids);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..addEntries(documentTypes.map((e) => MapEntry(e.id!, e)));
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return documentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<DocumentType> update(DocumentType documentType) async {
|
||||||
|
final updated = await _api.updateDocumentType(documentType);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
_subject.add(const {});
|
||||||
|
}
|
||||||
|
}
|
||||||
57
lib/core/repository/impl/saved_view_repository_impl.dart
Normal file
57
lib/core/repository/impl/saved_view_repository_impl.dart
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_api/src/models/saved_view_model.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
class SavedViewRepositoryImpl implements SavedViewRepository {
|
||||||
|
final PaperlessSavedViewsApi _api;
|
||||||
|
|
||||||
|
SavedViewRepositoryImpl(this._api);
|
||||||
|
|
||||||
|
final BehaviorSubject<Map<int, SavedView>> _subject =
|
||||||
|
BehaviorSubject.seeded({});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Map<int, SavedView>> get savedViews =>
|
||||||
|
_subject.stream.asBroadcastStream();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SavedView> create(SavedView view) async {
|
||||||
|
final created = await _api.save(view);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> delete(SavedView view) async {
|
||||||
|
await _api.delete(view);
|
||||||
|
final updatedState = {..._subject.value}..remove(view.id);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return view.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SavedView?> find(int id) async {
|
||||||
|
final found = await _api.find(id);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..update(id, (_) => found, ifAbsent: () => found);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final found = await _api.findAll(ids);
|
||||||
|
final updatedState = {
|
||||||
|
..._subject.value,
|
||||||
|
...{for (final view in found) view.id!: view},
|
||||||
|
};
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
lib/core/repository/impl/storage_path_repository_impl.dart
Normal file
66
lib/core/repository/impl/storage_path_repository_impl.dart
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart' show BehaviorSubject;
|
||||||
|
|
||||||
|
class StoragePathRepositoryImpl implements LabelRepository<StoragePath> {
|
||||||
|
final PaperlessLabelsApi _api;
|
||||||
|
|
||||||
|
final _subject = BehaviorSubject<Map<int, StoragePath>>.seeded(const {});
|
||||||
|
|
||||||
|
StoragePathRepositoryImpl(this._api);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Map<int, StoragePath>> get labels =>
|
||||||
|
_subject.stream.asBroadcastStream();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StoragePath> create(StoragePath storagePath) async {
|
||||||
|
final created = await _api.saveStoragePath(storagePath);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> delete(StoragePath storagePath) async {
|
||||||
|
await _api.deleteStoragePath(storagePath);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..removeWhere((k, v) => k == storagePath.id);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StoragePath?> find(int id) async {
|
||||||
|
final storagePath = await _api.getStoragePath(id);
|
||||||
|
if (storagePath != null) {
|
||||||
|
final updatedState = {..._subject.value}..[id] = storagePath;
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return storagePath;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Iterable<StoragePath>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final storagePaths = await _api.getStoragePaths(ids);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..addEntries(storagePaths.map((e) => MapEntry(e.id!, e)));
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return storagePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StoragePath> update(StoragePath storagePath) async {
|
||||||
|
final updated = await _api.updateStoragePath(storagePath);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
_subject.add(const {});
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/core/repository/impl/tag_repository_impl.dart
Normal file
65
lib/core/repository/impl/tag_repository_impl.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart' show BehaviorSubject;
|
||||||
|
|
||||||
|
class TagRepositoryImpl implements LabelRepository<Tag> {
|
||||||
|
final PaperlessLabelsApi _api;
|
||||||
|
|
||||||
|
final _subject = BehaviorSubject<Map<int, Tag>>.seeded(const {});
|
||||||
|
|
||||||
|
TagRepositoryImpl(this._api);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<Map<int, Tag>> get labels => _subject.stream.asBroadcastStream();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tag> create(Tag tag) async {
|
||||||
|
final created = await _api.saveTag(tag);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> delete(Tag tag) async {
|
||||||
|
await _api.deleteTag(tag);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..removeWhere((k, v) => k == tag.id);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tag?> find(int id) async {
|
||||||
|
final tag = await _api.getTag(id);
|
||||||
|
if (tag != null) {
|
||||||
|
final updatedState = {..._subject.value}..[id] = tag;
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Iterable<Tag>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final tags = await _api.getTags(ids);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..addEntries(tags.map((e) => MapEntry(e.id!, e)));
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Tag> update(Tag tag) async {
|
||||||
|
final updated = await _api.updateTag(tag);
|
||||||
|
final updatedState = {..._subject.value}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
_subject.add(updatedState);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
_subject.add(const {});
|
||||||
|
}
|
||||||
|
}
|
||||||
13
lib/core/repository/label_repository.dart
Normal file
13
lib/core/repository/label_repository.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
abstract class LabelRepository<T extends Label> {
|
||||||
|
Stream<Map<int, T>> get labels;
|
||||||
|
|
||||||
|
Future<T> create(T label);
|
||||||
|
Future<T?> find(int id);
|
||||||
|
Future<Iterable<T>> findAll([Iterable<int>? ids]);
|
||||||
|
Future<T> update(T label);
|
||||||
|
Future<void> delete(T label);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/src/widgets/container.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
|
||||||
|
class LabelRepositoriesProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const LabelRepositoriesProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
lib/core/repository/saved_view_repository.dart
Normal file
12
lib/core/repository/saved_view_repository.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
abstract class SavedViewRepository {
|
||||||
|
Stream<Map<int, SavedView>> get savedViews;
|
||||||
|
|
||||||
|
Future<SavedView> create(SavedView view);
|
||||||
|
Future<SavedView?> find(int id);
|
||||||
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]);
|
||||||
|
Future<int> delete(SavedView view);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.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/widgets/highlighted_text.dart';
|
import 'package:paperless_mobile/core/widgets/highlighted_text.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';
|
||||||
@@ -17,7 +18,6 @@ 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/labels/bloc/global_state_bloc_provider.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';
|
||||||
@@ -213,7 +213,29 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
Navigator.push<bool>(
|
Navigator.push<bool>(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Tag>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: DocumentEditPage(
|
child: DocumentEditPage(
|
||||||
document: cubit.state.document!,
|
document: cubit.state.document!,
|
||||||
onEdit: (updatedDocument) {
|
onEdit: (updatedDocument) {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||||
|
|
||||||
|
part 'document_upload_state.dart';
|
||||||
|
|
||||||
|
class DocumentUploadCubit extends Cubit<DocumentUploadState> {
|
||||||
|
final LocalVault _localVault;
|
||||||
|
final PaperlessDocumentsApi _documentApi;
|
||||||
|
|
||||||
|
final LabelRepository<Tag> _tagRepository;
|
||||||
|
final LabelRepository<Correspondent> _correspondentRepository;
|
||||||
|
final LabelRepository<DocumentType> _documentTypeRepository;
|
||||||
|
|
||||||
|
final List<StreamSubscription> _subs = const [];
|
||||||
|
|
||||||
|
DocumentUploadCubit({
|
||||||
|
required LocalVault localVault,
|
||||||
|
required PaperlessDocumentsApi documentApi,
|
||||||
|
required LabelRepository<Tag> tagRepository,
|
||||||
|
required LabelRepository<Correspondent> correspondentRepository,
|
||||||
|
required LabelRepository<DocumentType> documentTypeRepository,
|
||||||
|
}) : _documentApi = documentApi,
|
||||||
|
_tagRepository = tagRepository,
|
||||||
|
_correspondentRepository = correspondentRepository,
|
||||||
|
_documentTypeRepository = documentTypeRepository,
|
||||||
|
_localVault = localVault,
|
||||||
|
super(
|
||||||
|
const DocumentUploadState(
|
||||||
|
tags: {},
|
||||||
|
correspondents: {},
|
||||||
|
documentTypes: {},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
_subs.add(_tagRepository.labels.listen(
|
||||||
|
(tags) => emit(state.copyWith(tags: tags)),
|
||||||
|
));
|
||||||
|
_subs.add(_correspondentRepository.labels.listen(
|
||||||
|
(correspondents) => emit(state.copyWith(correspondents: correspondents)),
|
||||||
|
));
|
||||||
|
_subs.add(_documentTypeRepository.labels.listen(
|
||||||
|
(documentTypes) => emit(state.copyWith(documentTypes: documentTypes)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> upload(
|
||||||
|
Uint8List bytes, {
|
||||||
|
required String filename,
|
||||||
|
required String title,
|
||||||
|
required void Function(DocumentModel document)? onConsumptionFinished,
|
||||||
|
int? documentType,
|
||||||
|
int? correspondent,
|
||||||
|
Iterable<int> tags = const [],
|
||||||
|
DateTime? createdAt,
|
||||||
|
}) async {
|
||||||
|
final auth = await _localVault.loadAuthenticationInformation();
|
||||||
|
if (auth == null || !auth.isValid) {
|
||||||
|
throw const PaperlessServerException(ErrorCode.notAuthenticated);
|
||||||
|
}
|
||||||
|
await _documentApi.create(
|
||||||
|
bytes,
|
||||||
|
filename: filename,
|
||||||
|
title: title,
|
||||||
|
correspondent: correspondent,
|
||||||
|
documentType: documentType,
|
||||||
|
tags: tags,
|
||||||
|
createdAt: createdAt,
|
||||||
|
authToken: auth.token!,
|
||||||
|
serverUrl: auth.serverUrl,
|
||||||
|
);
|
||||||
|
if (onConsumptionFinished != null) {
|
||||||
|
_documentApi
|
||||||
|
.waitForConsumptionFinished(filename, title)
|
||||||
|
.then((value) => onConsumptionFinished(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
for (final sub in _subs) {
|
||||||
|
await sub.cancel();
|
||||||
|
}
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
part of 'document_upload_cubit.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class DocumentUploadState extends Equatable {
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
|
||||||
|
const DocumentUploadState({
|
||||||
|
required this.tags,
|
||||||
|
required this.correspondents,
|
||||||
|
required this.documentTypes,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
|
||||||
|
DocumentUploadState copyWith({
|
||||||
|
Map<int, Tag>? tags,
|
||||||
|
Map<int, Correspondent>? correspondents,
|
||||||
|
Map<int, DocumentType>? documentTypes,
|
||||||
|
}) {
|
||||||
|
return DocumentUploadState(
|
||||||
|
tags: tags ?? this.tags,
|
||||||
|
correspondents: correspondents ?? this.correspondents,
|
||||||
|
documentTypes: documentTypes ?? this.documentTypes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/type/types.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_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_document_type_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
|
class DocumentUploadPreparationPage extends StatefulWidget {
|
||||||
|
final Uint8List fileBytes;
|
||||||
|
final String? title;
|
||||||
|
final String? filename;
|
||||||
|
final void Function(DocumentModel)? onSuccessfullyConsumed;
|
||||||
|
|
||||||
|
const DocumentUploadPreparationPage({
|
||||||
|
Key? key,
|
||||||
|
required this.fileBytes,
|
||||||
|
this.title,
|
||||||
|
this.filename,
|
||||||
|
this.onSuccessfullyConsumed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DocumentUploadPreparationPage> createState() =>
|
||||||
|
_DocumentUploadPreparationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DocumentUploadPreparationPageState
|
||||||
|
extends State<DocumentUploadPreparationPage> {
|
||||||
|
static const fkFileName = "filename";
|
||||||
|
static final fileNameDateFormat = DateFormat("yyyy_MM_ddTHH_mm_ss");
|
||||||
|
|
||||||
|
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
||||||
|
|
||||||
|
PaperlessValidationErrors _errors = {};
|
||||||
|
bool _isUploadLoading = false;
|
||||||
|
late bool _syncTitleAndFilename;
|
||||||
|
final _now = DateTime.now();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_syncTitleAndFilename = widget.filename == null && widget.title == null;
|
||||||
|
initializeDateFormatting();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(S.of(context).documentsUploadPageTitle),
|
||||||
|
bottom: _isUploadLoading
|
||||||
|
? const PreferredSize(
|
||||||
|
child: LinearProgressIndicator(),
|
||||||
|
preferredSize: Size.fromHeight(4.0))
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
floatingActionButton: Visibility(
|
||||||
|
visible: MediaQuery.of(context).viewInsets.bottom == 0,
|
||||||
|
child: FloatingActionButton.extended(
|
||||||
|
onPressed: _onSubmit,
|
||||||
|
label: Text(S.of(context).genericActionUploadLabel),
|
||||||
|
icon: const Icon(Icons.upload),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: BlocBuilder<DocumentUploadCubit, DocumentUploadState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return FormBuilder(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
FormBuilderTextField(
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
name: DocumentModel.titleKey,
|
||||||
|
initialValue:
|
||||||
|
widget.title ?? "scan_${fileNameDateFormat.format(_now)}",
|
||||||
|
validator: FormBuilderValidators.required(),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: S.of(context).documentTitlePropertyLabel,
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: () {
|
||||||
|
_formKey.currentState?.fields[DocumentModel.titleKey]
|
||||||
|
?.didChange("");
|
||||||
|
if (_syncTitleAndFilename) {
|
||||||
|
_formKey.currentState?.fields[fkFileName]
|
||||||
|
?.didChange("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
errorText: _errors[DocumentModel.titleKey],
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
final String transformedValue =
|
||||||
|
_formatFilename(value ?? '');
|
||||||
|
if (_syncTitleAndFilename) {
|
||||||
|
_formKey.currentState?.fields[fkFileName]
|
||||||
|
?.didChange(transformedValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FormBuilderTextField(
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
readOnly: _syncTitleAndFilename,
|
||||||
|
enabled: !_syncTitleAndFilename,
|
||||||
|
name: fkFileName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: S.of(context).documentUploadFileNameLabel,
|
||||||
|
suffixText: ".pdf",
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () => _formKey.currentState?.fields[fkFileName]
|
||||||
|
?.didChange(''),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initialValue: widget.filename ??
|
||||||
|
"scan_${fileNameDateFormat.format(_now)}",
|
||||||
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
value: _syncTitleAndFilename,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(
|
||||||
|
() => _syncTitleAndFilename = value,
|
||||||
|
);
|
||||||
|
if (_syncTitleAndFilename) {
|
||||||
|
final String transformedValue = _formatFilename(_formKey
|
||||||
|
.currentState
|
||||||
|
?.fields[DocumentModel.titleKey]
|
||||||
|
?.value as String);
|
||||||
|
if (_syncTitleAndFilename) {
|
||||||
|
_formKey.currentState?.fields[fkFileName]
|
||||||
|
?.didChange(transformedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: Text(S
|
||||||
|
.of(context)
|
||||||
|
.documentUploadPageSynchronizeTitleAndFilenameLabel), //TODO: INTL
|
||||||
|
),
|
||||||
|
FormBuilderDateTimePicker(
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
format: DateFormat("dd. MMMM yyyy"), //TODO: INTL
|
||||||
|
inputType: InputType.date,
|
||||||
|
name: DocumentModel.createdKey,
|
||||||
|
initialValue: null,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.calendar_month_outlined),
|
||||||
|
labelText:
|
||||||
|
S.of(context).documentCreatedPropertyLabel + " *",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
labelCreationWidgetBuilder: (initialName) =>
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
child: AddDocumentTypePage(initialName: initialName),
|
||||||
|
),
|
||||||
|
label: S.of(context).documentDocumentTypePropertyLabel + " *",
|
||||||
|
name: DocumentModel.documentTypeKey,
|
||||||
|
state: state.documentTypes,
|
||||||
|
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||||
|
queryParameterNotAssignedBuilder:
|
||||||
|
DocumentTypeQuery.notAssigned,
|
||||||
|
prefixIcon: const Icon(Icons.description_outlined),
|
||||||
|
),
|
||||||
|
LabelFormField<Correspondent, CorrespondentQuery>(
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
labelCreationWidgetBuilder: (initialName) =>
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value:
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
child: AddCorrespondentPage(initialName: initialName),
|
||||||
|
),
|
||||||
|
label:
|
||||||
|
S.of(context).documentCorrespondentPropertyLabel + " *",
|
||||||
|
name: DocumentModel.correspondentKey,
|
||||||
|
state: state.correspondents,
|
||||||
|
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||||
|
queryParameterNotAssignedBuilder:
|
||||||
|
CorrespondentQuery.notAssigned,
|
||||||
|
prefixIcon: const Icon(Icons.person_outline),
|
||||||
|
),
|
||||||
|
const TagFormField(
|
||||||
|
name: DocumentModel.tagsKey,
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
anyAssignedSelectable: false,
|
||||||
|
excludeAllowed: false,
|
||||||
|
//Label: "Tags" + " *",
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"* " +
|
||||||
|
S
|
||||||
|
.of(context)
|
||||||
|
.uploadPageAutomaticallInferredFieldsHintText,
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
),
|
||||||
|
].padded(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSubmit() async {
|
||||||
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
|
final cubit = BlocProvider.of<DocumentUploadCubit>(context);
|
||||||
|
try {
|
||||||
|
setState(() => _isUploadLoading = true);
|
||||||
|
|
||||||
|
final fv = _formKey.currentState!.value;
|
||||||
|
|
||||||
|
final createdAt = fv[DocumentModel.createdKey] as DateTime?;
|
||||||
|
final title = fv[DocumentModel.titleKey] as String;
|
||||||
|
final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter;
|
||||||
|
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery;
|
||||||
|
final correspondent =
|
||||||
|
fv[DocumentModel.correspondentKey] as IdQueryParameter;
|
||||||
|
|
||||||
|
await cubit.upload(
|
||||||
|
widget.fileBytes,
|
||||||
|
filename:
|
||||||
|
_padWithPdfExtension(_formKey.currentState?.value[fkFileName]),
|
||||||
|
title: title,
|
||||||
|
onConsumptionFinished: widget.onSuccessfullyConsumed,
|
||||||
|
documentType: docType.id,
|
||||||
|
correspondent: correspondent.id,
|
||||||
|
tags: tags.ids,
|
||||||
|
createdAt: createdAt,
|
||||||
|
);
|
||||||
|
showSnackBar(context, S.of(context).documentUploadSuccessText);
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
} on PaperlessValidationErrors catch (PaperlessServerExceptions) {
|
||||||
|
setState(() => _errors = PaperlessServerExceptions);
|
||||||
|
} catch (unknownError, stackTrace) {
|
||||||
|
showErrorMessage(
|
||||||
|
context, const PaperlessServerException.unknown(), stackTrace);
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isUploadLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _padWithPdfExtension(String source) {
|
||||||
|
return source.endsWith(".pdf") ? source : '$source.pdf';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatFilename(String source) {
|
||||||
|
return source.replaceAll(RegExp(r"[\W_]"), "_");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
|
||||||
|
|
||||||
@prod
|
part 'documents_state.dart';
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class DocumentsCubit extends Cubit<DocumentsState> {
|
class DocumentsCubit extends Cubit<DocumentsState> {
|
||||||
final PaperlessDocumentsApi _api;
|
final PaperlessDocumentsApi _api;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
part of 'documents_cubit.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
|
|
||||||
class DocumentsState extends Equatable {
|
class DocumentsState extends Equatable {
|
||||||
final bool isLoaded;
|
final bool isLoaded;
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
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/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/labels/correspondent/bloc/correspondents_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/add_document_type_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/bloc/label_state.dart';
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/pages/add_storage_path_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
import 'package:paperless_mobile/features/labels/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';
|
||||||
@@ -57,10 +57,133 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return LabelsBlocProvider(
|
||||||
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () async {
|
onPressed: _onSubmit,
|
||||||
|
icon: const Icon(Icons.save),
|
||||||
|
label: Text(S.of(context).genericActionSaveLabel),
|
||||||
|
),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(S.of(context).documentEditPageTitle),
|
||||||
|
bottom: _isSubmitLoading
|
||||||
|
? const PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(4),
|
||||||
|
child: LinearProgressIndicator(),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
extendBody: true,
|
||||||
|
body: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
|
top: 8,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
),
|
||||||
|
child: FormBuilder(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(children: [
|
||||||
|
_buildTitleFormField().padded(),
|
||||||
|
_buildCreatedAtFormField().padded(),
|
||||||
|
_buildDocumentTypeFormField().padded(),
|
||||||
|
_buildCorrespondentFormField().padded(),
|
||||||
|
_buildStoragePathFormField().padded(),
|
||||||
|
TagFormField(
|
||||||
|
initialValue: IdsTagsQuery.included(widget.document.tags),
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
anyAssignedSelectable: false,
|
||||||
|
excludeAllowed: false,
|
||||||
|
name: fkTags,
|
||||||
|
).padded(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>
|
||||||
|
_buildStoragePathFormField() {
|
||||||
|
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
labelCreationWidgetBuilder: (initialValue) =>
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
child: AddStoragePathPage(initalValue: initialValue),
|
||||||
|
),
|
||||||
|
label: S.of(context).documentStoragePathPropertyLabel,
|
||||||
|
state: state.labels,
|
||||||
|
initialValue: StoragePathQuery.fromId(widget.document.storagePath),
|
||||||
|
name: fkStoragePath,
|
||||||
|
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||||
|
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
||||||
|
prefixIcon: const Icon(Icons.folder_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>
|
||||||
|
_buildCorrespondentFormField() {
|
||||||
|
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
labelCreationWidgetBuilder: (initialValue) =>
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
child: AddCorrespondentPage(initialName: initialValue),
|
||||||
|
),
|
||||||
|
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||||
|
state: state.labels,
|
||||||
|
initialValue:
|
||||||
|
CorrespondentQuery.fromId(widget.document.correspondent),
|
||||||
|
name: fkCorrespondent,
|
||||||
|
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||||
|
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
||||||
|
prefixIcon: const Icon(Icons.person_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>
|
||||||
|
_buildDocumentTypeFormField() {
|
||||||
|
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||||
|
notAssignedSelectable: false,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
labelCreationWidgetBuilder: (currentInput) =>
|
||||||
|
RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
child: AddDocumentTypePage(
|
||||||
|
initialName: currentInput,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||||
|
initialValue: DocumentTypeQuery.fromId(widget.document.documentType),
|
||||||
|
state: state.labels,
|
||||||
|
name: fkDocumentType,
|
||||||
|
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||||
|
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
||||||
|
prefixIcon: const Icon(Icons.description_outlined),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSubmit() 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 updatedDocument = widget.document.copyWith(
|
||||||
@@ -91,111 +214,6 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
icon: const Icon(Icons.save),
|
|
||||||
label: Text(S.of(context).genericActionSaveLabel),
|
|
||||||
),
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(S.of(context).documentEditPageTitle),
|
|
||||||
bottom: _isSubmitLoading
|
|
||||||
? const PreferredSize(
|
|
||||||
preferredSize: Size.fromHeight(4),
|
|
||||||
child: LinearProgressIndicator(),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
extendBody: true,
|
|
||||||
body: Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
|
||||||
top: 8,
|
|
||||||
left: 8,
|
|
||||||
right: 8,
|
|
||||||
),
|
|
||||||
child: FormBuilder(
|
|
||||||
key: _formKey,
|
|
||||||
child: ListView(children: [
|
|
||||||
_buildTitleFormField().padded(),
|
|
||||||
_buildCreatedAtFormField().padded(),
|
|
||||||
BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
labelCreationWidgetBuilder: (currentInput) =>
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
|
||||||
child: AddDocumentTypePage(
|
|
||||||
initialName: currentInput,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
|
||||||
initialValue:
|
|
||||||
DocumentTypeQuery.fromId(widget.document.documentType),
|
|
||||||
state: state.labels,
|
|
||||||
name: fkDocumentType,
|
|
||||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
|
||||||
queryParameterNotAssignedBuilder:
|
|
||||||
DocumentTypeQuery.notAssigned,
|
|
||||||
prefixIcon: const Icon(Icons.description_outlined),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).padded(),
|
|
||||||
BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
|
||||||
child: AddCorrespondentPage(initalValue: initialValue),
|
|
||||||
),
|
|
||||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
|
||||||
state: state.labels,
|
|
||||||
initialValue:
|
|
||||||
CorrespondentQuery.fromId(widget.document.correspondent),
|
|
||||||
name: fkCorrespondent,
|
|
||||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
|
||||||
queryParameterNotAssignedBuilder:
|
|
||||||
CorrespondentQuery.notAssigned,
|
|
||||||
prefixIcon: const Icon(Icons.person_outlined),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).padded(),
|
|
||||||
BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<StoragePathCubit>(context),
|
|
||||||
child: AddStoragePathPage(initalValue: initialValue),
|
|
||||||
),
|
|
||||||
label: S.of(context).documentStoragePathPropertyLabel,
|
|
||||||
state: state.labels,
|
|
||||||
initialValue:
|
|
||||||
StoragePathQuery.fromId(widget.document.storagePath),
|
|
||||||
name: fkStoragePath,
|
|
||||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
|
||||||
queryParameterNotAssignedBuilder:
|
|
||||||
StoragePathQuery.notAssigned,
|
|
||||||
prefixIcon: const Icon(Icons.folder_outlined),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).padded(),
|
|
||||||
TagFormField(
|
|
||||||
initialValue: IdsTagsQuery.included(widget.document.tags),
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
anyAssignedSelectable: false,
|
|
||||||
excludeAllowed: false,
|
|
||||||
name: fkTags,
|
|
||||||
).padded(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTitleFormField() {
|
Widget _buildTitleFormField() {
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ 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/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';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
|
||||||
@@ -15,11 +16,10 @@ import 'package:paperless_mobile/features/documents/view/widgets/search/document
|
|||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/documents_page_app_bar.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/documents_page_app_bar.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.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/saved_view/cubit/saved_view_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
@@ -132,9 +132,20 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
),
|
),
|
||||||
body: _buildBody(connectivityState),
|
body: _buildBody(connectivityState),
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
panelBuilder: (scrollController) => DocumentFilterPanel(
|
panelBuilder: (scrollController) =>
|
||||||
|
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return LabelsBlocProvider(
|
||||||
|
child: DocumentFilterPanel(
|
||||||
panelController: _filterPanelController,
|
panelController: _filterPanelController,
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
|
initialFilter: state.filter,
|
||||||
|
onFilterChanged: (filter) =>
|
||||||
|
BlocProvider.of<DocumentsCubit>(context)
|
||||||
|
.updateFilter(filter: filter),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -192,7 +203,29 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
onRefresh: _onRefresh,
|
onRefresh: _onRefresh,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
DocumentsPageAppBar(
|
BlocProvider(
|
||||||
|
create: (context) => SavedViewCubit(
|
||||||
|
RepositoryProvider.of<SavedViewRepository>(context)),
|
||||||
|
child: BlocListener<SavedViewCubit, SavedViewState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
final documentsCubit =
|
||||||
|
BlocProvider.of<DocumentsCubit>(context);
|
||||||
|
try {
|
||||||
|
if (state.selectedSavedViewId == null) {
|
||||||
|
documentsCubit.updateFilter();
|
||||||
|
} else {
|
||||||
|
final newFilter = state
|
||||||
|
.value[state.selectedSavedViewId]
|
||||||
|
?.toDocumentFilter();
|
||||||
|
if (newFilter != null) {
|
||||||
|
documentsCubit.updateFilter(filter: newFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: DocumentsPageAppBar(
|
||||||
actions: [
|
actions: [
|
||||||
const SortDocumentsButton(),
|
const SortDocumentsButton(),
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -202,12 +235,15 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
: Icons.grid_view,
|
: Icons.grid_view,
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
BlocProvider.of<ApplicationSettingsCubit>(context)
|
BlocProvider.of<ApplicationSettingsCubit>(
|
||||||
|
context)
|
||||||
.setViewType(
|
.setViewType(
|
||||||
settings.preferredViewType.toggle()),
|
settings.preferredViewType.toggle()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
child,
|
child,
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@@ -233,29 +269,11 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
MaterialPageRoute<DocumentModel?> _buildDetailsPageRoute(
|
MaterialPageRoute<DocumentModel?> _buildDetailsPageRoute(
|
||||||
DocumentModel document) {
|
DocumentModel document) {
|
||||||
return MaterialPageRoute(
|
return MaterialPageRoute(
|
||||||
builder: (_) => MultiBlocProvider(
|
builder: (_) => BlocProvider.value(
|
||||||
providers: [
|
value: DocumentDetailsCubit(getIt<PaperlessDocumentsApi>(), document),
|
||||||
BlocProvider.value(
|
child: const LabelRepositoriesProvider(
|
||||||
value: BlocProvider.of<DocumentsCubit>(context),
|
child: DocumentDetailsPage(),
|
||||||
),
|
),
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
|
||||||
),
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
|
||||||
),
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<TagCubit>(context),
|
|
||||||
),
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<StoragePathCubit>(context),
|
|
||||||
),
|
|
||||||
BlocProvider.value(
|
|
||||||
value:
|
|
||||||
DocumentDetailsCubit(getIt<PaperlessDocumentsApi>(), document),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: const DocumentDetailsPage(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
import 'package:paperless_mobile/core/widgets/empty_state.dart';
|
import 'package:paperless_mobile/core/widgets/empty_state.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/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class DocumentsEmptyState extends StatelessWidget {
|
class DocumentsEmptyState extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid_item.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
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/core/repository/provider/label_repositories_provider.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
|
||||||
@@ -34,7 +36,8 @@ class DocumentListView extends StatelessWidget {
|
|||||||
builderDelegate: PagedChildBuilderDelegate(
|
builderDelegate: PagedChildBuilderDelegate(
|
||||||
animateTransitions: true,
|
animateTransitions: true,
|
||||||
itemBuilder: (context, document, index) {
|
itemBuilder: (context, document, index) {
|
||||||
return DocumentListItem(
|
return LabelRepositoriesProvider(
|
||||||
|
child: DocumentListItem(
|
||||||
isLabelClickable: isLabelClickable,
|
isLabelClickable: isLabelClickable,
|
||||||
document: document,
|
document: document,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
@@ -49,6 +52,7 @@ class DocumentListView extends StatelessWidget {
|
|||||||
: false;
|
: false;
|
||||||
},
|
},
|
||||||
onTagSelected: onTagSelected,
|
onTagSelected: onTagSelected,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
noItemsFoundIndicatorBuilder: (context) => hasInternetConnection
|
noItemsFoundIndicatorBuilder: (context) => hasInternetConnection
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import 'package:flutter_form_builder/flutter_form_builder.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/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.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/correspondent/bloc/correspondents_cubit.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_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/storage_path/bloc/storage_path_cubit.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/bloc/saved_view_cubit.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:intl/intl.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
@@ -24,10 +21,15 @@ class DocumentFilterPanel extends StatefulWidget {
|
|||||||
final PanelController panelController;
|
final PanelController panelController;
|
||||||
final ScrollController scrollController;
|
final ScrollController scrollController;
|
||||||
|
|
||||||
|
final DocumentFilter initialFilter;
|
||||||
|
|
||||||
|
final void Function(DocumentFilter filter) onFilterChanged;
|
||||||
const DocumentFilterPanel({
|
const DocumentFilterPanel({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.panelController,
|
required this.panelController,
|
||||||
required this.scrollController,
|
required this.scrollController,
|
||||||
|
required this.onFilterChanged,
|
||||||
|
required this.initialFilter,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -63,13 +65,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
child: BlocConsumer<DocumentsCubit, DocumentsState>(
|
child: FormBuilder(
|
||||||
listener: (context, state) {
|
|
||||||
// Set initial values, otherwise they would not automatically update.
|
|
||||||
_patchFromFilter(state.filter);
|
|
||||||
},
|
|
||||||
builder: (context, state) {
|
|
||||||
return FormBuilder(
|
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -81,8 +77,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
label: Text(
|
label:
|
||||||
S.of(context).documentsFilterPageResetFilterLabel),
|
Text(S.of(context).documentsFilterPageResetFilterLabel),
|
||||||
onPressed: () => _resetFilter(context),
|
onPressed: () => _resetFilter(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -100,8 +96,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: _onApplyFilter,
|
onPressed: _onApplyFilter,
|
||||||
child: Text(
|
child:
|
||||||
S.of(context).documentsFilterPageApplyFilterLabel),
|
Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padded(),
|
).padded(),
|
||||||
@@ -119,23 +115,22 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
children: [
|
children: [
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(S.of(context).documentsFilterPageSearchLabel),
|
||||||
S.of(context).documentsFilterPageSearchLabel),
|
|
||||||
).padded(const EdgeInsets.only(left: 8.0)),
|
).padded(const EdgeInsets.only(left: 8.0)),
|
||||||
_buildQueryFormField(state),
|
_buildQueryFormField(),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child:
|
||||||
S.of(context).documentsFilterPageAdvancedLabel),
|
Text(S.of(context).documentsFilterPageAdvancedLabel),
|
||||||
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
||||||
_buildCreatedDateRangePickerFormField(state).padded(),
|
_buildCreatedDateRangePickerFormField().padded(),
|
||||||
_buildAddedDateRangePickerFormField(state).padded(),
|
_buildAddedDateRangePickerFormField().padded(),
|
||||||
_buildCorrespondentFormField(state).padded(),
|
_buildCorrespondentFormField().padded(),
|
||||||
_buildDocumentTypeFormField(state).padded(),
|
_buildDocumentTypeFormField().padded(),
|
||||||
_buildStoragePathFormField(state).padded(),
|
_buildStoragePathFormField().padded(),
|
||||||
TagFormField(
|
TagFormField(
|
||||||
name: DocumentModel.tagsKey,
|
name: DocumentModel.tagsKey,
|
||||||
initialValue: state.filter.tags,
|
initialValue: widget.initialFilter.tags,
|
||||||
allowCreation: false,
|
allowCreation: false,
|
||||||
).padded(),
|
).padded(),
|
||||||
// Required in order for the storage path field to be visible when typing
|
// Required in order for the storage path field to be visible when typing
|
||||||
@@ -148,8 +143,6 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -163,15 +156,16 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDocumentTypeFormField(DocumentsState docState) {
|
//TODO: Check if the blocs can be found in the context, otherwise just provide repository and create new bloc inside LabelFormField!
|
||||||
return BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
Widget _buildDocumentTypeFormField() {
|
||||||
|
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
name: fkDocumentType,
|
name: fkDocumentType,
|
||||||
state: state.labels,
|
state: state.labels,
|
||||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||||
initialValue: docState.filter.documentType,
|
initialValue: widget.initialFilter.documentType,
|
||||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.description_outlined),
|
prefixIcon: const Icon(Icons.description_outlined),
|
||||||
@@ -180,15 +174,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCorrespondentFormField(DocumentsState docState) {
|
Widget _buildCorrespondentFormField() {
|
||||||
return BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
name: fkCorrespondent,
|
name: fkCorrespondent,
|
||||||
state: state.labels,
|
state: state.labels,
|
||||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||||
initialValue: docState.filter.correspondent,
|
initialValue: widget.initialFilter.correspondent,
|
||||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.person_outline),
|
prefixIcon: const Icon(Icons.person_outline),
|
||||||
@@ -197,15 +191,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStoragePathFormField(DocumentsState docState) {
|
Widget _buildStoragePathFormField() {
|
||||||
return BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
name: fkStoragePath,
|
name: fkStoragePath,
|
||||||
state: state.labels,
|
state: state.labels,
|
||||||
label: S.of(context).documentStoragePathPropertyLabel,
|
label: S.of(context).documentStoragePathPropertyLabel,
|
||||||
initialValue: docState.filter.storagePath,
|
initialValue: widget.initialFilter.storagePath,
|
||||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||||
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
||||||
prefixIcon: const Icon(Icons.folder_outlined),
|
prefixIcon: const Icon(Icons.folder_outlined),
|
||||||
@@ -214,7 +208,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQueryFormField(DocumentsState state) {
|
Widget _buildQueryFormField() {
|
||||||
final queryType =
|
final queryType =
|
||||||
_formKey.currentState?.getRawValue(QueryTypeFormField.fkQueryType) ??
|
_formKey.currentState?.getRawValue(QueryTypeFormField.fkQueryType) ??
|
||||||
QueryType.titleAndContent;
|
QueryType.titleAndContent;
|
||||||
@@ -239,16 +233,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
prefixIcon: const Icon(Icons.search_outlined),
|
prefixIcon: const Icon(Icons.search_outlined),
|
||||||
labelText: label,
|
labelText: label,
|
||||||
suffixIcon: QueryTypeFormField(
|
suffixIcon: QueryTypeFormField(
|
||||||
initialValue: state.filter.queryType,
|
initialValue: widget.initialFilter.queryType,
|
||||||
afterSelected: (queryType) => setState(() {}),
|
afterSelected: (queryType) => setState(() {}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
initialValue: state.filter.queryText,
|
initialValue: widget.initialFilter.queryText,
|
||||||
).padded();
|
).padded();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDateRangePickerHelper(
|
Widget _buildDateRangePickerHelper(String formFieldKey) {
|
||||||
DocumentsState state, String formFieldKey) {
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -328,13 +321,13 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCreatedDateRangePickerFormField(DocumentsState state) {
|
Widget _buildCreatedDateRangePickerFormField() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
FormBuilderDateRangePicker(
|
FormBuilderDateRangePicker(
|
||||||
initialValue: _dateTimeRangeOfNullable(
|
initialValue: _dateTimeRangeOfNullable(
|
||||||
state.filter.createdDateAfter,
|
widget.initialFilter.createdDateAfter,
|
||||||
state.filter.createdDateBefore,
|
widget.initialFilter.createdDateBefore,
|
||||||
),
|
),
|
||||||
// Workaround for theme data not being correctly passed to daterangepicker, see
|
// Workaround for theme data not being correctly passed to daterangepicker, see
|
||||||
// https://github.com/flutter/flutter/issues/87580
|
// https://github.com/flutter/flutter/issues/87580
|
||||||
@@ -371,18 +364,18 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4.0),
|
const SizedBox(height: 4.0),
|
||||||
_buildDateRangePickerHelper(state, fkCreatedAt),
|
_buildDateRangePickerHelper(fkCreatedAt),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAddedDateRangePickerFormField(DocumentsState state) {
|
Widget _buildAddedDateRangePickerFormField() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
FormBuilderDateRangePicker(
|
FormBuilderDateRangePicker(
|
||||||
initialValue: _dateTimeRangeOfNullable(
|
initialValue: _dateTimeRangeOfNullable(
|
||||||
state.filter.addedDateAfter,
|
widget.initialFilter.addedDateAfter,
|
||||||
state.filter.addedDateBefore,
|
widget.initialFilter.addedDateBefore,
|
||||||
),
|
),
|
||||||
// Workaround for theme data not being correctly passed to daterangepicker, see
|
// Workaround for theme data not being correctly passed to daterangepicker, see
|
||||||
// https://github.com/flutter/flutter/issues/87580
|
// https://github.com/flutter/flutter/issues/87580
|
||||||
@@ -419,7 +412,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4.0),
|
const SizedBox(height: 4.0),
|
||||||
_buildDateRangePickerHelper(state, fkAddedAt),
|
_buildDateRangePickerHelper(fkAddedAt),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
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/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||||
const SortFieldSelectionBottomSheet({super.key});
|
final SortOrder initialSortOrder;
|
||||||
|
final SortField initialSortField;
|
||||||
|
|
||||||
|
final Future Function(SortField field, SortOrder order) onSubmit;
|
||||||
|
|
||||||
|
const SortFieldSelectionBottomSheet({
|
||||||
|
super.key,
|
||||||
|
required this.initialSortOrder,
|
||||||
|
required this.initialSortField,
|
||||||
|
required this.onSubmit,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SortFieldSelectionBottomSheet> createState() =>
|
State<SortFieldSelectionBottomSheet> createState() =>
|
||||||
@@ -17,81 +23,60 @@ class SortFieldSelectionBottomSheet extends StatefulWidget {
|
|||||||
|
|
||||||
class _SortFieldSelectionBottomSheetState
|
class _SortFieldSelectionBottomSheetState
|
||||||
extends State<SortFieldSelectionBottomSheet> {
|
extends State<SortFieldSelectionBottomSheet> {
|
||||||
SortField? _selectedFieldLoading;
|
late SortField _currentSortField;
|
||||||
SortOrder? _selectedOrderLoading;
|
late SortOrder _currentSortOrder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_currentSortField = widget.initialSortField;
|
||||||
|
_currentSortOrder = widget.initialSortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
child: Column(
|
||||||
bloc: getIt<DocumentsCubit>(),
|
|
||||||
builder: (context, state) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
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(
|
).padded(
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16)),
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||||
Column(
|
),
|
||||||
children: SortField.values
|
TextButton(
|
||||||
.map(
|
child: Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
||||||
(e) => _buildSortOption(
|
onPressed: () => widget.onSubmit(
|
||||||
e,
|
_currentSortField,
|
||||||
state.filter.sortOrder,
|
_currentSortOrder,
|
||||||
state.filter.sortField == e,
|
|
||||||
_selectedFieldLoading == e,
|
|
||||||
),
|
),
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
Column(
|
||||||
|
children: SortField.values.map(_buildSortOption).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSortOption(
|
Widget _buildSortOption(
|
||||||
SortField field,
|
SortField field,
|
||||||
SortOrder order,
|
|
||||||
bool isCurrentlySelected,
|
|
||||||
bool isNextSelected,
|
|
||||||
) {
|
) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
title: Text(
|
title: Text(
|
||||||
_localizedSortField(field),
|
_localizedSortField(field),
|
||||||
),
|
),
|
||||||
trailing: isNextSelected
|
trailing: _currentSortField == field
|
||||||
? (_buildOrderIcon(_selectedOrderLoading!))
|
? _buildOrderIcon(_currentSortOrder)
|
||||||
: (_selectedOrderLoading == null && isCurrentlySelected
|
: null,
|
||||||
? _buildOrderIcon(order)
|
|
||||||
: null),
|
|
||||||
onTap: () async {
|
|
||||||
setState(() {
|
|
||||||
_selectedFieldLoading = field;
|
|
||||||
_selectedOrderLoading =
|
|
||||||
isCurrentlySelected ? order.toggle() : SortOrder.descending;
|
|
||||||
});
|
|
||||||
BlocProvider.of<DocumentsCubit>(context)
|
|
||||||
.updateCurrentFilter((filter) => filter.copyWith(
|
|
||||||
sortOrder: isCurrentlySelected
|
|
||||||
? order.toggle()
|
|
||||||
: SortOrder.descending,
|
|
||||||
sortField: field,
|
|
||||||
))
|
|
||||||
.whenComplete(() {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_selectedFieldLoading = null;
|
|
||||||
_selectedOrderLoading = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class BulkDeleteConfirmationDialog extends StatelessWidget {
|
class BulkDeleteConfirmationDialog extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
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/bloc/documents_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/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';
|
||||||
@@ -79,7 +80,6 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
//TODO: replace with sorting stuff...
|
|
||||||
SavedViewSelectionWidget(height: 48, enabled: enabled),
|
SavedViewSelectionWidget(height: 48, enabled: enabled),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
||||||
|
|
||||||
@@ -33,11 +32,19 @@ class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context) => BlocProvider.value(
|
builder: (context) => FractionallySizedBox(
|
||||||
value: getIt<DocumentsCubit>(),
|
|
||||||
child: const FractionallySizedBox(
|
|
||||||
heightFactor: .6,
|
heightFactor: .6,
|
||||||
child: SortFieldSelectionBottomSheet(),
|
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return SortFieldSelectionBottomSheet(
|
||||||
|
initialSortField: state.filter.sortField,
|
||||||
|
initialSortOrder: state.filter.sortOrder,
|
||||||
|
onSubmit: (field, order) =>
|
||||||
|
BlocProvider.of<DocumentsCubit>(context).updateCurrentFilter(
|
||||||
|
(filter) => filter.copyWith(sortField: field, sortOrder: order),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
31
lib/features/edit_label/cubit/edit_label_cubit.dart
Normal file
31
lib/features/edit_label/cubit/edit_label_cubit.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_state.dart';
|
||||||
|
|
||||||
|
class EditLabelCubit<T extends Label> extends Cubit<EditLabelState<T>> {
|
||||||
|
final LabelRepository<T> _repository;
|
||||||
|
|
||||||
|
StreamSubscription<Map<int, T>>? _subscription;
|
||||||
|
|
||||||
|
EditLabelCubit(LabelRepository<T> repository)
|
||||||
|
: _repository = repository,
|
||||||
|
super(const EditLabelInitial()) {
|
||||||
|
_subscription = _repository.labels
|
||||||
|
.listen((labels) => emit(EditLabelState(labels: labels)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> create(T label) => _repository.create(label);
|
||||||
|
|
||||||
|
Future<void> update(T label) => _repository.update(label);
|
||||||
|
|
||||||
|
Future<void> delete(T label) => _repository.delete(label);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
lib/features/edit_label/cubit/edit_label_state.dart
Normal file
16
lib/features/edit_label/cubit/edit_label_state.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class EditLabelState<T> extends Equatable {
|
||||||
|
final Map<int, T> labels;
|
||||||
|
|
||||||
|
const EditLabelState({required this.labels});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [labels];
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditLabelInitial<T> extends EditLabelState<T> {
|
||||||
|
const EditLabelInitial() : super(labels: const {});
|
||||||
|
}
|
||||||
71
lib/features/edit_label/view/add_label_page.dart
Normal file
71
lib/features/edit_label/view/add_label_page.dart
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/label_form.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class AddLabelPage<T extends Label> extends StatelessWidget {
|
||||||
|
final String? initialName;
|
||||||
|
final Widget pageTitle;
|
||||||
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
|
final List<Widget> additionalFields;
|
||||||
|
|
||||||
|
const AddLabelPage({
|
||||||
|
super.key,
|
||||||
|
this.initialName,
|
||||||
|
required this.pageTitle,
|
||||||
|
required this.fromJsonT,
|
||||||
|
this.additionalFields = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit(
|
||||||
|
RepositoryProvider.of<LabelRepository<T>>(context),
|
||||||
|
),
|
||||||
|
child: AddLabelFormWidget(
|
||||||
|
pageTitle: pageTitle,
|
||||||
|
label: fromJsonT({'name': initialName}),
|
||||||
|
additionalFields: additionalFields,
|
||||||
|
fromJsonT: fromJsonT,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddLabelFormWidget<T extends Label> extends StatelessWidget {
|
||||||
|
final T? label;
|
||||||
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
|
final List<Widget> additionalFields;
|
||||||
|
|
||||||
|
final Widget pageTitle;
|
||||||
|
const AddLabelFormWidget({
|
||||||
|
super.key,
|
||||||
|
this.label,
|
||||||
|
required this.fromJsonT,
|
||||||
|
required this.additionalFields,
|
||||||
|
required this.pageTitle,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: pageTitle,
|
||||||
|
),
|
||||||
|
body: LabelForm<T>(
|
||||||
|
initialValue: label,
|
||||||
|
fromJsonT: fromJsonT,
|
||||||
|
submitButtonConfig: SubmitButtonConfig<T>(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text(S.of(context).genericActionCreateLabel),
|
||||||
|
onSubmit: BlocProvider.of<EditLabelCubit<T>>(context).create,
|
||||||
|
),
|
||||||
|
additionalFields: additionalFields,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
105
lib/features/edit_label/view/edit_label_page.dart
Normal file
105
lib/features/edit_label/view/edit_label_page.dart
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import 'package:flutter/material.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/label_form.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class EditLabelPage<T extends Label> extends StatelessWidget {
|
||||||
|
final T label;
|
||||||
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
|
final List<Widget> additionalFields;
|
||||||
|
|
||||||
|
const EditLabelPage({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.fromJsonT,
|
||||||
|
this.additionalFields = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit(
|
||||||
|
RepositoryProvider.of<LabelRepository<T>>(context),
|
||||||
|
),
|
||||||
|
child: EditLabelForm(
|
||||||
|
label: label,
|
||||||
|
additionalFields: additionalFields,
|
||||||
|
fromJsonT: fromJsonT,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditLabelForm<T extends Label> extends StatelessWidget {
|
||||||
|
final T label;
|
||||||
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
|
final List<Widget> additionalFields;
|
||||||
|
|
||||||
|
const EditLabelForm({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.fromJsonT,
|
||||||
|
required this.additionalFields,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(S.of(context).genericActionEditLabel),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => _onDelete(context),
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: LabelForm<T>(
|
||||||
|
initialValue: label,
|
||||||
|
fromJsonT: fromJsonT,
|
||||||
|
submitButtonConfig: SubmitButtonConfig<T>(
|
||||||
|
icon: const Icon(Icons.update),
|
||||||
|
label: Text(S.of(context).genericActionUpdateLabel),
|
||||||
|
onSubmit: BlocProvider.of<EditLabelCubit<T>>(context).update,
|
||||||
|
),
|
||||||
|
additionalFields: additionalFields,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDelete(BuildContext context) {
|
||||||
|
if ((label.documentCount ?? 0) > 0) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(S.of(context).editLabelPageConfirmDeletionDialogTitle),
|
||||||
|
content: Text(
|
||||||
|
S.of(context).editLabelPageDeletionDialogText,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(S.of(context).genericActionCancelLabel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
S.of(context).genericActionDeleteLabel,
|
||||||
|
style: TextStyle(color: Theme.of(context).errorColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
BlocProvider.of<EditLabelCubit<T>>(context).delete(label);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/widgets.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/add_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class AddCorrespondentPage extends StatelessWidget {
|
||||||
|
final String? initialName;
|
||||||
|
const AddCorrespondentPage({Key? key, this.initialName}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit<Correspondent>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
child: AddLabelPage<Correspondent>(
|
||||||
|
pageTitle: Text(S.of(context).addCorrespondentPageTitle),
|
||||||
|
fromJsonT: Correspondent.fromJson,
|
||||||
|
initialName: initialName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:flutter/material.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/add_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class AddDocumentTypePage extends StatelessWidget {
|
||||||
|
final String? initialName;
|
||||||
|
const AddDocumentTypePage({
|
||||||
|
super.key,
|
||||||
|
this.initialName,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
child: AddLabelPage<DocumentType>(
|
||||||
|
pageTitle: Text(S.of(context).addDocumentTypePageTitle),
|
||||||
|
fromJsonT: DocumentType.fromJson,
|
||||||
|
initialName: initialName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
lib/features/edit_label/view/impl/add_storage_path_page.dart
Normal file
31
lib/features/edit_label/view/impl/add_storage_path_page.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/material.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/add_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class AddStoragePathPage extends StatelessWidget {
|
||||||
|
final String? initalValue;
|
||||||
|
const AddStoragePathPage({Key? key, this.initalValue}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
child: AddLabelPage<StoragePath>(
|
||||||
|
pageTitle: Text(S.of(context).addStoragePathPageTitle),
|
||||||
|
fromJsonT: StoragePath.fromJson,
|
||||||
|
initialName: initalValue,
|
||||||
|
additionalFields: const [
|
||||||
|
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
||||||
|
SizedBox(height: 120.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/features/edit_label/view/impl/add_tag_page.dart
Normal file
43
lib/features/edit_label/view/impl/add_tag_page.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.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/add_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
|
class AddTagPage extends StatelessWidget {
|
||||||
|
final String? initialValue;
|
||||||
|
const AddTagPage({Key? key, this.initialValue}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit<Tag>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
child: AddLabelPage<Tag>(
|
||||||
|
pageTitle: Text(S.of(context).addTagPageTitle),
|
||||||
|
fromJsonT: Tag.fromJson,
|
||||||
|
initialName: initialValue,
|
||||||
|
additionalFields: [
|
||||||
|
FormBuilderColorPickerField(
|
||||||
|
name: Tag.colorKey,
|
||||||
|
valueTransformer: (color) => "#${color?.value.toRadixString(16)}",
|
||||||
|
decoration: InputDecoration(
|
||||||
|
label: Text(S.of(context).tagColorPropertyLabel),
|
||||||
|
),
|
||||||
|
colorPickerType: ColorPickerType.materialPicker,
|
||||||
|
initialValue: null,
|
||||||
|
),
|
||||||
|
FormBuilderCheckbox(
|
||||||
|
name: Tag.isInboxTagKey,
|
||||||
|
title: Text(S.of(context).tagInboxTagPropertyLabel),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/material.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/edit_label/cubit/edit_label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class EditCorrespondentPage extends StatelessWidget {
|
||||||
|
final Correspondent correspondent;
|
||||||
|
const EditCorrespondentPage({super.key, required this.correspondent});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => EditLabelCubit<Correspondent>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
child: EditLabelPage<Correspondent>(
|
||||||
|
label: correspondent,
|
||||||
|
fromJsonT: Correspondent.fromJson,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/widgets.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/edit_label/view/edit_label_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class EditDocumentTypePage extends StatelessWidget {
|
||||||
|
final DocumentType documentType;
|
||||||
|
const EditDocumentTypePage({super.key, required this.documentType});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
child: EditLabelPage<DocumentType>(
|
||||||
|
label: documentType,
|
||||||
|
fromJsonT: DocumentType.fromJson,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:flutter/material.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/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';
|
||||||
|
|
||||||
|
class EditStoragePathPage extends StatelessWidget {
|
||||||
|
final StoragePath storagePath;
|
||||||
|
const EditStoragePathPage({super.key, required this.storagePath});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
child: EditLabelPage<StoragePath>(
|
||||||
|
label: storagePath,
|
||||||
|
fromJsonT: StoragePath.fromJson,
|
||||||
|
additionalFields: [
|
||||||
|
StoragePathAutofillFormBuilderField(
|
||||||
|
name: StoragePath.pathKey,
|
||||||
|
initialValue: storagePath.path,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 120.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/features/edit_label/view/impl/edit_tag_page.dart
Normal file
43
lib/features/edit_label/view/impl/edit_tag_page.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.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';
|
||||||
|
|
||||||
|
class EditTagPage extends StatelessWidget {
|
||||||
|
final Tag tag;
|
||||||
|
|
||||||
|
const EditTagPage({super.key, required this.tag});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<Tag>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
child: EditLabelPage<Tag>(
|
||||||
|
label: tag,
|
||||||
|
fromJsonT: Tag.fromJson,
|
||||||
|
additionalFields: [
|
||||||
|
FormBuilderColorPickerField(
|
||||||
|
initialValue: tag.color,
|
||||||
|
name: Tag.colorKey,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
label: Text(S.of(context).tagColorPropertyLabel),
|
||||||
|
),
|
||||||
|
colorPickerType: ColorPickerType.blockPicker,
|
||||||
|
),
|
||||||
|
FormBuilderCheckbox(
|
||||||
|
initialValue: tag.isInboxTag,
|
||||||
|
name: Tag.isInboxTagKey,
|
||||||
|
title: Text(S.of(context).tagInboxTagPropertyLabel),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
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:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
@@ -9,27 +7,42 @@ import 'package:paperless_mobile/extensions/flutter_extensions.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 EditLabelPage<T extends Label> extends StatefulWidget {
|
class SubmitButtonConfig<T extends Label> {
|
||||||
final T label;
|
final Widget icon;
|
||||||
|
final Widget label;
|
||||||
final Future<void> Function(T) onSubmit;
|
final Future<void> Function(T) onSubmit;
|
||||||
final Future<void> Function(T) onDelete;
|
|
||||||
final T Function(JSON) fromJson;
|
SubmitButtonConfig({
|
||||||
|
required this.icon,
|
||||||
|
required this.label,
|
||||||
|
required this.onSubmit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LabelForm<T extends Label> extends StatefulWidget {
|
||||||
|
final T? initialValue;
|
||||||
|
|
||||||
|
final SubmitButtonConfig submitButtonConfig;
|
||||||
|
|
||||||
|
/// FromJson method to parse the form field values into a label instance.
|
||||||
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
|
|
||||||
|
/// List of additionally rendered form fields.
|
||||||
final List<Widget> additionalFields;
|
final List<Widget> additionalFields;
|
||||||
|
|
||||||
const EditLabelPage({
|
const LabelForm({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.label,
|
required this.initialValue,
|
||||||
required this.fromJson,
|
required this.fromJsonT,
|
||||||
required this.onSubmit,
|
|
||||||
required this.onDelete,
|
|
||||||
this.additionalFields = const [],
|
this.additionalFields = const [],
|
||||||
|
required this.submitButtonConfig,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EditLabelPage> createState() => _EditLabelPageState<T>();
|
State<LabelForm> createState() => _LabelFormState<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
class _LabelFormState<T extends Label> extends State<LabelForm<T>> {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
|
||||||
PaperlessValidationErrors _errors = {};
|
PaperlessValidationErrors _errors = {};
|
||||||
@@ -38,18 +51,9 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(S.of(context).genericActionEditLabel),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: _onDelete,
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
icon: const Icon(Icons.update),
|
icon: widget.submitButtonConfig.icon,
|
||||||
label: Text(S.of(context).genericActionUpdateLabel),
|
label: widget.submitButtonConfig.label,
|
||||||
onPressed: _onSubmit,
|
onPressed: _onSubmit,
|
||||||
),
|
),
|
||||||
body: FormBuilder(
|
body: FormBuilder(
|
||||||
@@ -63,7 +67,7 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
|||||||
errorText: _errors[Label.nameKey],
|
errorText: _errors[Label.nameKey],
|
||||||
),
|
),
|
||||||
validator: FormBuilderValidators.required(),
|
validator: FormBuilderValidators.required(),
|
||||||
initialValue: widget.label.name,
|
initialValue: widget.initialValue?.name,
|
||||||
onChanged: (val) => setState(() => _errors = {}),
|
onChanged: (val) => setState(() => _errors = {}),
|
||||||
),
|
),
|
||||||
FormBuilderTextField(
|
FormBuilderTextField(
|
||||||
@@ -72,12 +76,13 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
|||||||
labelText: S.of(context).labelMatchPropertyLabel,
|
labelText: S.of(context).labelMatchPropertyLabel,
|
||||||
errorText: _errors[Label.matchKey],
|
errorText: _errors[Label.matchKey],
|
||||||
),
|
),
|
||||||
initialValue: widget.label.match,
|
initialValue: widget.initialValue?.match,
|
||||||
onChanged: (val) => setState(() => _errors = {}),
|
onChanged: (val) => setState(() => _errors = {}),
|
||||||
),
|
),
|
||||||
FormBuilderDropdown<int?>(
|
FormBuilderDropdown<int?>(
|
||||||
|
//TODO: Extract to own widget.
|
||||||
name: Label.matchingAlgorithmKey,
|
name: Label.matchingAlgorithmKey,
|
||||||
initialValue: widget.label.matchingAlgorithm?.value ??
|
initialValue: widget.initialValue?.matchingAlgorithm?.value ??
|
||||||
MatchingAlgorithm.allWords.value,
|
MatchingAlgorithm.allWords.value,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: S.of(context).labelMatchingAlgorithmPropertyLabel,
|
labelText: S.of(context).labelMatchingAlgorithmPropertyLabel,
|
||||||
@@ -95,7 +100,7 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
|||||||
),
|
),
|
||||||
FormBuilderCheckbox(
|
FormBuilderCheckbox(
|
||||||
name: Label.isInsensitiveKey,
|
name: Label.isInsensitiveKey,
|
||||||
initialValue: widget.label.isInsensitive,
|
initialValue: widget.initialValue?.isInsensitive,
|
||||||
title: Text(S.of(context).labelIsInsensivitePropertyLabel),
|
title: Text(S.of(context).labelIsInsensivitePropertyLabel),
|
||||||
),
|
),
|
||||||
...widget.additionalFields,
|
...widget.additionalFields,
|
||||||
@@ -105,46 +110,14 @@ class _EditLabelPageState<T extends Label> extends State<EditLabelPage<T>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDelete() {
|
|
||||||
if ((widget.label.documentCount ?? 0) > 0) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: Text(S.of(context).editLabelPageConfirmDeletionDialogTitle),
|
|
||||||
content: Text(
|
|
||||||
S.of(context).editLabelPageDeletionDialogText,
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(S.of(context).genericActionCancelLabel),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
widget.onDelete(widget.label);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
S.of(context).genericActionDeleteLabel,
|
|
||||||
style: TextStyle(color: Theme.of(context).errorColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
widget.onDelete(widget.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSubmit() async {
|
void _onSubmit() async {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
try {
|
try {
|
||||||
final mergedJson = {
|
final mergedJson = {
|
||||||
...widget.label.toJson(),
|
...widget.initialValue?.toJson() ?? {},
|
||||||
..._formKey.currentState!.value
|
..._formKey.currentState!.value
|
||||||
};
|
};
|
||||||
await widget.onSubmit(widget.fromJson(mergedJson));
|
await widget.submitButtonConfig.onSubmit(widget.fromJsonT(mergedJson));
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
} on PaperlessValidationErrors catch (errorMessages) {
|
} on PaperlessValidationErrors catch (errorMessages) {
|
||||||
setState(() => _errors = errorMessages);
|
setState(() => _errors = errorMessages);
|
||||||
@@ -3,18 +3,16 @@ 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/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
import 'package:paperless_mobile/core/widgets/offline_banner.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
|
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/bottom_navigation_bar.dart';
|
import 'package:paperless_mobile/features/home/view/widget/bottom_navigation_bar.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart';
|
import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/scanner_page.dart';
|
import 'package:paperless_mobile/features/scan/view/scanner_page.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
@@ -59,17 +57,17 @@ class _HomePageState extends State<HomePage> {
|
|||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider.value(
|
BlocProvider.value(
|
||||||
value: getIt<DocumentsCubit>(),
|
value: DocumentsCubit(getIt<PaperlessDocumentsApi>()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const DocumentsPage(),
|
child: const DocumentsPage(),
|
||||||
),
|
),
|
||||||
BlocProvider.value(
|
BlocProvider.value(
|
||||||
value: getIt<DocumentScannerCubit>(),
|
value: DocumentScannerCubit(),
|
||||||
child: const ScannerPage(),
|
child: const ScannerPage(),
|
||||||
),
|
),
|
||||||
BlocProvider.value(
|
BlocProvider.value(
|
||||||
value: getIt<DocumentsCubit>(),
|
value: DocumentsCubit(getIt<PaperlessDocumentsApi>()),
|
||||||
child: const LabelsPage(),
|
child: const LabelsPage(),
|
||||||
),
|
),
|
||||||
][_currentIndex],
|
][_currentIndex],
|
||||||
@@ -78,20 +76,17 @@ class _HomePageState extends State<HomePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializeData(BuildContext context) {
|
void _initializeData(BuildContext context) {
|
||||||
try {
|
try {
|
||||||
return Future.wait([
|
RepositoryProvider.of<LabelRepository<Tag>>(context).findAll();
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context).findAll();
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context).findAll();
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context).findAll();
|
||||||
|
RepositoryProvider.of<SavedViewRepository>(context).findAll();
|
||||||
BlocProvider.of<PaperlessServerInformationCubit>(context)
|
BlocProvider.of<PaperlessServerInformationCubit>(context)
|
||||||
.updateInformtion(),
|
.updateInformtion();
|
||||||
getIt<DocumentTypeCubit>().initialize(),
|
|
||||||
getIt<CorrespondentCubit>().initialize(),
|
|
||||||
getIt<TagCubit>().initialize(),
|
|
||||||
getIt<StoragePathCubit>().initialize(),
|
|
||||||
getIt<SavedViewCubit>().initialize(),
|
|
||||||
]);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
return Future.error(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ 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/bloc/paperless_server_information_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.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/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/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/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.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/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';
|
||||||
@@ -188,11 +187,14 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
try {
|
try {
|
||||||
BlocProvider.of<AuthenticationCubit>(context).logout();
|
BlocProvider.of<AuthenticationCubit>(context).logout();
|
||||||
getIt<DocumentsCubit>().reset();
|
RepositoryProvider.of<LabelRepository<Tag>>(context).clear();
|
||||||
getIt<CorrespondentCubit>().reset();
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context)
|
||||||
getIt<DocumentTypeCubit>().reset();
|
.clear();
|
||||||
getIt<TagCubit>().reset();
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context)
|
||||||
getIt<DocumentScannerCubit>().reset();
|
.clear();
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context)
|
||||||
|
.clear();
|
||||||
|
RepositoryProvider.of<SavedViewRepository>(context).clear();
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
}
|
}
|
||||||
@@ -208,14 +210,15 @@ class InfoDrawer extends StatelessWidget {
|
|||||||
Future<void> _onOpenInbox(BuildContext context) async {
|
Future<void> _onOpenInbox(BuildContext context) async {
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => LabelRepositoriesProvider(
|
||||||
additionalProviders: [
|
child: BlocProvider(
|
||||||
BlocProvider<InboxCubit>.value(
|
create: (context) => InboxCubit(
|
||||||
value: getIt<InboxCubit>()..loadInbox(),
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
getIt<PaperlessDocumentsApi>(),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
child: const InboxPage(),
|
child: const InboxPage(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
maintainState: false,
|
maintainState: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.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/features/inbox/bloc/state/inbox_state.dart';
|
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
||||||
|
|
||||||
@injectable
|
|
||||||
class InboxCubit extends Cubit<InboxState> {
|
class InboxCubit extends Cubit<InboxState> {
|
||||||
final PaperlessLabelsApi _labelApi;
|
final LabelRepository<Tag> _tagsRepository;
|
||||||
final PaperlessDocumentsApi _documentsApi;
|
final PaperlessDocumentsApi _documentsApi;
|
||||||
|
|
||||||
InboxCubit(this._labelApi, this._documentsApi) : super(const InboxState());
|
InboxCubit(this._tagsRepository, this._documentsApi)
|
||||||
|
: super(const InboxState());
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Fetches inbox tag ids and loads the inbox items (documents).
|
/// Fetches inbox tag ids and loads the inbox items (documents).
|
||||||
///
|
///
|
||||||
Future<void> loadInbox() async {
|
Future<void> loadInbox() async {
|
||||||
final inboxTags = await _labelApi.getTags().then(
|
final inboxTags = await _tagsRepository.findAll().then(
|
||||||
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
||||||
);
|
);
|
||||||
if (inboxTags.isEmpty) {
|
if (inboxTags.isEmpty) {
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.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/provider/label_repositories_provider.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/documents/bloc/documents_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';
|
||||||
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/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||||
|
|
||||||
class InboxItem extends StatelessWidget {
|
class InboxItem extends StatelessWidget {
|
||||||
@@ -49,22 +48,20 @@ class InboxItem extends StatelessWidget {
|
|||||||
onTap: () => Navigator.push(
|
onTap: () => Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => BlocProvider.value(
|
||||||
additionalProviders: [
|
value: DocumentDetailsCubit(
|
||||||
BlocProvider<DocumentDetailsCubit>(
|
|
||||||
create: (context) => DocumentDetailsCubit(
|
|
||||||
getIt<PaperlessDocumentsApi>(),
|
getIt<PaperlessDocumentsApi>(),
|
||||||
document,
|
document,
|
||||||
),
|
),
|
||||||
),
|
child: const LabelRepositoriesProvider(
|
||||||
],
|
child: DocumentDetailsPage(
|
||||||
child: const DocumentDetailsPage(
|
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
isLabelClickable: false,
|
isLabelClickable: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
|
||||||
|
|
||||||
class GlobalStateBlocProvider extends StatelessWidget {
|
|
||||||
final List<BlocProvider> additionalProviders;
|
|
||||||
final Widget child;
|
|
||||||
const GlobalStateBlocProvider({
|
|
||||||
super.key,
|
|
||||||
this.additionalProviders = const [],
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MultiBlocProvider(
|
|
||||||
providers: [
|
|
||||||
BlocProvider.value(value: getIt<DocumentTypeCubit>()),
|
|
||||||
BlocProvider.value(value: getIt<CorrespondentCubit>()),
|
|
||||||
BlocProvider.value(value: getIt<TagCubit>()),
|
|
||||||
BlocProvider.value(value: getIt<StoragePathCubit>()),
|
|
||||||
BlocProvider.value(value: getIt<SavedViewCubit>()),
|
|
||||||
...additionalProviders,
|
|
||||||
],
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +1,43 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.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/features/labels/bloc/label_state.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||||
|
|
||||||
abstract class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
||||||
final PaperlessLabelsApi labelsApi;
|
final LabelRepository<T> _repository;
|
||||||
|
|
||||||
LabelCubit(this.labelsApi) : super(LabelState.initial());
|
late StreamSubscription _subscription;
|
||||||
|
|
||||||
@protected
|
LabelCubit(this._repository) : super(LabelState.initial()) {
|
||||||
void loadFrom(Iterable<T> items) {
|
_subscription = _repository.labels.listen(
|
||||||
emit(
|
(update) => emit(LabelState(isLoaded: true, labels: update)),
|
||||||
LabelState(
|
|
||||||
isLoaded: true,
|
|
||||||
labels: Map.fromIterable(items, key: (e) => (e as T).id!),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Adds [item] to the current state. A new state is automatically pushed
|
||||||
|
/// due to the subscription to the repository, which updates the state on
|
||||||
|
/// operation.
|
||||||
|
///
|
||||||
Future<T> add(T item) async {
|
Future<T> add(T item) async {
|
||||||
assert(item.id == null);
|
assert(item.id == null);
|
||||||
final addedItem = await save(item);
|
final addedItem = await _repository.create(item);
|
||||||
final newValues = {...state.labels};
|
|
||||||
newValues.putIfAbsent(addedItem.id!, () => addedItem);
|
|
||||||
emit(
|
|
||||||
LabelState(
|
|
||||||
isLoaded: true,
|
|
||||||
labels: newValues,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return addedItem;
|
return addedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<T> replace(T item) async {
|
Future<T> replace(T item) async {
|
||||||
assert(item.id != null);
|
assert(item.id != null);
|
||||||
final updatedItem = await update(item);
|
final updatedItem = await _repository.update(item);
|
||||||
final updatedValues = {...state.labels};
|
|
||||||
updatedValues[item.id!] = updatedItem;
|
|
||||||
emit(
|
|
||||||
LabelState(
|
|
||||||
isLoaded: state.isLoaded,
|
|
||||||
labels: updatedValues,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return updatedItem;
|
return updatedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> remove(T item) async {
|
Future<void> remove(T item) async {
|
||||||
assert(item.id != null);
|
assert(item.id != null);
|
||||||
if (state.labels.containsKey(item.id)) {
|
if (state.labels.containsKey(item.id)) {
|
||||||
final deletedId = await delete(item);
|
await _repository.delete(item);
|
||||||
final updatedValues = {...state.labels}..remove(deletedId);
|
|
||||||
emit(
|
|
||||||
LabelState(isLoaded: true, labels: updatedValues),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +45,9 @@ abstract class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
|||||||
emit(LabelState(isLoaded: false, labels: {}));
|
emit(LabelState(isLoaded: false, labels: {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialize();
|
@override
|
||||||
|
Future<void> close() {
|
||||||
@protected
|
_subscription.cancel();
|
||||||
Future<T> save(T item);
|
return super.close();
|
||||||
|
}
|
||||||
@protected
|
|
||||||
Future<T> update(T item);
|
|
||||||
|
|
||||||
@protected
|
|
||||||
Future<int> delete(T item);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/widgets.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/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class CorrespondentBlocProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const CorrespondentBlocProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<Correspondent>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/widgets.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/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class DocumentTypeBlocProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const DocumentTypeBlocProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
lib/features/labels/bloc/providers/labels_bloc_provider.dart
Normal file
39
lib/features/labels/bloc/providers/labels_bloc_provider.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter/widgets.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/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class LabelsBlocProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const LabelsBlocProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider<LabelCubit<StoragePath>>(
|
||||||
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider<LabelCubit<Correspondent>>(
|
||||||
|
create: (context) => LabelCubit<Correspondent>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider<LabelCubit<DocumentType>>(
|
||||||
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider<LabelCubit<Tag>>(
|
||||||
|
create: (context) => LabelCubit<Tag>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/widgets.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/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class StoragePathBlocProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const StoragePathBlocProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
lib/features/labels/bloc/providers/tag_bloc_provider.dart
Normal file
20
lib/features/labels/bloc/providers/tag_bloc_provider.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/widgets.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/labels/bloc/label_cubit.dart';
|
||||||
|
|
||||||
|
class TagBlocProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const TagBlocProvider({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<Tag>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
@prod
|
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class CorrespondentCubit extends LabelCubit<Correspondent> {
|
|
||||||
CorrespondentCubit(super.metaDataService);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> initialize() async {
|
|
||||||
return labelsApi.getCorrespondents().then(loadFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Correspondent> save(Correspondent item) =>
|
|
||||||
labelsApi.saveCorrespondent(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Correspondent> update(Correspondent item) =>
|
|
||||||
labelsApi.updateCorrespondent(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(Correspondent item) => labelsApi.deleteCorrespondent(item);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
|
|
||||||
class AddCorrespondentPage extends StatelessWidget {
|
|
||||||
final String? initalValue;
|
|
||||||
const AddCorrespondentPage({Key? key, this.initalValue}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AddLabelPage<Correspondent>(
|
|
||||||
addLabelStr: S.of(context).addCorrespondentPageTitle,
|
|
||||||
fromJson: Correspondent.fromJson,
|
|
||||||
cubit: BlocProvider.of<CorrespondentCubit>(context),
|
|
||||||
initialName: initalValue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
|
|
||||||
class EditCorrespondentPage extends StatelessWidget {
|
|
||||||
final Correspondent correspondent;
|
|
||||||
const EditCorrespondentPage({super.key, required this.correspondent});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EditLabelPage<Correspondent>(
|
|
||||||
label: correspondent,
|
|
||||||
onSubmit: BlocProvider.of<CorrespondentCubit>(context).replace,
|
|
||||||
onDelete: (correspondent) => _onDelete(context, correspondent),
|
|
||||||
fromJson: Correspondent.fromJson,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onDelete(
|
|
||||||
BuildContext context,
|
|
||||||
Correspondent correspondent,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
await BlocProvider.of<CorrespondentCubit>(context).remove(correspondent);
|
|
||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
|
||||||
if (cubit.state.filter.correspondent.id == correspondent.id) {
|
|
||||||
await cubit.updateCurrentFilter(
|
|
||||||
(filter) => filter.copyWith(
|
|
||||||
correspondent: const CorrespondentQuery.unset(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Navigator.pop(context);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +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/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/bloc/providers/correspondent_bloc_provider.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/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
@@ -22,9 +23,11 @@ class CorrespondentWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AbsorbPointer(
|
return CorrespondentBlocProvider(
|
||||||
|
child: AbsorbPointer(
|
||||||
absorbing: !isClickable,
|
absorbing: !isClickable,
|
||||||
child: BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
child:
|
||||||
|
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => _addCorrespondentToFilter(context),
|
onTap: () => _addCorrespondentToFilter(context),
|
||||||
@@ -39,6 +42,7 @@ class CorrespondentWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
@prod
|
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class DocumentTypeCubit extends LabelCubit<DocumentType> {
|
|
||||||
DocumentTypeCubit(super.metaDataService);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> initialize() async {
|
|
||||||
labelsApi.getDocumentTypes().then(loadFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<DocumentType> save(DocumentType item) =>
|
|
||||||
labelsApi.saveDocumentType(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<DocumentType> update(DocumentType item) =>
|
|
||||||
labelsApi.updateDocumentType(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(DocumentType item) => labelsApi.deleteDocumentType(item);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
|
|
||||||
class AddDocumentTypePage extends StatelessWidget {
|
|
||||||
final String? initialName;
|
|
||||||
const AddDocumentTypePage({Key? key, this.initialName}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AddLabelPage<DocumentType>(
|
|
||||||
addLabelStr: S.of(context).addDocumentTypePageTitle,
|
|
||||||
fromJson: DocumentType.fromJson,
|
|
||||||
cubit: BlocProvider.of<DocumentTypeCubit>(context),
|
|
||||||
initialName: initialName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
|
|
||||||
class EditDocumentTypePage extends StatelessWidget {
|
|
||||||
final DocumentType documentType;
|
|
||||||
const EditDocumentTypePage({super.key, required this.documentType});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EditLabelPage<DocumentType>(
|
|
||||||
label: documentType,
|
|
||||||
onSubmit: BlocProvider.of<DocumentTypeCubit>(context).replace,
|
|
||||||
onDelete: (docType) => _onDelete(docType, context),
|
|
||||||
fromJson: DocumentType.fromJson,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onDelete(DocumentType docType, BuildContext context) async {
|
|
||||||
try {
|
|
||||||
await BlocProvider.of<DocumentTypeCubit>(context).remove(docType);
|
|
||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
|
||||||
if (cubit.state.filter.documentType.id == docType.id) {
|
|
||||||
cubit.updateFilter(
|
|
||||||
filter: cubit.state.filter
|
|
||||||
.copyWith(documentType: const DocumentTypeQuery.unset()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
} finally {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
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/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/labels/document_type/bloc/document_type_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/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
@@ -19,11 +20,16 @@ class DocumentTypeWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AbsorbPointer(
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<DocumentType>(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
child: AbsorbPointer(
|
||||||
absorbing: !isClickable,
|
absorbing: !isClickable,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _addDocumentTypeToFilter(context),
|
onTap: () => _addDocumentTypeToFilter(context),
|
||||||
child: BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
child:
|
||||||
|
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Text(
|
return Text(
|
||||||
state.labels[documentTypeId]?.toString() ?? "-",
|
state.labels[documentTypeId]?.toString() ?? "-",
|
||||||
@@ -35,6 +41,7 @@ class DocumentTypeWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
|
|
||||||
@prod
|
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class StoragePathCubit extends LabelCubit<StoragePath> {
|
|
||||||
StoragePathCubit(super.metaDataService);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> initialize() async {
|
|
||||||
return labelsApi.getStoragePaths().then(loadFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<StoragePath> save(StoragePath item) => labelsApi.saveStoragePath(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<StoragePath> update(StoragePath item) =>
|
|
||||||
labelsApi.updateStoragePath(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(StoragePath item) => labelsApi.deleteStoragePath(item);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
|
|
||||||
class AddStoragePathPage extends StatelessWidget {
|
|
||||||
final String? initalValue;
|
|
||||||
const AddStoragePathPage({Key? key, this.initalValue}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AddLabelPage<StoragePath>(
|
|
||||||
addLabelStr: S.of(context).addStoragePathPageTitle,
|
|
||||||
fromJson: StoragePath.fromJson,
|
|
||||||
cubit: BlocProvider.of<StoragePathCubit>(context),
|
|
||||||
initialName: initalValue,
|
|
||||||
additionalFields: const [
|
|
||||||
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
|
||||||
SizedBox(height: 120.0),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_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/view/pages/edit_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
|
|
||||||
class EditStoragePathPage extends StatelessWidget {
|
|
||||||
final StoragePath storagePath;
|
|
||||||
const EditStoragePathPage({super.key, required this.storagePath});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EditLabelPage<StoragePath>(
|
|
||||||
label: storagePath,
|
|
||||||
onSubmit: BlocProvider.of<StoragePathCubit>(context).replace,
|
|
||||||
onDelete: (correspondent) => _onDelete(correspondent, context),
|
|
||||||
fromJson: StoragePath.fromJson,
|
|
||||||
additionalFields: [
|
|
||||||
StoragePathAutofillFormBuilderField(
|
|
||||||
name: StoragePath.pathKey,
|
|
||||||
initialValue: storagePath.path,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 120.0),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onDelete(StoragePath path, BuildContext context) async {
|
|
||||||
try {
|
|
||||||
await BlocProvider.of<StoragePathCubit>(context).remove(path);
|
|
||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
|
||||||
if (cubit.state.filter.storagePath.id == path.id) {
|
|
||||||
cubit.updateCurrentFilter(
|
|
||||||
(filter) => filter.copyWith(
|
|
||||||
storagePath: const StoragePathQuery.unset(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Navigator.pop(context);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
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/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/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/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
|
|
||||||
class StoragePathWidget extends StatelessWidget {
|
class StoragePathWidget extends StatelessWidget {
|
||||||
@@ -22,9 +23,13 @@ class StoragePathWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AbsorbPointer(
|
return BlocProvider(
|
||||||
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
child: AbsorbPointer(
|
||||||
absorbing: !isClickable,
|
absorbing: !isClickable,
|
||||||
child: BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
child: BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => _addStoragePathToFilter(context),
|
onTap: () => _addStoragePathToFilter(context),
|
||||||
@@ -39,6 +44,7 @@ class StoragePathWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
@prod
|
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class TagCubit extends LabelCubit<Tag> {
|
|
||||||
TagCubit(super.metaDataService);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> initialize() async {
|
|
||||||
return labelsApi.getTags().then(loadFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Tag> save(Tag item) => labelsApi.saveTag(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Tag> update(Tag item) => labelsApi.updateTag(item);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(Tag item) => labelsApi.deleteTag(item);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/add_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
|
||||||
|
|
||||||
class AddTagPage extends StatelessWidget {
|
|
||||||
final String? initialValue;
|
|
||||||
const AddTagPage({Key? key, this.initialValue}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AddLabelPage<Tag>(
|
|
||||||
addLabelStr: S.of(context).addTagPageTitle,
|
|
||||||
fromJson: Tag.fromJson,
|
|
||||||
cubit: BlocProvider.of<TagCubit>(context),
|
|
||||||
initialName: initialValue,
|
|
||||||
additionalFields: [
|
|
||||||
FormBuilderColorPickerField(
|
|
||||||
name: Tag.colorKey,
|
|
||||||
valueTransformer: (color) => "#${color?.value.toRadixString(16)}",
|
|
||||||
decoration: InputDecoration(
|
|
||||||
label: Text(S.of(context).tagColorPropertyLabel),
|
|
||||||
),
|
|
||||||
colorPickerType: ColorPickerType.materialPicker,
|
|
||||||
initialValue: null,
|
|
||||||
),
|
|
||||||
FormBuilderCheckbox(
|
|
||||||
name: Tag.isInboxTagKey,
|
|
||||||
title: Text(S.of(context).tagInboxTagPropertyLabel),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:form_builder_extra_fields/form_builder_extra_fields.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/edit_label_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
|
|
||||||
class EditTagPage extends StatelessWidget {
|
|
||||||
final Tag tag;
|
|
||||||
|
|
||||||
const EditTagPage({super.key, required this.tag});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EditLabelPage<Tag>(
|
|
||||||
label: tag,
|
|
||||||
onSubmit: (tag) async {
|
|
||||||
await BlocProvider.of<TagCubit>(context).replace(tag);
|
|
||||||
},
|
|
||||||
onDelete: (tag) => _onDelete(tag, context),
|
|
||||||
fromJson: Tag.fromJson,
|
|
||||||
additionalFields: [
|
|
||||||
FormBuilderColorPickerField(
|
|
||||||
initialValue: tag.color,
|
|
||||||
name: Tag.colorKey,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
label: Text(S.of(context).tagColorPropertyLabel),
|
|
||||||
),
|
|
||||||
colorPickerType: ColorPickerType.blockPicker,
|
|
||||||
),
|
|
||||||
FormBuilderCheckbox(
|
|
||||||
initialValue: tag.isInboxTag,
|
|
||||||
name: Tag.isInboxTagKey,
|
|
||||||
title: Text(S.of(context).tagInboxTagPropertyLabel),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onDelete(Tag tag, BuildContext context) async {
|
|
||||||
try {
|
|
||||||
await BlocProvider.of<TagCubit>(context).remove(tag);
|
|
||||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
|
||||||
final currentFilter = cubit.state.filter;
|
|
||||||
late DocumentFilter updatedFilter = currentFilter;
|
|
||||||
if (currentFilter.tags is IdsTagsQuery) {
|
|
||||||
if ((currentFilter.tags as IdsTagsQuery).includedIds.contains(tag.id)) {
|
|
||||||
updatedFilter = currentFilter.copyWith(
|
|
||||||
tags: (currentFilter.tags as IdsTagsQuery).withIdsRemoved(
|
|
||||||
[tag.id!],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cubit.updateFilter(filter: updatedFilter);
|
|
||||||
Navigator.pop(context);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,9 +3,12 @@ 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: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/provider/label_repositories_provider.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/label_state.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/tag_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/pages/add_tag_page.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class TagFormField extends StatefulWidget {
|
class TagFormField extends StatefulWidget {
|
||||||
@@ -41,11 +44,13 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final state = BlocProvider.of<TagCubit>(context).state;
|
|
||||||
_textEditingController = TextEditingController()
|
_textEditingController = TextEditingController()
|
||||||
..addListener(() {
|
..addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showCreationSuffixIcon = state.labels.values
|
_showCreationSuffixIcon = BlocProvider.of<LabelCubit<Tag>>(context)
|
||||||
|
.state
|
||||||
|
.labels
|
||||||
|
.values
|
||||||
.where(
|
.where(
|
||||||
(item) => item.name.toLowerCase().startsWith(
|
(item) => item.name.toLowerCase().startsWith(
|
||||||
_textEditingController.text.toLowerCase(),
|
_textEditingController.text.toLowerCase(),
|
||||||
@@ -61,7 +66,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
return TagBlocProvider(
|
||||||
|
child: BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
||||||
builder: (context, tagState) {
|
builder: (context, tagState) {
|
||||||
return FormBuilderField<TagsQuery>(
|
return FormBuilderField<TagsQuery>(
|
||||||
builder: (field) {
|
builder: (field) {
|
||||||
@@ -89,7 +95,9 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
.toList();
|
.toList();
|
||||||
if (field.value is IdsTagsQuery) {
|
if (field.value is IdsTagsQuery) {
|
||||||
suggestions.removeWhere((element) =>
|
suggestions.removeWhere((element) =>
|
||||||
(field.value as IdsTagsQuery).ids.contains(element));
|
(field.value as IdsTagsQuery)
|
||||||
|
.ids
|
||||||
|
.contains(element));
|
||||||
}
|
}
|
||||||
if (widget.notAssignedSelectable &&
|
if (widget.notAssignedSelectable &&
|
||||||
field.value is! OnlyNotAssignedTagsQuery) {
|
field.value is! OnlyNotAssignedTagsQuery) {
|
||||||
@@ -122,7 +130,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
title: Text(
|
title: Text(
|
||||||
tag.name,
|
tag.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onBackground),
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -172,6 +181,7 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
name: widget.name,
|
name: widget.name,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,8 +209,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
void _onAddTag(BuildContext context, FormFieldState<TagsQuery> field) async {
|
void _onAddTag(BuildContext context, FormFieldState<TagsQuery> field) async {
|
||||||
final Tag? tag = await Navigator.of(context).push<Tag>(
|
final Tag? tag = await Navigator.of(context).push<Tag>(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => BlocProvider.value(
|
builder: (_) => RepositoryProvider.value(
|
||||||
value: BlocProvider.of<TagCubit>(context),
|
value: RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
child: AddTagPage(initialValue: _textEditingController.text),
|
child: AddTagPage(initialValue: _textEditingController.text),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
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/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/bloc/tags_cubit.dart';
|
import 'package:paperless_mobile/features/labels/bloc/providers/tag_bloc_provider.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
||||||
|
|
||||||
class TagsWidget extends StatefulWidget {
|
class TagsWidget extends StatefulWidget {
|
||||||
@@ -30,7 +31,8 @@ class TagsWidget extends StatefulWidget {
|
|||||||
class _TagsWidgetState extends State<TagsWidget> {
|
class _TagsWidgetState extends State<TagsWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<TagCubit, LabelState<Tag>>(
|
return TagBlocProvider(
|
||||||
|
child: BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final children = widget.tagIds
|
final children = widget.tagIds
|
||||||
.where((id) => state.labels.containsKey(id))
|
.where((id) => state.labels.containsKey(id))
|
||||||
@@ -60,6 +62,7 @@ class _TagsWidgetState extends State<TagsWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/type/types.dart';
|
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
|
|
||||||
class AddLabelPage<T extends Label> extends StatefulWidget {
|
|
||||||
final String? initialName;
|
|
||||||
final String addLabelStr;
|
|
||||||
final T Function(Map<String, dynamic> json) fromJson;
|
|
||||||
final LabelCubit<T> cubit;
|
|
||||||
final List<Widget> additionalFields;
|
|
||||||
|
|
||||||
const AddLabelPage({
|
|
||||||
Key? key,
|
|
||||||
this.initialName,
|
|
||||||
required this.addLabelStr,
|
|
||||||
required this.fromJson,
|
|
||||||
required this.cubit,
|
|
||||||
this.additionalFields = const [],
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AddLabelPage> createState() => _AddLabelPageState<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AddLabelPageState<T extends Label> extends State<AddLabelPage<T>> {
|
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
PaperlessValidationErrors _errors = {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
resizeToAvoidBottomInset: true,
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(widget.addLabelStr),
|
|
||||||
),
|
|
||||||
floatingActionButton: Visibility(
|
|
||||||
visible: MediaQuery.of(context).viewInsets.bottom == 0,
|
|
||||||
child: FloatingActionButton.extended(
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
label: Text(S.of(context).genericActionCreateLabel),
|
|
||||||
onPressed: _onSubmit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: FormBuilder(
|
|
||||||
key: _formKey,
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
FormBuilderTextField(
|
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
||||||
name: Label.nameKey,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: S.of(context).labelNamePropertyLabel,
|
|
||||||
errorText: _errors[Label.nameKey],
|
|
||||||
),
|
|
||||||
initialValue: widget.initialName,
|
|
||||||
validator: FormBuilderValidators.required(),
|
|
||||||
onChanged: (val) => setState(() => _errors = {}),
|
|
||||||
),
|
|
||||||
FormBuilderTextField(
|
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
||||||
name: Label.matchKey,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: S.of(context).labelMatchPropertyLabel,
|
|
||||||
),
|
|
||||||
onChanged: (val) => setState(() => _errors = {}),
|
|
||||||
),
|
|
||||||
FormBuilderDropdown<int?>(
|
|
||||||
name: Label.matchingAlgorithmKey,
|
|
||||||
initialValue: MatchingAlgorithm.anyWord.value,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: S.of(context).labelMatchingAlgorithmPropertyLabel,
|
|
||||||
errorText: _errors[Label.matchingAlgorithmKey],
|
|
||||||
),
|
|
||||||
onChanged: (val) => setState(() => _errors = {}),
|
|
||||||
items: MatchingAlgorithm.values
|
|
||||||
.map((algo) => DropdownMenuItem<int?>(
|
|
||||||
child: Text(algo.name), //TODO: INTL
|
|
||||||
value: algo.value))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
FormBuilderCheckbox(
|
|
||||||
name: Label.isInsensitiveKey,
|
|
||||||
initialValue: true,
|
|
||||||
title: Text(S.of(context).labelIsInsensivitePropertyLabel),
|
|
||||||
),
|
|
||||||
...widget.additionalFields,
|
|
||||||
].padded(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSubmit() async {
|
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
|
||||||
try {
|
|
||||||
final label = await widget.cubit
|
|
||||||
.add(widget.fromJson(_formKey.currentState!.value));
|
|
||||||
Navigator.pop(context, label);
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
} on PaperlessValidationErrors catch (json) {
|
|
||||||
setState(() => _errors = json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
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/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_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_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_tag_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_correspondent_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_type_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/edit_correspondent_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/add_document_type_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/edit_document_type_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/pages/add_storage_path_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/pages/edit_storage_path_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/pages/add_tag_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/pages/edit_tag_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
@@ -35,10 +30,6 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
BlocProvider.of<CorrespondentCubit>(context).initialize();
|
|
||||||
BlocProvider.of<DocumentTypeCubit>(context).initialize();
|
|
||||||
BlocProvider.of<TagCubit>(context).initialize();
|
|
||||||
|
|
||||||
_tabController = TabController(length: 4, vsync: this)
|
_tabController = TabController(length: 4, vsync: this)
|
||||||
..addListener(() => setState(() => _currentIndex = _tabController.index));
|
..addListener(() => setState(() => _currentIndex = _tabController.index));
|
||||||
}
|
}
|
||||||
@@ -60,7 +51,12 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _onAddPressed,
|
onPressed: [
|
||||||
|
_openAddCorrespondentPage,
|
||||||
|
_openAddDocumentTypePage,
|
||||||
|
_openAddTagPage,
|
||||||
|
_openAddStoragePathPage,
|
||||||
|
][_currentIndex],
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -104,40 +100,52 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: [
|
children: [
|
||||||
LabelTabView<Correspondent>(
|
BlocProvider(
|
||||||
cubit: BlocProvider.of<CorrespondentCubit>(context),
|
create: (context) => LabelCubit(
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
|
),
|
||||||
|
child: LabelTabView<Correspondent>(
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
correspondent: CorrespondentQuery.fromId(label.id),
|
correspondent: CorrespondentQuery.fromId(label.id),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
),
|
),
|
||||||
onOpenEditPage: _openEditCorrespondentPage,
|
onEdit: _openEditCorrespondentPage,
|
||||||
emptyStateActionButtonLabel:
|
emptyStateActionButtonLabel:
|
||||||
S.of(context).labelsPageCorrespondentEmptyStateAddNewLabel,
|
S.of(context).labelsPageCorrespondentEmptyStateAddNewLabel,
|
||||||
emptyStateDescription: S
|
emptyStateDescription: S
|
||||||
.of(context)
|
.of(context)
|
||||||
.labelsPageCorrespondentEmptyStateDescriptionText,
|
.labelsPageCorrespondentEmptyStateDescriptionText,
|
||||||
onOpenAddNewPage: _onAddPressed,
|
onAddNew: _openAddCorrespondentPage,
|
||||||
),
|
),
|
||||||
LabelTabView<DocumentType>(
|
),
|
||||||
cubit: BlocProvider.of<DocumentTypeCubit>(context),
|
BlocProvider(
|
||||||
|
create: (context) => LabelCubit(
|
||||||
|
RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
),
|
||||||
|
child: LabelTabView<DocumentType>(
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
documentType: DocumentTypeQuery.fromId(label.id),
|
documentType: DocumentTypeQuery.fromId(label.id),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
),
|
),
|
||||||
onOpenEditPage: _openEditDocumentTypePage,
|
onEdit: _openEditDocumentTypePage,
|
||||||
emptyStateActionButtonLabel:
|
emptyStateActionButtonLabel:
|
||||||
S.of(context).labelsPageDocumentTypeEmptyStateAddNewLabel,
|
S.of(context).labelsPageDocumentTypeEmptyStateAddNewLabel,
|
||||||
emptyStateDescription:
|
emptyStateDescription: S
|
||||||
S.of(context).labelsPageDocumentTypeEmptyStateDescriptionText,
|
.of(context)
|
||||||
onOpenAddNewPage: _onAddPressed,
|
.labelsPageDocumentTypeEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddDocumentTypePage,
|
||||||
),
|
),
|
||||||
LabelTabView<Tag>(
|
),
|
||||||
cubit: BlocProvider.of<TagCubit>(context),
|
BlocProvider(
|
||||||
|
create: (context) => LabelCubit<Tag>(
|
||||||
|
RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
),
|
||||||
|
child: LabelTabView<Tag>(
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
tags: IdsTagsQuery.fromIds([label.id!]),
|
tags: IdsTagsQuery.fromIds([label.id!]),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
),
|
),
|
||||||
onOpenEditPage: _openEditTagPage,
|
onEdit: _openEditTagPage,
|
||||||
leadingBuilder: (t) => CircleAvatar(
|
leadingBuilder: (t) => CircleAvatar(
|
||||||
backgroundColor: t.color,
|
backgroundColor: t.color,
|
||||||
child: t.isInboxTag ?? false
|
child: t.isInboxTag ?? false
|
||||||
@@ -152,11 +160,15 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
S.of(context).labelsPageTagsEmptyStateAddNewLabel,
|
S.of(context).labelsPageTagsEmptyStateAddNewLabel,
|
||||||
emptyStateDescription:
|
emptyStateDescription:
|
||||||
S.of(context).labelsPageTagsEmptyStateDescriptionText,
|
S.of(context).labelsPageTagsEmptyStateDescriptionText,
|
||||||
onOpenAddNewPage: _onAddPressed,
|
onAddNew: _openAddTagPage,
|
||||||
),
|
),
|
||||||
LabelTabView<StoragePath>(
|
),
|
||||||
cubit: BlocProvider.of<StoragePathCubit>(context),
|
BlocProvider(
|
||||||
onOpenEditPage: _openEditStoragePathPage,
|
create: (context) => LabelCubit<StoragePath>(
|
||||||
|
RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
),
|
||||||
|
child: LabelTabView<StoragePath>(
|
||||||
|
onEdit: _openEditStoragePathPage,
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
storagePath: StoragePathQuery.fromId(label.id),
|
storagePath: StoragePathQuery.fromId(label.id),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
@@ -164,9 +176,11 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
contentBuilder: (path) => Text(path.path ?? ""),
|
contentBuilder: (path) => Text(path.path ?? ""),
|
||||||
emptyStateActionButtonLabel:
|
emptyStateActionButtonLabel:
|
||||||
S.of(context).labelsPageStoragePathEmptyStateAddNewLabel,
|
S.of(context).labelsPageStoragePathEmptyStateAddNewLabel,
|
||||||
emptyStateDescription:
|
emptyStateDescription: S
|
||||||
S.of(context).labelsPageStoragePathEmptyStateDescriptionText,
|
.of(context)
|
||||||
onOpenAddNewPage: _onAddPressed,
|
.labelsPageStoragePathEmptyStateDescriptionText,
|
||||||
|
onAddNew: _openAddStoragePathPage,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -178,12 +192,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
additionalProviders: [
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
BlocProvider<DocumentsCubit>.value(
|
|
||||||
value: BlocProvider.of<DocumentsCubit>(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: EditCorrespondentPage(correspondent: correspondent),
|
child: EditCorrespondentPage(correspondent: correspondent),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -194,12 +204,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
additionalProviders: [
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
BlocProvider<DocumentsCubit>.value(
|
|
||||||
value: BlocProvider.of<DocumentsCubit>(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: EditDocumentTypePage(documentType: docType),
|
child: EditDocumentTypePage(documentType: docType),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -210,12 +216,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
additionalProviders: [
|
value: RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
BlocProvider<DocumentsCubit>.value(
|
|
||||||
value: BlocProvider.of<DocumentsCubit>(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: EditTagPage(tag: tag),
|
child: EditTagPage(tag: tag),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -226,37 +228,61 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
additionalProviders: [
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
BlocProvider<DocumentsCubit>.value(
|
child: EditStoragePathPage(
|
||||||
value: getIt<DocumentsCubit>(),
|
storagePath: path,
|
||||||
),
|
),
|
||||||
],
|
|
||||||
child: EditStoragePathPage(storagePath: path),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddPressed() {
|
void _openAddCorrespondentPage() {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(
|
||||||
builder: (context) {
|
context,
|
||||||
late final Widget page;
|
MaterialPageRoute(
|
||||||
switch (_currentIndex) {
|
builder: (_) => RepositoryProvider.value(
|
||||||
case 0:
|
value: RepositoryProvider.of<LabelRepository<Correspondent>>(context),
|
||||||
page = const AddCorrespondentPage();
|
child: const AddCorrespondentPage(),
|
||||||
break;
|
),
|
||||||
case 1:
|
),
|
||||||
page = const AddDocumentTypePage();
|
);
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
page = const AddTagPage();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
page = const AddStoragePathPage();
|
|
||||||
}
|
}
|
||||||
return GlobalStateBlocProvider(child: page);
|
|
||||||
},
|
void _openAddDocumentTypePage() {
|
||||||
));
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<DocumentType>>(context),
|
||||||
|
child: const AddDocumentTypePage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openAddTagPage() {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<Tag>>(context),
|
||||||
|
child: const AddTagPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openAddStoragePathPage() {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => RepositoryProvider.value(
|
||||||
|
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||||
|
child: const AddStoragePathPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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/features/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/view/pages/linked_documents_page.dart';
|
import 'package:paperless_mobile/features/linked_documents_preview/view/pages/linked_documents_page.dart';
|
||||||
@@ -46,12 +45,11 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => GlobalStateBlocProvider(
|
builder: (context) => BlocProvider.value(
|
||||||
additionalProviders: [
|
value: LinkedDocumentsCubit(
|
||||||
BlocProvider<LinkedDocumentsCubit>.value(
|
getIt<PaperlessDocumentsApi>(),
|
||||||
value: getIt<LinkedDocumentsCubit>()
|
filter,
|
||||||
..initialize(filter)),
|
),
|
||||||
],
|
|
||||||
child: const LinkedDocumentsPage(),
|
child: const LinkedDocumentsPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.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/core/widgets/offline_widget.dart';
|
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||||
@@ -9,10 +10,9 @@ import 'package:paperless_mobile/features/labels/view/widgets/label_item.dart';
|
|||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
|
||||||
class LabelTabView<T extends Label> extends StatelessWidget {
|
class LabelTabView<T extends Label> extends StatelessWidget {
|
||||||
final LabelCubit<T> cubit;
|
|
||||||
final DocumentFilter Function(Label) filterBuilder;
|
final DocumentFilter Function(Label) filterBuilder;
|
||||||
final void Function(T) onOpenEditPage;
|
final void Function(T) onEdit;
|
||||||
final void Function() onOpenAddNewPage;
|
final void Function() onAddNew;
|
||||||
|
|
||||||
/// Displayed as the subtitle of the [ListTile]
|
/// Displayed as the subtitle of the [ListTile]
|
||||||
final Widget Function(T)? contentBuilder;
|
final Widget Function(T)? contentBuilder;
|
||||||
@@ -26,13 +26,12 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
|
|
||||||
const LabelTabView({
|
const LabelTabView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.cubit,
|
|
||||||
required this.filterBuilder,
|
required this.filterBuilder,
|
||||||
this.contentBuilder,
|
this.contentBuilder,
|
||||||
this.leadingBuilder,
|
this.leadingBuilder,
|
||||||
required this.onOpenEditPage,
|
required this.onEdit,
|
||||||
required this.emptyStateDescription,
|
required this.emptyStateDescription,
|
||||||
required this.onOpenAddNewPage,
|
required this.onAddNew,
|
||||||
required this.emptyStateActionButtonLabel,
|
required this.emptyStateActionButtonLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,10 +42,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
if (state == ConnectivityState.notConnected) {
|
if (state == ConnectivityState.notConnected) {
|
||||||
return const OfflineWidget();
|
return const OfflineWidget();
|
||||||
}
|
}
|
||||||
return RefreshIndicator(
|
return BlocBuilder<LabelCubit<T>, LabelState<T>>(
|
||||||
onRefresh: cubit.initialize,
|
|
||||||
child: BlocBuilder<Cubit<LabelState<T>>, LabelState<T>>(
|
|
||||||
bloc: cubit,
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final labels = state.labels.values.toList()..sort();
|
final labels = state.labels.values.toList()..sort();
|
||||||
if (labels.isEmpty) {
|
if (labels.isEmpty) {
|
||||||
@@ -59,7 +55,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: onOpenAddNewPage,
|
onPressed: onAddNew,
|
||||||
child: Text(emptyStateActionButtonLabel),
|
child: Text(emptyStateActionButtonLabel),
|
||||||
)
|
)
|
||||||
].padded(),
|
].padded(),
|
||||||
@@ -72,7 +68,7 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
name: l.name,
|
name: l.name,
|
||||||
content:
|
content:
|
||||||
contentBuilder?.call(l) ?? Text(l.match ?? '-'),
|
contentBuilder?.call(l) ?? Text(l.match ?? '-'),
|
||||||
onOpenEditPage: onOpenEditPage,
|
onOpenEditPage: onEdit,
|
||||||
filterBuilder: filterBuilder,
|
filterBuilder: filterBuilder,
|
||||||
leading: leadingBuilder?.call(l),
|
leading: leadingBuilder?.call(l),
|
||||||
label: l,
|
label: l,
|
||||||
@@ -80,7 +76,6 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
||||||
|
|
||||||
@injectable
|
|
||||||
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
|
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState> {
|
||||||
final PaperlessDocumentsApi _api;
|
final PaperlessDocumentsApi _api;
|
||||||
|
|
||||||
LinkedDocumentsCubit(this._api) : super(LinkedDocumentsState());
|
LinkedDocumentsCubit(this._api, DocumentFilter filter)
|
||||||
|
: super(LinkedDocumentsState(filter: filter)) {
|
||||||
|
_initialize();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initialize(DocumentFilter filter) async {
|
Future<void> _initialize() async {
|
||||||
final documents = await _api.find(
|
final documents = await _api.find(
|
||||||
filter.copyWith(
|
state.filter.copyWith(
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
emit(LinkedDocumentsState(
|
emit(LinkedDocumentsState(
|
||||||
isLoaded: true,
|
isLoaded: true,
|
||||||
documents: documents,
|
documents: documents,
|
||||||
filter: filter,
|
filter: state.filter,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
class LinkedDocumentsState {
|
class LinkedDocumentsState {
|
||||||
final bool isLoaded;
|
final bool isLoaded;
|
||||||
final PagedSearchResult<DocumentModel>? documents;
|
final PagedSearchResult<DocumentModel>? documents;
|
||||||
final DocumentFilter? filter;
|
final DocumentFilter filter;
|
||||||
|
|
||||||
LinkedDocumentsState({
|
LinkedDocumentsState({
|
||||||
this.filter,
|
required this.filter,
|
||||||
this.isLoaded = false,
|
this.isLoaded = false,
|
||||||
this.documents,
|
this.documents,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ 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';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
import 'package:paperless_mobile/features/linked_documents_preview/bloc/linked_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
import 'package:paperless_mobile/features/linked_documents_preview/bloc/state/linked_documents_state.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
@@ -64,15 +63,12 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (ctxt) => GlobalStateBlocProvider(
|
builder: (context) =>
|
||||||
additionalProviders: [
|
|
||||||
BlocProvider<DocumentDetailsCubit>.value(
|
BlocProvider<DocumentDetailsCubit>.value(
|
||||||
value: DocumentDetailsCubit(
|
value: DocumentDetailsCubit(
|
||||||
getIt<PaperlessDocumentsApi>(),
|
getIt<PaperlessDocumentsApi>(),
|
||||||
document,
|
document,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
child: const DocumentDetailsPage(
|
child: const DocumentDetailsPage(
|
||||||
isLabelClickable: false,
|
isLabelClickable: false,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_state.dart';
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||||
|
|
||||||
@prod
|
|
||||||
@test
|
|
||||||
@lazySingleton
|
|
||||||
class SavedViewCubit extends Cubit<SavedViewState> {
|
class SavedViewCubit extends Cubit<SavedViewState> {
|
||||||
final PaperlessSavedViewsApi _api;
|
final SavedViewRepository _repository;
|
||||||
SavedViewCubit(this._api) : super(SavedViewState(value: {}));
|
StreamSubscription? _subscription;
|
||||||
|
|
||||||
|
SavedViewCubit(this._repository) : super(SavedViewState(value: {})) {
|
||||||
|
_subscription = _repository.savedViews.listen(
|
||||||
|
(savedViews) => emit(state.copyWith(value: savedViews)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void selectView(SavedView? view) {
|
void selectView(SavedView? view) {
|
||||||
emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id));
|
emit(SavedViewState(value: state.value, selectedSavedViewId: view?.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SavedView> add(SavedView view) async {
|
Future<SavedView> add(SavedView view) async {
|
||||||
final savedView = await _api.save(view);
|
final savedView = await _repository.create(view);
|
||||||
emit(
|
emit(
|
||||||
SavedViewState(
|
SavedViewState(
|
||||||
value: {...state.value, savedView.id!: savedView},
|
value: {...state.value, savedView.id!: savedView},
|
||||||
@@ -26,22 +32,15 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> remove(SavedView view) async {
|
Future<int> remove(SavedView view) async {
|
||||||
final id = await _api.delete(view);
|
final id = await _repository.delete(view);
|
||||||
final newValue = {...state.value};
|
if (state.selectedSavedViewId == id) {
|
||||||
newValue.removeWhere((key, value) => key == id);
|
resetSelection();
|
||||||
emit(
|
}
|
||||||
SavedViewState(
|
|
||||||
value: newValue,
|
|
||||||
selectedSavedViewId: view.id == state.selectedSavedViewId
|
|
||||||
? null
|
|
||||||
: state.selectedSavedViewId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
final views = await _api.getAll();
|
final views = await _repository.findAll();
|
||||||
final values = {for (var element in views) element.id!: element};
|
final values = {for (var element in views) element.id!: element};
|
||||||
emit(SavedViewState(value: values));
|
emit(SavedViewState(value: values));
|
||||||
}
|
}
|
||||||
@@ -49,4 +48,10 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
|||||||
void resetSelection() {
|
void resetSelection() {
|
||||||
emit(SavedViewState(value: state.value));
|
emit(SavedViewState(value: state.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,4 +15,17 @@ class SavedViewState with EquatableMixin {
|
|||||||
value,
|
value,
|
||||||
selectedSavedViewId,
|
selectedSavedViewId,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
SavedViewState copyWith({
|
||||||
|
Map<int, SavedView>? value,
|
||||||
|
int? selectedSavedViewId,
|
||||||
|
bool overwriteSelectedSavedViewId = false,
|
||||||
|
}) {
|
||||||
|
return SavedViewState(
|
||||||
|
value: value ?? this.value,
|
||||||
|
selectedSavedViewId: overwriteSelectedSavedViewId
|
||||||
|
? selectedSavedViewId
|
||||||
|
: this.selectedSavedViewId,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ 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/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/bloc/saved_view_cubit.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_state.dart';
|
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.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';
|
||||||
|
|
||||||
@@ -94,18 +94,11 @@ class SavedViewSelectionWidget extends StatelessWidget {
|
|||||||
|
|
||||||
void _onSelected(
|
void _onSelected(
|
||||||
bool isSelected, BuildContext context, SavedView view) async {
|
bool isSelected, BuildContext context, SavedView view) async {
|
||||||
try {
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
BlocProvider.of<DocumentsCubit>(context)
|
|
||||||
.updateFilter(filter: view.toDocumentFilter());
|
|
||||||
BlocProvider.of<SavedViewCubit>(context).selectView(view);
|
BlocProvider.of<SavedViewCubit>(context).selectView(view);
|
||||||
} else {
|
} else {
|
||||||
BlocProvider.of<DocumentsCubit>(context).updateFilter();
|
|
||||||
BlocProvider.of<SavedViewCubit>(context).selectView(null);
|
BlocProvider.of<SavedViewCubit>(context).selectView(null);
|
||||||
}
|
}
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDelete(BuildContext context, SavedView view) async {
|
void _onDelete(BuildContext context, SavedView view) async {
|
||||||
|
|||||||
@@ -9,13 +9,8 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
|
|
||||||
@injectable
|
|
||||||
class DocumentScannerCubit extends Cubit<List<File>> {
|
class DocumentScannerCubit extends Cubit<List<File>> {
|
||||||
final PaperlessDocumentsApi _api;
|
DocumentScannerCubit() : super(const []);
|
||||||
|
|
||||||
static List<File> initialState = [];
|
|
||||||
|
|
||||||
DocumentScannerCubit(this._api) : super(initialState);
|
|
||||||
|
|
||||||
void addScan(File file) => emit([...state, file]);
|
void addScan(File file) => emit([...state, file]);
|
||||||
|
|
||||||
@@ -39,41 +34,9 @@ class DocumentScannerCubit extends Cubit<List<File>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
imageCache.clear();
|
imageCache.clear();
|
||||||
emit(initialState);
|
emit([]);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
throw const PaperlessServerException(ErrorCode.scanRemoveFailed);
|
throw const PaperlessServerException(ErrorCode.scanRemoveFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadDocument(
|
|
||||||
Uint8List bytes,
|
|
||||||
String fileName, {
|
|
||||||
required String title,
|
|
||||||
required void Function(DocumentModel document)? onConsumptionFinished,
|
|
||||||
int? documentType,
|
|
||||||
int? correspondent,
|
|
||||||
Iterable<int> tags = const [],
|
|
||||||
DateTime? createdAt,
|
|
||||||
}) async {
|
|
||||||
final auth = getIt<AuthenticationCubit>().state.authentication;
|
|
||||||
if (auth == null) {
|
|
||||||
throw const PaperlessServerException(ErrorCode.notAuthenticated);
|
|
||||||
}
|
|
||||||
await _api.create(
|
|
||||||
bytes,
|
|
||||||
filename: fileName,
|
|
||||||
title: title,
|
|
||||||
documentType: documentType,
|
|
||||||
correspondent: correspondent,
|
|
||||||
tags: tags,
|
|
||||||
createdAt: createdAt,
|
|
||||||
authToken: auth.token!,
|
|
||||||
serverUrl: auth.serverUrl,
|
|
||||||
);
|
|
||||||
if (onConsumptionFinished != null) {
|
|
||||||
_api
|
|
||||||
.waitForConsumptionFinished(fileName, title)
|
|
||||||
.then((value) => onConsumptionFinished(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,275 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/type/types.dart';
|
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/add_document_type_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
|
||||||
import 'package:paperless_mobile/util.dart';
|
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
class DocumentUploadPage extends StatefulWidget {
|
|
||||||
final Uint8List fileBytes;
|
|
||||||
final String? title;
|
|
||||||
final String? filename;
|
|
||||||
final void Function()? afterUpload;
|
|
||||||
final void Function(DocumentModel)? onSuccessfullyConsumed;
|
|
||||||
|
|
||||||
const DocumentUploadPage({
|
|
||||||
Key? key,
|
|
||||||
required this.fileBytes,
|
|
||||||
this.afterUpload,
|
|
||||||
this.title,
|
|
||||||
this.filename,
|
|
||||||
this.onSuccessfullyConsumed,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<DocumentUploadPage> createState() => _DocumentUploadPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
|
||||||
static const fkFileName = "filename";
|
|
||||||
static final fileNameDateFormat = DateFormat("yyyy_MM_ddTHH_mm_ss");
|
|
||||||
|
|
||||||
final GlobalKey<FormBuilderState> _formKey = GlobalKey();
|
|
||||||
|
|
||||||
PaperlessValidationErrors _errors = {};
|
|
||||||
bool _isUploadLoading = false;
|
|
||||||
late bool _syncTitleAndFilename;
|
|
||||||
final _now = DateTime.now();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_syncTitleAndFilename = widget.filename == null && widget.title == null;
|
|
||||||
initializeDateFormatting();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
resizeToAvoidBottomInset: true,
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(S.of(context).documentsUploadPageTitle),
|
|
||||||
bottom: _isUploadLoading
|
|
||||||
? const PreferredSize(
|
|
||||||
child: LinearProgressIndicator(),
|
|
||||||
preferredSize: Size.fromHeight(4.0))
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
floatingActionButton: Visibility(
|
|
||||||
visible: MediaQuery.of(context).viewInsets.bottom == 0,
|
|
||||||
child: FloatingActionButton.extended(
|
|
||||||
onPressed: _onSubmit,
|
|
||||||
label: Text(S.of(context).genericActionUploadLabel),
|
|
||||||
icon: const Icon(Icons.upload),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: FormBuilder(
|
|
||||||
key: _formKey,
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
FormBuilderTextField(
|
|
||||||
autovalidateMode: AutovalidateMode.always,
|
|
||||||
name: DocumentModel.titleKey,
|
|
||||||
initialValue:
|
|
||||||
widget.title ?? "scan_${fileNameDateFormat.format(_now)}",
|
|
||||||
validator: FormBuilderValidators.required(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: S.of(context).documentTitlePropertyLabel,
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: const Icon(Icons.close),
|
|
||||||
onPressed: () {
|
|
||||||
_formKey.currentState?.fields[DocumentModel.titleKey]
|
|
||||||
?.didChange("");
|
|
||||||
if (_syncTitleAndFilename) {
|
|
||||||
_formKey.currentState?.fields[fkFileName]?.didChange("");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
errorText: _errors[DocumentModel.titleKey],
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
final String transformedValue = _formatFilename(value ?? '');
|
|
||||||
if (_syncTitleAndFilename) {
|
|
||||||
_formKey.currentState?.fields[fkFileName]
|
|
||||||
?.didChange(transformedValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
FormBuilderTextField(
|
|
||||||
autovalidateMode: AutovalidateMode.always,
|
|
||||||
readOnly: _syncTitleAndFilename,
|
|
||||||
enabled: !_syncTitleAndFilename,
|
|
||||||
name: fkFileName,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: S.of(context).documentUploadFileNameLabel,
|
|
||||||
suffixText: ".pdf",
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () =>
|
|
||||||
_formKey.currentState?.fields[fkFileName]?.didChange(''),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
initialValue:
|
|
||||||
widget.filename ?? "scan_${fileNameDateFormat.format(_now)}",
|
|
||||||
),
|
|
||||||
SwitchListTile(
|
|
||||||
value: _syncTitleAndFilename,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(
|
|
||||||
() => _syncTitleAndFilename = value,
|
|
||||||
);
|
|
||||||
if (_syncTitleAndFilename) {
|
|
||||||
final String transformedValue = _formatFilename(_formKey
|
|
||||||
.currentState
|
|
||||||
?.fields[DocumentModel.titleKey]
|
|
||||||
?.value as String);
|
|
||||||
if (_syncTitleAndFilename) {
|
|
||||||
_formKey.currentState?.fields[fkFileName]
|
|
||||||
?.didChange(transformedValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: Text(S
|
|
||||||
.of(context)
|
|
||||||
.documentUploadPageSynchronizeTitleAndFilenameLabel), //TODO: INTL
|
|
||||||
),
|
|
||||||
FormBuilderDateTimePicker(
|
|
||||||
autovalidateMode: AutovalidateMode.always,
|
|
||||||
format: DateFormat("dd. MMMM yyyy"), //TODO: INTL
|
|
||||||
inputType: InputType.date,
|
|
||||||
name: DocumentModel.createdKey,
|
|
||||||
initialValue: null,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.calendar_month_outlined),
|
|
||||||
labelText: S.of(context).documentCreatedPropertyLabel + " *",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
|
||||||
bloc: getIt<DocumentTypeCubit>(), //TODO: Use provider
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
|
||||||
child: AddDocumentTypePage(initialName: initialValue),
|
|
||||||
),
|
|
||||||
label: S.of(context).documentDocumentTypePropertyLabel + " *",
|
|
||||||
name: DocumentModel.documentTypeKey,
|
|
||||||
state: state.labels,
|
|
||||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
|
||||||
queryParameterNotAssignedBuilder:
|
|
||||||
DocumentTypeQuery.notAssigned,
|
|
||||||
prefixIcon: const Icon(Icons.description_outlined),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
|
||||||
bloc: getIt<CorrespondentCubit>(), //TODO: Use provider
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
labelCreationWidgetBuilder: (initialValue) =>
|
|
||||||
BlocProvider.value(
|
|
||||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
|
||||||
child: AddCorrespondentPage(initalValue: initialValue),
|
|
||||||
),
|
|
||||||
label:
|
|
||||||
S.of(context).documentCorrespondentPropertyLabel + " *",
|
|
||||||
name: DocumentModel.correspondentKey,
|
|
||||||
state: state.labels,
|
|
||||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
|
||||||
queryParameterNotAssignedBuilder:
|
|
||||||
CorrespondentQuery.notAssigned,
|
|
||||||
prefixIcon: const Icon(Icons.person_outline),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const TagFormField(
|
|
||||||
name: DocumentModel.tagsKey,
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
anyAssignedSelectable: false,
|
|
||||||
excludeAllowed: false,
|
|
||||||
//Label: "Tags" + " *",
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"* " + S.of(context).uploadPageAutomaticallInferredFieldsHintText,
|
|
||||||
style: Theme.of(context).textTheme.caption,
|
|
||||||
),
|
|
||||||
].padded(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSubmit() async {
|
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
|
||||||
final cubit = BlocProvider.of<DocumentScannerCubit>(context);
|
|
||||||
try {
|
|
||||||
setState(() => _isUploadLoading = true);
|
|
||||||
|
|
||||||
final fv = _formKey.currentState!.value;
|
|
||||||
|
|
||||||
final createdAt = fv[DocumentModel.createdKey] as DateTime?;
|
|
||||||
final title = fv[DocumentModel.titleKey] as String;
|
|
||||||
final docType = fv[DocumentModel.documentTypeKey] as IdQueryParameter;
|
|
||||||
final tags = fv[DocumentModel.tagsKey] as IdsTagsQuery;
|
|
||||||
final correspondent =
|
|
||||||
fv[DocumentModel.correspondentKey] as IdQueryParameter;
|
|
||||||
|
|
||||||
await cubit.uploadDocument(
|
|
||||||
widget.fileBytes,
|
|
||||||
_padWithPdfExtension(_formKey.currentState?.value[fkFileName]),
|
|
||||||
onConsumptionFinished: widget.onSuccessfullyConsumed,
|
|
||||||
title: title,
|
|
||||||
documentType: docType.id,
|
|
||||||
correspondent: correspondent.id,
|
|
||||||
tags: tags.ids,
|
|
||||||
createdAt: createdAt,
|
|
||||||
);
|
|
||||||
|
|
||||||
cubit.reset(); //TODO: Access via provider
|
|
||||||
showSnackBar(context, S.of(context).documentUploadSuccessText);
|
|
||||||
Navigator.pop(context);
|
|
||||||
widget.afterUpload?.call();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
} on PaperlessValidationErrors catch (PaperlessServerExceptions) {
|
|
||||||
setState(() => _errors = PaperlessServerExceptions);
|
|
||||||
} catch (unknownError, stackTrace) {
|
|
||||||
showErrorMessage(
|
|
||||||
context, const PaperlessServerException.unknown(), stackTrace);
|
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
_isUploadLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _padWithPdfExtension(String source) {
|
|
||||||
return source.endsWith(".pdf") ? source : '$source.pdf';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatFilename(String source) {
|
|
||||||
return source.replaceAll(RegExp(r"[\W_]"), "_");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,17 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/core/global/constants.dart';
|
import 'package:paperless_mobile/core/global/constants.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/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
|
import 'package:paperless_mobile/core/store/local_vault.dart';
|
||||||
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
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/home/view/widget/info_drawer.dart';
|
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_widget.dart';
|
import 'package:paperless_mobile/features/scan/view/widgets/grid_image_item_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';
|
||||||
@@ -124,17 +128,29 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
final bytes = await doc.save();
|
final bytes = await doc.save();
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => LabelRepositoriesProvider(
|
||||||
additionalProviders: [
|
child: BlocProvider(
|
||||||
BlocProvider<DocumentScannerCubit>.value(
|
create: (context) => DocumentUploadCubit(
|
||||||
value: BlocProvider.of<DocumentScannerCubit>(context),
|
localVault: getIt<LocalVault>(),
|
||||||
|
documentApi: getIt<PaperlessDocumentsApi>(),
|
||||||
|
correspondentRepository:
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
),
|
),
|
||||||
],
|
documentTypeRepository:
|
||||||
child: DocumentUploadPage(
|
RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
tagRepository: RepositoryProvider.of<LabelRepository<Tag>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: DocumentUploadPreparationPage(
|
||||||
fileBytes: bytes,
|
fileBytes: bytes,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,15 +259,27 @@ class _ScannerPageState extends State<ScannerPage>
|
|||||||
}
|
}
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => GlobalStateBlocProvider(
|
builder: (_) => LabelRepositoriesProvider(
|
||||||
additionalProviders: [
|
child: BlocProvider(
|
||||||
BlocProvider<DocumentScannerCubit>.value(
|
create: (context) => DocumentUploadCubit(
|
||||||
value: BlocProvider.of<DocumentScannerCubit>(context),
|
localVault: getIt<LocalVault>(),
|
||||||
|
documentApi: getIt<PaperlessDocumentsApi>(),
|
||||||
|
correspondentRepository:
|
||||||
|
RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||||
|
context,
|
||||||
),
|
),
|
||||||
],
|
documentTypeRepository:
|
||||||
child: DocumentUploadPage(
|
RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||||
filename: filename,
|
context,
|
||||||
|
),
|
||||||
|
tagRepository: RepositoryProvider.of<LabelRepository<Tag>>(
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: DocumentUploadPreparationPage(
|
||||||
fileBytes: fileBytes,
|
fileBytes: fileBytes,
|
||||||
|
filename: filename,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,26 +13,31 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/bloc_changes_observer.dart';
|
import 'package:paperless_mobile/core/bloc/bloc_changes_observer.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||||
import 'package:paperless_mobile/core/global/constants.dart';
|
import 'package:paperless_mobile/core/global/constants.dart';
|
||||||
import 'package:paperless_mobile/core/global/http_self_signed_certificate_override.dart';
|
import 'package:paperless_mobile/core/global/http_self_signed_certificate_override.dart';
|
||||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/impl/correspondent_repository_impl.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/impl/document_type_repository_impl.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/impl/saved_view_repository_impl.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/impl/storage_path_repository_impl.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/impl/tag_repository_impl.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/di_initializer.dart';
|
import 'package:paperless_mobile/di_initializer.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
|
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
|
||||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/home_page.dart';
|
import 'package:paperless_mobile/features/home/view/home_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/login/view/login_page.dart';
|
import 'package:paperless_mobile/features/login/view/login_page.dart';
|
||||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/scan/view/document_upload_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/util.dart';
|
import 'package:paperless_mobile/util.dart';
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
Bloc.observer = BlocChangesObserver();
|
Bloc.observer = BlocChangesObserver();
|
||||||
@@ -52,7 +57,30 @@ void main() async {
|
|||||||
await getIt<ApplicationSettingsCubit>().initialize();
|
await getIt<ApplicationSettingsCubit>().initialize();
|
||||||
await getIt<AuthenticationCubit>().initialize();
|
await getIt<AuthenticationCubit>().initialize();
|
||||||
|
|
||||||
runApp(const PaperlessMobileEntrypoint());
|
// Create repositories
|
||||||
|
final LabelRepository<Tag> tagRepository =
|
||||||
|
TagRepositoryImpl(getIt<PaperlessLabelsApi>());
|
||||||
|
final LabelRepository<Correspondent> correspondentRepository =
|
||||||
|
CorrespondentRepositoryImpl(getIt<PaperlessLabelsApi>());
|
||||||
|
final LabelRepository<DocumentType> documentTypeRepository =
|
||||||
|
DocumentTypeRepositoryImpl(getIt<PaperlessLabelsApi>());
|
||||||
|
final LabelRepository<StoragePath> storagePathRepository =
|
||||||
|
StoragePathRepositoryImpl(getIt<PaperlessLabelsApi>());
|
||||||
|
final SavedViewRepository savedViewRepository =
|
||||||
|
SavedViewRepositoryImpl(getIt<PaperlessSavedViewsApi>());
|
||||||
|
|
||||||
|
runApp(
|
||||||
|
MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider.value(value: tagRepository),
|
||||||
|
RepositoryProvider.value(value: correspondentRepository),
|
||||||
|
RepositoryProvider.value(value: documentTypeRepository),
|
||||||
|
RepositoryProvider.value(value: storagePathRepository),
|
||||||
|
RepositoryProvider.value(value: savedViewRepository),
|
||||||
|
],
|
||||||
|
child: const PaperlessMobileEntrypoint(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaperlessMobileEntrypoint extends StatefulWidget {
|
class PaperlessMobileEntrypoint extends StatefulWidget {
|
||||||
@@ -71,9 +99,6 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
|||||||
BlocProvider<ConnectivityCubit>.value(
|
BlocProvider<ConnectivityCubit>.value(
|
||||||
value: getIt<ConnectivityCubit>(),
|
value: getIt<ConnectivityCubit>(),
|
||||||
),
|
),
|
||||||
BlocProvider<AuthenticationCubit>.value(
|
|
||||||
value: getIt<AuthenticationCubit>(),
|
|
||||||
),
|
|
||||||
BlocProvider<PaperlessServerInformationCubit>.value(
|
BlocProvider<PaperlessServerInformationCubit>.value(
|
||||||
value: getIt<PaperlessServerInformationCubit>(),
|
value: getIt<PaperlessServerInformationCubit>(),
|
||||||
),
|
),
|
||||||
@@ -126,7 +151,10 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
|
|||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
FormBuilderLocalizations.delegate,
|
FormBuilderLocalizations.delegate,
|
||||||
],
|
],
|
||||||
home: const AuthenticationWrapper(),
|
home: BlocProvider<AuthenticationCubit>.value(
|
||||||
|
value: getIt<AuthenticationCubit>(),
|
||||||
|
child: const AuthenticationWrapper(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -177,21 +205,21 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
|||||||
}
|
}
|
||||||
final filename = extractFilenameFromPath(file.path);
|
final filename = extractFilenameFromPath(file.path);
|
||||||
final bytes = File(file.path).readAsBytesSync();
|
final bytes = File(file.path).readAsBytesSync();
|
||||||
Navigator.push(
|
final success = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => GlobalStateBlocProvider(
|
builder: (context) => BlocProvider.value(
|
||||||
additionalProviders: [
|
value: getIt<DocumentScannerCubit>(),
|
||||||
BlocProvider.value(value: getIt<DocumentScannerCubit>()),
|
child: DocumentUploadPreparationPage(
|
||||||
],
|
|
||||||
child: DocumentUploadPage(
|
|
||||||
fileBytes: bytes,
|
fileBytes: bytes,
|
||||||
afterUpload: SystemNavigator.pop,
|
|
||||||
filename: filename,
|
filename: filename,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
if (success) {
|
||||||
|
SystemNavigator.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -232,17 +260,12 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
|||||||
},
|
},
|
||||||
builder: (context, authentication) {
|
builder: (context, authentication) {
|
||||||
if (authentication.isAuthenticated) {
|
if (authentication.isAuthenticated) {
|
||||||
return GlobalStateBlocProvider(
|
return const HomePage();
|
||||||
additionalProviders: [
|
|
||||||
BlocProvider.value(value: getIt<DocumentsCubit>()),
|
|
||||||
],
|
|
||||||
child: const HomePage(),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (authentication.wasLoginStored &&
|
// if (authentication.wasLoginStored &&
|
||||||
!(authentication.wasLocalAuthenticationSuccessful ?? false)) {
|
// !(authentication.wasLocalAuthenticationSuccessful ?? false)) {
|
||||||
return BiometricAuthenticationPage();
|
// return const BiometricAuthenticationPage();
|
||||||
}
|
// }
|
||||||
return const LoginPage();
|
return const LoginPage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,25 +14,25 @@ import 'package:paperless_api/src/models/labels/tag_model.dart';
|
|||||||
///
|
///
|
||||||
abstract class PaperlessLabelsApi {
|
abstract class PaperlessLabelsApi {
|
||||||
Future<Correspondent?> getCorrespondent(int id);
|
Future<Correspondent?> getCorrespondent(int id);
|
||||||
Future<List<Correspondent>> getCorrespondents();
|
Future<List<Correspondent>> getCorrespondents([Iterable<int>? ids]);
|
||||||
Future<Correspondent> saveCorrespondent(Correspondent correspondent);
|
Future<Correspondent> saveCorrespondent(Correspondent correspondent);
|
||||||
Future<Correspondent> updateCorrespondent(Correspondent correspondent);
|
Future<Correspondent> updateCorrespondent(Correspondent correspondent);
|
||||||
Future<int> deleteCorrespondent(Correspondent correspondent);
|
Future<int> deleteCorrespondent(Correspondent correspondent);
|
||||||
|
|
||||||
Future<Tag?> getTag(int id);
|
Future<Tag?> getTag(int id);
|
||||||
Future<List<Tag>> getTags({List<int>? ids});
|
Future<List<Tag>> getTags([Iterable<int>? ids]);
|
||||||
Future<Tag> saveTag(Tag tag);
|
Future<Tag> saveTag(Tag tag);
|
||||||
Future<Tag> updateTag(Tag tag);
|
Future<Tag> updateTag(Tag tag);
|
||||||
Future<int> deleteTag(Tag tag);
|
Future<int> deleteTag(Tag tag);
|
||||||
|
|
||||||
Future<DocumentType?> getDocumentType(int id);
|
Future<DocumentType?> getDocumentType(int id);
|
||||||
Future<List<DocumentType>> getDocumentTypes();
|
Future<List<DocumentType>> getDocumentTypes([Iterable<int>? ids]);
|
||||||
Future<DocumentType> saveDocumentType(DocumentType type);
|
Future<DocumentType> saveDocumentType(DocumentType type);
|
||||||
Future<DocumentType> updateDocumentType(DocumentType documentType);
|
Future<DocumentType> updateDocumentType(DocumentType documentType);
|
||||||
Future<int> deleteDocumentType(DocumentType documentType);
|
Future<int> deleteDocumentType(DocumentType documentType);
|
||||||
|
|
||||||
Future<StoragePath?> getStoragePath(int id);
|
Future<StoragePath?> getStoragePath(int id);
|
||||||
Future<List<StoragePath>> getStoragePaths();
|
Future<List<StoragePath>> getStoragePaths([Iterable<int>? ids]);
|
||||||
Future<StoragePath> saveStoragePath(StoragePath path);
|
Future<StoragePath> saveStoragePath(StoragePath path);
|
||||||
Future<StoragePath> updateStoragePath(StoragePath path);
|
Future<StoragePath> updateStoragePath(StoragePath path);
|
||||||
Future<int> deleteStoragePath(StoragePath path);
|
Future<int> deleteStoragePath(StoragePath path);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Tag>> getTags({List<int>? ids}) async {
|
Future<List<Tag>> getTags([Iterable<int>? ids]) async {
|
||||||
final results = await getCollection(
|
final results = await getCollection(
|
||||||
"/api/tags/?page=1&page_size=100000",
|
"/api/tags/?page=1&page_size=100000",
|
||||||
Tag.fromJson,
|
Tag.fromJson,
|
||||||
@@ -59,23 +59,31 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Correspondent>> getCorrespondents() {
|
Future<List<Correspondent>> getCorrespondents([Iterable<int>? ids]) async {
|
||||||
return getCollection(
|
final results = await getCollection(
|
||||||
"/api/correspondents/?page=1&page_size=100000",
|
"/api/correspondents/?page=1&page_size=100000",
|
||||||
Correspondent.fromJson,
|
Correspondent.fromJson,
|
||||||
ErrorCode.correspondentLoadFailed,
|
ErrorCode.correspondentLoadFailed,
|
||||||
client: client,
|
client: client,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return results
|
||||||
|
.where((element) => ids?.contains(element.id) ?? true)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<DocumentType>> getDocumentTypes() {
|
Future<List<DocumentType>> getDocumentTypes([Iterable<int>? ids]) async {
|
||||||
return getCollection(
|
final results = await getCollection(
|
||||||
"/api/document_types/?page=1&page_size=100000",
|
"/api/document_types/?page=1&page_size=100000",
|
||||||
DocumentType.fromJson,
|
DocumentType.fromJson,
|
||||||
ErrorCode.documentTypeLoadFailed,
|
ErrorCode.documentTypeLoadFailed,
|
||||||
client: client,
|
client: client,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return results
|
||||||
|
.where((element) => ids?.contains(element.id) ?? true)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -261,13 +269,17 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<StoragePath>> getStoragePaths() {
|
Future<List<StoragePath>> getStoragePaths([Iterable<int>? ids]) async {
|
||||||
return getCollection(
|
final results = await getCollection(
|
||||||
"/api/storage_paths/?page=1&page_size=100000",
|
"/api/storage_paths/?page=1&page_size=100000",
|
||||||
StoragePath.fromJson,
|
StoragePath.fromJson,
|
||||||
ErrorCode.storagePathLoadFailed,
|
ErrorCode.storagePathLoadFailed,
|
||||||
client: client,
|
client: client,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return results
|
||||||
|
.where((element) => ids?.contains(element.id) ?? true)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import 'package:paperless_api/src/models/saved_view_model.dart';
|
import 'package:paperless_api/src/models/saved_view_model.dart';
|
||||||
|
|
||||||
abstract class PaperlessSavedViewsApi {
|
abstract class PaperlessSavedViewsApi {
|
||||||
Future<List<SavedView>> getAll();
|
Future<SavedView> find(int id);
|
||||||
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]);
|
||||||
|
|
||||||
Future<SavedView> save(SavedView view);
|
Future<SavedView> save(SavedView view);
|
||||||
Future<int> delete(SavedView view);
|
Future<int> delete(SavedView view);
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
|
|||||||
PaperlessSavedViewsApiImpl(this.client);
|
PaperlessSavedViewsApiImpl(this.client);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<SavedView>> getAll() {
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
|
||||||
return getCollection(
|
final result = await getCollection(
|
||||||
"/api/saved_views/",
|
"/api/saved_views/",
|
||||||
SavedView.fromJson,
|
SavedView.fromJson,
|
||||||
ErrorCode.loadSavedViewsError,
|
ErrorCode.loadSavedViewsError,
|
||||||
client: client,
|
client: client,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return result.where((view) => ids?.contains(view.id!) ?? true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -51,4 +53,14 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
|
|||||||
httpStatusCode: response.statusCode,
|
httpStatusCode: response.statusCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SavedView> find(int id) {
|
||||||
|
return getSingleResult(
|
||||||
|
"/api/saved_views/$id/",
|
||||||
|
SavedView.fromJson,
|
||||||
|
ErrorCode.loadSavedViewsError,
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1147,12 +1147,12 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.5"
|
version: "1.4.5"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: rxdart
|
name: rxdart
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.4"
|
version: "0.27.7"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ dependencies:
|
|||||||
paperless_api:
|
paperless_api:
|
||||||
path: packages/paperless_api
|
path: packages/paperless_api
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
|
rxdart: ^0.27.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user