refactor: hide ungrouped feature (#3817)

* refactor: remove unused notification and listener

* revert: remove hide_ungrouped from group settings

* chore: add board layout setting

* chore: listen to layout settings on ui

* fix: duplicated group controller initialization

* chore: add a tooltip to the ungrouped items button

* chore: trailing comma
This commit is contained in:
Richard Shiue
2023-10-29 11:26:49 +08:00
committed by GitHub
parent 993532a2f0
commit b9a25f449f
36 changed files with 419 additions and 486 deletions

View File

@ -1,7 +1,7 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
@ -47,12 +47,10 @@ class GroupCallbacks {
}
class DatabaseLayoutSettingCallbacks {
final void Function(DatabaseLayoutSettingPB) onLayoutChanged;
final void Function(DatabaseLayoutSettingPB) onLoadLayout;
final void Function(DatabaseLayoutSettingPB) onLayoutSettingsChanged;
DatabaseLayoutSettingCallbacks({
required this.onLayoutChanged,
required this.onLoadLayout,
required this.onLayoutSettingsChanged,
});
}
@ -125,11 +123,11 @@ class DatabaseController {
void addListener({
DatabaseCallbacks? onDatabaseChanged,
DatabaseLayoutSettingCallbacks? onLayoutChanged,
DatabaseLayoutSettingCallbacks? onLayoutSettingsChanged,
GroupCallbacks? onGroupChanged,
}) {
if (onLayoutChanged != null) {
_layoutCallbacks.add(onLayoutChanged);
if (onLayoutSettingsChanged != null) {
_layoutCallbacks.add(onLayoutSettingsChanged);
}
if (onDatabaseChanged != null) {
@ -228,12 +226,14 @@ class DatabaseController {
);
}
Future<void> updateLayoutSetting(
CalendarLayoutSettingPB calendarlLayoutSetting,
) async {
Future<void> updateLayoutSetting({
BoardLayoutSettingPB? boardLayoutSetting,
CalendarLayoutSettingPB? calendarLayoutSetting,
}) async {
await _databaseViewBackendSvc
.updateLayoutSetting(
calendarLayoutSetting: calendarlLayoutSetting,
boardLayoutSetting: boardLayoutSetting,
calendarLayoutSetting: calendarLayoutSetting,
layoutType: databaseLayout,
)
.then((result) {
@ -241,15 +241,6 @@ class DatabaseController {
});
}
void updateGroupConfiguration(bool hideUngrouped) async {
final payload = GroupSettingChangesetPB(
viewId: viewId,
groupConfigurationId: "",
hideUngrouped: hideUngrouped,
);
DatabaseEventUpdateGroupConfiguration(payload).send();
}
Future<void> dispose() async {
await _databaseViewBackendSvc.closeView();
await fieldController.dispose();
@ -261,9 +252,6 @@ class DatabaseController {
}
Future<void> _loadGroups() async {
final configResult = await loadGroupConfigurations(viewId: viewId);
_handleGroupConfigurationChanged(configResult);
final groupsResult = await _databaseViewBackendSvc.loadGroups();
groupsResult.fold(
(groups) {
@ -280,10 +268,9 @@ class DatabaseController {
result.fold(
(newDatabaseLayoutSetting) {
databaseLayoutSetting = newDatabaseLayoutSetting;
databaseLayoutSetting?.freeze();
for (final callback in _layoutCallbacks) {
callback.onLoadLayout(newDatabaseLayoutSetting);
callback.onLayoutSettingsChanged(newDatabaseLayoutSetting);
}
},
(r) => Log.error(r),
@ -339,7 +326,6 @@ class DatabaseController {
void _listenOnGroupChanged() {
_groupListener.start(
onGroupConfigurationChanged: _handleGroupConfigurationChanged,
onNumOfGroupsChanged: (result) {
result.fold(
(changeset) {
@ -386,7 +372,7 @@ class DatabaseController {
databaseLayoutSetting?.freeze();
for (final callback in _layoutCallbacks) {
callback.onLayoutChanged(newLayout);
callback.onLayoutSettingsChanged(newLayout);
}
},
(r) => Log.error(r),
@ -394,29 +380,6 @@ class DatabaseController {
},
);
}
Future<Either<List<GroupSettingPB>, FlowyError>> loadGroupConfigurations({
required String viewId,
}) {
final payload = DatabaseViewIdPB(value: viewId);
return DatabaseEventGetGroupConfigurations(payload).send().then((result) {
return result.fold((l) => left(l.items), (r) => right(r));
});
}
void _handleGroupConfigurationChanged(
Either<List<GroupSettingPB>, FlowyError> result,
) {
result.fold(
(configurations) {
for (final callback in _groupCallbacks) {
callback.onGroupConfigurationChanged?.call(configurations);
}
},
(r) => Log.error(r),
);
}
}
class RowDataBuilder {

View File

@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
@ -114,11 +115,17 @@ class DatabaseViewBackendService {
Future<Either<Unit, FlowyError>> updateLayoutSetting({
required DatabaseLayoutPB layoutType,
BoardLayoutSettingPB? boardLayoutSetting,
CalendarLayoutSettingPB? calendarLayoutSetting,
}) {
final payload = LayoutSettingChangesetPB.create()
..viewId = viewId
..layoutType = layoutType;
if (boardLayoutSetting != null) {
payload.board = boardLayoutSetting;
}
if (calendarLayoutSetting != null) {
payload.calendar = calendarLayoutSetting;
}

View File

@ -15,8 +15,6 @@ typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
class DatabaseGroupListener {
final String viewId;
PublishNotifier<GroupConfigurationUpdateValue>? _groupConfigurationNotifier =
PublishNotifier();
PublishNotifier<GroupUpdateValue>? _numOfGroupsNotifier = PublishNotifier();
PublishNotifier<GroupByNewFieldValue>? _groupByFieldNotifier =
PublishNotifier();
@ -24,13 +22,9 @@ class DatabaseGroupListener {
DatabaseGroupListener(this.viewId);
void start({
required void Function(GroupConfigurationUpdateValue)
onGroupConfigurationChanged,
required void Function(GroupUpdateValue) onNumOfGroupsChanged,
required void Function(GroupByNewFieldValue) onGroupByNewField,
}) {
_groupConfigurationNotifier
?.addPublishListener(onGroupConfigurationChanged);
_numOfGroupsNotifier?.addPublishListener(onNumOfGroupsChanged);
_groupByFieldNotifier?.addPublishListener(onGroupByNewField);
_listener = DatabaseNotificationListener(
@ -44,13 +38,6 @@ class DatabaseGroupListener {
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidUpdateGroupConfiguration:
result.fold(
(payload) => _groupConfigurationNotifier?.value =
left(RepeatedGroupSettingPB.fromBuffer(payload).items),
(error) => _groupConfigurationNotifier?.value = right(error),
);
break;
case DatabaseNotification.DidUpdateNumOfGroups:
result.fold(
(payload) => _numOfGroupsNotifier?.value =
@ -72,9 +59,6 @@ class DatabaseGroupListener {
Future<void> stop() async {
await _listener?.stop();
_groupConfigurationNotifier?.dispose();
_groupConfigurationNotifier = null;
_numOfGroupsNotifier?.dispose();
_numOfGroupsNotifier = null;

View File

@ -1,50 +0,0 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:dartz/dartz.dart';
typedef NewLayoutFieldValue = Either<DatabaseLayoutSettingPB, FlowyError>;
class DatabaseCalendarLayoutListener {
final String viewId;
PublishNotifier<NewLayoutFieldValue>? _newLayoutFieldNotifier =
PublishNotifier();
DatabaseNotificationListener? _listener;
DatabaseCalendarLayoutListener(this.viewId);
void start({
required void Function(NewLayoutFieldValue) onCalendarLayoutChanged,
}) {
_newLayoutFieldNotifier?.addPublishListener(onCalendarLayoutChanged);
_listener = DatabaseNotificationListener(
objectId: viewId,
handler: _handler,
);
}
void _handler(
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidSetNewLayoutField:
result.fold(
(payload) => _newLayoutFieldNotifier?.value =
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
(error) => _newLayoutFieldNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_newLayoutFieldNotifier?.dispose();
_newLayoutFieldNotifier = null;
}
}

View File

@ -1,6 +1,7 @@
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -14,7 +15,7 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
final DatabaseController _databaseController;
final GroupBackendService _groupBackendSvc;
Function(List<FieldInfo>)? _onFieldsFn;
GroupCallbacks? _groupCallbacks;
DatabaseLayoutSettingCallbacks? _layoutSettingCallbacks;
DatabaseGroupBloc({
required String viewId,
@ -25,13 +26,13 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
DatabaseGroupState.initial(
viewId,
databaseController.fieldController.fieldInfos,
databaseController.databaseLayoutSetting!.board,
),
) {
on<DatabaseGroupEvent>(
(event, emit) async {
event.when(
initial: () {
_loadGroupConfigurations();
_startListening();
},
didReceiveFieldUpdate: (fieldInfos) {
@ -43,8 +44,8 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
);
result.fold((l) => null, (err) => Log.error(err));
},
didUpdateHideUngrouped: (bool hideUngrouped) {
emit(state.copyWith(hideUngrouped: hideUngrouped));
didUpdateLayoutSettings: (layoutSettings) {
emit(state.copyWith(layoutSettings: layoutSettings));
},
);
},
@ -58,7 +59,7 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
.removeListener(onFieldsListener: _onFieldsFn!);
_onFieldsFn = null;
}
_groupCallbacks = null;
_layoutSettingCallbacks = null;
return super.close();
}
@ -70,32 +71,18 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
listenWhen: () => !isClosed,
);
_groupCallbacks = GroupCallbacks(
onGroupConfigurationChanged: (configurations) {
if (isClosed) {
_layoutSettingCallbacks = DatabaseLayoutSettingCallbacks(
onLayoutSettingsChanged: (layoutSettings) {
if (isClosed || !layoutSettings.hasBoard()) {
return;
}
final configuration = configurations.first;
add(
DatabaseGroupEvent.didUpdateHideUngrouped(
configuration.hideUngrouped,
),
DatabaseGroupEvent.didUpdateLayoutSettings(layoutSettings.board),
);
},
);
_databaseController.addListener(onGroupChanged: _groupCallbacks);
}
void _loadGroupConfigurations() async {
final configResult = await _databaseController.loadGroupConfigurations(
viewId: _databaseController.viewId,
);
configResult.fold(
(configurations) {
final hideUngrouped = configurations.first.hideUngrouped;
add(DatabaseGroupEvent.didUpdateHideUngrouped(hideUngrouped));
},
(err) => Log.error(err),
_databaseController.addListener(
onLayoutSettingsChanged: _layoutSettingCallbacks,
);
}
}
@ -110,8 +97,9 @@ class DatabaseGroupEvent with _$DatabaseGroupEvent {
const factory DatabaseGroupEvent.didReceiveFieldUpdate(
List<FieldInfo> fields,
) = _DidReceiveFieldUpdate;
const factory DatabaseGroupEvent.didUpdateHideUngrouped(bool hideUngrouped) =
_DidUpdateHideUngrouped;
const factory DatabaseGroupEvent.didUpdateLayoutSettings(
BoardLayoutSettingPB layoutSettings,
) = _DidUpdateLayoutSettings;
}
@freezed
@ -119,16 +107,17 @@ class DatabaseGroupState with _$DatabaseGroupState {
const factory DatabaseGroupState({
required String viewId,
required List<FieldInfo> fieldInfos,
required bool hideUngrouped,
required BoardLayoutSettingPB layoutSettings,
}) = _DatabaseGroupState;
factory DatabaseGroupState.initial(
String viewId,
List<FieldInfo> fieldInfos,
BoardLayoutSettingPB layoutSettings,
) =>
DatabaseGroupState(
viewId: viewId,
fieldInfos: fieldInfos,
hideUngrouped: true,
layoutSettings: layoutSettings,
);
}

View File

@ -147,8 +147,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
),
);
},
didUpdateHideUngrouped: (bool hideUngrouped) {
emit(state.copyWith(hideUngrouped: hideUngrouped));
didUpdateLayoutSettings: (layoutSettings) {
emit(state.copyWith(layoutSettings: layoutSettings));
},
startEditingHeader: (String groupId) {
emit(
@ -200,7 +200,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
if (ungroupedGroupIndex != -1) {
ungroupedGroup = groups[ungroupedGroupIndex];
final group = groups.removeAt(ungroupedGroupIndex);
if (!state.hideUngrouped) {
if (!(state.layoutSettings?.hideUngroupedColumn ?? false)) {
groups.add(group);
}
}
@ -230,20 +230,21 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
}
},
);
final onGroupChanged = GroupCallbacks(
onGroupConfigurationChanged: (configurations) {
if (isClosed) return;
final config = configurations.first;
if (config.hideUngrouped) {
boardController.removeGroup(config.fieldId);
final onLayoutSettingsChanged = DatabaseLayoutSettingCallbacks(
onLayoutSettingsChanged: (layoutSettings) {
if (isClosed || !layoutSettings.hasBoard()) {
return;
}
if (layoutSettings.board.hideUngroupedColumn) {
boardController.removeGroup(ungroupedGroup!.fieldId);
} else if (ungroupedGroup != null) {
final newGroup = initializeGroupData(ungroupedGroup!);
final controller = initializeGroupController(ungroupedGroup!);
groupControllers[controller.group.groupId] = (controller);
boardController.addGroup(newGroup);
}
add(BoardEvent.didUpdateHideUngrouped(config.hideUngrouped));
add(BoardEvent.didUpdateLayoutSettings(layoutSettings.board));
},
);
final onGroupChanged = GroupCallbacks(
onGroupByField: (groups) {
if (isClosed) return;
ungroupedGroup = null;
@ -274,6 +275,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
databaseController.addListener(
onDatabaseChanged: onDatabaseChanged,
onLayoutSettingsChanged: onLayoutSettingsChanged,
onGroupChanged: onGroupChanged,
);
}
@ -360,8 +362,9 @@ class BoardEvent with _$BoardEvent {
) = _DidReceiveGridUpdate;
const factory BoardEvent.didReceiveGroups(List<GroupPB> groups) =
_DidReceiveGroups;
const factory BoardEvent.didUpdateHideUngrouped(bool hideUngrouped) =
_DidUpdateHideUngrouped;
const factory BoardEvent.didUpdateLayoutSettings(
BoardLayoutSettingPB layoutSettings,
) = _DidUpdateLayoutSettings;
}
@freezed
@ -376,7 +379,7 @@ class BoardState with _$BoardState {
BoardEditingRow? editingRow,
required LoadingState loadingState,
required Option<FlowyError> noneOrError,
required bool hideUngrouped,
required BoardLayoutSettingPB? layoutSettings,
}) = _BoardState;
factory BoardState.initial(String viewId) => BoardState(
@ -387,7 +390,7 @@ class BoardState with _$BoardState {
isEditingRow: false,
noneOrError: none(),
loadingState: const LoadingState.loading(),
hideUngrouped: false,
layoutSettings: null,
);
}

View File

@ -167,7 +167,8 @@ class _BoardContentState extends State<BoardContent> {
mainAxisSize: MainAxisSize.min,
children: [
const VSpace(8.0),
if (state.hideUngrouped) _buildBoardHeader(context),
if (state.layoutSettings?.hideUngroupedColumn ?? false)
_buildBoardHeader(context),
Expanded(
child: AppFlowyBoard(
boardScrollController: scrollManager,

View File

@ -17,6 +17,7 @@ import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -61,31 +62,36 @@ class _UnscheduledEventsButtonState extends State<UngroupedItemsButton> {
offset: const Offset(0, 8),
constraints:
const BoxConstraints(maxWidth: 282, maxHeight: 600),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
child: FlowyTooltip(
message: LocaleKeys.board_ungroupedButtonTooltip.tr(),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).dividerColor,
width: 1,
),
borderRadius: Corners.s6Border,
),
side: BorderSide(
color: Theme.of(context).dividerColor,
width: 1,
),
borderRadius: Corners.s6Border,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
visualDensity: VisualDensity.compact,
),
side: BorderSide(
color: Theme.of(context).dividerColor,
width: 1,
onPressed: () {
if (state.ungroupedItems.isNotEmpty) {
_popoverController.show();
}
},
child: FlowyText.regular(
"${LocaleKeys.board_ungroupedButtonText.tr()} (${state.ungroupedItems.length})",
fontSize: 10,
),
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
visualDensity: VisualDensity.compact,
),
onPressed: () {
if (state.ungroupedItems.isNotEmpty) {
_popoverController.show();
}
},
child: FlowyText.regular(
"${LocaleKeys.board_ungroupedButtonText.tr()} (${state.ungroupedItems.length})",
fontSize: 10,
),
),
popupBuilder: (context) {

View File

@ -198,7 +198,9 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
Future<void> _updateCalendarLayoutSetting(
CalendarLayoutSettingPB layoutSetting,
) async {
return databaseController.updateLayoutSetting(layoutSetting);
return databaseController.updateLayoutSetting(
calendarLayoutSetting: layoutSetting,
);
}
Future<CalendarEventData<CalendarDayEvent>?> _loadEvent(RowId rowId) async {
@ -319,14 +321,13 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
},
);
final onLayoutChanged = DatabaseLayoutSettingCallbacks(
onLayoutChanged: _didReceiveLayoutSetting,
onLoadLayout: _didReceiveLayoutSetting,
final onLayoutSettingsChanged = DatabaseLayoutSettingCallbacks(
onLayoutSettingsChanged: _didReceiveLayoutSetting,
);
databaseController.addListener(
onDatabaseChanged: onDatabaseChanged,
onLayoutChanged: onLayoutChanged,
onLayoutSettingsChanged: onLayoutSettingsChanged,
);
}

View File

@ -8,6 +8,7 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/common/
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:easy_localization/easy_localization.dart';
@ -18,6 +19,7 @@ import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:protobuf/protobuf.dart' hide FieldInfo;
class DatabaseGroupList extends StatelessWidget {
final String viewId;
@ -60,9 +62,9 @@ class DatabaseGroupList extends StatelessWidget {
),
),
Toggle(
value: !state.hideUngrouped,
value: !state.layoutSettings.hideUngroupedColumn,
onChanged: (value) =>
databaseController.updateGroupConfiguration(value),
_updateLayoutSettings(state.layoutSettings, value),
style: ToggleStyle.big,
padding: EdgeInsets.zero,
),
@ -105,6 +107,19 @@ class DatabaseGroupList extends StatelessWidget {
),
);
}
Future<void> _updateLayoutSettings(
BoardLayoutSettingPB layoutSettings,
bool hideUngrouped,
) {
layoutSettings.freeze();
final newLayoutSetting = layoutSettings.rebuild((message) {
message.hideUngroupedColumn = hideUngrouped;
});
return databaseController.updateLayoutSetting(
boardLayoutSetting: newLayoutSetting,
);
}
}
class _GridGroupCell extends StatelessWidget {

View File

@ -115,7 +115,9 @@ class ICalendarSettingImpl extends ICalendarSetting {
@override
void updateLayoutSettings(CalendarLayoutSettingPB layoutSettings) {
_databaseController.updateLayoutSetting(layoutSettings);
_databaseController.updateLayoutSetting(
calendarLayoutSetting: layoutSettings,
);
}
@override