feat: delete kanban board groups (#3925)

* feat: hide/unhide ui

* chore: implement collapsible side bar and adjust group header (#2)

* refactor: hidden columns into own file

* chore: adjust new group button position

* fix: flowy icon buton secondary color bleed

* chore: some UI adjustments

* fix: some regressions

* chore: proper group is_visible fetching

* chore: use a bloc to manage hidden groups

* fix: hiding groups not working

* chore: implement hidden group popups

* chore: proper ungrouped item column management

* chore: remove ungrouped items button

* chore: flowy hover build

* fix: clean up code

* test: integration tests

* fix: not null promise on null value

* fix: hide and unhide multiple groups

* chore: i18n and code review

* chore: missed review

* fix: rust-lib-test

* fix: dont completely remove flowyiconhovercolor

* chore: apply suggest

* fix: number of rows inside hidden groups not updating properly

* fix: hidden groups disappearing after collapse

* fix: hidden group title alignment

* fix: insert newly unhidden groups into the correct position

* chore: adjust padding all around

* feat: reorder hidden groups

* chore: adjust padding

* chore: collapse hidden groups section persist

* chore: no status group at beginning

* fix: hiding groups when grouping with other types

* chore: disable rename groups that arent supported

* chore: update appflowy board ref

* chore: better naming

* feat: delete kanban groups

* chore: forgot to save

* chore: fix build and small ui adjustments

* chore: add a confirm dialog when deleting a column

* fix: flutter lint

* test: add integration test

* chore: fix some design review issues

* chore: apply suggestions from Nathan

* fix: write lock on group controller

---------

Co-authored-by: Mathias Mogensen <mathias@appflowy.io>
This commit is contained in:
Richard Shiue
2023-11-28 10:43:22 +08:00
committed by GitHub
parent 3595de5e12
commit 9d61ca0278
28 changed files with 476 additions and 187 deletions

View File

@ -48,4 +48,14 @@ class GroupBackendService {
return DatabaseEventCreateGroup(payload).send();
}
Future<Either<Unit, FlowyError>> deleteGroup({
required String groupId,
}) {
final payload = DeleteGroupPayloadPB.create()
..viewId = viewId
..groupId = groupId;
return DatabaseEventDeleteGroup(payload).send();
}
}

View File

@ -99,6 +99,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
final result = await groupBackendSvc.createGroup(name: name);
result.fold((_) {}, (err) => Log.error(err));
},
deleteGroup: (groupId) async {
final result = await groupBackendSvc.deleteGroup(groupId: groupId);
result.fold((_) {}, (err) => Log.error(err));
},
didCreateRow: (group, row, int? index) {
emit(
state.copyWith(
@ -496,6 +500,7 @@ class BoardEvent with _$BoardEvent {
) = _ToggleGroupVisibility;
const factory BoardEvent.toggleHiddenSectionVisibility(bool isVisible) =
_ToggleHiddenSectionVisibility;
const factory BoardEvent.deleteGroup(String groupId) = _DeleteGroup;
const factory BoardEvent.reorderGroup(String fromGroupId, String toGroupId) =
_ReorderGroup;
const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError;

View File

@ -3,6 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_board/appflowy_board.dart';
@ -73,7 +74,6 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
Widget title = Expanded(
child: FlowyText.medium(
widget.groupData.headerData.groupName,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
);
@ -92,7 +92,6 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
.add(BoardEvent.startEditingHeader(widget.groupData.id)),
child: FlowyText.medium(
widget.groupData.headerData.groupName,
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
),
@ -119,6 +118,7 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
const HSpace(4),
FlowyTooltip(
message: LocaleKeys.board_column_addToColumnTopTooltip.tr(),
preferBelow: false,
child: FlowyIconButton(
width: 20,
icon: const FlowySvg(FlowySvgs.add_s),
@ -199,7 +199,7 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
Widget _groupOptionsButton(BuildContext context) {
return AppFlowyPopover(
clickHandler: PopoverClickHandler.gestureDetector,
margin: const EdgeInsets.fromLTRB(8, 8, 8, 4),
margin: const EdgeInsets.all(8),
constraints: BoxConstraints.loose(const Size(168, 300)),
direction: PopoverDirection.bottomWithLeftAligned,
child: FlowyIconButton(
@ -209,29 +209,31 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
),
popupBuilder: (popoverContext) {
final customGroupData = widget.groupData.customData as GroupData;
final isDefault = customGroupData.group.isDefault;
final menuItems = GroupOptions.values.toList();
if (!customGroupData.fieldType.canEditHeader) {
if (!customGroupData.fieldType.canEditHeader || isDefault) {
menuItems.remove(GroupOptions.rename);
}
return Column(
if (!customGroupData.fieldType.canDeleteGroup || isDefault) {
menuItems.remove(GroupOptions.delete);
}
return SeparatedColumn(
mainAxisSize: MainAxisSize.min,
separatorBuilder: () => const VSpace(4),
children: [
...menuItems.map(
(action) => SizedBox(
height: GridSize.popoverItemHeight,
child: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: FlowyButton(
leftIcon: FlowySvg(action.icon),
text: FlowyText.medium(
action.text,
overflow: TextOverflow.ellipsis,
),
onTap: () {
action.call(context, customGroupData.group);
PopoverContainer.of(popoverContext).close();
},
child: FlowyButton(
leftIcon: FlowySvg(action.icon),
text: FlowyText.medium(
action.text,
overflow: TextOverflow.ellipsis,
),
onTap: () {
action.call(context, customGroupData.group);
PopoverContainer.of(popoverContext).close();
},
),
),
),
@ -244,7 +246,8 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
enum GroupOptions {
rename,
hide;
hide,
delete;
void call(BuildContext context, GroupPB group) {
switch (this) {
@ -258,16 +261,28 @@ enum GroupOptions {
.read<BoardBloc>()
.add(BoardEvent.toggleGroupVisibility(group, false));
break;
case delete:
NavigatorAlertDialog(
title: LocaleKeys.board_column_deleteColumnConfirmation.tr(),
confirm: () {
context
.read<BoardBloc>()
.add(BoardEvent.deleteGroup(group.groupId));
},
).show(context);
break;
}
}
FlowySvgData get icon => switch (this) {
rename => FlowySvgs.edit_s,
hide => FlowySvgs.hide_s,
delete => FlowySvgs.delete_s,
};
String get text => switch (this) {
rename => LocaleKeys.board_column_renameColumn.tr(),
hide => LocaleKeys.board_column_hideColumn.tr(),
delete => LocaleKeys.board_column_deleteColumn.tr(),
};
}

View File

@ -71,7 +71,6 @@ class HiddenGroupsColumn extends StatelessWidget {
LocaleKeys
.board_hiddenGroupSection_sectionTitle
.tr(),
fontSize: 14,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).hintColor,
),

View File

@ -65,4 +65,13 @@ extension FieldTypeListExtension on FieldType {
FieldType.SingleSelect => true,
_ => false,
};
bool get canDeleteGroup => switch (this) {
FieldType.URL ||
FieldType.SingleSelect ||
FieldType.MultiSelect ||
FieldType.DateTime =>
true,
_ => false,
};
}

View File

@ -139,9 +139,13 @@ class _TextCellState extends State<TextCardCell> {
return Padding(
padding: CardSizes.cardCellPadding,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.showNotes) ...[
const FlowySvg(FlowySvgs.notes_s),
FlowySvg(
FlowySvgs.notes_s,
color: Theme.of(context).hintColor,
),
const HSpace(4),
],
Expanded(child: child),