mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 00:07:54 -06:00
fix: Refactor labels structure
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.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_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/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/settings/model/view_type.dart';
|
||||
|
||||
part 'documents_state.dart';
|
||||
part 'documents_cubit.g.dart';
|
||||
part 'documents_state.dart';
|
||||
|
||||
class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
with DocumentPagingBlocMixin {
|
||||
@@ -32,7 +30,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
storagePaths: _labelRepository.state.storagePaths,
|
||||
tags: _labelRepository.state.tags,
|
||||
)) {
|
||||
notifier.subscribe(
|
||||
notifier.addListener(
|
||||
this,
|
||||
onUpdated: (document) {
|
||||
replace(document);
|
||||
@@ -54,9 +52,9 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
);
|
||||
},
|
||||
);
|
||||
_labelRepository.subscribe(
|
||||
_labelRepository.addListener(
|
||||
this,
|
||||
onStateChanged: (labels) => emit(
|
||||
onChanged: (labels) => emit(
|
||||
state.copyWith(
|
||||
correspondents: labels.correspondents,
|
||||
documentTypes: labels.documentTypes,
|
||||
@@ -120,8 +118,8 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
notifier.unsubscribe(this);
|
||||
_labelRepository.unsubscribe(this);
|
||||
notifier.removeListener(this);
|
||||
_labelRepository.removeListener(this);
|
||||
return super.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -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/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/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/view_type_selection_widget.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/view/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
|
||||
@@ -358,6 +356,10 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
isLabelClickable: true,
|
||||
isLoading: state.isLoading,
|
||||
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 {
|
||||
final newView = await Navigator.of(context).push<SavedView?>(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LabelsBlocProvider(
|
||||
child: AddSavedViewPage(
|
||||
currentFilter: filter,
|
||||
),
|
||||
builder: (context) => BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
builder: (context, state) {
|
||||
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],
|
||||
initialChildSize: .9,
|
||||
maxChildSize: 1,
|
||||
builder: (context, controller) => LabelsBlocProvider(
|
||||
child: DocumentFilterPanel(
|
||||
initialFilter: context.read<DocumentsCubit>().state.filter,
|
||||
scrollController: controller,
|
||||
draggableSheetController: draggableSheetController,
|
||||
),
|
||||
builder: (context, controller) =>
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return DocumentFilterPanel(
|
||||
initialFilter: context.read<DocumentsCubit>().state.filter,
|
||||
scrollController: controller,
|
||||
draggableSheetController: draggableSheetController,
|
||||
correspondents: state.correspondents,
|
||||
documentTypes: state.documentTypes,
|
||||
storagePaths: state.storagePaths,
|
||||
tags: state.tags,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -119,7 +119,7 @@ class DocumentDetailedItem extends DocumentItem {
|
||||
textStyle: Theme.of(context).textTheme.titleSmall?.apply(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
correspondent: context.read<DocumentsCubit>().correspondent,
|
||||
correspondent: correspondents[document.correspondent],
|
||||
),
|
||||
],
|
||||
).paddedLTRB(8, 0, 8, 4),
|
||||
@@ -134,13 +134,13 @@ class DocumentDetailedItem extends DocumentItem {
|
||||
textStyle: Theme.of(context).textTheme.titleSmall?.apply(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
documentTypeId: document.documentType,
|
||||
documentType: documentTypes[document.documentType],
|
||||
),
|
||||
],
|
||||
).paddedLTRB(8, 0, 8, 4),
|
||||
TagsWidget(
|
||||
isMultiLine: false,
|
||||
tagIds: document.tags,
|
||||
tags: document.tags.map((e) => tags[e]!).toList(),
|
||||
).padded(),
|
||||
if (highlights != null)
|
||||
Html(
|
||||
|
||||
@@ -49,6 +49,11 @@ class DocumentFilterForm extends StatefulWidget {
|
||||
final DocumentFilter initialFilter;
|
||||
final ScrollController? scrollController;
|
||||
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({
|
||||
super.key,
|
||||
this.header,
|
||||
@@ -56,6 +61,10 @@ class DocumentFilterForm extends StatefulWidget {
|
||||
required this.initialFilter,
|
||||
this.scrollController,
|
||||
this.padding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
required this.correspondents,
|
||||
required this.documentTypes,
|
||||
required this.tags,
|
||||
required this.storagePaths,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -145,47 +154,35 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
|
||||
}
|
||||
|
||||
Widget _buildDocumentTypeFormField() {
|
||||
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkDocumentType,
|
||||
labelOptions: state.labels,
|
||||
textFieldLabel: S.of(context)!.documentType,
|
||||
initialValue: widget.initialFilter.documentType,
|
||||
prefixIcon: const Icon(Icons.description_outlined),
|
||||
);
|
||||
},
|
||||
return LabelFormField<DocumentType>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkDocumentType,
|
||||
labelOptions: widget.documentTypes,
|
||||
textFieldLabel: S.of(context)!.documentType,
|
||||
initialValue: widget.initialFilter.documentType,
|
||||
prefixIcon: const Icon(Icons.description_outlined),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCorrespondentFormField() {
|
||||
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkCorrespondent,
|
||||
labelOptions: state.labels,
|
||||
textFieldLabel: S.of(context)!.correspondent,
|
||||
initialValue: widget.initialFilter.correspondent,
|
||||
prefixIcon: const Icon(Icons.person_outline),
|
||||
);
|
||||
},
|
||||
return LabelFormField<Correspondent>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkCorrespondent,
|
||||
labelOptions: widget.correspondents,
|
||||
textFieldLabel: S.of(context)!.correspondent,
|
||||
initialValue: widget.initialFilter.correspondent,
|
||||
prefixIcon: const Icon(Icons.person_outline),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStoragePathFormField() {
|
||||
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkStoragePath,
|
||||
labelOptions: state.labels,
|
||||
textFieldLabel: S.of(context)!.storagePath,
|
||||
initialValue: widget.initialFilter.storagePath,
|
||||
prefixIcon: const Icon(Icons.folder_outlined),
|
||||
);
|
||||
},
|
||||
return LabelFormField<StoragePath>(
|
||||
formBuilderState: widget.formKey.currentState,
|
||||
name: DocumentFilterForm.fkStoragePath,
|
||||
labelOptions: widget.storagePaths,
|
||||
textFieldLabel: S.of(context)!.storagePath,
|
||||
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() {
|
||||
return BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
||||
builder: (context, state) {
|
||||
return TagFormField(
|
||||
name: DocumentModel.tagsKey,
|
||||
initialValue: widget.initialFilter.tags,
|
||||
allowCreation: false,
|
||||
selectableOptions: state.labels,
|
||||
);
|
||||
},
|
||||
Widget _buildTagsFormField() {
|
||||
return TagFormField(
|
||||
name: DocumentModel.tagsKey,
|
||||
initialValue: widget.initialFilter.tags,
|
||||
allowCreation: false,
|
||||
selectableOptions: widget.tags,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,20 @@ class DocumentFilterPanel extends StatefulWidget {
|
||||
final DocumentFilter initialFilter;
|
||||
final ScrollController scrollController;
|
||||
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({
|
||||
Key? key,
|
||||
required this.initialFilter,
|
||||
required this.scrollController,
|
||||
required this.draggableSheetController,
|
||||
required this.correspondents,
|
||||
required this.documentTypes,
|
||||
required this.tags,
|
||||
required this.storagePaths,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -38,10 +47,8 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
|
||||
void animateTitleByDrag() {
|
||||
setState(
|
||||
() {
|
||||
_heightAnimationValue = dp(
|
||||
((max(0.9, widget.draggableSheetController.size) - 0.9) / 0.1), 5);
|
||||
},
|
||||
() => _heightAnimationValue =
|
||||
dp(((max(0.9, widget.draggableSheetController.size) - 0.9) / 0.1), 5),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -96,6 +103,10 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
scrollController: widget.scrollController,
|
||||
initialFilter: widget.initialFilter,
|
||||
header: _buildPanelHeader(),
|
||||
correspondents: widget.correspondents,
|
||||
documentTypes: widget.documentTypes,
|
||||
storagePaths: widget.storagePaths,
|
||||
tags: widget.tags,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -10,6 +10,10 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
final SortOrder initialSortOrder;
|
||||
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;
|
||||
|
||||
@@ -18,6 +22,10 @@ class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
required this.initialSortOrder,
|
||||
required this.initialSortField,
|
||||
required this.onSubmit,
|
||||
required this.correspondents,
|
||||
required this.documentTypes,
|
||||
required this.tags,
|
||||
required this.storagePaths,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -67,31 +75,20 @@ class _SortFieldSelectionBottomSheetState
|
||||
Column(
|
||||
children: [
|
||||
_buildSortOption(SortField.archiveSerialNumber),
|
||||
BlocBuilder<LabelCubit<Correspondent>,
|
||||
LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return _buildSortOption(
|
||||
SortField.correspondentName,
|
||||
enabled: state.labels.values.fold<bool>(
|
||||
false,
|
||||
(previousValue, element) =>
|
||||
previousValue ||
|
||||
(element.documentCount ?? 0) > 0),
|
||||
);
|
||||
},
|
||||
_buildSortOption(
|
||||
SortField.correspondentName,
|
||||
enabled: widget.correspondents.values.fold<bool>(
|
||||
false,
|
||||
(previousValue, element) =>
|
||||
previousValue || (element.documentCount ?? 0) > 0),
|
||||
),
|
||||
_buildSortOption(SortField.title),
|
||||
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return _buildSortOption(
|
||||
SortField.documentType,
|
||||
enabled: state.labels.values.fold<bool>(
|
||||
false,
|
||||
(previousValue, element) =>
|
||||
previousValue ||
|
||||
(element.documentCount ?? 0) > 0),
|
||||
);
|
||||
},
|
||||
_buildSortOption(
|
||||
SortField.documentType,
|
||||
enabled: widget.documentTypes.values.fold<bool>(
|
||||
false,
|
||||
(previousValue, element) =>
|
||||
previousValue || (element.documentCount ?? 0) > 0),
|
||||
),
|
||||
_buildSortOption(SortField.created),
|
||||
_buildSortOption(SortField.added),
|
||||
|
||||
@@ -57,6 +57,10 @@ class SortDocumentsButton extends StatelessWidget {
|
||||
sortOrder: order,
|
||||
),
|
||||
),
|
||||
correspondents: state.correspondents,
|
||||
documentTypes: state.documentTypes,
|
||||
storagePaths: state.storagePaths,
|
||||
tags: state.tags,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user