mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
};
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ class HiddenGroupsColumn extends StatelessWidget {
|
||||
LocaleKeys
|
||||
.board_hiddenGroupSection_sectionTitle
|
||||
.tr(),
|
||||
fontSize: 14,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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),
|
||||
|
Reference in New Issue
Block a user