Переглянути джерело

Merge pull request #569 from AppFlowy-IO/feat/update_grid_test

Feat/update grid test
Nathan.fooo 2 роки тому
батько
коміт
7014451a8e
25 змінених файлів з 746 додано та 582 видалено
  1. 23 1
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  2. 8 0
      frontend/rust-lib/flowy-grid/src/event_map.rs
  3. 9 9
      frontend/rust-lib/flowy-grid/src/manager.rs
  4. 36 17
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  5. 7 7
      frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs
  6. 40 0
      frontend/rust-lib/flowy-grid/tests/grid/block_test.rs
  7. 48 0
      frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs
  8. 113 0
      frontend/rust-lib/flowy-grid/tests/grid/field_test.rs
  9. 0 381
      frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs
  10. 5 1
      frontend/rust-lib/flowy-grid/tests/grid/mod.rs
  11. 147 0
      frontend/rust-lib/flowy-grid/tests/grid/row_test.rs
  12. 67 39
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  13. 1 0
      frontend/rust-lib/flowy-grid/tests/grid/setting_test.rs
  14. 41 22
      shared-lib/flowy-grid-data-model/src/entities/grid_setting.rs
  15. 2 2
      shared-lib/flowy-grid-data-model/src/entities/mod.rs
  16. 10 10
      shared-lib/flowy-grid-data-model/src/parser/grid_info_parser.rs
  17. 0 66
      shared-lib/flowy-grid-data-model/src/revision/grid_info_rev.rs
  18. 4 4
      shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs
  19. 116 0
      shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs
  20. 2 2
      shared-lib/flowy-grid-data-model/src/revision/mod.rs
  21. 1 1
      shared-lib/flowy-sync/src/client_grid/grid_builder.rs
  22. 59 11
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  23. 5 7
      shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs
  24. 1 1
      shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs
  25. 1 1
      shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs

+ 23 - 1
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -15,10 +15,32 @@ pub(crate) async fn get_grid_data_handler(
 ) -> DataResult<Grid, FlowyError> {
     let grid_id: GridId = data.into_inner();
     let editor = manager.open_grid(grid_id).await?;
-    let grid = editor.grid_data().await?;
+    let grid = editor.get_grid_data().await?;
     data_result(grid)
 }
 
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
+pub(crate) async fn get_grid_setting_handler(
+    data: Data<GridId>,
+    manager: AppData<Arc<GridManager>>,
+) -> DataResult<GridSetting, FlowyError> {
+    let grid_id: GridId = data.into_inner();
+    let editor = manager.open_grid(grid_id).await?;
+    let grid_setting = editor.get_grid_setting().await?;
+    data_result(grid_setting.into())
+}
+
+#[tracing::instrument(level = "trace", skip(data, manager), err)]
+pub(crate) async fn update_grid_setting_handler(
+    data: Data<GridSettingChangesetPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> Result<(), FlowyError> {
+    let params: GridSettingChangesetParams = data.into_inner().try_into()?;
+    let editor = manager.open_grid(&params.grid_id).await?;
+    let _ = editor.update_grid_setting(params).await?;
+    Ok(())
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_grid_blocks_handler(
     data: Data<QueryGridBlocksPayload>,

+ 8 - 0
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -10,6 +10,8 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
     module = module
         .event(GridEvent::GetGridData, get_grid_data_handler)
         .event(GridEvent::GetGridBlocks, get_grid_blocks_handler)
+        .event(GridEvent::GetGridSetting, get_grid_setting_handler)
+        .event(GridEvent::UpdateGridSetting, get_grid_setting_handler)
         // Field
         .event(GridEvent::GetFields, get_fields_handler)
         .event(GridEvent::UpdateField, update_field_handler)
@@ -49,6 +51,12 @@ pub enum GridEvent {
     #[event(input = "QueryGridBlocksPayload", output = "RepeatedGridBlock")]
     GetGridBlocks = 1,
 
+    #[event(input = "GridId", output = "GridSetting")]
+    GetGridSetting = 2,
+
+    #[event(input = "GridId", input = "GridSettingChangesetPayload")]
+    UpdateGridSetting = 3,
+
     #[event(input = "QueryFieldPayload", output = "RepeatedField")]
     GetFields = 10,
 

+ 9 - 9
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -1,4 +1,4 @@
-use crate::services::grid_editor::GridMetaEditor;
+use crate::services::grid_editor::GridRevisionEditor;
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::persistence::kv::GridKVPersistence;
 use crate::services::persistence::GridDatabase;
@@ -6,7 +6,7 @@ use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_database::ConnectionPool;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::revision::{BuildGridContext, GridInfoRevision, GridRevision};
+use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridSettingRevision};
 use flowy_revision::disk::{SQLiteGridBlockMetaRevisionPersistence, SQLiteGridRevisionPersistence};
 use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket};
 use flowy_sync::client_grid::{make_block_meta_delta, make_grid_delta};
@@ -20,7 +20,7 @@ pub trait GridUser: Send + Sync {
 }
 
 pub struct GridManager {
-    editor_map: Arc<DashMap<String, Arc<GridMetaEditor>>>,
+    editor_map: Arc<DashMap<String, Arc<GridRevisionEditor>>>,
     grid_user: Arc<dyn GridUser>,
     block_index_cache: Arc<BlockIndexCache>,
     #[allow(dead_code)]
@@ -67,7 +67,7 @@ impl GridManager {
     }
 
     #[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
-    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<GridMetaEditor>> {
+    pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<GridRevisionEditor>> {
         let grid_id = grid_id.as_ref();
         tracing::Span::current().record("grid_id", &grid_id);
         self.get_or_create_grid_editor(grid_id).await
@@ -92,14 +92,14 @@ impl GridManager {
     // pub fn update_grid_info()
 
     // #[tracing::instrument(level = "debug", skip(self), err)]
-    pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
+    pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
         match self.editor_map.get(grid_id) {
             None => Err(FlowyError::internal().context("Should call open_grid function first")),
             Some(editor) => Ok(editor.clone()),
         }
     }
 
-    async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
+    async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridRevisionEditor>> {
         match self.editor_map.get(grid_id) {
             None => {
                 tracing::trace!("Create grid editor with id: {}", grid_id);
@@ -121,10 +121,10 @@ impl GridManager {
         &self,
         grid_id: &str,
         pool: Arc<ConnectionPool>,
-    ) -> Result<Arc<GridMetaEditor>, FlowyError> {
+    ) -> Result<Arc<GridRevisionEditor>, FlowyError> {
         let user = self.grid_user.clone();
         let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
-        let grid_editor = GridMetaEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
+        let grid_editor = GridRevisionEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
         Ok(grid_editor)
     }
 
@@ -160,7 +160,7 @@ pub async fn make_grid_view_data(
         grid_id: view_id.to_string(),
         fields: build_context.field_revs,
         blocks: build_context.blocks,
-        info: GridInfoRevision::default(),
+        setting: GridSettingRevision::default(),
     };
 
     // Create grid

+ 36 - 17
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -20,7 +20,7 @@ use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
-pub struct GridMetaEditor {
+pub struct GridRevisionEditor {
     grid_id: String,
     user: Arc<dyn GridUser>,
     grid_pad: Arc<RwLock<GridRevisionPad>>,
@@ -28,13 +28,13 @@ pub struct GridMetaEditor {
     block_manager: Arc<GridBlockManager>,
 }
 
-impl Drop for GridMetaEditor {
+impl Drop for GridRevisionEditor {
     fn drop(&mut self) {
         tracing::trace!("Drop GridMetaEditor");
     }
 }
 
-impl GridMetaEditor {
+impl GridRevisionEditor {
     pub async fn new(
         grid_id: &str,
         user: Arc<dyn GridUser>,
@@ -46,7 +46,7 @@ impl GridMetaEditor {
         let grid_pad = rev_manager.load::<GridPadBuilder>(Some(cloud)).await?;
         let rev_manager = Arc::new(rev_manager);
         let grid_pad = Arc::new(RwLock::new(grid_pad));
-        let blocks = grid_pad.read().await.get_block_metas();
+        let blocks = grid_pad.read().await.get_block_revs();
 
         let block_meta_manager = Arc::new(GridBlockManager::new(grid_id, &user, blocks, persistence).await?);
         Ok(Arc::new(Self {
@@ -165,13 +165,15 @@ impl GridMetaEditor {
 
     pub async fn replace_field(&self, field_rev: FieldRevision) -> FlowyResult<()> {
         let field_id = field_rev.id.clone();
-        let _ = self.modify(|pad| Ok(pad.replace_field_rev(field_rev)?)).await?;
+        let _ = self
+            .modify(|grid_pad| Ok(grid_pad.replace_field_rev(field_rev)?))
+            .await?;
         let _ = self.notify_did_update_grid_field(&field_id).await?;
         Ok(())
     }
 
     pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
-        let _ = self.modify(|grid| Ok(grid.delete_field_rev(field_id)?)).await?;
+        let _ = self.modify(|grid_pad| Ok(grid_pad.delete_field_rev(field_id)?)).await?;
         let field_order = FieldOrder::from(field_id);
         let notified_changeset = GridFieldChangeset::delete(&self.grid_id, vec![field_order]);
         let _ = self.notify_did_update_grid(notified_changeset).await?;
@@ -242,12 +244,16 @@ impl GridMetaEditor {
     }
 
     pub async fn create_block(&self, grid_block: GridBlockRevision) -> FlowyResult<()> {
-        let _ = self.modify(|grid| Ok(grid.create_block_meta(grid_block)?)).await?;
+        let _ = self
+            .modify(|grid_pad| Ok(grid_pad.create_block_rev(grid_block)?))
+            .await?;
         Ok(())
     }
 
     pub async fn update_block(&self, changeset: GridBlockRevisionChangeset) -> FlowyResult<()> {
-        let _ = self.modify(|grid| Ok(grid.update_block_meta(changeset)?)).await?;
+        let _ = self
+            .modify(|grid_pad| Ok(grid_pad.update_block_rev(changeset)?))
+            .await?;
         Ok(())
     }
 
@@ -256,7 +262,7 @@ impl GridMetaEditor {
         let block_id = self.block_id().await?;
 
         // insert empty row below the row whose id is upper_row_id
-        let row_rev_ctx = CreateRowMetaBuilder::new(&field_revs).build();
+        let row_rev_ctx = CreateRowRevisionBuilder::new(&field_revs).build();
         let row_rev = make_row_rev_from_context(&block_id, row_rev_ctx);
         let row_order = RowOrder::from(&row_rev);
 
@@ -269,7 +275,7 @@ impl GridMetaEditor {
         Ok(row_order)
     }
 
-    pub async fn insert_rows(&self, contexts: Vec<CreateRowMetaPayload>) -> FlowyResult<Vec<RowOrder>> {
+    pub async fn insert_rows(&self, contexts: Vec<CreateRowRevisionPayload>) -> FlowyResult<Vec<RowOrder>> {
         let block_id = self.block_id().await?;
         let mut rows_by_block_id: HashMap<String, Vec<RowRevision>> = HashMap::new();
         let mut row_orders = vec![];
@@ -400,7 +406,7 @@ impl GridMetaEditor {
     }
 
     pub async fn get_block_metas(&self) -> FlowyResult<Vec<GridBlockRevision>> {
-        let grid_blocks = self.grid_pad.read().await.get_block_metas();
+        let grid_blocks = self.grid_pad.read().await.get_block_revs();
         Ok(grid_blocks)
     }
 
@@ -412,11 +418,11 @@ impl GridMetaEditor {
         Ok(())
     }
 
-    pub async fn grid_data(&self) -> FlowyResult<Grid> {
+    pub async fn get_grid_data(&self) -> FlowyResult<Grid> {
         let pad_read_guard = self.grid_pad.read().await;
         let field_orders = pad_read_guard.get_field_orders();
         let mut block_orders = vec![];
-        for block_order in pad_read_guard.get_block_metas() {
+        for block_order in pad_read_guard.get_block_revs() {
             let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?;
             let block_order = GridBlockOrder {
                 block_id: block_order.block_id,
@@ -432,13 +438,26 @@ impl GridMetaEditor {
         })
     }
 
+    pub async fn get_grid_setting(&self) -> FlowyResult<GridSettingRevision> {
+        let pad_read_guard = self.grid_pad.read().await;
+        let grid_setting_rev = pad_read_guard.get_grid_setting_rev();
+        Ok(grid_setting_rev)
+    }
+
+    pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
+        let _ = self
+            .modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
+            .await?;
+        Ok(())
+    }
+
     pub async fn grid_block_snapshots(&self, block_ids: Option<Vec<String>>) -> FlowyResult<Vec<GridBlockSnapshot>> {
         let block_ids = match block_ids {
             None => self
                 .grid_pad
                 .read()
                 .await
-                .get_block_metas()
+                .get_block_revs()
                 .into_iter()
                 .map(|block_meta| block_meta.block_id)
                 .collect::<Vec<String>>(),
@@ -488,7 +507,7 @@ impl GridMetaEditor {
 
     pub async fn duplicate_grid(&self) -> FlowyResult<BuildGridContext> {
         let grid_pad = self.grid_pad.read().await;
-        let original_blocks = grid_pad.get_block_metas();
+        let original_blocks = grid_pad.get_block_revs();
         let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_meta().await;
 
         let mut blocks_meta_data = vec![];
@@ -547,7 +566,7 @@ impl GridMetaEditor {
     }
 
     async fn block_id(&self) -> FlowyResult<String> {
-        match self.grid_pad.read().await.get_block_metas().last() {
+        match self.grid_pad.read().await.get_block_revs().last() {
             None => Err(FlowyError::internal().context("There is no grid block in this grid")),
             Some(grid_block) => Ok(grid_block.block_id.clone()),
         }
@@ -593,7 +612,7 @@ impl GridMetaEditor {
 }
 
 #[cfg(feature = "flowy_unit_test")]
-impl GridMetaEditor {
+impl GridRevisionEditor {
     pub fn rev_manager(&self) -> Arc<RevisionManager> {
         self.rev_manager.clone()
     }

+ 7 - 7
frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs

@@ -5,19 +5,19 @@ use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, R
 use indexmap::IndexMap;
 use std::collections::HashMap;
 
-pub struct CreateRowMetaBuilder<'a> {
+pub struct CreateRowRevisionBuilder<'a> {
     field_rev_map: HashMap<&'a String, &'a FieldRevision>,
-    payload: CreateRowMetaPayload,
+    payload: CreateRowRevisionPayload,
 }
 
-impl<'a> CreateRowMetaBuilder<'a> {
+impl<'a> CreateRowRevisionBuilder<'a> {
     pub fn new(fields: &'a [FieldRevision]) -> Self {
         let field_rev_map = fields
             .iter()
             .map(|field| (&field.id, field))
             .collect::<HashMap<&String, &FieldRevision>>();
 
-        let payload = CreateRowMetaPayload {
+        let payload = CreateRowRevisionPayload {
             row_id: gen_row_id(),
             cell_by_field_id: Default::default(),
             height: DEFAULT_ROW_HEIGHT,
@@ -70,12 +70,12 @@ impl<'a> CreateRowMetaBuilder<'a> {
         self
     }
 
-    pub fn build(self) -> CreateRowMetaPayload {
+    pub fn build(self) -> CreateRowRevisionPayload {
         self.payload
     }
 }
 
-pub fn make_row_rev_from_context(block_id: &str, payload: CreateRowMetaPayload) -> RowRevision {
+pub fn make_row_rev_from_context(block_id: &str, payload: CreateRowRevisionPayload) -> RowRevision {
     RowRevision {
         id: payload.row_id,
         block_id: block_id.to_owned(),
@@ -85,7 +85,7 @@ pub fn make_row_rev_from_context(block_id: &str, payload: CreateRowMetaPayload)
     }
 }
 
-pub struct CreateRowMetaPayload {
+pub struct CreateRowRevisionPayload {
     pub row_id: String,
     pub cell_by_field_id: IndexMap<String, CellRevision>,
     pub height: i32,

+ 40 - 0
frontend/rust-lib/flowy-grid/tests/grid/block_test.rs

@@ -0,0 +1,40 @@
+use crate::grid::script::EditorScript::*;
+use crate::grid::script::*;
+use flowy_grid_data_model::revision::{GridBlockRevision, GridBlockRevisionChangeset};
+
+#[tokio::test]
+async fn grid_create_block() {
+    let grid_block = GridBlockRevision::new();
+    let scripts = vec![
+        AssertBlockCount(1),
+        CreateBlock { block: grid_block },
+        AssertBlockCount(2),
+    ];
+    GridEditorTest::new().await.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_update_block() {
+    let grid_block = GridBlockRevision::new();
+    let mut cloned_grid_block = grid_block.clone();
+    let changeset = GridBlockRevisionChangeset {
+        block_id: grid_block.block_id.clone(),
+        start_row_index: Some(2),
+        row_count: Some(10),
+    };
+
+    cloned_grid_block.start_row_index = 2;
+    cloned_grid_block.row_count = 10;
+
+    let scripts = vec![
+        AssertBlockCount(1),
+        CreateBlock { block: grid_block },
+        UpdateBlock { changeset },
+        AssertBlockCount(2),
+        AssertBlockEqual {
+            block_index: 1,
+            block: cloned_grid_block,
+        },
+    ];
+    GridEditorTest::new().await.run_scripts(scripts).await;
+}

+ 48 - 0
frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs

@@ -0,0 +1,48 @@
+use crate::grid::script::EditorScript::*;
+use crate::grid::script::*;
+use flowy_grid::services::field::{MultiSelectTypeOption, SelectOptionCellContentChangeset, SingleSelectTypeOption};
+use flowy_grid_data_model::entities::{CellChangeset, FieldType};
+
+#[tokio::test]
+async fn grid_cell_update() {
+    let mut test = GridEditorTest::new().await;
+    let field_revs = &test.field_revs;
+    let row_revs = &test.row_revs;
+    let grid_blocks = &test.grid_block_revs;
+
+    // For the moment, We only have one block to store rows
+    let block_id = &grid_blocks.first().unwrap().block_id;
+
+    let mut scripts = vec![];
+    for (_, row_rev) in row_revs.iter().enumerate() {
+        for field_rev in field_revs {
+            let data = match field_rev.field_type {
+                FieldType::RichText => "".to_string(),
+                FieldType::Number => "123".to_string(),
+                FieldType::DateTime => make_date_cell_string("123"),
+                FieldType::SingleSelect => {
+                    let type_option = SingleSelectTypeOption::from(field_rev);
+                    SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
+                }
+                FieldType::MultiSelect => {
+                    let type_option = MultiSelectTypeOption::from(field_rev);
+                    SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
+                }
+                FieldType::Checkbox => "1".to_string(),
+                FieldType::URL => "1".to_string(),
+            };
+
+            scripts.push(UpdateCell {
+                changeset: CellChangeset {
+                    grid_id: block_id.to_string(),
+                    row_id: row_rev.id.clone(),
+                    field_id: field_rev.id.clone(),
+                    cell_content_changeset: Some(data),
+                },
+                is_err: false,
+            });
+        }
+    }
+
+    test.run_scripts(scripts).await;
+}

+ 113 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_test.rs

@@ -0,0 +1,113 @@
+use crate::grid::script::EditorScript::*;
+use crate::grid::script::*;
+use flowy_grid::services::field::{SelectOption, SingleSelectTypeOption};
+use flowy_grid_data_model::entities::FieldChangesetParams;
+use flowy_grid_data_model::revision::TypeOptionDataEntry;
+
+#[tokio::test]
+async fn grid_create_field() {
+    let mut test = GridEditorTest::new().await;
+    let (params, field_rev) = create_text_field(&test.grid_id);
+
+    let scripts = vec![
+        CreateField { params },
+        AssertFieldEqual {
+            field_index: test.field_count,
+            field_rev,
+        },
+    ];
+    test.run_scripts(scripts).await;
+
+    let (params, field_rev) = create_single_select_field(&test.grid_id);
+    let scripts = vec![
+        CreateField { params },
+        AssertFieldEqual {
+            field_index: test.field_count,
+            field_rev,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_create_duplicate_field() {
+    let mut test = GridEditorTest::new().await;
+    let (params, _) = create_text_field(&test.grid_id);
+    let field_count = test.field_count;
+    let expected_field_count = field_count + 1;
+    let scripts = vec![
+        CreateField { params: params.clone() },
+        CreateField { params },
+        AssertFieldCount(expected_field_count),
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_update_field_with_empty_change() {
+    let mut test = GridEditorTest::new().await;
+    let (params, field_rev) = create_single_select_field(&test.grid_id);
+    let changeset = FieldChangesetParams {
+        field_id: field_rev.id.clone(),
+        grid_id: test.grid_id.clone(),
+        ..Default::default()
+    };
+
+    let scripts = vec![
+        CreateField { params },
+        UpdateField { changeset },
+        AssertFieldEqual {
+            field_index: test.field_count,
+            field_rev,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_update_field() {
+    let mut test = GridEditorTest::new().await;
+    let (params, single_select_field) = create_single_select_field(&test.grid_id);
+
+    let mut single_select_type_option = SingleSelectTypeOption::from(&single_select_field);
+    single_select_type_option.options.push(SelectOption::new("Unknown"));
+    let changeset = FieldChangesetParams {
+        field_id: single_select_field.id.clone(),
+        grid_id: test.grid_id.clone(),
+        frozen: Some(true),
+        width: Some(1000),
+        type_option_data: Some(single_select_type_option.protobuf_bytes().to_vec()),
+        ..Default::default()
+    };
+
+    // The expected_field must be equal to the field that applied the changeset
+    let mut expected_field_rev = single_select_field.clone();
+    expected_field_rev.frozen = true;
+    expected_field_rev.width = 1000;
+    expected_field_rev.insert_type_option_entry(&single_select_type_option);
+
+    let scripts = vec![
+        CreateField { params },
+        UpdateField { changeset },
+        AssertFieldEqual {
+            field_index: test.field_count,
+            field_rev: expected_field_rev,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_delete_field() {
+    let mut test = GridEditorTest::new().await;
+    let original_field_count = test.field_count;
+    let (params, text_field_rev) = create_text_field(&test.grid_id);
+    let scripts = vec![
+        CreateField { params },
+        DeleteField {
+            field_rev: text_field_rev,
+        },
+        AssertFieldCount(original_field_count),
+    ];
+    test.run_scripts(scripts).await;
+}

+ 0 - 381
frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs

@@ -1,381 +0,0 @@
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
-use chrono::NaiveDateTime;
-use flowy_grid::services::field::{
-    DateCellContentChangeset, DateCellData, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset,
-    SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
-};
-use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder};
-use flowy_grid_data_model::entities::{CellChangeset, FieldChangesetParams, FieldType};
-use flowy_grid_data_model::revision::{
-    GridBlockRevision, GridBlockRevisionChangeset, RowMetaChangeset, TypeOptionDataEntry,
-};
-
-#[tokio::test]
-async fn grid_create_field() {
-    let mut test = GridEditorTest::new().await;
-    let (text_field_params, text_field_rev) = create_text_field(&test.grid_id);
-    let (single_select_params, single_select_field) = create_single_select_field(&test.grid_id);
-    let scripts = vec![
-        CreateField {
-            params: text_field_params,
-        },
-        AssertFieldEqual {
-            field_index: test.field_count,
-            field_rev: text_field_rev,
-        },
-    ];
-    test.run_scripts(scripts).await;
-
-    let scripts = vec![
-        CreateField {
-            params: single_select_params,
-        },
-        AssertFieldEqual {
-            field_index: test.field_count,
-            field_rev: single_select_field,
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_create_duplicate_field() {
-    let mut test = GridEditorTest::new().await;
-    let (params, _) = create_text_field(&test.grid_id);
-    let field_count = test.field_count;
-    let expected_field_count = field_count + 1;
-    let scripts = vec![
-        CreateField { params: params.clone() },
-        CreateField { params },
-        AssertFieldCount(expected_field_count),
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_update_field_with_empty_change() {
-    let mut test = GridEditorTest::new().await;
-    let (params, field_rev) = create_single_select_field(&test.grid_id);
-    let changeset = FieldChangesetParams {
-        field_id: field_rev.id.clone(),
-        grid_id: test.grid_id.clone(),
-        ..Default::default()
-    };
-
-    let scripts = vec![
-        CreateField { params },
-        UpdateField { changeset },
-        AssertFieldEqual {
-            field_index: test.field_count,
-            field_rev,
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_update_field() {
-    let mut test = GridEditorTest::new().await;
-    let (single_select_params, single_select_field) = create_single_select_field(&test.grid_id);
-    let mut cloned_field = single_select_field.clone();
-
-    let mut single_select_type_option = SingleSelectTypeOption::from(&single_select_field);
-    single_select_type_option.options.push(SelectOption::new("Unknown"));
-    let changeset = FieldChangesetParams {
-        field_id: single_select_field.id.clone(),
-        grid_id: test.grid_id.clone(),
-        frozen: Some(true),
-        width: Some(1000),
-        type_option_data: Some(single_select_type_option.protobuf_bytes().to_vec()),
-        ..Default::default()
-    };
-
-    cloned_field.frozen = true;
-    cloned_field.width = 1000;
-    cloned_field.insert_type_option_entry(&single_select_type_option);
-
-    let scripts = vec![
-        CreateField {
-            params: single_select_params,
-        },
-        UpdateField { changeset },
-        AssertFieldEqual {
-            field_index: test.field_count,
-            field_rev: cloned_field,
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_delete_field() {
-    let mut test = GridEditorTest::new().await;
-    let expected_field_count = test.field_count;
-    let (text_params, text_field) = create_text_field(&test.grid_id);
-    let scripts = vec![
-        CreateField { params: text_params },
-        DeleteField { field_rev: text_field },
-        AssertFieldCount(expected_field_count),
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_create_block() {
-    let grid_block = GridBlockRevision::new();
-    let scripts = vec![
-        AssertBlockCount(1),
-        CreateBlock { block: grid_block },
-        AssertBlockCount(2),
-    ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_update_block() {
-    let grid_block = GridBlockRevision::new();
-    let mut cloned_grid_block = grid_block.clone();
-    let changeset = GridBlockRevisionChangeset {
-        block_id: grid_block.block_id.clone(),
-        start_row_index: Some(2),
-        row_count: Some(10),
-    };
-
-    cloned_grid_block.start_row_index = 2;
-    cloned_grid_block.row_count = 10;
-
-    let scripts = vec![
-        AssertBlockCount(1),
-        CreateBlock { block: grid_block },
-        UpdateBlock { changeset },
-        AssertBlockCount(2),
-        AssertBlockEqual {
-            block_index: 1,
-            block: cloned_grid_block,
-        },
-    ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_create_row() {
-    let scripts = vec![AssertRowCount(3), CreateEmptyRow, CreateEmptyRow, AssertRowCount(5)];
-    GridEditorTest::new().await.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_create_row2() {
-    let mut test = GridEditorTest::new().await;
-    let create_row_context = CreateRowMetaBuilder::new(&test.field_revs).build();
-    let scripts = vec![
-        AssertRowCount(3),
-        CreateRow {
-            context: create_row_context,
-        },
-        AssertRowCount(4),
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_update_row() {
-    let mut test = GridEditorTest::new().await;
-    let context = CreateRowMetaBuilder::new(&test.field_revs).build();
-    let changeset = RowMetaChangeset {
-        row_id: context.row_id.clone(),
-        height: None,
-        visibility: None,
-        cell_by_field_id: Default::default(),
-    };
-
-    let scripts = vec![
-        AssertRowCount(3),
-        CreateRow { context },
-        UpdateRow {
-            changeset: changeset.clone(),
-        },
-        AssertRow { changeset },
-        AssertRowCount(4),
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_delete_row() {
-    let mut test = GridEditorTest::new().await;
-    let context_1 = CreateRowMetaBuilder::new(&test.field_revs).build();
-    let context_2 = CreateRowMetaBuilder::new(&test.field_revs).build();
-    let row_ids = vec![context_1.row_id.clone(), context_2.row_id.clone()];
-    let scripts = vec![
-        AssertRowCount(3),
-        CreateRow { context: context_1 },
-        CreateRow { context: context_2 },
-        AssertBlockCount(1),
-        AssertBlock {
-            block_index: 0,
-            row_count: 5,
-            start_row_index: 0,
-        },
-        DeleteRow { row_ids },
-        AssertBlock {
-            block_index: 0,
-            row_count: 3,
-            start_row_index: 0,
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_row_add_cells_test() {
-    let mut test = GridEditorTest::new().await;
-    let mut builder = CreateRowMetaBuilder::new(&test.field_revs);
-    for field in &test.field_revs {
-        match field.field_type {
-            FieldType::RichText => {
-                builder.add_cell(&field.id, "hello world".to_owned()).unwrap();
-            }
-            FieldType::Number => {
-                builder.add_cell(&field.id, "18,443".to_owned()).unwrap();
-            }
-            FieldType::DateTime => {
-                builder
-                    .add_cell(&field.id, make_date_cell_string("1647251762"))
-                    .unwrap();
-            }
-            FieldType::SingleSelect => {
-                let type_option = SingleSelectTypeOption::from(field);
-                let option = type_option.options.first().unwrap();
-                builder.add_select_option_cell(&field.id, option.id.clone()).unwrap();
-            }
-            FieldType::MultiSelect => {
-                let type_option = MultiSelectTypeOption::from(field);
-                let ops_ids = type_option
-                    .options
-                    .iter()
-                    .map(|option| option.id.clone())
-                    .collect::<Vec<_>>()
-                    .join(SELECTION_IDS_SEPARATOR);
-                builder.add_select_option_cell(&field.id, ops_ids).unwrap();
-            }
-            FieldType::Checkbox => {
-                builder.add_cell(&field.id, "false".to_string()).unwrap();
-            }
-            FieldType::URL => {
-                builder.add_cell(&field.id, "1".to_string()).unwrap();
-            }
-        }
-    }
-    let context = builder.build();
-    let scripts = vec![CreateRow { context }, AssertGridMetaPad];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_row_add_date_cell_test() {
-    let mut test = GridEditorTest::new().await;
-    let mut builder = CreateRowMetaBuilder::new(&test.field_revs);
-    let mut date_field = None;
-    let timestamp = 1647390674;
-    for field in &test.field_revs {
-        if field.field_type == FieldType::DateTime {
-            date_field = Some(field.clone());
-            NaiveDateTime::from_timestamp(123, 0);
-            // The data should not be empty
-            assert!(builder.add_cell(&field.id, "".to_string()).is_err());
-            assert!(builder.add_cell(&field.id, make_date_cell_string("123")).is_ok());
-            assert!(builder
-                .add_cell(&field.id, make_date_cell_string(&timestamp.to_string()))
-                .is_ok());
-        }
-    }
-    let context = builder.build();
-    let date_field = date_field.unwrap();
-    let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
-    assert_eq!(
-        decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
-            .parse::<DateCellData>()
-            .unwrap()
-            .date,
-        "2022/03/16",
-    );
-    let scripts = vec![CreateRow { context }];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn grid_cell_update() {
-    let mut test = GridEditorTest::new().await;
-    let field_revs = &test.field_revs;
-    let row_revs = &test.row_revs;
-    let grid_blocks = &test.grid_block_revs;
-    assert_eq!(row_revs.len(), 3);
-    assert_eq!(grid_blocks.len(), 1);
-
-    let block_id = &grid_blocks.first().unwrap().block_id;
-    let mut scripts = vec![];
-    for (index, row_rev) in row_revs.iter().enumerate() {
-        for field_rev in field_revs {
-            if index == 0 {
-                let data = match field_rev.field_type {
-                    FieldType::RichText => "".to_string(),
-                    FieldType::Number => "123".to_string(),
-                    FieldType::DateTime => make_date_cell_string("123"),
-                    FieldType::SingleSelect => {
-                        let type_option = SingleSelectTypeOption::from(field_rev);
-                        SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
-                    }
-                    FieldType::MultiSelect => {
-                        let type_option = MultiSelectTypeOption::from(field_rev);
-                        SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str()
-                    }
-                    FieldType::Checkbox => "1".to_string(),
-                    FieldType::URL => "1".to_string(),
-                };
-
-                scripts.push(UpdateCell {
-                    changeset: CellChangeset {
-                        grid_id: block_id.to_string(),
-                        row_id: row_rev.id.clone(),
-                        field_id: field_rev.id.clone(),
-                        cell_content_changeset: Some(data),
-                    },
-                    is_err: false,
-                });
-            }
-
-            if index == 1 {
-                let (data, is_err) = match field_rev.field_type {
-                    FieldType::RichText => ("1".to_string().repeat(10001), true),
-                    FieldType::Number => ("abc".to_string(), true),
-                    FieldType::DateTime => ("abc".to_string(), true),
-                    FieldType::SingleSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false),
-                    FieldType::MultiSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false),
-                    FieldType::Checkbox => ("2".to_string(), false),
-                    FieldType::URL => ("2".to_string(), false),
-                };
-
-                scripts.push(UpdateCell {
-                    changeset: CellChangeset {
-                        grid_id: block_id.to_string(),
-                        row_id: row_rev.id.clone(),
-                        field_id: field_rev.id.clone(),
-                        cell_content_changeset: Some(data),
-                    },
-                    is_err,
-                });
-            }
-        }
-    }
-
-    test.run_scripts(scripts).await;
-}
-
-fn make_date_cell_string(s: &str) -> String {
-    serde_json::to_string(&DateCellContentChangeset {
-        date: Some(s.to_string()),
-        time: None,
-    })
-    .unwrap()
-}

+ 5 - 1
frontend/rust-lib/flowy-grid/tests/grid/mod.rs

@@ -1,2 +1,6 @@
-mod grid_test;
+mod block_test;
+mod cell_test;
+mod field_test;
+mod row_test;
 mod script;
+mod setting_test;

+ 147 - 0
frontend/rust-lib/flowy-grid/tests/grid/row_test.rs

@@ -0,0 +1,147 @@
+use crate::grid::script::EditorScript::*;
+use crate::grid::script::*;
+use chrono::NaiveDateTime;
+use flowy_grid::services::field::{
+    DateCellData, MultiSelectTypeOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
+};
+use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowRevisionBuilder};
+use flowy_grid_data_model::entities::FieldType;
+use flowy_grid_data_model::revision::RowMetaChangeset;
+
+#[tokio::test]
+async fn grid_create_row_count_test() {
+    let test = GridEditorTest::new().await;
+    let create_row_context = CreateRowRevisionBuilder::new(&test.field_revs).build();
+    let scripts = vec![
+        AssertRowCount(3),
+        CreateEmptyRow,
+        CreateEmptyRow,
+        CreateRow {
+            context: create_row_context,
+        },
+        AssertRowCount(6),
+    ];
+    GridEditorTest::new().await.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_update_row() {
+    let mut test = GridEditorTest::new().await;
+    let context = CreateRowRevisionBuilder::new(&test.field_revs).build();
+    let changeset = RowMetaChangeset {
+        row_id: context.row_id.clone(),
+        height: None,
+        visibility: None,
+        cell_by_field_id: Default::default(),
+    };
+
+    let scripts = vec![AssertRowCount(3), CreateRow { context }, UpdateRow { changeset }];
+    test.run_scripts(scripts).await;
+
+    let expected_row = (&*test.row_revs.last().cloned().unwrap()).clone();
+    let scripts = vec![AssertRow { expected_row }, AssertRowCount(4)];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_delete_row() {
+    let mut test = GridEditorTest::new().await;
+    let context_1 = CreateRowRevisionBuilder::new(&test.field_revs).build();
+    let context_2 = CreateRowRevisionBuilder::new(&test.field_revs).build();
+    let row_ids = vec![context_1.row_id.clone(), context_2.row_id.clone()];
+    let scripts = vec![
+        AssertRowCount(3),
+        CreateRow { context: context_1 },
+        CreateRow { context: context_2 },
+        AssertBlockCount(1),
+        AssertBlock {
+            block_index: 0,
+            row_count: 5,
+            start_row_index: 0,
+        },
+        DeleteRows { row_ids },
+        AssertBlock {
+            block_index: 0,
+            row_count: 3,
+            start_row_index: 0,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_row_add_cells_test() {
+    let mut test = GridEditorTest::new().await;
+    let mut builder = CreateRowRevisionBuilder::new(&test.field_revs);
+    for field in &test.field_revs {
+        match field.field_type {
+            FieldType::RichText => {
+                builder.add_cell(&field.id, "hello world".to_owned()).unwrap();
+            }
+            FieldType::Number => {
+                builder.add_cell(&field.id, "18,443".to_owned()).unwrap();
+            }
+            FieldType::DateTime => {
+                builder
+                    .add_cell(&field.id, make_date_cell_string("1647251762"))
+                    .unwrap();
+            }
+            FieldType::SingleSelect => {
+                let type_option = SingleSelectTypeOption::from(field);
+                let option = type_option.options.first().unwrap();
+                builder.add_select_option_cell(&field.id, option.id.clone()).unwrap();
+            }
+            FieldType::MultiSelect => {
+                let type_option = MultiSelectTypeOption::from(field);
+                let ops_ids = type_option
+                    .options
+                    .iter()
+                    .map(|option| option.id.clone())
+                    .collect::<Vec<_>>()
+                    .join(SELECTION_IDS_SEPARATOR);
+                builder.add_select_option_cell(&field.id, ops_ids).unwrap();
+            }
+            FieldType::Checkbox => {
+                builder.add_cell(&field.id, "false".to_string()).unwrap();
+            }
+            FieldType::URL => {
+                builder.add_cell(&field.id, "1".to_string()).unwrap();
+            }
+        }
+    }
+    let context = builder.build();
+    let scripts = vec![CreateRow { context }, AssertGridRevisionPad];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_row_add_date_cell_test() {
+    let mut test = GridEditorTest::new().await;
+    let mut builder = CreateRowRevisionBuilder::new(&test.field_revs);
+    let mut date_field = None;
+    let timestamp = 1647390674;
+    for field in &test.field_revs {
+        if field.field_type == FieldType::DateTime {
+            date_field = Some(field.clone());
+            NaiveDateTime::from_timestamp(123, 0);
+            // The data should not be empty
+            assert!(builder.add_cell(&field.id, "".to_string()).is_err());
+            assert!(builder.add_cell(&field.id, make_date_cell_string("123")).is_ok());
+            assert!(builder
+                .add_cell(&field.id, make_date_cell_string(&timestamp.to_string()))
+                .is_ok());
+        }
+    }
+    let context = builder.build();
+    let date_field = date_field.unwrap();
+    let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
+    assert_eq!(
+        decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
+            .parse::<DateCellData>()
+            .unwrap()
+            .date,
+        "2022/03/16",
+    );
+    let scripts = vec![CreateRow { context }];
+    test.run_scripts(scripts).await;
+}

+ 67 - 39
frontend/rust-lib/flowy-grid/tests/grid/script.rs

@@ -1,14 +1,9 @@
 use bytes::Bytes;
 use flowy_grid::services::field::*;
-use flowy_grid::services::grid_editor::{GridMetaEditor, GridPadBuilder};
-use flowy_grid::services::row::CreateRowMetaPayload;
-use flowy_grid_data_model::entities::{
-    CellChangeset, Field, FieldChangesetParams, FieldOrder, FieldType, InsertFieldParams, RowOrder,
-};
-use flowy_grid_data_model::revision::{
-    BuildGridContext, FieldRevision, GridBlockRevision, GridBlockRevisionChangeset, RowMetaChangeset, RowRevision,
-    TypeOptionDataEntry,
-};
+use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor};
+use flowy_grid::services::row::CreateRowRevisionPayload;
+use flowy_grid_data_model::entities::*;
+use flowy_grid_data_model::revision::*;
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_sync::client_grid::GridBuilder;
 use flowy_test::helper::ViewTest;
@@ -52,15 +47,15 @@ pub enum EditorScript {
     },
     CreateEmptyRow,
     CreateRow {
-        context: CreateRowMetaPayload,
+        context: CreateRowRevisionPayload,
     },
     UpdateRow {
         changeset: RowMetaChangeset,
     },
     AssertRow {
-        changeset: RowMetaChangeset,
+        expected_row: RowRevision,
     },
-    DeleteRow {
+    DeleteRows {
         row_ids: Vec<String>,
     },
     UpdateCell {
@@ -68,14 +63,19 @@ pub enum EditorScript {
         is_err: bool,
     },
     AssertRowCount(usize),
-    // AssertRowEqual{ row_index: usize, row: RowMeta},
-    AssertGridMetaPad,
+    UpdateGridSetting {
+        params: GridSettingChangesetParams,
+    },
+    AssertGridSetting {
+        expected_setting: GridSettingRevision,
+    },
+    AssertGridRevisionPad,
 }
 
 pub struct GridEditorTest {
     pub sdk: FlowySDKTest,
     pub grid_id: String,
-    pub editor: Arc<GridMetaEditor>,
+    pub editor: Arc<GridRevisionEditor>,
     pub field_revs: Vec<FieldRevision>,
     pub grid_block_revs: Vec<GridBlockRevision>,
     pub row_revs: Vec<Arc<RowRevision>>,
@@ -88,22 +88,28 @@ impl GridEditorTest {
     pub async fn new() -> Self {
         let sdk = FlowySDKTest::default();
         let _ = sdk.init_user().await;
-        let build_context = make_template_1_grid();
+        let build_context = make_test_grid();
         let view_data: Bytes = build_context.into();
         let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
         let field_revs = editor.get_field_revs::<FieldOrder>(None).await.unwrap();
         let grid_blocks = editor.get_block_metas().await.unwrap();
-        let row_revs = get_row_revs(&editor).await;
+        let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs;
+        assert_eq!(row_revs.len(), 3);
+        assert_eq!(grid_blocks.len(), 1);
+
+        // It seems like you should add the field in the make_test_grid() function.
+        // Because we assert the initialize count of the fields is equal to FieldType::COUNT.
+        assert_eq!(field_revs.len(), FieldType::COUNT);
 
         let grid_id = test.view.id;
         Self {
             sdk,
             grid_id,
             editor,
-            field_revs: field_revs,
+            field_revs,
             grid_block_revs: grid_blocks,
-            row_revs: row_revs,
+            row_revs,
             field_count: FieldType::COUNT,
             row_order_by_row_id: HashMap::default(),
         }
@@ -192,7 +198,7 @@ impl GridEditorTest {
                 self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
             }
             EditorScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
-            EditorScript::DeleteRow { row_ids } => {
+            EditorScript::DeleteRows { row_ids } => {
                 let row_orders = row_ids
                     .into_iter()
                     .map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
@@ -202,16 +208,21 @@ impl GridEditorTest {
                 self.row_revs = self.get_row_revs().await;
                 self.grid_block_revs = self.editor.get_block_metas().await.unwrap();
             }
-            EditorScript::AssertRow { changeset } => {
-                let row = self.row_revs.iter().find(|row| row.id == changeset.row_id).unwrap();
-
-                if let Some(visibility) = changeset.visibility {
-                    assert_eq!(row.visibility, visibility);
-                }
-
-                if let Some(height) = changeset.height {
-                    assert_eq!(row.height, height);
-                }
+            EditorScript::AssertRow { expected_row } => {
+                let row = &*self
+                    .row_revs
+                    .iter()
+                    .find(|row| row.id == expected_row.id)
+                    .cloned()
+                    .unwrap();
+                assert_eq!(&expected_row, row);
+                // if let Some(visibility) = changeset.visibility {
+                //     assert_eq!(row.visibility, visibility);
+                // }
+                //
+                // if let Some(height) = changeset.height {
+                //     assert_eq!(row.height, height);
+                // }
             }
             EditorScript::UpdateCell { changeset, is_err } => {
                 let result = self.editor.update_cell(changeset).await;
@@ -222,10 +233,17 @@ impl GridEditorTest {
                     self.row_revs = self.get_row_revs().await;
                 }
             }
-            EditorScript::AssertRowCount(count) => {
-                assert_eq!(self.row_revs.len(), count);
+            EditorScript::AssertRowCount(expected_row_count) => {
+                assert_eq!(expected_row_count, self.row_revs.len());
+            }
+            EditorScript::UpdateGridSetting { params } => {
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
             }
-            EditorScript::AssertGridMetaPad => {
+            EditorScript::AssertGridSetting { expected_setting } => {
+                let setting = self.editor.get_grid_setting().await.unwrap();
+                assert_eq!(expected_setting, setting);
+            }
+            EditorScript::AssertGridRevisionPad => {
                 sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
                 let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap();
                 let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
@@ -235,14 +253,16 @@ impl GridEditorTest {
     }
 
     async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
-        get_row_revs(&self.editor).await
+        self.editor
+            .grid_block_snapshots(None)
+            .await
+            .unwrap()
+            .pop()
+            .unwrap()
+            .row_revs
     }
 }
 
-async fn get_row_revs(editor: &Arc<GridMetaEditor>) -> Vec<Arc<RowRevision>> {
-    editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs
-}
-
 pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldRevision) {
     let field_rev = FieldBuilder::new(RichTextTypeOptionBuilder::default())
         .name("Name")
@@ -310,7 +330,7 @@ pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldRev
     (params, cloned_field_rev)
 }
 
-fn make_template_1_grid() -> BuildGridContext {
+fn make_test_grid() -> BuildGridContext {
     let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
         .name("Name")
         .visibility(true)
@@ -365,3 +385,11 @@ fn make_template_1_grid() -> BuildGridContext {
         .add_empty_row()
         .build()
 }
+
+pub fn make_date_cell_string(s: &str) -> String {
+    serde_json::to_string(&DateCellContentChangeset {
+        date: Some(s.to_string()),
+        time: None,
+    })
+    .unwrap()
+}

+ 1 - 0
frontend/rust-lib/flowy-grid/tests/grid/setting_test.rs

@@ -0,0 +1 @@
+

+ 41 - 22
shared-lib/flowy-grid-data-model/src/entities/grid_info.rs → shared-lib/flowy-grid-data-model/src/entities/grid_setting.rs

@@ -1,28 +1,42 @@
 use crate::parser::{NotEmptyStr, ViewFilterParser, ViewGroupParser, ViewSortParser};
-use flowy_derive::ProtoBuf;
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error_code::ErrorCode;
+use std::collections::HashMap;
 use std::convert::TryInto;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct ViewExtData {
+pub struct GridSetting {
     #[pb(index = 1)]
-    pub filter: ViewFilter,
+    pub filter: HashMap<String, GridFilter>,
 
     #[pb(index = 2)]
-    pub group: ViewGroup,
+    pub group: HashMap<String, GridGroup>,
 
     #[pb(index = 3)]
-    pub sort: ViewSort,
+    pub sort: HashMap<String, GridSort>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[repr(u8)]
+pub enum GridLayoutType {
+    Table = 0,
+    Board = 1,
+}
+
+impl std::default::Default for GridLayoutType {
+    fn default() -> Self {
+        GridLayoutType::Table
+    }
 }
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct ViewFilter {
+pub struct GridFilter {
     #[pb(index = 1, one_of)]
     pub field_id: Option<String>,
 }
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct ViewGroup {
+pub struct GridGroup {
     #[pb(index = 1, one_of)]
     pub group_field_id: Option<String>,
 
@@ -31,37 +45,41 @@ pub struct ViewGroup {
 }
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
-pub struct ViewSort {
+pub struct GridSort {
     #[pb(index = 1, one_of)]
     pub field_id: Option<String>,
 }
 
 #[derive(Default, ProtoBuf)]
-pub struct GridInfoChangesetPayload {
+pub struct GridSettingChangesetPayload {
     #[pb(index = 1)]
     pub grid_id: String,
 
-    #[pb(index = 2, one_of)]
-    pub filter: Option<ViewFilter>,
+    #[pb(index = 2)]
+    pub layout_type: GridLayoutType,
 
     #[pb(index = 3, one_of)]
-    pub group: Option<ViewGroup>,
+    pub filter: Option<GridFilter>,
 
     #[pb(index = 4, one_of)]
-    pub sort: Option<ViewSort>,
+    pub group: Option<GridGroup>,
+
+    #[pb(index = 5, one_of)]
+    pub sort: Option<GridSort>,
 }
 
-pub struct GridInfoChangesetParams {
-    pub view_id: String,
-    pub filter: Option<ViewFilter>,
-    pub group: Option<ViewGroup>,
-    pub sort: Option<ViewSort>,
+pub struct GridSettingChangesetParams {
+    pub grid_id: String,
+    pub layout_type: GridLayoutType,
+    pub filter: Option<GridFilter>,
+    pub group: Option<GridGroup>,
+    pub sort: Option<GridSort>,
 }
 
-impl TryInto<GridInfoChangesetParams> for GridInfoChangesetPayload {
+impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayload {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<GridInfoChangesetParams, Self::Error> {
+    fn try_into(self) -> Result<GridSettingChangesetParams, Self::Error> {
         let view_id = NotEmptyStr::parse(self.grid_id)
             .map_err(|_| ErrorCode::FieldIdIsEmpty)?
             .0;
@@ -81,8 +99,9 @@ impl TryInto<GridInfoChangesetParams> for GridInfoChangesetPayload {
             Some(sort) => Some(ViewSortParser::parse(sort)?),
         };
 
-        Ok(GridInfoChangesetParams {
-            view_id,
+        Ok(GridSettingChangesetParams {
+            grid_id: view_id,
+            layout_type: self.layout_type,
             filter,
             group,
             sort,

+ 2 - 2
shared-lib/flowy-grid-data-model/src/entities/mod.rs

@@ -1,7 +1,7 @@
 mod field;
 mod grid;
-mod grid_info;
+mod grid_setting;
 
 pub use field::*;
 pub use grid::*;
-pub use grid_info::*;
+pub use grid_setting::*;

+ 10 - 10
shared-lib/flowy-grid-data-model/src/parser/grid_info_parser.rs

@@ -1,24 +1,24 @@
-use crate::entities::{ViewFilter, ViewGroup, ViewSort};
+use crate::entities::{GridFilter, GridGroup, GridSort};
 use crate::parser::NotEmptyStr;
 use flowy_error_code::ErrorCode;
 
-pub struct ViewFilterParser(pub ViewFilter);
+pub struct ViewFilterParser(pub GridFilter);
 
 impl ViewFilterParser {
-    pub fn parse(value: ViewFilter) -> Result<ViewFilter, ErrorCode> {
+    pub fn parse(value: GridFilter) -> Result<GridFilter, ErrorCode> {
         let field_id = match value.field_id {
             None => None,
             Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
         };
 
-        Ok(ViewFilter { field_id })
+        Ok(GridFilter { field_id })
     }
 }
 
-pub struct ViewGroupParser(pub ViewGroup);
+pub struct ViewGroupParser(pub GridGroup);
 
 impl ViewGroupParser {
-    pub fn parse(value: ViewGroup) -> Result<ViewGroup, ErrorCode> {
+    pub fn parse(value: GridGroup) -> Result<GridGroup, ErrorCode> {
         let group_field_id = match value.group_field_id {
             None => None,
             Some(group_field_id) => Some(
@@ -37,22 +37,22 @@ impl ViewGroupParser {
             ),
         };
 
-        Ok(ViewGroup {
+        Ok(GridGroup {
             group_field_id,
             sub_group_field_id,
         })
     }
 }
 
-pub struct ViewSortParser(pub ViewSort);
+pub struct ViewSortParser(pub GridSort);
 
 impl ViewSortParser {
-    pub fn parse(value: ViewSort) -> Result<ViewSort, ErrorCode> {
+    pub fn parse(value: GridSort) -> Result<GridSort, ErrorCode> {
         let field_id = match value.field_id {
             None => None,
             Some(field_id) => Some(NotEmptyStr::parse(field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0),
         };
 
-        Ok(ViewSort { field_id })
+        Ok(GridSort { field_id })
     }
 }

+ 0 - 66
shared-lib/flowy-grid-data-model/src/revision/grid_info_rev.rs

@@ -1,66 +0,0 @@
-use crate::entities::{ViewFilter, ViewGroup, ViewSort};
-use serde::{Deserialize, Serialize};
-use serde_repr::*;
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct GridInfoRevision {
-    pub filter: GridFilterRevision,
-    pub group: GridGroupRevision,
-    pub sort: GridSortRevision,
-    pub layout: GridLayoutRevision,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct GridLayoutRevision {
-    pub ty: GridLayoutType,
-}
-
-#[derive(Debug, Clone, Serialize_repr, Deserialize_repr)]
-#[repr(u8)]
-pub enum GridLayoutType {
-    Table = 0,
-    Board = 1,
-}
-
-impl std::default::Default for GridLayoutType {
-    fn default() -> Self {
-        GridLayoutType::Table
-    }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct GridFilterRevision {
-    pub field_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct GridGroupRevision {
-    pub group_field_id: Option<String>,
-    pub sub_group_field_id: Option<String>,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
-pub struct GridSortRevision {
-    field_id: Option<String>,
-}
-
-impl std::convert::From<GridFilterRevision> for ViewFilter {
-    fn from(rev: GridFilterRevision) -> Self {
-        ViewFilter { field_id: rev.field_id }
-    }
-}
-
-impl std::convert::From<GridGroupRevision> for ViewGroup {
-    fn from(rev: GridGroupRevision) -> Self {
-        ViewGroup {
-            group_field_id: rev.group_field_id,
-            sub_group_field_id: rev.sub_group_field_id,
-        }
-    }
-}
-
-impl std::convert::From<GridSortRevision> for ViewSort {
-    fn from(rev: GridSortRevision) -> Self {
-        ViewSort { field_id: rev.field_id }
-    }
-}

+ 4 - 4
shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs

@@ -1,5 +1,5 @@
 use crate::entities::{CellChangeset, Field, FieldOrder, FieldType, RowOrder};
-use crate::revision::GridInfoRevision;
+use crate::revision::GridSettingRevision;
 use bytes::Bytes;
 use indexmap::IndexMap;
 use nanoid::nanoid;
@@ -32,8 +32,8 @@ pub struct GridRevision {
     pub fields: Vec<FieldRevision>,
     pub blocks: Vec<GridBlockRevision>,
 
-    #[serde(default, skip)]
-    pub info: GridInfoRevision,
+    #[serde(skip)]
+    pub setting: GridSettingRevision,
 }
 
 impl GridRevision {
@@ -42,7 +42,7 @@ impl GridRevision {
             grid_id: grid_id.to_owned(),
             fields: vec![],
             blocks: vec![],
-            info: GridInfoRevision::default(),
+            setting: GridSettingRevision::default(),
         }
     }
 }

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

@@ -0,0 +1,116 @@
+use crate::entities::{GridFilter, GridGroup, GridLayoutType, GridSetting, GridSort};
+use indexmap::IndexMap;
+use serde::{Deserialize, Serialize};
+use serde_repr::*;
+use std::collections::HashMap;
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
+pub struct GridSettingRevision {
+    #[serde(with = "indexmap::serde_seq")]
+    pub filter: IndexMap<GridLayoutRevision, GridFilterRevision>,
+
+    #[serde(with = "indexmap::serde_seq")]
+    pub group: IndexMap<GridLayoutRevision, GridGroupRevision>,
+
+    #[serde(with = "indexmap::serde_seq")]
+    pub sort: IndexMap<GridLayoutRevision, GridSortRevision>,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
+#[repr(u8)]
+pub enum GridLayoutRevision {
+    Table = 0,
+    Board = 1,
+}
+
+impl ToString for GridLayoutRevision {
+    fn to_string(&self) -> String {
+        let layout_rev = self.clone() as u8;
+        layout_rev.to_string()
+    }
+}
+
+impl std::default::Default for GridLayoutRevision {
+    fn default() -> Self {
+        GridLayoutRevision::Table
+    }
+}
+
+impl std::convert::From<GridLayoutRevision> for GridLayoutType {
+    fn from(rev: GridLayoutRevision) -> Self {
+        match rev {
+            GridLayoutRevision::Table => GridLayoutType::Table,
+            GridLayoutRevision::Board => GridLayoutType::Board,
+        }
+    }
+}
+
+impl std::convert::From<GridLayoutType> for GridLayoutRevision {
+    fn from(layout: GridLayoutType) -> Self {
+        match layout {
+            GridLayoutType::Table => GridLayoutRevision::Table,
+            GridLayoutType::Board => GridLayoutRevision::Board,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
+pub struct GridFilterRevision {
+    pub field_id: Option<String>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
+pub struct GridGroupRevision {
+    pub group_field_id: Option<String>,
+    pub sub_group_field_id: Option<String>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
+pub struct GridSortRevision {
+    pub field_id: Option<String>,
+}
+
+impl std::convert::From<GridFilterRevision> for GridFilter {
+    fn from(rev: GridFilterRevision) -> Self {
+        GridFilter { field_id: rev.field_id }
+    }
+}
+
+impl std::convert::From<GridGroupRevision> for GridGroup {
+    fn from(rev: GridGroupRevision) -> Self {
+        GridGroup {
+            group_field_id: rev.group_field_id,
+            sub_group_field_id: rev.sub_group_field_id,
+        }
+    }
+}
+
+impl std::convert::From<GridSortRevision> for GridSort {
+    fn from(rev: GridSortRevision) -> Self {
+        GridSort { field_id: rev.field_id }
+    }
+}
+
+impl std::convert::From<GridSettingRevision> for GridSetting {
+    fn from(rev: GridSettingRevision) -> Self {
+        let filter: HashMap<String, GridFilter> = rev
+            .filter
+            .into_iter()
+            .map(|(layout_rev, filter_rev)| (layout_rev.to_string(), filter_rev.into()))
+            .collect();
+
+        let group: HashMap<String, GridGroup> = rev
+            .group
+            .into_iter()
+            .map(|(layout_rev, group_rev)| (layout_rev.to_string(), group_rev.into()))
+            .collect();
+
+        let sort: HashMap<String, GridSort> = rev
+            .sort
+            .into_iter()
+            .map(|(layout_rev, sort_rev)| (layout_rev.to_string(), sort_rev.into()))
+            .collect();
+
+        GridSetting { filter, group, sort }
+    }
+}

+ 2 - 2
shared-lib/flowy-grid-data-model/src/revision/mod.rs

@@ -1,5 +1,5 @@
-mod grid_info_rev;
 mod grid_rev;
+mod grid_setting_rev;
 
-pub use grid_info_rev::*;
 pub use grid_rev::*;
+pub use grid_setting_rev::*;

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

@@ -79,7 +79,7 @@ mod tests {
             grid_id,
             fields: build_context.field_revs,
             blocks: build_context.blocks,
-            info: Default::default(),
+            setting: Default::default(),
         };
 
         let grid_meta_delta = make_grid_delta(&grid_rev);

+ 59 - 11
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -2,10 +2,11 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{internal_error, CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
 use bytes::Bytes;
-use flowy_grid_data_model::entities::FieldType;
 use flowy_grid_data_model::entities::{FieldChangesetParams, FieldOrder};
+use flowy_grid_data_model::entities::{FieldType, GridSettingChangesetParams};
 use flowy_grid_data_model::revision::{
-    gen_block_id, gen_grid_id, FieldRevision, GridBlockRevision, GridBlockRevisionChangeset, GridRevision,
+    gen_block_id, gen_grid_id, FieldRevision, GridBlockRevision, GridBlockRevisionChangeset, GridFilterRevision,
+    GridGroupRevision, GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
 };
 use lib_infra::util::move_vec_element;
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
@@ -279,7 +280,7 @@ impl GridRevisionPad {
         }
     }
 
-    pub fn create_block_meta(&mut self, block: GridBlockRevision) -> CollaborateResult<Option<GridChangeset>> {
+    pub fn create_block_rev(&mut self, block: GridBlockRevision) -> CollaborateResult<Option<GridChangeset>> {
         self.modify_grid(|grid_meta| {
             if grid_meta.blocks.iter().any(|b| b.block_id == block.block_id) {
                 tracing::warn!("Duplicate grid block");
@@ -302,11 +303,11 @@ impl GridRevisionPad {
         })
     }
 
-    pub fn get_block_metas(&self) -> Vec<GridBlockRevision> {
+    pub fn get_block_revs(&self) -> Vec<GridBlockRevision> {
         self.grid_rev.blocks.clone()
     }
 
-    pub fn update_block_meta(
+    pub fn update_block_rev(
         &mut self,
         changeset: GridBlockRevisionChangeset,
     ) -> CollaborateResult<Option<GridChangeset>> {
@@ -328,6 +329,53 @@ impl GridRevisionPad {
         })
     }
 
+    pub fn get_grid_setting_rev(&self) -> GridSettingRevision {
+        self.grid_rev.setting.clone()
+    }
+
+    pub fn update_grid_setting_rev(
+        &mut self,
+        changeset: GridSettingChangesetParams,
+    ) -> CollaborateResult<Option<GridChangeset>> {
+        self.modify_grid(|grid_rev| {
+            let mut is_changed = None;
+            let layout_rev: GridLayoutRevision = changeset.layout_type.into();
+
+            if let Some(filter) = changeset.filter {
+                grid_rev.setting.filter.insert(
+                    layout_rev.clone(),
+                    GridFilterRevision {
+                        field_id: filter.field_id,
+                    },
+                );
+                is_changed = Some(())
+            }
+
+            if let Some(group) = changeset.group {
+                grid_rev.setting.group.insert(
+                    layout_rev.clone(),
+                    GridGroupRevision {
+                        group_field_id: group.group_field_id,
+                        sub_group_field_id: group.sub_group_field_id,
+                    },
+                );
+                is_changed = Some(())
+            }
+
+            if let Some(sort) = changeset.sort {
+                grid_rev.setting.sort.insert(
+                    layout_rev,
+                    GridSortRevision {
+                        field_id: sort.field_id,
+                    },
+                );
+                is_changed = Some(())
+            }
+
+            Ok(is_changed)
+        })
+    }
+
     pub fn md5(&self) -> String {
         md5(&self.delta.to_delta_bytes())
     }
@@ -365,32 +413,32 @@ impl GridRevisionPad {
         }
     }
 
-    pub fn modify_block<F>(&mut self, block_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
+    fn modify_block<F>(&mut self, block_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
     where
         F: FnOnce(&mut GridBlockRevision) -> CollaborateResult<Option<()>>,
     {
         self.modify_grid(
-            |grid_meta| match grid_meta.blocks.iter().position(|block| block.block_id == block_id) {
+            |grid_rev| match grid_rev.blocks.iter().position(|block| block.block_id == block_id) {
                 None => {
                     tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id);
                     Ok(None)
                 }
-                Some(index) => f(&mut grid_meta.blocks[index]),
+                Some(index) => f(&mut grid_rev.blocks[index]),
             },
         )
     }
 
-    pub fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
+    fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
     where
         F: FnOnce(&mut FieldRevision) -> CollaborateResult<Option<()>>,
     {
         self.modify_grid(
-            |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
+            |grid_rev| match grid_rev.fields.iter().position(|field| field.id == field_id) {
                 None => {
                     tracing::warn!("[GridMetaPad]: Can't find any field with id: {}", field_id);
                     Ok(None)
                 }
-                Some(index) => f(&mut grid_meta.fields[index]),
+                Some(index) => f(&mut grid_rev.fields[index]),
             },
         )
     }

+ 5 - 7
shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs

@@ -15,7 +15,7 @@ use walkdir::WalkDir;
 
 pub fn parse_protobuf_context_from(crate_paths: Vec<String>) -> Vec<ProtobufCrateContext> {
     let crate_infos = parse_crate_info_from_path(crate_paths);
-    let contexts = crate_infos
+    crate_infos
         .into_iter()
         .map(|crate_info| {
             let proto_output_path = crate_info.proto_output_path();
@@ -28,9 +28,7 @@ pub fn parse_protobuf_context_from(crate_paths: Vec<String>) -> Vec<ProtobufCrat
 
             ProtobufCrateContext::from_crate_info(crate_info, files)
         })
-        .collect::<Vec<ProtobufCrateContext>>();
-
-    contexts
+        .collect::<Vec<ProtobufCrateContext>>()
 }
 
 fn parse_files_protobuf(proto_crate_path: &Path, proto_output_path: &Path) -> Vec<ProtoFile> {
@@ -71,7 +69,7 @@ fn parse_files_protobuf(proto_crate_path: &Path, proto_output_path: &Path) -> Ve
                 .iter()
                 .filter(|field| field.attrs.pb_index().is_some())
                 .for_each(|field| {
-                    ref_types.push(field.ty_as_str().to_string());
+                    ref_types.push(field.ty_as_str());
                     struct_template.set_field(field);
                 });
 
@@ -95,8 +93,8 @@ fn parse_files_protobuf(proto_crate_path: &Path, proto_output_path: &Path) -> Ve
         if !enums.is_empty() || !structs.is_empty() {
             let structs: Vec<String> = structs.iter().map(|s| s.name.clone()).collect();
             let enums: Vec<String> = enums.iter().map(|e| e.name.clone()).collect();
-            ref_types.retain(|s| !structs.contains(&s));
-            ref_types.retain(|s| !enums.contains(&s));
+            ref_types.retain(|s| !structs.contains(s));
+            ref_types.retain(|s| !enums.contains(s));
 
             let info = ProtoFile {
                 file_path: path.clone(),

+ 1 - 1
shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs

@@ -76,7 +76,7 @@ fn write_proto_files(crate_contexts: &[ProtobufCrateContext]) {
             let mut file_content = file.syntax.clone();
 
             // import
-            file_content.push_str(&gen_import_content(&file, &file_path_content_map));
+            file_content.push_str(&gen_import_content(file, &file_path_content_map));
 
             // content
             file_content.push_str(&file.content);

+ 1 - 1
shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs

@@ -69,7 +69,7 @@ impl ProtobufCrate {
         ProtobufCrate {
             crate_path: config.crate_path,
             crate_folder: config.crate_folder,
-            flowy_config: config.flowy_config.clone(),
+            flowy_config: config.flowy_config,
         }
     }