mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 08:08:14 -06:00
feat: Add bulk edit forms
This commit is contained in:
@@ -0,0 +1,185 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:equatable/equatable.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';
|
||||||
|
|
||||||
|
part 'document_bulk_action_state.dart';
|
||||||
|
|
||||||
|
class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
||||||
|
final PaperlessDocumentsApi _documentsApi;
|
||||||
|
final LabelRepository<Correspondent> _correspondentRepository;
|
||||||
|
final LabelRepository<DocumentType> _documentTypeRepository;
|
||||||
|
final LabelRepository<Tag> _tagRepository;
|
||||||
|
final LabelRepository<StoragePath> _storagePathRepository;
|
||||||
|
final DocumentChangedNotifier _notifier;
|
||||||
|
|
||||||
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
|
|
||||||
|
DocumentBulkActionCubit(
|
||||||
|
this._documentsApi,
|
||||||
|
this._correspondentRepository,
|
||||||
|
this._documentTypeRepository,
|
||||||
|
this._tagRepository,
|
||||||
|
this._storagePathRepository,
|
||||||
|
this._notifier, {
|
||||||
|
required List<DocumentModel> selection,
|
||||||
|
}) : super(
|
||||||
|
DocumentBulkActionState(
|
||||||
|
selection: selection,
|
||||||
|
correspondentOptions:
|
||||||
|
(_correspondentRepository.current?.hasLoaded ?? false)
|
||||||
|
? _correspondentRepository.current!.values!
|
||||||
|
: {},
|
||||||
|
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(
|
||||||
|
this,
|
||||||
|
onDeleted: (document) {
|
||||||
|
// Remove items from internal selection after the document was deleted.
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
selection: state.selection
|
||||||
|
.whereNot((element) => element.id == document.id)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
_subscriptions.add(
|
||||||
|
_tagRepository.values.listen((event) {
|
||||||
|
if (event?.hasLoaded ?? false) {
|
||||||
|
emit(state.copyWith(tagOptions: event!.values));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
_subscriptions.add(
|
||||||
|
_correspondentRepository.values.listen((event) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> bulkDelete() async {
|
||||||
|
final deletedDocumentIds = await _documentsApi.bulkAction(
|
||||||
|
BulkDeleteAction(state.selection.map((e) => e.id).toList()),
|
||||||
|
);
|
||||||
|
final deletedDocuments = state.selection
|
||||||
|
.where((element) => deletedDocumentIds.contains(element.id));
|
||||||
|
for (final doc in deletedDocuments) {
|
||||||
|
_notifier.notifyUpdated(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> bulkModifyCorrespondent(int? correspondentId) async {
|
||||||
|
final modifiedDocumentIds = await _documentsApi.bulkAction(
|
||||||
|
BulkModifyLabelAction.correspondent(
|
||||||
|
state.selectedIds,
|
||||||
|
labelId: correspondentId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final updatedDocuments = state.selection
|
||||||
|
.where((element) => modifiedDocumentIds.contains(element.id))
|
||||||
|
.map((doc) => doc.copyWith(correspondent: () => correspondentId));
|
||||||
|
for (final doc in updatedDocuments) {
|
||||||
|
_notifier.notifyUpdated(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> bulkModifyDocumentType(int? documentTypeId) async {
|
||||||
|
final modifiedDocumentIds = await _documentsApi.bulkAction(
|
||||||
|
BulkModifyLabelAction.documentType(
|
||||||
|
state.selectedIds,
|
||||||
|
labelId: documentTypeId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final updatedDocuments = state.selection
|
||||||
|
.where((element) => modifiedDocumentIds.contains(element.id))
|
||||||
|
.map((doc) => doc.copyWith(documentType: () => documentTypeId));
|
||||||
|
for (final doc in updatedDocuments) {
|
||||||
|
_notifier.notifyUpdated(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> bulkModifyStoragePath(int? storagePathId) async {
|
||||||
|
final modifiedDocumentIds = await _documentsApi.bulkAction(
|
||||||
|
BulkModifyLabelAction.storagePath(
|
||||||
|
state.selectedIds,
|
||||||
|
labelId: storagePathId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final updatedDocuments = state.selection
|
||||||
|
.where((element) => modifiedDocumentIds.contains(element.id))
|
||||||
|
.map((doc) => doc.copyWith(storagePath: () => storagePathId));
|
||||||
|
for (final doc in updatedDocuments) {
|
||||||
|
_notifier.notifyUpdated(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> bulkModifyTags({
|
||||||
|
Iterable<int> addTagIds = const [],
|
||||||
|
Iterable<int> removeTagIds = const [],
|
||||||
|
}) async {
|
||||||
|
final modifiedDocumentIds = await _documentsApi.bulkAction(
|
||||||
|
BulkModifyTagsAction(
|
||||||
|
state.selectedIds,
|
||||||
|
addTags: addTagIds,
|
||||||
|
removeTags: removeTagIds,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final updatedDocuments = state.selection
|
||||||
|
.where((element) => modifiedDocumentIds.contains(element.id))
|
||||||
|
.map(
|
||||||
|
(doc) => doc.copyWith(
|
||||||
|
tags: [
|
||||||
|
...doc.tags.toSet().difference(addTagIds.toSet()),
|
||||||
|
...addTagIds
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
for (final doc in updatedDocuments) {
|
||||||
|
_notifier.notifyUpdated(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_notifier.unsubscribe(this);
|
||||||
|
for (final sub in _subscriptions) {
|
||||||
|
sub.cancel();
|
||||||
|
}
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
part of 'document_bulk_action_cubit.dart';
|
||||||
|
|
||||||
|
class DocumentBulkActionState extends Equatable {
|
||||||
|
final List<DocumentModel> selection;
|
||||||
|
final Map<int, Correspondent> correspondentOptions;
|
||||||
|
final Map<int, DocumentType> documentTypeOptions;
|
||||||
|
final Map<int, Tag> tagOptions;
|
||||||
|
final Map<int, StoragePath> storagePathOptions;
|
||||||
|
|
||||||
|
const DocumentBulkActionState({
|
||||||
|
this.correspondentOptions = const {},
|
||||||
|
this.documentTypeOptions = const {},
|
||||||
|
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);
|
||||||
|
|
||||||
|
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,98 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
import 'package:flutter/src/widgets/placeholder.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.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/labels/view/widgets/label_form_field.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class BulkEditLabelBottomSheet<T extends Label> extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final String formFieldLabel;
|
||||||
|
final Widget formFieldPrefixIcon;
|
||||||
|
final Map<int, T> Function(DocumentBulkActionState state)
|
||||||
|
availableOptionsSelector;
|
||||||
|
final void Function(int? selectedId) onSubmit;
|
||||||
|
final int? initialValue;
|
||||||
|
const BulkEditLabelBottomSheet({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.formFieldLabel,
|
||||||
|
required this.formFieldPrefixIcon,
|
||||||
|
required this.availableOptionsSelector,
|
||||||
|
required this.onSubmit,
|
||||||
|
this.initialValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BulkEditLabelBottomSheet<T>> createState() =>
|
||||||
|
_BulkEditLabelBottomSheetState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BulkEditLabelBottomSheetState<T extends Label>
|
||||||
|
extends State<BulkEditLabelBottomSheet<T>> {
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
|
child: BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
).paddedOnly(bottom: 24),
|
||||||
|
FormBuilder(
|
||||||
|
key: _formKey,
|
||||||
|
child: LabelFormField<T>(
|
||||||
|
initialValue:
|
||||||
|
IdQueryParameter.fromId(widget.initialValue),
|
||||||
|
name: "labelFormField",
|
||||||
|
labelOptions: widget.availableOptionsSelector(state),
|
||||||
|
textFieldLabel: widget.formFieldLabel,
|
||||||
|
formBuilderState: _formKey.currentState,
|
||||||
|
prefixIcon: widget.formFieldPrefixIcon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
const DialogCancelButton(),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState?.saveAndValidate() ??
|
||||||
|
false) {
|
||||||
|
final value = _formKey.currentState
|
||||||
|
?.getRawValue('labelFormField')
|
||||||
|
as IdQueryParameter?;
|
||||||
|
widget.onSubmit(value?.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(S.of(context)!.apply),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padded(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.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/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';
|
||||||
|
|
||||||
|
class BulkEditTagsBottomSheet extends StatefulWidget {
|
||||||
|
const BulkEditTagsBottomSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BulkEditTagsBottomSheet> createState() =>
|
||||||
|
_BulkEditTagsBottomSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
List<int> _tagsToRemove = [];
|
||||||
|
List<int> _tagsToAdd = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||||
|
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(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
|
child: BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Bulk modify tags",
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
).paddedOnly(bottom: 24),
|
||||||
|
FormBuilder(
|
||||||
|
key: _formKey,
|
||||||
|
child: TagFormField(
|
||||||
|
initialValue: IdsTagsQuery(
|
||||||
|
sharedTags.map((tag) => IncludeTagIdQuery(tag)),
|
||||||
|
),
|
||||||
|
name: "labelFormField",
|
||||||
|
selectableOptions: state.tagOptions,
|
||||||
|
allowCreation: false,
|
||||||
|
anyAssignedSelectable: false,
|
||||||
|
excludeAllowed: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text("Tags removed after apply"),
|
||||||
|
Wrap(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text("Tags added after apply"),
|
||||||
|
Wrap(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
const DialogCancelButton(),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState?.saveAndValidate() ??
|
||||||
|
false) {
|
||||||
|
final value = _formKey.currentState
|
||||||
|
?.getRawValue('labelFormField')
|
||||||
|
as IdsTagsQuery;
|
||||||
|
context
|
||||||
|
.read<DocumentBulkActionCubit>()
|
||||||
|
.bulkModifyTags(
|
||||||
|
addTagIds: value.includedIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(S.of(context)!.apply),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padded(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,43 +58,6 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
await reload();
|
await reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> bulkEditTags(
|
|
||||||
Iterable<DocumentModel> documents, {
|
|
||||||
Iterable<int> addTags = const [],
|
|
||||||
Iterable<int> removeTags = const [],
|
|
||||||
}) async {
|
|
||||||
debugPrint("[DocumentsCubit] bulkEditTags");
|
|
||||||
final edited = await api.bulkAction(BulkModifyTagsAction(
|
|
||||||
documents.map((doc) => doc.id),
|
|
||||||
addTags: addTags,
|
|
||||||
removeTags: removeTags,
|
|
||||||
));
|
|
||||||
|
|
||||||
await reload();
|
|
||||||
for (final id in edited) {
|
|
||||||
final doc =
|
|
||||||
state.documents.firstWhereOrNull((element) => element.id == id);
|
|
||||||
if (doc != null) {
|
|
||||||
notifier.notifyUpdated(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> bulkAction(BulkAction action) async {
|
|
||||||
debugPrint("[DocumentsCubit] bulkEditLabel");
|
|
||||||
|
|
||||||
final edited = await api.bulkAction(action);
|
|
||||||
await reload();
|
|
||||||
|
|
||||||
for (final id in edited) {
|
|
||||||
final doc =
|
|
||||||
state.documents.firstWhereOrNull((element) => element.id == id);
|
|
||||||
if (doc != null) {
|
|
||||||
notifier.notifyUpdated(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleDocumentSelection(DocumentModel model) {
|
void toggleDocumentSelection(DocumentModel model) {
|
||||||
debugPrint("[DocumentsCubit] toggleSelection");
|
debugPrint("[DocumentsCubit] toggleSelection");
|
||||||
if (state.selectedIds.contains(model.id)) {
|
if (state.selectedIds.contains(model.id)) {
|
||||||
|
|||||||
@@ -4,20 +4,13 @@ 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/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/view/widgets/bulk_edit_label_bottom_sheet.dart';
|
||||||
|
import 'package:paperless_mobile/features/document_bulk_action/view/widgets/bulk_edit_tags_bottom_sheet.dart';
|
||||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/correspondent_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/document_type_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/labels_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/cubit/providers/storage_path_bloc_provider.dart';
|
|
||||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.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:provider/provider.dart';
|
|
||||||
|
|
||||||
class DocumentSelectionSliverAppBar extends StatelessWidget {
|
class DocumentSelectionSliverAppBar extends StatelessWidget {
|
||||||
final DocumentsState state;
|
final DocumentsState state;
|
||||||
@@ -62,17 +55,19 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: Size.fromHeight(kTextTabBarHeight),
|
preferredSize: const Size.fromHeight(kTextTabBarHeight),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: kTextTabBarHeight,
|
height: kTextTabBarHeight,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
_buildBulkEditCorrespondentChip(context)
|
_buildBulkEditCorrespondentChip(context)
|
||||||
.paddedOnly(left: 8, right: 8),
|
.paddedOnly(left: 8, right: 4),
|
||||||
_buildBulkEditDocumentTypeChip(context).paddedOnly(right: 8),
|
_buildBulkEditDocumentTypeChip(context)
|
||||||
_buildBulkEditTagChip(context).paddedOnly(right: 8),
|
.paddedOnly(left: 4, right: 4),
|
||||||
_buildBulkEditStoragePathChip(context).paddedOnly(right: 8),
|
_buildBulkEditStoragePathChip(context)
|
||||||
|
.paddedOnly(left: 4, right: 4),
|
||||||
|
// _buildBulkEditTagsChip(context).paddedOnly(left: 4, right: 4),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -83,13 +78,12 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
Widget _buildBulkEditCorrespondentChip(BuildContext context) {
|
Widget _buildBulkEditCorrespondentChip(BuildContext context) {
|
||||||
return ActionChip(
|
return ActionChip(
|
||||||
label: Text(S.of(context)!.correspondent),
|
label: Text(S.of(context)!.correspondent),
|
||||||
avatar: Icon(Icons.edit),
|
avatar: const Icon(Icons.edit),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
final initialValue = state.selection.every((element) =>
|
final initialValue = state.selection.every((element) =>
|
||||||
element.correspondent == state.selection.first.correspondent)
|
element.correspondent == state.selection.first.correspondent)
|
||||||
? IdQueryParameter.fromId(state.selection.first.correspondent)
|
? state.selection.first.correspondent
|
||||||
: const IdQueryParameter.unset();
|
: null;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -97,41 +91,35 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isScrollControlled: true,
|
isScrollControlled: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BulkEditBottomSheet(
|
return BlocProvider(
|
||||||
formKey: _formKey,
|
create: (context) => DocumentBulkActionCubit(
|
||||||
formFieldName: "correspondent",
|
context.read(),
|
||||||
initialValue: initialValue,
|
context.read(),
|
||||||
selectedIds: state.selectedIds,
|
context.read(),
|
||||||
actionBuilder: (int? id) => BulkModifyLabelAction.correspondent(
|
context.read(),
|
||||||
state.selectedIds,
|
context.read(),
|
||||||
labelId: id,
|
context.read(),
|
||||||
|
selection: state.selection,
|
||||||
),
|
),
|
||||||
formField: CorrespondentBlocProvider(
|
child: Builder(builder: (context) {
|
||||||
child: BlocBuilder<LabelCubit<Correspondent>,
|
return BulkEditLabelBottomSheet<Correspondent>(
|
||||||
LabelState<Correspondent>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<Correspondent>(
|
|
||||||
name: "correspondent",
|
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
notAssignedSelectable: false,
|
title: "Bulk edit correspondent",
|
||||||
labelCreationWidgetBuilder: (initialName) {
|
availableOptionsSelector: (state) =>
|
||||||
return AddCorrespondentPage(
|
state.correspondentOptions,
|
||||||
initialName: initialName,
|
formFieldLabel: S.of(context)!.correspondent,
|
||||||
|
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
||||||
|
onSubmit: (selectedId) async {
|
||||||
|
await context
|
||||||
|
.read<DocumentBulkActionCubit>()
|
||||||
|
.bulkModifyCorrespondent(selectedId);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
labelOptions: state.labels,
|
|
||||||
textFieldLabel: "Correspondent",
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
prefixIcon: const Icon(Icons.person),
|
|
||||||
).padded();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onQuerySubmitted: context.read<DocumentsCubit>().bulkAction,
|
|
||||||
title: 'Bulk edit correspondent',
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -142,13 +130,12 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
Widget _buildBulkEditDocumentTypeChip(BuildContext context) {
|
Widget _buildBulkEditDocumentTypeChip(BuildContext context) {
|
||||||
return ActionChip(
|
return ActionChip(
|
||||||
label: Text(S.of(context)!.documentType),
|
label: Text(S.of(context)!.documentType),
|
||||||
avatar: Icon(Icons.edit),
|
avatar: const Icon(Icons.edit),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
final initialValue = state.selection.every((element) =>
|
final initialValue = state.selection.every((element) =>
|
||||||
element.documentType == state.selection.first.documentType)
|
element.documentType == state.selection.first.documentType)
|
||||||
? IdQueryParameter.fromId(state.selection.first.documentType)
|
? state.selection.first.documentType
|
||||||
: const IdQueryParameter.unset();
|
: null;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -156,100 +143,35 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isScrollControlled: true,
|
isScrollControlled: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BulkEditBottomSheet(
|
return BlocProvider(
|
||||||
formKey: _formKey,
|
create: (context) => DocumentBulkActionCubit(
|
||||||
formFieldName: "documentType",
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
selection: state.selection,
|
||||||
|
),
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
return BulkEditLabelBottomSheet<DocumentType>(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
selectedIds: state.selectedIds,
|
title: "Bulk edit document type",
|
||||||
actionBuilder: (int? id) => BulkModifyLabelAction.documentType(
|
availableOptionsSelector: (state) =>
|
||||||
state.selectedIds,
|
state.documentTypeOptions,
|
||||||
labelId: id,
|
formFieldLabel: S.of(context)!.documentType,
|
||||||
),
|
formFieldPrefixIcon: const Icon(Icons.person_outline),
|
||||||
formField: DocumentTypeBlocProvider(
|
onSubmit: (selectedId) async {
|
||||||
child: BlocBuilder<LabelCubit<DocumentType>,
|
await context
|
||||||
LabelState<DocumentType>>(
|
.read<DocumentBulkActionCubit>()
|
||||||
builder: (context, state) {
|
.bulkModifyDocumentType(selectedId);
|
||||||
return LabelFormField<DocumentType>(
|
Navigator.pop(context);
|
||||||
name: "documentType",
|
|
||||||
initialValue: initialValue,
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
labelCreationWidgetBuilder: (initialName) {
|
|
||||||
return AddDocumentTypePage(
|
|
||||||
initialName: initialName,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
labelOptions: state.labels,
|
|
||||||
textFieldLabel: S.of(context)!.documentType,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
prefixIcon: const Icon(Icons.person),
|
|
||||||
).padded();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onQuerySubmitted: context.read<DocumentsCubit>().bulkAction,
|
|
||||||
title: 'Bulk edit document type',
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBulkEditTagChip(BuildContext context) {
|
|
||||||
return ActionChip(
|
|
||||||
label: Text(S.of(context)!.correspondent),
|
|
||||||
avatar: Icon(Icons.edit),
|
|
||||||
onPressed: () {
|
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
final initialValue = state.selection.every((element) =>
|
|
||||||
element.correspondent == state.selection.first.correspondent)
|
|
||||||
? IdQueryParameter.fromId(state.selection.first.correspondent)
|
|
||||||
: const IdQueryParameter.unset();
|
|
||||||
showModalBottomSheet(
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(16),
|
|
||||||
topRight: Radius.circular(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isScrollControlled: true,
|
|
||||||
context: context,
|
|
||||||
builder: (_) {
|
|
||||||
return BulkEditBottomSheet(
|
|
||||||
formKey: _formKey,
|
|
||||||
formFieldName: "correspondent",
|
|
||||||
initialValue: initialValue,
|
|
||||||
selectedIds: state.selectedIds,
|
|
||||||
actionBuilder: (int? id) => BulkModifyLabelAction.correspondent(
|
|
||||||
state.selectedIds,
|
|
||||||
labelId: id,
|
|
||||||
),
|
|
||||||
formField: CorrespondentBlocProvider(
|
|
||||||
child: BlocBuilder<LabelCubit<Correspondent>,
|
|
||||||
LabelState<Correspondent>>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return LabelFormField<Correspondent>(
|
|
||||||
name: "correspondent",
|
|
||||||
initialValue: initialValue,
|
|
||||||
notAssignedSelectable: false,
|
|
||||||
labelCreationWidgetBuilder: (initialName) {
|
|
||||||
return AddCorrespondentPage(
|
|
||||||
initialName: initialName,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
labelOptions: state.labels,
|
|
||||||
textFieldLabel: "Correspondent",
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
prefixIcon: const Icon(Icons.person),
|
|
||||||
).padded();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onQuerySubmitted: context.read<DocumentsCubit>().bulkAction,
|
|
||||||
title: 'Bulk edit correspondent',
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -260,13 +182,12 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
Widget _buildBulkEditStoragePathChip(BuildContext context) {
|
Widget _buildBulkEditStoragePathChip(BuildContext context) {
|
||||||
return ActionChip(
|
return ActionChip(
|
||||||
label: Text(S.of(context)!.storagePath),
|
label: Text(S.of(context)!.storagePath),
|
||||||
avatar: Icon(Icons.edit),
|
avatar: const Icon(Icons.edit),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
|
||||||
final initialValue = state.selection.every((element) =>
|
final initialValue = state.selection.every((element) =>
|
||||||
element.storagePath == state.selection.first.storagePath)
|
element.storagePath == state.selection.first.storagePath)
|
||||||
? IdQueryParameter.fromId(state.selection.first.storagePath)
|
? state.selection.first.storagePath
|
||||||
: const IdQueryParameter.unset();
|
: null;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -274,41 +195,69 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isScrollControlled: true,
|
isScrollControlled: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BulkEditBottomSheet(
|
return BlocProvider(
|
||||||
formKey: _formKey,
|
create: (context) => DocumentBulkActionCubit(
|
||||||
formFieldName: "storagePath",
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
selection: state.selection,
|
||||||
|
),
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
return BulkEditLabelBottomSheet<StoragePath>(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
selectedIds: state.selectedIds,
|
title: "Bulk edit storage path",
|
||||||
actionBuilder: (int? id) => BulkModifyLabelAction.storagePath(
|
availableOptionsSelector: (state) => state.storagePathOptions,
|
||||||
state.selectedIds,
|
formFieldLabel: S.of(context)!.storagePath,
|
||||||
labelId: id,
|
formFieldPrefixIcon: const Icon(Icons.folder_open_outlined),
|
||||||
),
|
onSubmit: (selectedId) async {
|
||||||
formField: StoragePathBlocProvider(
|
await context
|
||||||
child: BlocBuilder<LabelCubit<StoragePath>,
|
.read<DocumentBulkActionCubit>()
|
||||||
LabelState<StoragePath>>(
|
.bulkModifyStoragePath(selectedId);
|
||||||
builder: (context, state) {
|
Navigator.pop(context);
|
||||||
return LabelFormField<StoragePath>(
|
},
|
||||||
name: "storagePath",
|
);
|
||||||
initialValue: initialValue,
|
}),
|
||||||
notAssignedSelectable: false,
|
|
||||||
labelCreationWidgetBuilder: (initialName) {
|
|
||||||
return AddStoragePathPage(
|
|
||||||
initalName: initialName,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
labelOptions: state.labels,
|
);
|
||||||
textFieldLabel: S.of(context)!.storagePath,
|
|
||||||
formBuilderState: _formKey.currentState,
|
|
||||||
prefixIcon: const Icon(Icons.person),
|
|
||||||
).padded();
|
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBulkEditTagsChip(BuildContext context) {
|
||||||
|
return ActionChip(
|
||||||
|
label: Text(S.of(context)!.tags),
|
||||||
|
avatar: const Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onQuerySubmitted: context.read<DocumentsCubit>().bulkAction,
|
isScrollControlled: false,
|
||||||
title: 'Bulk edit storage path',
|
context: context,
|
||||||
|
builder: (_) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => DocumentBulkActionCubit(
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
context.read(),
|
||||||
|
selection: state.selection,
|
||||||
|
),
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
return const BulkEditTagsBottomSheet();
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -316,82 +265,3 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BulkEditBottomSheet extends StatefulWidget {
|
|
||||||
final Future<void> Function(BulkAction action) onQuerySubmitted;
|
|
||||||
final List<int> selectedIds;
|
|
||||||
final IdQueryParameter initialValue;
|
|
||||||
final String title;
|
|
||||||
final Widget formField;
|
|
||||||
final String formFieldName;
|
|
||||||
final BulkAction Function(int? id) actionBuilder;
|
|
||||||
final GlobalKey<FormBuilderState> formKey;
|
|
||||||
const BulkEditBottomSheet({
|
|
||||||
super.key,
|
|
||||||
required this.initialValue,
|
|
||||||
required this.onQuerySubmitted,
|
|
||||||
required this.selectedIds,
|
|
||||||
required this.title,
|
|
||||||
required this.formField,
|
|
||||||
required this.formFieldName,
|
|
||||||
required this.actionBuilder,
|
|
||||||
required this.formKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<BulkEditBottomSheet> createState() => _BulkEditBottomSheetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BulkEditBottomSheetState extends State<BulkEditBottomSheet> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.title,
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
|
||||||
).padded(16),
|
|
||||||
FormBuilder(
|
|
||||||
key: widget.formKey,
|
|
||||||
child: widget.formField,
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomRight,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
const DialogCancelButton().paddedOnly(right: 8),
|
|
||||||
FilledButton(
|
|
||||||
child: Text(S.of(context)!.apply),
|
|
||||||
onPressed: () async {
|
|
||||||
if (widget.formKey.currentState?.saveAndValidate() ??
|
|
||||||
false) {
|
|
||||||
final value = widget
|
|
||||||
.formKey
|
|
||||||
.currentState!
|
|
||||||
.fields[widget.formFieldName]
|
|
||||||
?.value as IdQueryParameter;
|
|
||||||
final id = value.id;
|
|
||||||
await widget.onQuerySubmitted(widget.actionBuilder(id));
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
showSnackBar(
|
|
||||||
context,
|
|
||||||
"Documents successfully edited.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padded(16),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class _LabelFormFieldState<T extends Label> extends State<LabelFormField<T>> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: widget.prefixIcon,
|
prefixIcon: widget.prefixIcon,
|
||||||
label: Text(widget.textFieldLabel),
|
label: Text(widget.textFieldLabel),
|
||||||
hintText: _getLocalizedHint(context),
|
hintText: S.of(context)!.startTyping,
|
||||||
suffixIcon: _buildSuffixIcon(context),
|
suffixIcon: _buildSuffixIcon(context),
|
||||||
),
|
),
|
||||||
selectionToTextTransformer: (suggestion) {
|
selectionToTextTransformer: (suggestion) {
|
||||||
@@ -192,14 +192,4 @@ class _LabelFormFieldState<T extends Label> extends State<LabelFormField<T>> {
|
|||||||
);
|
);
|
||||||
_textEditingController.clear();
|
_textEditingController.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getLocalizedHint(BuildContext context) {
|
|
||||||
if (T == Correspondent) {
|
|
||||||
return S.of(context)!.startTyping;
|
|
||||||
} else if (T == DocumentType) {
|
|
||||||
return S.of(context)!.startTyping;
|
|
||||||
} else {
|
|
||||||
return S.of(context)!.filterTags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user