浏览代码

feat: config view lens

appflowy 2 年之前
父节点
当前提交
0514b005ca
共有 36 个文件被更改,包括 853 次插入374 次删除
  1. 1 1
      frontend/app_flowy/lib/plugins/board/board.dart
  2. 1 0
      frontend/rust-lib/Cargo.lock
  3. 1 1
      frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/down.sql
  4. 1 1
      frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/up.sql
  5. 7 7
      frontend/rust-lib/flowy-database/src/macros.rs
  6. 2 2
      frontend/rust-lib/flowy-database/src/schema.rs
  7. 2 3
      frontend/rust-lib/flowy-folder/src/services/folder_editor.rs
  8. 1 0
      frontend/rust-lib/flowy-grid/Cargo.toml
  9. 6 9
      frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs
  10. 2 0
      frontend/rust-lib/flowy-grid/src/entities/row_entities.rs
  11. 3 3
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  12. 1 1
      frontend/rust-lib/flowy-grid/src/manager.rs
  13. 3 3
      frontend/rust-lib/flowy-grid/src/services/block_editor.rs
  14. 11 15
      frontend/rust-lib/flowy-grid/src/services/block_manager.rs
  15. 7 4
      frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs
  16. 7 0
      frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs
  17. 41 74
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  18. 156 7
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  19. 204 0
      frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
  20. 1 4
      frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs
  21. 2 15
      frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs
  22. 1 5
      frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs
  23. 43 69
      frontend/rust-lib/flowy-grid/src/services/group/group_service.rs
  24. 2 1
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  25. 7 2
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  26. 1 1
      frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs
  27. 13 13
      frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs
  28. 1 2
      frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs
  29. 233 0
      frontend/rust-lib/flowy-revision/src/cache/disk/grid_view_impl.rs
  30. 2 0
      frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs
  31. 1 6
      shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs
  32. 2 1
      shared-lib/flowy-grid-data-model/src/revision/grid_view.rs
  33. 3 3
      shared-lib/flowy-sync/src/client_folder/builder.rs
  34. 5 115
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  35. 75 2
      shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs
  36. 4 4
      shared-lib/lib-infra/src/future.rs

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

@@ -31,7 +31,7 @@ class BoardPluginBuilder implements PluginBuilder {
 
 class BoardPluginConfig implements PluginConfig {
   @override
-  bool get creatable => false;
+  bool get creatable => true;
 }
 
 class BoardPlugin extends Plugin {

+ 1 - 0
frontend/rust-lib/Cargo.lock

@@ -935,6 +935,7 @@ dependencies = [
  "atomic_refcell",
  "bytes",
  "chrono",
+ "crossbeam-utils",
  "dart-notify",
  "dashmap",
  "diesel",

+ 1 - 1
frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/down.sql

@@ -1,2 +1,2 @@
 -- This file should undo anything in `up.sql`
-DROP TABLE grid_view_table;
+DROP TABLE grid_view_rev_table;

+ 1 - 1
frontend/rust-lib/flowy-database/migrations/2022-08-15-020544_grid-view/up.sql

@@ -1,7 +1,7 @@
 -- Your SQL goes here
 
 
-CREATE TABLE grid_view_table (
+CREATE TABLE grid_view_rev_table (
     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
     object_id TEXT NOT NULL DEFAULT '',
     base_rev_id BIGINT NOT NULL DEFAULT 0,

+ 7 - 7
frontend/rust-lib/flowy-database/src/macros.rs

@@ -177,20 +177,20 @@ macro_rules! impl_rev_state_map {
             }
         }
 
-        impl std::convert::From<$target> for RevisionState {
+        impl std::convert::From<$target> for crate::disk::RevisionState {
             fn from(s: $target) -> Self {
                 match s {
-                    $target::Sync => RevisionState::Sync,
-                    $target::Ack => RevisionState::Ack,
+                    $target::Sync => crate::disk::RevisionState::Sync,
+                    $target::Ack => crate::disk::RevisionState::Ack,
                 }
             }
         }
 
-        impl std::convert::From<RevisionState> for $target {
-            fn from(s: RevisionState) -> Self {
+        impl std::convert::From<crate::disk::RevisionState> for $target {
+            fn from(s: crate::disk::RevisionState) -> Self {
                 match s {
-                    RevisionState::Sync => $target::Sync,
-                    RevisionState::Ack => $target::Ack,
+                    crate::disk::RevisionState::Sync => $target::Sync,
+                    crate::disk::RevisionState::Ack => $target::Ack,
                 }
             }
         }

+ 2 - 2
frontend/rust-lib/flowy-database/src/schema.rs

@@ -43,7 +43,7 @@ table! {
 }
 
 table! {
-    grid_view_table (id) {
+    grid_view_rev_table (id) {
         id -> Integer,
         object_id -> Text,
         base_rev_id -> BigInt,
@@ -136,7 +136,7 @@ allow_tables_to_appear_in_same_query!(
     grid_block_index_table,
     grid_meta_rev_table,
     grid_rev_table,
-    grid_view_table,
+    grid_view_rev_table,
     kv_table,
     rev_snapshot,
     rev_table,

+ 2 - 3
frontend/rust-lib/flowy-folder/src/services/folder_editor.rs

@@ -4,13 +4,12 @@ use flowy_error::{FlowyError, FlowyResult};
 use flowy_revision::{
     RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
 };
-use flowy_sync::util::make_delta_from_revisions;
+use flowy_sync::util::make_text_delta_from_revisions;
 use flowy_sync::{
     client_folder::{FolderChangeset, FolderPad},
     entities::{revision::Revision, ws_data::ServerRevisionWSData},
 };
 use lib_infra::future::FutureResult;
-use lib_ot::core::PhantomAttributes;
 
 use parking_lot::RwLock;
 use std::sync::Arc;
@@ -132,7 +131,7 @@ impl FolderEditor {
 pub struct FolderRevisionCompactor();
 impl RevisionCompactor for FolderRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        let delta = make_text_delta_from_revisions(revisions)?;
         Ok(delta.json_bytes())
     }
 }

+ 1 - 0
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -40,6 +40,7 @@ regex = "1.5.6"
 url = { version = "2"}
 futures = "0.3.15"
 atomic_refcell = "0.1.8"
+crossbeam-utils = "0.8.7"
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }

+ 6 - 9
frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs

@@ -1,4 +1,4 @@
-use crate::entities::RowPB;
+use crate::entities::{CreateRowParams, RowPB};
 use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::parser::NotEmptyStr;
@@ -11,20 +11,17 @@ pub struct CreateBoardCardPayloadPB {
     #[pb(index = 2)]
     pub group_id: String,
 }
-pub struct CreateBoardCardParams {
-    pub grid_id: String,
-    pub group_id: String,
-}
 
-impl TryInto<CreateBoardCardParams> for CreateBoardCardPayloadPB {
+impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<CreateBoardCardParams, Self::Error> {
+    fn try_into(self) -> Result<CreateRowParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?;
-        Ok(CreateBoardCardParams {
+        Ok(CreateRowParams {
             grid_id: grid_id.0,
-            group_id: group_id.0,
+            start_row_id: None,
+            group_id: Some(group_id.0),
         })
     }
 }

+ 2 - 0
frontend/rust-lib/flowy-grid/src/entities/row_entities.rs

@@ -58,6 +58,7 @@ pub struct CreateRowPayloadPB {
 pub struct CreateRowParams {
     pub grid_id: String,
     pub start_row_id: Option<String>,
+    pub group_id: Option<String>,
 }
 
 impl TryInto<CreateRowParams> for CreateRowPayloadPB {
@@ -68,6 +69,7 @@ impl TryInto<CreateRowParams> for CreateRowPayloadPB {
         Ok(CreateRowParams {
             grid_id: grid_id.0,
             start_row_id: self.start_row_id,
+            group_id: None,
         })
     }
 }

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

@@ -266,7 +266,7 @@ pub(crate) async fn create_row_handler(
 ) -> DataResult<RowPB, FlowyError> {
     let params: CreateRowParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
-    let row = editor.create_row(params.start_row_id).await?;
+    let row = editor.create_row(params).await?;
     data_result(row)
 }
 
@@ -419,8 +419,8 @@ pub(crate) async fn create_board_card_handler(
     data: Data<CreateBoardCardPayloadPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<RowPB, FlowyError> {
-    let params: CreateBoardCardParams = data.into_inner().try_into()?;
+    let params: CreateRowParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(params.grid_id.as_ref())?;
-    let row = editor.create_board_card(&params.group_id).await?;
+    let row = editor.create_row(params).await?;
     data_result(row)
 }

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

@@ -1,4 +1,4 @@
-use crate::services::block_revision_editor::GridBlockRevisionCompactor;
+use crate::services::block_editor::GridBlockRevisionCompactor;
 use crate::services::grid_editor::{GridRevisionCompactor, GridRevisionEditor};
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::persistence::kv::GridKVPersistence;

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs → frontend/rust-lib/flowy-grid/src/services/block_editor.rs

@@ -5,9 +5,9 @@ use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaCh
 use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
 use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad};
 use flowy_sync::entities::revision::Revision;
-use flowy_sync::util::make_delta_from_revisions;
+use flowy_sync::util::make_text_delta_from_revisions;
 use lib_infra::future::FutureResult;
-use lib_ot::core::PhantomAttributes;
+
 use std::borrow::Cow;
 use std::sync::Arc;
 use tokio::sync::RwLock;
@@ -200,7 +200,7 @@ impl RevisionObjectBuilder for GridBlockRevisionPadBuilder {
 pub struct GridBlockRevisionCompactor();
 impl RevisionCompactor for GridBlockRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        let delta = make_text_delta_from_revisions(revisions)?;
         Ok(delta.json_bytes())
     }
 }

+ 11 - 15
frontend/rust-lib/flowy-grid/src/services/block_manager.rs

@@ -1,7 +1,7 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::{CellChangesetPB, GridBlockChangesetPB, InsertedRowPB, RowPB};
 use crate::manager::GridUser;
-use crate::services::block_revision_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor};
+use crate::services::block_editor::{GridBlockRevisionCompactor, GridBlockRevisionEditor};
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlockSnapshot};
 use dashmap::DashMap;
@@ -17,8 +17,6 @@ use std::sync::Arc;
 
 type BlockId = String;
 pub(crate) struct GridBlockManager {
-    #[allow(dead_code)]
-    grid_id: String,
     user: Arc<dyn GridUser>,
     persistence: Arc<BlockIndexCache>,
     block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
@@ -26,16 +24,13 @@ pub(crate) struct GridBlockManager {
 
 impl GridBlockManager {
     pub(crate) async fn new(
-        grid_id: &str,
         user: &Arc<dyn GridUser>,
         block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
         persistence: Arc<BlockIndexCache>,
     ) -> FlowyResult<Self> {
         let block_editors = make_block_editors(user, block_meta_revs).await?;
         let user = user.clone();
-        let grid_id = grid_id.to_owned();
         let manager = Self {
-            grid_id,
             user,
             block_editors,
             persistence,
@@ -44,7 +39,7 @@ impl GridBlockManager {
     }
 
     // #[tracing::instrument(level = "trace", skip(self))]
-    pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
+    pub(crate) async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
         debug_assert!(!block_id.is_empty());
         match self.block_editors.get(block_id) {
             None => {
@@ -59,13 +54,13 @@ impl GridBlockManager {
 
     async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
         let block_id = self.persistence.get_block_id(row_id)?;
-        Ok(self.get_editor(&block_id).await?)
+        Ok(self.get_block_editor(&block_id).await?)
     }
 
     pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<i32> {
         let block_id = row_rev.block_id.clone();
         let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
-        let editor = self.get_editor(&row_rev.block_id).await?;
+        let editor = self.get_block_editor(&row_rev.block_id).await?;
 
         let mut index_row_order = InsertedRowPB::from(&row_rev);
         let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
@@ -82,7 +77,7 @@ impl GridBlockManager {
         let mut changesets = vec![];
         for (block_id, row_revs) in rows_by_block_id {
             let mut inserted_row_orders = vec![];
-            let editor = self.get_editor(&block_id).await?;
+            let editor = self.get_block_editor(&block_id).await?;
             let mut row_count = 0;
             for row in row_revs {
                 let _ = self.persistence.insert(&row.block_id, &row.id)?;
@@ -130,7 +125,7 @@ impl GridBlockManager {
     pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
         let row_id = row_id.to_owned();
         let block_id = self.persistence.get_block_id(&row_id)?;
-        let editor = self.get_editor(&block_id).await?;
+        let editor = self.get_block_editor(&block_id).await?;
         match editor.get_row_info(&row_id).await? {
             None => {}
             Some(row_info) => {
@@ -147,7 +142,7 @@ impl GridBlockManager {
     pub(crate) async fn delete_rows(&self, row_orders: Vec<RowPB>) -> FlowyResult<Vec<GridBlockMetaRevisionChangeset>> {
         let mut changesets = vec![];
         for grid_block in block_from_row_orders(row_orders) {
-            let editor = self.get_editor(&grid_block.id).await?;
+            let editor = self.get_block_editor(&grid_block.id).await?;
             let row_ids = grid_block
                 .rows
                 .into_iter()
@@ -207,7 +202,7 @@ impl GridBlockManager {
     }
 
     pub async fn get_row_orders(&self, block_id: &str) -> FlowyResult<Vec<RowPB>> {
-        let editor = self.get_editor(block_id).await?;
+        let editor = self.get_block_editor(block_id).await?;
         editor.get_row_infos::<&str>(None).await
     }
 
@@ -227,7 +222,7 @@ impl GridBlockManager {
             }
             Some(block_ids) => {
                 for block_id in block_ids {
-                    let editor = self.get_editor(&block_id).await?;
+                    let editor = self.get_block_editor(&block_id).await?;
                     let row_revs = editor.get_row_revs::<&str>(None).await?;
                     snapshots.push(GridBlockSnapshot { block_id, row_revs });
                 }
@@ -250,6 +245,7 @@ impl GridBlockManager {
     }
 }
 
+/// Initialize each block editor
 async fn make_block_editors(
     user: &Arc<dyn GridUser>,
     block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
@@ -264,7 +260,7 @@ async fn make_block_editors(
 }
 
 async fn make_block_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> {
-    tracing::trace!("Open block:{} meta editor", block_id);
+    tracing::trace!("Open block:{} editor", block_id);
     let token = user.token()?;
     let user_id = user.user_id()?;
     let pool = user.db_pool()?;

+ 7 - 4
frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs

@@ -3,7 +3,7 @@ use crate::entities::{
     SelectOptionFilterConfigurationPB, TextFilterConfigurationPB,
 };
 use dashmap::DashMap;
-use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
+use flowy_grid_data_model::revision::{FieldRevision, FilterConfigurationRevision, RowRevision};
 use flowy_sync::client_grid::GridRevisionPad;
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -74,6 +74,7 @@ impl FilterCache {
         this
     }
 
+    #[allow(dead_code)]
     pub(crate) fn remove(&self, filter_id: &FilterId) {
         let _ = match filter_id.field_type {
             FieldType::RichText => {
@@ -104,13 +105,15 @@ impl FilterCache {
 /// Refresh the filter according to the field id.
 pub(crate) async fn refresh_filter_cache(
     cache: Arc<FilterCache>,
-    field_ids: Option<Vec<String>>,
+    _field_ids: Option<Vec<String>>,
     grid_pad: &Arc<RwLock<GridRevisionPad>>,
 ) {
     let grid_pad = grid_pad.read().await;
-    let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default();
+    // let filters_revs = grid_pad.get_filters(field_ids).unwrap_or_default();
+    // TODO nathan
+    let filter_revs: Vec<Arc<FilterConfigurationRevision>> = vec![];
 
-    for filter_rev in filters_revs {
+    for filter_rev in filter_revs {
         match grid_pad.get_field_rev(&filter_rev.field_id) {
             None => {}
             Some((_, field_rev)) => {

+ 7 - 0
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -1,3 +1,8 @@
+#![allow(clippy::all)]
+#![allow(unused_attributes)]
+#![allow(dead_code)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
 use crate::dart_notification::{send_dart_notification, GridNotification};
 use crate::entities::{FieldType, GridBlockChangesetPB};
 use crate::services::block_manager::GridBlockManager;
@@ -22,8 +27,10 @@ use std::sync::Arc;
 use tokio::sync::RwLock;
 
 pub(crate) struct GridFilterService {
+    #[allow(dead_code)]
     scheduler: Arc<dyn GridServiceTaskScheduler>,
     grid_pad: Arc<RwLock<GridRevisionPad>>,
+    #[allow(dead_code)]
     block_manager: Arc<GridBlockManager>,
     filter_cache: Arc<FilterCache>,
     filter_result_cache: Arc<FilterResultCache>,

+ 41 - 74
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -5,15 +5,13 @@ use crate::manager::{GridTaskSchedulerRwLock, GridUser};
 use crate::services::block_manager::GridBlockManager;
 use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes};
 use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
-use crate::services::filter::{GridFilterChangeset, GridFilterService};
-
-use crate::services::grid_view_editor::GridViewRevisionEditor;
-use crate::services::group::GridGroupService;
+use crate::services::filter::GridFilterService;
+use crate::services::grid_view_manager::GridViewManager;
 use crate::services::persistence::block_index::BlockIndexCache;
 use crate::services::row::{
     make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder,
 };
-use crate::services::setting::make_grid_setting;
+
 use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::*;
@@ -22,26 +20,23 @@ use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeseri
 use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
 use flowy_sync::entities::revision::Revision;
 use flowy_sync::errors::CollaborateResult;
-use flowy_sync::util::make_delta_from_revisions;
+use flowy_sync::util::make_text_delta_from_revisions;
 use lib_infra::future::FutureResult;
-use lib_ot::core::PhantomAttributes;
+
 use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
 pub struct GridRevisionEditor {
-    pub(crate) grid_id: String,
+    pub grid_id: String,
     user: Arc<dyn GridUser>,
     grid_pad: Arc<RwLock<GridRevisionPad>>,
-    view_editor: Arc<GridViewRevisionEditor>,
+    view_manager: Arc<GridViewManager>,
     rev_manager: Arc<RevisionManager>,
     block_manager: Arc<GridBlockManager>,
 
     #[allow(dead_code)]
     pub(crate) filter_service: Arc<GridFilterService>,
-
-    #[allow(dead_code)]
-    pub(crate) group_service: Arc<RwLock<GridGroupService>>,
 }
 
 impl Drop for GridRevisionEditor {
@@ -64,20 +59,30 @@ impl GridRevisionEditor {
         let rev_manager = Arc::new(rev_manager);
         let grid_pad = Arc::new(RwLock::new(grid_pad));
 
+        // Block manager
         let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
-        let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
+        let block_manager = Arc::new(GridBlockManager::new(&user, block_meta_revs, persistence).await?);
         let filter_service =
             GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
-        let group_service =
-            GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
+
+        // View manager
+        let view_manager = Arc::new(
+            GridViewManager::new(
+                user.clone(),
+                grid_pad.clone(),
+                block_manager.clone(),
+                Arc::new(task_scheduler.clone()),
+            )
+            .await?,
+        );
         let editor = Arc::new(Self {
             grid_id: grid_id.to_owned(),
             user,
             grid_pad,
             rev_manager,
             block_manager,
+            view_manager,
             filter_service: Arc::new(filter_service),
-            group_service: Arc::new(RwLock::new(group_service)),
         });
 
         Ok(editor)
@@ -279,9 +284,15 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowPB> {
-        let row_rev = self.create_row_rev().await?;
-        self.create_row_pb(row_rev, start_row_id).await
+    pub async fn create_row(&self, params: CreateRowParams) -> FlowyResult<RowPB> {
+        let mut row_rev = self.create_row_rev().await?;
+
+        self.view_manager.update_row(&mut row_rev, &params).await;
+
+        let row_pb = self.create_row_pb(row_rev, params.start_row_id.clone()).await?;
+
+        self.view_manager.did_create_row(&row_pb, &params).await;
+        Ok(row_pb)
     }
 
     pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
@@ -331,7 +342,7 @@ impl GridRevisionEditor {
 
     pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
         let _ = self.block_manager.delete_row(row_id).await?;
-        self.group_service.read().await.did_delete_card(row_id.to_owned()).await;
+        self.view_manager.delete_row(row_id).await;
         Ok(())
     }
 
@@ -445,34 +456,15 @@ impl GridRevisionEditor {
     }
 
     pub async fn get_grid_setting(&self) -> FlowyResult<GridSettingPB> {
-        let read_guard = self.grid_pad.read().await;
-        let grid_setting_rev = read_guard.get_setting_rev();
-        let field_revs = read_guard.get_field_revs(None)?;
-        let grid_setting = make_grid_setting(grid_setting_rev, &field_revs);
-        Ok(grid_setting)
+        self.view_manager.get_setting().await
     }
 
     pub async fn get_grid_filter(&self) -> FlowyResult<Vec<GridFilterConfiguration>> {
-        let read_guard = self.grid_pad.read().await;
-        match read_guard.get_filters(None) {
-            Some(filter_revs) => Ok(filter_revs
-                .iter()
-                .map(|filter_rev| filter_rev.as_ref().into())
-                .collect::<Vec<GridFilterConfiguration>>()),
-            None => Ok(vec![]),
-        }
+        self.view_manager.get_filters().await
     }
 
     pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
-        let filter_changeset = GridFilterChangeset::from(&params);
-        let _ = self
-            .modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
-            .await?;
-
-        let filter_service = self.filter_service.clone();
-        tokio::spawn(async move {
-            filter_service.apply_changeset(filter_changeset).await;
-        });
+        let _ = self.view_manager.update_setting(params).await?;
         Ok(())
     }
 
@@ -522,22 +514,9 @@ impl GridRevisionEditor {
     }
 
     pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
-        match self.block_manager.get_row_rev(row_id).await? {
-            None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
-            Some(row_rev) => {
-                let _ = self
-                    .block_manager
-                    .move_row(row_rev.clone(), from as usize, to as usize)
-                    .await?;
-            }
-        }
-        Ok(())
+        self.view_manager.move_row(row_id, from, to).await
     }
 
-    pub async fn move_board_card(&self, group_id: &str, from: i32, to: i32) -> FlowyResult<()> {
-        self.group_service.write().await.move_card(group_id, from, to).await;
-        Ok(())
-    }
     pub async fn delta_bytes(&self) -> Bytes {
         self.grid_pad.read().await.delta_bytes()
     }
@@ -550,7 +529,10 @@ impl GridRevisionEditor {
         let mut blocks_meta_data = vec![];
         if original_blocks.len() == duplicated_blocks.len() {
             for (index, original_block_meta) in original_blocks.iter().enumerate() {
-                let grid_block_meta_editor = self.block_manager.get_editor(&original_block_meta.block_id).await?;
+                let grid_block_meta_editor = self
+                    .block_manager
+                    .get_block_editor(&original_block_meta.block_id)
+                    .await?;
                 let duplicated_block_id = &duplicated_blocks[index].block_id;
 
                 tracing::trace!("Duplicate block:{} meta data", duplicated_block_id);
@@ -569,24 +551,9 @@ impl GridRevisionEditor {
         })
     }
 
-    pub async fn create_board_card(&self, group_id: &str) -> FlowyResult<RowPB> {
-        let mut row_rev = self.create_row_rev().await?;
-        let _ = self
-            .group_service
-            .write()
-            .await
-            .update_board_card(&mut row_rev, group_id)
-            .await;
-
-        let row_pb = self.create_row_pb(row_rev, None).await?;
-        self.group_service.read().await.did_create_card(group_id, &row_pb).await;
-        Ok(row_pb)
-    }
-
     #[tracing::instrument(level = "trace", skip_all, err)]
     pub async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
-        let groups = self.group_service.write().await.load_groups().await.unwrap_or_default();
-        Ok(RepeatedGridGroupPB { items: groups })
+        self.view_manager.load_groups().await
     }
 
     async fn create_row_rev(&self) -> FlowyResult<RowRevision> {
@@ -717,7 +684,7 @@ impl RevisionCloudService for GridRevisionCloudService {
 pub struct GridRevisionCompactor();
 impl RevisionCompactor for GridRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        let delta = make_text_delta_from_revisions(revisions)?;
         Ok(delta.json_bytes())
     }
 }

+ 156 - 7
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -1,31 +1,163 @@
 use flowy_error::{FlowyError, FlowyResult};
 
+use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, GroupPB, RowPB};
+use crate::services::grid_editor_task::GridServiceTaskScheduler;
+use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService};
+use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision};
 use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder};
-use flowy_sync::client_grid::GridViewRevisionPad;
+use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
 use flowy_sync::entities::revision::Revision;
-use lib_infra::future::FutureResult;
+
+use crate::services::setting::make_grid_setting;
+use flowy_sync::entities::grid::GridSettingChangesetParams;
+use lib_infra::future::{wrap_future, AFFuture, FutureResult};
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
+pub trait GridViewRevisionDelegate: Send + Sync + 'static {
+    fn get_field_revs(&self) -> AFFuture<Vec<Arc<FieldRevision>>>;
+    fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>>;
+}
+
+pub trait GridViewRevisionDataSource: Send + Sync + 'static {
+    fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>>;
+}
+
 #[allow(dead_code)]
 pub struct GridViewRevisionEditor {
-    #[allow(dead_code)]
+    user_id: String,
+    view_id: String,
     pad: Arc<RwLock<GridViewRevisionPad>>,
-    #[allow(dead_code)]
     rev_manager: Arc<RevisionManager>,
+    delegate: Arc<dyn GridViewRevisionDelegate>,
+    data_source: Arc<dyn GridViewRevisionDataSource>,
+    group_service: Arc<RwLock<GroupService>>,
+    scheduler: Arc<dyn GridServiceTaskScheduler>,
 }
 
 impl GridViewRevisionEditor {
-    #[allow(dead_code)]
-    pub async fn new(token: &str, mut rev_manager: RevisionManager) -> FlowyResult<Self> {
+    pub(crate) async fn new<Delegate, DataSource>(
+        user_id: &str,
+        token: &str,
+        view_id: String,
+        delegate: Delegate,
+        data_source: DataSource,
+        scheduler: Arc<dyn GridServiceTaskScheduler>,
+        mut rev_manager: RevisionManager,
+    ) -> FlowyResult<Self>
+    where
+        Delegate: GridViewRevisionDelegate,
+        DataSource: GridViewRevisionDataSource,
+    {
         let cloud = Arc::new(GridViewRevisionCloudService {
             token: token.to_owned(),
         });
         let view_revision_pad = rev_manager.load::<GridViewRevisionPadBuilder>(Some(cloud)).await?;
         let pad = Arc::new(RwLock::new(view_revision_pad));
         let rev_manager = Arc::new(rev_manager);
+        let group_service = GroupService::new(Box::new(pad.clone())).await;
+        let user_id = user_id.to_owned();
+        Ok(Self {
+            pad,
+            user_id,
+            view_id,
+            rev_manager,
+            scheduler,
+            delegate: Arc::new(delegate),
+            data_source: Arc::new(data_source),
+            group_service: Arc::new(RwLock::new(group_service)),
+        })
+    }
+
+    pub(crate) async fn create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
+        match params.group_id.as_ref() {
+            None => {}
+            Some(group_id) => {
+                self.group_service
+                    .read()
+                    .await
+                    .update_row(row_rev, group_id, |field_id| self.delegate.get_field_rev(&field_id))
+                    .await;
+            }
+        }
+        todo!()
+    }
+
+    pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
+        match params.group_id.as_ref() {
+            None => {}
+            Some(group_id) => {
+                self.group_service.read().await.did_create_row(group_id, row_pb).await;
+            }
+        }
+    }
+
+    pub(crate) async fn delete_row(&self, row_id: &str) {
+        self.group_service.read().await.did_delete_card(row_id.to_owned()).await;
+    }
+
+    pub(crate) async fn load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
+        let field_revs = self.delegate.get_field_revs().await;
+        let row_revs = self.data_source.row_revs().await;
+        let mut write_guard = self.group_service.write().await;
+        match write_guard.load_groups(&field_revs, row_revs).await {
+            None => Ok(vec![]),
+            Some(groups) => Ok(groups),
+        }
+    }
+
+    pub(crate) async fn get_setting(&self) -> GridSettingPB {
+        let field_revs = self.delegate.get_field_revs().await;
+        let grid_setting = make_grid_setting(self.pad.read().await.get_setting_rev(), &field_revs);
+        grid_setting
+    }
+
+    pub(crate) async fn update_setting(&self, changeset: GridSettingChangesetParams) -> FlowyResult<()> {
+        let _ = self.modify(|pad| Ok(pad.update_setting(changeset)?)).await;
+        Ok(())
+    }
 
-        Ok(Self { pad, rev_manager })
+    pub(crate) async fn get_filters(&self) -> Vec<GridFilterConfiguration> {
+        let field_revs = self.delegate.get_field_revs().await;
+        match self.pad.read().await.get_setting_rev().get_all_filters(&field_revs) {
+            None => vec![],
+            Some(filters) => filters
+                .into_values()
+                .flatten()
+                .map(|filter| GridFilterConfiguration::from(filter.as_ref()))
+                .collect(),
+        }
+    }
+
+    async fn modify<F>(&self, f: F) -> FlowyResult<()>
+    where
+        F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,
+    {
+        let mut write_guard = self.pad.write().await;
+        match f(&mut *write_guard)? {
+            None => {}
+            Some(change) => {
+                let _ = self.apply_change(change).await?;
+            }
+        }
+        Ok(())
+    }
+
+    async fn apply_change(&self, change: GridViewRevisionChangeset) -> FlowyResult<()> {
+        let GridViewRevisionChangeset { delta, md5 } = change;
+        let user_id = self.user_id.clone();
+        let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
+        let delta_data = delta.json_bytes();
+        let revision = Revision::new(
+            &self.rev_manager.object_id,
+            base_rev_id,
+            rev_id,
+            delta_data,
+            &user_id,
+            md5,
+        );
+        let _ = self.rev_manager.add_local_revision(&revision).await?;
+        Ok(())
     }
 }
 
@@ -50,3 +182,20 @@ impl RevisionObjectBuilder for GridViewRevisionPadBuilder {
         Ok(pad)
     }
 }
+
+impl GroupConfigurationDelegate for Arc<RwLock<GridViewRevisionPad>> {
+    fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision> {
+        let view_pad = self.clone();
+        wrap_future(async move {
+            let grid_pad = view_pad.read().await;
+            let configurations = grid_pad.get_groups(&field_rev.id, &field_rev.field_type_rev);
+            match configurations {
+                None => default_group_configuration(&field_rev),
+                Some(mut configurations) => {
+                    assert_eq!(configurations.len(), 1);
+                    (&*configurations.pop().unwrap()).clone()
+                }
+            }
+        })
+    }
+}

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

@@ -0,0 +1,204 @@
+use crate::manager::GridUser;
+use crate::services::grid_view_editor::{GridViewRevisionDataSource, GridViewRevisionDelegate, GridViewRevisionEditor};
+use bytes::Bytes;
+
+use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB};
+use crate::services::grid_editor_task::GridServiceTaskScheduler;
+
+use crate::services::block_manager::GridBlockManager;
+use dashmap::DashMap;
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
+use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
+use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
+use flowy_sync::client_grid::GridRevisionPad;
+use flowy_sync::entities::revision::Revision;
+
+use flowy_sync::util::make_text_delta_from_revisions;
+
+use flowy_sync::entities::grid::GridSettingChangesetParams;
+
+use lib_infra::future::{wrap_future, AFFuture};
+use std::sync::Arc;
+use tokio::sync::RwLock;
+
+type ViewId = String;
+
+pub(crate) struct GridViewManager {
+    user: Arc<dyn GridUser>,
+    grid_pad: Arc<RwLock<GridRevisionPad>>,
+    block_manager: Arc<GridBlockManager>,
+    view_editors: DashMap<ViewId, Arc<GridViewRevisionEditor>>,
+    scheduler: Arc<dyn GridServiceTaskScheduler>,
+}
+
+impl GridViewManager {
+    pub(crate) async fn new(
+        user: Arc<dyn GridUser>,
+        grid_pad: Arc<RwLock<GridRevisionPad>>,
+        block_manager: Arc<GridBlockManager>,
+        scheduler: Arc<dyn GridServiceTaskScheduler>,
+    ) -> FlowyResult<Self> {
+        Ok(Self {
+            user,
+            grid_pad,
+            scheduler,
+            block_manager,
+            view_editors: DashMap::default(),
+        })
+    }
+
+    pub(crate) async fn update_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
+        for view_editor in self.view_editors.iter() {
+            view_editor.create_row(row_rev, params).await;
+        }
+    }
+
+    pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
+        for view_editor in self.view_editors.iter() {
+            view_editor.did_create_row(row_pb, params).await;
+        }
+    }
+
+    pub(crate) async fn delete_row(&self, row_id: &str) {
+        for view_editor in self.view_editors.iter() {
+            view_editor.delete_row(row_id).await;
+        }
+    }
+
+    pub(crate) async fn get_setting(&self) -> FlowyResult<GridSettingPB> {
+        let view_editor = self.get_default_view_editor().await?;
+        Ok(view_editor.get_setting().await)
+    }
+
+    pub(crate) async fn update_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
+        let view_editor = self.get_default_view_editor().await?;
+        let _ = view_editor.update_setting(params).await?;
+        Ok(())
+    }
+
+    pub(crate) async fn get_filters(&self) -> FlowyResult<Vec<GridFilterConfiguration>> {
+        let view_editor = self.get_default_view_editor().await?;
+        Ok(view_editor.get_filters().await)
+    }
+
+    pub(crate) async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
+        let view_editor = self.get_default_view_editor().await?;
+        let groups = view_editor.load_groups().await?;
+        Ok(RepeatedGridGroupPB { items: groups })
+    }
+
+    pub(crate) async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
+        match self.block_manager.get_row_rev(row_id).await? {
+            None => tracing::warn!("Move row failed, can not find the row:{}", row_id),
+            Some(row_rev) => {
+                let _ = self
+                    .block_manager
+                    .move_row(row_rev.clone(), from as usize, to as usize)
+                    .await?;
+            }
+        }
+        Ok(())
+    }
+
+    pub(crate) async fn get_view_editor(&self, view_id: &str) -> FlowyResult<Arc<GridViewRevisionEditor>> {
+        debug_assert!(!view_id.is_empty());
+        match self.view_editors.get(view_id) {
+            None => {
+                let editor = Arc::new(
+                    make_view_editor(
+                        &self.user,
+                        view_id,
+                        self.grid_pad.clone(),
+                        self.block_manager.clone(),
+                        self.scheduler.clone(),
+                    )
+                    .await?,
+                );
+                self.view_editors.insert(view_id.to_owned(), editor.clone());
+                Ok(editor)
+            }
+            Some(view_editor) => Ok(view_editor.clone()),
+        }
+    }
+
+    async fn get_default_view_editor(&self) -> FlowyResult<Arc<GridViewRevisionEditor>> {
+        let grid_id = self.grid_pad.read().await.grid_id();
+        self.get_view_editor(&grid_id).await
+    }
+}
+
+async fn make_view_editor<Delegate, DataSource>(
+    user: &Arc<dyn GridUser>,
+    view_id: &str,
+    delegate: Delegate,
+    data_source: DataSource,
+    scheduler: Arc<dyn GridServiceTaskScheduler>,
+) -> FlowyResult<GridViewRevisionEditor>
+where
+    Delegate: GridViewRevisionDelegate,
+    DataSource: GridViewRevisionDataSource,
+{
+    tracing::trace!("Open view:{} editor", view_id);
+    let token = user.token()?;
+    let user_id = user.user_id()?;
+    let pool = user.db_pool()?;
+    let view_id = view_id.to_owned();
+
+    let disk_cache = SQLiteGridViewRevisionPersistence::new(&user_id, pool.clone());
+    let rev_persistence = RevisionPersistence::new(&user_id, &view_id, disk_cache);
+    let rev_compactor = GridViewRevisionCompactor();
+
+    let snapshot_persistence = SQLiteRevisionSnapshotPersistence::new(&view_id, pool);
+    let rev_manager = RevisionManager::new(&user_id, &view_id, rev_persistence, rev_compactor, snapshot_persistence);
+    GridViewRevisionEditor::new(&user_id, &token, view_id, delegate, data_source, scheduler, rev_manager).await
+}
+
+pub struct GridViewRevisionCompactor();
+impl RevisionCompactor for GridViewRevisionCompactor {
+    fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
+        let delta = make_text_delta_from_revisions(revisions)?;
+        Ok(delta.json_bytes())
+    }
+}
+
+impl GridViewRevisionDataSource for Arc<GridBlockManager> {
+    fn row_revs(&self) -> AFFuture<Vec<Arc<RowRevision>>> {
+        let block_manager = self.clone();
+
+        wrap_future(async move {
+            let blocks = block_manager.get_block_snapshots(None).await.unwrap();
+            blocks
+                .into_iter()
+                .map(|block| block.row_revs)
+                .flatten()
+                .collect::<Vec<Arc<RowRevision>>>()
+        })
+    }
+}
+
+impl GridViewRevisionDelegate for Arc<RwLock<GridRevisionPad>> {
+    fn get_field_revs(&self) -> AFFuture<Vec<Arc<FieldRevision>>> {
+        let pad = self.clone();
+        wrap_future(async move {
+            match pad.read().await.get_field_revs(None) {
+                Ok(field_revs) => field_revs,
+                Err(e) => {
+                    tracing::error!("[GridViewRevisionDelegate] get field revisions failed: {}", e);
+                    vec![]
+                }
+            }
+        })
+    }
+
+    fn get_field_rev(&self, field_id: &str) -> AFFuture<Option<Arc<FieldRevision>>> {
+        let pad = self.clone();
+        let field_id = field_id.to_owned();
+        wrap_future(async move {
+            pad.read()
+                .await
+                .get_field_rev(&field_id)
+                .map(|(_, field_rev)| field_rev.clone())
+        })
+    }
+}

+ 1 - 4
frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs

@@ -4,9 +4,7 @@ use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
 use std::sync::Arc;
 
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
-use crate::services::group::{
-    Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
-};
+use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable};
 
 pub type CheckboxGroupController =
     GroupController<CheckboxGroupConfigurationPB, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser>;
@@ -45,7 +43,6 @@ impl GroupGenerator for CheckboxGroupGenerator {
     fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         _type_option: &Option<Self::TypeOptionType>,
-        _cell_content_provider: &dyn GroupCellContentProvider,
     ) -> Vec<Group> {
         let check_group = Group {
             id: "true".to_string(),

+ 2 - 15
frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs

@@ -11,14 +11,6 @@ use indexmap::IndexMap;
 use std::marker::PhantomData;
 use std::sync::Arc;
 
-pub trait GroupCellContentProvider {
-    /// We need to group the rows base on the deduplication cell content when the field type is
-    /// RichText.
-    fn deduplication_cell_content(&self, _field_id: &str) -> Vec<String> {
-        vec![]
-    }
-}
-
 pub trait GroupGenerator {
     type ConfigurationType;
     type TypeOptionType;
@@ -26,7 +18,6 @@ pub trait GroupGenerator {
     fn generate_groups(
         configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
-        cell_content_provider: &dyn GroupCellContentProvider,
     ) -> Vec<Group>;
 }
 
@@ -86,18 +77,14 @@ where
     T: TypeOptionDataDeserializer,
     G: GroupGenerator<ConfigurationType = C, TypeOptionType = T>,
 {
-    pub fn new(
-        field_rev: &Arc<FieldRevision>,
-        configuration: GroupConfigurationRevision,
-        cell_content_provider: &dyn GroupCellContentProvider,
-    ) -> FlowyResult<Self> {
+    pub fn new(field_rev: &Arc<FieldRevision>, configuration: GroupConfigurationRevision) -> FlowyResult<Self> {
         let configuration = match configuration.content {
             None => None,
             Some(content) => Some(C::try_from(Bytes::from(content))?),
         };
         let field_type_rev = field_rev.field_type_rev;
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
-        let groups = G::generate_groups(&configuration, &type_option, cell_content_provider);
+        let groups = G::generate_groups(&configuration, &type_option);
 
         let default_group = Group {
             id: DEFAULT_GROUP_ID.to_owned(),

+ 1 - 5
frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs

@@ -8,9 +8,7 @@ use std::sync::Arc;
 use crate::services::field::{
     MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
 };
-use crate::services::group::{
-    Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
-};
+use crate::services::group::{Group, GroupActionHandler, GroupController, GroupGenerator, Groupable};
 
 // SingleSelect
 pub type SingleSelectGroupController = GroupController<
@@ -59,7 +57,6 @@ impl GroupGenerator for SingleSelectGroupGenerator {
     fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
-        _cell_content_provider: &dyn GroupCellContentProvider,
     ) -> Vec<Group> {
         match type_option {
             None => vec![],
@@ -125,7 +122,6 @@ impl GroupGenerator for MultiSelectGroupGenerator {
     fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
-        _cell_content_provider: &dyn GroupCellContentProvider,
     ) -> Vec<Group> {
         match type_option {
             None => vec![],

+ 43 - 69
frontend/rust-lib/flowy-grid/src/services/group/group_service.rs

@@ -4,55 +4,42 @@ use crate::entities::{
     NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB,
     UrlGroupConfigurationPB,
 };
-use crate::services::block_manager::GridBlockManager;
-use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::group::{
-    CheckboxGroupController, GroupActionHandler, GroupCellContentProvider, MultiSelectGroupController,
-    SingleSelectGroupController,
+    CheckboxGroupController, GroupActionHandler, MultiSelectGroupController, SingleSelectGroupController,
 };
-
 use bytes::Bytes;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision};
-use flowy_sync::client_grid::GridRevisionPad;
-
+use lib_infra::future::AFFuture;
+use std::future::Future;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
-pub(crate) struct GridGroupService {
-    #[allow(dead_code)]
-    scheduler: Arc<dyn GridServiceTaskScheduler>,
-    grid_pad: Arc<RwLock<GridRevisionPad>>,
-    block_manager: Arc<GridBlockManager>,
-    group_action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
+pub trait GroupConfigurationDelegate: Send + Sync + 'static {
+    fn get_group_configuration(&self, field_rev: Arc<FieldRevision>) -> AFFuture<GroupConfigurationRevision>;
+}
+
+pub(crate) struct GroupService {
+    delegate: Box<dyn GroupConfigurationDelegate>,
+    action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
 }
 
-impl GridGroupService {
-    pub(crate) async fn new<S: GridServiceTaskScheduler>(
-        grid_pad: Arc<RwLock<GridRevisionPad>>,
-        block_manager: Arc<GridBlockManager>,
-        scheduler: S,
-    ) -> Self {
-        let scheduler = Arc::new(scheduler);
+impl GroupService {
+    pub(crate) async fn new(delegate: Box<dyn GroupConfigurationDelegate>) -> Self {
         Self {
-            scheduler,
-            grid_pad,
-            block_manager,
-            group_action_handler: None,
+            delegate,
+            action_handler: None,
         }
     }
 
-    pub(crate) async fn load_groups(&mut self) -> Option<Vec<GroupPB>> {
-        let field_rev = find_group_field(self.grid_pad.read().await.fields()).unwrap();
+    pub(crate) async fn load_groups(
+        &mut self,
+        field_revs: &[Arc<FieldRevision>],
+        row_revs: Vec<Arc<RowRevision>>,
+    ) -> Option<Vec<GroupPB>> {
+        let field_rev = find_group_field(field_revs).unwrap();
         let field_type: FieldType = field_rev.field_type_rev.into();
-        let configuration = self.get_group_configuration(&field_rev).await;
-
-        let blocks = self.block_manager.get_block_snapshots(None).await.unwrap();
-        let row_revs = blocks
-            .into_iter()
-            .map(|block| block.row_revs)
-            .flatten()
-            .collect::<Vec<Arc<RowRevision>>>();
+        let configuration = self.delegate.get_group_configuration(field_rev.clone()).await;
 
         match self
             .build_groups(&field_type, &field_rev, row_revs, configuration)
@@ -63,36 +50,25 @@ impl GridGroupService {
         }
     }
 
-    #[tracing::instrument(level = "debug", skip(self, row_rev))]
-    pub(crate) async fn update_board_card(&self, row_rev: &mut RowRevision, group_id: &str) {
-        if let Some(group_action_handler) = self.group_action_handler.as_ref() {
+    pub(crate) async fn update_row<F, O>(&self, row_rev: &mut RowRevision, group_id: &str, f: F)
+    where
+        F: FnOnce(String) -> O,
+        O: Future<Output = Option<Arc<FieldRevision>>> + Send + Sync + 'static,
+    {
+        if let Some(group_action_handler) = self.action_handler.as_ref() {
             let field_id = group_action_handler.read().await.field_id().to_owned();
-
-            match self.grid_pad.read().await.get_field_rev(&field_id) {
-                None => tracing::warn!("Fail to create card because the field does not exist"),
-                Some((_, field_rev)) => {
+            match f(field_id).await {
+                None => {}
+                Some(field_rev) => {
                     group_action_handler
                         .write()
                         .await
-                        .update_card(row_rev, field_rev, group_id);
+                        .update_card(row_rev, &field_rev, group_id);
                 }
             }
         }
     }
-
-    pub(crate) async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
-        let grid_pad = self.grid_pad.read().await;
-        let setting = grid_pad.get_setting_rev();
-        let configurations = setting.get_groups(&field_rev.id, &field_rev.field_type_rev);
-        match configurations {
-            None => default_group_configuration(field_rev),
-            Some(mut configurations) => {
-                assert_eq!(configurations.len(), 1);
-                (&*configurations.pop().unwrap()).clone()
-            }
-        }
-    }
-
+    #[allow(dead_code)]
     pub async fn move_card(&self, _group_id: &str, _from: i32, _to: i32) {
         // BoardCardChangesetPB {
         //     group_id: "".to_string(),
@@ -103,20 +79,20 @@ impl GridGroupService {
         // let row_pb = make_row_from_row_rev(row_rev);
         todo!()
     }
-
+    #[allow(dead_code)]
     pub async fn did_delete_card(&self, _row_id: String) {
         // let changeset = BoardCardChangesetPB::delete(group_id.to_owned(), vec![row_id]);
         // self.notify_did_update_board(changeset).await;
         todo!()
     }
 
-    pub async fn did_create_card(&self, group_id: &str, row_pb: &RowPB) {
+    pub async fn did_create_row(&self, group_id: &str, row_pb: &RowPB) {
         let changeset = BoardCardChangesetPB::insert(group_id.to_owned(), vec![row_pb.clone()]);
         self.notify_did_update_board(changeset).await;
     }
 
     pub async fn notify_did_update_board(&self, changeset: BoardCardChangesetPB) {
-        if self.group_action_handler.is_none() {
+        if self.action_handler.is_none() {
             return;
         }
         send_dart_notification(&changeset.group_id, GridNotification::DidUpdateBoard)
@@ -143,16 +119,16 @@ impl GridGroupService {
                 // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
             }
             FieldType::SingleSelect => {
-                let controller = SingleSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
-                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
+                let controller = SingleSelectGroupController::new(field_rev, configuration)?;
+                self.action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             FieldType::MultiSelect => {
-                let controller = MultiSelectGroupController::new(field_rev, configuration, &self.grid_pad)?;
-                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
+                let controller = MultiSelectGroupController::new(field_rev, configuration)?;
+                self.action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             FieldType::Checkbox => {
-                let controller = CheckboxGroupController::new(field_rev, configuration, &self.grid_pad)?;
-                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
+                let controller = CheckboxGroupController::new(field_rev, configuration)?;
+                self.action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             FieldType::URL => {
                 // let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
@@ -160,7 +136,7 @@ impl GridGroupService {
         };
 
         let mut groups = vec![];
-        if let Some(group_action_handler) = self.group_action_handler.as_ref() {
+        if let Some(group_action_handler) = self.action_handler.as_ref() {
             let mut write_guard = group_action_handler.write().await;
             let _ = write_guard.group_rows(&row_revs, field_rev)?;
             groups = write_guard.get_groups();
@@ -182,9 +158,7 @@ fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevisi
     field_rev
 }
 
-impl GroupCellContentProvider for Arc<RwLock<GridRevisionPad>> {}
-
-fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
+pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
     let field_type: FieldType = field_rev.field_type_rev.into();
     let bytes: Bytes = match field_type {
         FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(),

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

@@ -1,13 +1,14 @@
 mod util;
 
+pub mod block_editor;
 mod block_manager;
-pub mod block_revision_editor;
 pub mod cell;
 pub mod field;
 mod filter;
 pub mod grid_editor;
 mod grid_editor_task;
 pub mod grid_view_editor;
+pub mod grid_view_manager;
 pub mod group;
 pub mod persistence;
 pub mod row;

+ 7 - 2
frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs

@@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
 use crate::grid::block_test::util::GridRowTestBuilder;
 use crate::grid::grid_editor::GridEditorTest;
 
-use flowy_grid::entities::{FieldType, GridCellIdParams, RowPB};
+use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, RowPB};
 use flowy_grid::services::field::*;
 use flowy_grid_data_model::revision::{
     GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
@@ -77,7 +77,12 @@ impl GridRowTest {
     pub async fn run_script(&mut self, script: RowScript) {
         match script {
             RowScript::CreateEmptyRow => {
-                let row_order = self.editor.create_row(None).await.unwrap();
+                let params = CreateRowParams {
+                    grid_id: self.editor.grid_id.clone(),
+                    start_row_id: None,
+                    group_id: None,
+                };
+                let row_order = self.editor.create_row(params).await.unwrap();
                 self.row_order_by_row_id
                     .insert(row_order.row_id().to_owned(), row_order);
                 self.row_revs = self.get_row_revs().await;

+ 1 - 1
frontend/rust-lib/flowy-revision/src/cache/disk/document_impl.rs

@@ -1,5 +1,5 @@
 use crate::cache::disk::RevisionDiskCache;
-use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
+use crate::disk::{RevisionChangeset, RevisionRecord};
 use bytes::Bytes;
 use diesel::{sql_types::Integer, update, SqliteConnection};
 use flowy_database::{

+ 13 - 13
frontend/rust-lib/flowy-revision/src/cache/disk/grid_block_impl.rs

@@ -1,5 +1,5 @@
 use crate::cache::disk::RevisionDiskCache;
-use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
+use crate::disk::{RevisionChangeset, RevisionRecord};
 use bytes::Bytes;
 use diesel::{sql_types::Integer, update, SqliteConnection};
 use flowy_database::{
@@ -103,7 +103,7 @@ impl GridMetaRevisionSql {
                     record.revision.object_id,
                     record.revision.rev_id
                 );
-                let rev_state: GridMetaRevisionState = record.state.into();
+                let rev_state: GridBlockRevisionState = record.state.into();
                 (
                     dsl::object_id.eq(record.revision.object_id),
                     dsl::base_rev_id.eq(record.revision.base_rev_id),
@@ -121,7 +121,7 @@ impl GridMetaRevisionSql {
     }
 
     fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
-        let state: GridMetaRevisionState = changeset.state.clone().into();
+        let state: GridBlockRevisionState = changeset.state.clone().into();
         let filter = dsl::grid_meta_rev_table
             .filter(dsl::rev_id.eq(changeset.rev_id.as_ref()))
             .filter(dsl::object_id.eq(changeset.object_id));
@@ -146,7 +146,7 @@ impl GridMetaRevisionSql {
         if let Some(rev_ids) = rev_ids {
             sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
         }
-        let rows = sql.order(dsl::rev_id.asc()).load::<GridMetaRevisionTable>(conn)?;
+        let rows = sql.order(dsl::rev_id.asc()).load::<GridBlockRevisionTable>(conn)?;
         let records = rows
             .into_iter()
             .map(|row| mk_revision_record_from_table(user_id, row))
@@ -166,7 +166,7 @@ impl GridMetaRevisionSql {
             .filter(dsl::rev_id.le(range.end))
             .filter(dsl::object_id.eq(object_id))
             .order(dsl::rev_id.asc())
-            .load::<GridMetaRevisionTable>(conn)?;
+            .load::<GridBlockRevisionTable>(conn)?;
 
         let revisions = rev_tables
             .into_iter()
@@ -192,31 +192,31 @@ impl GridMetaRevisionSql {
 
 #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
 #[table_name = "grid_meta_rev_table"]
-struct GridMetaRevisionTable {
+struct GridBlockRevisionTable {
     id: i32,
     object_id: String,
     base_rev_id: i64,
     rev_id: i64,
     data: Vec<u8>,
-    state: GridMetaRevisionState,
+    state: GridBlockRevisionState,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
 #[repr(i32)]
 #[sql_type = "Integer"]
-pub enum GridMetaRevisionState {
+pub enum GridBlockRevisionState {
     Sync = 0,
     Ack = 1,
 }
-impl_sql_integer_expression!(GridMetaRevisionState);
-impl_rev_state_map!(GridMetaRevisionState);
-impl std::default::Default for GridMetaRevisionState {
+impl_sql_integer_expression!(GridBlockRevisionState);
+impl_rev_state_map!(GridBlockRevisionState);
+impl std::default::Default for GridBlockRevisionState {
     fn default() -> Self {
-        GridMetaRevisionState::Sync
+        GridBlockRevisionState::Sync
     }
 }
 
-fn mk_revision_record_from_table(user_id: &str, table: GridMetaRevisionTable) -> RevisionRecord {
+fn mk_revision_record_from_table(user_id: &str, table: GridBlockRevisionTable) -> RevisionRecord {
     let md5 = md5(&table.data);
     let revision = Revision::new(
         &table.object_id,

+ 1 - 2
frontend/rust-lib/flowy-revision/src/cache/disk/grid_impl.rs

@@ -1,6 +1,5 @@
 use crate::cache::disk::RevisionDiskCache;
-use crate::disk::{RevisionChangeset, RevisionRecord, RevisionState};
-
+use crate::disk::{RevisionChangeset, RevisionRecord};
 use bytes::Bytes;
 use diesel::{sql_types::Integer, update, SqliteConnection};
 use flowy_database::{

+ 233 - 0
frontend/rust-lib/flowy-revision/src/cache/disk/grid_view_impl.rs

@@ -0,0 +1,233 @@
+use crate::disk::{RevisionChangeset, RevisionDiskCache, RevisionRecord};
+use bytes::Bytes;
+use diesel::{sql_types::Integer, update, SqliteConnection};
+use flowy_database::{
+    impl_sql_integer_expression, insert_or_ignore_into,
+    prelude::*,
+    schema::{grid_view_rev_table, grid_view_rev_table::dsl},
+    ConnectionPool,
+};
+use flowy_error::{internal_error, FlowyError, FlowyResult};
+use flowy_sync::{
+    entities::revision::{Revision, RevisionRange},
+    util::md5,
+};
+use std::sync::Arc;
+
+pub struct SQLiteGridViewRevisionPersistence {
+    user_id: String,
+    pub(crate) pool: Arc<ConnectionPool>,
+}
+
+impl SQLiteGridViewRevisionPersistence {
+    pub fn new(user_id: &str, pool: Arc<ConnectionPool>) -> Self {
+        Self {
+            user_id: user_id.to_owned(),
+            pool,
+        }
+    }
+}
+
+impl RevisionDiskCache for SQLiteGridViewRevisionPersistence {
+    type Error = FlowyError;
+
+    fn create_revision_records(&self, revision_records: Vec<RevisionRecord>) -> Result<(), Self::Error> {
+        let conn = self.pool.get().map_err(internal_error)?;
+        let _ = GridViewRevisionSql::create(revision_records, &*conn)?;
+        Ok(())
+    }
+
+    fn read_revision_records(
+        &self,
+        object_id: &str,
+        rev_ids: Option<Vec<i64>>,
+    ) -> Result<Vec<RevisionRecord>, Self::Error> {
+        let conn = self.pool.get().map_err(internal_error)?;
+        let records = GridViewRevisionSql::read(&self.user_id, object_id, rev_ids, &*conn)?;
+        Ok(records)
+    }
+
+    fn read_revision_records_with_range(
+        &self,
+        object_id: &str,
+        range: &RevisionRange,
+    ) -> Result<Vec<RevisionRecord>, Self::Error> {
+        let conn = &*self.pool.get().map_err(internal_error)?;
+        let revisions = GridViewRevisionSql::read_with_range(&self.user_id, object_id, range.clone(), conn)?;
+        Ok(revisions)
+    }
+
+    fn update_revision_record(&self, changesets: Vec<RevisionChangeset>) -> FlowyResult<()> {
+        let conn = &*self.pool.get().map_err(internal_error)?;
+        let _ = conn.immediate_transaction::<_, FlowyError, _>(|| {
+            for changeset in changesets {
+                let _ = GridViewRevisionSql::update(changeset, conn)?;
+            }
+            Ok(())
+        })?;
+        Ok(())
+    }
+
+    fn delete_revision_records(&self, object_id: &str, rev_ids: Option<Vec<i64>>) -> Result<(), Self::Error> {
+        let conn = &*self.pool.get().map_err(internal_error)?;
+        let _ = GridViewRevisionSql::delete(object_id, rev_ids, conn)?;
+        Ok(())
+    }
+
+    fn delete_and_insert_records(
+        &self,
+        object_id: &str,
+        deleted_rev_ids: Option<Vec<i64>>,
+        inserted_records: Vec<RevisionRecord>,
+    ) -> Result<(), Self::Error> {
+        let conn = self.pool.get().map_err(internal_error)?;
+        conn.immediate_transaction::<_, FlowyError, _>(|| {
+            let _ = GridViewRevisionSql::delete(object_id, deleted_rev_ids, &*conn)?;
+            let _ = GridViewRevisionSql::create(inserted_records, &*conn)?;
+            Ok(())
+        })
+    }
+}
+
+struct GridViewRevisionSql();
+impl GridViewRevisionSql {
+    fn create(revision_records: Vec<RevisionRecord>, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        // Batch insert: https://diesel.rs/guides/all-about-inserts.html
+        let records = revision_records
+            .into_iter()
+            .map(|record| {
+                tracing::trace!(
+                    "[GridViewRevisionSql] create revision: {}:{:?}",
+                    record.revision.object_id,
+                    record.revision.rev_id
+                );
+                let rev_state: GridViewRevisionState = record.state.into();
+                (
+                    dsl::object_id.eq(record.revision.object_id),
+                    dsl::base_rev_id.eq(record.revision.base_rev_id),
+                    dsl::rev_id.eq(record.revision.rev_id),
+                    dsl::data.eq(record.revision.delta_data),
+                    dsl::state.eq(rev_state),
+                )
+            })
+            .collect::<Vec<_>>();
+
+        let _ = insert_or_ignore_into(dsl::grid_view_rev_table)
+            .values(&records)
+            .execute(conn)?;
+        Ok(())
+    }
+
+    fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        let state: GridViewRevisionState = changeset.state.clone().into();
+        let filter = dsl::grid_view_rev_table
+            .filter(dsl::rev_id.eq(changeset.rev_id.as_ref()))
+            .filter(dsl::object_id.eq(changeset.object_id));
+        let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
+        tracing::debug!(
+            "[GridViewRevisionSql] update revision:{} state:to {:?}",
+            changeset.rev_id,
+            changeset.state
+        );
+        Ok(())
+    }
+
+    fn read(
+        user_id: &str,
+        object_id: &str,
+        rev_ids: Option<Vec<i64>>,
+        conn: &SqliteConnection,
+    ) -> Result<Vec<RevisionRecord>, FlowyError> {
+        let mut sql = dsl::grid_view_rev_table
+            .filter(dsl::object_id.eq(object_id))
+            .into_boxed();
+        if let Some(rev_ids) = rev_ids {
+            sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
+        }
+        let rows = sql.order(dsl::rev_id.asc()).load::<GridViewRevisionTable>(conn)?;
+        let records = rows
+            .into_iter()
+            .map(|row| mk_revision_record_from_table(user_id, row))
+            .collect::<Vec<_>>();
+
+        Ok(records)
+    }
+
+    fn read_with_range(
+        user_id: &str,
+        object_id: &str,
+        range: RevisionRange,
+        conn: &SqliteConnection,
+    ) -> Result<Vec<RevisionRecord>, FlowyError> {
+        let rev_tables = dsl::grid_view_rev_table
+            .filter(dsl::rev_id.ge(range.start))
+            .filter(dsl::rev_id.le(range.end))
+            .filter(dsl::object_id.eq(object_id))
+            .order(dsl::rev_id.asc())
+            .load::<GridViewRevisionTable>(conn)?;
+
+        let revisions = rev_tables
+            .into_iter()
+            .map(|table| mk_revision_record_from_table(user_id, table))
+            .collect::<Vec<_>>();
+        Ok(revisions)
+    }
+
+    fn delete(object_id: &str, rev_ids: Option<Vec<i64>>, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        let mut sql = diesel::delete(dsl::grid_view_rev_table).into_boxed();
+        sql = sql.filter(dsl::object_id.eq(object_id));
+
+        if let Some(rev_ids) = rev_ids {
+            tracing::trace!("[GridViewRevisionSql] Delete revision: {}:{:?}", object_id, rev_ids);
+            sql = sql.filter(dsl::rev_id.eq_any(rev_ids));
+        }
+
+        let affected_row = sql.execute(conn)?;
+        tracing::trace!("[GridViewRevisionSql] Delete {} rows", affected_row);
+        Ok(())
+    }
+}
+
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
+#[table_name = "grid_view_rev_table"]
+struct GridViewRevisionTable {
+    id: i32,
+    object_id: String,
+    base_rev_id: i64,
+    rev_id: i64,
+    data: Vec<u8>,
+    state: GridViewRevisionState,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
+#[repr(i32)]
+#[sql_type = "Integer"]
+pub enum GridViewRevisionState {
+    Sync = 0,
+    Ack = 1,
+}
+impl_sql_integer_expression!(GridViewRevisionState);
+impl_rev_state_map!(GridViewRevisionState);
+
+impl std::default::Default for GridViewRevisionState {
+    fn default() -> Self {
+        GridViewRevisionState::Sync
+    }
+}
+
+fn mk_revision_record_from_table(user_id: &str, table: GridViewRevisionTable) -> RevisionRecord {
+    let md5 = md5(&table.data);
+    let revision = Revision::new(
+        &table.object_id,
+        table.base_rev_id,
+        table.rev_id,
+        Bytes::from(table.data),
+        user_id,
+        md5,
+    );
+    RevisionRecord {
+        revision,
+        state: table.state.into(),
+        write_to_disk: false,
+    }
+}

+ 2 - 0
frontend/rust-lib/flowy-revision/src/cache/disk/mod.rs

@@ -1,10 +1,12 @@
 mod document_impl;
 mod grid_block_impl;
 mod grid_impl;
+mod grid_view_impl;
 
 pub use document_impl::*;
 pub use grid_block_impl::*;
 pub use grid_impl::*;
+pub use grid_view_impl::*;
 
 use flowy_error::FlowyResult;
 use flowy_sync::entities::revision::{RevId, Revision, RevisionRange};

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

@@ -1,4 +1,4 @@
-use crate::revision::{GridBlockRevision, SettingRevision};
+use crate::revision::GridBlockRevision;
 use bytes::Bytes;
 use indexmap::IndexMap;
 use nanoid::nanoid;
@@ -23,9 +23,6 @@ pub struct GridRevision {
     pub grid_id: String,
     pub fields: Vec<Arc<FieldRevision>>,
     pub blocks: Vec<Arc<GridBlockMetaRevision>>,
-
-    #[serde(default)]
-    pub setting: SettingRevision,
 }
 
 impl GridRevision {
@@ -34,7 +31,6 @@ impl GridRevision {
             grid_id: grid_id.to_owned(),
             fields: vec![],
             blocks: vec![],
-            setting: SettingRevision::default(),
         }
     }
 
@@ -43,7 +39,6 @@ impl GridRevision {
             grid_id: grid_id.to_owned(),
             fields: context.field_revs,
             blocks: context.blocks.into_iter().map(Arc::new).collect(),
-            setting: Default::default(),
         }
     }
 }

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

@@ -2,6 +2,7 @@ use crate::revision::SettingRevision;
 use nanoid::nanoid;
 use serde::{Deserialize, Serialize};
 
+#[allow(dead_code)]
 pub fn gen_grid_view_id() -> String {
     nanoid!(6)
 }
@@ -23,7 +24,7 @@ pub struct GridViewRevision {
 impl GridViewRevision {
     pub fn new(grid_id: String) -> Self {
         GridViewRevision {
-            view_id: gen_grid_view_id(),
+            view_id: grid_id.clone(),
             grid_id,
             setting: Default::default(),
             row_orders: vec![],

+ 3 - 3
shared-lib/flowy-sync/src/client_folder/builder.rs

@@ -1,5 +1,5 @@
 use crate::entities::folder::FolderDelta;
-use crate::util::make_delta_from_revisions;
+use crate::util::make_text_delta_from_revisions;
 use crate::{
     client_folder::{default_folder_delta, FolderPad},
     entities::revision::Revision,
@@ -7,7 +7,7 @@ use crate::{
 };
 
 use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
-use lib_ot::core::PhantomAttributes;
+
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize)]
@@ -37,7 +37,7 @@ impl FolderPadBuilder {
     }
 
     pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
-        let mut folder_delta: FolderDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        let mut folder_delta: FolderDelta = make_text_delta_from_revisions(revisions)?;
         if folder_delta.is_empty() {
             folder_delta = default_folder_delta();
         }

+ 5 - 115
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -1,14 +1,11 @@
-use crate::entities::grid::{
-    CreateGridFilterParams, CreateGridGroupParams, FieldChangesetParams, GridSettingChangesetParams,
-};
+use crate::entities::grid::FieldChangesetParams;
 use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{internal_error, CollaborateError, CollaborateResult};
-use crate::util::{cal_diff, make_delta_from_revisions};
+use crate::util::{cal_diff, make_text_delta_from_revisions};
 use bytes::Bytes;
 use flowy_grid_data_model::revision::{
-    gen_block_id, gen_grid_filter_id, gen_grid_group_id, gen_grid_id, FieldRevision, FieldTypeRevision,
-    FilterConfigurationRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, GridRevision,
-    GroupConfigurationRevision, SettingRevision,
+    gen_block_id, gen_grid_id, FieldRevision, FieldTypeRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset,
+    GridRevision,
 };
 use lib_infra::util::move_vec_element;
 use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
@@ -68,7 +65,7 @@ impl GridRevisionPad {
     }
 
     pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
-        let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        let grid_delta: GridRevisionDelta = make_text_delta_from_revisions(revisions)?;
         Self::from_delta(grid_delta)
     }
 
@@ -347,95 +344,6 @@ impl GridRevisionPad {
         })
     }
 
-    pub fn get_setting_rev(&self) -> &SettingRevision {
-        &self.grid_rev.setting
-    }
-
-    /// If layout is None, then the default layout will be the read from GridSettingRevision
-    pub fn get_filters(&self, field_ids: Option<Vec<String>>) -> Option<Vec<Arc<FilterConfigurationRevision>>> {
-        let mut filter_revs = vec![];
-        let field_revs = self.get_field_revs(None).ok()?;
-
-        field_revs.iter().for_each(|field_rev| {
-            let mut is_contain = true;
-            if let Some(field_ids) = &field_ids {
-                is_contain = field_ids.contains(&field_rev.id);
-            }
-
-            if is_contain {
-                // Only return the filters for the current fields' type.
-                let field_id = &field_rev.id;
-                let field_type_rev = &field_rev.field_type_rev;
-                if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(field_id, field_type_rev) {
-                    filter_revs.append(&mut t_filter_revs);
-                }
-            }
-        });
-
-        Some(filter_revs)
-    }
-
-    pub fn update_grid_setting_rev(
-        &mut self,
-        changeset: GridSettingChangesetParams,
-    ) -> CollaborateResult<Option<GridRevisionChangeset>> {
-        self.modify_grid(|grid_rev| {
-            let mut is_changed = None;
-            if let Some(params) = changeset.insert_filter {
-                grid_rev
-                    .setting
-                    .insert_filter(&params.field_id, &params.field_type_rev, make_filter_revision(&params));
-                is_changed = Some(())
-            }
-            if let Some(params) = changeset.delete_filter {
-                if let Some(filters) = grid_rev
-                    .setting
-                    .get_mut_filters(&params.field_id, &params.field_type_rev)
-                {
-                    filters.retain(|filter| filter.id != params.filter_id);
-                }
-            }
-            if let Some(params) = changeset.insert_group {
-                grid_rev
-                    .setting
-                    .insert_group(&params.field_id, &params.field_type_rev, make_group_revision(&params));
-                is_changed = Some(());
-            }
-            if let Some(params) = changeset.delete_group {
-                if let Some(groups) = grid_rev
-                    .setting
-                    .get_mut_groups(&params.field_id, &params.field_type_rev)
-                {
-                    groups.retain(|filter| filter.id != params.group_id);
-                }
-            }
-            if let Some(_sort) = changeset.insert_sort {
-                // let rev = GridSortRevision {
-                //     id: gen_grid_sort_id(),
-                //     field_id: sort.field_id,
-                // };
-                //
-                // grid_rev
-                //     .setting
-                //     .sorts
-                //     .entry(layout_rev.clone())
-                //     .or_insert_with(std::vec::Vec::new)
-                //     .push(rev);
-                is_changed = Some(())
-            }
-
-            if let Some(_delete_sort_id) = changeset.delete_sort {
-                // match grid_rev.setting.sorts.get_mut(&layout_rev) {
-                //     Some(sorts) => sorts.retain(|sort| sort.id != delete_sort_id),
-                //     None => {
-                //         tracing::warn!("Can't find the sort with {:?}", layout_rev);
-                //     }
-                // }
-            }
-            Ok(is_changed)
-        })
-    }
-
     pub fn md5(&self) -> String {
         md5(&self.delta.json_bytes())
     }
@@ -548,21 +456,3 @@ impl std::default::Default for GridRevisionPad {
         }
     }
 }
-
-fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision {
-    FilterConfigurationRevision {
-        id: gen_grid_filter_id(),
-        field_id: params.field_id.clone(),
-        condition: params.condition,
-        content: params.content.clone(),
-    }
-}
-
-fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision {
-    GroupConfigurationRevision {
-        id: gen_grid_group_id(),
-        field_id: params.field_id.clone(),
-        field_type_rev: params.field_type_rev,
-        content: params.content.clone(),
-    }
-}

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

@@ -1,9 +1,11 @@
+use crate::entities::grid::{CreateGridFilterParams, CreateGridGroupParams, GridSettingChangesetParams};
 use crate::entities::revision::{md5, Revision};
 use crate::errors::{internal_error, CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_text_delta_from_revisions};
 use flowy_grid_data_model::revision::{
-    FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision,
-    GroupConfigurationRevision, GroupConfigurationsByFieldId, SortConfigurationsByFieldId,
+    gen_grid_filter_id, gen_grid_group_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision,
+    FilterConfigurationsByFieldId, GridViewRevision, GroupConfigurationRevision, GroupConfigurationsByFieldId,
+    SettingRevision, SortConfigurationsByFieldId,
 };
 use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder};
 use std::sync::Arc;
@@ -48,6 +50,59 @@ impl GridViewRevisionPad {
         Self::from_delta(delta)
     }
 
+    pub fn get_setting_rev(&self) -> &SettingRevision {
+        &self.view.setting
+    }
+
+    pub fn update_setting(
+        &mut self,
+        changeset: GridSettingChangesetParams,
+    ) -> CollaborateResult<Option<GridViewRevisionChangeset>> {
+        self.modify(|view| {
+            let mut is_changed = None;
+            if let Some(params) = changeset.insert_filter {
+                view.setting.filters.insert_object(
+                    &params.field_id,
+                    &params.field_type_rev,
+                    make_filter_revision(&params),
+                );
+                is_changed = Some(())
+            }
+            if let Some(params) = changeset.delete_filter {
+                if let Some(filters) = view
+                    .setting
+                    .filters
+                    .get_mut_objects(&params.field_id, &params.field_type_rev)
+                {
+                    filters.retain(|filter| filter.id != params.filter_id);
+                    is_changed = Some(())
+                }
+            }
+            if let Some(params) = changeset.insert_group {
+                view.setting.groups.remove_all();
+                view.setting.groups.insert_object(
+                    &params.field_id,
+                    &params.field_type_rev,
+                    make_group_revision(&params),
+                );
+
+                is_changed = Some(());
+            }
+            if let Some(params) = changeset.delete_group {
+                if let Some(groups) = view
+                    .setting
+                    .groups
+                    .get_mut_objects(&params.field_id, &params.field_type_rev)
+                {
+                    groups.retain(|group| group.id != params.group_id);
+                    is_changed = Some(());
+                }
+            }
+
+            Ok(is_changed)
+        })
+    }
+
     pub fn get_all_groups(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
         self.setting.groups.get_all_objects(field_revs)
     }
@@ -161,6 +216,24 @@ impl GridViewRevisionPad {
     }
 }
 
+fn make_filter_revision(params: &CreateGridFilterParams) -> FilterConfigurationRevision {
+    FilterConfigurationRevision {
+        id: gen_grid_filter_id(),
+        field_id: params.field_id.clone(),
+        condition: params.condition,
+        content: params.content.clone(),
+    }
+}
+
+fn make_group_revision(params: &CreateGridGroupParams) -> GroupConfigurationRevision {
+    GroupConfigurationRevision {
+        id: gen_grid_group_id(),
+        field_id: params.field_id.clone(),
+        field_type_rev: params.field_type_rev,
+        content: params.content.clone(),
+    }
+}
+
 pub struct GridViewRevisionChangeset {
     pub delta: TextDelta,
     pub md5: String,

+ 4 - 4
shared-lib/lib-infra/src/future.rs

@@ -8,20 +8,20 @@ use std::{
     task::{Context, Poll},
 };
 
-pub fn wrap_future<T, O>(f: T) -> FnFuture<O>
+pub fn wrap_future<T, O>(f: T) -> AFFuture<O>
 where
     T: Future<Output = O> + Send + Sync + 'static,
 {
-    FnFuture { fut: Box::pin(f) }
+    AFFuture { fut: Box::pin(f) }
 }
 
 #[pin_project]
-pub struct FnFuture<T> {
+pub struct AFFuture<T> {
     #[pin]
     pub fut: Pin<Box<dyn Future<Output = T> + Sync + Send>>,
 }
 
-impl<T> Future for FnFuture<T>
+impl<T> Future for AFFuture<T>
 where
     T: Send + Sync,
 {