فهرست منبع

chore: fix bugs when switch group field

appflowy 2 سال پیش
والد
کامیت
4f8e012d54
51فایلهای تغییر یافته به همراه297 افزوده شده و 162 حذف شده
  1. 7 1
      frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
  2. 1 1
      frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart
  3. 1 1
      frontend/app_flowy/lib/plugins/board/application/card/board_date_cell_bloc.dart
  4. 1 1
      frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart
  5. 3 3
      frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
  6. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
  7. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
  8. 1 1
      frontend/app_flowy/lib/plugins/grid/application/block/block_cache.dart
  9. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart
  10. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/date_cell_bloc.dart
  11. 38 18
      frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart
  12. 0 1
      frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart
  13. 0 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart
  14. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart
  15. 1 1
      frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart
  16. 1 1
      frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart
  17. 1 1
      frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart
  18. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart
  19. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart
  20. 1 1
      frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart
  21. 20 32
      frontend/app_flowy/lib/plugins/grid/application/setting/group_bloc.dart
  22. 1 1
      frontend/app_flowy/lib/plugins/grid/application/setting/property_bloc.dart
  23. 16 0
      frontend/app_flowy/lib/plugins/grid/application/setting/setting_service.dart
  24. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart
  25. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/layout/layout.dart
  26. 0 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart
  27. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
  28. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
  29. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
  30. 47 20
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
  31. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
  32. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_setting.dart
  33. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
  34. 1 1
      frontend/app_flowy/lib/startup/deps_resolver.dart
  35. 1 0
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart
  36. 6 6
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs
  37. 5 5
      frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs
  38. 7 8
      frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs
  39. 26 0
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  40. 6 3
      frontend/rust-lib/flowy-grid/src/event_map.rs
  41. 11 3
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  42. 43 11
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  43. 16 6
      frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
  44. 3 1
      frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
  45. 1 1
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  46. 1 1
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs
  47. 2 2
      frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs
  48. 4 4
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  49. 5 5
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs
  50. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs
  51. 2 2
      shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs

+ 7 - 1
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -1,6 +1,6 @@
 import 'dart:async';
 import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
 import 'package:appflowy_board/appflowy_board.dart';
@@ -204,16 +204,20 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
         }
       },
       didLoadGroups: (groups) {
+        if (isClosed) return;
         initializeGroups(groups);
         add(BoardEvent.didReceiveGroups(groups));
       },
       onDeletedGroup: (groupIds) {
+        if (isClosed) return;
         //
       },
       onInsertedGroup: (insertedGroups) {
+        if (isClosed) return;
         //
       },
       onUpdatedGroup: (updatedGroups) {
+        if (isClosed) return;
         for (final group in updatedGroups) {
           final columnController =
               boardController.getColumnController(group.groupId);
@@ -224,6 +228,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
         Log.error(err);
       },
       onResetGroups: (groups) {
+        if (isClosed) return;
+
         initializeGroups(groups);
         add(BoardEvent.didReceiveGroups(groups));
       },

+ 1 - 1
frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart

@@ -1,7 +1,7 @@
 import 'dart:collection';
 
 import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/grid_service.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/board/application/card/board_date_cell_bloc.dart

@@ -1,5 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/board/application/card/card_data_controller.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/plugins/board/presentation/card/card_cell_builder.dart';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 import 'package:flutter/foundation.dart';

+ 3 - 3
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -5,7 +5,7 @@ import 'dart:collection';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_detail.dart';
@@ -82,8 +82,7 @@ class _BoardContentState extends State<BoardContent> {
     return BlocListener<BoardBloc, BoardState>(
       listener: (context, state) => _handleEditState(state, context),
       child: BlocBuilder<BoardBloc, BoardState>(
-        buildWhen: (previous, current) =>
-            previous.groupIds.length != current.groupIds.length,
+        buildWhen: (previous, current) => previous.groupIds != current.groupIds,
         builder: (context, state) {
           final theme = context.read<AppTheme>();
           return Container(
@@ -95,6 +94,7 @@ class _BoardContentState extends State<BoardContent> {
                   const _ToolbarBlocAdaptor(),
                   Expanded(
                     child: AFBoard(
+                      key: UniqueKey(),
                       scrollManager: scrollManager,
                       scrollController: scrollController,
                       dataController: context.read<BoardBloc>().boardController,

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/plugins/board/application/toolbar/board_setting_bloc.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_group.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_property.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/block/block_cache.dart

@@ -2,7 +2,7 @@ import 'dart:async';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
 
-import '../field/field_cache.dart';
+import '../field/field_controller.dart';
 import '../row/row_cache.dart';
 import 'block_listener.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart

@@ -16,7 +16,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_listener.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'dart:convert' show utf8;
 
-import '../../field/field_cache.dart';
+import '../../field/field_controller.dart';
 import '../../field/type_option/type_option_context.dart';
 import 'cell_field_notifier.dart';
 part 'cell_service.freezed.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/date_cell_bloc.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 38 - 18
frontend/app_flowy/lib/plugins/grid/application/field/field_cache.dart → frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart

@@ -2,10 +2,12 @@ import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
 import 'package:app_flowy/plugins/grid/application/grid_service.dart';
 import 'package:app_flowy/plugins/grid/application/setting/setting_listener.dart';
+import 'package:app_flowy/plugins/grid/application/setting/setting_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import '../row/row_cache.dart';
 
@@ -35,11 +37,17 @@ class GridFieldController {
   final Map<OnChangeset, OnChangeset> _changesetCallbackMap = {};
 
   _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
+  List<String> _groupFieldIds = [];
   final GridFFIService _gridFFIService;
+  final SettingFFIService _settingFFIService;
+
+  List<GridFieldContext> get fieldContexts =>
+      [..._fieldNotifier?.fieldContexts ?? []];
 
   GridFieldController({required this.gridId})
       : _fieldListener = GridFieldsListener(gridId: gridId),
         _gridFFIService = GridFFIService(gridId: gridId),
+        _settingFFIService = SettingFFIService(viewId: gridId),
         _settingListener = SettingListener(gridId: gridId) {
     //Listen on field's changes
     _fieldListener.start(onFieldsChanged: (result) {
@@ -59,24 +67,38 @@ class GridFieldController {
     //Listen on setting changes
     _settingListener.start(onSettingUpdated: (result) {
       result.fold(
-        (setting) {
-          final List<String> groupFieldIds = setting.groupConfigurations.items
-              .map((item) => item.groupFieldId)
-              .toList();
-          bool isChanged = false;
-          if (_fieldNotifier != null) {
-            for (var field in _fieldNotifier!.fieldContexts) {
-              if (groupFieldIds.contains(field.id)) {
-                field._isGroupField = true;
-                isChanged = true;
-              }
-            }
-            if (isChanged) _fieldNotifier?.notify();
-          }
-        },
+        (setting) => _updateFieldsWhenSettingChanged(setting),
         (r) => Log.error(r),
       );
     });
+
+    _settingFFIService.getSetting().then((result) {
+      result.fold(
+        (setting) => _updateFieldsWhenSettingChanged(setting),
+        (err) => Log.error(err),
+      );
+    });
+  }
+
+  void _updateFieldsWhenSettingChanged(GridSettingPB setting) {
+    _groupFieldIds = setting.groupConfigurations.items
+        .map((item) => item.groupFieldId)
+        .toList();
+
+    _updateFieldContexts();
+  }
+
+  void _updateFieldContexts() {
+    if (_fieldNotifier != null) {
+      for (var field in _fieldNotifier!.fieldContexts) {
+        if (_groupFieldIds.contains(field.id)) {
+          field._isGroupField = true;
+        } else {
+          field._isGroupField = false;
+        }
+      }
+      _fieldNotifier?.notify();
+    }
   }
 
   Future<void> dispose() async {
@@ -85,9 +107,6 @@ class GridFieldController {
     _fieldNotifier = null;
   }
 
-  List<GridFieldContext> get fieldContexts =>
-      [..._fieldNotifier?.fieldContexts ?? []];
-
   Future<Either<Unit, FlowyError>> loadFields(
       {required List<FieldIdPB> fieldIds}) async {
     final result = await _gridFFIService.getFields(fieldIds: fieldIds);
@@ -97,6 +116,7 @@ class GridFieldController {
           _fieldNotifier?.fieldContexts = newFields.items
               .map((field) => GridFieldContext(field: field))
               .toList();
+          _updateFieldContexts();
           return left(unit);
         },
         (err) => right(err),

+ 0 - 1
frontend/app_flowy/lib/plugins/grid/application/field/field_service.dart

@@ -6,7 +6,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 
-import 'field_cache.dart';
 part 'field_service.freezed.dart';
 
 /// FieldService consists of lots of event functions. We define the events in the backend(Rust),

+ 0 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart

@@ -1,4 +1,3 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_data_controller.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_infra/notifier.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/grid_bloc.dart

@@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'block/block_cache.dart';
-import 'field/field_cache.dart';
+import 'field/field_controller.dart';
 import 'grid_data_controller.dart';
 import 'row/row_cache.dart';
 import 'dart:collection';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/grid_data_controller.dart

@@ -8,7 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
 import 'dart:async';
 import 'package:dartz/dartz.dart';
 import 'block/block_cache.dart';
-import 'field/field_cache.dart';
+import 'field/field_controller.dart';
 import 'prelude.dart';
 import 'row/row_cache.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/grid_header_bloc.dart

@@ -4,7 +4,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import 'field/field_cache.dart';
+import 'field/field_controller.dart';
 
 part 'grid_header_bloc.freezed.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_bloc.dart

@@ -1,6 +1,6 @@
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:equatable/equatable.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart

@@ -1,6 +1,6 @@
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/row/row_data_controller.dart

@@ -2,7 +2,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_
 import 'package:flutter/material.dart';
 import '../../presentation/widgets/cell/cell_builder.dart';
 import '../cell/cell_service/cell_service.dart';
-import '../field/field_cache.dart';
+import '../field/field_controller.dart';
 import 'row_cache.dart';
 
 typedef OnRowChanged = void Function(GridCellMap, RowsChangedReason);

+ 20 - 32
frontend/app_flowy/lib/plugins/grid/application/setting/group_bloc.dart

@@ -1,45 +1,37 @@
-import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
-import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 
-import '../field/field_cache.dart';
-import 'setting_controller.dart';
+import '../field/field_controller.dart';
+import 'setting_service.dart';
 
 part 'group_bloc.freezed.dart';
 
 class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
   final GridFieldController _fieldController;
-  final SettingController _settingController;
+  final SettingFFIService _settingFFIService;
   Function(List<GridFieldContext>)? _onFieldsFn;
 
   GridGroupBloc(
       {required String viewId, required GridFieldController fieldController})
       : _fieldController = fieldController,
-        _settingController = SettingController(viewId: viewId),
+        _settingFFIService = SettingFFIService(viewId: viewId),
         super(GridGroupState.initial(viewId, fieldController.fieldContexts)) {
     on<GridGroupEvent>(
       (event, emit) async {
-        await event.map(
-          initial: (_Initial value) {
+        event.when(
+          initial: () {
             _startListening();
           },
-          setFieldVisibility: (_SetFieldVisibility value) async {
-            final fieldService =
-                FieldService(gridId: viewId, fieldId: value.fieldId);
-            final result =
-                await fieldService.updateField(visibility: value.visibility);
-            result.fold(
-              (l) => null,
-              (err) => Log.error(err),
-            );
-          },
-          didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
-            emit(state.copyWith(fieldContexts: value.fields));
+          didReceiveFieldUpdate: (fieldContexts) {
+            emit(state.copyWith(fieldContexts: fieldContexts));
           },
-          moveField: (_MoveField value) {
-            //
+          setGroupByField: (String fieldId, FieldType fieldType) {
+            _settingFFIService.groupByField(
+              fieldId: fieldId,
+              fieldType: fieldType,
+            );
           },
         );
       },
@@ -56,28 +48,24 @@ class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
   }
 
   void _startListening() {
-    _onFieldsFn = (fields) => add(GridGroupEvent.didReceiveFieldUpdate(fields));
+    _onFieldsFn = (fieldContexts) =>
+        add(GridGroupEvent.didReceiveFieldUpdate(fieldContexts));
     _fieldController.addListener(
       onFields: _onFieldsFn,
       listenWhen: () => !isClosed,
     );
-
-    _settingController.startListeing(
-      onSettingUpdated: (setting) {},
-      onError: (err) {},
-    );
   }
 }
 
 @freezed
 class GridGroupEvent with _$GridGroupEvent {
   const factory GridGroupEvent.initial() = _Initial;
-  const factory GridGroupEvent.setFieldVisibility(
-      String fieldId, bool visibility) = _SetFieldVisibility;
+  const factory GridGroupEvent.setGroupByField(
+    String fieldId,
+    FieldType fieldType,
+  ) = _GroupByField;
   const factory GridGroupEvent.didReceiveFieldUpdate(
       List<GridFieldContext> fields) = _DidReceiveFieldUpdate;
-  const factory GridGroupEvent.moveField(int fromIndex, int toIndex) =
-      _MoveField;
 }
 
 @freezed

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/setting/property_bloc.dart

@@ -4,7 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 
-import '../field/field_cache.dart';
+import '../field/field_controller.dart';
 
 part 'property_bloc.freezed.dart';
 

+ 16 - 0
frontend/app_flowy/lib/plugins/grid/application/setting/setting_service.dart

@@ -1,7 +1,9 @@
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
 
 class SettingFFIService {
@@ -13,4 +15,18 @@ class SettingFFIService {
     final payload = GridIdPB.create()..value = viewId;
     return GridEventGetGridSetting(payload).send();
   }
+
+  Future<Either<Unit, FlowyError>> groupByField({
+    required String fieldId,
+    required FieldType fieldType,
+  }) {
+    final insertGroupPayload = InsertGroupPayloadPB.create()
+      ..fieldId = fieldId
+      ..fieldType = fieldType;
+    final payload = GridSettingChangesetPayloadPB.create()
+      ..gridId = viewId
+      ..insertGroup = insertGroupPayload;
+
+    return GridEventUpdateGridSetting(payload).send();
+  }
 }

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/grid_bloc.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/layout/layout.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'sizes.dart';
 
 class GridLayout {

+ 0 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart

@@ -1,4 +1,3 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_cell_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart

@@ -1,5 +1,5 @@
 import 'package:app_flowy/generated/locale_keys.g.dart';
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart

@@ -1,6 +1,6 @@
 import 'dart:typed_data';
 
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart

@@ -215,7 +215,7 @@ class _RowDetailCell extends StatelessWidget {
             SizedBox(
               width: 150,
               child: FieldCellButton(
-                field: cellId.fieldContext.fieldContext,
+                field: cellId.fieldContext.field,
                 onTap: () => _showFieldEditor(context),
               ),
             ),
@@ -233,7 +233,7 @@ class _RowDetailCell extends StatelessWidget {
       fieldName: cellId.fieldContext.name,
       typeOptionLoader: FieldTypeOptionLoader(
         gridId: cellId.gridId,
-        field: cellId.fieldContext.fieldContext,
+        field: cellId.fieldContext.field,
       ),
     ).show(context);
   }

+ 47 - 20
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart

@@ -1,8 +1,9 @@
-import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
@@ -29,10 +30,10 @@ class GridGroupList extends StatelessWidget {
       )..add(const GridGroupEvent.initial()),
       child: BlocBuilder<GridGroupBloc, GridGroupState>(
         builder: (context, state) {
-          final cells = state.fieldContexts.map((field) {
+          final cells = state.fieldContexts.map((fieldContext) {
             return _GridGroupCell(
-              fieldContext: field,
-              key: ValueKey(field.id),
+              fieldContext: fieldContext,
+              key: ValueKey(fieldContext.id),
             );
           }).toList();
 
@@ -51,7 +52,22 @@ class GridGroupList extends StatelessWidget {
     );
   }
 
-  void show(BuildContext context) {}
+  void show(BuildContext context) {
+    FlowyOverlay.of(context).insertWithAnchor(
+      widget: OverlayContainer(
+        constraints: BoxConstraints.loose(const Size(260, 400)),
+        child: this,
+      ),
+      identifier: identifier(),
+      anchorContext: context,
+      anchorDirection: AnchorDirection.bottomRight,
+      style: FlowyOverlayStyle(blur: false),
+    );
+  }
+
+  static String identifier() {
+    return (GridGroupList).toString();
+  }
 }
 
 class _GridGroupCell extends StatelessWidget {
@@ -61,23 +77,34 @@ class _GridGroupCell extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-
-    // final checkmark = field.visibility
-    //     ? svgWidget('home/show', color: theme.iconColor)
-    //     : svgWidget('home/hide', color: theme.iconColor);
+    final theme = context.read<AppTheme>();
 
-    // Padding(
-    //                   padding: const EdgeInsets.only(right: 6),
-    //                   child: svgWidget("grid/checkmark"),
-    //                 ),
+    Widget? rightIcon;
+    if (fieldContext.isGroupField) {
+      rightIcon = Padding(
+        padding: const EdgeInsets.all(2.0),
+        child: svgWidget("grid/checkmark"),
+      );
+    }
 
-    return FlowyButton(
-      text: FlowyText.medium(fieldContext.name, fontSize: 12),
-      hoverColor: theme.hover,
-      leftIcon:
-          svgWidget(fieldContext.fieldType.iconName(), color: theme.iconColor),
-      onTap: () {},
+    return SizedBox(
+      height: GridSize.typeOptionItemHeight,
+      child: FlowyButton(
+        text: FlowyText.medium(fieldContext.name, fontSize: 12),
+        hoverColor: theme.hover,
+        leftIcon: svgWidget(fieldContext.fieldType.iconName(),
+            color: theme.iconColor),
+        rightIcon: rightIcon,
+        onTap: () {
+          context.read<GridGroupBloc>().add(
+                GridGroupEvent.setGroupByField(
+                  fieldContext.id,
+                  fieldContext.fieldType,
+                ),
+              );
+          FlowyOverlay.of(context).remove(GridGroupList.identifier());
+        },
+      ),
     );
   }
 }

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

@@ -13,7 +13,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.dart';
 
-import '../../../application/field/field_cache.dart';
+import '../../../application/field/field_controller.dart';
 import '../../layout/sizes.dart';
 import '../header/field_editor.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_setting.dart

@@ -11,7 +11,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:app_flowy/generated/locale_keys.g.dart';
-import '../../../application/field/field_cache.dart';
+import '../../../application/field/field_controller.dart';
 import '../../layout/sizes.dart';
 import 'grid_property.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart

@@ -5,7 +5,7 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../../application/field/field_cache.dart';
+import '../../../application/field/field_controller.dart';
 import '../../layout/sizes.dart';
 import 'grid_setting.dart';
 

+ 1 - 1
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -21,7 +21,7 @@ import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:get_it/get_it.dart';
 
-import '../plugins/grid/application/field/field_cache.dart';
+import '../plugins/grid/application/field/field_controller.dart';
 
 class DependencyResolver {
   static Future<void> resolve(GetIt getIt) async {

+ 1 - 0
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart

@@ -91,6 +91,7 @@ class AFBoardDataController extends ChangeNotifier
 
   void clear() {
     _columnDatas.clear();
+    _columnControllers.clear();
     notifyListeners();
   }
 

+ 6 - 6
frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs

@@ -78,7 +78,7 @@ pub struct DeleteFilterParams {
 }
 
 #[derive(ProtoBuf, Debug, Default, Clone)]
-pub struct CreateGridFilterPayloadPB {
+pub struct InsertFilterPayloadPB {
     #[pb(index = 1)]
     pub field_id: String,
 
@@ -92,7 +92,7 @@ pub struct CreateGridFilterPayloadPB {
     pub content: Option<String>,
 }
 
-impl CreateGridFilterPayloadPB {
+impl InsertFilterPayloadPB {
     #[allow(dead_code)]
     pub fn new<T: Into<i32>>(field_rev: &FieldRevision, condition: T, content: Option<String>) -> Self {
         Self {
@@ -104,10 +104,10 @@ impl CreateGridFilterPayloadPB {
     }
 }
 
-impl TryInto<CreateFilterParams> for CreateGridFilterPayloadPB {
+impl TryInto<InsertFilterParams> for InsertFilterPayloadPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<CreateFilterParams, Self::Error> {
+    fn try_into(self) -> Result<InsertFilterParams, Self::Error> {
         let field_id = NotEmptyStr::parse(self.field_id)
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
@@ -130,7 +130,7 @@ impl TryInto<CreateFilterParams> for CreateGridFilterPayloadPB {
             }
         }
 
-        Ok(CreateFilterParams {
+        Ok(InsertFilterParams {
             field_id,
             field_type_rev: self.field_type.into(),
             condition,
@@ -139,7 +139,7 @@ impl TryInto<CreateFilterParams> for CreateGridFilterPayloadPB {
     }
 }
 
-pub struct CreateFilterParams {
+pub struct InsertFilterParams {
     pub field_id: String,
     pub field_type_rev: FieldTypeRevision,
     pub condition: u8,

+ 5 - 5
frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs

@@ -130,7 +130,7 @@ impl std::convert::From<Vec<Arc<GroupConfigurationRevision>>> for RepeatedGridGr
 }
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct CreateGridGroupPayloadPB {
+pub struct InsertGroupPayloadPB {
     #[pb(index = 1)]
     pub field_id: String,
 
@@ -138,22 +138,22 @@ pub struct CreateGridGroupPayloadPB {
     pub field_type: FieldType,
 }
 
-impl TryInto<CreatGroupParams> for CreateGridGroupPayloadPB {
+impl TryInto<InsertGroupParams> for InsertGroupPayloadPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<CreatGroupParams, Self::Error> {
+    fn try_into(self) -> Result<InsertGroupParams, Self::Error> {
         let field_id = NotEmptyStr::parse(self.field_id)
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
 
-        Ok(CreatGroupParams {
+        Ok(InsertGroupParams {
             field_id,
             field_type_rev: self.field_type.into(),
         })
     }
 }
 
-pub struct CreatGroupParams {
+pub struct InsertGroupParams {
     pub field_id: String,
     pub field_type_rev: FieldTypeRevision,
 }

+ 7 - 8
frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs

@@ -1,13 +1,12 @@
 use crate::entities::{
-    CreatGroupParams, CreateFilterParams, CreateGridFilterPayloadPB, CreateGridGroupPayloadPB, DeleteFilterParams,
-    DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, RepeatedGridFilterConfigurationPB,
+    DeleteFilterParams, DeleteFilterPayloadPB, DeleteGroupParams, DeleteGroupPayloadPB, InsertFilterParams,
+    InsertFilterPayloadPB, InsertGroupParams, InsertGroupPayloadPB, RepeatedGridFilterConfigurationPB,
     RepeatedGridGroupConfigurationPB,
 };
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::parser::NotEmptyStr;
 use flowy_grid_data_model::revision::LayoutRevision;
-use std::collections::HashMap;
 use std::convert::TryInto;
 use strum::IntoEnumIterator;
 use strum_macros::EnumIter;
@@ -85,13 +84,13 @@ pub struct GridSettingChangesetPayloadPB {
     pub layout_type: GridLayout,
 
     #[pb(index = 3, one_of)]
-    pub insert_filter: Option<CreateGridFilterPayloadPB>,
+    pub insert_filter: Option<InsertFilterPayloadPB>,
 
     #[pb(index = 4, one_of)]
     pub delete_filter: Option<DeleteFilterPayloadPB>,
 
     #[pb(index = 5, one_of)]
-    pub insert_group: Option<CreateGridGroupPayloadPB>,
+    pub insert_group: Option<InsertGroupPayloadPB>,
 
     #[pb(index = 6, one_of)]
     pub delete_group: Option<DeleteGroupPayloadPB>,
@@ -102,7 +101,7 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
 
     fn try_into(self) -> Result<GridSettingChangesetParams, Self::Error> {
         let view_id = NotEmptyStr::parse(self.grid_id)
-            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .map_err(|_| ErrorCode::ViewIdInvalid)?
             .0;
 
         let insert_filter = match self.insert_filter {
@@ -139,9 +138,9 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayloadPB {
 pub struct GridSettingChangesetParams {
     pub grid_id: String,
     pub layout_type: LayoutRevision,
-    pub insert_filter: Option<CreateFilterParams>,
+    pub insert_filter: Option<InsertFilterParams>,
     pub delete_filter: Option<DeleteFilterParams>,
-    pub insert_group: Option<CreatGroupParams>,
+    pub insert_group: Option<InsertGroupParams>,
     pub delete_group: Option<DeleteGroupParams>,
 }
 

+ 26 - 0
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -35,6 +35,32 @@ pub(crate) async fn get_grid_setting_handler(
     data_result(grid_setting)
 }
 
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
+pub(crate) async fn update_grid_setting_handler(
+    data: Data<GridSettingChangesetPayloadPB>,
+    manager: AppData<Arc<GridManager>>,
+) -> Result<(), FlowyError> {
+    let params: GridSettingChangesetParams = data.into_inner().try_into()?;
+
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    if let Some(insert_params) = params.insert_group {
+        let _ = editor.create_group(insert_params).await?;
+    }
+
+    if let Some(delete_params) = params.delete_group {
+        let _ = editor.delete_group(delete_params).await?;
+    }
+
+    if let Some(create_filter) = params.insert_filter {
+        let _ = editor.create_filter(create_filter).await?;
+    }
+
+    if let Some(delete_filter) = params.delete_filter {
+        let _ = editor.delete_filter(delete_filter).await?;
+    }
+    Ok(())
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_grid_blocks_handler(
     data: Data<QueryBlocksPayloadPB>,

+ 6 - 3
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -11,7 +11,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::GetGrid, get_grid_handler)
         .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
         .event(GridEvent::GetGridSetting, get_grid_setting_handler)
-        // .event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
+        .event(GridEvent::UpdateGridSetting, update_grid_setting_handler)
         // Field
         .event(GridEvent::GetFields, get_fields_handler)
         .event(GridEvent::UpdateField, update_field_handler)
@@ -75,8 +75,8 @@ pub enum GridEvent {
 
     /// [UpdateGridSetting] event is used to update the grid's settings.
     ///
-    /// The event handler accepts [GridIdPB] and return errors if failed to modify the grid's settings.
-    #[event(input = "GridIdPB", input = "GridSettingChangesetPayloadPB")]
+    /// The event handler accepts [GridSettingChangesetPayloadPB] and return errors if failed to modify the grid's settings.
+    #[event(input = "GridSettingChangesetPayloadPB")]
     UpdateGridSetting = 3,
 
     /// [GetFields] event is used to get the grid's settings.
@@ -225,4 +225,7 @@ pub enum GridEvent {
 
     #[event(input = "MoveGroupRowPayloadPB")]
     MoveGroupRow = 112,
+
+    #[event(input = "MoveGroupRowPayloadPB")]
+    GroupByField = 113,
 }

+ 11 - 3
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -211,7 +211,7 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    pub async fn group_field(&self, field_id: &str) -> FlowyResult<()> {
+    pub async fn group_by_field(&self, field_id: &str) -> FlowyResult<()> {
         let _ = self.view_manager.group_by_field(field_id).await?;
         Ok(())
     }
@@ -536,8 +536,16 @@ impl GridRevisionEditor {
         self.view_manager.get_filters().await
     }
 
-    pub async fn update_filter(&self, params: CreateFilterParams) -> FlowyResult<()> {
-        let _ = self.view_manager.update_filter(params).await?;
+    pub async fn create_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
+        self.view_manager.insert_or_update_group(params).await
+    }
+
+    pub async fn delete_group(&self, params: DeleteGroupParams) -> FlowyResult<()> {
+        self.view_manager.delete_group(params).await
+    }
+
+    pub async fn create_filter(&self, params: InsertFilterParams) -> FlowyResult<()> {
+        let _ = self.view_manager.insert_or_update_filter(params).await?;
         Ok(())
     }
 

+ 43 - 11
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -1,14 +1,15 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::{
-    CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfigurationPB, GridGroupConfigurationPB,
-    GridLayout, GridLayoutPB, GridSettingPB, GroupChangesetPB, GroupPB, GroupViewChangesetPB, InsertedGroupPB,
-    InsertedRowPB, MoveGroupParams, RepeatedGridFilterConfigurationPB, RepeatedGridGroupConfigurationPB, RowPB,
+    CreateRowParams, DeleteFilterParams, DeleteGroupParams, GridFilterConfigurationPB, GridGroupConfigurationPB,
+    GridLayout, GridLayoutPB, GridSettingPB, GroupChangesetPB, GroupPB, GroupViewChangesetPB, InsertFilterParams,
+    InsertGroupParams, InsertedGroupPB, InsertedRowPB, MoveGroupParams, RepeatedGridFilterConfigurationPB,
+    RepeatedGridGroupConfigurationPB, RowPB,
 };
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
 use crate::services::group::{
-    find_group_field, make_group_controller, GroupConfigurationReader, GroupConfigurationWriter, GroupController,
-    MoveGroupRowContext,
+    default_group_configuration, find_group_field, make_group_controller, GroupConfigurationReader,
+    GroupConfigurationWriter, GroupController, MoveGroupRowContext,
 };
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
@@ -19,7 +20,6 @@ use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilde
 use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
 use flowy_sync::entities::revision::Revision;
 use lib_infra::future::{wrap_future, AFFuture, FutureResult};
-use std::collections::HashMap;
 use std::future::Future;
 use std::sync::Arc;
 use tokio::sync::RwLock;
@@ -210,15 +210,40 @@ impl GridViewRevisionEditor {
         }
     }
 
-    pub(crate) async fn insert_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> {
+    pub(crate) async fn insert_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
+        if let Some(field_rev) = self.field_delegate.get_field_rev(&params.field_id).await {
+            let _ = self
+                .modify(|pad| {
+                    let configuration = default_group_configuration(&field_rev);
+                    let changeset = pad.insert_group(&params.field_id, &params.field_type_rev, configuration)?;
+                    Ok(changeset)
+                })
+                .await?;
+        }
+        if self.group_controller.read().await.field_id() != params.field_id {
+            let _ = self.group_by_field(&params.field_id).await?;
+            self.notify_did_update_setting().await;
+        }
+        Ok(())
+    }
+
+    pub(crate) async fn delete_group(&self, params: DeleteGroupParams) -> FlowyResult<()> {
+        self.modify(|pad| {
+            let changeset = pad.delete_filter(&params.field_id, &params.field_type_rev, &params.group_id)?;
+            Ok(changeset)
+        })
+        .await
+    }
+
+    pub(crate) async fn insert_filter(&self, params: InsertFilterParams) -> FlowyResult<()> {
         self.modify(|pad| {
             let filter_rev = FilterConfigurationRevision {
                 id: gen_grid_filter_id(),
-                field_id: insert_filter.field_id.clone(),
-                condition: insert_filter.condition,
-                content: insert_filter.content,
+                field_id: params.field_id.clone(),
+                condition: params.condition,
+                content: params.content,
             };
-            let changeset = pad.insert_filter(&insert_filter.field_id, &insert_filter.field_type_rev, filter_rev)?;
+            let changeset = pad.insert_filter(&params.field_id, &params.field_type_rev, filter_rev)?;
             Ok(changeset)
         })
         .await
@@ -279,6 +304,13 @@ impl GridViewRevisionEditor {
         Ok(())
     }
 
+    async fn notify_did_update_setting(&self) {
+        let setting = self.get_setting().await;
+        send_dart_notification(&self.view_id, GridNotification::DidUpdateGridSetting)
+            .payload(setting)
+            .send();
+    }
+
     pub async fn notify_did_update_group(&self, changeset: GroupChangesetPB) {
         send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup)
             .payload(changeset)

+ 16 - 6
frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs

@@ -1,6 +1,6 @@
 use crate::entities::{
-    CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfigurationPB, GridSettingPB, MoveGroupParams,
-    RepeatedGridGroupPB, RowPB,
+    CreateRowParams, DeleteFilterParams, DeleteGroupParams, GridFilterConfigurationPB, GridSettingPB,
+    InsertFilterParams, InsertGroupParams, MoveGroupParams, RepeatedGridGroupPB, RowPB,
 };
 use crate::manager::GridUser;
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
@@ -110,14 +110,14 @@ impl GridViewManager {
         Ok(view_editor.get_filters().await)
     }
 
-    pub(crate) async fn update_filter(&self, insert_filter: CreateFilterParams) -> FlowyResult<()> {
+    pub(crate) async fn insert_or_update_filter(&self, params: InsertFilterParams) -> FlowyResult<()> {
         let view_editor = self.get_default_view_editor().await?;
-        view_editor.insert_filter(insert_filter).await
+        view_editor.insert_filter(params).await
     }
 
-    pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> {
+    pub(crate) async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> {
         let view_editor = self.get_default_view_editor().await?;
-        view_editor.delete_filter(delete_filter).await
+        view_editor.delete_filter(params).await
     }
 
     pub(crate) async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
@@ -126,6 +126,16 @@ impl GridViewManager {
         Ok(RepeatedGridGroupPB { items: groups })
     }
 
+    pub(crate) async fn insert_or_update_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
+        let view_editor = self.get_default_view_editor().await?;
+        view_editor.insert_group(params).await
+    }
+
+    pub(crate) async fn delete_group(&self, params: DeleteGroupParams) -> FlowyResult<()> {
+        let view_editor = self.get_default_view_editor().await?;
+        view_editor.delete_group(params).await
+    }
+
     pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
         let view_editor = self.get_default_view_editor().await?;
         let _ = view_editor.move_group(params).await?;

+ 3 - 1
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs

@@ -133,6 +133,7 @@ where
         }
     }
 
+    #[tracing::instrument(level = "debug", skip(self, generated_groups), err)]
     pub(crate) fn init_groups(
         &mut self,
         generated_groups: Vec<GeneratedGroup>,
@@ -316,7 +317,8 @@ where
 fn merge_groups(old_groups: &[GroupRevision], new_groups: Vec<GroupRevision>) -> MergeGroupResult {
     let mut merge_result = MergeGroupResult::new();
     if old_groups.is_empty() {
-        merge_result.all_group_revs = new_groups;
+        merge_result.all_group_revs = new_groups.clone();
+        merge_result.new_group_revs = new_groups;
         return merge_result;
     }
 

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -89,7 +89,7 @@ where
         let field_type_rev = field_rev.ty;
         let type_option = field_rev.get_type_option::<T>(field_type_rev);
         let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
-        let _ = configuration.init_groups(groups, false)?;
+        let _ = configuration.init_groups(groups, true)?;
 
         Ok(Self {
             field_id: field_rev.id.clone(),

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs

@@ -16,7 +16,7 @@ impl DefaultGroupController {
         let group = Group::new(
             DEFAULT_GROUP_CONTROLLER.to_owned(),
             field_rev.id.clone(),
-            "Oops".to_owned(),
+            "".to_owned(),
             "".to_owned(),
         );
         Self {

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs

@@ -1,4 +1,4 @@
-use crate::entities::{CreateFilterParams, DeleteFilterParams, GridLayout, GridSettingChangesetParams};
+use crate::entities::{DeleteFilterParams, GridLayout, GridSettingChangesetParams, InsertFilterParams};
 
 pub struct GridSettingChangesetBuilder {
     params: GridSettingChangesetParams,
@@ -17,7 +17,7 @@ impl GridSettingChangesetBuilder {
         Self { params }
     }
 
-    pub fn insert_filter(mut self, params: CreateFilterParams) -> Self {
+    pub fn insert_filter(mut self, params: InsertFilterParams) -> Self {
         self.params.insert_filter = Some(params);
         self
     }

+ 4 - 4
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -3,14 +3,14 @@
 #![allow(dead_code)]
 #![allow(unused_imports)]
 
-use flowy_grid::entities::{CreateFilterParams, CreateGridFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB};
+use flowy_grid::entities::{InsertFilterParams, InsertFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB};
 use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
 use crate::grid::grid_editor::GridEditorTest;
 
 pub enum FilterScript {
     InsertGridTableFilter {
-        payload: CreateGridFilterPayloadPB,
+        payload: InsertFilterPayloadPB,
     },
     AssertTableFilterCount {
         count: i32,
@@ -47,8 +47,8 @@ impl GridFilterTest {
         match script {
            
             FilterScript::InsertGridTableFilter { payload } => {
-                let params: CreateFilterParams = payload.try_into().unwrap();
-                let _ = self.editor.update_filter(params).await.unwrap();
+                let params: InsertFilterParams = payload.try_into().unwrap();
+                let _ = self.editor.create_filter(params).await.unwrap();
             }
             FilterScript::AssertTableFilterCount { count } => {
                 let filters = self.editor.get_grid_filter().await.unwrap();

+ 5 - 5
frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs

@@ -1,13 +1,13 @@
 use crate::grid::filter_test::script::FilterScript::*;
 use crate::grid::filter_test::script::*;
-use flowy_grid::entities::{CreateGridFilterPayloadPB, FieldType, TextFilterCondition};
+use flowy_grid::entities::{FieldType, InsertFilterPayloadPB, TextFilterCondition};
 use flowy_grid_data_model::revision::FieldRevision;
 
 #[tokio::test]
 async fn grid_filter_create_test() {
     let mut test = GridFilterTest::new().await;
     let field_rev = test.get_field_rev(FieldType::RichText);
-    let payload = CreateGridFilterPayloadPB::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
+    let payload = InsertFilterPayloadPB::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
     let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
     test.run_scripts(scripts).await;
 }
@@ -19,7 +19,7 @@ async fn grid_filter_invalid_condition_panic_test() {
     let field_rev = test.get_field_rev(FieldType::RichText).clone();
 
     // 100 is not a valid condition, so this test should be panic.
-    let payload = CreateGridFilterPayloadPB::new(&field_rev, 100, Some("".to_owned()));
+    let payload = InsertFilterPayloadPB::new(&field_rev, 100, Some("".to_owned()));
     let scripts = vec![InsertGridTableFilter { payload }];
     test.run_scripts(scripts).await;
 }
@@ -46,6 +46,6 @@ async fn grid_filter_delete_test() {
 #[tokio::test]
 async fn grid_filter_get_rows_test() {}
 
-fn create_filter(field_rev: &FieldRevision, condition: TextFilterCondition, s: &str) -> CreateGridFilterPayloadPB {
-    CreateGridFilterPayloadPB::new(field_rev, condition, Some(s.to_owned()))
+fn create_filter(field_rev: &FieldRevision, condition: TextFilterCondition, s: &str) -> InsertFilterPayloadPB {
+    InsertFilterPayloadPB::new(field_rev, condition, Some(s.to_owned()))
 }

+ 1 - 1
frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs

@@ -188,7 +188,7 @@ impl GridGroupTest {
                 .await;
             }
             GroupScript::GroupByField { field_id } => {
-                self.editor.group_field(&field_id).await.unwrap();
+                self.editor.group_by_field(&field_id).await.unwrap();
             }
         }
     }

+ 2 - 2
shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs

@@ -61,12 +61,12 @@ impl GridViewRevisionPad {
         &mut self,
         field_id: &str,
         field_type: &FieldTypeRevision,
-        group_rev: GroupConfigurationRevision,
+        group_configuration_rev: GroupConfigurationRevision,
     ) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
         self.modify(|view| {
             // Only save one group
             view.groups.clear();
-            view.groups.add_object(field_id, field_type, group_rev);
+            view.groups.add_object(field_id, field_type, group_configuration_rev);
             Ok(Some(()))
         })
     }