fix: Refactor labels structure

This commit is contained in:
Anton Stubenbord
2023-04-05 19:44:58 +02:00
parent a2388b014b
commit 79ccdd0946
35 changed files with 357 additions and 234 deletions

View File

@@ -23,8 +23,8 @@ class DocumentChangedNotifier {
_deleted.add(deleted); _deleted.add(deleted);
} }
void subscribe( void addListener(
dynamic subscriber, { Object subscriber, {
DocumentChangedCallback? onUpdated, DocumentChangedCallback? onUpdated,
DocumentChangedCallback? onDeleted, DocumentChangedCallback? onDeleted,
}) { }) {
@@ -41,7 +41,7 @@ class DocumentChangedNotifier {
); );
} }
void unsubscribe(dynamic subscriber) { void removeListener(Object subscriber) {
_subscribers[subscriber]?.forEach((element) { _subscribers[subscriber]?.forEach((element) {
element.cancel(); element.cancel();
}); });

View File

@@ -8,7 +8,9 @@ class LabelRepository extends HydratedCubit<LabelRepositoryState> {
final PaperlessLabelsApi _api; final PaperlessLabelsApi _api;
final Map<Object, StreamSubscription> _subscribers = {}; final Map<Object, StreamSubscription> _subscribers = {};
void subscribe( LabelRepository(this._api) : super(const LabelRepositoryState());
void addListener(
Object source, { Object source, {
required void Function(LabelRepositoryState) onChanged, required void Function(LabelRepositoryState) onChanged,
}) { }) {
@@ -18,12 +20,19 @@ class LabelRepository extends HydratedCubit<LabelRepositoryState> {
}); });
} }
void unsubscribe(Object source) async { void removeListener(Object source) async {
await _subscribers[source]?.cancel(); await _subscribers[source]?.cancel();
_subscribers.remove(source); _subscribers.remove(source);
} }
LabelRepository(this._api) : super(const LabelRepositoryState()); Future<void> initialize() {
return Future.wait([
findAllCorrespondents(),
findAllDocumentTypes(),
findAllStoragePaths(),
findAllTags(),
]);
}
Future<Tag> createTag(Tag object) async { Future<Tag> createTag(Tag object) async {
final created = await _api.saveTag(object); final created = await _api.saveTag(object);

View File

@@ -30,7 +30,7 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
tags: _labelRepository.state.tags, tags: _labelRepository.state.tags,
), ),
) { ) {
_notifier.subscribe( _notifier.addListener(
this, this,
onDeleted: (document) { onDeleted: (document) {
// Remove items from internal selection after the document was deleted. // Remove items from internal selection after the document was deleted.
@@ -43,7 +43,7 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
); );
}, },
); );
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onChanged: (labels) { onChanged: (labels) {
emit( emit(
@@ -142,8 +142,8 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
@override @override
Future<void> close() { Future<void> close() {
_notifier.unsubscribe(this); _notifier.removeListener(this);
_labelRepository.unsubscribe(this); _labelRepository.removeListener(this);
return super.close(); return super.close();
} }
} }

View File

@@ -31,8 +31,8 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
}) : super(DocumentDetailsState( }) : super(DocumentDetailsState(
document: initialDocument, document: initialDocument,
)) { )) {
_notifier.subscribe(this, onUpdated: replace); _notifier.addListener(this, onUpdated: replace);
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onChanged: (labels) => emit( onChanged: (labels) => emit(
state.copyWith( state.copyWith(
@@ -210,7 +210,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
for (final element in _subscriptions) { for (final element in _subscriptions) {
await element.cancel(); await element.cancel();
} }
_notifier.unsubscribe(this); _notifier.removeListener(this);
await super.close(); await super.close();
} }
} }

View File

@@ -176,6 +176,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
builder: (context, state) { builder: (context, state) {
return BlocProvider( return BlocProvider(
create: (context) => SimilarDocumentsCubit( create: (context) => SimilarDocumentsCubit(
context.read(),
context.read(), context.read(),
context.read(), context.read(),
documentId: state.document.id, documentId: state.document.id,
@@ -186,7 +187,10 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
document: state.document, document: state.document,
itemSpacing: _itemSpacing, itemSpacing: _itemSpacing,
queryString: widget.titleAndContentQueryString, queryString: widget.titleAndContentQueryString,
availableCorrespondents: state.correspondents,
availableDocumentTypes: state.documentTypes,
availableTags: state.tags,
availableStoragePaths: state.storagePaths,
), ),
DocumentContentWidget( DocumentContentWidget(
isFullContentLoaded: state.isFullContentLoaded, isFullContentLoaded: state.isFullContentLoaded,
@@ -215,7 +219,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>( return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
builder: (context, state) { builder: (context, state) {
final _filteredSuggestions = final _filteredSuggestions =
state.suggestions.documentDifference(state.document); state.suggestions?.documentDifference(state.document);
return BlocBuilder<ConnectivityCubit, ConnectivityState>( return BlocBuilder<ConnectivityCubit, ConnectivityState>(
builder: (context, connectivityState) { builder: (context, connectivityState) {
if (!connectivityState.isConnected) { if (!connectivityState.isConnected) {
@@ -223,7 +227,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
} }
return b.Badge( return b.Badge(
position: b.BadgePosition.topEnd(top: -12, end: -6), position: b.BadgePosition.topEnd(top: -12, end: -6),
showBadge: _filteredSuggestions.hasSuggestions, showBadge: _filteredSuggestions?.hasSuggestions ?? false,
child: Tooltip( child: Tooltip(
message: S.of(context)!.editDocumentTooltip, message: S.of(context)!.editDocumentTooltip,
preferBelow: false, preferBelow: false,
@@ -234,7 +238,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
), ),
), ),
badgeContent: Text( badgeContent: Text(
'${_filteredSuggestions.suggestionsCount}', '${_filteredSuggestions?.suggestionsCount ?? 0}',
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
), ),
@@ -300,13 +304,10 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
providers: [ providers: [
BlocProvider.value( BlocProvider.value(
value: DocumentEditCubit( value: DocumentEditCubit(
document, context.read(),
documentsApi: context.read(), context.read(),
correspondentRepository: context.read(), context.read(),
documentTypeRepository: context.read(), document: document,
storagePathRepository: context.read(),
tagRepository: context.read(),
notifier: context.read(),
), ),
), ),
BlocProvider<DocumentDetailsCubit>.value( BlocProvider<DocumentDetailsCubit>.value(

View File

@@ -32,10 +32,10 @@ class DocumentEditCubit extends Cubit<DocumentEditState> {
tags: _labelRepository.state.tags, tags: _labelRepository.state.tags,
), ),
) { ) {
_notifier.subscribe(this, onUpdated: replace); _notifier.addListener(this, onUpdated: replace);
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onStateChanged: (labels) => emit(state.copyWith()), onChanged: (labels) => emit(state.copyWith()),
); );
} }
@@ -71,7 +71,7 @@ class DocumentEditCubit extends Cubit<DocumentEditState> {
for (final sub in _subscriptions) { for (final sub in _subscriptions) {
sub.cancel(); sub.cancel();
} }
_notifier.unsubscribe(this); _notifier.removeListener(this);
return super.close(); return super.close();
} }
} }

View File

@@ -21,7 +21,7 @@ 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';
class DocumentEditPage extends StatefulWidget { class DocumentEditPage extends StatefulWidget {
final FieldSuggestions suggestions; final FieldSuggestions? suggestions;
const DocumentEditPage({ const DocumentEditPage({
Key? key, Key? key,
required this.suggestions, required this.suggestions,
@@ -43,13 +43,13 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
final GlobalKey<FormBuilderState> _formKey = GlobalKey(); final GlobalKey<FormBuilderState> _formKey = GlobalKey();
bool _isSubmitLoading = false; bool _isSubmitLoading = false;
late final FieldSuggestions _filteredSuggestions; late final FieldSuggestions? _filteredSuggestions;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_filteredSuggestions = widget.suggestions _filteredSuggestions = widget.suggestions
.documentDifference(context.read<DocumentEditCubit>().state.document); ?.documentDifference(context.read<DocumentEditCubit>().state.document);
} }
@override @override
@@ -115,12 +115,14 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
excludeAllowed: false, excludeAllowed: false,
name: fkTags, name: fkTags,
selectableOptions: state.tags, selectableOptions: state.tags,
suggestions: _filteredSuggestions.tags suggestions: (_filteredSuggestions?.tags.toSet() ??
.toSet() {})
.difference(state.document.tags.toSet()) .difference(state.document.tags.toSet())
.isNotEmpty .isNotEmpty
? _buildSuggestionsSkeleton<int>( ? _buildSuggestionsSkeleton<int>(
suggestions: _filteredSuggestions.tags, suggestions:
(_filteredSuggestions?.tags.toSet() ??
{}),
itemBuilder: (context, itemData) { itemBuilder: (context, itemData) {
final tag = state.tags[itemData]!; final tag = state.tags[itemData]!;
return ActionChip( return ActionChip(
@@ -216,8 +218,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
LabelFormField<Correspondent>( LabelFormField<Correspondent>(
notAssignedSelectable: false, notAssignedSelectable: false,
formBuilderState: _formKey.currentState, formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider( labelCreationWidgetBuilder: (initialValue) =>
create: (context) => context.read<LabelRepository<Correspondent>>(), RepositoryProvider.value(
value: context.read<LabelRepository>(),
child: AddCorrespondentPage(initialName: initialValue), child: AddCorrespondentPage(initialName: initialValue),
), ),
textFieldLabel: S.of(context)!.correspondent, textFieldLabel: S.of(context)!.correspondent,
@@ -226,9 +229,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
name: fkCorrespondent, name: fkCorrespondent,
prefixIcon: const Icon(Icons.person_outlined), prefixIcon: const Icon(Icons.person_outlined),
), ),
if (_filteredSuggestions.hasSuggestedCorrespondents) if (_filteredSuggestions?.hasSuggestedCorrespondents ?? false)
_buildSuggestionsSkeleton<int>( _buildSuggestionsSkeleton<int>(
suggestions: _filteredSuggestions.correspondents, suggestions: _filteredSuggestions!.correspondents,
itemBuilder: (context, itemData) => ActionChip( itemBuilder: (context, itemData) => ActionChip(
label: Text(options[itemData]!.name), label: Text(options[itemData]!.name),
onPressed: () => _formKey.currentState?.fields[fkCorrespondent] onPressed: () => _formKey.currentState?.fields[fkCorrespondent]
@@ -248,8 +251,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
LabelFormField<DocumentType>( LabelFormField<DocumentType>(
notAssignedSelectable: false, notAssignedSelectable: false,
formBuilderState: _formKey.currentState, formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (currentInput) => RepositoryProvider( labelCreationWidgetBuilder: (currentInput) =>
create: (context) => context.read<LabelRepository<DocumentType>>(), RepositoryProvider.value(
value: context.read<LabelRepository>(),
child: AddDocumentTypePage( child: AddDocumentTypePage(
initialName: currentInput, initialName: currentInput,
), ),
@@ -260,9 +264,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
name: fkDocumentType, name: fkDocumentType,
prefixIcon: const Icon(Icons.description_outlined), prefixIcon: const Icon(Icons.description_outlined),
), ),
if (_filteredSuggestions.hasSuggestedDocumentTypes) if (_filteredSuggestions?.hasSuggestedDocumentTypes ?? false)
_buildSuggestionsSkeleton<int>( _buildSuggestionsSkeleton<int>(
suggestions: _filteredSuggestions.documentTypes, suggestions: _filteredSuggestions!.documentTypes,
itemBuilder: (context, itemData) => ActionChip( itemBuilder: (context, itemData) => ActionChip(
label: Text(options[itemData]!.name), label: Text(options[itemData]!.name),
onPressed: () => _formKey.currentState?.fields[fkDocumentType] onPressed: () => _formKey.currentState?.fields[fkDocumentType]
@@ -327,9 +331,9 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
format: DateFormat.yMMMMd(), format: DateFormat.yMMMMd(),
initialEntryMode: DatePickerEntryMode.calendar, initialEntryMode: DatePickerEntryMode.calendar,
), ),
if (_filteredSuggestions.hasSuggestedDates) if (_filteredSuggestions?.hasSuggestedDates ?? false)
_buildSuggestionsSkeleton<DateTime>( _buildSuggestionsSkeleton<DateTime>(
suggestions: _filteredSuggestions.dates, suggestions: _filteredSuggestions!.dates,
itemBuilder: (context, itemData) => ActionChip( itemBuilder: (context, itemData) => ActionChip(
label: Text(DateFormat.yMMMd().format(itemData)), label: Text(DateFormat.yMMMd().format(itemData)),
onPressed: () => _formKey.currentState?.fields[fkCreatedDate] onPressed: () => _formKey.currentState?.fields[fkCreatedDate]

View File

@@ -12,7 +12,6 @@ import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart'; import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
import 'package:paperless_mobile/core/global/constants.dart'; import 'package:paperless_mobile/core/global/constants.dart';
import 'package:paperless_mobile/core/repository/label_repository.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.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/app_drawer/view/app_drawer.dart'; import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
@@ -198,20 +197,14 @@ class _ScannerPageState extends State<ScannerPage>
); );
final uploadResult = await Navigator.of(context).push<DocumentUploadResult>( final uploadResult = await Navigator.of(context).push<DocumentUploadResult>(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => LabelRepositoriesProvider( builder: (_) => BlocProvider(
child: BlocProvider( create: (context) => DocumentUploadCubit(
create: (context) => DocumentUploadCubit( context.read(),
documentApi: context.read<PaperlessDocumentsApi>(), context.read(),
correspondentRepository: ),
context.read<LabelRepository<Correspondent>>(), child: DocumentUploadPreparationPage(
documentTypeRepository: fileBytes: file.bytes,
context.read<LabelRepository<DocumentType>>(), fileExtension: file.extension,
tagRepository: context.read<LabelRepository<Tag>>(),
),
child: DocumentUploadPreparationPage(
fileBytes: file.bytes,
fileExtension: file.extension,
),
), ),
), ),
), ),
@@ -316,22 +309,16 @@ class _ScannerPageState extends State<ScannerPage>
} }
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => LabelRepositoriesProvider( builder: (_) => BlocProvider(
child: BlocProvider( create: (context) => DocumentUploadCubit(
create: (context) => DocumentUploadCubit( context.read(),
documentApi: context.read<PaperlessDocumentsApi>(), context.read(),
correspondentRepository: ),
context.read<LabelRepository<Correspondent>>(), child: DocumentUploadPreparationPage(
documentTypeRepository: fileBytes: file.readAsBytesSync(),
context.read<LabelRepository<DocumentType>>(), filename: fileDescription.filename,
tagRepository: context.read<LabelRepository<Tag>>(), title: fileDescription.filename,
), fileExtension: fileDescription.extension,
child: DocumentUploadPreparationPage(
fileBytes: file.readAsBytesSync(),
filename: fileDescription.filename,
title: fileDescription.filename,
fileExtension: fileDescription.extension,
),
), ),
), ),
), ),

View File

@@ -2,6 +2,7 @@ import 'package:collection/collection.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:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.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';
@@ -15,12 +16,22 @@ class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
with DocumentPagingBlocMixin { with DocumentPagingBlocMixin {
@override @override
final PaperlessDocumentsApi api; final PaperlessDocumentsApi api;
final LabelRepository _labelRepository;
@override @override
final DocumentChangedNotifier notifier; final DocumentChangedNotifier notifier;
DocumentSearchCubit(this.api, this.notifier) DocumentSearchCubit(this.api, this.notifier, this._labelRepository)
: super(const DocumentSearchState()) { : super(const DocumentSearchState()) {
notifier.subscribe( _labelRepository.addListener(this, onChanged: (labels) {
emit(state.copyWith(
correspondents: labels.correspondents,
documentTypes: labels.documentTypes,
tags: labels.tags,
storagePaths: labels.storagePaths,
));
});
notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,
onUpdated: replace, onUpdated: replace,
@@ -89,7 +100,7 @@ class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
@override @override
Future<void> close() { Future<void> close() {
notifier.unsubscribe(this); notifier.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -13,6 +13,12 @@ class DocumentSearchState extends DocumentPagingState {
final List<String> suggestions; final List<String> suggestions;
@JsonKey() @JsonKey()
final ViewType viewType; final ViewType viewType;
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const DocumentSearchState({ const DocumentSearchState({
this.view = SearchView.suggestions, this.view = SearchView.suggestions,
this.searchHistory = const [], this.searchHistory = const [],
@@ -22,6 +28,10 @@ class DocumentSearchState extends DocumentPagingState {
super.hasLoaded, super.hasLoaded,
super.isLoading, super.isLoading,
super.value, super.value,
this.correspondents = const {},
this.documentTypes = const {},
this.tags = const {},
this.storagePaths = const {},
}); });
@override @override
@@ -31,6 +41,10 @@ class DocumentSearchState extends DocumentPagingState {
suggestions, suggestions,
view, view,
viewType, viewType,
correspondents,
documentTypes,
tags,
storagePaths,
]; ];
@override @override
@@ -57,6 +71,10 @@ class DocumentSearchState extends DocumentPagingState {
List<String>? suggestions, List<String>? suggestions,
SearchView? view, SearchView? view,
ViewType? viewType, ViewType? viewType,
Map<int, Correspondent>? correspondents,
Map<int, DocumentType>? documentTypes,
Map<int, Tag>? tags,
Map<int, StoragePath>? storagePaths,
}) { }) {
return DocumentSearchState( return DocumentSearchState(
value: value ?? this.value, value: value ?? this.value,
@@ -67,6 +85,10 @@ class DocumentSearchState extends DocumentPagingState {
view: view ?? this.view, view: view ?? this.view,
suggestions: suggestions ?? this.suggestions, suggestions: suggestions ?? this.suggestions,
viewType: viewType ?? this.viewType, viewType: viewType ?? this.viewType,
correspondents: correspondents ?? this.correspondents,
documentTypes: documentTypes ?? this.documentTypes,
tags: tags ?? this.tags,
storagePaths: storagePaths ?? this.storagePaths,
); );
} }

View File

@@ -21,6 +21,7 @@ Future<void> showDocumentSearchPage(BuildContext context) {
create: (context) => DocumentSearchCubit( create: (context) => DocumentSearchCubit(
context.read(), context.read(),
context.read(), context.read(),
context.read(),
), ),
child: const DocumentSearchPage(), child: const DocumentSearchPage(),
), ),
@@ -229,6 +230,10 @@ class _DocumentSearchPageState extends State<DocumentSearchPage> {
), ),
); );
}, },
correspondents: state.correspondents,
documentTypes: state.documentTypes,
tags: state.tags,
storagePaths: state.storagePaths,
) )
], ],
); );

View File

@@ -5,42 +5,26 @@ import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.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/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/tag_repository_state.dart';
part 'document_upload_state.dart'; part 'document_upload_state.dart';
class DocumentUploadCubit extends Cubit<DocumentUploadState> { class DocumentUploadCubit extends Cubit<DocumentUploadState> {
final PaperlessDocumentsApi _documentApi; final PaperlessDocumentsApi _documentApi;
final LabelRepository<Tag> _tagRepository; final LabelRepository _labelRepository;
final LabelRepository<Correspondent> _correspondentRepository;
final LabelRepository<DocumentType> _documentTypeRepository;
final List<StreamSubscription> _subs = []; DocumentUploadCubit(this._labelRepository, this._documentApi)
: super(const DocumentUploadState()) {
DocumentUploadCubit({ _labelRepository.addListener(
required PaperlessDocumentsApi documentApi, this,
required LabelRepository<Tag> tagRepository, onChanged: (labels) {
required LabelRepository<Correspondent> correspondentRepository, emit(state.copyWith(
required LabelRepository<DocumentType> documentTypeRepository, correspondents: labels.correspondents,
}) : _documentApi = documentApi, documentTypes: labels.documentTypes,
_tagRepository = tagRepository, tags: labels.tags,
_correspondentRepository = correspondentRepository, ));
_documentTypeRepository = documentTypeRepository, },
super(const DocumentUploadState()) { );
_subs.add(_tagRepository.values.listen(
(tags) => emit(state.copyWith(tags: tags?.values)),
));
_subs.add(_correspondentRepository.values.listen(
(correspondents) =>
emit(state.copyWith(correspondents: correspondents?.values)),
));
_subs.add(_documentTypeRepository.values.listen(
(documentTypes) =>
emit(state.copyWith(documentTypes: documentTypes?.values)),
));
} }
Future<String?> upload( Future<String?> upload(
@@ -65,9 +49,7 @@ class DocumentUploadCubit extends Cubit<DocumentUploadState> {
@override @override
Future<void> close() async { Future<void> close() async {
for (final sub in _subs) { _labelRepository.removeListener(this);
await sub.cancel();
}
return super.close(); return super.close();
} }
} }

View File

@@ -192,9 +192,8 @@ class _DocumentUploadPreparationPageState
notAssignedSelectable: false, notAssignedSelectable: false,
formBuilderState: _formKey.currentState, formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialName) => labelCreationWidgetBuilder: (initialName) =>
RepositoryProvider( RepositoryProvider.value(
create: (context) => value: context.read<LabelRepository>(),
context.read<LabelRepository<Correspondent>>(),
child: AddCorrespondentPage(initialName: initialName), child: AddCorrespondentPage(initialName: initialName),
), ),
textFieldLabel: S.of(context)!.correspondent + " *", textFieldLabel: S.of(context)!.correspondent + " *",
@@ -207,9 +206,8 @@ class _DocumentUploadPreparationPageState
notAssignedSelectable: false, notAssignedSelectable: false,
formBuilderState: _formKey.currentState, formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialName) => labelCreationWidgetBuilder: (initialName) =>
RepositoryProvider( RepositoryProvider.value(
create: (context) => value: context.read<LabelRepository>(),
context.read<LabelRepository<DocumentType>>(),
child: AddDocumentTypePage(initialName: initialName), child: AddDocumentTypePage(initialName: initialName),
), ),
textFieldLabel: S.of(context)!.documentType + " *", textFieldLabel: S.of(context)!.documentType + " *",
@@ -229,7 +227,7 @@ class _DocumentUploadPreparationPageState
"* " + S.of(context)!.uploadInferValuesHint, "* " + S.of(context)!.uploadInferValuesHint,
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
SizedBox(height: 300), const SizedBox(height: 300),
].padded(), ].padded(),
), ),
); );

View File

@@ -1,19 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.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/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:json_annotation/json_annotation.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/settings/model/view_type.dart';
part 'documents_state.dart';
part 'documents_cubit.g.dart'; part 'documents_cubit.g.dart';
part 'documents_state.dart';
class DocumentsCubit extends HydratedCubit<DocumentsState> class DocumentsCubit extends HydratedCubit<DocumentsState>
with DocumentPagingBlocMixin { with DocumentPagingBlocMixin {
@@ -32,7 +30,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
storagePaths: _labelRepository.state.storagePaths, storagePaths: _labelRepository.state.storagePaths,
tags: _labelRepository.state.tags, tags: _labelRepository.state.tags,
)) { )) {
notifier.subscribe( notifier.addListener(
this, this,
onUpdated: (document) { onUpdated: (document) {
replace(document); replace(document);
@@ -54,9 +52,9 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
); );
}, },
); );
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onStateChanged: (labels) => emit( onChanged: (labels) => emit(
state.copyWith( state.copyWith(
correspondents: labels.correspondents, correspondents: labels.correspondents,
documentTypes: labels.documentTypes, documentTypes: labels.documentTypes,
@@ -120,8 +118,8 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
@override @override
Future<void> close() { Future<void> close() {
notifier.unsubscribe(this); notifier.removeListener(this);
_labelRepository.unsubscribe(this); _labelRepository.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -12,11 +12,9 @@ import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart'; import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_panel.dart'; import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_panel.dart';
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
import 'package:paperless_mobile/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart';
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart'; import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
import 'package:paperless_mobile/features/labels/cubit/providers/labels_bloc_provider.dart';
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart'; import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart'; import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
@@ -358,6 +356,10 @@ class _DocumentsPageState extends State<DocumentsPage>
isLabelClickable: true, isLabelClickable: true,
isLoading: state.isLoading, isLoading: state.isLoading,
selectedDocumentIds: state.selectedIds, selectedDocumentIds: state.selectedIds,
correspondents: state.correspondents,
documentTypes: state.documentTypes,
tags: state.tags,
storagePaths: state.storagePaths,
); );
}, },
), ),
@@ -391,10 +393,16 @@ class _DocumentsPageState extends State<DocumentsPage>
void _onCreateSavedView(DocumentFilter filter) async { void _onCreateSavedView(DocumentFilter filter) async {
final newView = await Navigator.of(context).push<SavedView?>( final newView = await Navigator.of(context).push<SavedView?>(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LabelsBlocProvider( builder: (context) => BlocBuilder<SavedViewCubit, SavedViewState>(
child: AddSavedViewPage( builder: (context, state) {
currentFilter: filter, return AddSavedViewPage(
), currentFilter: filter,
correspondents: state.correspondents,
documentTypes: state.documentTypes,
storagePaths: state.storagePaths,
tags: state.tags,
);
},
), ),
), ),
); );
@@ -428,12 +436,19 @@ class _DocumentsPageState extends State<DocumentsPage>
snapSizes: const [0.9, 1], snapSizes: const [0.9, 1],
initialChildSize: .9, initialChildSize: .9,
maxChildSize: 1, maxChildSize: 1,
builder: (context, controller) => LabelsBlocProvider( builder: (context, controller) =>
child: DocumentFilterPanel( BlocBuilder<DocumentsCubit, DocumentsState>(
initialFilter: context.read<DocumentsCubit>().state.filter, builder: (context, state) {
scrollController: controller, return DocumentFilterPanel(
draggableSheetController: draggableSheetController, initialFilter: context.read<DocumentsCubit>().state.filter,
), scrollController: controller,
draggableSheetController: draggableSheetController,
correspondents: state.correspondents,
documentTypes: state.documentTypes,
storagePaths: state.storagePaths,
tags: state.tags,
);
},
), ),
), ),
), ),

View File

@@ -119,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,
), ),
correspondent: context.read<DocumentsCubit>().correspondent, correspondent: correspondents[document.correspondent],
), ),
], ],
).paddedLTRB(8, 0, 8, 4), ).paddedLTRB(8, 0, 8, 4),
@@ -134,13 +134,13 @@ 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,
), ),
documentTypeId: document.documentType, documentType: documentTypes[document.documentType],
), ),
], ],
).paddedLTRB(8, 0, 8, 4), ).paddedLTRB(8, 0, 8, 4),
TagsWidget( TagsWidget(
isMultiLine: false, isMultiLine: false,
tagIds: document.tags, tags: document.tags.map((e) => tags[e]!).toList(),
).padded(), ).padded(),
if (highlights != null) if (highlights != null)
Html( Html(

View File

@@ -49,6 +49,11 @@ class DocumentFilterForm extends StatefulWidget {
final DocumentFilter initialFilter; final DocumentFilter initialFilter;
final ScrollController? scrollController; final ScrollController? scrollController;
final EdgeInsets padding; final EdgeInsets padding;
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const DocumentFilterForm({ const DocumentFilterForm({
super.key, super.key,
this.header, this.header,
@@ -56,6 +61,10 @@ class DocumentFilterForm extends StatefulWidget {
required this.initialFilter, required this.initialFilter,
this.scrollController, this.scrollController,
this.padding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16), this.padding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
required this.correspondents,
required this.documentTypes,
required this.tags,
required this.storagePaths,
}); });
@override @override
@@ -145,47 +154,35 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
} }
Widget _buildDocumentTypeFormField() { Widget _buildDocumentTypeFormField() {
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>( return LabelFormField<DocumentType>(
builder: (context, state) { formBuilderState: widget.formKey.currentState,
return LabelFormField<DocumentType>( name: DocumentFilterForm.fkDocumentType,
formBuilderState: widget.formKey.currentState, labelOptions: widget.documentTypes,
name: DocumentFilterForm.fkDocumentType, textFieldLabel: S.of(context)!.documentType,
labelOptions: state.labels, initialValue: widget.initialFilter.documentType,
textFieldLabel: S.of(context)!.documentType, prefixIcon: const Icon(Icons.description_outlined),
initialValue: widget.initialFilter.documentType,
prefixIcon: const Icon(Icons.description_outlined),
);
},
); );
} }
Widget _buildCorrespondentFormField() { Widget _buildCorrespondentFormField() {
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>( return LabelFormField<Correspondent>(
builder: (context, state) { formBuilderState: widget.formKey.currentState,
return LabelFormField<Correspondent>( name: DocumentFilterForm.fkCorrespondent,
formBuilderState: widget.formKey.currentState, labelOptions: widget.correspondents,
name: DocumentFilterForm.fkCorrespondent, textFieldLabel: S.of(context)!.correspondent,
labelOptions: state.labels, initialValue: widget.initialFilter.correspondent,
textFieldLabel: S.of(context)!.correspondent, prefixIcon: const Icon(Icons.person_outline),
initialValue: widget.initialFilter.correspondent,
prefixIcon: const Icon(Icons.person_outline),
);
},
); );
} }
Widget _buildStoragePathFormField() { Widget _buildStoragePathFormField() {
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>( return LabelFormField<StoragePath>(
builder: (context, state) { formBuilderState: widget.formKey.currentState,
return LabelFormField<StoragePath>( name: DocumentFilterForm.fkStoragePath,
formBuilderState: widget.formKey.currentState, labelOptions: widget.storagePaths,
name: DocumentFilterForm.fkStoragePath, textFieldLabel: S.of(context)!.storagePath,
labelOptions: state.labels, initialValue: widget.initialFilter.storagePath,
textFieldLabel: S.of(context)!.storagePath, prefixIcon: const Icon(Icons.folder_outlined),
initialValue: widget.initialFilter.storagePath,
prefixIcon: const Icon(Icons.folder_outlined),
);
},
); );
} }
@@ -197,16 +194,12 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
); );
} }
BlocBuilder<LabelCubit<Tag>, LabelState<Tag>> _buildTagsFormField() { Widget _buildTagsFormField() {
return BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>( return TagFormField(
builder: (context, state) { name: DocumentModel.tagsKey,
return TagFormField( initialValue: widget.initialFilter.tags,
name: DocumentModel.tagsKey, allowCreation: false,
initialValue: widget.initialFilter.tags, selectableOptions: widget.tags,
allowCreation: false,
selectableOptions: state.labels,
);
},
); );
} }
} }

View File

@@ -13,11 +13,20 @@ class DocumentFilterPanel extends StatefulWidget {
final DocumentFilter initialFilter; final DocumentFilter initialFilter;
final ScrollController scrollController; final ScrollController scrollController;
final DraggableScrollableController draggableSheetController; final DraggableScrollableController draggableSheetController;
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const DocumentFilterPanel({ const DocumentFilterPanel({
Key? key, Key? key,
required this.initialFilter, required this.initialFilter,
required this.scrollController, required this.scrollController,
required this.draggableSheetController, required this.draggableSheetController,
required this.correspondents,
required this.documentTypes,
required this.tags,
required this.storagePaths,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -38,10 +47,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
void animateTitleByDrag() { void animateTitleByDrag() {
setState( setState(
() { () => _heightAnimationValue =
_heightAnimationValue = dp( dp(((max(0.9, widget.draggableSheetController.size) - 0.9) / 0.1), 5),
((max(0.9, widget.draggableSheetController.size) - 0.9) / 0.1), 5);
},
); );
} }
@@ -96,6 +103,10 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
scrollController: widget.scrollController, scrollController: widget.scrollController,
initialFilter: widget.initialFilter, initialFilter: widget.initialFilter,
header: _buildPanelHeader(), header: _buildPanelHeader(),
correspondents: widget.correspondents,
documentTypes: widget.documentTypes,
storagePaths: widget.storagePaths,
tags: widget.tags,
), ),
), ),
); );

View File

@@ -10,6 +10,10 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class SortFieldSelectionBottomSheet extends StatefulWidget { class SortFieldSelectionBottomSheet extends StatefulWidget {
final SortOrder initialSortOrder; final SortOrder initialSortOrder;
final SortField? initialSortField; final SortField? initialSortField;
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
final Future Function(SortField? field, SortOrder order) onSubmit; final Future Function(SortField? field, SortOrder order) onSubmit;
@@ -18,6 +22,10 @@ class SortFieldSelectionBottomSheet extends StatefulWidget {
required this.initialSortOrder, required this.initialSortOrder,
required this.initialSortField, required this.initialSortField,
required this.onSubmit, required this.onSubmit,
required this.correspondents,
required this.documentTypes,
required this.tags,
required this.storagePaths,
}); });
@override @override
@@ -67,31 +75,20 @@ class _SortFieldSelectionBottomSheetState
Column( Column(
children: [ children: [
_buildSortOption(SortField.archiveSerialNumber), _buildSortOption(SortField.archiveSerialNumber),
BlocBuilder<LabelCubit<Correspondent>, _buildSortOption(
LabelState<Correspondent>>( SortField.correspondentName,
builder: (context, state) { enabled: widget.correspondents.values.fold<bool>(
return _buildSortOption( false,
SortField.correspondentName, (previousValue, element) =>
enabled: state.labels.values.fold<bool>( previousValue || (element.documentCount ?? 0) > 0),
false,
(previousValue, element) =>
previousValue ||
(element.documentCount ?? 0) > 0),
);
},
), ),
_buildSortOption(SortField.title), _buildSortOption(SortField.title),
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>( _buildSortOption(
builder: (context, state) { SortField.documentType,
return _buildSortOption( enabled: widget.documentTypes.values.fold<bool>(
SortField.documentType, false,
enabled: state.labels.values.fold<bool>( (previousValue, element) =>
false, previousValue || (element.documentCount ?? 0) > 0),
(previousValue, element) =>
previousValue ||
(element.documentCount ?? 0) > 0),
);
},
), ),
_buildSortOption(SortField.created), _buildSortOption(SortField.created),
_buildSortOption(SortField.added), _buildSortOption(SortField.added),

View File

@@ -57,6 +57,10 @@ class SortDocumentsButton extends StatelessWidget {
sortOrder: order, sortOrder: order,
), ),
), ),
correspondents: state.correspondents,
documentTypes: state.documentTypes,
storagePaths: state.storagePaths,
tags: state.tags,
), ),
), ),
), ),

View File

@@ -13,7 +13,7 @@ class EditLabelCubit extends Cubit<EditLabelState> with LabelCubitMixin {
final LabelRepository labelRepository; final LabelRepository labelRepository;
EditLabelCubit(this.labelRepository) : super(const EditLabelState()) { EditLabelCubit(this.labelRepository) : super(const EditLabelState()) {
labelRepository.subscribe( labelRepository.addListener(
this, this,
onChanged: (labels) => state.copyWith( onChanged: (labels) => state.copyWith(
correspondents: labels.correspondents, correspondents: labels.correspondents,
@@ -26,7 +26,7 @@ class EditLabelCubit extends Cubit<EditLabelState> with LabelCubitMixin {
@override @override
Future<void> close() { Future<void> close() {
labelRepository.unsubscribe(this); labelRepository.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -157,10 +157,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BlocProvider.value( builder: (context) => BlocProvider.value(
value: DocumentUploadCubit( value: DocumentUploadCubit(
documentApi: context.read(), context.read(),
tagRepository: context.read(), context.read(),
correspondentRepository: context.read(),
documentTypeRepository: context.read(),
), ),
child: DocumentUploadPreparationPage( child: DocumentUploadPreparationPage(
fileBytes: bytes, fileBytes: bytes,
@@ -239,11 +237,13 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
create: (context) => DocumentsCubit( create: (context) => DocumentsCubit(
context.read(), context.read(),
context.read(), context.read(),
context.read(),
), ),
), ),
BlocProvider( BlocProvider(
create: (context) => SavedViewCubit( create: (context) => SavedViewCubit(
context.read(), context.read(),
context.read(),
), ),
), ),
], ],

View File

@@ -32,7 +32,7 @@ class InboxCubit extends HydratedCubit<InboxState>
this._labelRepository, this._labelRepository,
this.notifier, this.notifier,
) : super(InboxState(labels: _labelRepository.state)) { ) : super(InboxState(labels: _labelRepository.state)) {
notifier.subscribe( notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,
onUpdated: (document) { onUpdated: (document) {
@@ -47,7 +47,7 @@ class InboxCubit extends HydratedCubit<InboxState>
} }
}, },
); );
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onChanged: (labels) { onChanged: (labels) {
emit(state.copyWith(labels: labels)); emit(state.copyWith(labels: labels));
@@ -209,7 +209,7 @@ class InboxCubit extends HydratedCubit<InboxState>
@override @override
Future<void> close() { Future<void> close() {
_labelRepository.unsubscribe(this); _labelRepository.removeListener(this);
return super.close(); return super.close();
} }
} }

View File

@@ -12,7 +12,7 @@ class LabelCubit extends Cubit<LabelState> with LabelCubitMixin<LabelState> {
final LabelRepository labelRepository; final LabelRepository labelRepository;
LabelCubit(this.labelRepository) : super(const LabelState()) { LabelCubit(this.labelRepository) : super(const LabelState()) {
labelRepository.subscribe( labelRepository.addListener(
this, this,
onChanged: (labels) { onChanged: (labels) {
emit(state.copyWith( emit(state.copyWith(
@@ -27,7 +27,7 @@ class LabelCubit extends Cubit<LabelState> with LabelCubitMixin<LabelState> {
@override @override
Future<void> close() { Future<void> close() {
labelRepository.unsubscribe(this); labelRepository.removeListener(this);
return super.close(); return super.close();
} }

View File

@@ -27,7 +27,7 @@ class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
this._labelRepository, this._labelRepository,
) : super(const LinkedDocumentsState()) { ) : super(const LinkedDocumentsState()) {
updateFilter(filter: filter); updateFilter(filter: filter);
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onChanged: (labels) { onChanged: (labels) {
emit(state.copyWith( emit(state.copyWith(
@@ -38,7 +38,7 @@ class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
)); ));
}, },
); );
notifier.subscribe( notifier.addListener(
this, this,
onUpdated: replace, onUpdated: replace,
onDeleted: remove, onDeleted: remove,

View File

@@ -172,7 +172,7 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
@override @override
Future<void> close() { Future<void> close() {
notifier.unsubscribe(this); notifier.removeListener(this);
return super.close(); return super.close();
} }
} }

View File

@@ -21,7 +21,7 @@ class SavedViewCubit extends Cubit<SavedViewState> {
storagePaths: _labelRepository.state.storagePaths, storagePaths: _labelRepository.state.storagePaths,
tags: _labelRepository.state.tags, tags: _labelRepository.state.tags,
)) { )) {
_labelRepository.subscribe( _labelRepository.addListener(
this, this,
onChanged: (labels) { onChanged: (labels) {
emit( emit(
@@ -76,7 +76,7 @@ class SavedViewCubit extends Cubit<SavedViewState> {
@override @override
Future<void> close() { Future<void> close() {
_savedViewRepository.unsubscribe(this); _savedViewRepository.unsubscribe(this);
_labelRepository.unsubscribe(this); _labelRepository.removeListener(this);
return super.close(); return super.close();
} }
} }

View File

@@ -8,7 +8,18 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class AddSavedViewPage extends StatefulWidget { class AddSavedViewPage extends StatefulWidget {
final DocumentFilter currentFilter; final DocumentFilter currentFilter;
const AddSavedViewPage({super.key, required this.currentFilter}); final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const AddSavedViewPage({
super.key,
required this.currentFilter,
required this.correspondents,
required this.documentTypes,
required this.tags,
required this.storagePaths,
});
@override @override
State<AddSavedViewPage> createState() => _AddSavedViewPageState(); State<AddSavedViewPage> createState() => _AddSavedViewPageState();
@@ -72,6 +83,10 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
formKey: _filterFormKey, formKey: _filterFormKey,
initialFilter: widget.currentFilter, initialFilter: widget.currentFilter,
correspondents: widget.correspondents,
documentTypes: widget.documentTypes,
storagePaths: widget.storagePaths,
tags: widget.tags,
), ),
), ),
], ],

View File

@@ -23,7 +23,7 @@ class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
this.notifier, { this.notifier, {
required this.savedView, required this.savedView,
}) : super(const SavedViewDetailsState()) { }) : super(const SavedViewDetailsState()) {
notifier.subscribe( notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,
onUpdated: replace, onUpdated: replace,

View File

@@ -5,17 +5,30 @@ class SavedViewDetailsState extends DocumentPagingState {
@JsonKey() @JsonKey()
final ViewType viewType; final ViewType viewType;
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const SavedViewDetailsState({ const SavedViewDetailsState({
this.viewType = ViewType.list, this.viewType = ViewType.list,
super.filter, super.filter,
super.hasLoaded, super.hasLoaded,
super.isLoading, super.isLoading,
super.value, super.value,
this.correspondents = const {},
this.documentTypes = const {},
this.tags = const {},
this.storagePaths = const {},
}); });
@override @override
List<Object?> get props => [ List<Object?> get props => [
viewType, viewType,
correspondents,
documentTypes,
tags,
storagePaths,
...super.props, ...super.props,
]; ];
@@ -40,6 +53,10 @@ class SavedViewDetailsState extends DocumentPagingState {
List<PagedSearchResult<DocumentModel>>? value, List<PagedSearchResult<DocumentModel>>? value,
DocumentFilter? filter, DocumentFilter? filter,
ViewType? viewType, ViewType? viewType,
Map<int, Correspondent>? correspondents,
Map<int, DocumentType>? documentTypes,
Map<int, Tag>? tags,
Map<int, StoragePath>? storagePaths,
}) { }) {
return SavedViewDetailsState( return SavedViewDetailsState(
hasLoaded: hasLoaded ?? this.hasLoaded, hasLoaded: hasLoaded ?? this.hasLoaded,
@@ -47,6 +64,10 @@ class SavedViewDetailsState extends DocumentPagingState {
value: value ?? this.value, value: value ?? this.value,
filter: filter ?? this.filter, filter: filter ?? this.filter,
viewType: viewType ?? this.viewType, viewType: viewType ?? this.viewType,
correspondents: correspondents ?? this.correspondents,
documentTypes: documentTypes ?? this.documentTypes,
tags: tags ?? this.tags,
storagePaths: storagePaths ?? this.storagePaths,
); );
} }

View File

@@ -86,6 +86,10 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage>
); );
}, },
viewType: state.viewType, viewType: state.viewType,
correspondents: state.correspondents,
documentTypes: state.documentTypes,
tags: state.tags,
storagePaths: state.storagePaths,
), ),
if (state.hasLoaded && state.isLoading) if (state.hasLoaded && state.isLoading)
const SliverToBoxAdapter( const SliverToBoxAdapter(

View File

@@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/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/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
@@ -13,19 +14,33 @@ class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
@override @override
final PaperlessDocumentsApi api; final PaperlessDocumentsApi api;
final LabelRepository _labelRepository;
@override @override
final DocumentChangedNotifier notifier; final DocumentChangedNotifier notifier;
SimilarDocumentsCubit( SimilarDocumentsCubit(
this.api, this.api,
this.notifier, { this.notifier,
this._labelRepository, {
required this.documentId, required this.documentId,
}) : super(const SimilarDocumentsState()) { }) : super(const SimilarDocumentsState()) {
notifier.subscribe( notifier.addListener(
this, this,
onDeleted: remove, onDeleted: remove,
onUpdated: replace, onUpdated: replace,
); );
_labelRepository.addListener(
this,
onChanged: (labels) {
emit(state.copyWith(
correspondents: labels.correspondents,
documentTypes: labels.documentTypes,
tags: labels.tags,
storagePaths: labels.storagePaths,
));
},
);
} }
Future<void> initialize() async { Future<void> initialize() async {
@@ -38,4 +53,11 @@ class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
); );
} }
} }
@override
Future<void> close() {
notifier.removeListener(this);
_labelRepository.removeListener(this);
return super.close();
}
} }

View File

@@ -1,11 +1,20 @@
part of 'similar_documents_cubit.dart'; part of 'similar_documents_cubit.dart';
class SimilarDocumentsState extends DocumentPagingState { class SimilarDocumentsState extends DocumentPagingState {
final Map<int, Correspondent> correspondents;
final Map<int, DocumentType> documentTypes;
final Map<int, Tag> tags;
final Map<int, StoragePath> storagePaths;
const SimilarDocumentsState({ const SimilarDocumentsState({
super.filter, super.filter,
super.hasLoaded, super.hasLoaded,
super.isLoading, super.isLoading,
super.value, super.value,
this.correspondents = const {},
this.documentTypes = const {},
this.tags = const {},
this.storagePaths = const {},
}); });
@override @override
@@ -14,6 +23,10 @@ class SimilarDocumentsState extends DocumentPagingState {
hasLoaded, hasLoaded,
isLoading, isLoading,
value, value,
correspondents,
documentTypes,
tags,
storagePaths,
]; ];
@override @override
@@ -36,12 +49,20 @@ class SimilarDocumentsState extends DocumentPagingState {
bool? isLoading, bool? isLoading,
List<PagedSearchResult<DocumentModel>>? value, List<PagedSearchResult<DocumentModel>>? value,
DocumentFilter? filter, DocumentFilter? filter,
Map<int, Correspondent>? correspondents,
Map<int, DocumentType>? documentTypes,
Map<int, Tag>? tags,
Map<int, StoragePath>? storagePaths,
}) { }) {
return SimilarDocumentsState( return SimilarDocumentsState(
hasLoaded: hasLoaded ?? this.hasLoaded, hasLoaded: hasLoaded ?? this.hasLoaded,
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
value: value ?? this.value, value: value ?? this.value,
filter: filter ?? this.filter, filter: filter ?? this.filter,
correspondents: correspondents ?? this.correspondents,
documentTypes: documentTypes ?? this.documentTypes,
tags: tags ?? this.tags,
storagePaths: storagePaths ?? this.storagePaths,
); );
} }
} }

View File

@@ -78,6 +78,10 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
), ),
); );
}, },
correspondents: state.correspondents,
documentTypes: state.documentTypes,
tags: state.tags,
storagePaths: state.storagePaths,
); );
}, },
); );

View File

@@ -21,7 +21,6 @@ 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/saved_view_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';
@@ -101,7 +100,7 @@ void main() async {
// Create repositories // Create repositories
final labelRepository = LabelRepository(labelsApi); final labelRepository = LabelRepository(labelsApi);
final savedViewRepository = SavedViewRepositoryImpl(savedViewsApi); final savedViewRepository = SavedViewRepository(savedViewsApi);
//Create cubits/blocs //Create cubits/blocs
final authCubit = AuthenticationCubit( final authCubit = AuthenticationCubit(