Ver código fonte

fix: save group rev without apply change

appflowy 2 anos atrás
pai
commit
93f5b5d754

+ 1 - 0
frontend/rust-lib/flowy-error/src/errors.rs

@@ -66,6 +66,7 @@ impl FlowyError {
     static_flowy_error!(user_not_exist, ErrorCode::UserNotExist);
     static_flowy_error!(text_too_long, ErrorCode::TextTooLong);
     static_flowy_error!(invalid_data, ErrorCode::InvalidData);
+    static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds);
 }
 
 impl std::convert::From<ErrorCode> for FlowyError {

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

@@ -109,10 +109,19 @@ pub struct GroupViewChangesetPB {
     pub view_id: String,
 
     #[pb(index = 2)]
-    pub inserted_groups: Vec<GroupPB>,
+    pub inserted_groups: Vec<InsertedGroupPB>,
 
     #[pb(index = 3)]
     pub deleted_groups: Vec<String>,
 }
 
 impl GroupViewChangesetPB {}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct InsertedGroupPB {
+    #[pb(index = 1)]
+    pub group: GroupPB,
+
+    #[pb(index = 2)]
+    pub index: i32,
+}

+ 79 - 27
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -1,14 +1,12 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::{
     CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridLayout, GridLayoutPB,
-    GridSettingChangesetParams, GridSettingPB, GroupPB, GroupRowsChangesetPB, GroupViewChangesetPB, InsertedRowPB,
+    GridSettingPB, GroupPB, GroupRowsChangesetPB, GroupViewChangesetPB, InsertedGroupPB, InsertedRowPB,
     MoveGroupParams, RepeatedGridConfigurationFilterPB, RepeatedGridGroupConfigurationPB, RowPB,
 };
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
-use crate::services::group::{
-    default_group_configuration, GroupConfigurationReader, GroupConfigurationWriter, GroupService,
-};
+use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter, GroupService};
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
     gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision,
@@ -19,6 +17,7 @@ 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::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use tokio::sync::RwLock;
@@ -37,6 +36,7 @@ pub struct GridViewRevisionEditor {
 }
 
 impl GridViewRevisionEditor {
+    #[tracing::instrument(level = "trace", skip_all, err)]
     pub(crate) async fn new(
         user_id: &str,
         token: &str,
@@ -109,7 +109,7 @@ impl GridViewRevisionEditor {
         // Send the group notification if the current view has groups;
         if let Some(changesets) = self
             .group_service
-            .write()
+            .read()
             .await
             .did_delete_row(row_rev, |field_id| self.field_delegate.get_field_rev(&field_id))
             .await
@@ -123,7 +123,7 @@ impl GridViewRevisionEditor {
     pub(crate) async fn did_update_row(&self, row_rev: &RowRevision) {
         if let Some(changesets) = self
             .group_service
-            .write()
+            .read()
             .await
             .did_update_row(row_rev, |field_id| self.field_delegate.get_field_rev(&field_id))
             .await
@@ -142,7 +142,7 @@ impl GridViewRevisionEditor {
     ) {
         if let Some(changesets) = self
             .group_service
-            .write()
+            .read()
             .await
             .did_move_row(row_rev, row_changeset, upper_row_id, |field_id| {
                 self.field_delegate.get_field_rev(&field_id)
@@ -156,11 +156,13 @@ impl GridViewRevisionEditor {
         }
     }
 
+    #[tracing::instrument(level = "trace", skip(self))]
     pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
         let groups = if !self.did_load_group.load(Ordering::SeqCst) {
             self.did_load_group.store(true, Ordering::SeqCst);
             let field_revs = self.field_delegate.get_field_revs().await;
             let row_revs = self.row_delegate.gv_row_revs().await;
+
             match self
                 .group_service
                 .write()
@@ -174,11 +176,37 @@ impl GridViewRevisionEditor {
         } else {
             self.group_service.read().await.groups().await
         };
+
+        tracing::trace!("Number of groups: {}", groups.len());
         Ok(groups.into_iter().map(GroupPB::from).collect())
     }
 
     pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
-        todo!()
+        let _ = self
+            .group_service
+            .read()
+            .await
+            .move_group(&params.from_group_id, &params.to_group_id)
+            .await?;
+
+        match self.group_service.read().await.get_group(&params.from_group_id).await {
+            None => {}
+            Some((index, group)) => {
+                let inserted_group = InsertedGroupPB {
+                    group: GroupPB::from(group),
+                    index: index as i32,
+                };
+
+                let changeset = GroupViewChangesetPB {
+                    view_id: "".to_string(),
+                    inserted_groups: vec![inserted_group],
+                    deleted_groups: vec![params.from_group_id.clone()],
+                };
+
+                self.notify_did_update_view(changeset).await;
+            }
+        }
+        Ok(())
     }
 
     pub(crate) async fn get_setting(&self) -> GridSettingPB {
@@ -291,17 +319,19 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
 struct GroupConfigurationReaderImpl(Arc<RwLock<GridViewRevisionPad>>);
 
 impl GroupConfigurationReader for GroupConfigurationReaderImpl {
-    fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Arc<GroupConfigurationRevision>> {
+    fn get_group_configuration(
+        &self,
+        field_rev: Arc<FieldRevision>,
+    ) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
         let view_pad = self.0.clone();
         wrap_future(async move {
-            let view_pad = view_pad.read().await;
-            let configurations = view_pad.get_groups(&field_rev.id, &field_rev.ty);
-            match configurations {
-                None => {
-                    let default_configuration = default_group_configuration(&field_rev);
-                    Arc::new(default_configuration)
-                }
-                Some(configuration) => configuration,
+            let mut groups = view_pad.read().await.groups.get_objects(&field_rev.id, &field_rev.ty)?;
+
+            if groups.is_empty() {
+                None
+            } else {
+                debug_assert_eq!(groups.len(), 1);
+                Some(groups.pop().unwrap())
             }
         })
     }
@@ -328,17 +358,25 @@ impl GroupConfigurationWriter for GroupConfigurationWriterImpl {
         let field_id = field_id.to_owned();
 
         wrap_future(async move {
-            match view_pad.write().await.get_mut_group(
-                &field_id,
-                &field_type,
-                &configuration_id,
-                |group_configuration| {
-                    group_configuration.content = content;
-                },
-            )? {
-                None => Ok(()),
-                Some(changeset) => apply_change(&user_id, rev_manager, changeset).await,
+            let is_contained = view_pad.read().await.contains_group(&field_id, &field_type);
+            let changeset = if is_contained {
+                view_pad.write().await.with_mut_group(
+                    &field_id,
+                    &field_type,
+                    &configuration_id,
+                    |group_configuration| {
+                        group_configuration.content = content;
+                    },
+                )?
+            } else {
+                let group_rev = GroupConfigurationRevision::new(field_id.clone(), field_type, content)?;
+                view_pad.write().await.insert_group(&field_id, &field_type, group_rev)?
+            };
+
+            if let Some(changeset) = changeset {
+                let _ = apply_change(&user_id, rev_manager, changeset).await?;
             }
+            Ok(())
         })
     }
 }
@@ -371,3 +409,17 @@ pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<Field
         group_configuration_by_field_id: groups_by_field_id,
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use lib_ot::core::TextDelta;
+
+    #[test]
+    fn test() {
+        let s1 = r#"[{"insert":"{\"view_id\":\"fTURELffPr\",\"grid_id\":\"fTURELffPr\",\"layout\":0,\"filters\":[],\"groups\":[]}"}]"#;
+        let _delta_1 = TextDelta::from_json(s1).unwrap();
+
+        let s2 = r#"[{"retain":195},{"insert":"{\\\"group_id\\\":\\\"wD9i\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"xZtv\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"tFV2\\\",\\\"visible\\\":true}"},{"retain":10}]"#;
+        let _delta_2 = TextDelta::from_json(s2).unwrap();
+    }
+}

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

@@ -1,6 +1,6 @@
 use crate::entities::{
-    CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridSettingChangesetParams,
-    GridSettingPB, MoveGroupParams, RepeatedGridGroupPB, RowPB,
+    CreateFilterParams, CreateRowParams, DeleteFilterParams, GridFilterConfiguration, GridSettingPB, MoveGroupParams,
+    RepeatedGridGroupPB, RowPB,
 };
 use crate::manager::GridUser;
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
@@ -122,7 +122,7 @@ impl GridViewManager {
 
     pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
         let view_editor = self.get_default_view_editor().await?;
-        let _s = view_editor.move_group(params).await?;
+        let _ = view_editor.move_group(params).await?;
         Ok(())
     }
 
@@ -175,12 +175,11 @@ async fn make_view_editor(
     row_delegate: Arc<dyn GridViewRowDelegate>,
     scheduler: Arc<dyn GridServiceTaskScheduler>,
 ) -> FlowyResult<GridViewRevisionEditor> {
-    tracing::trace!("Open view:{} editor", view_id);
-
     let rev_manager = make_grid_view_rev_manager(user, view_id).await?;
     let user_id = user.user_id()?;
     let token = user.token()?;
     let view_id = view_id.to_owned();
+
     GridViewRevisionEditor::new(
         &user_id,
         &token,
@@ -194,7 +193,6 @@ async fn make_view_editor(
 }
 
 pub async fn make_grid_view_rev_manager(user: &Arc<dyn GridUser>, view_id: &str) -> FlowyResult<RevisionManager> {
-    tracing::trace!("Open view:{} editor", view_id);
     let user_id = user.user_id()?;
     let pool = user.db_pool()?;
 

+ 52 - 15
frontend/rust-lib/flowy-grid/src/services/group/configuration.rs

@@ -1,5 +1,5 @@
-use crate::services::group::Group;
-use flowy_error::FlowyResult;
+use crate::services::group::{default_group_configuration, Group};
+use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
     FieldRevision, FieldTypeRevision, GroupConfigurationContent, GroupConfigurationRevision, GroupRecordRevision,
 };
@@ -9,7 +9,10 @@ use lib_infra::future::AFFuture;
 use std::sync::Arc;
 
 pub trait GroupConfigurationReader: Send + Sync + 'static {
-    fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Arc<GroupConfigurationRevision>>;
+    fn get_group_configuration(
+        &self,
+        field_rev: Arc<FieldRevision>,
+    ) -> AFFuture<Option<Arc<GroupConfigurationRevision>>>;
 }
 
 pub trait GroupConfigurationWriter: Send + Sync + 'static {
@@ -39,7 +42,22 @@ where
         reader: Arc<dyn GroupConfigurationReader>,
         writer: Arc<dyn GroupConfigurationWriter>,
     ) -> FlowyResult<Self> {
-        let configuration_rev = reader.get_group_configuration(field_rev.clone()).await;
+        let configuration_rev = match reader.get_group_configuration(field_rev.clone()).await {
+            None => {
+                let default_group_configuration = default_group_configuration(&field_rev);
+                writer
+                    .save_group_configuration(
+                        &field_rev.id,
+                        field_rev.ty,
+                        &default_group_configuration.id,
+                        default_group_configuration.content.clone(),
+                    )
+                    .await?;
+                Arc::new(default_group_configuration)
+            }
+            Some(configuration) => configuration,
+        };
+
         let configuration_id = configuration_rev.id.clone();
         let configuration = C::from_configuration_content(&configuration_rev.content)?;
         Ok(Self {
@@ -63,6 +81,8 @@ where
         let (group_revs, groups) = merge_groups(self.configuration.get_groups(), groups);
         self.configuration.set_groups(group_revs);
         let _ = self.save_configuration().await?;
+
+        tracing::trace!("merge new groups: {}", groups.len());
         groups.into_iter().for_each(|group| {
             self.groups_map.insert(group.id.clone(), group);
         });
@@ -97,8 +117,25 @@ where
         self.groups_map.get_mut(group_id)
     }
 
-    pub(crate) fn get_group(&mut self, group_id: &str) -> Option<&Group> {
-        self.groups_map.get(group_id)
+    pub(crate) fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
+        let from_group_index = self.groups_map.get_index_of(from_group_id);
+        let to_group_index = self.groups_map.get_index_of(to_group_id);
+        match (from_group_index, to_group_index) {
+            (Some(from_index), Some(to_index)) => {
+                self.groups_map.swap_indices(from_index, to_index);
+                self.configuration.swap_group(from_group_id, to_group_id);
+                Ok(())
+            }
+            _ => Err(FlowyError::out_of_bounds()),
+        }
+    }
+
+    // Returns the index and group specified by the group_id
+    pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> {
+        match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) {
+            (Some(index), Some(group)) => Some((index, group)),
+            _ => None,
+        }
     }
 
     pub async fn save_configuration(&self) -> FlowyResult<()> {
@@ -111,17 +148,17 @@ where
     }
 }
 
-impl<T> GroupConfigurationReader for Arc<T>
-where
-    T: GroupConfigurationReader,
-{
-    fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Arc<GroupConfigurationRevision>> {
-        (**self).get_group_configuration(field_rev)
-    }
-}
+// impl<T> GroupConfigurationReader for Arc<T>
+// where
+//     T: GroupConfigurationReader,
+// {
+//     fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<Arc<GroupConfigurationRevision>> {
+//         (**self).get_group_configuration(field_rev)
+//     }
+// }
 
 fn merge_groups(old_group_revs: &[GroupRecordRevision], groups: Vec<Group>) -> (Vec<GroupRecordRevision>, Vec<Group>) {
-    // tracing::trace!("Merge group: old: {}, new: {}", old_group.len(), groups.len());
+    tracing::trace!("Merge group: old: {}, new: {}", old_group_revs.len(), groups.len());
     if old_group_revs.is_empty() {
         let new_groups = groups
             .iter()

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

@@ -36,7 +36,9 @@ pub trait GroupControllerSharedOperation: Send + Sync {
     // The field that is used for grouping the rows
     fn field_id(&self) -> &str;
     fn groups(&self) -> Vec<Group>;
+    fn get_group(&self, group_id: &str) -> Option<(usize, Group)>;
     fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>>;
+    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
     fn did_update_row(
         &mut self,
         row_rev: &RowRevision,
@@ -118,6 +120,11 @@ where
         self.configuration.clone_groups()
     }
 
+    fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
+        let group = self.configuration.get_group(group_id)?;
+        Some((group.0, group.1.clone()))
+    }
+
     fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<Vec<Group>> {
         for row_rev in row_revs {
             if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
@@ -156,6 +163,10 @@ where
         Ok(groups)
     }
 
+    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
+        self.configuration.move_group(from_group_id, to_group_id)
+    }
+
     fn did_update_row(
         &mut self,
         row_rev: &RowRevision,

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

@@ -71,10 +71,9 @@ impl GroupAction for MultiSelectGroupController {
 
 impl GroupController for MultiSelectGroupController {
     fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) {
-        let group: Option<&Group> = self.configuration.get_group(group_id);
-        match group {
+        match self.configuration.get_group(group_id) {
             None => tracing::warn!("Can not find the group: {}", group_id),
-            Some(group) => {
+            Some((_, group)) => {
                 let cell_rev = insert_select_option_cell(group.id.clone(), field_rev);
                 row_rev.cells.insert(field_rev.id.clone(), cell_rev);
             }

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

@@ -43,6 +43,14 @@ impl GroupService {
         }
     }
 
+    pub(crate) async fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
+        if let Some(group_controller) = self.group_controller.as_ref() {
+            group_controller.read().await.get_group(group_id)
+        } else {
+            None
+        }
+    }
+
     pub(crate) async fn load_groups(
         &mut self,
         field_revs: &[Arc<FieldRevision>],
@@ -164,9 +172,20 @@ impl GroupService {
         }
     }
 
+    #[tracing::instrument(level = "trace", skip_all)]
+    pub(crate) async fn move_group(&self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()> {
+        match self.group_controller.as_ref() {
+            None => Ok(()),
+            Some(group_controller) => {
+                let _ = group_controller.write().await.move_group(from_group_id, to_group_id)?;
+                Ok(())
+            }
+        }
+    }
+
     #[tracing::instrument(level = "trace", skip_all)]
     async fn make_group_controller(
-        &mut self,
+        &self,
         field_type: &FieldType,
         field_rev: &Arc<FieldRevision>,
     ) -> FlowyResult<Option<Arc<RwLock<dyn GroupController>>>> {

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

@@ -4,11 +4,15 @@ use flowy_grid::services::cell::insert_select_option_cell;
 use flowy_grid_data_model::revision::RowChangeset;
 
 pub enum GroupScript {
-    AssertGroup {
+    AssertGroupRowCount {
         group_index: usize,
         row_count: usize,
     },
     AssertGroupCount(usize),
+    AssertGroup {
+        group_index: usize,
+        expected_group: GroupPB,
+    },
     AssertRow {
         group_index: usize,
         row_index: usize,
@@ -56,7 +60,7 @@ impl GridGroupTest {
 
     pub async fn run_script(&mut self, script: GroupScript) {
         match script {
-            GroupScript::AssertGroup { group_index, row_count } => {
+            GroupScript::AssertGroupRowCount { group_index, row_count } => {
                 assert_eq!(row_count, self.group_at_index(group_index).await.rows.len());
             }
             GroupScript::AssertGroupCount(count) => {
@@ -142,6 +146,13 @@ impl GridGroupTest {
                 self.editor.move_group(params).await.unwrap();
                 //
             }
+            GroupScript::AssertGroup {
+                group_index,
+                expected_group: group_pb,
+            } => {
+                let group = self.group_at_index(group_index).await;
+                assert_eq!(group.group_id, group_pb.group_id);
+            }
         }
     }
 

+ 36 - 14
frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs

@@ -6,15 +6,15 @@ async fn board_init_test() {
     let mut test = GridGroupTest::new().await;
     let scripts = vec![
         AssertGroupCount(3),
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 2,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 1,
             row_count: 2,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 2,
             row_count: 1,
         },
@@ -34,7 +34,7 @@ async fn board_move_row_test() {
             to_group_index: 0,
             to_row_index: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 2,
         },
@@ -58,11 +58,11 @@ async fn board_move_row_to_other_group_test() {
             to_group_index: 1,
             to_row_index: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 1,
             row_count: 3,
         },
@@ -106,13 +106,13 @@ async fn board_create_row_test() {
     let mut test = GridGroupTest::new().await;
     let scripts = vec![
         CreateRow { group_index: 0 },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 3,
         },
         CreateRow { group_index: 1 },
         CreateRow { group_index: 1 },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 1,
             row_count: 4,
         },
@@ -128,7 +128,7 @@ async fn board_delete_row_test() {
             group_index: 0,
             row_index: 0,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 1,
         },
@@ -148,7 +148,7 @@ async fn board_delete_all_row_test() {
             group_index: 0,
             row_index: 0,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 0,
         },
@@ -166,11 +166,11 @@ async fn board_update_row_test() {
             row_index: 0,
             to_group_index: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 1,
             row_count: 3,
         },
@@ -188,14 +188,36 @@ async fn board_reorder_group_test() {
             row_index: 0,
             to_group_index: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 0,
             row_count: 1,
         },
-        AssertGroup {
+        AssertGroupRowCount {
             group_index: 1,
             row_count: 3,
         },
     ];
     test.run_scripts(scripts).await;
 }
+
+#[tokio::test]
+async fn board_move_group_test() {
+    let mut test = GridGroupTest::new().await;
+    let group_0 = test.group_at_index(0).await;
+    let group_1 = test.group_at_index(1).await;
+    let scripts = vec![
+        MoveGroup {
+            from_group_index: 0,
+            to_group_index: 1,
+        },
+        AssertGroup {
+            group_index: 0,
+            expected_group: group_1,
+        },
+        AssertGroup {
+            group_index: 1,
+            expected_group: group_0,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}

+ 1 - 1
frontend/rust-lib/flowy-sdk/src/lib.rs

@@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String {
     filters.push(format!("lib_ot={}", level));
     filters.push(format!("lib_ws={}", level));
     filters.push(format!("lib_infra={}", level));
-    // filters.push(format!("flowy_sync={}", level));
+    filters.push(format!("flowy_sync={}", level));
     // filters.push(format!("flowy_revision={}", level));
     // filters.push(format!("lib_dispatch={}", level));
 

+ 3 - 0
shared-lib/flowy-error-code/src/code.rs

@@ -125,6 +125,9 @@ pub enum ErrorCode {
 
     #[display(fmt = "Invalid data")]
     InvalidData = 1000,
+
+    #[display(fmt = "Out of bounds")]
+    OutOfBounds = 10001,
 }
 
 impl ErrorCode {

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

@@ -49,7 +49,7 @@ where
             .get_mut(field_id)
             .and_then(|object_rev_map| object_rev_map.get_mut(field_type));
         if value.is_none() {
-            tracing::warn!("Can't find the {:?} with", std::any::type_name::<T>());
+            tracing::warn!("[Configuration] Can't find the {:?} with", std::any::type_name::<T>());
         }
         value
     }

+ 28 - 77
shared-lib/flowy-grid-data-model/src/revision/grid_view.rs

@@ -1,11 +1,7 @@
-use crate::revision::{
-    FieldRevision, FieldTypeRevision, FilterConfiguration, FilterConfigurationRevision, FilterConfigurationsByFieldId,
-    GroupConfiguration, GroupConfigurationRevision, GroupConfigurationsByFieldId,
-};
+use crate::revision::{FilterConfiguration, GroupConfiguration};
 use nanoid::nanoid;
 use serde::{Deserialize, Serialize};
 use serde_repr::*;
-use std::sync::Arc;
 
 #[allow(dead_code)]
 pub fn gen_grid_view_id() -> String {
@@ -40,15 +36,15 @@ pub struct GridViewRevision {
 
     pub layout: LayoutRevision,
 
+    #[serde(default)]
     pub filters: FilterConfiguration,
 
     #[serde(default)]
     pub groups: GroupConfiguration,
-
-    // For the moment, we just use the order returned from the GridRevision
-    #[allow(dead_code)]
-    #[serde(skip, rename = "row")]
-    pub row_orders: Vec<RowOrderRevision>,
+    // // For the moment, we just use the order returned from the GridRevision
+    // #[allow(dead_code)]
+    // #[serde(skip, rename = "rows")]
+    // pub row_orders: Vec<RowOrderRevision>,
 }
 
 impl GridViewRevision {
@@ -59,78 +55,33 @@ impl GridViewRevision {
             layout: Default::default(),
             filters: Default::default(),
             groups: Default::default(),
-            row_orders: vec![],
-        }
-    }
-
-    pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
-        self.groups.get_all_objects(field_revs)
-    }
-
-    pub fn get_groups(
-        &self,
-        field_id: &str,
-        field_type_rev: &FieldTypeRevision,
-    ) -> Option<Arc<GroupConfigurationRevision>> {
-        let mut groups = self.groups.get_objects(field_id, field_type_rev)?;
-        if groups.is_empty() {
-            debug_assert_eq!(groups.len(), 1);
-            Some(groups.pop().unwrap())
-        } else {
-            None
+            // row_orders: vec![],
         }
     }
-
-    pub fn get_mut_groups(
-        &mut self,
-        field_id: &str,
-        field_type: &FieldTypeRevision,
-    ) -> Option<&mut Vec<Arc<GroupConfigurationRevision>>> {
-        self.groups.get_mut_objects(field_id, field_type)
-    }
-
-    pub fn insert_group(
-        &mut self,
-        field_id: &str,
-        field_type: &FieldTypeRevision,
-        group_rev: GroupConfigurationRevision,
-    ) {
-        // only one group can be set
-        self.groups.remove_all();
-        self.groups.insert_object(field_id, field_type, group_rev);
-    }
-
-    pub fn get_all_filters(&self, field_revs: &[Arc<FieldRevision>]) -> Option<FilterConfigurationsByFieldId> {
-        self.filters.get_all_objects(field_revs)
-    }
-
-    pub fn get_filters(
-        &self,
-        field_id: &str,
-        field_type_rev: &FieldTypeRevision,
-    ) -> Option<Vec<Arc<FilterConfigurationRevision>>> {
-        self.filters.get_objects(field_id, field_type_rev)
-    }
-
-    pub fn get_mut_filters(
-        &mut self,
-        field_id: &str,
-        field_type: &FieldTypeRevision,
-    ) -> Option<&mut Vec<Arc<FilterConfigurationRevision>>> {
-        self.filters.get_mut_objects(field_id, field_type)
-    }
-
-    pub fn insert_filter(
-        &mut self,
-        field_id: &str,
-        field_type: &FieldTypeRevision,
-        filter_rev: FilterConfigurationRevision,
-    ) {
-        self.filters.insert_object(field_id, field_type, filter_rev);
-    }
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
 pub struct RowOrderRevision {
     pub row_id: String,
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::revision::GridViewRevision;
+
+    #[test]
+    fn grid_view_revision_serde_test() {
+        let grid_view_revision = GridViewRevision {
+            view_id: "1".to_string(),
+            grid_id: "1".to_string(),
+            layout: Default::default(),
+            filters: Default::default(),
+            groups: Default::default(),
+        };
+        let s = serde_json::to_string(&grid_view_revision).unwrap();
+        assert_eq!(
+            s,
+            r#"{"view_id":"1","grid_id":"1","layout":0,"filters":[],"groups":[]}"#
+        );
+    }
+}

+ 19 - 0
shared-lib/flowy-grid-data-model/src/revision/group_rev.rs

@@ -12,8 +12,23 @@ pub trait GroupConfigurationContent: Sized {
         &[]
     }
 
+    fn mut_groups(&mut self) -> &mut Vec<GroupRecordRevision> {
+        todo!()
+    }
+
     fn set_groups(&mut self, _new_groups: Vec<GroupRecordRevision>) {}
 
+    fn swap_group(&mut self, from_group_id: &str, to_group_id: &str) {
+        let from_index = self
+            .get_groups()
+            .iter()
+            .position(|group| group.group_id == from_group_id);
+        let to_index = self.get_groups().iter().position(|group| group.group_id == to_group_id);
+        if let (Some(from), Some(to)) = (from_index, to_index) {
+            self.mut_groups().swap(from, to);
+        }
+    }
+
     fn with_mut_group<F>(&mut self, _group_id: &str, _f: F)
     where
         F: FnOnce(&mut GroupRecordRevision),
@@ -120,6 +135,10 @@ impl GroupConfigurationContent for SelectOptionGroupConfigurationRevision {
         &self.groups
     }
 
+    fn mut_groups(&mut self) -> &mut Vec<GroupRecordRevision> {
+        &mut self.groups
+    }
+
     fn set_groups(&mut self, new_groups: Vec<GroupRecordRevision>) {
         self.groups = new_groups;
     }

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

@@ -52,7 +52,26 @@ impl GridViewRevisionPad {
         self.groups.get_all_objects(field_revs)
     }
 
-    pub fn get_mut_group<F: FnOnce(&mut GroupConfigurationRevision)>(
+    #[tracing::instrument(level = "trace", skip_all, err)]
+    pub fn insert_group(
+        &mut self,
+        field_id: &str,
+        field_type: &FieldTypeRevision,
+        group_rev: GroupConfigurationRevision,
+    ) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
+        self.modify(|view| {
+            view.groups.insert_object(field_id, field_type, group_rev);
+            Ok(Some(()))
+        })
+    }
+
+    #[tracing::instrument(level = "trace", skip_all)]
+    pub fn contains_group(&self, field_id: &str, field_type: &FieldTypeRevision) -> bool {
+        self.view.groups.get_objects(field_id, field_type).is_some()
+    }
+
+    #[tracing::instrument(level = "trace", skip_all, err)]
+    pub fn with_mut_group<F: FnOnce(&mut GroupConfigurationRevision)>(
         &mut self,
         field_id: &str,
         field_type: &FieldTypeRevision,
@@ -147,6 +166,8 @@ impl GridViewRevisionPad {
                     None => Ok(None),
                     Some(delta) => {
                         self.delta = self.delta.compose(&delta)?;
+                        tracing::info!("GridView: {:?}", delta);
+
                         let md5 = md5(&self.delta.json_bytes());
                         Ok(Some(GridViewRevisionChangeset { delta, md5 }))
                     }