mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2026-01-31 10:25:03 -06:00
fix: Add labels to each cubit using repositories and state properties, remove label cubits
This commit is contained in:
@@ -1,34 +0,0 @@
|
|||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:rxdart/subjects.dart';
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Base repository class which all repositories should implement
|
|
||||||
///
|
|
||||||
abstract class BaseRepository<T> extends Cubit<IndexedRepositoryState<T>>
|
|
||||||
with HydratedMixin {
|
|
||||||
final IndexedRepositoryState<T> _initialState;
|
|
||||||
|
|
||||||
BaseRepository(this._initialState) : super(_initialState) {
|
|
||||||
hydrate();
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream<IndexedRepositoryState<T>?> get values =>
|
|
||||||
BehaviorSubject.seeded(state)..addStream(super.stream);
|
|
||||||
|
|
||||||
IndexedRepositoryState<T>? get current => state;
|
|
||||||
|
|
||||||
bool get isInitialized => state.hasLoaded;
|
|
||||||
|
|
||||||
Future<T> create(T object);
|
|
||||||
Future<T?> find(int id);
|
|
||||||
Future<Iterable<T>> findAll([Iterable<int>? ids]);
|
|
||||||
Future<T> update(T object);
|
|
||||||
Future<int> delete(T object);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> clear() async {
|
|
||||||
await super.clear();
|
|
||||||
emit(_initialState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
|
|
||||||
class CorrespondentRepositoryImpl extends LabelRepository<Correspondent> {
|
|
||||||
final PaperlessLabelsApi _api;
|
|
||||||
|
|
||||||
CorrespondentRepositoryImpl(this._api)
|
|
||||||
: super(const CorrespondentRepositoryState());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Correspondent> create(Correspondent correspondent) async {
|
|
||||||
final created = await _api.saveCorrespondent(correspondent);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..putIfAbsent(created.id!, () => created);
|
|
||||||
emit(CorrespondentRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(Correspondent correspondent) async {
|
|
||||||
await _api.deleteCorrespondent(correspondent);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..removeWhere((k, v) => k == correspondent.id);
|
|
||||||
emit(CorrespondentRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return correspondent.id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Correspondent?> find(int id) async {
|
|
||||||
final correspondent = await _api.getCorrespondent(id);
|
|
||||||
if (correspondent != null) {
|
|
||||||
final updatedState = {...state.values ?? {}}..[id] = correspondent;
|
|
||||||
emit(CorrespondentRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return correspondent;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Iterable<Correspondent>> findAll([Iterable<int>? ids]) async {
|
|
||||||
final correspondents = await _api.getCorrespondents(ids);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..addEntries(correspondents.map((e) => MapEntry(e.id!, e)));
|
|
||||||
emit(CorrespondentRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return correspondents;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Correspondent> update(Correspondent correspondent) async {
|
|
||||||
final updated = await _api.updateCorrespondent(correspondent);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..update(updated.id!, (_) => updated);
|
|
||||||
emit(CorrespondentRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
CorrespondentRepositoryState fromJson(Map<String, dynamic> json) {
|
|
||||||
return CorrespondentRepositoryState.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson(covariant CorrespondentRepositoryState state) {
|
|
||||||
return state.toJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
|
|
||||||
class DocumentTypeRepositoryImpl extends LabelRepository<DocumentType> {
|
|
||||||
final PaperlessLabelsApi _api;
|
|
||||||
|
|
||||||
DocumentTypeRepositoryImpl(this._api)
|
|
||||||
: super(const DocumentTypeRepositoryState());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<DocumentType> create(DocumentType documentType) async {
|
|
||||||
final created = await _api.saveDocumentType(documentType);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..putIfAbsent(created.id!, () => created);
|
|
||||||
emit(DocumentTypeRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(DocumentType documentType) async {
|
|
||||||
await _api.deleteDocumentType(documentType);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..removeWhere((k, v) => k == documentType.id);
|
|
||||||
emit(DocumentTypeRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return documentType.id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<DocumentType?> find(int id) async {
|
|
||||||
final documentType = await _api.getDocumentType(id);
|
|
||||||
if (documentType != null) {
|
|
||||||
final updatedState = {...state.values ?? {}}..[id] = documentType;
|
|
||||||
emit(DocumentTypeRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return documentType;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Iterable<DocumentType>> findAll([Iterable<int>? ids]) async {
|
|
||||||
final documentTypes = await _api.getDocumentTypes(ids);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..addEntries(documentTypes.map((e) => MapEntry(e.id!, e)));
|
|
||||||
emit(DocumentTypeRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return documentTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<DocumentType> update(DocumentType documentType) async {
|
|
||||||
final updated = await _api.updateDocumentType(documentType);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..update(updated.id!, (_) => updated);
|
|
||||||
emit(DocumentTypeRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
DocumentTypeRepositoryState fromJson(Map<String, dynamic> json) {
|
|
||||||
return DocumentTypeRepositoryState.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson(covariant DocumentTypeRepositoryState state) {
|
|
||||||
return state.toJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/saved_view_repository_state.dart';
|
|
||||||
|
|
||||||
class SavedViewRepositoryImpl extends SavedViewRepository {
|
|
||||||
final PaperlessSavedViewsApi _api;
|
|
||||||
|
|
||||||
SavedViewRepositoryImpl(this._api) : super(const SavedViewRepositoryState());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<SavedView> create(SavedView object) async {
|
|
||||||
final created = await _api.save(object);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..putIfAbsent(created.id!, () => created);
|
|
||||||
emit(SavedViewRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(SavedView view) async {
|
|
||||||
await _api.delete(view);
|
|
||||||
final updatedState = {...state.values ?? {}}..remove(view.id);
|
|
||||||
emit(SavedViewRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return view.id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<SavedView?> find(int id) async {
|
|
||||||
final found = await _api.find(id);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..update(id, (_) => found, ifAbsent: () => found);
|
|
||||||
emit(SavedViewRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
|
|
||||||
final found = await _api.findAll(ids);
|
|
||||||
final updatedState = {
|
|
||||||
...state.values ?? {},
|
|
||||||
...{for (final view in found) view.id!: view},
|
|
||||||
};
|
|
||||||
emit(SavedViewRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<SavedView> update(SavedView object) {
|
|
||||||
throw UnimplementedError(
|
|
||||||
"Saved view update is not yet implemented as it is not supported by the API.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
SavedViewRepositoryState fromJson(Map<String, dynamic> json) {
|
|
||||||
return SavedViewRepositoryState.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson(covariant SavedViewRepositoryState state) {
|
|
||||||
return state.toJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:rxdart/rxdart.dart' show BehaviorSubject;
|
|
||||||
|
|
||||||
class StoragePathRepositoryImpl extends LabelRepository<StoragePath> {
|
|
||||||
final PaperlessLabelsApi _api;
|
|
||||||
|
|
||||||
StoragePathRepositoryImpl(this._api)
|
|
||||||
: super(const StoragePathRepositoryState());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<StoragePath> create(StoragePath storagePath) async {
|
|
||||||
final created = await _api.saveStoragePath(storagePath);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..putIfAbsent(created.id!, () => created);
|
|
||||||
emit(StoragePathRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(StoragePath storagePath) async {
|
|
||||||
await _api.deleteStoragePath(storagePath);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..removeWhere((k, v) => k == storagePath.id);
|
|
||||||
emit(StoragePathRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return storagePath.id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<StoragePath?> find(int id) async {
|
|
||||||
final storagePath = await _api.getStoragePath(id);
|
|
||||||
if (storagePath != null) {
|
|
||||||
final updatedState = {...state.values ?? {}}..[id] = storagePath;
|
|
||||||
emit(StoragePathRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return storagePath;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Iterable<StoragePath>> findAll([Iterable<int>? ids]) async {
|
|
||||||
final storagePaths = await _api.getStoragePaths(ids);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..addEntries(storagePaths.map((e) => MapEntry(e.id!, e)));
|
|
||||||
emit(StoragePathRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return storagePaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<StoragePath> update(StoragePath storagePath) async {
|
|
||||||
final updated = await _api.updateStoragePath(storagePath);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..update(updated.id!, (_) => updated);
|
|
||||||
emit(StoragePathRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
StoragePathRepositoryState fromJson(Map<String, dynamic> json) {
|
|
||||||
return StoragePathRepositoryState.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson(covariant StoragePathRepositoryState state) {
|
|
||||||
return state.toJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
|
||||||
|
|
||||||
class TagRepositoryImpl extends LabelRepository<Tag> {
|
|
||||||
final PaperlessLabelsApi _api;
|
|
||||||
|
|
||||||
TagRepositoryImpl(this._api) : super(const TagRepositoryState());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Tag> create(Tag object) async {
|
|
||||||
final created = await _api.saveTag(object);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..putIfAbsent(created.id!, () => created);
|
|
||||||
emit(TagRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> delete(Tag tag) async {
|
|
||||||
await _api.deleteTag(tag);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..removeWhere((k, v) => k == tag.id);
|
|
||||||
emit(TagRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return tag.id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Tag?> find(int id) async {
|
|
||||||
final tag = await _api.getTag(id);
|
|
||||||
if (tag != null) {
|
|
||||||
final updatedState = {...state.values ?? {}}..[id] = tag;
|
|
||||||
emit(TagRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Iterable<Tag>> findAll([Iterable<int>? ids]) async {
|
|
||||||
final tags = await _api.getTags(ids);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..addEntries(tags.map((e) => MapEntry(e.id!, e)));
|
|
||||||
emit(TagRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Tag> update(Tag tag) async {
|
|
||||||
final updated = await _api.updateTag(tag);
|
|
||||||
final updatedState = {...state.values ?? {}}
|
|
||||||
..update(updated.id!, (_) => updated);
|
|
||||||
emit(TagRepositoryState(values: updatedState, hasLoaded: true));
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TagRepositoryState? fromJson(Map<String, dynamic> json) {
|
|
||||||
return TagRepositoryState.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic>? toJson(covariant TagRepositoryState state) {
|
|
||||||
return state.toJson();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,218 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'dart:async';
|
||||||
import 'package:paperless_mobile/core/repository/base_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
|
|
||||||
abstract class LabelRepository<T extends Label> extends BaseRepository<T> {
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
LabelRepository(IndexedRepositoryState<T> initial) : super(initial);
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository_state.dart';
|
||||||
|
|
||||||
|
class LabelRepository extends HydratedCubit<LabelRepositoryState> {
|
||||||
|
final PaperlessLabelsApi _api;
|
||||||
|
final Map<Object, StreamSubscription> _subscribers = {};
|
||||||
|
|
||||||
|
void subscribe(
|
||||||
|
Object source, {
|
||||||
|
required void Function(LabelRepositoryState) onChanged,
|
||||||
|
}) {
|
||||||
|
_subscribers.putIfAbsent(source, () {
|
||||||
|
onChanged(state);
|
||||||
|
return stream.listen((event) => onChanged(event));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsubscribe(Object source) async {
|
||||||
|
await _subscribers[source]?.cancel();
|
||||||
|
_subscribers.remove(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelRepository(this._api) : super(const LabelRepositoryState());
|
||||||
|
|
||||||
|
Future<Tag> createTag(Tag object) async {
|
||||||
|
final created = await _api.saveTag(object);
|
||||||
|
final updatedState = {...state.tags}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
emit(state.copyWith(tags: updatedState));
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteTag(Tag tag) async {
|
||||||
|
await _api.deleteTag(tag);
|
||||||
|
final updatedState = {...state.tags}..removeWhere((k, v) => k == tag.id);
|
||||||
|
emit(state.copyWith(tags: updatedState));
|
||||||
|
return tag.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tag?> findTag(int id) async {
|
||||||
|
final tag = await _api.getTag(id);
|
||||||
|
if (tag != null) {
|
||||||
|
final updatedState = {...state.tags}..[id] = tag;
|
||||||
|
emit(state.copyWith(tags: updatedState));
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<Tag>> findAllTags([Iterable<int>? ids]) async {
|
||||||
|
final tags = await _api.getTags(ids);
|
||||||
|
final updatedState = {...state.tags}
|
||||||
|
..addEntries(tags.map((e) => MapEntry(e.id!, e)));
|
||||||
|
emit(state.copyWith(tags: updatedState));
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tag> updateTag(Tag tag) async {
|
||||||
|
final updated = await _api.updateTag(tag);
|
||||||
|
final updatedState = {...state.tags}..update(updated.id!, (_) => updated);
|
||||||
|
emit(state.copyWith(tags: updatedState));
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Correspondent> createCorrespondent(Correspondent correspondent) async {
|
||||||
|
final created = await _api.saveCorrespondent(correspondent);
|
||||||
|
final updatedState = {...state.correspondents}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
emit(state.copyWith(correspondents: updatedState));
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteCorrespondent(Correspondent correspondent) async {
|
||||||
|
await _api.deleteCorrespondent(correspondent);
|
||||||
|
final updatedState = {...state.correspondents}
|
||||||
|
..removeWhere((k, v) => k == correspondent.id);
|
||||||
|
emit(state.copyWith(correspondents: updatedState));
|
||||||
|
|
||||||
|
return correspondent.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Correspondent?> findCorrespondent(int id) async {
|
||||||
|
final correspondent = await _api.getCorrespondent(id);
|
||||||
|
if (correspondent != null) {
|
||||||
|
final updatedState = {...state.correspondents}..[id] = correspondent;
|
||||||
|
emit(state.copyWith(correspondents: updatedState));
|
||||||
|
|
||||||
|
return correspondent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<Correspondent>> findAllCorrespondents(
|
||||||
|
[Iterable<int>? ids]) async {
|
||||||
|
final correspondents = await _api.getCorrespondents(ids);
|
||||||
|
final updatedState = {...state.correspondents}
|
||||||
|
..addEntries(correspondents.map((e) => MapEntry(e.id!, e)));
|
||||||
|
emit(state.copyWith(correspondents: updatedState));
|
||||||
|
|
||||||
|
return correspondents;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Correspondent> updateCorrespondent(Correspondent correspondent) async {
|
||||||
|
final updated = await _api.updateCorrespondent(correspondent);
|
||||||
|
final updatedState = {...state.correspondents}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
emit(state.copyWith(correspondents: updatedState));
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DocumentType> createDocumentType(DocumentType documentType) async {
|
||||||
|
final created = await _api.saveDocumentType(documentType);
|
||||||
|
final updatedState = {...state.documentTypes}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
emit(state.copyWith(documentTypes: updatedState));
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteDocumentType(DocumentType documentType) async {
|
||||||
|
await _api.deleteDocumentType(documentType);
|
||||||
|
final updatedState = {...state.documentTypes}
|
||||||
|
..removeWhere((k, v) => k == documentType.id);
|
||||||
|
emit(state.copyWith(documentTypes: updatedState));
|
||||||
|
return documentType.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DocumentType?> findDocumentType(int id) async {
|
||||||
|
final documentType = await _api.getDocumentType(id);
|
||||||
|
if (documentType != null) {
|
||||||
|
final updatedState = {...state.documentTypes}..[id] = documentType;
|
||||||
|
emit(state.copyWith(documentTypes: updatedState));
|
||||||
|
return documentType;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<DocumentType>> findAllDocumentTypes(
|
||||||
|
[Iterable<int>? ids]) async {
|
||||||
|
final documentTypes = await _api.getDocumentTypes(ids);
|
||||||
|
final updatedState = {...state.documentTypes}
|
||||||
|
..addEntries(documentTypes.map((e) => MapEntry(e.id!, e)));
|
||||||
|
emit(state.copyWith(documentTypes: updatedState));
|
||||||
|
return documentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DocumentType> updateDocumentType(DocumentType documentType) async {
|
||||||
|
final updated = await _api.updateDocumentType(documentType);
|
||||||
|
final updatedState = {...state.documentTypes}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
emit(state.copyWith(documentTypes: updatedState));
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StoragePath> createStoragePath(StoragePath storagePath) async {
|
||||||
|
final created = await _api.saveStoragePath(storagePath);
|
||||||
|
final updatedState = {...state.storagePaths}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
emit(state.copyWith(storagePaths: updatedState));
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteStoragePath(StoragePath storagePath) async {
|
||||||
|
await _api.deleteStoragePath(storagePath);
|
||||||
|
final updatedState = {...state.storagePaths}
|
||||||
|
..removeWhere((k, v) => k == storagePath.id);
|
||||||
|
emit(state.copyWith(storagePaths: updatedState));
|
||||||
|
return storagePath.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StoragePath?> findStoragePath(int id) async {
|
||||||
|
final storagePath = await _api.getStoragePath(id);
|
||||||
|
if (storagePath != null) {
|
||||||
|
final updatedState = {...state.storagePaths}..[id] = storagePath;
|
||||||
|
emit(state.copyWith(storagePaths: updatedState));
|
||||||
|
return storagePath;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<StoragePath>> findAllStoragePaths(
|
||||||
|
[Iterable<int>? ids]) async {
|
||||||
|
final storagePaths = await _api.getStoragePaths(ids);
|
||||||
|
final updatedState = {...state.storagePaths}
|
||||||
|
..addEntries(storagePaths.map((e) => MapEntry(e.id!, e)));
|
||||||
|
emit(state.copyWith(storagePaths: updatedState));
|
||||||
|
return storagePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StoragePath> updateStoragePath(StoragePath storagePath) async {
|
||||||
|
final updated = await _api.updateStoragePath(storagePath);
|
||||||
|
final updatedState = {...state.storagePaths}
|
||||||
|
..update(updated.id!, (_) => updated);
|
||||||
|
emit(state.copyWith(storagePaths: updatedState));
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_subscribers.forEach((key, subscription) {
|
||||||
|
subscription.cancel();
|
||||||
|
});
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
LabelRepositoryState? fromJson(Map<String, dynamic> json) {
|
||||||
|
return LabelRepositoryState.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic>? toJson(LabelRepositoryState state) {
|
||||||
|
return state.toJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
part 'label_repository_state.freezed.dart';
|
||||||
|
part 'label_repository_state.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class LabelRepositoryState with _$LabelRepositoryState {
|
||||||
|
const factory LabelRepositoryState({
|
||||||
|
@Default({}) Map<int, Correspondent> correspondents,
|
||||||
|
@Default({}) Map<int, DocumentType> documentTypes,
|
||||||
|
@Default({}) Map<int, Tag> tags,
|
||||||
|
@Default({}) Map<int, StoragePath> storagePaths,
|
||||||
|
}) = _LabelRepositoryState;
|
||||||
|
|
||||||
|
factory LabelRepositoryState.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$LabelRepositoryStateFromJson(json);
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'label_repository_state.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
LabelRepositoryState _$LabelRepositoryStateFromJson(Map<String, dynamic> json) {
|
||||||
|
return _LabelRepositoryState.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$LabelRepositoryState {
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$LabelRepositoryStateCopyWith<LabelRepositoryState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $LabelRepositoryStateCopyWith<$Res> {
|
||||||
|
factory $LabelRepositoryStateCopyWith(LabelRepositoryState value,
|
||||||
|
$Res Function(LabelRepositoryState) then) =
|
||||||
|
_$LabelRepositoryStateCopyWithImpl<$Res, LabelRepositoryState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$LabelRepositoryStateCopyWithImpl<$Res,
|
||||||
|
$Val extends LabelRepositoryState>
|
||||||
|
implements $LabelRepositoryStateCopyWith<$Res> {
|
||||||
|
_$LabelRepositoryStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_LabelRepositoryStateCopyWith<$Res>
|
||||||
|
implements $LabelRepositoryStateCopyWith<$Res> {
|
||||||
|
factory _$$_LabelRepositoryStateCopyWith(_$_LabelRepositoryState value,
|
||||||
|
$Res Function(_$_LabelRepositoryState) then) =
|
||||||
|
__$$_LabelRepositoryStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_LabelRepositoryStateCopyWithImpl<$Res>
|
||||||
|
extends _$LabelRepositoryStateCopyWithImpl<$Res, _$_LabelRepositoryState>
|
||||||
|
implements _$$_LabelRepositoryStateCopyWith<$Res> {
|
||||||
|
__$$_LabelRepositoryStateCopyWithImpl(_$_LabelRepositoryState _value,
|
||||||
|
$Res Function(_$_LabelRepositoryState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_LabelRepositoryState(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$_LabelRepositoryState implements _LabelRepositoryState {
|
||||||
|
const _$_LabelRepositoryState(
|
||||||
|
{final Map<int, Correspondent> correspondents = const {},
|
||||||
|
final Map<int, DocumentType> documentTypes = const {},
|
||||||
|
final Map<int, Tag> tags = const {},
|
||||||
|
final Map<int, StoragePath> storagePaths = const {}})
|
||||||
|
: _correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_tags = tags,
|
||||||
|
_storagePaths = storagePaths;
|
||||||
|
|
||||||
|
factory _$_LabelRepositoryState.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$_LabelRepositoryStateFromJson(json);
|
||||||
|
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LabelRepositoryState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_LabelRepositoryState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_tags),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_LabelRepositoryStateCopyWith<_$_LabelRepositoryState> get copyWith =>
|
||||||
|
__$$_LabelRepositoryStateCopyWithImpl<_$_LabelRepositoryState>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$_LabelRepositoryStateToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _LabelRepositoryState implements LabelRepositoryState {
|
||||||
|
const factory _LabelRepositoryState(
|
||||||
|
{final Map<int, Correspondent> correspondents,
|
||||||
|
final Map<int, DocumentType> documentTypes,
|
||||||
|
final Map<int, Tag> tags,
|
||||||
|
final Map<int, StoragePath> storagePaths}) = _$_LabelRepositoryState;
|
||||||
|
|
||||||
|
factory _LabelRepositoryState.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$_LabelRepositoryState.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_LabelRepositoryStateCopyWith<_$_LabelRepositoryState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,35 +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/core/repository/impl/document_type_repository_impl.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
|
||||||
|
|
||||||
class LabelRepositoriesProvider extends StatelessWidget {
|
|
||||||
final Widget child;
|
|
||||||
const LabelRepositoriesProvider({super.key, required this.child});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MultiRepositoryProvider(
|
|
||||||
providers: [
|
|
||||||
RepositoryProvider(
|
|
||||||
create: (context) => context.read<LabelRepository<Correspondent>>(),
|
|
||||||
),
|
|
||||||
RepositoryProvider(
|
|
||||||
create: (context) => context.read<LabelRepository<DocumentType>>(),
|
|
||||||
),
|
|
||||||
RepositoryProvider(
|
|
||||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
|
||||||
),
|
|
||||||
RepositoryProvider(
|
|
||||||
create: (context) => context.read<LabelRepository<Tag>>(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,80 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'dart:async';
|
||||||
import 'package:paperless_mobile/core/repository/base_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/saved_view_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
|
|
||||||
abstract class SavedViewRepository extends BaseRepository<SavedView> {
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
SavedViewRepository(super.initialState);
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository_state.dart';
|
||||||
|
|
||||||
|
class SavedViewRepository extends HydratedCubit<SavedViewRepositoryState> {
|
||||||
|
final PaperlessSavedViewsApi _api;
|
||||||
|
final Map<Object, StreamSubscription> _subscribers = {};
|
||||||
|
|
||||||
|
void subscribe(
|
||||||
|
Object source,
|
||||||
|
void Function(Map<int, SavedView>) onChanged,
|
||||||
|
) {
|
||||||
|
_subscribers.putIfAbsent(source, () {
|
||||||
|
onChanged(state.savedViews);
|
||||||
|
return stream.listen((event) => onChanged(event.savedViews));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsubscribe(Object source) async {
|
||||||
|
await _subscribers[source]?.cancel();
|
||||||
|
_subscribers.remove(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedViewRepository(this._api) : super(const SavedViewRepositoryState());
|
||||||
|
|
||||||
|
Future<SavedView> create(SavedView object) async {
|
||||||
|
final created = await _api.save(object);
|
||||||
|
final updatedState = {...state.savedViews}
|
||||||
|
..putIfAbsent(created.id!, () => created);
|
||||||
|
emit(state.copyWith(savedViews: updatedState));
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> delete(SavedView view) async {
|
||||||
|
await _api.delete(view);
|
||||||
|
final updatedState = {...state.savedViews}..remove(view.id);
|
||||||
|
emit(state.copyWith(savedViews: updatedState));
|
||||||
|
return view.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SavedView?> find(int id) async {
|
||||||
|
final found = await _api.find(id);
|
||||||
|
if (found != null) {
|
||||||
|
final updatedState = {...state.savedViews}
|
||||||
|
..update(id, (_) => found, ifAbsent: () => found);
|
||||||
|
emit(state.copyWith(savedViews: updatedState));
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]) async {
|
||||||
|
final found = await _api.findAll(ids);
|
||||||
|
final updatedState = {
|
||||||
|
...state.savedViews,
|
||||||
|
...{for (final view in found) view.id!: view},
|
||||||
|
};
|
||||||
|
emit(state.copyWith(savedViews: updatedState));
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_subscribers.forEach((key, subscription) {
|
||||||
|
subscription.cancel();
|
||||||
|
});
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
SavedViewRepositoryState? fromJson(Map<String, dynamic> json) {
|
||||||
|
return SavedViewRepositoryState.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic>? toJson(SavedViewRepositoryState state) {
|
||||||
|
return state.toJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
part 'saved_view_repository_state.freezed.dart';
|
||||||
|
part 'saved_view_repository_state.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SavedViewRepositoryState with _$SavedViewRepositoryState {
|
||||||
|
const factory SavedViewRepositoryState({
|
||||||
|
@Default({}) Map<int, SavedView> savedViews,
|
||||||
|
}) = _SavedViewRepositoryState;
|
||||||
|
|
||||||
|
factory SavedViewRepositoryState.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SavedViewRepositoryStateFromJson(json);
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'saved_view_repository_state.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
SavedViewRepositoryState _$SavedViewRepositoryStateFromJson(
|
||||||
|
Map<String, dynamic> json) {
|
||||||
|
return _SavedViewRepositoryState.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SavedViewRepositoryState {
|
||||||
|
Map<int, SavedView> get savedViews => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$SavedViewRepositoryStateCopyWith<SavedViewRepositoryState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SavedViewRepositoryStateCopyWith<$Res> {
|
||||||
|
factory $SavedViewRepositoryStateCopyWith(SavedViewRepositoryState value,
|
||||||
|
$Res Function(SavedViewRepositoryState) then) =
|
||||||
|
_$SavedViewRepositoryStateCopyWithImpl<$Res, SavedViewRepositoryState>;
|
||||||
|
@useResult
|
||||||
|
$Res call({Map<int, SavedView> savedViews});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SavedViewRepositoryStateCopyWithImpl<$Res,
|
||||||
|
$Val extends SavedViewRepositoryState>
|
||||||
|
implements $SavedViewRepositoryStateCopyWith<$Res> {
|
||||||
|
_$SavedViewRepositoryStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? savedViews = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
savedViews: null == savedViews
|
||||||
|
? _value.savedViews
|
||||||
|
: savedViews // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, SavedView>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_SavedViewRepositoryStateCopyWith<$Res>
|
||||||
|
implements $SavedViewRepositoryStateCopyWith<$Res> {
|
||||||
|
factory _$$_SavedViewRepositoryStateCopyWith(
|
||||||
|
_$_SavedViewRepositoryState value,
|
||||||
|
$Res Function(_$_SavedViewRepositoryState) then) =
|
||||||
|
__$$_SavedViewRepositoryStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({Map<int, SavedView> savedViews});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_SavedViewRepositoryStateCopyWithImpl<$Res>
|
||||||
|
extends _$SavedViewRepositoryStateCopyWithImpl<$Res,
|
||||||
|
_$_SavedViewRepositoryState>
|
||||||
|
implements _$$_SavedViewRepositoryStateCopyWith<$Res> {
|
||||||
|
__$$_SavedViewRepositoryStateCopyWithImpl(_$_SavedViewRepositoryState _value,
|
||||||
|
$Res Function(_$_SavedViewRepositoryState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? savedViews = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_SavedViewRepositoryState(
|
||||||
|
savedViews: null == savedViews
|
||||||
|
? _value._savedViews
|
||||||
|
: savedViews // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, SavedView>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$_SavedViewRepositoryState implements _SavedViewRepositoryState {
|
||||||
|
const _$_SavedViewRepositoryState(
|
||||||
|
{final Map<int, SavedView> savedViews = const {}})
|
||||||
|
: _savedViews = savedViews;
|
||||||
|
|
||||||
|
factory _$_SavedViewRepositoryState.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$_SavedViewRepositoryStateFromJson(json);
|
||||||
|
|
||||||
|
final Map<int, SavedView> _savedViews;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, SavedView> get savedViews {
|
||||||
|
if (_savedViews is EqualUnmodifiableMapView) return _savedViews;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_savedViews);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SavedViewRepositoryState(savedViews: $savedViews)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_SavedViewRepositoryState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._savedViews, _savedViews));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType, const DeepCollectionEquality().hash(_savedViews));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_SavedViewRepositoryStateCopyWith<_$_SavedViewRepositoryState>
|
||||||
|
get copyWith => __$$_SavedViewRepositoryStateCopyWithImpl<
|
||||||
|
_$_SavedViewRepositoryState>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$_SavedViewRepositoryStateToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SavedViewRepositoryState implements SavedViewRepositoryState {
|
||||||
|
const factory _SavedViewRepositoryState(
|
||||||
|
{final Map<int, SavedView> savedViews}) = _$_SavedViewRepositoryState;
|
||||||
|
|
||||||
|
factory _SavedViewRepositoryState.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$_SavedViewRepositoryState.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, SavedView> get savedViews;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_SavedViewRepositoryStateCopyWith<_$_SavedViewRepositoryState>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
|
|
||||||
part 'correspondent_repository_state.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class CorrespondentRepositoryState
|
|
||||||
extends IndexedRepositoryState<Correspondent> {
|
|
||||||
const CorrespondentRepositoryState({
|
|
||||||
super.values = const {},
|
|
||||||
super.hasLoaded,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
CorrespondentRepositoryState copyWith({
|
|
||||||
Map<int, Correspondent>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
}) {
|
|
||||||
return CorrespondentRepositoryState(
|
|
||||||
values: values ?? this.values,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory CorrespondentRepositoryState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$CorrespondentRepositoryStateFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$CorrespondentRepositoryStateToJson(this);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'document_type_repository_state.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class DocumentTypeRepositoryState extends IndexedRepositoryState<DocumentType> {
|
|
||||||
const DocumentTypeRepositoryState({
|
|
||||||
super.values = const {},
|
|
||||||
super.hasLoaded,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
DocumentTypeRepositoryState copyWith({
|
|
||||||
Map<int, DocumentType>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
}) {
|
|
||||||
return DocumentTypeRepositoryState(
|
|
||||||
values: values ?? this.values,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory DocumentTypeRepositoryState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$DocumentTypeRepositoryStateFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$DocumentTypeRepositoryStateToJson(this);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'saved_view_repository_state.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class SavedViewRepositoryState extends IndexedRepositoryState<SavedView> {
|
|
||||||
const SavedViewRepositoryState({
|
|
||||||
super.values = const {},
|
|
||||||
super.hasLoaded = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
SavedViewRepositoryState copyWith({
|
|
||||||
Map<int, SavedView>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
}) {
|
|
||||||
return SavedViewRepositoryState(
|
|
||||||
values: values ?? this.values,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory SavedViewRepositoryState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$SavedViewRepositoryStateFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$SavedViewRepositoryStateToJson(this);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'storage_path_repository_state.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class StoragePathRepositoryState extends IndexedRepositoryState<StoragePath> {
|
|
||||||
const StoragePathRepositoryState({
|
|
||||||
super.values = const {},
|
|
||||||
super.hasLoaded = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
StoragePathRepositoryState copyWith({
|
|
||||||
Map<int, StoragePath>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
}) {
|
|
||||||
return StoragePathRepositoryState(
|
|
||||||
values: values ?? this.values,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory StoragePathRepositoryState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$StoragePathRepositoryStateFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$StoragePathRepositoryStateToJson(this);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
|
|
||||||
part 'tag_repository_state.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class TagRepositoryState extends IndexedRepositoryState<Tag> {
|
|
||||||
const TagRepositoryState({
|
|
||||||
super.values = const {},
|
|
||||||
super.hasLoaded = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
TagRepositoryState copyWith({
|
|
||||||
Map<int, Tag>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
}) {
|
|
||||||
return TagRepositoryState(
|
|
||||||
values: values ?? this.values,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory TagRepositoryState.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$TagRepositoryStateFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$TagRepositoryStateToJson(this);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
abstract class IndexedRepositoryState<T> {
|
|
||||||
final Map<int, T>? values;
|
|
||||||
final bool hasLoaded;
|
|
||||||
|
|
||||||
const IndexedRepositoryState({
|
|
||||||
required this.values,
|
|
||||||
this.hasLoaded = false,
|
|
||||||
}) : assert(!(values == null) || !hasLoaded);
|
|
||||||
|
|
||||||
IndexedRepositoryState.loaded(this.values) : hasLoaded = true;
|
|
||||||
|
|
||||||
IndexedRepositoryState<T> copyWith({
|
|
||||||
Map<int, T>? values,
|
|
||||||
bool? hasLoaded,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -6,45 +6,28 @@ import 'package:equatable/equatable.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'document_bulk_action_state.dart';
|
part 'document_bulk_action_state.dart';
|
||||||
|
part 'document_bulk_action_cubit.freezed.dart';
|
||||||
|
|
||||||
class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
||||||
final PaperlessDocumentsApi _documentsApi;
|
final PaperlessDocumentsApi _documentsApi;
|
||||||
final LabelRepository<Correspondent> _correspondentRepository;
|
final LabelRepository _labelRepository;
|
||||||
final LabelRepository<DocumentType> _documentTypeRepository;
|
|
||||||
final LabelRepository<Tag> _tagRepository;
|
|
||||||
final LabelRepository<StoragePath> _storagePathRepository;
|
|
||||||
final DocumentChangedNotifier _notifier;
|
final DocumentChangedNotifier _notifier;
|
||||||
|
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
|
||||||
|
|
||||||
DocumentBulkActionCubit(
|
DocumentBulkActionCubit(
|
||||||
this._documentsApi,
|
this._documentsApi,
|
||||||
this._correspondentRepository,
|
this._labelRepository,
|
||||||
this._documentTypeRepository,
|
|
||||||
this._tagRepository,
|
|
||||||
this._storagePathRepository,
|
|
||||||
this._notifier, {
|
this._notifier, {
|
||||||
required List<DocumentModel> selection,
|
required List<DocumentModel> selection,
|
||||||
}) : super(
|
}) : super(
|
||||||
DocumentBulkActionState(
|
DocumentBulkActionState(
|
||||||
selection: selection,
|
selection: selection,
|
||||||
correspondentOptions:
|
correspondents: _labelRepository.state.correspondents,
|
||||||
(_correspondentRepository.current?.hasLoaded ?? false)
|
documentTypes: _labelRepository.state.documentTypes,
|
||||||
? _correspondentRepository.current!.values!
|
storagePaths: _labelRepository.state.storagePaths,
|
||||||
: {},
|
tags: _labelRepository.state.tags,
|
||||||
tagOptions: (_tagRepository.current?.hasLoaded ?? false)
|
|
||||||
? _tagRepository.current!.values!
|
|
||||||
: {},
|
|
||||||
documentTypeOptions:
|
|
||||||
(_documentTypeRepository.current?.hasLoaded ?? false)
|
|
||||||
? _documentTypeRepository.current!.values!
|
|
||||||
: {},
|
|
||||||
storagePathOptions:
|
|
||||||
(_storagePathRepository.current?.hasLoaded ?? false)
|
|
||||||
? _storagePathRepository.current!.values!
|
|
||||||
: {},
|
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_notifier.subscribe(
|
_notifier.subscribe(
|
||||||
@@ -60,35 +43,18 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_subscriptions.add(
|
_labelRepository.subscribe(
|
||||||
_tagRepository.values.listen((event) {
|
this,
|
||||||
if (event?.hasLoaded ?? false) {
|
onChanged: (labels) {
|
||||||
emit(state.copyWith(tagOptions: event!.values));
|
emit(
|
||||||
}
|
state.copyWith(
|
||||||
}),
|
correspondents: labels.correspondents,
|
||||||
);
|
documentTypes: labels.documentTypes,
|
||||||
_subscriptions.add(
|
storagePaths: labels.storagePaths,
|
||||||
_correspondentRepository.values.listen((event) {
|
tags: labels.tags,
|
||||||
if (event?.hasLoaded ?? false) {
|
),
|
||||||
emit(state.copyWith(
|
);
|
||||||
correspondentOptions: event!.values,
|
},
|
||||||
));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_documentTypeRepository.values.listen((event) {
|
|
||||||
if (event?.hasLoaded ?? false) {
|
|
||||||
emit(state.copyWith(documentTypeOptions: event!.values));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_storagePathRepository.values.listen((event) {
|
|
||||||
if (event?.hasLoaded ?? false) {
|
|
||||||
emit(state.copyWith(storagePathOptions: event!.values));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,9 +143,7 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
|||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_notifier.unsubscribe(this);
|
_notifier.unsubscribe(this);
|
||||||
for (final sub in _subscriptions) {
|
_labelRepository.unsubscribe(this);
|
||||||
sub.cancel();
|
|
||||||
}
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'document_bulk_action_cubit.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$DocumentBulkActionState {
|
||||||
|
List<DocumentModel> get selection => throw _privateConstructorUsedError;
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$DocumentBulkActionStateCopyWith<DocumentBulkActionState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DocumentBulkActionStateCopyWith<$Res> {
|
||||||
|
factory $DocumentBulkActionStateCopyWith(DocumentBulkActionState value,
|
||||||
|
$Res Function(DocumentBulkActionState) then) =
|
||||||
|
_$DocumentBulkActionStateCopyWithImpl<$Res, DocumentBulkActionState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{List<DocumentModel> selection,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DocumentBulkActionStateCopyWithImpl<$Res,
|
||||||
|
$Val extends DocumentBulkActionState>
|
||||||
|
implements $DocumentBulkActionStateCopyWith<$Res> {
|
||||||
|
_$DocumentBulkActionStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? selection = null,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
selection: null == selection
|
||||||
|
? _value.selection
|
||||||
|
: selection // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<DocumentModel>,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_DocumentBulkActionStateCopyWith<$Res>
|
||||||
|
implements $DocumentBulkActionStateCopyWith<$Res> {
|
||||||
|
factory _$$_DocumentBulkActionStateCopyWith(_$_DocumentBulkActionState value,
|
||||||
|
$Res Function(_$_DocumentBulkActionState) then) =
|
||||||
|
__$$_DocumentBulkActionStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{List<DocumentModel> selection,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_DocumentBulkActionStateCopyWithImpl<$Res>
|
||||||
|
extends _$DocumentBulkActionStateCopyWithImpl<$Res,
|
||||||
|
_$_DocumentBulkActionState>
|
||||||
|
implements _$$_DocumentBulkActionStateCopyWith<$Res> {
|
||||||
|
__$$_DocumentBulkActionStateCopyWithImpl(_$_DocumentBulkActionState _value,
|
||||||
|
$Res Function(_$_DocumentBulkActionState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? selection = null,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_DocumentBulkActionState(
|
||||||
|
selection: null == selection
|
||||||
|
? _value._selection
|
||||||
|
: selection // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<DocumentModel>,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$_DocumentBulkActionState extends _DocumentBulkActionState {
|
||||||
|
const _$_DocumentBulkActionState(
|
||||||
|
{required final List<DocumentModel> selection,
|
||||||
|
required final Map<int, Correspondent> correspondents,
|
||||||
|
required final Map<int, DocumentType> documentTypes,
|
||||||
|
required final Map<int, Tag> tags,
|
||||||
|
required final Map<int, StoragePath> storagePaths})
|
||||||
|
: _selection = selection,
|
||||||
|
_correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_tags = tags,
|
||||||
|
_storagePaths = storagePaths,
|
||||||
|
super._();
|
||||||
|
|
||||||
|
final List<DocumentModel> _selection;
|
||||||
|
@override
|
||||||
|
List<DocumentModel> get selection {
|
||||||
|
if (_selection is EqualUnmodifiableListView) return _selection;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DocumentBulkActionState(selection: $selection, correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_DocumentBulkActionState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._selection, _selection) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(_selection),
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_tags),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_DocumentBulkActionStateCopyWith<_$_DocumentBulkActionState>
|
||||||
|
get copyWith =>
|
||||||
|
__$$_DocumentBulkActionStateCopyWithImpl<_$_DocumentBulkActionState>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _DocumentBulkActionState extends DocumentBulkActionState {
|
||||||
|
const factory _DocumentBulkActionState(
|
||||||
|
{required final List<DocumentModel> selection,
|
||||||
|
required final Map<int, Correspondent> correspondents,
|
||||||
|
required final Map<int, DocumentType> documentTypes,
|
||||||
|
required final Map<int, Tag> tags,
|
||||||
|
required final Map<int, StoragePath> storagePaths}) =
|
||||||
|
_$_DocumentBulkActionState;
|
||||||
|
const _DocumentBulkActionState._() : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<DocumentModel> get selection;
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_DocumentBulkActionStateCopyWith<_$_DocumentBulkActionState>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,44 +1,15 @@
|
|||||||
part of 'document_bulk_action_cubit.dart';
|
part of 'document_bulk_action_cubit.dart';
|
||||||
|
|
||||||
class DocumentBulkActionState extends Equatable {
|
@freezed
|
||||||
final List<DocumentModel> selection;
|
class DocumentBulkActionState with _$DocumentBulkActionState {
|
||||||
final Map<int, Correspondent> correspondentOptions;
|
const DocumentBulkActionState._();
|
||||||
final Map<int, DocumentType> documentTypeOptions;
|
const factory DocumentBulkActionState({
|
||||||
final Map<int, Tag> tagOptions;
|
required List<DocumentModel> selection,
|
||||||
final Map<int, StoragePath> storagePathOptions;
|
required Map<int, Correspondent> correspondents,
|
||||||
|
required Map<int, DocumentType> documentTypes,
|
||||||
const DocumentBulkActionState({
|
required Map<int, Tag> tags,
|
||||||
this.correspondentOptions = const {},
|
required Map<int, StoragePath> storagePaths,
|
||||||
this.documentTypeOptions = const {},
|
}) = _DocumentBulkActionState;
|
||||||
this.tagOptions = const {},
|
|
||||||
this.storagePathOptions = const {},
|
|
||||||
this.selection = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [
|
|
||||||
selection,
|
|
||||||
correspondentOptions,
|
|
||||||
documentTypeOptions,
|
|
||||||
tagOptions,
|
|
||||||
storagePathOptions,
|
|
||||||
];
|
|
||||||
|
|
||||||
Iterable<int> get selectedIds => selection.map((d) => d.id);
|
Iterable<int> get selectedIds => selection.map((d) => d.id);
|
||||||
|
|
||||||
DocumentBulkActionState copyWith({
|
|
||||||
List<DocumentModel>? selection,
|
|
||||||
Map<int, Correspondent>? correspondentOptions,
|
|
||||||
Map<int, DocumentType>? documentTypeOptions,
|
|
||||||
Map<int, Tag>? tagOptions,
|
|
||||||
Map<int, StoragePath>? storagePathOptions,
|
|
||||||
}) {
|
|
||||||
return DocumentBulkActionState(
|
|
||||||
selection: selection ?? this.selection,
|
|
||||||
correspondentOptions: correspondentOptions ?? this.correspondentOptions,
|
|
||||||
documentTypeOptions: documentTypeOptions ?? this.documentTypeOptions,
|
|
||||||
storagePathOptions: storagePathOptions ?? this.storagePathOptions,
|
|
||||||
tagOptions: tagOptions ?? this.tagOptions,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
|
||||||
|
class BulkEditPage<int, T extends Label> extends StatefulWidget {
|
||||||
|
final bool enableMultipleChoice;
|
||||||
|
final Map<int, T> availableOptions;
|
||||||
|
|
||||||
|
const BulkEditPage({
|
||||||
|
super.key,
|
||||||
|
required this.enableMultipleChoice,
|
||||||
|
required this.availableOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BulkEditPage> createState() => _BulkEditPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BulkEditPageState extends State<BulkEditPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,11 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package: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:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart';
|
import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
class BulkEditTagsBottomSheet extends StatefulWidget {
|
class BulkEditTagsBottomSheet extends StatefulWidget {
|
||||||
@@ -20,29 +19,42 @@ class BulkEditTagsBottomSheet extends StatefulWidget {
|
|||||||
|
|
||||||
class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
List<int> _tagsToRemove = [];
|
final _textEditingController = TextEditingController();
|
||||||
List<int> _tagsToAdd = [];
|
late Set<int> _sharedTags;
|
||||||
|
late Set<int> _nonSharedTags;
|
||||||
|
final Set<int> _sharedTagsToRemove = {};
|
||||||
|
final Set<int> _nonSharedTagsToRemove = {};
|
||||||
|
final Set<int> _tagsToAdd = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final state = context.read<DocumentBulkActionCubit>().state;
|
||||||
|
_sharedTags = state.selection
|
||||||
|
.map((doc) => doc.tags)
|
||||||
|
.reduce((previousValue, element) =>
|
||||||
|
previousValue.toSet().intersection(element.toSet()))
|
||||||
|
.toSet();
|
||||||
|
print(_sharedTags.map((e) => e).join(", "));
|
||||||
|
_nonSharedTags = state.selection
|
||||||
|
.map((doc) => doc.tags)
|
||||||
|
.flattened
|
||||||
|
.toSet()
|
||||||
|
.difference(_sharedTags)
|
||||||
|
.toSet();
|
||||||
|
print(_nonSharedTags.map((e) => e).join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
return BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final sharedTags = state.selection
|
|
||||||
.map((doc) => doc.tags)
|
|
||||||
.reduce((previousValue, element) =>
|
|
||||||
previousValue.toSet().intersection(element.toSet()))
|
|
||||||
.toList();
|
|
||||||
final nonSharedTags = state.selection
|
|
||||||
.map((doc) => doc.tags)
|
|
||||||
.flattened
|
|
||||||
.toSet()
|
|
||||||
.difference(sharedTags.toSet())
|
|
||||||
.toList();
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
child: BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
child: BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
print(state);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@@ -54,25 +66,123 @@ class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
|||||||
"Bulk modify tags",
|
"Bulk modify tags",
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
).paddedOnly(bottom: 24),
|
).paddedOnly(bottom: 24),
|
||||||
FormBuilder(
|
TypeAheadFormField<Tag>(
|
||||||
key: _formKey,
|
textFieldConfiguration: TextFieldConfiguration(
|
||||||
child: TagFormField(
|
controller: _textEditingController,
|
||||||
initialValue: IdsTagsQuery(
|
decoration: const InputDecoration(
|
||||||
sharedTags.map((tag) => IncludeTagIdQuery(tag)),
|
labelText: "Tags",
|
||||||
|
hintText: "Start typing to add tags...",
|
||||||
),
|
),
|
||||||
name: "labelFormField",
|
|
||||||
selectableOptions: state.tagOptions,
|
|
||||||
allowCreation: false,
|
|
||||||
anyAssignedSelectable: false,
|
|
||||||
excludeAllowed: false,
|
|
||||||
),
|
),
|
||||||
|
onSuggestionSelected: (suggestion) {
|
||||||
|
setState(() {
|
||||||
|
_tagsToAdd.add(suggestion.id!);
|
||||||
|
});
|
||||||
|
_textEditingController.clear();
|
||||||
|
},
|
||||||
|
itemBuilder: (context, option) {
|
||||||
|
return ListTile(
|
||||||
|
leading: SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: option.color!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(option.name),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
suggestionsCallback: (pattern) {
|
||||||
|
final searchString = pattern.toLowerCase();
|
||||||
|
return state.tags.entries
|
||||||
|
.where(
|
||||||
|
(tag) => tag.value.name
|
||||||
|
.toLowerCase()
|
||||||
|
.contains(searchString),
|
||||||
|
)
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toSet()
|
||||||
|
.difference(_sharedTags)
|
||||||
|
.difference(_nonSharedTags)
|
||||||
|
.map((e) => state.tags[e]!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text("Shared tags"),
|
||||||
|
Wrap(
|
||||||
|
children: _sharedTags
|
||||||
|
.map(
|
||||||
|
(tag) => RemovableTagWidget(
|
||||||
|
tag: state.tags[tag]!,
|
||||||
|
onDeleted: (tag) {
|
||||||
|
setState(() {
|
||||||
|
_sharedTagsToRemove.add(tag);
|
||||||
|
_sharedTags.remove(tag);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text("Tags removed after apply"),
|
Text("Non-shared tags"),
|
||||||
Wrap(),
|
Wrap(
|
||||||
|
children: _nonSharedTags
|
||||||
|
.map(
|
||||||
|
(tag) => RemovableTagWidget(
|
||||||
|
tag: state.tags[tag]!,
|
||||||
|
onDeleted: (tag) {
|
||||||
|
setState(() {
|
||||||
|
_nonSharedTagsToRemove.add(tag);
|
||||||
|
_nonSharedTags.remove(tag);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
Text("Remove"),
|
||||||
|
Wrap(
|
||||||
|
children: _sharedTagsToRemove.map((tag) {
|
||||||
|
return RemovableTagWidget(
|
||||||
|
tag: state.tags[tag]!,
|
||||||
|
onDeleted: (tag) {
|
||||||
|
setState(() {
|
||||||
|
_sharedTagsToRemove.remove(tag);
|
||||||
|
_sharedTags.add(tag);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList() +
|
||||||
|
_nonSharedTagsToRemove.map((tag) {
|
||||||
|
return RemovableTagWidget(
|
||||||
|
tag: state.tags[tag]!,
|
||||||
|
onDeleted: (tag) {
|
||||||
|
setState(() {
|
||||||
|
_nonSharedTagsToRemove.remove(tag);
|
||||||
|
_nonSharedTags.add(tag);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text("Tags added after apply"),
|
Text("Add"),
|
||||||
Wrap(),
|
Wrap(
|
||||||
|
children: _tagsToAdd
|
||||||
|
.map(
|
||||||
|
(tag) => RemovableTagWidget(
|
||||||
|
tag: state.tags[tag]!,
|
||||||
|
onDeleted: (tag) {
|
||||||
|
setState(() {
|
||||||
|
_tagsToAdd.remove(tag);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
@@ -107,3 +217,28 @@ class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RemovableTagWidget extends StatelessWidget {
|
||||||
|
final Tag tag;
|
||||||
|
final void Function(int tagId) onDeleted;
|
||||||
|
const RemovableTagWidget(
|
||||||
|
{super.key, required this.tag, required this.onDeleted});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Chip(
|
||||||
|
label: Text(
|
||||||
|
tag.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: tag.textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleted: () => onDeleted(tag.id!),
|
||||||
|
deleteIcon: Icon(Icons.clear),
|
||||||
|
backgroundColor: tag.color,
|
||||||
|
deleteIconColor: tag.textColor,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
side: BorderSide.none,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
// import 'package:flutter/src/widgets/placeholder.dart';
|
||||||
|
|
||||||
|
// class LabelBulkSelectionWidget extends StatelessWidget {
|
||||||
|
// final int labelId;
|
||||||
|
// final String title;
|
||||||
|
// final bool selected;
|
||||||
|
// final bool excluded;
|
||||||
|
// final Widget Function(int id) leadingWidgetBuilder;
|
||||||
|
// final void Function(int id) onSelected;
|
||||||
|
// final void Function(int id) onUnselected;
|
||||||
|
// final void Function(int id) onRemoved;
|
||||||
|
|
||||||
|
// const LabelBulkSelectionWidget({
|
||||||
|
// super.key,
|
||||||
|
// required this.labelId,
|
||||||
|
// required this.title,
|
||||||
|
// required this.leadingWidgetBuilder,
|
||||||
|
// required this.onSelected,
|
||||||
|
// required this.onUnselected,
|
||||||
|
// required this.onRemoved,
|
||||||
|
// });
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return ListTile(
|
||||||
|
// title: Text(title),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -2,32 +2,47 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:open_filex/open_filex.dart';
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_description.dart';
|
import 'package:paperless_mobile/core/service/file_description.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||||
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
|
import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
part 'document_details_cubit.freezed.dart';
|
||||||
part 'document_details_state.dart';
|
part 'document_details_state.dart';
|
||||||
|
|
||||||
class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
||||||
final PaperlessDocumentsApi _api;
|
final PaperlessDocumentsApi _api;
|
||||||
final DocumentChangedNotifier _notifier;
|
final DocumentChangedNotifier _notifier;
|
||||||
final LocalNotificationService _notificationService;
|
final LocalNotificationService _notificationService;
|
||||||
|
final LabelRepository _labelRepository;
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
DocumentDetailsCubit(
|
DocumentDetailsCubit(
|
||||||
this._api,
|
this._api,
|
||||||
|
this._labelRepository,
|
||||||
this._notifier,
|
this._notifier,
|
||||||
this._notificationService, {
|
this._notificationService, {
|
||||||
required DocumentModel initialDocument,
|
required DocumentModel initialDocument,
|
||||||
}) : super(DocumentDetailsState(document: initialDocument)) {
|
}) : super(DocumentDetailsState(
|
||||||
|
document: initialDocument,
|
||||||
|
)) {
|
||||||
_notifier.subscribe(this, onUpdated: replace);
|
_notifier.subscribe(this, onUpdated: replace);
|
||||||
|
_labelRepository.subscribe(
|
||||||
|
this,
|
||||||
|
onChanged: (labels) => emit(
|
||||||
|
state.copyWith(
|
||||||
|
correspondents: labels.correspondents,
|
||||||
|
documentTypes: labels.documentTypes,
|
||||||
|
tags: labels.tags,
|
||||||
|
storagePaths: labels.storagePaths,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
loadSuggestions();
|
loadSuggestions();
|
||||||
loadMetaData();
|
loadMetaData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,350 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'document_details_cubit.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$DocumentDetailsState {
|
||||||
|
DocumentModel get document => throw _privateConstructorUsedError;
|
||||||
|
DocumentMetaData? get metaData => throw _privateConstructorUsedError;
|
||||||
|
bool get isFullContentLoaded => throw _privateConstructorUsedError;
|
||||||
|
String? get fullContent => throw _privateConstructorUsedError;
|
||||||
|
FieldSuggestions? get suggestions => throw _privateConstructorUsedError;
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$DocumentDetailsStateCopyWith<DocumentDetailsState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DocumentDetailsStateCopyWith<$Res> {
|
||||||
|
factory $DocumentDetailsStateCopyWith(DocumentDetailsState value,
|
||||||
|
$Res Function(DocumentDetailsState) then) =
|
||||||
|
_$DocumentDetailsStateCopyWithImpl<$Res, DocumentDetailsState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{DocumentModel document,
|
||||||
|
DocumentMetaData? metaData,
|
||||||
|
bool isFullContentLoaded,
|
||||||
|
String? fullContent,
|
||||||
|
FieldSuggestions? suggestions,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DocumentDetailsStateCopyWithImpl<$Res,
|
||||||
|
$Val extends DocumentDetailsState>
|
||||||
|
implements $DocumentDetailsStateCopyWith<$Res> {
|
||||||
|
_$DocumentDetailsStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? document = null,
|
||||||
|
Object? metaData = freezed,
|
||||||
|
Object? isFullContentLoaded = null,
|
||||||
|
Object? fullContent = freezed,
|
||||||
|
Object? suggestions = freezed,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
document: null == document
|
||||||
|
? _value.document
|
||||||
|
: document // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentModel,
|
||||||
|
metaData: freezed == metaData
|
||||||
|
? _value.metaData
|
||||||
|
: metaData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentMetaData?,
|
||||||
|
isFullContentLoaded: null == isFullContentLoaded
|
||||||
|
? _value.isFullContentLoaded
|
||||||
|
: isFullContentLoaded // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
fullContent: freezed == fullContent
|
||||||
|
? _value.fullContent
|
||||||
|
: fullContent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
suggestions: freezed == suggestions
|
||||||
|
? _value.suggestions
|
||||||
|
: suggestions // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FieldSuggestions?,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_DocumentDetailsStateCopyWith<$Res>
|
||||||
|
implements $DocumentDetailsStateCopyWith<$Res> {
|
||||||
|
factory _$$_DocumentDetailsStateCopyWith(_$_DocumentDetailsState value,
|
||||||
|
$Res Function(_$_DocumentDetailsState) then) =
|
||||||
|
__$$_DocumentDetailsStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{DocumentModel document,
|
||||||
|
DocumentMetaData? metaData,
|
||||||
|
bool isFullContentLoaded,
|
||||||
|
String? fullContent,
|
||||||
|
FieldSuggestions? suggestions,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_DocumentDetailsStateCopyWithImpl<$Res>
|
||||||
|
extends _$DocumentDetailsStateCopyWithImpl<$Res, _$_DocumentDetailsState>
|
||||||
|
implements _$$_DocumentDetailsStateCopyWith<$Res> {
|
||||||
|
__$$_DocumentDetailsStateCopyWithImpl(_$_DocumentDetailsState _value,
|
||||||
|
$Res Function(_$_DocumentDetailsState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? document = null,
|
||||||
|
Object? metaData = freezed,
|
||||||
|
Object? isFullContentLoaded = null,
|
||||||
|
Object? fullContent = freezed,
|
||||||
|
Object? suggestions = freezed,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_DocumentDetailsState(
|
||||||
|
document: null == document
|
||||||
|
? _value.document
|
||||||
|
: document // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentModel,
|
||||||
|
metaData: freezed == metaData
|
||||||
|
? _value.metaData
|
||||||
|
: metaData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentMetaData?,
|
||||||
|
isFullContentLoaded: null == isFullContentLoaded
|
||||||
|
? _value.isFullContentLoaded
|
||||||
|
: isFullContentLoaded // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
fullContent: freezed == fullContent
|
||||||
|
? _value.fullContent
|
||||||
|
: fullContent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
suggestions: freezed == suggestions
|
||||||
|
? _value.suggestions
|
||||||
|
: suggestions // ignore: cast_nullable_to_non_nullable
|
||||||
|
as FieldSuggestions?,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$_DocumentDetailsState implements _DocumentDetailsState {
|
||||||
|
const _$_DocumentDetailsState(
|
||||||
|
{required this.document,
|
||||||
|
this.metaData,
|
||||||
|
this.isFullContentLoaded = false,
|
||||||
|
this.fullContent,
|
||||||
|
this.suggestions,
|
||||||
|
final Map<int, Correspondent> correspondents = const {},
|
||||||
|
final Map<int, DocumentType> documentTypes = const {},
|
||||||
|
final Map<int, Tag> tags = const {},
|
||||||
|
final Map<int, StoragePath> storagePaths = const {}})
|
||||||
|
: _correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_tags = tags,
|
||||||
|
_storagePaths = storagePaths;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DocumentModel document;
|
||||||
|
@override
|
||||||
|
final DocumentMetaData? metaData;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFullContentLoaded;
|
||||||
|
@override
|
||||||
|
final String? fullContent;
|
||||||
|
@override
|
||||||
|
final FieldSuggestions? suggestions;
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DocumentDetailsState(document: $document, metaData: $metaData, isFullContentLoaded: $isFullContentLoaded, fullContent: $fullContent, suggestions: $suggestions, correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_DocumentDetailsState &&
|
||||||
|
(identical(other.document, document) ||
|
||||||
|
other.document == document) &&
|
||||||
|
(identical(other.metaData, metaData) ||
|
||||||
|
other.metaData == metaData) &&
|
||||||
|
(identical(other.isFullContentLoaded, isFullContentLoaded) ||
|
||||||
|
other.isFullContentLoaded == isFullContentLoaded) &&
|
||||||
|
(identical(other.fullContent, fullContent) ||
|
||||||
|
other.fullContent == fullContent) &&
|
||||||
|
(identical(other.suggestions, suggestions) ||
|
||||||
|
other.suggestions == suggestions) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
document,
|
||||||
|
metaData,
|
||||||
|
isFullContentLoaded,
|
||||||
|
fullContent,
|
||||||
|
suggestions,
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_tags),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_DocumentDetailsStateCopyWith<_$_DocumentDetailsState> get copyWith =>
|
||||||
|
__$$_DocumentDetailsStateCopyWithImpl<_$_DocumentDetailsState>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _DocumentDetailsState implements DocumentDetailsState {
|
||||||
|
const factory _DocumentDetailsState(
|
||||||
|
{required final DocumentModel document,
|
||||||
|
final DocumentMetaData? metaData,
|
||||||
|
final bool isFullContentLoaded,
|
||||||
|
final String? fullContent,
|
||||||
|
final FieldSuggestions? suggestions,
|
||||||
|
final Map<int, Correspondent> correspondents,
|
||||||
|
final Map<int, DocumentType> documentTypes,
|
||||||
|
final Map<int, Tag> tags,
|
||||||
|
final Map<int, StoragePath> storagePaths}) = _$_DocumentDetailsState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DocumentModel get document;
|
||||||
|
@override
|
||||||
|
DocumentMetaData? get metaData;
|
||||||
|
@override
|
||||||
|
bool get isFullContentLoaded;
|
||||||
|
@override
|
||||||
|
String? get fullContent;
|
||||||
|
@override
|
||||||
|
FieldSuggestions? get suggestions;
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_DocumentDetailsStateCopyWith<_$_DocumentDetailsState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,42 +1,16 @@
|
|||||||
part of 'document_details_cubit.dart';
|
part of 'document_details_cubit.dart';
|
||||||
|
|
||||||
class DocumentDetailsState with EquatableMixin {
|
@freezed
|
||||||
final DocumentModel document;
|
class DocumentDetailsState with _$DocumentDetailsState {
|
||||||
final DocumentMetaData? metaData;
|
const factory DocumentDetailsState({
|
||||||
final bool isFullContentLoaded;
|
required DocumentModel document,
|
||||||
final String? fullContent;
|
|
||||||
final FieldSuggestions suggestions;
|
|
||||||
|
|
||||||
const DocumentDetailsState({
|
|
||||||
required this.document,
|
|
||||||
this.metaData,
|
|
||||||
this.suggestions = const FieldSuggestions(),
|
|
||||||
this.isFullContentLoaded = false,
|
|
||||||
this.fullContent,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [
|
|
||||||
document,
|
|
||||||
suggestions,
|
|
||||||
isFullContentLoaded,
|
|
||||||
fullContent,
|
|
||||||
metaData,
|
|
||||||
];
|
|
||||||
|
|
||||||
DocumentDetailsState copyWith({
|
|
||||||
DocumentModel? document,
|
|
||||||
FieldSuggestions? suggestions,
|
|
||||||
bool? isFullContentLoaded,
|
|
||||||
String? fullContent,
|
|
||||||
DocumentMetaData? metaData,
|
DocumentMetaData? metaData,
|
||||||
}) {
|
@Default(false) bool isFullContentLoaded,
|
||||||
return DocumentDetailsState(
|
String? fullContent,
|
||||||
document: document ?? this.document,
|
FieldSuggestions? suggestions,
|
||||||
suggestions: suggestions ?? this.suggestions,
|
@Default({}) Map<int, Correspondent> correspondents,
|
||||||
isFullContentLoaded: isFullContentLoaded ?? this.isFullContentLoaded,
|
@Default({}) Map<int, DocumentType> documentTypes,
|
||||||
fullContent: fullContent ?? this.fullContent,
|
@Default({}) Map<int, Tag> tags,
|
||||||
metaData: metaData ?? this.metaData,
|
@Default({}) Map<int, StoragePath> storagePaths,
|
||||||
);
|
}) = _DocumentDetailsState;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
document: state.document,
|
document: state.document,
|
||||||
itemSpacing: _itemSpacing,
|
itemSpacing: _itemSpacing,
|
||||||
queryString: widget.titleAndContentQueryString,
|
queryString: widget.titleAndContentQueryString,
|
||||||
|
|
||||||
),
|
),
|
||||||
DocumentContentWidget(
|
DocumentContentWidget(
|
||||||
isFullContentLoaded: state.isFullContentLoaded,
|
isFullContentLoaded: state.isFullContentLoaded,
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
|||||||
|
|
||||||
class DocumentOverviewWidget extends StatelessWidget {
|
class DocumentOverviewWidget extends StatelessWidget {
|
||||||
final DocumentModel document;
|
final DocumentModel document;
|
||||||
|
final Map<int, Correspondent> availableCorrespondents;
|
||||||
|
final Map<int, DocumentType> availableDocumentTypes;
|
||||||
|
final Map<int, Tag> availableTags;
|
||||||
|
final Map<int, StoragePath> availableStoragePaths;
|
||||||
final String? queryString;
|
final String? queryString;
|
||||||
final double itemSpacing;
|
final double itemSpacing;
|
||||||
const DocumentOverviewWidget({
|
const DocumentOverviewWidget({
|
||||||
@@ -18,6 +22,10 @@ class DocumentOverviewWidget extends StatelessWidget {
|
|||||||
required this.document,
|
required this.document,
|
||||||
this.queryString,
|
this.queryString,
|
||||||
required this.itemSpacing,
|
required this.itemSpacing,
|
||||||
|
required this.availableCorrespondents,
|
||||||
|
required this.availableDocumentTypes,
|
||||||
|
required this.availableTags,
|
||||||
|
required this.availableStoragePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -47,7 +55,7 @@ class DocumentOverviewWidget extends StatelessWidget {
|
|||||||
label: S.of(context)!.documentType,
|
label: S.of(context)!.documentType,
|
||||||
content: LabelText<DocumentType>(
|
content: LabelText<DocumentType>(
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
id: document.documentType,
|
label: availableDocumentTypes[document.documentType],
|
||||||
),
|
),
|
||||||
).paddedOnly(bottom: itemSpacing),
|
).paddedOnly(bottom: itemSpacing),
|
||||||
),
|
),
|
||||||
@@ -57,7 +65,7 @@ class DocumentOverviewWidget extends StatelessWidget {
|
|||||||
label: S.of(context)!.correspondent,
|
label: S.of(context)!.correspondent,
|
||||||
content: LabelText<Correspondent>(
|
content: LabelText<Correspondent>(
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
id: document.correspondent,
|
label: availableCorrespondents[document.correspondent],
|
||||||
),
|
),
|
||||||
).paddedOnly(bottom: itemSpacing),
|
).paddedOnly(bottom: itemSpacing),
|
||||||
),
|
),
|
||||||
@@ -65,8 +73,8 @@ class DocumentOverviewWidget extends StatelessWidget {
|
|||||||
visible: document.storagePath != null,
|
visible: document.storagePath != null,
|
||||||
child: DetailsItem(
|
child: DetailsItem(
|
||||||
label: S.of(context)!.storagePath,
|
label: S.of(context)!.storagePath,
|
||||||
content: StoragePathWidget(
|
content: LabelText<StoragePath>(
|
||||||
pathId: document.storagePath,
|
label: availableStoragePaths[document.storagePath],
|
||||||
),
|
),
|
||||||
).paddedOnly(bottom: itemSpacing),
|
).paddedOnly(bottom: itemSpacing),
|
||||||
),
|
),
|
||||||
@@ -78,7 +86,7 @@ class DocumentOverviewWidget extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: TagsWidget(
|
child: TagsWidget(
|
||||||
isClickable: false,
|
isClickable: false,
|
||||||
tagIds: document.tags,
|
tags: document.tags.map((e) => availableTags[e]!).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).paddedOnly(bottom: itemSpacing),
|
).paddedOnly(bottom: itemSpacing),
|
||||||
|
|||||||
@@ -1,66 +1,41 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
|
||||||
part 'document_edit_state.dart';
|
part 'document_edit_state.dart';
|
||||||
|
part 'document_edit_cubit.freezed.dart';
|
||||||
|
|
||||||
class DocumentEditCubit extends Cubit<DocumentEditState> {
|
class DocumentEditCubit extends Cubit<DocumentEditState> {
|
||||||
final DocumentModel _initialDocument;
|
final DocumentModel _initialDocument;
|
||||||
final PaperlessDocumentsApi _docsApi;
|
final PaperlessDocumentsApi _docsApi;
|
||||||
|
|
||||||
final DocumentChangedNotifier _notifier;
|
final DocumentChangedNotifier _notifier;
|
||||||
final LabelRepository<Correspondent> _correspondentRepository;
|
final LabelRepository _labelRepository;
|
||||||
final LabelRepository<DocumentType> _documentTypeRepository;
|
|
||||||
final LabelRepository<StoragePath> _storagePathRepository;
|
|
||||||
final LabelRepository<Tag> _tagRepository;
|
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
|
|
||||||
DocumentEditCubit(
|
DocumentEditCubit(
|
||||||
DocumentModel document, {
|
this._labelRepository,
|
||||||
required PaperlessDocumentsApi documentsApi,
|
this._docsApi,
|
||||||
required LabelRepository<Correspondent> correspondentRepository,
|
this._notifier, {
|
||||||
required LabelRepository<DocumentType> documentTypeRepository,
|
required DocumentModel document,
|
||||||
required LabelRepository<StoragePath> storagePathRepository,
|
|
||||||
required LabelRepository<Tag> tagRepository,
|
|
||||||
required DocumentChangedNotifier notifier,
|
|
||||||
}) : _initialDocument = document,
|
}) : _initialDocument = document,
|
||||||
_docsApi = documentsApi,
|
|
||||||
_correspondentRepository = correspondentRepository,
|
|
||||||
_documentTypeRepository = documentTypeRepository,
|
|
||||||
_storagePathRepository = storagePathRepository,
|
|
||||||
_tagRepository = tagRepository,
|
|
||||||
_notifier = notifier,
|
|
||||||
super(
|
super(
|
||||||
DocumentEditState(
|
DocumentEditState(
|
||||||
document: document,
|
document: document,
|
||||||
correspondents: correspondentRepository.current?.values ?? {},
|
correspondents: _labelRepository.state.correspondents,
|
||||||
documentTypes: documentTypeRepository.current?.values ?? {},
|
documentTypes: _labelRepository.state.documentTypes,
|
||||||
storagePaths: storagePathRepository.current?.values ?? {},
|
storagePaths: _labelRepository.state.storagePaths,
|
||||||
tags: tagRepository.current?.values ?? {},
|
tags: _labelRepository.state.tags,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_notifier.subscribe(this, onUpdated: replace);
|
_notifier.subscribe(this, onUpdated: replace);
|
||||||
_subscriptions.add(
|
_labelRepository.subscribe(
|
||||||
_correspondentRepository.values
|
this,
|
||||||
.listen((v) => emit(state.copyWith(correspondents: v?.values))),
|
onStateChanged: (labels) => emit(state.copyWith()),
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_documentTypeRepository.values
|
|
||||||
.listen((v) => emit(state.copyWith(documentTypes: v?.values))),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_storagePathRepository.values
|
|
||||||
.listen((v) => emit(state.copyWith(storagePaths: v?.values))),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_tagRepository.values.listen(
|
|
||||||
(v) => emit(state.copyWith(tags: v?.values)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,20 +45,20 @@ class DocumentEditCubit extends Cubit<DocumentEditState> {
|
|||||||
|
|
||||||
// Reload changed labels (documentCount property changes with removal/add)
|
// Reload changed labels (documentCount property changes with removal/add)
|
||||||
if (document.documentType != _initialDocument.documentType) {
|
if (document.documentType != _initialDocument.documentType) {
|
||||||
_documentTypeRepository
|
_labelRepository.findDocumentType(
|
||||||
.find((document.documentType ?? _initialDocument.documentType)!);
|
(document.documentType ?? _initialDocument.documentType)!);
|
||||||
}
|
}
|
||||||
if (document.correspondent != _initialDocument.correspondent) {
|
if (document.correspondent != _initialDocument.correspondent) {
|
||||||
_correspondentRepository
|
_labelRepository.findCorrespondent(
|
||||||
.find((document.correspondent ?? _initialDocument.correspondent)!);
|
(document.correspondent ?? _initialDocument.correspondent)!);
|
||||||
}
|
}
|
||||||
if (document.storagePath != _initialDocument.storagePath) {
|
if (document.storagePath != _initialDocument.storagePath) {
|
||||||
_storagePathRepository
|
_labelRepository.findStoragePath(
|
||||||
.find((document.storagePath ?? _initialDocument.storagePath)!);
|
(document.storagePath ?? _initialDocument.storagePath)!);
|
||||||
}
|
}
|
||||||
if (!const DeepCollectionEquality.unordered()
|
if (!const DeepCollectionEquality.unordered()
|
||||||
.equals(document.tags, _initialDocument.tags)) {
|
.equals(document.tags, _initialDocument.tags)) {
|
||||||
_tagRepository.findAll(document.tags);
|
_labelRepository.findAllTags(document.tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'document_edit_cubit.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$DocumentEditState {
|
||||||
|
DocumentModel get document => throw _privateConstructorUsedError;
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$DocumentEditStateCopyWith<DocumentEditState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DocumentEditStateCopyWith<$Res> {
|
||||||
|
factory $DocumentEditStateCopyWith(
|
||||||
|
DocumentEditState value, $Res Function(DocumentEditState) then) =
|
||||||
|
_$DocumentEditStateCopyWithImpl<$Res, DocumentEditState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{DocumentModel document,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, StoragePath> storagePaths,
|
||||||
|
Map<int, Tag> tags});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DocumentEditStateCopyWithImpl<$Res, $Val extends DocumentEditState>
|
||||||
|
implements $DocumentEditStateCopyWith<$Res> {
|
||||||
|
_$DocumentEditStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? document = null,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
Object? tags = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
document: null == document
|
||||||
|
? _value.document
|
||||||
|
: document // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentModel,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_DocumentEditStateCopyWith<$Res>
|
||||||
|
implements $DocumentEditStateCopyWith<$Res> {
|
||||||
|
factory _$$_DocumentEditStateCopyWith(_$_DocumentEditState value,
|
||||||
|
$Res Function(_$_DocumentEditState) then) =
|
||||||
|
__$$_DocumentEditStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{DocumentModel document,
|
||||||
|
Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, StoragePath> storagePaths,
|
||||||
|
Map<int, Tag> tags});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_DocumentEditStateCopyWithImpl<$Res>
|
||||||
|
extends _$DocumentEditStateCopyWithImpl<$Res, _$_DocumentEditState>
|
||||||
|
implements _$$_DocumentEditStateCopyWith<$Res> {
|
||||||
|
__$$_DocumentEditStateCopyWithImpl(
|
||||||
|
_$_DocumentEditState _value, $Res Function(_$_DocumentEditState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? document = null,
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
Object? tags = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_DocumentEditState(
|
||||||
|
document: null == document
|
||||||
|
? _value.document
|
||||||
|
: document // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DocumentModel,
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$_DocumentEditState implements _DocumentEditState {
|
||||||
|
const _$_DocumentEditState(
|
||||||
|
{required this.document,
|
||||||
|
required final Map<int, Correspondent> correspondents,
|
||||||
|
required final Map<int, DocumentType> documentTypes,
|
||||||
|
required final Map<int, StoragePath> storagePaths,
|
||||||
|
required final Map<int, Tag> tags})
|
||||||
|
: _correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_storagePaths = storagePaths,
|
||||||
|
_tags = tags;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DocumentModel document;
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DocumentEditState(document: $document, correspondents: $correspondents, documentTypes: $documentTypes, storagePaths: $storagePaths, tags: $tags)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_DocumentEditState &&
|
||||||
|
(identical(other.document, document) ||
|
||||||
|
other.document == document) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
document,
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths),
|
||||||
|
const DeepCollectionEquality().hash(_tags));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_DocumentEditStateCopyWith<_$_DocumentEditState> get copyWith =>
|
||||||
|
__$$_DocumentEditStateCopyWithImpl<_$_DocumentEditState>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _DocumentEditState implements DocumentEditState {
|
||||||
|
const factory _DocumentEditState(
|
||||||
|
{required final DocumentModel document,
|
||||||
|
required final Map<int, Correspondent> correspondents,
|
||||||
|
required final Map<int, DocumentType> documentTypes,
|
||||||
|
required final Map<int, StoragePath> storagePaths,
|
||||||
|
required final Map<int, Tag> tags}) = _$_DocumentEditState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DocumentModel get document;
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_DocumentEditStateCopyWith<_$_DocumentEditState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,43 +1,12 @@
|
|||||||
part of 'document_edit_cubit.dart';
|
part of 'document_edit_cubit.dart';
|
||||||
|
|
||||||
class DocumentEditState extends Equatable {
|
@freezed
|
||||||
final DocumentModel document;
|
class DocumentEditState with _$DocumentEditState {
|
||||||
|
const factory DocumentEditState({
|
||||||
final Map<int, Correspondent> correspondents;
|
required DocumentModel document,
|
||||||
final Map<int, DocumentType> documentTypes;
|
required Map<int, Correspondent> correspondents,
|
||||||
final Map<int, StoragePath> storagePaths;
|
required Map<int, DocumentType> documentTypes,
|
||||||
final Map<int, Tag> tags;
|
required Map<int, StoragePath> storagePaths,
|
||||||
|
required Map<int, Tag> tags,
|
||||||
const DocumentEditState({
|
}) = _DocumentEditState;
|
||||||
required this.correspondents,
|
|
||||||
required this.documentTypes,
|
|
||||||
required this.storagePaths,
|
|
||||||
required this.tags,
|
|
||||||
required this.document,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [
|
|
||||||
correspondents,
|
|
||||||
documentTypes,
|
|
||||||
storagePaths,
|
|
||||||
tags,
|
|
||||||
document,
|
|
||||||
];
|
|
||||||
|
|
||||||
DocumentEditState copyWith({
|
|
||||||
Map<int, Correspondent>? correspondents,
|
|
||||||
Map<int, DocumentType>? documentTypes,
|
|
||||||
Map<int, StoragePath>? storagePaths,
|
|
||||||
Map<int, Tag>? tags,
|
|
||||||
DocumentModel? document,
|
|
||||||
}) {
|
|
||||||
return DocumentEditState(
|
|
||||||
document: document ?? this.document,
|
|
||||||
correspondents: correspondents ?? this.correspondents,
|
|
||||||
documentTypes: documentTypes ?? this.documentTypes,
|
|
||||||
storagePaths: storagePaths ?? this.storagePaths,
|
|
||||||
tags: tags ?? this.tags,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,8 +194,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
LabelFormField<StoragePath>(
|
LabelFormField<StoragePath>(
|
||||||
notAssignedSelectable: false,
|
notAssignedSelectable: false,
|
||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider(
|
labelCreationWidgetBuilder: (initialValue) =>
|
||||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
RepositoryProvider.value(
|
||||||
|
value: context.read<LabelRepository>(),
|
||||||
child: AddStoragePathPage(initalName: initialValue),
|
child: AddStoragePathPage(initalName: initialValue),
|
||||||
),
|
),
|
||||||
textFieldLabel: S.of(context)!.storagePath,
|
textFieldLabel: S.of(context)!.storagePath,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
@@ -19,10 +20,18 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
|
|
||||||
|
final LabelRepository _labelRepository;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final DocumentChangedNotifier notifier;
|
final DocumentChangedNotifier notifier;
|
||||||
|
|
||||||
DocumentsCubit(this.api, this.notifier) : super(const DocumentsState()) {
|
DocumentsCubit(this.api, this.notifier, this._labelRepository)
|
||||||
|
: super(DocumentsState(
|
||||||
|
correspondents: _labelRepository.state.correspondents,
|
||||||
|
documentTypes: _labelRepository.state.documentTypes,
|
||||||
|
storagePaths: _labelRepository.state.storagePaths,
|
||||||
|
tags: _labelRepository.state.tags,
|
||||||
|
)) {
|
||||||
notifier.subscribe(
|
notifier.subscribe(
|
||||||
this,
|
this,
|
||||||
onUpdated: (document) {
|
onUpdated: (document) {
|
||||||
@@ -45,6 +54,17 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
_labelRepository.subscribe(
|
||||||
|
this,
|
||||||
|
onStateChanged: (labels) => emit(
|
||||||
|
state.copyWith(
|
||||||
|
correspondents: labels.correspondents,
|
||||||
|
documentTypes: labels.documentTypes,
|
||||||
|
storagePaths: labels.storagePaths,
|
||||||
|
tags: labels.tags,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> bulkDelete(List<DocumentModel> documents) async {
|
Future<void> bulkDelete(List<DocumentModel> documents) async {
|
||||||
@@ -101,6 +121,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
notifier.unsubscribe(this);
|
notifier.unsubscribe(this);
|
||||||
|
_labelRepository.unsubscribe(this);
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
part of 'documents_cubit.dart';
|
part of 'documents_cubit.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable(ignoreUnannotated: true)
|
||||||
class DocumentsState extends DocumentPagingState {
|
class DocumentsState extends DocumentPagingState {
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
final List<DocumentModel> selection;
|
final List<DocumentModel> selection;
|
||||||
|
|
||||||
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
final Map<int, StoragePath> storagePaths;
|
||||||
|
|
||||||
|
@JsonKey()
|
||||||
final ViewType viewType;
|
final ViewType viewType;
|
||||||
|
|
||||||
const DocumentsState({
|
const DocumentsState({
|
||||||
@@ -14,6 +19,10 @@ class DocumentsState extends DocumentPagingState {
|
|||||||
super.filter = const DocumentFilter(),
|
super.filter = const DocumentFilter(),
|
||||||
super.hasLoaded = false,
|
super.hasLoaded = false,
|
||||||
super.isLoading = false,
|
super.isLoading = false,
|
||||||
|
this.correspondents = const {},
|
||||||
|
this.documentTypes = const {},
|
||||||
|
this.tags = const {},
|
||||||
|
this.storagePaths = const {},
|
||||||
});
|
});
|
||||||
|
|
||||||
List<int> get selectedIds => selection.map((e) => e.id).toList();
|
List<int> get selectedIds => selection.map((e) => e.id).toList();
|
||||||
@@ -25,6 +34,10 @@ class DocumentsState extends DocumentPagingState {
|
|||||||
DocumentFilter? filter,
|
DocumentFilter? filter,
|
||||||
List<DocumentModel>? selection,
|
List<DocumentModel>? selection,
|
||||||
ViewType? viewType,
|
ViewType? viewType,
|
||||||
|
Map<int, Correspondent>? correspondents,
|
||||||
|
Map<int, DocumentType>? documentTypes,
|
||||||
|
Map<int, Tag>? tags,
|
||||||
|
Map<int, StoragePath>? storagePaths,
|
||||||
}) {
|
}) {
|
||||||
return DocumentsState(
|
return DocumentsState(
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
hasLoaded: hasLoaded ?? this.hasLoaded,
|
||||||
@@ -33,6 +46,10 @@ class DocumentsState extends DocumentPagingState {
|
|||||||
filter: filter ?? this.filter,
|
filter: filter ?? this.filter,
|
||||||
selection: selection ?? this.selection,
|
selection: selection ?? this.selection,
|
||||||
viewType: viewType ?? this.viewType,
|
viewType: viewType ?? this.viewType,
|
||||||
|
correspondents: correspondents ?? this.correspondents,
|
||||||
|
documentTypes: documentTypes ?? this.documentTypes,
|
||||||
|
tags: tags ?? this.tags,
|
||||||
|
storagePaths: storagePaths ?? this.storagePaths,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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/repository/provider/label_repositories_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/document_grid_loading_widget.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/document_grid_loading_widget.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_detailed_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/items/document_detailed_item.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_grid_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/items/document_grid_item.dart';
|
||||||
@@ -25,7 +24,13 @@ abstract class AdaptiveDocumentsView extends StatelessWidget {
|
|||||||
final void Function(int? id)? onDocumentTypeSelected;
|
final void Function(int? id)? onDocumentTypeSelected;
|
||||||
final void Function(int? id)? onStoragePathSelected;
|
final void Function(int? id)? onStoragePathSelected;
|
||||||
|
|
||||||
bool get showLoadingPlaceholder => (!hasLoaded && isLoading);
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
final Map<int, StoragePath> storagePaths;
|
||||||
|
|
||||||
|
bool get showLoadingPlaceholder => !hasLoaded && isLoading;
|
||||||
|
|
||||||
const AdaptiveDocumentsView({
|
const AdaptiveDocumentsView({
|
||||||
super.key,
|
super.key,
|
||||||
this.selectedDocumentIds = const [],
|
this.selectedDocumentIds = const [],
|
||||||
@@ -42,6 +47,10 @@ abstract class AdaptiveDocumentsView extends StatelessWidget {
|
|||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.hasLoaded,
|
required this.hasLoaded,
|
||||||
this.enableHeroAnimation = true,
|
this.enableHeroAnimation = true,
|
||||||
|
required this.correspondents,
|
||||||
|
required this.documentTypes,
|
||||||
|
required this.tags,
|
||||||
|
required this.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
AdaptiveDocumentsView.fromPagedState(
|
AdaptiveDocumentsView.fromPagedState(
|
||||||
@@ -58,6 +67,10 @@ abstract class AdaptiveDocumentsView extends StatelessWidget {
|
|||||||
required this.hasInternetConnection,
|
required this.hasInternetConnection,
|
||||||
this.viewType = ViewType.list,
|
this.viewType = ViewType.list,
|
||||||
this.selectedDocumentIds = const [],
|
this.selectedDocumentIds = const [],
|
||||||
|
required this.correspondents,
|
||||||
|
required this.documentTypes,
|
||||||
|
required this.tags,
|
||||||
|
required this.storagePaths,
|
||||||
}) : documents = state.documents,
|
}) : documents = state.documents,
|
||||||
isLoading = state.isLoading,
|
isLoading = state.isLoading,
|
||||||
hasLoaded = state.hasLoaded;
|
hasLoaded = state.hasLoaded;
|
||||||
@@ -80,6 +93,10 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
super.enableHeroAnimation,
|
super.enableHeroAnimation,
|
||||||
required super.isLoading,
|
required super.isLoading,
|
||||||
required super.hasLoaded,
|
required super.hasLoaded,
|
||||||
|
required super.correspondents,
|
||||||
|
required super.documentTypes,
|
||||||
|
required super.tags,
|
||||||
|
required super.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -96,27 +113,29 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
|
|
||||||
Widget _buildListView() {
|
Widget _buildListView() {
|
||||||
if (showLoadingPlaceholder) {
|
if (showLoadingPlaceholder) {
|
||||||
return DocumentsListLoadingWidget.sliver();
|
return const DocumentsListLoadingWidget.sliver();
|
||||||
}
|
}
|
||||||
return SliverList(
|
return SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
childCount: documents.length,
|
childCount: documents.length,
|
||||||
(context, index) {
|
(context, index) {
|
||||||
final document = documents.elementAt(index);
|
final document = documents.elementAt(index);
|
||||||
return LabelRepositoriesProvider(
|
return DocumentListItem(
|
||||||
child: DocumentListItem(
|
isLabelClickable: isLabelClickable,
|
||||||
isLabelClickable: isLabelClickable,
|
document: document,
|
||||||
document: document,
|
onTap: onTap,
|
||||||
onTap: onTap,
|
isSelected: selectedDocumentIds.contains(document.id),
|
||||||
isSelected: selectedDocumentIds.contains(document.id),
|
onSelected: onSelected,
|
||||||
onSelected: onSelected,
|
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
||||||
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
onTagSelected: onTagSelected,
|
||||||
onTagSelected: onTagSelected,
|
onCorrespondentSelected: onCorrespondentSelected,
|
||||||
onCorrespondentSelected: onCorrespondentSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
correspondents: correspondents,
|
||||||
),
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -133,21 +152,23 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
childCount: documents.length,
|
childCount: documents.length,
|
||||||
(context, index) {
|
(context, index) {
|
||||||
final document = documents.elementAt(index);
|
final document = documents.elementAt(index);
|
||||||
return LabelRepositoriesProvider(
|
return DocumentDetailedItem(
|
||||||
child: DocumentDetailedItem(
|
isLabelClickable: isLabelClickable,
|
||||||
isLabelClickable: isLabelClickable,
|
document: document,
|
||||||
document: document,
|
onTap: onTap,
|
||||||
onTap: onTap,
|
isSelected: selectedDocumentIds.contains(document.id),
|
||||||
isSelected: selectedDocumentIds.contains(document.id),
|
onSelected: onSelected,
|
||||||
onSelected: onSelected,
|
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
||||||
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
onTagSelected: onTagSelected,
|
||||||
onTagSelected: onTagSelected,
|
onCorrespondentSelected: onCorrespondentSelected,
|
||||||
onCorrespondentSelected: onCorrespondentSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
highlights: document.searchHit?.highlights,
|
||||||
highlights: document.searchHit?.highlights,
|
correspondents: correspondents,
|
||||||
),
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -180,6 +201,10 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
|
correspondents: correspondents,
|
||||||
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -205,6 +230,10 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
super.selectedDocumentIds,
|
super.selectedDocumentIds,
|
||||||
super.viewType,
|
super.viewType,
|
||||||
super.enableHeroAnimation = true,
|
super.enableHeroAnimation = true,
|
||||||
|
required super.correspondents,
|
||||||
|
required super.documentTypes,
|
||||||
|
required super.tags,
|
||||||
|
required super.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -231,20 +260,22 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
itemCount: documents.length,
|
itemCount: documents.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final document = documents.elementAt(index);
|
final document = documents.elementAt(index);
|
||||||
return LabelRepositoriesProvider(
|
return DocumentListItem(
|
||||||
child: DocumentListItem(
|
isLabelClickable: isLabelClickable,
|
||||||
isLabelClickable: isLabelClickable,
|
document: document,
|
||||||
document: document,
|
onTap: onTap,
|
||||||
onTap: onTap,
|
isSelected: selectedDocumentIds.contains(document.id),
|
||||||
isSelected: selectedDocumentIds.contains(document.id),
|
onSelected: onSelected,
|
||||||
onSelected: onSelected,
|
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
||||||
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
onTagSelected: onTagSelected,
|
||||||
onTagSelected: onTagSelected,
|
onCorrespondentSelected: onCorrespondentSelected,
|
||||||
onCorrespondentSelected: onCorrespondentSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
correspondents: correspondents,
|
||||||
),
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -252,7 +283,7 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
|
|
||||||
Widget _buildFullView() {
|
Widget _buildFullView() {
|
||||||
if (showLoadingPlaceholder) {
|
if (showLoadingPlaceholder) {
|
||||||
return DocumentsListLoadingWidget();
|
return const DocumentsListLoadingWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
@@ -263,20 +294,22 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
itemCount: documents.length,
|
itemCount: documents.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final document = documents.elementAt(index);
|
final document = documents.elementAt(index);
|
||||||
return LabelRepositoriesProvider(
|
return DocumentDetailedItem(
|
||||||
child: DocumentDetailedItem(
|
isLabelClickable: isLabelClickable,
|
||||||
isLabelClickable: isLabelClickable,
|
document: document,
|
||||||
document: document,
|
onTap: onTap,
|
||||||
onTap: onTap,
|
isSelected: selectedDocumentIds.contains(document.id),
|
||||||
isSelected: selectedDocumentIds.contains(document.id),
|
onSelected: onSelected,
|
||||||
onSelected: onSelected,
|
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
||||||
isSelectionActive: selectedDocumentIds.isNotEmpty,
|
onTagSelected: onTagSelected,
|
||||||
onTagSelected: onTagSelected,
|
onCorrespondentSelected: onCorrespondentSelected,
|
||||||
onCorrespondentSelected: onCorrespondentSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
correspondents: correspondents,
|
||||||
),
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -284,7 +317,7 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
|
|
||||||
Widget _buildGridView() {
|
Widget _buildGridView() {
|
||||||
if (showLoadingPlaceholder) {
|
if (showLoadingPlaceholder) {
|
||||||
return DocumentGridLoadingWidget();
|
return const DocumentGridLoadingWidget();
|
||||||
}
|
}
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
@@ -311,6 +344,10 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
|||||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||||
onStoragePathSelected: onStoragePathSelected,
|
onStoragePathSelected: onStoragePathSelected,
|
||||||
enableHeroAnimation: enableHeroAnimation,
|
enableHeroAnimation: enableHeroAnimation,
|
||||||
|
correspondents: correspondents,
|
||||||
|
documentTypes: documentTypes,
|
||||||
|
storagePaths: storagePaths,
|
||||||
|
tags: tags,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.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/documents/view/widgets/items/document_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.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';
|
||||||
@@ -26,6 +28,10 @@ class DocumentDetailedItem extends DocumentItem {
|
|||||||
super.onStoragePathSelected,
|
super.onStoragePathSelected,
|
||||||
super.onTagSelected,
|
super.onTagSelected,
|
||||||
super.onTap,
|
super.onTap,
|
||||||
|
required super.tags,
|
||||||
|
required super.correspondents,
|
||||||
|
required super.documentTypes,
|
||||||
|
required super.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -113,7 +119,7 @@ class DocumentDetailedItem extends DocumentItem {
|
|||||||
textStyle: Theme.of(context).textTheme.titleSmall?.apply(
|
textStyle: Theme.of(context).textTheme.titleSmall?.apply(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
correspondentId: document.correspondent,
|
correspondent: context.read<DocumentsCubit>().correspondent,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddedLTRB(8, 0, 8, 4),
|
).paddedLTRB(8, 0, 8, 4),
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class DocumentGridItem extends DocumentItem {
|
|||||||
super.onTagSelected,
|
super.onTagSelected,
|
||||||
super.onTap,
|
super.onTap,
|
||||||
required super.enableHeroAnimation,
|
required super.enableHeroAnimation,
|
||||||
|
required super.tags,
|
||||||
|
required super.correspondents,
|
||||||
|
required super.documentTypes,
|
||||||
|
required super.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,10 +58,10 @@ class DocumentGridItem extends DocumentItem {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
CorrespondentWidget(
|
CorrespondentWidget(
|
||||||
correspondentId: document.correspondent,
|
correspondent: correspondents[document.correspondent],
|
||||||
),
|
),
|
||||||
DocumentTypeWidget(
|
DocumentTypeWidget(
|
||||||
documentTypeId: document.documentType,
|
documentType: documentTypes[document.documentType],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
document.title,
|
document.title,
|
||||||
@@ -67,7 +71,7 @@ class DocumentGridItem extends DocumentItem {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
TagsWidget(
|
TagsWidget(
|
||||||
tagIds: document.tags,
|
tags: document.tags.map((e) => tags[e]!).toList(),
|
||||||
isMultiLine: false,
|
isMultiLine: false,
|
||||||
onTagSelected: onTagSelected,
|
onTagSelected: onTagSelected,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ abstract class DocumentItem extends StatelessWidget {
|
|||||||
final bool isLabelClickable;
|
final bool isLabelClickable;
|
||||||
final bool enableHeroAnimation;
|
final bool enableHeroAnimation;
|
||||||
|
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
final Map<int, StoragePath> storagePaths;
|
||||||
|
|
||||||
final void Function(int tagId)? onTagSelected;
|
final void Function(int tagId)? onTagSelected;
|
||||||
final void Function(int? correspondentId)? onCorrespondentSelected;
|
final void Function(int? correspondentId)? onCorrespondentSelected;
|
||||||
final void Function(int? documentTypeId)? onDocumentTypeSelected;
|
final void Function(int? documentTypeId)? onDocumentTypeSelected;
|
||||||
@@ -28,5 +33,9 @@ abstract class DocumentItem extends StatelessWidget {
|
|||||||
this.onDocumentTypeSelected,
|
this.onDocumentTypeSelected,
|
||||||
this.onStoragePathSelected,
|
this.onStoragePathSelected,
|
||||||
required this.enableHeroAnimation,
|
required this.enableHeroAnimation,
|
||||||
|
required this.tags,
|
||||||
|
required this.correspondents,
|
||||||
|
required this.documentTypes,
|
||||||
|
required this.storagePaths,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:paperless_api/paperless_api.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/documents/view/widgets/items/document_item.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/document_type_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/tags/view/widgets/tags_widget.dart';
|
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||||
|
|
||||||
@@ -25,113 +24,109 @@ class DocumentListItem extends DocumentItem {
|
|||||||
super.onTagSelected,
|
super.onTagSelected,
|
||||||
super.onTap,
|
super.onTap,
|
||||||
super.enableHeroAnimation = true,
|
super.enableHeroAnimation = true,
|
||||||
|
required super.tags,
|
||||||
|
required super.correspondents,
|
||||||
|
required super.documentTypes,
|
||||||
|
required super.storagePaths,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DocumentTypeBlocProvider(
|
return Material(
|
||||||
child: Material(
|
child: ListTile(
|
||||||
child: ListTile(
|
dense: true,
|
||||||
dense: true,
|
selected: isSelected,
|
||||||
selected: isSelected,
|
onTap: () => _onTap(),
|
||||||
onTap: () => _onTap(),
|
selectedTileColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
selectedTileColor: Theme.of(context).colorScheme.inversePrimary,
|
onLongPress: () => onSelected?.call(document),
|
||||||
onLongPress: () => onSelected?.call(document),
|
title: Column(
|
||||||
title: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Row(
|
children: [
|
||||||
children: [
|
AbsorbPointer(
|
||||||
AbsorbPointer(
|
absorbing: isSelectionActive,
|
||||||
absorbing: isSelectionActive,
|
child: CorrespondentWidget(
|
||||||
child: CorrespondentWidget(
|
isClickable: isLabelClickable,
|
||||||
isClickable: isLabelClickable,
|
correspondent: correspondents[document.correspondent],
|
||||||
correspondentId: document.correspondent,
|
onSelected: onCorrespondentSelected,
|
||||||
onSelected: onCorrespondentSelected,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
document.title,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
AbsorbPointer(
|
|
||||||
absorbing: isSelectionActive,
|
|
||||||
child: TagsWidget(
|
|
||||||
isClickable: isLabelClickable,
|
|
||||||
tagIds: document.tags,
|
|
||||||
isMultiLine: false,
|
|
||||||
onTagSelected: (id) => onTagSelected?.call(id),
|
|
||||||
),
|
),
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
Text(
|
||||||
subtitle: Padding(
|
document.title,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
overflow: TextOverflow.ellipsis,
|
||||||
child: BlocBuilder<LabelCubit<DocumentType>,
|
maxLines: 1,
|
||||||
LabelState<DocumentType>>(
|
),
|
||||||
builder: (context, docTypes) {
|
AbsorbPointer(
|
||||||
return RichText(
|
absorbing: isSelectionActive,
|
||||||
maxLines: 1,
|
child: TagsWidget(
|
||||||
overflow: TextOverflow.ellipsis,
|
isClickable: isLabelClickable,
|
||||||
text: TextSpan(
|
tags: document.tags.map((e) => tags[e]!).toList(),
|
||||||
text: DateFormat.yMMMd().format(document.created),
|
isMultiLine: false,
|
||||||
style: Theme.of(context)
|
onTagSelected: (id) => onTagSelected?.call(id),
|
||||||
.textTheme
|
|
||||||
.labelSmall
|
|
||||||
?.apply(color: Colors.grey),
|
|
||||||
children: document.documentType != null
|
|
||||||
? [
|
|
||||||
const TextSpan(text: '\u30FB'),
|
|
||||||
TextSpan(
|
|
||||||
text: docTypes
|
|
||||||
.labels[document.documentType]?.name,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// DateFormat.yMMMd().format(document.created),
|
|
||||||
// style: Theme.of(context)
|
|
||||||
// .textTheme
|
|
||||||
// .bodySmall
|
|
||||||
// ?.apply(color: Colors.grey),
|
|
||||||
// ),
|
|
||||||
// if (document.documentType != null) ...[
|
|
||||||
// Text("\u30FB"),
|
|
||||||
// DocumentTypeWidget(
|
|
||||||
// documentTypeId: document.documentType,
|
|
||||||
// textStyle: Theme.of(context).textTheme.bodySmall?.apply(
|
|
||||||
// color: Colors.grey,
|
|
||||||
// overflow: TextOverflow.ellipsis,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
isThreeLine: document.tags.isNotEmpty,
|
|
||||||
leading: AspectRatio(
|
|
||||||
aspectRatio: _a4AspectRatio,
|
|
||||||
child: GestureDetector(
|
|
||||||
child: DocumentPreview(
|
|
||||||
document: document,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
enableHero: enableHeroAnimation,
|
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: RichText(
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
text: TextSpan(
|
||||||
|
text: DateFormat.yMMMd().format(document.created),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelSmall
|
||||||
|
?.apply(color: Colors.grey),
|
||||||
|
children: document.documentType != null
|
||||||
|
? [
|
||||||
|
const TextSpan(text: '\u30FB'),
|
||||||
|
TextSpan(
|
||||||
|
text: documentTypes[document.documentType]?.name,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
contentPadding: const EdgeInsets.all(8.0),
|
// Row(
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// DateFormat.yMMMd().format(document.created),
|
||||||
|
// style: Theme.of(context)
|
||||||
|
// .textTheme
|
||||||
|
// .bodySmall
|
||||||
|
// ?.apply(color: Colors.grey),
|
||||||
|
// ),
|
||||||
|
// if (document.documentType != null) ...[
|
||||||
|
// Text("\u30FB"),
|
||||||
|
// DocumentTypeWidget(
|
||||||
|
// documentTypeId: document.documentType,
|
||||||
|
// textStyle: Theme.of(context).textTheme.bodySmall?.apply(
|
||||||
|
// color: Colors.grey,
|
||||||
|
// overflow: TextOverflow.ellipsis,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
|
isThreeLine: document.tags.isNotEmpty,
|
||||||
|
leading: AspectRatio(
|
||||||
|
aspectRatio: _a4AspectRatio,
|
||||||
|
child: GestureDetector(
|
||||||
|
child: DocumentPreview(
|
||||||
|
document: document,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
enableHero: enableHeroAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.all(8.0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-19
@@ -71,7 +71,7 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
.paddedOnly(left: 4, right: 4),
|
.paddedOnly(left: 4, right: 4),
|
||||||
_buildBulkEditStoragePathChip(context)
|
_buildBulkEditStoragePathChip(context)
|
||||||
.paddedOnly(left: 4, right: 4),
|
.paddedOnly(left: 4, right: 4),
|
||||||
// _buildBulkEditTagsChip(context).paddedOnly(left: 4, right: 4),
|
_buildBulkEditTagsChip(context).paddedOnly(left: 4, right: 4),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -100,9 +100,6 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DocumentBulkActionCubit(
|
create: (context) => DocumentBulkActionCubit(
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
@@ -112,8 +109,7 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
return BulkEditLabelBottomSheet<Correspondent>(
|
return BulkEditLabelBottomSheet<Correspondent>(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
title: "Bulk edit correspondent",
|
title: "Bulk edit correspondent",
|
||||||
availableOptionsSelector: (state) =>
|
availableOptionsSelector: (state) => state.correspondents,
|
||||||
state.correspondentOptions,
|
|
||||||
formFieldLabel: S.of(context)!.correspondent,
|
formFieldLabel: S.of(context)!.correspondent,
|
||||||
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
||||||
onSubmit: (selectedId) async {
|
onSubmit: (selectedId) async {
|
||||||
@@ -152,9 +148,6 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DocumentBulkActionCubit(
|
create: (context) => DocumentBulkActionCubit(
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
@@ -164,8 +157,7 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
return BulkEditLabelBottomSheet<DocumentType>(
|
return BulkEditLabelBottomSheet<DocumentType>(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
title: "Bulk edit document type",
|
title: "Bulk edit document type",
|
||||||
availableOptionsSelector: (state) =>
|
availableOptionsSelector: (state) => state.documentTypes,
|
||||||
state.documentTypeOptions,
|
|
||||||
formFieldLabel: S.of(context)!.documentType,
|
formFieldLabel: S.of(context)!.documentType,
|
||||||
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
||||||
onSubmit: (selectedId) async {
|
onSubmit: (selectedId) async {
|
||||||
@@ -204,9 +196,6 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DocumentBulkActionCubit(
|
create: (context) => DocumentBulkActionCubit(
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
@@ -216,7 +205,7 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
return BulkEditLabelBottomSheet<StoragePath>(
|
return BulkEditLabelBottomSheet<StoragePath>(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
title: "Bulk edit storage path",
|
title: "Bulk edit storage path",
|
||||||
availableOptionsSelector: (state) => state.storagePathOptions,
|
availableOptionsSelector: (state) => state.storagePaths,
|
||||||
formFieldLabel: S.of(context)!.storagePath,
|
formFieldLabel: S.of(context)!.storagePath,
|
||||||
formFieldPrefixIcon: const Icon(Icons.folder_open_outlined),
|
formFieldPrefixIcon: const Icon(Icons.folder_open_outlined),
|
||||||
onSubmit: (selectedId) async {
|
onSubmit: (selectedId) async {
|
||||||
@@ -246,14 +235,11 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isScrollControlled: false,
|
isScrollControlled: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => DocumentBulkActionCubit(
|
create: (context) => DocumentBulkActionCubit(
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
|
|||||||
@@ -43,14 +43,7 @@ class SortDocumentsButton extends StatelessWidget {
|
|||||||
child: MultiBlocProvider(
|
child: MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => LabelCubit<DocumentType>(
|
create: (context) => LabelCubit(context.read()),
|
||||||
context.read<LabelRepository<DocumentType>>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
|
||||||
create: (context) => LabelCubit<Correspondent>(
|
|
||||||
context.read<LabelRepository<Correspondent>>(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: SortFieldSelectionBottomSheet(
|
child: SortFieldSelectionBottomSheet(
|
||||||
|
|||||||
@@ -1,34 +1,44 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/cubit/label_cubit_mixin.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
part 'edit_label_state.dart';
|
part 'edit_label_state.dart';
|
||||||
|
part 'edit_label_cubit.freezed.dart';
|
||||||
|
|
||||||
class EditLabelCubit<T extends Label> extends Cubit<EditLabelState<T>> {
|
class EditLabelCubit extends Cubit<EditLabelState> with LabelCubitMixin {
|
||||||
final LabelRepository<T> _repository;
|
@override
|
||||||
|
final LabelRepository labelRepository;
|
||||||
|
|
||||||
StreamSubscription? _subscription;
|
EditLabelCubit(this.labelRepository) : super(const EditLabelState()) {
|
||||||
|
labelRepository.subscribe(
|
||||||
EditLabelCubit(LabelRepository<T> repository)
|
this,
|
||||||
: _repository = repository,
|
onChanged: (labels) => state.copyWith(
|
||||||
super(EditLabelState<T>(labels: repository.current?.values ?? {})) {
|
correspondents: labels.correspondents,
|
||||||
_subscription = repository.values.listen(
|
documentTypes: labels.documentTypes,
|
||||||
(event) => emit(EditLabelState(labels: event?.values ?? {})),
|
tags: labels.tags,
|
||||||
|
storagePaths: labels.storagePaths,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<T> create(T label) => _repository.create(label);
|
|
||||||
|
|
||||||
Future<T> update(T label) => _repository.update(label);
|
|
||||||
|
|
||||||
Future<void> delete(T label) => _repository.delete(label);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_subscription?.cancel();
|
labelRepository.unsubscribe(this);
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents => state.correspondents;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes => state.documentTypes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths => state.storagePaths;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags => state.tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'edit_label_cubit.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$EditLabelState {
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$EditLabelStateCopyWith<EditLabelState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $EditLabelStateCopyWith<$Res> {
|
||||||
|
factory $EditLabelStateCopyWith(
|
||||||
|
EditLabelState value, $Res Function(EditLabelState) then) =
|
||||||
|
_$EditLabelStateCopyWithImpl<$Res, EditLabelState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$EditLabelStateCopyWithImpl<$Res, $Val extends EditLabelState>
|
||||||
|
implements $EditLabelStateCopyWith<$Res> {
|
||||||
|
_$EditLabelStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_EditLabelStateCopyWith<$Res>
|
||||||
|
implements $EditLabelStateCopyWith<$Res> {
|
||||||
|
factory _$$_EditLabelStateCopyWith(
|
||||||
|
_$_EditLabelState value, $Res Function(_$_EditLabelState) then) =
|
||||||
|
__$$_EditLabelStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_EditLabelStateCopyWithImpl<$Res>
|
||||||
|
extends _$EditLabelStateCopyWithImpl<$Res, _$_EditLabelState>
|
||||||
|
implements _$$_EditLabelStateCopyWith<$Res> {
|
||||||
|
__$$_EditLabelStateCopyWithImpl(
|
||||||
|
_$_EditLabelState _value, $Res Function(_$_EditLabelState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_EditLabelState(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$_EditLabelState implements _EditLabelState {
|
||||||
|
const _$_EditLabelState(
|
||||||
|
{final Map<int, Correspondent> correspondents = const {},
|
||||||
|
final Map<int, DocumentType> documentTypes = const {},
|
||||||
|
final Map<int, Tag> tags = const {},
|
||||||
|
final Map<int, StoragePath> storagePaths = const {}})
|
||||||
|
: _correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_tags = tags,
|
||||||
|
_storagePaths = storagePaths;
|
||||||
|
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'EditLabelState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_EditLabelState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_tags),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_EditLabelStateCopyWith<_$_EditLabelState> get copyWith =>
|
||||||
|
__$$_EditLabelStateCopyWithImpl<_$_EditLabelState>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _EditLabelState implements EditLabelState {
|
||||||
|
const factory _EditLabelState(
|
||||||
|
{final Map<int, Correspondent> correspondents,
|
||||||
|
final Map<int, DocumentType> documentTypes,
|
||||||
|
final Map<int, Tag> tags,
|
||||||
|
final Map<int, StoragePath> storagePaths}) = _$_EditLabelState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_EditLabelStateCopyWith<_$_EditLabelState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
part of 'edit_label_cubit.dart';
|
part of 'edit_label_cubit.dart';
|
||||||
|
|
||||||
class EditLabelState<T> extends Equatable {
|
@freezed
|
||||||
final Map<int, T> labels;
|
class EditLabelState with _$EditLabelState {
|
||||||
|
const factory EditLabelState({
|
||||||
const EditLabelState({this.labels = const {}});
|
@Default({}) Map<int, Correspondent> correspondents,
|
||||||
|
@Default({}) Map<int, DocumentType> documentTypes,
|
||||||
@override
|
@Default({}) Map<int, Tag> tags,
|
||||||
List<Object> get props => [labels];
|
@Default({}) Map<int, StoragePath> storagePaths,
|
||||||
|
}) = _EditLabelState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/features/edit_label/view/label_form.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
@@ -12,6 +11,7 @@ class AddLabelPage<T extends Label> extends StatelessWidget {
|
|||||||
final Widget pageTitle;
|
final Widget pageTitle;
|
||||||
final T Function(Map<String, dynamic> json) fromJsonT;
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
final List<Widget> additionalFields;
|
final List<Widget> additionalFields;
|
||||||
|
final Future<T> Function(BuildContext context, T label) onSubmit;
|
||||||
|
|
||||||
const AddLabelPage({
|
const AddLabelPage({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -19,19 +19,21 @@ class AddLabelPage<T extends Label> extends StatelessWidget {
|
|||||||
required this.pageTitle,
|
required this.pageTitle,
|
||||||
required this.fromJsonT,
|
required this.fromJsonT,
|
||||||
this.additionalFields = const [],
|
this.additionalFields = const [],
|
||||||
|
required this.onSubmit,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<T>>(),
|
context.read<LabelRepository>(),
|
||||||
),
|
),
|
||||||
child: AddLabelFormWidget(
|
child: AddLabelFormWidget(
|
||||||
pageTitle: pageTitle,
|
pageTitle: pageTitle,
|
||||||
label: initialName != null ? fromJsonT({'name': initialName}) : null,
|
label: initialName != null ? fromJsonT({'name': initialName}) : null,
|
||||||
additionalFields: additionalFields,
|
additionalFields: additionalFields,
|
||||||
fromJsonT: fromJsonT,
|
fromJsonT: fromJsonT,
|
||||||
|
onSubmit: onSubmit,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,7 @@ class AddLabelFormWidget<T extends Label> extends StatelessWidget {
|
|||||||
final T? label;
|
final T? label;
|
||||||
final T Function(Map<String, dynamic> json) fromJsonT;
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
final List<Widget> additionalFields;
|
final List<Widget> additionalFields;
|
||||||
|
final Future<T> Function(BuildContext context, T label) onSubmit;
|
||||||
|
|
||||||
final Widget pageTitle;
|
final Widget pageTitle;
|
||||||
const AddLabelFormWidget({
|
const AddLabelFormWidget({
|
||||||
@@ -49,6 +52,7 @@ class AddLabelFormWidget<T extends Label> extends StatelessWidget {
|
|||||||
required this.fromJsonT,
|
required this.fromJsonT,
|
||||||
required this.additionalFields,
|
required this.additionalFields,
|
||||||
required this.pageTitle,
|
required this.pageTitle,
|
||||||
|
required this.onSubmit,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -63,7 +67,7 @@ class AddLabelFormWidget<T extends Label> extends StatelessWidget {
|
|||||||
submitButtonConfig: SubmitButtonConfig<T>(
|
submitButtonConfig: SubmitButtonConfig<T>(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
label: Text(S.of(context)!.create),
|
label: Text(S.of(context)!.create),
|
||||||
onSubmit: context.read<EditLabelCubit<T>>().create,
|
onSubmit: (label) => onSubmit(context, label),
|
||||||
),
|
),
|
||||||
additionalFields: additionalFields,
|
additionalFields: additionalFields,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,40 +1,43 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/features/edit_label/view/label_form.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/constants.dart';
|
|
||||||
|
|
||||||
class EditLabelPage<T extends Label> extends StatelessWidget {
|
class EditLabelPage<T extends Label> extends StatelessWidget {
|
||||||
final T label;
|
final T label;
|
||||||
final T Function(Map<String, dynamic> json) fromJsonT;
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
final List<Widget> additionalFields;
|
final List<Widget> additionalFields;
|
||||||
|
final Future<T> Function(BuildContext context, T label) onSubmit;
|
||||||
|
final Future<void> Function(BuildContext context, T label) onDelete;
|
||||||
|
|
||||||
const EditLabelPage({
|
const EditLabelPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.fromJsonT,
|
required this.fromJsonT,
|
||||||
this.additionalFields = const [],
|
this.additionalFields = const [],
|
||||||
|
required this.onSubmit,
|
||||||
|
required this.onDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<T>>(),
|
context.read<LabelRepository>(),
|
||||||
),
|
),
|
||||||
child: EditLabelForm(
|
child: EditLabelForm(
|
||||||
label: label,
|
label: label,
|
||||||
additionalFields: additionalFields,
|
additionalFields: additionalFields,
|
||||||
fromJsonT: fromJsonT,
|
fromJsonT: fromJsonT,
|
||||||
|
onSubmit: onSubmit,
|
||||||
|
onDelete: onDelete,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -44,12 +47,16 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
final T label;
|
final T label;
|
||||||
final T Function(Map<String, dynamic> json) fromJsonT;
|
final T Function(Map<String, dynamic> json) fromJsonT;
|
||||||
final List<Widget> additionalFields;
|
final List<Widget> additionalFields;
|
||||||
|
final Future<T> Function(BuildContext context, T label) onSubmit;
|
||||||
|
final Future<void> Function(BuildContext context, T label) onDelete;
|
||||||
|
|
||||||
const EditLabelForm({
|
const EditLabelForm({
|
||||||
super.key,
|
super.key,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.fromJsonT,
|
required this.fromJsonT,
|
||||||
required this.additionalFields,
|
required this.additionalFields,
|
||||||
|
required this.onSubmit,
|
||||||
|
required this.onDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -70,7 +77,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
submitButtonConfig: SubmitButtonConfig<T>(
|
submitButtonConfig: SubmitButtonConfig<T>(
|
||||||
icon: const Icon(Icons.save),
|
icon: const Icon(Icons.save),
|
||||||
label: Text(S.of(context)!.saveChanges),
|
label: Text(S.of(context)!.saveChanges),
|
||||||
onSubmit: context.read<EditLabelCubit<T>>().update,
|
onSubmit: (label) => onSubmit(context, label),
|
||||||
),
|
),
|
||||||
additionalFields: additionalFields,
|
additionalFields: additionalFields,
|
||||||
),
|
),
|
||||||
@@ -107,7 +114,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
false;
|
false;
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
try {
|
try {
|
||||||
context.read<EditLabelCubit<T>>().delete(label);
|
onDelete(context, label);
|
||||||
} on PaperlessServerException catch (error) {
|
} on PaperlessServerException catch (error) {
|
||||||
showErrorMessage(context, error);
|
showErrorMessage(context, error);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
@@ -116,7 +123,7 @@ class EditLabelForm<T extends Label> extends StatelessWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.read<EditLabelCubit<T>>().delete(label);
|
onDelete(context, label);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/edit_label/view/add_label_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
@@ -14,13 +12,15 @@ class AddCorrespondentPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<Correspondent>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<Correspondent>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: AddLabelPage<Correspondent>(
|
child: AddLabelPage<Correspondent>(
|
||||||
pageTitle: Text(S.of(context)!.addCorrespondent),
|
pageTitle: Text(S.of(context)!.addCorrespondent),
|
||||||
fromJsonT: Correspondent.fromJson,
|
fromJsonT: Correspondent.fromJson,
|
||||||
initialName: initialName,
|
initialName: initialName,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addCorrespondent(label),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/edit_label/view/add_label_page.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
@@ -17,13 +15,15 @@ class AddDocumentTypePage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<DocumentType>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<DocumentType>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: AddLabelPage<DocumentType>(
|
child: AddLabelPage<DocumentType>(
|
||||||
pageTitle: Text(S.of(context)!.addDocumentType),
|
pageTitle: Text(S.of(context)!.addDocumentType),
|
||||||
fromJsonT: DocumentType.fromJson,
|
fromJsonT: DocumentType.fromJson,
|
||||||
initialName: initialName,
|
initialName: initialName,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addDocumentType(label),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/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/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||||
@@ -15,13 +13,15 @@ class AddStoragePathPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<StoragePath>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<StoragePath>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: AddLabelPage<StoragePath>(
|
child: AddLabelPage<StoragePath>(
|
||||||
pageTitle: Text(S.of(context)!.addStoragePath),
|
pageTitle: Text(S.of(context)!.addStoragePath),
|
||||||
fromJsonT: StoragePath.fromJson,
|
fromJsonT: StoragePath.fromJson,
|
||||||
initialName: initalName,
|
initialName: initalName,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addStoragePath(label),
|
||||||
additionalFields: const [
|
additionalFields: const [
|
||||||
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
||||||
SizedBox(height: 120.0),
|
SizedBox(height: 120.0),
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package: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/state/impl/tag_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.dart';
|
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.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/edit_label/view/add_label_page.dart';
|
||||||
@@ -18,13 +16,15 @@ class AddTagPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<Tag>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<Tag>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: AddLabelPage<Tag>(
|
child: AddLabelPage<Tag>(
|
||||||
pageTitle: Text(S.of(context)!.addTag),
|
pageTitle: Text(S.of(context)!.addTag),
|
||||||
fromJsonT: Tag.fromJson,
|
fromJsonT: Tag.fromJson,
|
||||||
initialName: initialValue,
|
initialName: initialValue,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addTag(label),
|
||||||
additionalFields: [
|
additionalFields: [
|
||||||
FormBuilderColorPickerField(
|
FormBuilderColorPickerField(
|
||||||
name: Tag.colorKey,
|
name: Tag.colorKey,
|
||||||
|
|||||||
@@ -1,8 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
|
|
||||||
@@ -13,12 +11,16 @@ class EditCorrespondentPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<Correspondent>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<Correspondent>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<Correspondent>(
|
child: EditLabelPage<Correspondent>(
|
||||||
label: correspondent,
|
label: correspondent,
|
||||||
fromJsonT: Correspondent.fromJson,
|
fromJsonT: Correspondent.fromJson,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addCorrespondent(label),
|
||||||
|
onDelete: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().removeCorrespondent(label),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
|
|
||||||
@@ -13,12 +12,16 @@ class EditDocumentTypePage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<DocumentType>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<DocumentType>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<DocumentType>(
|
child: EditLabelPage<DocumentType>(
|
||||||
label: documentType,
|
label: documentType,
|
||||||
fromJsonT: DocumentType.fromJson,
|
fromJsonT: DocumentType.fromJson,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addDocumentType(label),
|
||||||
|
onDelete: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().removeDocumentType(label),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_autofill_form_builder_field.dart';
|
||||||
@@ -14,12 +12,16 @@ class EditStoragePathPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<StoragePath>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<StoragePath>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<StoragePath>(
|
child: EditLabelPage<StoragePath>(
|
||||||
label: storagePath,
|
label: storagePath,
|
||||||
fromJsonT: StoragePath.fromJson,
|
fromJsonT: StoragePath.fromJson,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addStoragePath(label),
|
||||||
|
onDelete: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().removeStoragePath(label),
|
||||||
additionalFields: [
|
additionalFields: [
|
||||||
StoragePathAutofillFormBuilderField(
|
StoragePathAutofillFormBuilderField(
|
||||||
name: StoragePath.pathKey,
|
name: StoragePath.pathKey,
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package: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/state/impl/tag_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.dart';
|
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_color_picker.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/edit_label_page.dart';
|
||||||
@@ -17,12 +15,16 @@ class EditTagPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => EditLabelCubit<Tag>(
|
create: (context) => EditLabelCubit(
|
||||||
context.read<LabelRepository<Tag>>(),
|
context.read(),
|
||||||
),
|
),
|
||||||
child: EditLabelPage<Tag>(
|
child: EditLabelPage<Tag>(
|
||||||
label: tag,
|
label: tag,
|
||||||
fromJsonT: Tag.fromJson,
|
fromJsonT: Tag.fromJson,
|
||||||
|
onSubmit: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().addTag(label),
|
||||||
|
onDelete: (context, label) =>
|
||||||
|
context.read<EditLabelCubit>().removeTag(label),
|
||||||
additionalFields: [
|
additionalFields: [
|
||||||
FormBuilderColorPickerField(
|
FormBuilderColorPickerField(
|
||||||
initialValue: tag.color,
|
initialValue: tag.color,
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
|||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:paperless_mobile/helpers/file_helpers.dart';
|
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
|
||||||
@@ -60,8 +58,6 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
|
||||||
context.read(),
|
|
||||||
);
|
);
|
||||||
_listenToInboxChanges();
|
_listenToInboxChanges();
|
||||||
|
|
||||||
@@ -260,17 +256,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => LabelCubit<Correspondent>(context.read()),
|
create: (context) => LabelCubit(context.read()),
|
||||||
),
|
)
|
||||||
BlocProvider(
|
|
||||||
create: (context) => LabelCubit<DocumentType>(context.read()),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
|
||||||
create: (context) => LabelCubit<Tag>(context.read()),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
|
||||||
create: (context) => LabelCubit<StoragePath>(context.read()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: const LabelsPage(),
|
child: const LabelsPage(),
|
||||||
),
|
),
|
||||||
@@ -345,15 +332,13 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializeData(BuildContext context) {
|
void _initializeData(BuildContext context) {
|
||||||
try {
|
Future.wait([
|
||||||
context.read<LabelRepository<Tag>>().findAll();
|
context.read<LabelRepository>().initialize(),
|
||||||
context.read<LabelRepository<Correspondent>>().findAll();
|
context.read<SavedViewRepository>().findAll(),
|
||||||
context.read<LabelRepository<DocumentType>>().findAll();
|
context.read<PaperlessServerInformationCubit>().updateInformtion(),
|
||||||
context.read<LabelRepository<StoragePath>>().findAll();
|
]).onError<PaperlessServerException>((error, stackTrace) {
|
||||||
context.read<SavedViewRepository>().findAll();
|
|
||||||
context.read<PaperlessServerInformationCubit>().updateInformtion();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
showErrorMessage(context, error, stackTrace);
|
||||||
}
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,7 @@ class VerifyIdentityPage extends StatelessWidget {
|
|||||||
|
|
||||||
void _logout(BuildContext context) {
|
void _logout(BuildContext context) {
|
||||||
context.read<AuthenticationCubit>().logout();
|
context.read<AuthenticationCubit>().logout();
|
||||||
context.read<LabelRepository<Tag>>().clear();
|
context.read<LabelRepository>().clear();
|
||||||
context.read<LabelRepository<Correspondent>>().clear();
|
|
||||||
context.read<LabelRepository<DocumentType>>().clear();
|
|
||||||
context.read<LabelRepository<StoragePath>>().clear();
|
|
||||||
context.read<SavedViewRepository>().clear();
|
context.read<SavedViewRepository>().clear();
|
||||||
HydratedBloc.storage.clear();
|
HydratedBloc.storage.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:json_annotation/json_annotation.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository_state.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||||
|
|
||||||
@@ -13,9 +14,7 @@ part 'inbox_state.dart';
|
|||||||
|
|
||||||
class InboxCubit extends HydratedCubit<InboxState>
|
class InboxCubit extends HydratedCubit<InboxState>
|
||||||
with DocumentPagingBlocMixin {
|
with DocumentPagingBlocMixin {
|
||||||
final LabelRepository<Tag> _tagsRepository;
|
final LabelRepository _labelRepository;
|
||||||
final LabelRepository<Correspondent> _correspondentRepository;
|
|
||||||
final LabelRepository<DocumentType> _documentTypeRepository;
|
|
||||||
|
|
||||||
final PaperlessDocumentsApi _documentsApi;
|
final PaperlessDocumentsApi _documentsApi;
|
||||||
|
|
||||||
@@ -24,27 +23,15 @@ class InboxCubit extends HydratedCubit<InboxState>
|
|||||||
|
|
||||||
final PaperlessServerStatsApi _statsApi;
|
final PaperlessServerStatsApi _statsApi;
|
||||||
|
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PaperlessDocumentsApi get api => _documentsApi;
|
PaperlessDocumentsApi get api => _documentsApi;
|
||||||
|
|
||||||
InboxCubit(
|
InboxCubit(
|
||||||
this._tagsRepository,
|
|
||||||
this._documentsApi,
|
this._documentsApi,
|
||||||
this._correspondentRepository,
|
|
||||||
this._documentTypeRepository,
|
|
||||||
this._statsApi,
|
this._statsApi,
|
||||||
|
this._labelRepository,
|
||||||
this.notifier,
|
this.notifier,
|
||||||
) : super(
|
) : super(InboxState(labels: _labelRepository.state)) {
|
||||||
InboxState(
|
|
||||||
availableCorrespondents:
|
|
||||||
_correspondentRepository.current?.values ?? {},
|
|
||||||
availableDocumentTypes:
|
|
||||||
_documentTypeRepository.current?.values ?? {},
|
|
||||||
availableTags: _tagsRepository.current?.values ?? {},
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
notifier.subscribe(
|
notifier.subscribe(
|
||||||
this,
|
this,
|
||||||
onDeleted: remove,
|
onDeleted: remove,
|
||||||
@@ -60,28 +47,11 @@ class InboxCubit extends HydratedCubit<InboxState>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_subscriptions.add(
|
_labelRepository.subscribe(
|
||||||
_tagsRepository.values.listen((event) {
|
this,
|
||||||
if (event?.hasLoaded ?? false) {
|
onChanged: (labels) {
|
||||||
emit(state.copyWith(availableTags: event!.values));
|
emit(state.copyWith(labels: labels));
|
||||||
}
|
},
|
||||||
}),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_correspondentRepository.values.listen((event) {
|
|
||||||
if (event?.hasLoaded ?? false) {
|
|
||||||
emit(state.copyWith(
|
|
||||||
availableCorrespondents: event!.values,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
_subscriptions.add(
|
|
||||||
_documentTypeRepository.values.listen((event) {
|
|
||||||
if (event?.hasLoaded ?? false) {
|
|
||||||
emit(state.copyWith(availableDocumentTypes: event!.values));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
refreshItemsInInboxCount(false);
|
refreshItemsInInboxCount(false);
|
||||||
@@ -105,7 +75,7 @@ class InboxCubit extends HydratedCubit<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 _tagsRepository.findAll().then(
|
final inboxTags = await _labelRepository.findAllTags().then(
|
||||||
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -133,7 +103,7 @@ class InboxCubit extends HydratedCubit<InboxState>
|
|||||||
///
|
///
|
||||||
Future<void> reloadInbox() async {
|
Future<void> reloadInbox() async {
|
||||||
emit(state.copyWith(hasLoaded: false, isLoading: true));
|
emit(state.copyWith(hasLoaded: false, isLoading: true));
|
||||||
final inboxTags = await _tagsRepository.findAll().then(
|
final inboxTags = await _labelRepository.findAllTags().then(
|
||||||
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -239,9 +209,7 @@ class InboxCubit extends HydratedCubit<InboxState>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
for (var sub in _subscriptions) {
|
_labelRepository.unsubscribe(this);
|
||||||
sub.cancel();
|
|
||||||
}
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ part of 'inbox_cubit.dart';
|
|||||||
class InboxState extends DocumentPagingState {
|
class InboxState extends DocumentPagingState {
|
||||||
final Iterable<int> inboxTags;
|
final Iterable<int> inboxTags;
|
||||||
|
|
||||||
final Map<int, Tag> availableTags;
|
final LabelRepositoryState labels;
|
||||||
|
|
||||||
final Map<int, DocumentType> availableDocumentTypes;
|
|
||||||
|
|
||||||
final Map<int, Correspondent> availableCorrespondents;
|
|
||||||
|
|
||||||
final int itemsInInboxCount;
|
final int itemsInInboxCount;
|
||||||
|
|
||||||
@@ -22,10 +18,8 @@ class InboxState extends DocumentPagingState {
|
|||||||
super.filter = const DocumentFilter(),
|
super.filter = const DocumentFilter(),
|
||||||
this.inboxTags = const [],
|
this.inboxTags = const [],
|
||||||
this.isHintAcknowledged = false,
|
this.isHintAcknowledged = false,
|
||||||
this.availableTags = const {},
|
|
||||||
this.availableDocumentTypes = const {},
|
|
||||||
this.availableCorrespondents = const {},
|
|
||||||
this.itemsInInboxCount = 0,
|
this.itemsInInboxCount = 0,
|
||||||
|
this.labels = const LabelRepositoryState(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -37,10 +31,8 @@ class InboxState extends DocumentPagingState {
|
|||||||
inboxTags,
|
inboxTags,
|
||||||
documents,
|
documents,
|
||||||
isHintAcknowledged,
|
isHintAcknowledged,
|
||||||
availableTags,
|
|
||||||
availableDocumentTypes,
|
|
||||||
availableCorrespondents,
|
|
||||||
itemsInInboxCount,
|
itemsInInboxCount,
|
||||||
|
labels,
|
||||||
];
|
];
|
||||||
|
|
||||||
InboxState copyWith({
|
InboxState copyWith({
|
||||||
@@ -50,9 +42,7 @@ class InboxState extends DocumentPagingState {
|
|||||||
List<PagedSearchResult<DocumentModel>>? value,
|
List<PagedSearchResult<DocumentModel>>? value,
|
||||||
DocumentFilter? filter,
|
DocumentFilter? filter,
|
||||||
bool? isHintAcknowledged,
|
bool? isHintAcknowledged,
|
||||||
Map<int, Tag>? availableTags,
|
LabelRepositoryState? labels,
|
||||||
Map<int, Correspondent>? availableCorrespondents,
|
|
||||||
Map<int, DocumentType>? availableDocumentTypes,
|
|
||||||
Map<int, FieldSuggestions>? suggestions,
|
Map<int, FieldSuggestions>? suggestions,
|
||||||
int? itemsInInboxCount,
|
int? itemsInInboxCount,
|
||||||
}) {
|
}) {
|
||||||
@@ -62,11 +52,7 @@ class InboxState extends DocumentPagingState {
|
|||||||
value: value ?? super.value,
|
value: value ?? super.value,
|
||||||
inboxTags: inboxTags ?? this.inboxTags,
|
inboxTags: inboxTags ?? this.inboxTags,
|
||||||
isHintAcknowledged: isHintAcknowledged ?? this.isHintAcknowledged,
|
isHintAcknowledged: isHintAcknowledged ?? this.isHintAcknowledged,
|
||||||
availableCorrespondents:
|
labels: labels ?? this.labels,
|
||||||
availableCorrespondents ?? this.availableCorrespondents,
|
|
||||||
availableDocumentTypes:
|
|
||||||
availableDocumentTypes ?? this.availableDocumentTypes,
|
|
||||||
availableTags: availableTags ?? this.availableTags,
|
|
||||||
filter: filter ?? super.filter,
|
filter: filter ?? super.filter,
|
||||||
itemsInInboxCount: itemsInInboxCount ?? this.itemsInInboxCount,
|
itemsInInboxCount: itemsInInboxCount ?? this.itemsInInboxCount,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,60 +32,98 @@ class _InboxItemState extends State<InboxItem> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return BlocBuilder<InboxCubit, InboxState>(
|
||||||
behavior: HitTestBehavior.translucent,
|
builder: (context, state) {
|
||||||
onTap: () async {
|
return GestureDetector(
|
||||||
Navigator.pushNamed(
|
behavior: HitTestBehavior.translucent,
|
||||||
context,
|
onTap: () async {
|
||||||
DocumentDetailsRoute.routeName,
|
Navigator.pushNamed(
|
||||||
arguments: DocumentDetailsRouteArguments(
|
context,
|
||||||
document: widget.document,
|
DocumentDetailsRoute.routeName,
|
||||||
isLabelClickable: false,
|
arguments: DocumentDetailsRouteArguments(
|
||||||
|
document: widget.document,
|
||||||
|
isLabelClickable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: InboxItem.a4AspectRatio,
|
||||||
|
child: DocumentPreview(
|
||||||
|
document: widget.document,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
enableHero: false,
|
||||||
|
),
|
||||||
|
).padded(),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildTitle().paddedOnly(left: 8, right: 8, top: 8),
|
||||||
|
const Spacer(),
|
||||||
|
_buildTextWithLeadingIcon(
|
||||||
|
Icon(
|
||||||
|
Icons.person_outline,
|
||||||
|
size: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.fontSize,
|
||||||
|
),
|
||||||
|
LabelText<Correspondent>(
|
||||||
|
label: state.labels.correspondents[
|
||||||
|
widget.document.correspondent],
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
placeholder: "-",
|
||||||
|
),
|
||||||
|
).paddedSymmetrically(horizontal: 8),
|
||||||
|
_buildTextWithLeadingIcon(
|
||||||
|
Icon(
|
||||||
|
Icons.description_outlined,
|
||||||
|
size: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.fontSize,
|
||||||
|
),
|
||||||
|
LabelText<DocumentType>(
|
||||||
|
label: state.labels.documentTypes[
|
||||||
|
widget.document.documentType],
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
placeholder: "-",
|
||||||
|
),
|
||||||
|
).paddedSymmetrically(horizontal: 8),
|
||||||
|
const Spacer(),
|
||||||
|
TagsWidget(
|
||||||
|
tags: widget.document.tags
|
||||||
|
.map((e) => state.labels.tags[e]!)
|
||||||
|
.toList(),
|
||||||
|
isMultiLine: false,
|
||||||
|
isClickable: false,
|
||||||
|
showShortNames: true,
|
||||||
|
dense: true,
|
||||||
|
).paddedOnly(left: 8, bottom: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 56,
|
||||||
|
child: _buildActions(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddedOnly(left: 8, top: 8, bottom: 8),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
|
||||||
height: 200,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: InboxItem.a4AspectRatio,
|
|
||||||
child: DocumentPreview(
|
|
||||||
document: widget.document,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
enableHero: false,
|
|
||||||
),
|
|
||||||
).padded(),
|
|
||||||
Flexible(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildTitle().paddedOnly(left: 8, right: 8, top: 8),
|
|
||||||
const Spacer(),
|
|
||||||
_buildCorrespondent(context)
|
|
||||||
.paddedSymmetrically(horizontal: 8),
|
|
||||||
_buildDocumentType(context)
|
|
||||||
.paddedSymmetrically(horizontal: 8),
|
|
||||||
const Spacer(),
|
|
||||||
_buildTags().paddedOnly(left: 8, bottom: 8),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: _buildActions(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).paddedOnly(left: 8, top: 8, bottom: 8),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,44 +249,6 @@ class _InboxItemState extends State<InboxItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TagsWidget _buildTags() {
|
|
||||||
return TagsWidget(
|
|
||||||
tagIds: widget.document.tags,
|
|
||||||
isMultiLine: false,
|
|
||||||
isClickable: false,
|
|
||||||
showShortNames: true,
|
|
||||||
dense: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Row _buildDocumentType(BuildContext context) {
|
|
||||||
return _buildTextWithLeadingIcon(
|
|
||||||
Icon(
|
|
||||||
Icons.description_outlined,
|
|
||||||
size: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
|
||||||
),
|
|
||||||
LabelText<DocumentType>(
|
|
||||||
id: widget.document.documentType,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
placeholder: "-",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Row _buildCorrespondent(BuildContext context) {
|
|
||||||
return _buildTextWithLeadingIcon(
|
|
||||||
Icon(
|
|
||||||
Icons.person_outline,
|
|
||||||
size: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
|
||||||
),
|
|
||||||
LabelText<Correspondent>(
|
|
||||||
id: widget.document.correspondent,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
placeholder: "-",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Text _buildTitle() {
|
Text _buildTitle() {
|
||||||
return Text(
|
return Text(
|
||||||
widget.document.title,
|
widget.document.title,
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
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/features/labels/cubit/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/correspondent_bloc_provider.dart';
|
|
||||||
|
|
||||||
class CorrespondentWidget extends StatelessWidget {
|
class CorrespondentWidget extends StatelessWidget {
|
||||||
final int? correspondentId;
|
final Correspondent? correspondent;
|
||||||
final void Function(int? id)? onSelected;
|
final void Function(int? id)? onSelected;
|
||||||
final Color? textColor;
|
final Color? textColor;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
@@ -13,7 +10,7 @@ class CorrespondentWidget extends StatelessWidget {
|
|||||||
|
|
||||||
const CorrespondentWidget({
|
const CorrespondentWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.correspondentId,
|
required this.correspondent,
|
||||||
this.textColor,
|
this.textColor,
|
||||||
this.isClickable = true,
|
this.isClickable = true,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
@@ -22,25 +19,18 @@ class CorrespondentWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CorrespondentBlocProvider(
|
return AbsorbPointer(
|
||||||
child: AbsorbPointer(
|
absorbing: !isClickable,
|
||||||
absorbing: !isClickable,
|
child: GestureDetector(
|
||||||
child:
|
onTap: () => onSelected?.call(correspondent?.id),
|
||||||
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
child: Text(
|
||||||
builder: (context, state) {
|
correspondent?.name ?? "-",
|
||||||
return GestureDetector(
|
maxLines: 1,
|
||||||
onTap: () => onSelected?.call(correspondentId!),
|
overflow: TextOverflow.ellipsis,
|
||||||
child: Text(
|
style:
|
||||||
(state.getLabel(correspondentId)?.name) ?? "-",
|
(textStyle ?? Theme.of(context).textTheme.bodyMedium)?.copyWith(
|
||||||
maxLines: 1,
|
color: textColor ?? Theme.of(context).colorScheme.primary,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
style: (textStyle ?? Theme.of(context).textTheme.bodyMedium)
|
|
||||||
?.copyWith(
|
|
||||||
color: textColor ?? Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,68 +1,45 @@
|
|||||||
import 'dart:async';
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/cubit/label_cubit_mixin.dart';
|
||||||
|
|
||||||
part 'label_state.dart';
|
part 'label_state.dart';
|
||||||
|
part 'label_cubit.freezed.dart';
|
||||||
|
|
||||||
class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
class LabelCubit extends Cubit<LabelState> with LabelCubitMixin<LabelState> {
|
||||||
final LabelRepository<T> _repository;
|
@override
|
||||||
|
final LabelRepository labelRepository;
|
||||||
|
|
||||||
late StreamSubscription _subscription;
|
LabelCubit(this.labelRepository) : super(const LabelState()) {
|
||||||
|
labelRepository.subscribe(
|
||||||
LabelCubit(LabelRepository<T> repository)
|
this,
|
||||||
: _repository = repository,
|
onChanged: (labels) {
|
||||||
super(LabelState(
|
emit(state.copyWith(
|
||||||
isLoaded: repository.isInitialized,
|
correspondents: labels.correspondents,
|
||||||
labels: repository.current?.values ?? {},
|
documentTypes: labels.documentTypes,
|
||||||
)) {
|
storagePaths: labels.storagePaths,
|
||||||
_subscription = _repository.values.listen(
|
tags: labels.tags,
|
||||||
(event) {
|
));
|
||||||
if (event == null) {
|
|
||||||
emit(LabelState());
|
|
||||||
}
|
|
||||||
emit(
|
|
||||||
LabelState(isLoaded: event!.hasLoaded, labels: event.values ?? {}));
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
@override
|
||||||
/// Adds [item] to the current state. A new state is automatically pushed
|
Future<void> close() {
|
||||||
/// due to the subscription to the repository, which updates the state on
|
labelRepository.unsubscribe(this);
|
||||||
/// operation.
|
return super.close();
|
||||||
///
|
|
||||||
Future<T> add(T item) async {
|
|
||||||
assert(item.id == null);
|
|
||||||
final addedItem = await _repository.create(item);
|
|
||||||
return addedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> reload() {
|
|
||||||
return _repository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<T> replace(T item) async {
|
|
||||||
assert(item.id != null);
|
|
||||||
final updatedItem = await _repository.update(item);
|
|
||||||
return updatedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> remove(T item) async {
|
|
||||||
assert(item.id != null);
|
|
||||||
if (state.labels.containsKey(item.id)) {
|
|
||||||
await _repository.delete(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
emit(LabelState(isLoaded: false, labels: {}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Map<int, Correspondent> get correspondents => state.correspondents;
|
||||||
_subscription.cancel();
|
|
||||||
return super.close();
|
@override
|
||||||
}
|
Map<int, DocumentType> get documentTypes => state.documentTypes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths => state.storagePaths;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags => state.tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'label_cubit.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$LabelState {
|
||||||
|
Map<int, Correspondent> get correspondents =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, DocumentType> get documentTypes =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
Map<int, Tag> get tags => throw _privateConstructorUsedError;
|
||||||
|
Map<int, StoragePath> get storagePaths => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$LabelStateCopyWith<LabelState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $LabelStateCopyWith<$Res> {
|
||||||
|
factory $LabelStateCopyWith(
|
||||||
|
LabelState value, $Res Function(LabelState) then) =
|
||||||
|
_$LabelStateCopyWithImpl<$Res, LabelState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$LabelStateCopyWithImpl<$Res, $Val extends LabelState>
|
||||||
|
implements $LabelStateCopyWith<$Res> {
|
||||||
|
_$LabelStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value.correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value.documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value.storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$_LabelStateCopyWith<$Res>
|
||||||
|
implements $LabelStateCopyWith<$Res> {
|
||||||
|
factory _$$_LabelStateCopyWith(
|
||||||
|
_$_LabelState value, $Res Function(_$_LabelState) then) =
|
||||||
|
__$$_LabelStateCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{Map<int, Correspondent> correspondents,
|
||||||
|
Map<int, DocumentType> documentTypes,
|
||||||
|
Map<int, Tag> tags,
|
||||||
|
Map<int, StoragePath> storagePaths});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$_LabelStateCopyWithImpl<$Res>
|
||||||
|
extends _$LabelStateCopyWithImpl<$Res, _$_LabelState>
|
||||||
|
implements _$$_LabelStateCopyWith<$Res> {
|
||||||
|
__$$_LabelStateCopyWithImpl(
|
||||||
|
_$_LabelState _value, $Res Function(_$_LabelState) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? correspondents = null,
|
||||||
|
Object? documentTypes = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? storagePaths = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$_LabelState(
|
||||||
|
correspondents: null == correspondents
|
||||||
|
? _value._correspondents
|
||||||
|
: correspondents // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Correspondent>,
|
||||||
|
documentTypes: null == documentTypes
|
||||||
|
? _value._documentTypes
|
||||||
|
: documentTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, DocumentType>,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, Tag>,
|
||||||
|
storagePaths: null == storagePaths
|
||||||
|
? _value._storagePaths
|
||||||
|
: storagePaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<int, StoragePath>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$_LabelState implements _LabelState {
|
||||||
|
const _$_LabelState(
|
||||||
|
{final Map<int, Correspondent> correspondents = const {},
|
||||||
|
final Map<int, DocumentType> documentTypes = const {},
|
||||||
|
final Map<int, Tag> tags = const {},
|
||||||
|
final Map<int, StoragePath> storagePaths = const {}})
|
||||||
|
: _correspondents = correspondents,
|
||||||
|
_documentTypes = documentTypes,
|
||||||
|
_tags = tags,
|
||||||
|
_storagePaths = storagePaths;
|
||||||
|
|
||||||
|
final Map<int, Correspondent> _correspondents;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Correspondent> get correspondents {
|
||||||
|
if (_correspondents is EqualUnmodifiableMapView) return _correspondents;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_correspondents);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, DocumentType> _documentTypes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, DocumentType> get documentTypes {
|
||||||
|
if (_documentTypes is EqualUnmodifiableMapView) return _documentTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_documentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, Tag> _tags;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, Tag> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableMapView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<int, StoragePath> _storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<int, StoragePath> get storagePaths {
|
||||||
|
if (_storagePaths is EqualUnmodifiableMapView) return _storagePaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_storagePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LabelState(correspondents: $correspondents, documentTypes: $documentTypes, tags: $tags, storagePaths: $storagePaths)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$_LabelState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._correspondents, _correspondents) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._documentTypes, _documentTypes) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._storagePaths, _storagePaths));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(_correspondents),
|
||||||
|
const DeepCollectionEquality().hash(_documentTypes),
|
||||||
|
const DeepCollectionEquality().hash(_tags),
|
||||||
|
const DeepCollectionEquality().hash(_storagePaths));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$_LabelStateCopyWith<_$_LabelState> get copyWith =>
|
||||||
|
__$$_LabelStateCopyWithImpl<_$_LabelState>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _LabelState implements LabelState {
|
||||||
|
const factory _LabelState(
|
||||||
|
{final Map<int, Correspondent> correspondents,
|
||||||
|
final Map<int, DocumentType> documentTypes,
|
||||||
|
final Map<int, Tag> tags,
|
||||||
|
final Map<int, StoragePath> storagePaths}) = _$_LabelState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
@override
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
@override
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
@override
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$_LabelStateCopyWith<_$_LabelState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
|
||||||
|
mixin LabelCubitMixin<T> on BlocBase<T> {
|
||||||
|
LabelRepository get labelRepository;
|
||||||
|
|
||||||
|
Map<int, Correspondent> get correspondents;
|
||||||
|
Map<int, DocumentType> get documentTypes;
|
||||||
|
Map<int, Tag> get tags;
|
||||||
|
Map<int, StoragePath> get storagePaths;
|
||||||
|
|
||||||
|
Future<Correspondent> addCorrespondent(Correspondent item) async {
|
||||||
|
assert(item.id == null);
|
||||||
|
final addedItem = await labelRepository.createCorrespondent(item);
|
||||||
|
return addedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reloadCorrespondents() {
|
||||||
|
return labelRepository.findAllCorrespondents();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Correspondent> replaceCorrespondent(Correspondent item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
final updatedItem = await labelRepository.updateCorrespondent(item);
|
||||||
|
return updatedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeCorrespondent(Correspondent item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
if (correspondents.containsKey(item.id)) {
|
||||||
|
await labelRepository.deleteCorrespondent(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DocumentType> addDocumentType(DocumentType item) async {
|
||||||
|
assert(item.id == null);
|
||||||
|
final addedItem = await labelRepository.createDocumentType(item);
|
||||||
|
return addedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reloadDocumentTypes() {
|
||||||
|
return labelRepository.findAllDocumentTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DocumentType> replaceDocumentType(DocumentType item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
final updatedItem = await labelRepository.updateDocumentType(item);
|
||||||
|
return updatedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeDocumentType(DocumentType item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
if (documentTypes.containsKey(item.id)) {
|
||||||
|
await labelRepository.deleteDocumentType(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StoragePath> addStoragePath(StoragePath item) async {
|
||||||
|
assert(item.id == null);
|
||||||
|
final addedItem = await labelRepository.createStoragePath(item);
|
||||||
|
return addedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reloadStoragePaths() {
|
||||||
|
return labelRepository.findAllStoragePaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StoragePath> replaceStoragePath(StoragePath item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
final updatedItem = await labelRepository.updateStoragePath(item);
|
||||||
|
return updatedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeStoragePath(StoragePath item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
if (storagePaths.containsKey(item.id)) {
|
||||||
|
await labelRepository.deleteStoragePath(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tag> addTag(Tag item) async {
|
||||||
|
assert(item.id == null);
|
||||||
|
final addedItem = await labelRepository.createTag(item);
|
||||||
|
return addedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reloadTags() {
|
||||||
|
return labelRepository.findAllTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tag> replaceTag(Tag item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
final updatedItem = await labelRepository.updateTag(item);
|
||||||
|
return updatedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeTag(Tag item) async {
|
||||||
|
assert(item.id != null);
|
||||||
|
if (tags.containsKey(item.id)) {
|
||||||
|
await labelRepository.deleteTag(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,11 @@
|
|||||||
part of 'label_cubit.dart';
|
part of 'label_cubit.dart';
|
||||||
|
|
||||||
class LabelState<T extends Label> {
|
@freezed
|
||||||
LabelState.initial() : this(isLoaded: false, labels: {});
|
class LabelState with _$LabelState {
|
||||||
final bool isLoaded;
|
const factory LabelState({
|
||||||
final Map<int, T> labels;
|
@Default({}) Map<int, Correspondent> correspondents,
|
||||||
|
@Default({}) Map<int, DocumentType> documentTypes,
|
||||||
LabelState({
|
@Default({}) Map<int, Tag> tags,
|
||||||
this.isLoaded = false,
|
@Default({}) Map<int, StoragePath> storagePaths,
|
||||||
this.labels = const {},
|
}) = _LabelState;
|
||||||
});
|
|
||||||
|
|
||||||
T? getLabel(int? key) {
|
|
||||||
if (isLoaded) {
|
|
||||||
return labels[key];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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>(
|
|
||||||
context.read<LabelRepository<Correspondent>>(),
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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>(
|
|
||||||
context.read<LabelRepository<DocumentType>>(),
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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>(
|
|
||||||
context.read<LabelRepository<StoragePath>>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
BlocProvider<LabelCubit<Correspondent>>(
|
|
||||||
create: (context) => LabelCubit<Correspondent>(
|
|
||||||
context.read<LabelRepository<Correspondent>>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
BlocProvider<LabelCubit<DocumentType>>(
|
|
||||||
create: (context) => LabelCubit<DocumentType>(
|
|
||||||
context.read<LabelRepository<DocumentType>>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
BlocProvider<LabelCubit<Tag>>(
|
|
||||||
create: (context) => LabelCubit<Tag>(
|
|
||||||
context.read<LabelRepository<Tag>>(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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>(
|
|
||||||
context.read<LabelRepository<StoragePath>>(),
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/core/repository/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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>(
|
|
||||||
context.read<LabelRepository<Tag>>(),
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
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/features/labels/cubit/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/document_type_bloc_provider.dart';
|
|
||||||
|
|
||||||
class DocumentTypeWidget extends StatelessWidget {
|
class DocumentTypeWidget extends StatelessWidget {
|
||||||
final int? documentTypeId;
|
final DocumentType? documentType;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
final void Function(int? id)? onSelected;
|
final void Function(int? id)? onSelected;
|
||||||
const DocumentTypeWidget({
|
const DocumentTypeWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.documentTypeId,
|
required this.documentType,
|
||||||
this.isClickable = true,
|
this.isClickable = true,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.onSelected,
|
this.onSelected,
|
||||||
@@ -19,23 +16,16 @@ class DocumentTypeWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DocumentTypeBlocProvider(
|
return AbsorbPointer(
|
||||||
child: AbsorbPointer(
|
absorbing: !isClickable,
|
||||||
absorbing: !isClickable,
|
child: GestureDetector(
|
||||||
child: GestureDetector(
|
onTap: () => onSelected?.call(documentType?.id),
|
||||||
onTap: () => onSelected?.call(documentTypeId),
|
child: Text(
|
||||||
child:
|
documentType?.toString() ?? "-",
|
||||||
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
style: (textStyle ?? Theme.of(context).textTheme.bodyMedium)
|
||||||
builder: (context, state) {
|
?.copyWith(color: Theme.of(context).colorScheme.tertiary),
|
||||||
return Text(
|
overflow: TextOverflow.ellipsis,
|
||||||
state.labels[documentTypeId]?.toString() ?? "-",
|
maxLines: 1,
|
||||||
style: (textStyle ?? Theme.of(context).textTheme.bodyMedium)
|
|
||||||
?.copyWith(color: Theme.of(context).colorScheme.tertiary),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
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/features/labels/cubit/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/storage_path_bloc_provider.dart';
|
|
||||||
|
|
||||||
class StoragePathWidget extends StatelessWidget {
|
class StoragePathWidget extends StatelessWidget {
|
||||||
final int? pathId;
|
final StoragePath? storagePath;
|
||||||
final Color? textColor;
|
final Color? textColor;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
final void Function(int? id)? onSelected;
|
final void Function(int? id)? onSelected;
|
||||||
|
|
||||||
const StoragePathWidget({
|
const StoragePathWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.pathId,
|
this.storagePath,
|
||||||
this.textColor,
|
this.textColor,
|
||||||
this.isClickable = true,
|
this.isClickable = true,
|
||||||
this.onSelected,
|
this.onSelected,
|
||||||
@@ -20,23 +17,17 @@ class StoragePathWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StoragePathBlocProvider(
|
return AbsorbPointer(
|
||||||
child: AbsorbPointer(
|
absorbing: !isClickable,
|
||||||
absorbing: !isClickable,
|
child: GestureDetector(
|
||||||
child: BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
onTap: () => onSelected?.call(storagePath?.id),
|
||||||
builder: (context, state) {
|
child: Text(
|
||||||
return GestureDetector(
|
storagePath?.name ?? "-",
|
||||||
onTap: () => onSelected?.call(pathId),
|
maxLines: 1,
|
||||||
child: Text(
|
overflow: TextOverflow.ellipsis,
|
||||||
state.getLabel(pathId)?.name ?? "-",
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
maxLines: 1,
|
color: textColor ?? Theme.of(context).colorScheme.primary,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: textColor ?? Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|||||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
|
||||||
import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
|
import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_tag_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_tag_page.dart';
|
||||||
@@ -21,6 +20,8 @@ class TagFormField extends StatefulWidget {
|
|||||||
final bool excludeAllowed;
|
final bool excludeAllowed;
|
||||||
final Map<int, Tag> selectableOptions;
|
final Map<int, Tag> selectableOptions;
|
||||||
final Widget? suggestions;
|
final Widget? suggestions;
|
||||||
|
final String? labelText;
|
||||||
|
final String? hintText;
|
||||||
|
|
||||||
const TagFormField({
|
const TagFormField({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -32,6 +33,8 @@ class TagFormField extends StatefulWidget {
|
|||||||
this.excludeAllowed = true,
|
this.excludeAllowed = true,
|
||||||
required this.selectableOptions,
|
required this.selectableOptions,
|
||||||
this.suggestions,
|
this.suggestions,
|
||||||
|
this.labelText,
|
||||||
|
this.hintText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -94,8 +97,8 @@ class _TagFormFieldState extends State<TagFormField> {
|
|||||||
Icons.label_outline,
|
Icons.label_outline,
|
||||||
),
|
),
|
||||||
suffixIcon: _buildSuffixIcon(context, field),
|
suffixIcon: _buildSuffixIcon(context, field),
|
||||||
labelText: S.of(context)!.tags,
|
labelText: widget.labelText ?? S.of(context)!.tags,
|
||||||
hintText: S.of(context)!.filterTags,
|
hintText: widget.hintText ?? S.of(context)!.filterTags,
|
||||||
),
|
),
|
||||||
controller: _textEditingController,
|
controller: _textEditingController,
|
||||||
),
|
),
|
||||||
@@ -240,8 +243,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: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<Tag>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: AddTagPage(initialValue: _textEditingController.text),
|
child: AddTagPage(initialValue: _textEditingController.text),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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 StatelessWidget {
|
class TagsWidget extends StatelessWidget {
|
||||||
final Iterable<int> tagIds;
|
final List<Tag> tags;
|
||||||
final bool isMultiLine;
|
final bool isMultiLine;
|
||||||
final void Function(int tagId)? onTagSelected;
|
final void Function(int tagId)? onTagSelected;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
@@ -15,7 +12,7 @@ class TagsWidget extends StatelessWidget {
|
|||||||
|
|
||||||
const TagsWidget({
|
const TagsWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.tagIds,
|
required this.tags,
|
||||||
this.isMultiLine = true,
|
this.isMultiLine = true,
|
||||||
this.isClickable = true,
|
this.isClickable = true,
|
||||||
this.onTagSelected,
|
this.onTagSelected,
|
||||||
@@ -25,36 +22,33 @@ class TagsWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TagBlocProvider(
|
return Builder(
|
||||||
child: BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
builder: (context) {
|
||||||
builder: (context, state) {
|
final children = tags
|
||||||
final children = tagIds
|
.map(
|
||||||
.where((id) => state.labels.containsKey(id))
|
(tag) => TagWidget(
|
||||||
.map(
|
tag: tag,
|
||||||
(id) => TagWidget(
|
isClickable: isClickable,
|
||||||
tag: state.getLabel(id)!,
|
onSelected: () => onTagSelected?.call(tag.id!),
|
||||||
isClickable: isClickable,
|
showShortName: showShortNames,
|
||||||
onSelected: () => onTagSelected?.call(id),
|
dense: dense,
|
||||||
showShortName: showShortNames,
|
),
|
||||||
dense: dense,
|
)
|
||||||
),
|
.toList();
|
||||||
)
|
if (isMultiLine) {
|
||||||
.toList();
|
return Wrap(
|
||||||
if (isMultiLine) {
|
runAlignment: WrapAlignment.start,
|
||||||
return Wrap(
|
children: children,
|
||||||
runAlignment: WrapAlignment.start,
|
runSpacing: 4,
|
||||||
children: children,
|
spacing: 4,
|
||||||
runSpacing: 4,
|
);
|
||||||
spacing: 4,
|
} else {
|
||||||
);
|
return SingleChildScrollView(
|
||||||
} else {
|
scrollDirection: Axis.horizontal,
|
||||||
return SingleChildScrollView(
|
child: Row(children: children),
|
||||||
scrollDirection: Axis.horizontal,
|
);
|
||||||
child: Row(children: children),
|
}
|
||||||
);
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_he
|
|||||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/material/search/colored_tab_bar.dart';
|
import 'package:paperless_mobile/core/widgets/material/search/colored_tab_bar.dart';
|
||||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||||
@@ -17,6 +16,7 @@ import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_typ
|
|||||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_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/edit_label/view/impl/edit_tag_page.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/labels/cubit/label_cubit_mixin.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/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -141,12 +141,12 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
notificationPredicate: (notification) =>
|
notificationPredicate: (notification) =>
|
||||||
connectedState.isConnected,
|
connectedState.isConnected,
|
||||||
onRefresh: () => [
|
onRefresh: () => [
|
||||||
context.read<LabelCubit<Correspondent>>(),
|
context.read<LabelCubit>().reloadCorrespondents,
|
||||||
context.read<LabelCubit<DocumentType>>(),
|
context.read<LabelCubit>().reloadDocumentTypes,
|
||||||
context.read<LabelCubit<Tag>>(),
|
context.read<LabelCubit>().reloadTags,
|
||||||
context.read<LabelCubit<StoragePath>>(),
|
context.read<LabelCubit>().reloadStoragePaths,
|
||||||
][_currentIndex]
|
][_currentIndex]
|
||||||
.reload(),
|
.call(),
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: [
|
children: [
|
||||||
@@ -157,6 +157,10 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
SliverOverlapInjector(handle: searchBarHandle),
|
SliverOverlapInjector(handle: searchBarHandle),
|
||||||
SliverOverlapInjector(handle: tabBarHandle),
|
SliverOverlapInjector(handle: tabBarHandle),
|
||||||
LabelTabView<Correspondent>(
|
LabelTabView<Correspondent>(
|
||||||
|
labels: context
|
||||||
|
.watch<LabelCubit>()
|
||||||
|
.state
|
||||||
|
.correspondents,
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
correspondent:
|
correspondent:
|
||||||
IdQueryParameter.fromId(label.id),
|
IdQueryParameter.fromId(label.id),
|
||||||
@@ -180,6 +184,10 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
SliverOverlapInjector(handle: searchBarHandle),
|
SliverOverlapInjector(handle: searchBarHandle),
|
||||||
SliverOverlapInjector(handle: tabBarHandle),
|
SliverOverlapInjector(handle: tabBarHandle),
|
||||||
LabelTabView<DocumentType>(
|
LabelTabView<DocumentType>(
|
||||||
|
labels: context
|
||||||
|
.watch<LabelCubit>()
|
||||||
|
.state
|
||||||
|
.documentTypes,
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
documentType:
|
documentType:
|
||||||
IdQueryParameter.fromId(label.id),
|
IdQueryParameter.fromId(label.id),
|
||||||
@@ -203,6 +211,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
SliverOverlapInjector(handle: searchBarHandle),
|
SliverOverlapInjector(handle: searchBarHandle),
|
||||||
SliverOverlapInjector(handle: tabBarHandle),
|
SliverOverlapInjector(handle: tabBarHandle),
|
||||||
LabelTabView<Tag>(
|
LabelTabView<Tag>(
|
||||||
|
labels:
|
||||||
|
context.watch<LabelCubit>().state.tags,
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
tags: IdsTagsQuery.fromIds([label.id!]),
|
tags: IdsTagsQuery.fromIds([label.id!]),
|
||||||
pageSize: label.documentCount ?? 0,
|
pageSize: label.documentCount ?? 0,
|
||||||
@@ -234,6 +244,10 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
SliverOverlapInjector(handle: searchBarHandle),
|
SliverOverlapInjector(handle: searchBarHandle),
|
||||||
SliverOverlapInjector(handle: tabBarHandle),
|
SliverOverlapInjector(handle: tabBarHandle),
|
||||||
LabelTabView<StoragePath>(
|
LabelTabView<StoragePath>(
|
||||||
|
labels: context
|
||||||
|
.watch<LabelCubit>()
|
||||||
|
.state
|
||||||
|
.storagePaths,
|
||||||
onEdit: _openEditStoragePathPage,
|
onEdit: _openEditStoragePathPage,
|
||||||
filterBuilder: (label) => DocumentFilter(
|
filterBuilder: (label) => DocumentFilter(
|
||||||
storagePath:
|
storagePath:
|
||||||
@@ -267,8 +281,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<Correspondent>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: EditCorrespondentPage(correspondent: correspondent),
|
child: EditCorrespondentPage(correspondent: correspondent),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -279,8 +293,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<DocumentType>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: EditDocumentTypePage(documentType: docType),
|
child: EditDocumentTypePage(documentType: docType),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -291,8 +305,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<Tag>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: EditTagPage(tag: tag),
|
child: EditTagPage(tag: tag),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -303,8 +317,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: EditStoragePathPage(
|
child: EditStoragePathPage(
|
||||||
storagePath: path,
|
storagePath: path,
|
||||||
),
|
),
|
||||||
@@ -317,8 +331,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<Correspondent>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: const AddCorrespondentPage(),
|
child: const AddCorrespondentPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -329,8 +343,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<DocumentType>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: const AddDocumentTypePage(),
|
child: const AddDocumentTypePage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -341,8 +355,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<Tag>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: const AddTagPage(),
|
child: const AddTagPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -353,8 +367,8 @@ class _LabelsPageState extends State<LabelsPage>
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => RepositoryProvider(
|
builder: (_) => RepositoryProvider.value(
|
||||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
value: context.read<LabelRepository>(),
|
||||||
child: const AddStoragePathPage(),
|
child: const AddStoragePathPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class LabelItem<T extends Label> extends StatelessWidget {
|
|||||||
filter,
|
filter,
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
|
context.read(),
|
||||||
),
|
),
|
||||||
child: const LinkedDocumentsPage(),
|
child: const LinkedDocumentsPage(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ 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/translation/matching_algorithm_localization_mapper.dart';
|
import 'package:paperless_mobile/core/translation/matching_algorithm_localization_mapper.dart';
|
||||||
import 'package:paperless_mobile/features/labels/cubit/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/view/widgets/label_item.dart';
|
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 Map<int, T> labels;
|
||||||
final DocumentFilter Function(Label) filterBuilder;
|
final DocumentFilter Function(Label) filterBuilder;
|
||||||
final void Function(T) onEdit;
|
final void Function(T) onEdit;
|
||||||
final void Function() onAddNew;
|
final void Function() onAddNew;
|
||||||
@@ -32,69 +32,61 @@ class LabelTabView<T extends Label> extends StatelessWidget {
|
|||||||
required this.emptyStateDescription,
|
required this.emptyStateDescription,
|
||||||
required this.onAddNew,
|
required this.onAddNew,
|
||||||
required this.emptyStateActionButtonLabel,
|
required this.emptyStateActionButtonLabel,
|
||||||
|
required this.labels,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
create: (context) => LabelCubit<T>(
|
builder: (context, connectivityState) {
|
||||||
context.read(),
|
if (!connectivityState.isConnected) {
|
||||||
),
|
return const OfflineWidget();
|
||||||
child: BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
}
|
||||||
builder: (context, connectivityState) {
|
final sortedLabels = labels.values.toList()..sort();
|
||||||
return BlocBuilder<LabelCubit<T>, LabelState<T>>(
|
if (labels.isEmpty) {
|
||||||
builder: (context, state) {
|
return SliverFillRemaining(
|
||||||
if (!state.isLoaded && !connectivityState.isConnected) {
|
child: Center(
|
||||||
return const OfflineWidget();
|
child: Column(
|
||||||
}
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
final labels = state.labels.values.toList()..sort();
|
children: [
|
||||||
if (labels.isEmpty) {
|
Text(
|
||||||
return SliverFillRemaining(
|
emptyStateDescription,
|
||||||
child: Center(
|
textAlign: TextAlign.center,
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
emptyStateDescription,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: onAddNew,
|
|
||||||
child: Text(emptyStateActionButtonLabel),
|
|
||||||
),
|
|
||||||
].padded(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
TextButton(
|
||||||
}
|
onPressed: onAddNew,
|
||||||
return SliverList(
|
child: Text(emptyStateActionButtonLabel),
|
||||||
delegate: SliverChildBuilderDelegate(
|
),
|
||||||
(context, index) {
|
].padded(),
|
||||||
final l = labels.elementAt(index);
|
),
|
||||||
return LabelItem<T>(
|
),
|
||||||
name: l.name,
|
);
|
||||||
content: contentBuilder?.call(l) ??
|
}
|
||||||
Text(
|
return SliverList(
|
||||||
translateMatchingAlgorithmName(
|
delegate: SliverChildBuilderDelegate(
|
||||||
context, l.matchingAlgorithm) +
|
(context, index) {
|
||||||
((l.match?.isNotEmpty ?? false)
|
final l = sortedLabels.elementAt(index);
|
||||||
? ": ${l.match}"
|
return LabelItem<T>(
|
||||||
: ""),
|
name: l.name,
|
||||||
maxLines: 2,
|
content: contentBuilder?.call(l) ??
|
||||||
),
|
Text(
|
||||||
onOpenEditPage: onEdit,
|
translateMatchingAlgorithmName(
|
||||||
filterBuilder: filterBuilder,
|
context, l.matchingAlgorithm) +
|
||||||
leading: leadingBuilder?.call(l),
|
((l.match?.isNotEmpty ?? false)
|
||||||
label: l,
|
? ": ${l.match}"
|
||||||
);
|
: ""),
|
||||||
},
|
maxLines: 2,
|
||||||
childCount: labels.length,
|
),
|
||||||
),
|
onOpenEditPage: onEdit,
|
||||||
|
filterBuilder: filterBuilder,
|
||||||
|
leading: leadingBuilder?.call(l),
|
||||||
|
label: l,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
childCount: labels.length,
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,24 @@
|
|||||||
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/label_repository.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
|
||||||
|
|
||||||
class LabelText<T extends Label> extends StatelessWidget {
|
class LabelText<T extends Label> extends StatelessWidget {
|
||||||
final int? id;
|
final T? label;
|
||||||
final String placeholder;
|
final String placeholder;
|
||||||
final TextStyle? style;
|
final TextStyle? style;
|
||||||
|
|
||||||
const LabelText({
|
const LabelText({
|
||||||
super.key,
|
super.key,
|
||||||
this.style,
|
this.style,
|
||||||
this.id,
|
|
||||||
this.placeholder = "",
|
this.placeholder = "",
|
||||||
|
required this.label,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return Text(
|
||||||
create: (context) => LabelCubit<T>(
|
label?.toString() ?? placeholder,
|
||||||
context.read<LabelRepository<T>>(),
|
style: style,
|
||||||
),
|
maxLines: 1,
|
||||||
child: BlocBuilder<LabelCubit<T>, LabelState<T>>(
|
overflow: TextOverflow.ellipsis,
|
||||||
builder: (context, state) {
|
|
||||||
return Text(
|
|
||||||
state.labels[id]?.toString() ?? placeholder,
|
|
||||||
style: style,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
@@ -17,12 +18,26 @@ class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
|
|||||||
@override
|
@override
|
||||||
final DocumentChangedNotifier notifier;
|
final DocumentChangedNotifier notifier;
|
||||||
|
|
||||||
|
final LabelRepository _labelRepository;
|
||||||
|
|
||||||
LinkedDocumentsCubit(
|
LinkedDocumentsCubit(
|
||||||
DocumentFilter filter,
|
DocumentFilter filter,
|
||||||
this.api,
|
this.api,
|
||||||
this.notifier,
|
this.notifier,
|
||||||
|
this._labelRepository,
|
||||||
) : super(const LinkedDocumentsState()) {
|
) : super(const LinkedDocumentsState()) {
|
||||||
updateFilter(filter: filter);
|
updateFilter(filter: filter);
|
||||||
|
_labelRepository.subscribe(
|
||||||
|
this,
|
||||||
|
onChanged: (labels) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
correspondents: labels.correspondents,
|
||||||
|
documentTypes: labels.documentTypes,
|
||||||
|
tags: labels.tags,
|
||||||
|
storagePaths: labels.storagePaths,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
notifier.subscribe(
|
notifier.subscribe(
|
||||||
this,
|
this,
|
||||||
onUpdated: replace,
|
onUpdated: replace,
|
||||||
|
|||||||
@@ -4,12 +4,22 @@ part of 'linked_documents_cubit.dart';
|
|||||||
class LinkedDocumentsState extends DocumentPagingState {
|
class LinkedDocumentsState extends DocumentPagingState {
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final ViewType viewType;
|
final ViewType viewType;
|
||||||
|
|
||||||
|
final Map<int, Correspondent> correspondents;
|
||||||
|
final Map<int, DocumentType> documentTypes;
|
||||||
|
final Map<int, StoragePath> storagePaths;
|
||||||
|
final Map<int, Tag> tags;
|
||||||
|
|
||||||
const LinkedDocumentsState({
|
const LinkedDocumentsState({
|
||||||
this.viewType = ViewType.list,
|
this.viewType = ViewType.list,
|
||||||
super.filter,
|
super.filter,
|
||||||
super.isLoading,
|
super.isLoading,
|
||||||
super.hasLoaded,
|
super.hasLoaded,
|
||||||
super.value,
|
super.value,
|
||||||
|
this.correspondents = const {},
|
||||||
|
this.documentTypes = const {},
|
||||||
|
this.storagePaths = const {},
|
||||||
|
this.tags = const {},
|
||||||
});
|
});
|
||||||
|
|
||||||
LinkedDocumentsState copyWith({
|
LinkedDocumentsState copyWith({
|
||||||
@@ -18,6 +28,10 @@ class LinkedDocumentsState extends DocumentPagingState {
|
|||||||
bool? hasLoaded,
|
bool? hasLoaded,
|
||||||
List<PagedSearchResult<DocumentModel>>? value,
|
List<PagedSearchResult<DocumentModel>>? value,
|
||||||
ViewType? viewType,
|
ViewType? viewType,
|
||||||
|
Map<int, Correspondent>? correspondents,
|
||||||
|
Map<int, DocumentType>? documentTypes,
|
||||||
|
Map<int, StoragePath>? storagePaths,
|
||||||
|
Map<int, Tag>? tags,
|
||||||
}) {
|
}) {
|
||||||
return LinkedDocumentsState(
|
return LinkedDocumentsState(
|
||||||
filter: filter ?? this.filter,
|
filter: filter ?? this.filter,
|
||||||
@@ -25,6 +39,10 @@ class LinkedDocumentsState extends DocumentPagingState {
|
|||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
hasLoaded: hasLoaded ?? this.hasLoaded,
|
||||||
value: value ?? this.value,
|
value: value ?? this.value,
|
||||||
viewType: viewType ?? this.viewType,
|
viewType: viewType ?? this.viewType,
|
||||||
|
correspondents: correspondents ?? this.correspondents,
|
||||||
|
documentTypes: documentTypes ?? this.documentTypes,
|
||||||
|
storagePaths: storagePaths ?? this.storagePaths,
|
||||||
|
tags: tags ?? this.tags,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
correspondents: state.correspondents,
|
||||||
|
documentTypes: state.documentTypes,
|
||||||
|
storagePaths: state.storagePaths,
|
||||||
|
tags: state.tags,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,42 +2,72 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.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/saved_view_repository.dart';
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
|
|
||||||
part 'saved_view_state.dart';
|
part 'saved_view_state.dart';
|
||||||
|
part 'saved_view_cubit.freezed.dart';
|
||||||
|
|
||||||
class SavedViewCubit extends Cubit<SavedViewState> {
|
class SavedViewCubit extends Cubit<SavedViewState> {
|
||||||
final SavedViewRepository _repository;
|
final SavedViewRepository _savedViewRepository;
|
||||||
StreamSubscription? _subscription;
|
final LabelRepository _labelRepository;
|
||||||
|
|
||||||
SavedViewCubit(this._repository) : super(const SavedViewState()) {
|
SavedViewCubit(this._savedViewRepository, this._labelRepository)
|
||||||
_subscription = _repository.values.listen(
|
: super(SavedViewState.initial(
|
||||||
(savedViews) {
|
correspondents: _labelRepository.state.correspondents,
|
||||||
if (savedViews?.hasLoaded ?? false) {
|
documentTypes: _labelRepository.state.documentTypes,
|
||||||
emit(state.copyWith(value: savedViews?.values, hasLoaded: true));
|
storagePaths: _labelRepository.state.storagePaths,
|
||||||
} else {
|
tags: _labelRepository.state.tags,
|
||||||
emit(state.copyWith(hasLoaded: false));
|
)) {
|
||||||
}
|
_labelRepository.subscribe(
|
||||||
|
this,
|
||||||
|
onChanged: (labels) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
correspondents: labels.correspondents,
|
||||||
|
documentTypes: labels.documentTypes,
|
||||||
|
tags: labels.tags,
|
||||||
|
storagePaths: labels.storagePaths,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_savedViewRepository.subscribe(this, (views) {
|
||||||
|
emit(
|
||||||
|
state.maybeWhen(
|
||||||
|
loaded:
|
||||||
|
(savedViews, correspondents, documentTypes, tags, storagePaths) =>
|
||||||
|
(state as _SavedViewLoadedState).copyWith(
|
||||||
|
savedViews: views,
|
||||||
|
),
|
||||||
|
orElse: () => state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SavedView> add(SavedView view) async {
|
Future<SavedView> add(SavedView view) async {
|
||||||
final savedView = await _repository.create(view);
|
return _savedViewRepository.create(view);
|
||||||
emit(state.copyWith(value: {...state.value, savedView.id!: savedView}));
|
|
||||||
return savedView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> remove(SavedView view) {
|
Future<int> remove(SavedView view) {
|
||||||
return _repository.delete(view);
|
return _savedViewRepository.delete(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
final views = await _repository.findAll();
|
final views = await _savedViewRepository.findAll();
|
||||||
final values = {for (var element in views) element.id!: element};
|
final values = {for (var element in views) element.id!: element};
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
emit(SavedViewState(value: values, hasLoaded: true));
|
emit(SavedViewState.loaded(
|
||||||
|
savedViews: values,
|
||||||
|
correspondents: state.correspondents,
|
||||||
|
documentTypes: state.documentTypes,
|
||||||
|
storagePaths: state.storagePaths,
|
||||||
|
tags: state.tags,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +75,8 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_subscription?.cancel();
|
_savedViewRepository.unsubscribe(this);
|
||||||
|
_labelRepository.unsubscribe(this);
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +1,33 @@
|
|||||||
part of 'saved_view_cubit.dart';
|
part of 'saved_view_cubit.dart';
|
||||||
|
|
||||||
class SavedViewState extends Equatable {
|
@freezed
|
||||||
final bool hasLoaded;
|
class SavedViewState with _$SavedViewState {
|
||||||
final Map<int, SavedView> value;
|
const factory SavedViewState.initial({
|
||||||
|
required Map<int, Correspondent> correspondents,
|
||||||
|
required Map<int, DocumentType> documentTypes,
|
||||||
|
required Map<int, Tag> tags,
|
||||||
|
required Map<int, StoragePath> storagePaths,
|
||||||
|
}) = _SavedViewIntialState;
|
||||||
|
|
||||||
const SavedViewState({
|
const factory SavedViewState.loading({
|
||||||
this.value = const {},
|
required Map<int, Correspondent> correspondents,
|
||||||
this.hasLoaded = false,
|
required Map<int, DocumentType> documentTypes,
|
||||||
});
|
required Map<int, Tag> tags,
|
||||||
|
required Map<int, StoragePath> storagePaths,
|
||||||
|
}) = _SavedViewLoadingState;
|
||||||
|
|
||||||
@override
|
const factory SavedViewState.loaded({
|
||||||
List<Object?> get props => [
|
required Map<int, SavedView> savedViews,
|
||||||
hasLoaded,
|
required Map<int, Correspondent> correspondents,
|
||||||
value,
|
required Map<int, DocumentType> documentTypes,
|
||||||
];
|
required Map<int, Tag> tags,
|
||||||
|
required Map<int, StoragePath> storagePaths,
|
||||||
|
}) = _SavedViewLoadedState;
|
||||||
|
|
||||||
SavedViewState copyWith({
|
const factory SavedViewState.error({
|
||||||
Map<int, SavedView>? value,
|
required Map<int, Correspondent> correspondents,
|
||||||
int? selectedSavedViewId,
|
required Map<int, DocumentType> documentTypes,
|
||||||
bool overwriteSelectedSavedViewId = false,
|
required Map<int, Tag> tags,
|
||||||
bool? hasLoaded,
|
required Map<int, StoragePath> storagePaths,
|
||||||
}) {
|
}) = _SavedViewErrorState;
|
||||||
return SavedViewState(
|
|
||||||
value: value ?? this.value,
|
|
||||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(),
|
const Divider(),
|
||||||
Text(
|
Text(
|
||||||
"Review filter",
|
"Review filter",
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
@@ -80,42 +80,6 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Padding _buildOld(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
FormBuilder(
|
|
||||||
key: _savedViewFormKey,
|
|
||||||
child: Expanded(
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
FormBuilderTextField(
|
|
||||||
name: fkName,
|
|
||||||
validator: FormBuilderValidators.required(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
label: Text(S.of(context)!.name),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FormBuilderCheckbox(
|
|
||||||
name: fkShowOnDashboard,
|
|
||||||
initialValue: false,
|
|
||||||
title: Text(S.of(context)!.showOnDashboard),
|
|
||||||
),
|
|
||||||
FormBuilderCheckbox(
|
|
||||||
name: fkShowInSidebar,
|
|
||||||
initialValue: false,
|
|
||||||
title: Text(S.of(context)!.showInSidebar),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onCreate(BuildContext context) {
|
void _onCreate(BuildContext context) {
|
||||||
if (_savedViewFormKey.currentState?.saveAndValidate() ?? false) {
|
if (_savedViewFormKey.currentState?.saveAndValidate() ?? false) {
|
||||||
Navigator.pop(
|
Navigator.pop(
|
||||||
|
|||||||
@@ -12,52 +12,70 @@ class SavedViewList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final savedViewCubit = context.read<SavedViewCubit>();
|
|
||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectivity) {
|
builder: (context, connectivity) {
|
||||||
return BlocBuilder<SavedViewCubit, SavedViewState>(
|
return BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.value.isEmpty) {
|
return state.when(
|
||||||
return SliverToBoxAdapter(
|
initial: (correspondents, documentTypes, tags, storagePaths) =>
|
||||||
child: HintCard(
|
Container(),
|
||||||
hintText:
|
loading: (correspondents, documentTypes, tags, storagePaths) =>
|
||||||
S.of(context)!.createViewsToQuicklyFilterYourDocuments,
|
Center(
|
||||||
),
|
child: Text("Saved views loading..."),
|
||||||
);
|
),
|
||||||
}
|
loaded: (savedViews, correspondents, documentTypes, tags,
|
||||||
return SliverList(
|
storagePaths) {
|
||||||
delegate: SliverChildBuilderDelegate(
|
if (savedViews.isEmpty) {
|
||||||
(context, index) {
|
return SliverToBoxAdapter(
|
||||||
final view = state.value.values.elementAt(index);
|
child: HintCard(
|
||||||
return ListTile(
|
hintText: S
|
||||||
enabled: connectivity.isConnected,
|
.of(context)!
|
||||||
title: Text(view.name),
|
.createViewsToQuicklyFilterYourDocuments,
|
||||||
subtitle: Text(
|
|
||||||
S.of(context)!.nFiltersSet(view.filterRules.length),
|
|
||||||
),
|
),
|
||||||
onTap: () {
|
);
|
||||||
Navigator.of(context).push(
|
}
|
||||||
MaterialPageRoute(
|
return SliverList(
|
||||||
builder: (context) => MultiBlocProvider(
|
delegate: SliverChildBuilderDelegate(
|
||||||
providers: [
|
(context, index) {
|
||||||
BlocProvider(
|
final view = savedViews.values.elementAt(index);
|
||||||
create: (context) => SavedViewDetailsCubit(
|
return ListTile(
|
||||||
context.read(),
|
enabled: connectivity.isConnected,
|
||||||
context.read(),
|
title: Text(view.name),
|
||||||
savedView: view,
|
subtitle: Text(
|
||||||
|
S.of(context)!.nFiltersSet(view.filterRules.length),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => SavedViewDetailsCubit(
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
savedView: view,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: SavedViewDetailsPage(
|
||||||
|
onDelete:
|
||||||
|
context.read<SavedViewCubit>().remove,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
child: SavedViewDetailsPage(
|
|
||||||
onDelete: savedViewCubit.remove,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
childCount: savedViews.length,
|
||||||
},
|
),
|
||||||
childCount: state.value.length,
|
);
|
||||||
|
},
|
||||||
|
error: (correspondents, documentTypes, tags, storagePaths) =>
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
"An error occurred while trying to load the saved views.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,10 +82,7 @@ class AccountSettingsDialog extends StatelessWidget {
|
|||||||
try {
|
try {
|
||||||
await context.read<AuthenticationCubit>().logout();
|
await context.read<AuthenticationCubit>().logout();
|
||||||
await context.read<ApplicationSettingsCubit>().clear();
|
await context.read<ApplicationSettingsCubit>().clear();
|
||||||
await context.read<LabelRepository<Tag>>().clear();
|
await context.read<LabelRepository>().clear();
|
||||||
await context.read<LabelRepository<Correspondent>>().clear();
|
|
||||||
await context.read<LabelRepository<DocumentType>>().clear();
|
|
||||||
await context.read<LabelRepository<StoragePath>>().clear();
|
|
||||||
await context.read<SavedViewRepository>().clear();
|
await context.read<SavedViewRepository>().clear();
|
||||||
await HydratedBloc.storage.clear();
|
await HydratedBloc.storage.clear();
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
|||||||
+3
-20
@@ -21,17 +21,12 @@ import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.da
|
|||||||
import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
|
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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/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/label_repository.dart';
|
||||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
import 'package:paperless_mobile/core/security/session_manager.dart';
|
import 'package:paperless_mobile/core/security/session_manager.dart';
|
||||||
import 'package:paperless_mobile/core/service/connectivity_status_service.dart';
|
import 'package:paperless_mobile/core/service/connectivity_status_service.dart';
|
||||||
import 'package:paperless_mobile/core/service/dio_file_service.dart';
|
import 'package:paperless_mobile/core/service/dio_file_service.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.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/home/view/home_page.dart';
|
import 'package:paperless_mobile/features/home/view/home_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/widget/verify_identity_page.dart';
|
import 'package:paperless_mobile/features/home/view/widget/verify_identity_page.dart';
|
||||||
@@ -105,10 +100,7 @@ void main() async {
|
|||||||
await connectivityCubit.initialize();
|
await connectivityCubit.initialize();
|
||||||
|
|
||||||
// Create repositories
|
// Create repositories
|
||||||
final tagRepository = TagRepositoryImpl(labelsApi);
|
final labelRepository = LabelRepository(labelsApi);
|
||||||
final correspondentRepository = CorrespondentRepositoryImpl(labelsApi);
|
|
||||||
final documentTypeRepository = DocumentTypeRepositoryImpl(labelsApi);
|
|
||||||
final storagePathRepository = StoragePathRepositoryImpl(labelsApi);
|
|
||||||
final savedViewRepository = SavedViewRepositoryImpl(savedViewsApi);
|
final savedViewRepository = SavedViewRepositoryImpl(savedViewsApi);
|
||||||
|
|
||||||
//Create cubits/blocs
|
//Create cubits/blocs
|
||||||
@@ -163,17 +155,8 @@ void main() async {
|
|||||||
],
|
],
|
||||||
child: MultiRepositoryProvider(
|
child: MultiRepositoryProvider(
|
||||||
providers: [
|
providers: [
|
||||||
RepositoryProvider<LabelRepository<Tag>>.value(
|
RepositoryProvider<LabelRepository>.value(
|
||||||
value: tagRepository,
|
value: labelRepository,
|
||||||
),
|
|
||||||
RepositoryProvider<LabelRepository<Correspondent>>.value(
|
|
||||||
value: correspondentRepository,
|
|
||||||
),
|
|
||||||
RepositoryProvider<LabelRepository<DocumentType>>.value(
|
|
||||||
value: documentTypeRepository,
|
|
||||||
),
|
|
||||||
RepositoryProvider<LabelRepository<StoragePath>>.value(
|
|
||||||
value: storagePathRepository,
|
|
||||||
),
|
),
|
||||||
RepositoryProvider<SavedViewRepository>.value(
|
RepositoryProvider<SavedViewRepository>.value(
|
||||||
value: savedViewRepository,
|
value: savedViewRepository,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart';
|
import 'package:paperless_mobile/features/document_details/cubit/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';
|
||||||
|
|
||||||
@@ -19,9 +18,11 @@ class DocumentDetailsRoute extends StatelessWidget {
|
|||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
context.read(),
|
context.read(),
|
||||||
|
context.read(),
|
||||||
initialDocument: args.document,
|
initialDocument: args.document,
|
||||||
),
|
),
|
||||||
child: LabelRepositoriesProvider(
|
child: RepositoryProvider.value(
|
||||||
|
value: context.read(),
|
||||||
child: DocumentDetailsPage(
|
child: DocumentDetailsPage(
|
||||||
allowEdit: args.allowEdit,
|
allowEdit: args.allowEdit,
|
||||||
isLabelClickable: args.isLabelClickable,
|
isLabelClickable: args.isLabelClickable,
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
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<SavedView> find(int id);
|
Future<SavedView?> find(int id);
|
||||||
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]);
|
Future<Iterable<SavedView>> findAll([Iterable<int>? ids]);
|
||||||
|
|
||||||
Future<SavedView> save(SavedView view);
|
Future<SavedView> save(SavedView view);
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ class PaperlessSavedViewsApiImpl implements PaperlessSavedViewsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SavedView> find(int id) {
|
Future<SavedView?> find(int id) {
|
||||||
return getSingleResult(
|
return getSingleResult(
|
||||||
"/api/saved_views/$id/",
|
"/api/saved_views/$id/",
|
||||||
SavedView.fromJson,
|
SavedView.fromJson,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:paperless_api/src/models/paperless_server_exception.dart';
|
import 'package:paperless_api/src/models/paperless_server_exception.dart';
|
||||||
|
|
||||||
Future<T> getSingleResult<T>(
|
Future<T?> getSingleResult<T>(
|
||||||
String url,
|
String url,
|
||||||
T Function(Map<String, dynamic>) fromJson,
|
T Function(Map<String, dynamic>) fromJson,
|
||||||
ErrorCode errorCode, {
|
ErrorCode errorCode, {
|
||||||
|
|||||||
@@ -731,6 +731,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.0"
|
version: "8.4.0"
|
||||||
|
freezed:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: freezed
|
||||||
|
sha256: e819441678f1679b719008ff2ff0ef045d66eed9f9ec81166ca0d9b02a187454
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
freezed_annotation:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: freezed_annotation
|
||||||
|
sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ dependencies:
|
|||||||
dynamic_color: ^1.5.4
|
dynamic_color: ^1.5.4
|
||||||
flutter_html: ^3.0.0-alpha.6
|
flutter_html: ^3.0.0-alpha.6
|
||||||
in_app_review: ^2.0.6
|
in_app_review: ^2.0.6
|
||||||
|
freezed_annotation: ^2.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
@@ -105,6 +106,7 @@ dev_dependencies:
|
|||||||
json_serializable: ^6.5.4
|
json_serializable: ^6.5.4
|
||||||
dart_code_metrics: ^5.4.0
|
dart_code_metrics: ^5.4.0
|
||||||
auto_route_generator: ^5.0.3
|
auto_route_generator: ^5.0.3
|
||||||
|
freezed: ^2.3.2
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
Reference in New Issue
Block a user