mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 16:07:58 -06:00
fix: Add labels to each cubit using repositories and state properties, remove label cubits
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class BulkEditPage<int, 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();
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,11 @@ 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/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 {
|
||||
@@ -20,29 +19,42 @@ class BulkEditTagsBottomSheet extends StatefulWidget {
|
||||
|
||||
class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
List<int> _tagsToRemove = [];
|
||||
List<int> _tagsToAdd = [];
|
||||
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) {
|
||||
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) {
|
||||
print(state);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SingleChildScrollView(
|
||||
@@ -54,25 +66,123 @@ class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
||||
"Bulk modify tags",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
).paddedOnly(bottom: 24),
|
||||
FormBuilder(
|
||||
key: _formKey,
|
||||
child: TagFormField(
|
||||
initialValue: IdsTagsQuery(
|
||||
sharedTags.map((tag) => IncludeTagIdQuery(tag)),
|
||||
TypeAheadFormField<Tag>(
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
controller: _textEditingController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Tags",
|
||||
hintText: "Start typing to add tags...",
|
||||
),
|
||||
name: "labelFormField",
|
||||
selectableOptions: state.tagOptions,
|
||||
allowCreation: false,
|
||||
anyAssignedSelectable: false,
|
||||
excludeAllowed: false,
|
||||
),
|
||||
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("Tags removed after apply"),
|
||||
Wrap(),
|
||||
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("Tags added after apply"),
|
||||
Wrap(),
|
||||
Text("Add"),
|
||||
Wrap(
|
||||
children: _tagsToAdd
|
||||
.map(
|
||||
(tag) => RemovableTagWidget(
|
||||
tag: state.tags[tag]!,
|
||||
onDeleted: (tag) {
|
||||
setState(() {
|
||||
_tagsToAdd.remove(tag);
|
||||
});
|
||||
}),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
@@ -107,3 +217,28 @@ class _BulkEditTagsBottomSheetState extends State<BulkEditTagsBottomSheet> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,30 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/src/widgets/framework.dart';
|
||||
// import 'package:flutter/src/widgets/placeholder.dart';
|
||||
|
||||
// class LabelBulkSelectionWidget extends StatelessWidget {
|
||||
// final int labelId;
|
||||
// final String title;
|
||||
// final bool selected;
|
||||
// final bool excluded;
|
||||
// final Widget Function(int id) leadingWidgetBuilder;
|
||||
// final void Function(int id) onSelected;
|
||||
// final void Function(int id) onUnselected;
|
||||
// final void Function(int id) onRemoved;
|
||||
|
||||
// const LabelBulkSelectionWidget({
|
||||
// super.key,
|
||||
// required this.labelId,
|
||||
// required this.title,
|
||||
// required this.leadingWidgetBuilder,
|
||||
// required this.onSelected,
|
||||
// required this.onUnselected,
|
||||
// required this.onRemoved,
|
||||
// });
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return ListTile(
|
||||
// title: Text(title),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user