Pārlūkot izejas kodu

chore: reload group when group by new field

appflowy 2 gadi atpakaļ
vecāks
revīzija
bb7cddc7f7
26 mainītis faili ar 353 papildinājumiem un 209 dzēšanām
  1. 27 17
      frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
  2. 28 18
      frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart
  3. 24 8
      frontend/app_flowy/lib/plugins/board/application/board_listener.dart
  4. 5 0
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart
  5. 1 0
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  6. 4 1
      frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs
  7. 21 23
      frontend/rust-lib/flowy-grid/src/services/field/field_operation.rs
  8. 2 1
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  9. 84 21
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  10. 6 0
      frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
  11. 13 8
      frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
  12. 15 16
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  13. 3 4
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
  14. 10 10
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs
  15. 6 8
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
  16. 5 7
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
  17. 2 2
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
  18. 4 3
      frontend/rust-lib/flowy-grid/src/services/group/group_util.rs
  19. 7 0
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
  20. 21 20
      frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs
  21. 32 22
      frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs
  22. 9 1
      shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs
  23. 7 3
      shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs
  24. 1 1
      shared-lib/lib-ot/src/core/document/document_operation.rs
  25. 1 0
      shared-lib/lib-ot/src/core/document/mod.rs
  26. 15 15
      shared-lib/lib-ot/tests/main.rs

+ 27 - 17
frontend/app_flowy/lib/plugins/board/application/board_bloc.dart

@@ -110,9 +110,11 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
             emit(state.copyWith(noneOrError: some(error)));
           },
           didReceiveGroups: (List<GroupPB> groups) {
-            emit(state.copyWith(
-              groupIds: groups.map((group) => group.groupId).toList(),
-            ));
+            emit(
+              state.copyWith(
+                groupIds: groups.map((group) => group.groupId).toList(),
+              ),
+            );
           },
         );
       },
@@ -154,6 +156,23 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
   }
 
   void initializeGroups(List<GroupPB> groups) {
+    for (var controller in groupControllers.values) {
+      controller.dispose();
+    }
+    groupControllers.clear();
+    boardController.clear();
+
+    //
+    List<AFBoardColumnData> columns = groups.map((group) {
+      return AFBoardColumnData(
+        id: group.groupId,
+        name: group.desc,
+        items: _buildRows(group),
+        customData: group,
+      );
+    }).toList();
+    boardController.addColumns(columns);
+
     for (final group in groups) {
       final delegate = GroupControllerDelegateImpl(
         controller: boardController,
@@ -184,16 +203,6 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
         }
       },
       didLoadGroups: (groups) {
-        List<AFBoardColumnData> columns = groups.map((group) {
-          return AFBoardColumnData(
-            id: group.groupId,
-            name: group.desc,
-            items: _buildRows(group),
-            customData: group,
-          );
-        }).toList();
-
-        boardController.addColumns(columns);
         initializeGroups(groups);
         add(BoardEvent.didReceiveGroups(groups));
       },
@@ -204,18 +213,19 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
         //
       },
       onUpdatedGroup: (updatedGroups) {
-        //
         for (final group in updatedGroups) {
           final columnController =
               boardController.getColumnController(group.groupId);
-          if (columnController != null) {
-            columnController.updateColumnName(group.desc);
-          }
+          columnController?.updateColumnName(group.desc);
         }
       },
       onError: (err) {
         Log.error(err);
       },
+      onResetGroups: (groups) {
+        initializeGroups(groups);
+        add(BoardEvent.didReceiveGroups(groups));
+      },
     );
   }
 

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

@@ -18,6 +18,7 @@ typedef DidLoadGroups = void Function(List<GroupPB>);
 typedef OnUpdatedGroup = void Function(List<GroupPB>);
 typedef OnDeletedGroup = void Function(List<String>);
 typedef OnInsertedGroup = void Function(List<InsertedGroupPB>);
+typedef OnResetGroups = void Function(List<GroupPB>);
 
 typedef OnRowsChanged = void Function(
   List<RowInfo>,
@@ -65,6 +66,7 @@ class BoardDataController {
     required OnUpdatedGroup onUpdatedGroup,
     required OnDeletedGroup onDeletedGroup,
     required OnInsertedGroup onInsertedGroup,
+    required OnResetGroups onResetGroups,
     required OnError? onError,
   }) {
     _onGridChanged = onGridChanged;
@@ -77,24 +79,32 @@ class BoardDataController {
       _onFieldsChanged?.call(UnmodifiableListView(fields));
     });
 
-    _listener.start(onBoardChanged: (result) {
-      result.fold(
-        (changeset) {
-          if (changeset.updateGroups.isNotEmpty) {
-            onUpdatedGroup.call(changeset.updateGroups);
-          }
-
-          if (changeset.insertedGroups.isNotEmpty) {
-            onInsertedGroup.call(changeset.insertedGroups);
-          }
-
-          if (changeset.deletedGroups.isNotEmpty) {
-            onDeletedGroup.call(changeset.deletedGroups);
-          }
-        },
-        (e) => _onError?.call(e),
-      );
-    });
+    _listener.start(
+      onBoardChanged: (result) {
+        result.fold(
+          (changeset) {
+            if (changeset.updateGroups.isNotEmpty) {
+              onUpdatedGroup.call(changeset.updateGroups);
+            }
+
+            if (changeset.insertedGroups.isNotEmpty) {
+              onInsertedGroup.call(changeset.insertedGroups);
+            }
+
+            if (changeset.deletedGroups.isNotEmpty) {
+              onDeletedGroup.call(changeset.deletedGroups);
+            }
+          },
+          (e) => _onError?.call(e),
+        );
+      },
+      onGroupByNewField: (result) {
+        result.fold(
+          (groups) => onResetGroups(groups),
+          (e) => _onError?.call(e),
+        );
+      },
+    );
   }
 
   Future<Either<Unit, FlowyError>> loadData() async {

+ 24 - 8
frontend/app_flowy/lib/plugins/board/application/board_listener.dart

@@ -5,20 +5,26 @@ import 'package:flowy_infra/notifier.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
 import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
 
-typedef UpdateBoardNotifiedValue = Either<GroupViewChangesetPB, FlowyError>;
+typedef GroupUpdateValue = Either<GroupViewChangesetPB, FlowyError>;
+typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
 
 class BoardListener {
   final String viewId;
-  PublishNotifier<UpdateBoardNotifiedValue>? _groupNotifier = PublishNotifier();
+  PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
+  PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
+      PublishNotifier();
   GridNotificationListener? _listener;
   BoardListener(this.viewId);
 
   void start({
-    required void Function(UpdateBoardNotifiedValue) onBoardChanged,
+    required void Function(GroupUpdateValue) onBoardChanged,
+    required void Function(GroupByNewFieldValue) onGroupByNewField,
   }) {
-    _groupNotifier?.addPublishListener(onBoardChanged);
+    _groupUpdateNotifier?.addPublishListener(onBoardChanged);
+    _groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
     _listener = GridNotificationListener(
       objectId: viewId,
       handler: _handler,
@@ -32,9 +38,16 @@ class BoardListener {
     switch (ty) {
       case GridNotification.DidUpdateGroupView:
         result.fold(
-          (payload) => _groupNotifier?.value =
+          (payload) => _groupUpdateNotifier?.value =
               left(GroupViewChangesetPB.fromBuffer(payload)),
-          (error) => _groupNotifier?.value = right(error),
+          (error) => _groupUpdateNotifier?.value = right(error),
+        );
+        break;
+      case GridNotification.DidGroupByNewField:
+        result.fold(
+          (payload) => _groupByNewFieldNotifier?.value =
+              left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
+          (error) => _groupByNewFieldNotifier?.value = right(error),
         );
         break;
       default:
@@ -44,7 +57,10 @@ class BoardListener {
 
   Future<void> stop() async {
     await _listener?.stop();
-    _groupNotifier?.dispose();
-    _groupNotifier = null;
+    _groupUpdateNotifier?.dispose();
+    _groupUpdateNotifier = null;
+
+    _groupByNewFieldNotifier?.dispose();
+    _groupByNewFieldNotifier = null;
   }
 }

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

@@ -89,6 +89,11 @@ class AFBoardDataController extends ChangeNotifier
     if (columnIds.isNotEmpty && notify) notifyListeners();
   }
 
+  void clear() {
+    _columnDatas.clear();
+    notifyListeners();
+  }
+
   AFBoardColumnDataController? getColumnController(String columnId) {
     final columnController = _columnControllers[columnId];
     if (columnController == null) {

+ 1 - 0
frontend/rust-lib/flowy-grid/src/dart_notification.rs

@@ -13,6 +13,7 @@ pub enum GridNotification {
     DidUpdateField = 50,
     DidUpdateGroupView = 60,
     DidUpdateGroup = 61,
+    DidGroupByNewField = 62,
 }
 
 impl std::default::Default for GridNotification {

+ 4 - 1
frontend/rust-lib/flowy-grid/src/entities/group_entities/group_changeset.rs

@@ -145,7 +145,10 @@ pub struct GroupViewChangesetPB {
 
 impl GroupViewChangesetPB {
     pub fn is_empty(&self) -> bool {
-        self.inserted_groups.is_empty() && self.deleted_groups.is_empty() && self.update_groups.is_empty()
+        self.new_groups.is_empty()
+            && self.inserted_groups.is_empty()
+            && self.deleted_groups.is_empty()
+            && self.update_groups.is_empty()
     }
 }
 

+ 21 - 23
frontend/rust-lib/flowy-grid/src/services/field/field_operation.rs

@@ -1,14 +1,13 @@
-use crate::entities::{FieldChangesetParams, FieldType};
-use crate::services::field::{select_option_operation, SelectOptionPB};
+use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
 use crate::services::grid_editor::GridRevisionEditor;
 use flowy_error::FlowyResult;
-use flowy_grid_data_model::revision::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataFormat};
+use flowy_grid_data_model::revision::{TypeOptionDataDeserializer, TypeOptionDataFormat};
 use std::sync::Arc;
 
-pub async fn edit_field<T>(
+pub async fn edit_field_type_option<T>(
     field_id: &str,
     editor: Arc<GridRevisionEditor>,
-    action: impl FnOnce(&mut T) -> bool,
+    action: impl FnOnce(&mut T),
 ) -> FlowyResult<()>
 where
     T: TypeOptionDataDeserializer + TypeOptionDataFormat,
@@ -19,29 +18,28 @@ where
     };
 
     if let Some(mut type_option) = get_type_option.await {
-        if action(&mut type_option) {
-            let changeset = FieldChangesetParams { ..Default::default() };
-            let _ = editor.update_field(changeset).await?;
-        }
+        action(&mut type_option);
+        let bytes = type_option.protobuf_bytes().to_vec();
+        let _ = editor
+            .update_field_type_option(&editor.grid_id, field_id, bytes)
+            .await?;
     }
 
     Ok(())
 }
 
-pub fn insert_single_select_option(field_rev: &mut FieldRevision, options: Vec<SelectOptionPB>) -> FlowyResult<()> {
-    if options.is_empty() {
-        return Ok(());
-    }
-    let mut type_option = select_option_operation(field_rev)?;
-    options.into_iter().for_each(|option| type_option.insert_option(option));
-    Ok(())
+pub async fn edit_single_select_type_option(
+    field_id: &str,
+    editor: Arc<GridRevisionEditor>,
+    action: impl FnOnce(&mut SingleSelectTypeOptionPB),
+) -> FlowyResult<()> {
+    edit_field_type_option(field_id, editor, action).await
 }
 
-pub fn insert_multi_select_option(field_rev: &mut FieldRevision, options: Vec<SelectOptionPB>) -> FlowyResult<()> {
-    if options.is_empty() {
-        return Ok(());
-    }
-    let mut type_option = select_option_operation(field_rev)?;
-    options.into_iter().for_each(|option| type_option.insert_option(option));
-    Ok(())
+pub async fn edit_multi_select_type_option(
+    field_id: &str,
+    editor: Arc<GridRevisionEditor>,
+    action: impl FnOnce(&mut MultiSelectTypeOptionPB),
+) -> FlowyResult<()> {
+    edit_field_type_option(field_id, editor, action).await
 }

+ 2 - 1
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -212,7 +212,8 @@ impl GridRevisionEditor {
     }
 
     pub async fn group_field(&self, field_id: &str) -> FlowyResult<()> {
-        todo!()
+        let _ = self.view_manager.group_by_field(field_id).await?;
+        Ok(())
     }
 
     pub async fn switch_to_field_type(&self, field_id: &str, field_type: &FieldType) -> FlowyResult<()> {

+ 84 - 21
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -7,7 +7,8 @@ use crate::entities::{
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
 use crate::services::group::{
-    make_group_controller, GroupConfigurationReader, GroupConfigurationWriter, GroupController, MoveGroupRowContext,
+    find_group_field, make_group_controller, GroupConfigurationReader, GroupConfigurationWriter, GroupController,
+    MoveGroupRowContext,
 };
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
@@ -34,7 +35,6 @@ pub struct GridViewRevisionEditor {
     group_controller: Arc<RwLock<Box<dyn GroupController>>>,
     scheduler: Arc<dyn GridServiceTaskScheduler>,
 }
-
 impl GridViewRevisionEditor {
     #[tracing::instrument(level = "trace", skip_all, err)]
     pub(crate) async fn new(
@@ -52,25 +52,15 @@ impl GridViewRevisionEditor {
         let view_revision_pad = rev_manager.load::<GridViewRevisionPadBuilder>(Some(cloud)).await?;
         let pad = Arc::new(RwLock::new(view_revision_pad));
         let rev_manager = Arc::new(rev_manager);
-
-        // Load group
-        let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
-        let configuration_writer = GroupConfigurationWriterImpl {
-            user_id: user_id.to_owned(),
-            rev_manager: rev_manager.clone(),
-            view_pad: pad.clone(),
-        };
-        let field_revs = field_delegate.get_field_revs().await;
-        let row_revs = row_delegate.gv_row_revs().await;
-        let group_controller = make_group_controller(
+        let group_controller = new_group_controller(
+            user_id.to_owned(),
             view_id.clone(),
-            field_revs,
-            row_revs,
-            configuration_reader,
-            configuration_writer,
+            pad.clone(),
+            rev_manager.clone(),
+            field_delegate.clone(),
+            row_delegate.clone(),
         )
         .await?;
-
         let user_id = user_id.to_owned();
         Ok(Self {
             pad,
@@ -258,6 +248,37 @@ impl GridViewRevisionEditor {
         Ok(())
     }
 
+    pub(crate) async fn group_by_field(&self, field_id: &str) -> FlowyResult<()> {
+        if let Some(field_rev) = self.field_delegate.get_field_rev(field_id).await {
+            let new_group_controller = new_group_controller_with_field_rev(
+                self.user_id.clone(),
+                self.view_id.clone(),
+                self.pad.clone(),
+                self.rev_manager.clone(),
+                field_rev,
+                self.row_delegate.clone(),
+            )
+            .await?;
+
+            let new_groups = new_group_controller.groups().into_iter().map(GroupPB::from).collect();
+
+            *self.group_controller.write().await = new_group_controller;
+            let changeset = GroupViewChangesetPB {
+                view_id: self.view_id.clone(),
+                new_groups,
+                ..Default::default()
+            };
+
+            debug_assert!(!changeset.is_empty());
+            if !changeset.is_empty() {
+                send_dart_notification(&changeset.view_id, GridNotification::DidGroupByNewField)
+                    .payload(changeset)
+                    .send();
+            }
+        }
+        Ok(())
+    }
+
     pub async fn notify_did_update_group(&self, changeset: GroupChangesetPB) {
         send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup)
             .payload(changeset)
@@ -314,6 +335,48 @@ impl GridViewRevisionEditor {
         }
     }
 }
+async fn new_group_controller(
+    user_id: String,
+    view_id: String,
+    pad: Arc<RwLock<GridViewRevisionPad>>,
+    rev_manager: Arc<RevisionManager>,
+    field_delegate: Arc<dyn GridViewFieldDelegate>,
+    row_delegate: Arc<dyn GridViewRowDelegate>,
+) -> FlowyResult<Box<dyn GroupController>> {
+    let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
+    let field_revs = field_delegate.get_field_revs().await;
+    // Read the group field or find a new group field
+    let field_rev = configuration_reader
+        .get_configuration()
+        .await
+        .and_then(|configuration| {
+            field_revs
+                .iter()
+                .find(|field_rev| field_rev.id == configuration.field_id)
+                .cloned()
+        })
+        .unwrap_or_else(|| find_group_field(&field_revs).unwrap());
+
+    new_group_controller_with_field_rev(user_id, view_id, pad, rev_manager, field_rev, row_delegate).await
+}
+
+async fn new_group_controller_with_field_rev(
+    user_id: String,
+    view_id: String,
+    pad: Arc<RwLock<GridViewRevisionPad>>,
+    rev_manager: Arc<RevisionManager>,
+    field_rev: Arc<FieldRevision>,
+    row_delegate: Arc<dyn GridViewRowDelegate>,
+) -> FlowyResult<Box<dyn GroupController>> {
+    let configuration_reader = GroupConfigurationReaderImpl(pad.clone());
+    let configuration_writer = GroupConfigurationWriterImpl {
+        user_id,
+        rev_manager,
+        view_pad: pad,
+    };
+    let row_revs = row_delegate.gv_row_revs().await;
+    make_group_controller(view_id, field_rev, row_revs, configuration_reader, configuration_writer).await
+}
 
 async fn apply_change(
     user_id: &str,
@@ -353,10 +416,10 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
 struct GroupConfigurationReaderImpl(Arc<RwLock<GridViewRevisionPad>>);
 
 impl GroupConfigurationReader for GroupConfigurationReaderImpl {
-    fn get_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
+    fn get_configuration(&self) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
         let view_pad = self.0.clone();
         wrap_future(async move {
-            let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?;
+            let mut groups = view_pad.read().await.get_all_groups();
             if groups.is_empty() {
                 None
             } else {
@@ -411,7 +474,7 @@ pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<Field
         })
         .unwrap_or_default();
     let groups_by_field_id = view_pad
-        .get_all_groups(field_revs)
+        .get_groups_by_field_revs(field_revs)
         .map(|groups_by_field_id| {
             groups_by_field_id
                 .into_iter()

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

@@ -84,6 +84,12 @@ impl GridViewManager {
         }
     }
 
+    pub(crate) async fn group_by_field(&self, field_id: &str) -> FlowyResult<()> {
+        let view_editor = self.get_default_view_editor().await?;
+        let _ = view_editor.group_by_field(field_id).await?;
+        Ok(())
+    }
+
     pub(crate) async fn did_update_cell(&self, row_id: &str, _field_id: &str) {
         self.did_update_row(row_id).await
     }

+ 13 - 8
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs

@@ -1,4 +1,4 @@
-use crate::entities::{GroupPB, GroupViewChangesetPB, InsertedGroupPB};
+use crate::entities::{GroupPB, GroupViewChangesetPB};
 use crate::services::group::{default_group_configuration, GeneratedGroup, Group};
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
@@ -12,7 +12,7 @@ use std::marker::PhantomData;
 use std::sync::Arc;
 
 pub trait GroupConfigurationReader: Send + Sync + 'static {
-    fn get_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
+    fn get_configuration(&self) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
 }
 
 pub trait GroupConfigurationWriter: Send + Sync + 'static {
@@ -38,8 +38,8 @@ impl<T> std::fmt::Display for GroupContext<T> {
 }
 
 pub struct GroupContext<C> {
-    view_id: String,
-    pub configuration: Arc<GroupConfigurationRevision>,
+    pub view_id: String,
+    configuration: Arc<GroupConfigurationRevision>,
     configuration_content: PhantomData<C>,
     field_rev: Arc<FieldRevision>,
     groups_map: IndexMap<String, Group>,
@@ -69,7 +69,7 @@ where
             rows: vec![],
             filter_content: "".to_string(),
         };
-        let configuration = match reader.get_configuration(field_rev.clone()).await {
+        let configuration = match reader.get_configuration().await {
             None => {
                 let default_configuration = default_group_configuration(&field_rev);
                 writer
@@ -133,9 +133,10 @@ where
         }
     }
 
-    pub(crate) fn init_group_revs(
+    pub(crate) fn init_groups(
         &mut self,
         generated_groups: Vec<GeneratedGroup>,
+        reset: bool,
     ) -> FlowyResult<Option<GroupViewChangesetPB>> {
         let mut new_groups = vec![];
         let mut filter_content_map = HashMap::new();
@@ -149,7 +150,11 @@ where
             new_group_revs,
             updated_group_revs: _,
             deleted_group_revs,
-        } = merge_groups(&self.configuration.groups, new_groups);
+        } = if reset {
+            merge_groups(&[], new_groups)
+        } else {
+            merge_groups(&self.configuration.groups, new_groups)
+        };
 
         let deleted_group_ids = deleted_group_revs
             .into_iter()
@@ -180,7 +185,7 @@ where
                         group_rev.update_with_other(&old_group);
 
                         // Take the GroupRevision if the name has changed
-                        if is_group_changed(&group_rev, &old_group) {
+                        if is_group_changed(group_rev, &old_group) {
                             old_group.name = group_rev.name.clone();
                             is_changed = true;
                             configuration.groups.insert(pos, old_group);

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

@@ -74,7 +74,7 @@ pub trait GroupControllerSharedOperation: Send + Sync {
 pub struct GenericGroupController<C, T, G, P> {
     pub field_id: String,
     pub type_option: Option<T>,
-    pub configuration: GroupContext<C>,
+    pub group_ctx: GroupContext<C>,
     group_action_phantom: PhantomData<G>,
     cell_parser_phantom: PhantomData<P>,
 }
@@ -89,12 +89,12 @@ 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_group_revs(groups)?;
+        let _ = configuration.init_groups(groups, false)?;
 
         Ok(Self {
             field_id: field_rev.id.clone(),
             type_option,
-            configuration,
+            group_ctx: configuration,
             group_action_phantom: PhantomData,
             cell_parser_phantom: PhantomData,
         })
@@ -107,7 +107,7 @@ where
         row_rev: &RowRevision,
         other_group_changesets: &[GroupChangesetPB],
     ) -> GroupChangesetPB {
-        let default_group = self.configuration.get_mut_default_group();
+        let default_group = self.group_ctx.get_mut_default_group();
 
         // [other_group_inserted_row] contains all the inserted rows except the default group.
         let other_group_inserted_row = other_group_changesets
@@ -182,11 +182,11 @@ where
     }
 
     fn groups(&self) -> Vec<Group> {
-        self.configuration.clone_groups()
+        self.group_ctx.clone_groups()
     }
 
     fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
-        let group = self.configuration.get_group(group_id)?;
+        let group = self.group_ctx.get_group(group_id)?;
         Some((group.0, group.1.clone()))
     }
 
@@ -197,7 +197,7 @@ where
                 let mut grouped_rows: Vec<GroupedRow> = vec![];
                 let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
                 let cell_data = cell_bytes.parser::<P>()?;
-                for group in self.configuration.concrete_groups() {
+                for group in self.group_ctx.concrete_groups() {
                     if self.can_group(&group.filter_content, &cell_data) {
                         grouped_rows.push(GroupedRow {
                             row: row_rev.into(),
@@ -207,25 +207,25 @@ where
                 }
 
                 if grouped_rows.is_empty() {
-                    self.configuration.get_mut_default_group().add_row(row_rev.into());
+                    self.group_ctx.get_mut_default_group().add_row(row_rev.into());
                 } else {
                     for group_row in grouped_rows {
-                        if let Some(group) = self.configuration.get_mut_group(&group_row.group_id) {
+                        if let Some(group) = self.group_ctx.get_mut_group(&group_row.group_id) {
                             group.add_row(group_row.row);
                         }
                     }
                 }
             } else {
-                self.configuration.get_mut_default_group().add_row(row_rev.into());
+                self.group_ctx.get_mut_default_group().add_row(row_rev.into());
             }
         }
 
-        tracing::Span::current().record("group_result", &format!("{},", self.configuration,).as_str());
+        tracing::Span::current().record("group_result", &format!("{},", self.group_ctx,).as_str());
         Ok(())
     }
 
     fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
-        self.configuration.move_group(from_group_id, to_group_id)
+        self.group_ctx.move_group(from_group_id, to_group_id)
     }
 
     fn did_update_row(
@@ -273,10 +273,9 @@ where
     }
 
     fn did_update_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
-        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, &self.configuration, &type_option);
-        let changeset = self.configuration.init_group_revs(groups)?;
+        let type_option = field_rev.get_type_option::<T>(field_rev.ty);
+        let groups = G::generate_groups(&field_rev.id, &self.group_ctx, &type_option);
+        let changeset = self.group_ctx.init_groups(groups, false)?;
         Ok(changeset)
     }
 }

+ 3 - 4
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs

@@ -5,7 +5,6 @@ use crate::services::group::configuration::GroupContext;
 use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
-use crate::services::group::entities::Group;
 
 use crate::services::group::GeneratedGroup;
 use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision};
@@ -54,9 +53,9 @@ impl GroupGenerator for CheckboxGroupGenerator {
     type TypeOptionType = CheckboxTypeOptionPB;
 
     fn generate_groups(
-        field_id: &str,
-        group_ctx: &Self::Context,
-        type_option: &Option<Self::TypeOptionType>,
+        _field_id: &str,
+        _group_ctx: &Self::Context,
+        _type_option: &Option<Self::TypeOptionType>,
     ) -> Vec<GeneratedGroup> {
         let check_group = GeneratedGroup {
             group_rev: GroupRevision::new("true".to_string(), CHECK.to_string()),

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

@@ -35,46 +35,46 @@ impl GroupControllerSharedOperation for DefaultGroupController {
         vec![self.group.clone()]
     }
 
-    fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
+    fn get_group(&self, _group_id: &str) -> Option<(usize, Group)> {
         Some((0, self.group.clone()))
     }
 
-    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], _field_rev: &FieldRevision) -> FlowyResult<()> {
         row_revs.iter().for_each(|row_rev| {
             self.group.add_row(RowPB::from(row_rev));
         });
         Ok(())
     }
 
-    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
+    fn move_group(&mut self, _from_group_id: &str, _to_group_id: &str) -> FlowyResult<()> {
         Ok(())
     }
 
     fn did_update_row(
         &mut self,
-        row_rev: &RowRevision,
-        field_rev: &FieldRevision,
+        _row_rev: &RowRevision,
+        _field_rev: &FieldRevision,
     ) -> FlowyResult<Vec<GroupChangesetPB>> {
         todo!()
     }
 
     fn did_delete_row(
         &mut self,
-        row_rev: &RowRevision,
-        field_rev: &FieldRevision,
+        _row_rev: &RowRevision,
+        _field_rev: &FieldRevision,
     ) -> FlowyResult<Vec<GroupChangesetPB>> {
         todo!()
     }
 
-    fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupChangesetPB>> {
+    fn move_group_row(&mut self, _context: MoveGroupRowContext) -> FlowyResult<Vec<GroupChangesetPB>> {
         todo!()
     }
 
-    fn did_update_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
+    fn did_update_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
         Ok(None)
     }
 }
 
 impl GroupController for DefaultGroupController {
-    fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {}
+    fn will_create_row(&mut self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {}
 }

+ 6 - 8
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs

@@ -7,11 +7,9 @@ use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
 use crate::services::group::controller_impls::select_option_controller::util::*;
-use crate::services::group::entities::Group;
+
 use crate::services::group::GeneratedGroup;
-use flowy_grid_data_model::revision::{
-    FieldRevision, GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision,
-};
+use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
 
 // MultiSelect
 pub type MultiSelectGroupController = GenericGroupController<
@@ -30,7 +28,7 @@ impl GroupAction for MultiSelectGroupController {
 
     fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -40,7 +38,7 @@ impl GroupAction for MultiSelectGroupController {
 
     fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -50,7 +48,7 @@ impl GroupAction for MultiSelectGroupController {
 
     fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) {
                 group_changeset.push(changeset);
             }
@@ -61,7 +59,7 @@ impl GroupAction for MultiSelectGroupController {
 
 impl GroupController for MultiSelectGroupController {
     fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
-        match self.configuration.get_group(group_id) {
+        match self.group_ctx.get_group(group_id) {
             None => tracing::warn!("Can not find the group: {}", group_id),
             Some((_, group)) => {
                 let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);

+ 5 - 7
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs

@@ -10,9 +10,7 @@ use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
 
 use crate::services::group::GeneratedGroup;
-use flowy_grid_data_model::revision::{
-    FieldRevision, GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision,
-};
+use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
 
 // SingleSelect
 pub type SingleSelectGroupController = GenericGroupController<
@@ -30,7 +28,7 @@ impl GroupAction for SingleSelectGroupController {
 
     fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -40,7 +38,7 @@ impl GroupAction for SingleSelectGroupController {
 
     fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -50,7 +48,7 @@ impl GroupAction for SingleSelectGroupController {
 
     fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
-        self.configuration.iter_mut_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) {
                 group_changeset.push(changeset);
             }
@@ -61,7 +59,7 @@ impl GroupAction for SingleSelectGroupController {
 
 impl GroupController for SingleSelectGroupController {
     fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
-        let group: Option<&mut Group> = self.configuration.get_mut_group(group_id);
+        let group: Option<&mut Group> = self.group_ctx.get_mut_group(group_id);
         match group {
             None => {}
             Some(group) => {

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs

@@ -127,8 +127,8 @@ pub fn move_select_option_row(
 }
 
 pub fn generate_select_option_groups(
-    field_id: &str,
-    group_ctx: &SelectOptionGroupContext,
+    _field_id: &str,
+    _group_ctx: &SelectOptionGroupContext,
     options: &[SelectOptionPB],
 ) -> Vec<GeneratedGroup> {
     let groups = options

+ 4 - 3
frontend/rust-lib/flowy-grid/src/services/group/group_util.rs

@@ -16,7 +16,7 @@ use std::sync::Arc;
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub async fn make_group_controller<R, W>(
     view_id: String,
-    field_revs: Vec<Arc<FieldRevision>>,
+    field_rev: Arc<FieldRevision>,
     row_revs: Vec<Arc<RowRevision>>,
     configuration_reader: R,
     configuration_writer: W,
@@ -25,11 +25,12 @@ where
     R: GroupConfigurationReader,
     W: GroupConfigurationWriter,
 {
-    let field_rev = find_group_field(&field_revs).unwrap();
     let field_type: FieldType = field_rev.ty.into();
+
     let mut group_controller: Box<dyn GroupController>;
     let configuration_reader = Arc::new(configuration_reader);
     let configuration_writer = Arc::new(configuration_writer);
+
     match field_type {
         FieldType::SingleSelect => {
             let configuration =
@@ -61,7 +62,7 @@ where
     Ok(group_controller)
 }
 
-fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevision>> {
+pub fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevision>> {
     let field_rev = field_revs
         .iter()
         .find(|field_rev| {

+ 7 - 0
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -195,6 +195,8 @@ fn make_test_grid() -> BuildGridContext {
                         FieldType::SingleSelect => {
                             row_builder.insert_single_select_cell(|mut options| options.remove(0))
                         }
+                        FieldType::MultiSelect => row_builder
+                            .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]),
                         FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
                         _ => "".to_owned(),
                     };
@@ -209,6 +211,8 @@ fn make_test_grid() -> BuildGridContext {
                         FieldType::SingleSelect => {
                             row_builder.insert_single_select_cell(|mut options| options.remove(0))
                         }
+                        FieldType::MultiSelect => row_builder
+                            .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]),
                         FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
                         _ => "".to_owned(),
                     };
@@ -223,6 +227,9 @@ fn make_test_grid() -> BuildGridContext {
                         FieldType::SingleSelect => {
                             row_builder.insert_single_select_cell(|mut options| options.remove(1))
                         }
+                        FieldType::MultiSelect => {
+                            row_builder.insert_multi_select_cell(|mut options| vec![options.remove(0)])
+                        }
                         FieldType::Checkbox => row_builder.insert_checkbox_cell("false"),
                         _ => "".to_owned(),
                     };

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

@@ -1,13 +1,13 @@
 use crate::grid::grid_editor::GridEditorTest;
 use flowy_grid::entities::{
-    CreateRowParams, FieldChangesetParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB,
+    CreateRowParams, FieldType, GridLayout, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB,
 };
 use flowy_grid::services::cell::{delete_select_option_cell, insert_select_option_cell};
-use flowy_grid::services::field::{select_option_operation, SelectOptionOperation};
+use flowy_grid::services::field::{
+    edit_single_select_type_option, SelectOptionOperation, SelectOptionPB, SingleSelectTypeOptionPB,
+};
 use flowy_grid_data_model::revision::{FieldRevision, RowChangeset};
 use std::sync::Arc;
-use std::time::Duration;
-use tokio::time::interval;
 
 pub enum GroupScript {
     AssertGroupRowCount {
@@ -46,10 +46,10 @@ pub enum GroupScript {
         from_group_index: usize,
         to_group_index: usize,
     },
-    UpdateField {
-        changeset: FieldChangesetParams,
+    UpdateSingleSelectOption {
+        inserted_options: Vec<SelectOptionPB>,
     },
-    GroupField {
+    GroupByField {
         field_id: String,
     },
 }
@@ -179,12 +179,15 @@ impl GridGroupTest {
                 assert_eq!(group.group_id, group_pb.group_id);
                 assert_eq!(group.desc, group_pb.desc);
             }
-            GroupScript::UpdateField { changeset } => {
-                self.editor.update_field(changeset).await.unwrap();
-                let mut interval = interval(Duration::from_millis(130));
-                interval.tick().await;
+            GroupScript::UpdateSingleSelectOption { inserted_options } => {
+                self.edit_single_select_type_option(|type_option| {
+                    for inserted_option in inserted_options {
+                        type_option.insert_option(inserted_option);
+                    }
+                })
+                .await;
             }
-            GroupScript::GroupField { field_id } => {
+            GroupScript::GroupByField { field_id } => {
                 self.editor.group_field(&field_id).await.unwrap();
             }
         }
@@ -200,6 +203,7 @@ impl GridGroupTest {
         groups.rows.get(row_index).unwrap().clone()
     }
 
+    #[allow(dead_code)]
     pub async fn get_multi_select_field(&self) -> Arc<FieldRevision> {
         let field = self
             .inner
@@ -211,7 +215,7 @@ impl GridGroupTest {
             })
             .unwrap()
             .clone();
-        return field;
+        field
     }
 
     pub async fn get_single_select_field(&self) -> Arc<FieldRevision> {
@@ -226,14 +230,11 @@ impl GridGroupTest {
             .clone()
     }
 
-    pub async fn edit_single_select_type_option(&self, f: impl FnOnce(Box<dyn SelectOptionOperation>)) {
+    pub async fn edit_single_select_type_option(&self, action: impl FnOnce(&mut SingleSelectTypeOptionPB)) {
         let single_select = self.get_single_select_field().await;
-        let mut field_rev = self.editor.get_field_rev(&single_select.id).await.unwrap();
-        let mut_field_rev = Arc::make_mut(&mut field_rev);
-        let mut type_option = select_option_operation(mut_field_rev)?;
-        f(type_option);
-        mut_field_rev.insert_type_option(&*type_option);
-        let _ = self.editor.replace_field(field_rev).await?;
+        edit_single_select_type_option(&single_select.id, self.editor.clone(), action)
+            .await
+            .unwrap();
     }
 }
 

+ 32 - 22
frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs

@@ -1,6 +1,7 @@
 use crate::grid::group_test::script::GridGroupTest;
 use crate::grid::group_test::script::GroupScript::*;
-use flowy_grid::entities::FieldChangesetParams;
+
+use flowy_grid::services::field::SelectOptionPB;
 
 #[tokio::test]
 async fn group_init_test() {
@@ -370,32 +371,41 @@ async fn group_move_group_test() {
 }
 
 #[tokio::test]
-async fn group_update_field_test() {
+async fn group_insert_single_select_option_test() {
     let mut test = GridGroupTest::new().await;
-    let group = test.group_at_index(0).await;
-    let changeset = FieldChangesetParams {
-        field_id: group.field_id.clone(),
-        grid_id: test.grid_id.clone(),
-        name: Some("ABC".to_string()),
-        ..Default::default()
-    };
+    let new_option_name = "New option";
+    let scripts = vec![
+        AssertGroupCount(4),
+        UpdateSingleSelectOption {
+            inserted_options: vec![SelectOptionPB::new(new_option_name)],
+        },
+        AssertGroupCount(5),
+    ];
+    test.run_scripts(scripts).await;
+
+    // the group at index 4 is the default_group, so the new insert group will be the
+    // index 3.
+    let group_3 = test.group_at_index(3).await;
+    assert_eq!(group_3.desc, new_option_name);
+}
 
-    // group.desc = "ABC".to_string();
+#[tokio::test]
+async fn group_group_by_other_field() {
+    let mut test = GridGroupTest::new().await;
+    let multi_select_field = test.get_multi_select_field().await;
     let scripts = vec![
-        UpdateField { changeset },
-        AssertGroup {
+        GroupByField {
+            field_id: multi_select_field.id.clone(),
+        },
+        AssertGroupRowCount {
             group_index: 0,
-            expected_group: group,
+            row_count: 3,
+        },
+        AssertGroupRowCount {
+            group_index: 1,
+            row_count: 2,
         },
+        AssertGroupCount(4),
     ];
     test.run_scripts(scripts).await;
 }
-
-// #[tokio::test]
-// async fn group_multi_select_field_test() {
-//     let mut test = GridGroupTest::new().await;
-//     let multi_select_field = test.get_multi_select_field().await;
-//
-//     let scripts = vec![];
-//     test.run_scripts(scripts).await;
-// }

+ 9 - 1
shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs

@@ -60,7 +60,7 @@ where
             .cloned()
     }
 
-    pub fn get_all_objects(&self, field_revs: &[Arc<FieldRevision>]) -> Option<HashMap<String, Vec<Arc<T>>>> {
+    pub fn get_objects_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<HashMap<String, Vec<Arc<T>>>> {
         // Get the objects according to the FieldType, so we need iterate the field_revs.
         let objects_by_field_id = field_revs
             .iter()
@@ -76,6 +76,10 @@ where
         Some(objects_by_field_id)
     }
 
+    pub fn get_all_objects(&self) -> Vec<Arc<T>> {
+        self.inner.values().map(|map| map.all_objects()).flatten().collect()
+    }
+
     /// add object to the end of the list
     pub fn add_object(&mut self, field_id: &str, field_type: &FieldTypeRevision, object: T) {
         let object_rev_map = self
@@ -111,6 +115,10 @@ where
     pub fn new() -> Self {
         ObjectIndexMap::default()
     }
+
+    pub fn all_objects(&self) -> Vec<Arc<T>> {
+        self.object_by_field_type.values().cloned().flatten().collect()
+    }
 }
 
 impl<T> std::ops::Deref for ObjectIndexMap<T>

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

@@ -48,8 +48,12 @@ impl GridViewRevisionPad {
         Self::from_delta(delta)
     }
 
-    pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
-        self.groups.get_all_objects(field_revs)
+    pub fn get_groups_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
+        self.groups.get_objects_by_field_revs(field_revs)
+    }
+
+    pub fn get_all_groups(&self) -> Vec<Arc<GroupConfigurationRevision>> {
+        self.groups.get_all_objects()
     }
 
     #[tracing::instrument(level = "trace", skip_all, err)]
@@ -111,7 +115,7 @@ impl GridViewRevisionPad {
     }
 
     pub fn get_all_filters(&self, field_revs: &[Arc<FieldRevision>]) -> Option<FilterConfigurationsByFieldId> {
-        self.filters.get_all_objects(field_revs)
+        self.filters.get_objects_by_field_revs(field_revs)
     }
 
     pub fn get_filters(

+ 1 - 1
shared-lib/lib-ot/src/core/document/document_operation.rs

@@ -178,7 +178,7 @@ mod tests {
                 node_type: "text".into(),
                 attributes: NodeAttributes::new(),
                 delta: None,
-                children: vec![Box::new(NodeSubTree::new("text".into()))],
+                children: vec![Box::new(NodeSubTree::new("text"))],
             })],
         };
         let result = serde_json::to_string(&insert).unwrap();

+ 1 - 0
shared-lib/lib-ot/src/core/document/mod.rs

@@ -1,3 +1,4 @@
+#![allow(clippy::module_inception)]
 mod attributes;
 mod document;
 mod document_operation;

+ 15 - 15
shared-lib/lib-ot/tests/main.rs

@@ -13,7 +13,7 @@ fn test_documents() {
     let mut document = DocumentTree::new();
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![0].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![0].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     document.apply(transaction).unwrap();
@@ -47,16 +47,16 @@ fn test_inserts_nodes() {
     let mut document = DocumentTree::new();
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![0].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![1].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![2].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![0].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![1].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![2].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     document.apply(transaction).unwrap();
 
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![1].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![1].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     document.apply(transaction).unwrap();
@@ -69,11 +69,11 @@ fn test_inserts_subtrees() {
         let mut tb = TransactionBuilder::new(&document);
         tb.insert_nodes_at_path(
             &vec![0].into(),
-            &vec![Box::new(NodeSubTree {
+            &[Box::new(NodeSubTree {
                 node_type: "text".into(),
                 attributes: NodeAttributes::new(),
                 delta: None,
-                children: vec![Box::new(NodeSubTree::new("image".into()))],
+                children: vec![Box::new(NodeSubTree::new("image"))],
             })],
         );
         tb.finalize()
@@ -90,9 +90,9 @@ fn test_update_nodes() {
     let mut document = DocumentTree::new();
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![0].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![1].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![2].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![0].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![1].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![2].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     document.apply(transaction).unwrap();
@@ -115,9 +115,9 @@ fn test_delete_nodes() {
     let mut document = DocumentTree::new();
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![0].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![1].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![2].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![0].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![1].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![2].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     document.apply(transaction).unwrap();
@@ -138,8 +138,8 @@ fn test_errors() {
     let mut document = DocumentTree::new();
     let transaction = {
         let mut tb = TransactionBuilder::new(&document);
-        tb.insert_nodes_at_path(&vec![0].into(), &vec![Box::new(NodeSubTree::new("text"))]);
-        tb.insert_nodes_at_path(&vec![100].into(), &vec![Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![0].into(), &[Box::new(NodeSubTree::new("text"))]);
+        tb.insert_nodes_at_path(&vec![100].into(), &[Box::new(NodeSubTree::new("text"))]);
         tb.finalize()
     };
     let result = document.apply(transaction);