mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-07 11:15:49 -06:00
feat: Add bulk edit options (WIP)
This commit is contained in:
@@ -196,7 +196,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
|||||||
formBuilderState: _formKey.currentState,
|
formBuilderState: _formKey.currentState,
|
||||||
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider(
|
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider(
|
||||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
||||||
child: AddStoragePathPage(initalValue: initialValue),
|
child: AddStoragePathPage(initalName: initialValue),
|
||||||
),
|
),
|
||||||
textFieldLabel: S.of(context)!.storagePath,
|
textFieldLabel: S.of(context)!.storagePath,
|
||||||
labelOptions: options,
|
labelOptions: options,
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ class SliverSearchBar extends StatelessWidget {
|
|||||||
floating: floating,
|
floating: floating,
|
||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
delegate: CustomizableSliverPersistentHeaderDelegate(
|
delegate: CustomizableSliverPersistentHeaderDelegate(
|
||||||
minExtent: 56 + 8,
|
minExtent: kToolbarHeight + 8,
|
||||||
maxExtent: 56 + 8,
|
maxExtent: kToolbarHeight + 8,
|
||||||
child: Padding(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: SearchBar(
|
child: SearchBar(
|
||||||
height: 56,
|
height: kToolbarHeight,
|
||||||
supportingText: S.of(context)!.searchDocuments,
|
supportingText: S.of(context)!.searchDocuments,
|
||||||
onTap: () => showDocumentSearchPage(context),
|
onTap: () => showDocumentSearchPage(context),
|
||||||
leadingIcon: IconButton(
|
leadingIcon: IconButton(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
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:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
@@ -24,8 +25,25 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
DocumentsCubit(this.api, this.notifier) : super(const DocumentsState()) {
|
DocumentsCubit(this.api, this.notifier) : super(const DocumentsState()) {
|
||||||
notifier.subscribe(
|
notifier.subscribe(
|
||||||
this,
|
this,
|
||||||
onUpdated: replace,
|
onUpdated: (document) {
|
||||||
onDeleted: remove,
|
replace(document);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
selection: state.selection
|
||||||
|
.map((e) => e.id == document.id ? document : e)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onDeleted: (document) {
|
||||||
|
remove(document);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
selection:
|
||||||
|
state.selection.where((e) => e.id != document.id).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,12 +64,35 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
|||||||
Iterable<int> removeTags = const [],
|
Iterable<int> removeTags = const [],
|
||||||
}) async {
|
}) async {
|
||||||
debugPrint("[DocumentsCubit] bulkEditTags");
|
debugPrint("[DocumentsCubit] bulkEditTags");
|
||||||
await api.bulkAction(BulkModifyTagsAction(
|
final edited = await api.bulkAction(BulkModifyTagsAction(
|
||||||
documents.map((doc) => doc.id),
|
documents.map((doc) => doc.id),
|
||||||
addTags: addTags,
|
addTags: addTags,
|
||||||
removeTags: removeTags,
|
removeTags: removeTags,
|
||||||
));
|
));
|
||||||
|
|
||||||
await reload();
|
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) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_he
|
|||||||
import 'package:paperless_mobile/core/widgets/material/search/colored_tab_bar.dart';
|
import 'package:paperless_mobile/core/widgets/material/search/colored_tab_bar.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
|
||||||
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
||||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
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';
|
||||||
@@ -167,7 +166,8 @@ class _DocumentsPageState extends State<DocumentsPage>
|
|||||||
if (state.selection.isNotEmpty) {
|
if (state.selection.isNotEmpty) {
|
||||||
// Show selection app bar when selection mode is active
|
// Show selection app bar when selection mode is active
|
||||||
return DocumentSelectionSliverAppBar(
|
return DocumentSelectionSliverAppBar(
|
||||||
state: state);
|
state: state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return const SliverSearchBar(floating: true);
|
return const SliverSearchBar(floating: true);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class _DocumentFilterFormState extends State<DocumentFilterForm> {
|
|||||||
slivers: [
|
slivers: [
|
||||||
if (widget.header != null) widget.header!,
|
if (widget.header != null) widget.header!,
|
||||||
..._buildFormFieldList(),
|
..._buildFormFieldList(),
|
||||||
SliverToBoxAdapter(
|
const SliverToBoxAdapter(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/src/widgets/framework.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter/src/widgets/placeholder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
import 'package:paperless_mobile/features/documents/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';
|
import 'package:provider/provider.dart';
|
||||||
@@ -50,6 +61,337 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(kTextTabBarHeight),
|
||||||
|
child: SizedBox(
|
||||||
|
height: kTextTabBarHeight,
|
||||||
|
child: ListView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
children: [
|
||||||
|
_buildBulkEditCorrespondentChip(context)
|
||||||
|
.paddedOnly(left: 8, right: 8),
|
||||||
|
_buildBulkEditDocumentTypeChip(context).paddedOnly(right: 8),
|
||||||
|
_buildBulkEditTagChip(context).paddedOnly(right: 8),
|
||||||
|
_buildBulkEditStoragePathChip(context).paddedOnly(right: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBulkEditCorrespondentChip(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',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBulkEditDocumentTypeChip(BuildContext context) {
|
||||||
|
return ActionChip(
|
||||||
|
label: Text(S.of(context)!.documentType),
|
||||||
|
avatar: Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
final initialValue = state.selection.every((element) =>
|
||||||
|
element.documentType == state.selection.first.documentType)
|
||||||
|
? IdQueryParameter.fromId(state.selection.first.documentType)
|
||||||
|
: 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: "documentType",
|
||||||
|
initialValue: initialValue,
|
||||||
|
selectedIds: state.selectedIds,
|
||||||
|
actionBuilder: (int? id) => BulkModifyLabelAction.documentType(
|
||||||
|
state.selectedIds,
|
||||||
|
labelId: id,
|
||||||
|
),
|
||||||
|
formField: DocumentTypeBlocProvider(
|
||||||
|
child: BlocBuilder<LabelCubit<DocumentType>,
|
||||||
|
LabelState<DocumentType>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return LabelFormField<DocumentType>(
|
||||||
|
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',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBulkEditStoragePathChip(BuildContext context) {
|
||||||
|
return ActionChip(
|
||||||
|
label: Text(S.of(context)!.storagePath),
|
||||||
|
avatar: Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
|
final initialValue = state.selection.every((element) =>
|
||||||
|
element.storagePath == state.selection.first.storagePath)
|
||||||
|
? IdQueryParameter.fromId(state.selection.first.storagePath)
|
||||||
|
: 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: "storagePath",
|
||||||
|
initialValue: initialValue,
|
||||||
|
selectedIds: state.selectedIds,
|
||||||
|
actionBuilder: (int? id) => BulkModifyLabelAction.storagePath(
|
||||||
|
state.selectedIds,
|
||||||
|
labelId: id,
|
||||||
|
),
|
||||||
|
formField: StoragePathBlocProvider(
|
||||||
|
child: BlocBuilder<LabelCubit<StoragePath>,
|
||||||
|
LabelState<StoragePath>>(
|
||||||
|
builder: (context, state) {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onQuerySubmitted: context.read<DocumentsCubit>().bulkAction,
|
||||||
|
title: 'Bulk edit storage path',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import 'package:paperless_mobile/features/labels/storage_path/view/widgets/stora
|
|||||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AddStoragePathPage extends StatelessWidget {
|
class AddStoragePathPage extends StatelessWidget {
|
||||||
final String? initalValue;
|
final String? initalName;
|
||||||
const AddStoragePathPage({Key? key, this.initalValue}) : super(key: key);
|
const AddStoragePathPage({Key? key, this.initalName}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -21,7 +21,7 @@ class AddStoragePathPage extends StatelessWidget {
|
|||||||
child: AddLabelPage<StoragePath>(
|
child: AddLabelPage<StoragePath>(
|
||||||
pageTitle: Text(S.of(context)!.addStoragePath),
|
pageTitle: Text(S.of(context)!.addStoragePath),
|
||||||
fromJsonT: StoragePath.fromJson,
|
fromJsonT: StoragePath.fromJson,
|
||||||
initialName: initalValue,
|
initialName: initalName,
|
||||||
additionalFields: const [
|
additionalFields: const [
|
||||||
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
StoragePathAutofillFormBuilderField(name: StoragePath.pathKey),
|
||||||
SizedBox(height: 120.0),
|
SizedBox(height: 120.0),
|
||||||
|
|||||||
@@ -48,3 +48,34 @@ class BulkModifyTagsAction extends BulkAction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BulkModifyLabelAction extends BulkAction {
|
||||||
|
final String _labelName;
|
||||||
|
final int? labelId;
|
||||||
|
|
||||||
|
BulkModifyLabelAction.correspondent(
|
||||||
|
super.documents, {
|
||||||
|
required this.labelId,
|
||||||
|
}) : _labelName = 'correspondent';
|
||||||
|
|
||||||
|
BulkModifyLabelAction.documentType(
|
||||||
|
super.documents, {
|
||||||
|
required this.labelId,
|
||||||
|
}) : _labelName = 'document_type';
|
||||||
|
|
||||||
|
BulkModifyLabelAction.storagePath(
|
||||||
|
super.documents, {
|
||||||
|
required this.labelId,
|
||||||
|
}) : _labelName = 'storage_path';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'documents': documentIds.toList(),
|
||||||
|
'method': 'set_$_labelName',
|
||||||
|
'parameters': {
|
||||||
|
_labelName: labelId,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user