mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 16:07:52 -06:00
feat: Finalize bulk edits and reworked form fields
This commit is contained in:
@@ -65,7 +65,7 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
||||
final deletedDocuments = state.selection
|
||||
.where((element) => deletedDocumentIds.contains(element.id));
|
||||
for (final doc in deletedDocuments) {
|
||||
_notifier.notifyUpdated(doc);
|
||||
_notifier.notifyDeleted(doc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class DocumentBulkActionCubit extends Cubit<DocumentBulkActionState> {
|
||||
final updatedDocuments = state.selection
|
||||
.where((element) => modifiedDocumentIds.contains(element.id))
|
||||
.map((doc) => doc.copyWith(tags: [
|
||||
...doc.tags.toSet().difference(addTagIds.toSet()),
|
||||
...doc.tags.toSet().difference(removeTagIds.toSet()),
|
||||
...addTagIds
|
||||
]));
|
||||
for (final doc in updatedDocuments) {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class BulkEditPage<T extends Label> extends StatefulWidget {
|
||||
final bool enableMultipleChoice;
|
||||
final Map<int, T> availableOptions;
|
||||
|
||||
const BulkEditPage({
|
||||
super.key,
|
||||
required this.enableMultipleChoice,
|
||||
required this.availableOptions,
|
||||
});
|
||||
|
||||
@override
|
||||
State<BulkEditPage> createState() => _BulkEditPageState();
|
||||
}
|
||||
|
||||
class _BulkEditPageState extends State<BulkEditPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
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:flutter_typeahead/flutter_typeahead.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/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>();
|
||||
final _textEditingController = TextEditingController();
|
||||
late Set<int> _sharedTags;
|
||||
late Set<int> _nonSharedTags;
|
||||
final Set<int> _sharedTagsToRemove = {};
|
||||
final Set<int> _nonSharedTagsToRemove = {};
|
||||
final Set<int> _tagsToAdd = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final state = context.read<DocumentBulkActionCubit>().state;
|
||||
_sharedTags = state.selection
|
||||
.map((doc) => doc.tags)
|
||||
.reduce((previousValue, element) =>
|
||||
previousValue.toSet().intersection(element.toSet()))
|
||||
.toSet();
|
||||
print(_sharedTags.map((e) => e).join(", "));
|
||||
_nonSharedTags = state.selection
|
||||
.map((doc) => doc.tags)
|
||||
.flattened
|
||||
.toSet()
|
||||
.difference(_sharedTags)
|
||||
.toSet();
|
||||
print(_nonSharedTags.map((e) => e).join(", "));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||
builder: (context, state) {
|
||||
print(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),
|
||||
TypeAheadFormField<Tag>(
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
controller: _textEditingController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Tags",
|
||||
hintText: "Start typing to add tags...",
|
||||
),
|
||||
),
|
||||
onSuggestionSelected: (suggestion) {
|
||||
setState(() {
|
||||
_tagsToAdd.add(suggestion.id!);
|
||||
});
|
||||
_textEditingController.clear();
|
||||
},
|
||||
itemBuilder: (context, option) {
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: option.color!,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(option.name),
|
||||
);
|
||||
},
|
||||
suggestionsCallback: (pattern) {
|
||||
final searchString = pattern.toLowerCase();
|
||||
return state.tags.entries
|
||||
.where(
|
||||
(tag) => tag.value.name
|
||||
.toLowerCase()
|
||||
.contains(searchString),
|
||||
)
|
||||
.map((e) => e.key)
|
||||
.toSet()
|
||||
.difference(_sharedTags)
|
||||
.difference(_nonSharedTags)
|
||||
.map((e) => state.tags[e]!);
|
||||
},
|
||||
),
|
||||
Text("Shared tags"),
|
||||
Wrap(
|
||||
children: _sharedTags
|
||||
.map(
|
||||
(tag) => RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_sharedTagsToRemove.add(tag);
|
||||
_sharedTags.remove(tag);
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text("Non-shared tags"),
|
||||
Wrap(
|
||||
children: _nonSharedTags
|
||||
.map(
|
||||
(tag) => RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_nonSharedTagsToRemove.add(tag);
|
||||
_nonSharedTags.remove(tag);
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
Text("Remove"),
|
||||
Wrap(
|
||||
children: _sharedTagsToRemove.map((tag) {
|
||||
return RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_sharedTagsToRemove.remove(tag);
|
||||
_sharedTags.add(tag);
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList() +
|
||||
_nonSharedTagsToRemove.map((tag) {
|
||||
return RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_nonSharedTagsToRemove.remove(tag);
|
||||
_nonSharedTags.add(tag);
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text("Add"),
|
||||
Wrap(
|
||||
children: _tagsToAdd
|
||||
.map(
|
||||
(tag) => RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_tagsToAdd.remove(tag);
|
||||
});
|
||||
}),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RemovableTagWidget extends StatelessWidget {
|
||||
final Tag tag;
|
||||
final void Function(int tagId) onDeleted;
|
||||
const RemovableTagWidget(
|
||||
{super.key, required this.tag, required this.onDeleted});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Chip(
|
||||
label: Text(
|
||||
tag.name,
|
||||
style: TextStyle(
|
||||
color: tag.textColor,
|
||||
),
|
||||
),
|
||||
onDeleted: () => onDeleted(tag.id!),
|
||||
deleteIcon: Icon(Icons.clear),
|
||||
backgroundColor: tag.color,
|
||||
deleteIconColor: tag.textColor,
|
||||
padding: EdgeInsets.zero,
|
||||
side: BorderSide.none,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class ConfirmBulkModifyLabelDialog extends StatelessWidget {
|
||||
final int selectionCount;
|
||||
final String content;
|
||||
const ConfirmBulkModifyLabelDialog({
|
||||
super.key,
|
||||
required this.selectionCount,
|
||||
required this.content,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(S.of(context)!.confirmAction),
|
||||
content: RichText(
|
||||
text: TextSpan(
|
||||
text: content,
|
||||
children: [
|
||||
const TextSpan(text: "\n\n"),
|
||||
TextSpan(
|
||||
text: S.of(context)!.areYouSureYouWantToContinue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: const [
|
||||
DialogCancelButton(),
|
||||
DialogConfirmButton(
|
||||
style: DialogConfirmButtonStyle.danger,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter/src/widgets/placeholder.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class ConfirmBulkModifyTagsDialog extends StatelessWidget {
|
||||
final int selectionCount;
|
||||
final List<String> removeTags;
|
||||
final List<String> addTags;
|
||||
const ConfirmBulkModifyTagsDialog({
|
||||
super.key,
|
||||
required this.removeTags,
|
||||
required this.addTags,
|
||||
required this.selectionCount,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(S.of(context)!.confirmAction),
|
||||
content: RichText(
|
||||
text: TextSpan(
|
||||
text: _buildText(context),
|
||||
children: [
|
||||
const TextSpan(text: "\n\n"),
|
||||
TextSpan(
|
||||
text: S.of(context)!.areYouSureYouWantToContinue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: const [
|
||||
DialogCancelButton(),
|
||||
DialogConfirmButton(
|
||||
style: DialogConfirmButtonStyle.danger,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _buildText(BuildContext context) {
|
||||
if (removeTags.isNotEmpty && addTags.isNotEmpty) {
|
||||
return S.of(context)!.bulkEditTagsModifyMessage(
|
||||
addTags.join(", "),
|
||||
selectionCount,
|
||||
removeTags.join(", "),
|
||||
);
|
||||
} else if (removeTags.isNotEmpty) {
|
||||
return S.of(context)!.bulkEditTagsRemoveMessage(
|
||||
selectionCount,
|
||||
removeTags.join(", "),
|
||||
);
|
||||
} else {
|
||||
return S.of(context)!.bulkEditTagsAddMessage(
|
||||
selectionCount,
|
||||
addTags.join(", "),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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_confirm_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/form_fields/fullscreen_selection_form.dart';
|
||||
import 'package:paperless_mobile/features/document_bulk_action/cubit/document_bulk_action_cubit.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class FullscreenBulkEditLabelFormField extends StatefulWidget {
|
||||
class FullscreenBulkEditLabelPage extends StatefulWidget {
|
||||
final String hintText;
|
||||
final Map<int, Label> options;
|
||||
final List<DocumentModel> selection;
|
||||
final int? Function(DocumentModel document) labelMapper;
|
||||
final Widget leadingIcon;
|
||||
final void Function(int? id) onSubmit;
|
||||
final Function(String name) Function(int count) removeStringFnBuilder;
|
||||
final String Function(String name) Function(int count) assignStringFnBuilder;
|
||||
|
||||
FullscreenBulkEditLabelFormField({
|
||||
FullscreenBulkEditLabelPage({
|
||||
super.key,
|
||||
required this.options,
|
||||
required this.selection,
|
||||
@@ -23,20 +25,27 @@ class FullscreenBulkEditLabelFormField extends StatefulWidget {
|
||||
required this.leadingIcon,
|
||||
required this.hintText,
|
||||
required this.onSubmit,
|
||||
required this.removeStringFnBuilder,
|
||||
required this.assignStringFnBuilder,
|
||||
}) : assert(selection.isNotEmpty);
|
||||
|
||||
@override
|
||||
State<FullscreenBulkEditLabelFormField> createState() =>
|
||||
_FullscreenBulkEditLabelFormFieldState();
|
||||
State<FullscreenBulkEditLabelPage> createState() =>
|
||||
_FullscreenBulkEditLabelPageState();
|
||||
}
|
||||
|
||||
class _FullscreenBulkEditLabelFormFieldState<T extends Label>
|
||||
extends State<FullscreenBulkEditLabelFormField> {
|
||||
class _FullscreenBulkEditLabelPageState<T extends Label>
|
||||
extends State<FullscreenBulkEditLabelPage> {
|
||||
final _controller = TextEditingController();
|
||||
|
||||
LabelSelection? _selection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
if (_initialValues.length == 1 && _initialValues.first != null) {
|
||||
_selection = LabelSelection(_initialValues.first);
|
||||
}
|
||||
@@ -46,13 +55,19 @@ class _FullscreenBulkEditLabelFormFieldState<T extends Label>
|
||||
widget.selection.map(widget.labelMapper).toSet().toList();
|
||||
|
||||
Iterable<int> _generateOrderedLabels() sync* {
|
||||
for (var label in _initialValues) {
|
||||
final _availableValues = widget.options.values
|
||||
.where(
|
||||
(e) => e.name.normalized().contains(_controller.text.normalized()))
|
||||
.map((e) => e.id!)
|
||||
.toSet();
|
||||
for (var label
|
||||
in _initialValues.toSet().intersection(_availableValues.toSet())) {
|
||||
if (label != null) {
|
||||
yield label;
|
||||
}
|
||||
}
|
||||
for (final id
|
||||
in widget.options.keys.whereNot((e) => _initialValues.contains(e))) {
|
||||
in _availableValues.whereNot((e) => _initialValues.contains(e))) {
|
||||
yield id;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +79,7 @@ class _FullscreenBulkEditLabelFormFieldState<T extends Label>
|
||||
(_initialValues.length == 1 &&
|
||||
_selection?.label == _initialValues.first);
|
||||
return FullscreenSelectionForm(
|
||||
controller: _controller,
|
||||
hintText: widget.hintText,
|
||||
leadingIcon: widget.leadingIcon,
|
||||
selectionBuilder: (context, index) =>
|
||||
@@ -101,16 +117,10 @@ class _FullscreenBulkEditLabelFormFieldState<T extends Label>
|
||||
content: Text(
|
||||
S.of(context)!.areYouSureYouWantToContinue,
|
||||
),
|
||||
actions: [
|
||||
const DialogCancelButton(),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: Text(
|
||||
S.of(context)!.confirm,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
actions: const [
|
||||
DialogCancelButton(),
|
||||
DialogConfirmButton(
|
||||
style: DialogConfirmButtonStyle.danger,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -0,0 +1,180 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/form_fields/fullscreen_selection_form.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_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/confirm_bulk_modify_tags_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class FullscreenBulkEditTagsWidget extends StatefulWidget {
|
||||
const FullscreenBulkEditTagsWidget({super.key});
|
||||
|
||||
@override
|
||||
State<FullscreenBulkEditTagsWidget> createState() =>
|
||||
_FullscreenBulkEditTagsWidgetState();
|
||||
}
|
||||
|
||||
class _FullscreenBulkEditTagsWidgetState
|
||||
extends State<FullscreenBulkEditTagsWidget> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
|
||||
/// Tags shared by all documents
|
||||
late final List<int> _sharedTags;
|
||||
|
||||
/// Tags not assigned to at least one document in the selection
|
||||
late final List<int> _nonSharedTags;
|
||||
|
||||
List<int> _addTags = [];
|
||||
List<int> _removeTags = [];
|
||||
late List<int> _filteredTags;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final state = context.read<DocumentBulkActionCubit>().state;
|
||||
_sharedTags = state.selection
|
||||
.map((e) => e.tags)
|
||||
.map((e) => e.toSet())
|
||||
.fold(
|
||||
state.tags.values.map((e) => e.id!).toSet(),
|
||||
(previousValue, element) => previousValue.intersection(element),
|
||||
)
|
||||
.toList();
|
||||
_nonSharedTags = state.selection
|
||||
.map((e) => e.tags)
|
||||
.flattened
|
||||
.toSet()
|
||||
.difference(_sharedTags.toSet())
|
||||
.toList();
|
||||
_filteredTags = state.tags.keys.toList();
|
||||
_controller.addListener(() {
|
||||
setState(() {
|
||||
_filteredTags = context
|
||||
.read<DocumentBulkActionCubit>()
|
||||
.state
|
||||
.tags
|
||||
.values
|
||||
.where((e) =>
|
||||
e.name.normalized().contains(_controller.text.normalized()))
|
||||
.map((e) => e.id!)
|
||||
.toList();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
List<int> get _assignedTags => [..._sharedTags, ..._nonSharedTags];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DocumentBulkActionCubit, DocumentBulkActionState>(
|
||||
builder: (context, state) {
|
||||
return FullscreenSelectionForm(
|
||||
controller: _controller,
|
||||
floatingActionButton: _addTags.isNotEmpty || _removeTags.isNotEmpty
|
||||
? FloatingActionButton.extended(
|
||||
label: Text(S.of(context)!.apply),
|
||||
icon: const Icon(Icons.done),
|
||||
onPressed: _submit,
|
||||
)
|
||||
: null,
|
||||
hintText: S.of(context)!.startTyping,
|
||||
leadingIcon: const Icon(Icons.label_outline),
|
||||
selectionBuilder: (context, index) {
|
||||
return _buildTagOption(
|
||||
_filteredTags[index],
|
||||
state.tags,
|
||||
);
|
||||
},
|
||||
selectionCount: _filteredTags.length,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTagOption(int id, Map<int, Tag> options) {
|
||||
Widget? icon;
|
||||
if (_sharedTags.contains(id) && !_removeTags.contains(id)) {
|
||||
// Tag is assigned to all documents and not marked for removal
|
||||
// => will remain assigned
|
||||
icon = const Icon(Icons.done);
|
||||
} else if (_addTags.contains(id)) {
|
||||
// tag is marked to be added
|
||||
icon = const Icon(Icons.done);
|
||||
} else if (_nonSharedTags.contains(id) && !_removeTags.contains(id)) {
|
||||
// Tag is neither shared among all documents, nor marked to be removed or
|
||||
// added but assigned to at least one document
|
||||
icon = const Icon(Icons.remove);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
title: Text(options[id]!.name),
|
||||
trailing: icon,
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: options[id]!.color,
|
||||
foregroundColor: options[id]!.textColor,
|
||||
child: options[id]!.isInboxTag ? const Icon(Icons.inbox) : null,
|
||||
),
|
||||
onTap: () {
|
||||
if (_addTags.contains(id)) {
|
||||
setState(() {
|
||||
_addTags.remove(id);
|
||||
});
|
||||
if (_assignedTags.contains(id)) {
|
||||
setState(() {
|
||||
_removeTags.add(id);
|
||||
});
|
||||
}
|
||||
} else if (_removeTags.contains(id)) {
|
||||
setState(() {
|
||||
_removeTags.remove(id);
|
||||
});
|
||||
if (!_sharedTags.contains(id)) {
|
||||
setState(() {
|
||||
_addTags.add(id);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (_sharedTags.contains(id)) {
|
||||
setState(() {
|
||||
_removeTags.add(id);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_addTags.add(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _submit() async {
|
||||
if (_addTags.isNotEmpty || _removeTags.isNotEmpty) {
|
||||
final bloc = context.read<DocumentBulkActionCubit>();
|
||||
final addNames = _addTags
|
||||
.map((value) => "\"${bloc.state.tags[value]!.name}\"")
|
||||
.toList();
|
||||
final removeNames = _removeTags
|
||||
.map((value) => "\"${bloc.state.tags[value]!.name}\"")
|
||||
.toList();
|
||||
final shouldPerformAction = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => ConfirmBulkModifyTagsDialog(
|
||||
selectionCount: bloc.state.selection.length,
|
||||
addTags: addNames,
|
||||
removeTags: removeNames,
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (shouldPerformAction) {
|
||||
bloc.bulkModifyTags(
|
||||
removeTagIds: _removeTags,
|
||||
addTagIds: _addTags,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user