Browse Source

chore: add group action handler to intercept the create row progress

appflowy 2 years ago
parent
commit
f0914cd6f1

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

@@ -146,9 +146,9 @@ pub struct GridBlockChangesetPB {
     pub hide_rows: Vec<String>,
     pub hide_rows: Vec<String>,
 }
 }
 impl GridBlockChangesetPB {
 impl GridBlockChangesetPB {
-    pub fn insert(block_id: &str, inserted_rows: Vec<InsertedRowPB>) -> Self {
+    pub fn insert(block_id: String, inserted_rows: Vec<InsertedRowPB>) -> Self {
         Self {
         Self {
-            block_id: block_id.to_owned(),
+            block_id,
             inserted_rows,
             inserted_rows,
             ..Default::default()
             ..Default::default()
         }
         }

+ 29 - 0
frontend/rust-lib/flowy-grid/src/entities/group_entities/board_card.rs

@@ -0,0 +1,29 @@
+use flowy_derive::ProtoBuf;
+use flowy_error::ErrorCode;
+use flowy_grid_data_model::parser::NotEmptyStr;
+
+#[derive(ProtoBuf, Debug, Default, Clone)]
+pub struct CreateBoardCardPayloadPB {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub group_id: String,
+}
+pub struct CreateBoardCardParams {
+    pub grid_id: String,
+    pub group_id: String,
+}
+
+impl TryInto<CreateBoardCardParams> for CreateBoardCardPayloadPB {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateBoardCardParams, 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 {
+            grid_id: grid_id.0,
+            group_id: group_id.0,
+        })
+    }
+}

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

@@ -1,5 +1,7 @@
+mod board_card;
 mod configuration;
 mod configuration;
 mod group;
 mod group;
 
 
+pub use board_card::*;
 pub use configuration::*;
 pub use configuration::*;
 pub use group::*;
 pub use group::*;

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

@@ -416,3 +416,14 @@ pub(crate) async fn get_groups_handler(
     let group = editor.load_groups().await?;
     let group = editor.load_groups().await?;
     data_result(group)
     data_result(group)
 }
 }
+
+#[tracing::instrument(level = "debug", skip(data, manager), err)]
+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 editor = manager.get_grid_editor(params.grid_id.as_ref())?;
+    let row = editor.create_board_card().await?;
+    data_result(row)
+}

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

@@ -39,6 +39,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         // Date
         // Date
         .event(GridEvent::UpdateDateCell, update_date_cell_handler)
         .event(GridEvent::UpdateDateCell, update_date_cell_handler)
         // Group
         // Group
+        .event(GridEvent::CreateBoardCard, create_row_handler)
         .event(GridEvent::GetGroup, get_groups_handler);
         .event(GridEvent::GetGroup, get_groups_handler);
 
 
     module
     module
@@ -209,4 +210,7 @@ pub enum GridEvent {
 
 
     #[event(input = "GridIdPB", output = "RepeatedGridGroupPB")]
     #[event(input = "GridIdPB", output = "RepeatedGridGroupPB")]
     GetGroup = 100,
     GetGroup = 100,
+
+    #[event(input = "CreateBoardCardPayloadPB", output = "RowPB")]
+    CreateBoardCard = 110,
 }
 }

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

@@ -62,22 +62,16 @@ impl GridBlockManager {
         Ok(self.get_editor(&block_id).await?)
         Ok(self.get_editor(&block_id).await?)
     }
     }
 
 
-    pub(crate) async fn create_row(
-        &self,
-        block_id: &str,
-        row_rev: RowRevision,
-        start_row_id: Option<String>,
-    ) -> FlowyResult<i32> {
+    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 _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
         let editor = self.get_editor(&row_rev.block_id).await?;
         let editor = self.get_editor(&row_rev.block_id).await?;
 
 
         let mut index_row_order = InsertedRowPB::from(&row_rev);
         let mut index_row_order = InsertedRowPB::from(&row_rev);
         let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
         let (row_count, row_index) = editor.create_row(row_rev, start_row_id).await?;
         index_row_order.index = row_index;
         index_row_order.index = row_index;
-
-        let _ = self
-            .notify_did_update_block(block_id, GridBlockChangesetPB::insert(block_id, vec![index_row_order]))
-            .await?;
+        let changeset = GridBlockChangesetPB::insert(block_id.clone(), vec![index_row_order]);
+        let _ = self.notify_did_update_block(&block_id, changeset).await?;
         Ok(row_count)
         Ok(row_count)
     }
     }
 
 
@@ -98,10 +92,16 @@ impl GridBlockManager {
                 row_order.index = index;
                 row_order.index = index;
                 inserted_row_orders.push(row_order);
                 inserted_row_orders.push(row_order);
             }
             }
-            changesets.push(GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count));
+            changesets.push(GridBlockMetaRevisionChangeset::from_row_count(
+                block_id.clone(),
+                row_count,
+            ));
 
 
             let _ = self
             let _ = self
-                .notify_did_update_block(&block_id, GridBlockChangesetPB::insert(&block_id, inserted_row_orders))
+                .notify_did_update_block(
+                    &block_id,
+                    GridBlockChangesetPB::insert(block_id.clone(), inserted_row_orders),
+                )
                 .await?;
                 .await?;
         }
         }
 
 
@@ -154,7 +154,7 @@ impl GridBlockManager {
                 .map(|row_info| Cow::Owned(row_info.row_id().to_owned()))
                 .map(|row_info| Cow::Owned(row_info.row_id().to_owned()))
                 .collect::<Vec<Cow<String>>>();
                 .collect::<Vec<Cow<String>>>();
             let row_count = editor.delete_rows(row_ids).await?;
             let row_count = editor.delete_rows(row_ids).await?;
-            let changeset = GridBlockMetaRevisionChangeset::from_row_count(&grid_block.id, row_count);
+            let changeset = GridBlockMetaRevisionChangeset::from_row_count(grid_block.id.clone(), row_count);
             changesets.push(changeset);
             changesets.push(changeset);
         }
         }
 
 

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

@@ -37,7 +37,7 @@ pub struct GridRevisionEditor {
     pub(crate) filter_service: Arc<GridFilterService>,
     pub(crate) filter_service: Arc<GridFilterService>,
 
 
     #[allow(dead_code)]
     #[allow(dead_code)]
-    pub(crate) group_service: Arc<GridGroupService>,
+    pub(crate) group_service: Arc<RwLock<GridGroupService>>,
 }
 }
 
 
 impl Drop for GridRevisionEditor {
 impl Drop for GridRevisionEditor {
@@ -62,17 +62,17 @@ impl GridRevisionEditor {
         let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
         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(grid_id, &user, block_meta_revs, persistence).await?);
         let filter_service =
         let filter_service =
-            Arc::new(GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
+            GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
         let group_service =
         let group_service =
-            Arc::new(GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
+            GridGroupService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await;
         let editor = Arc::new(Self {
         let editor = Arc::new(Self {
             grid_id: grid_id.to_owned(),
             grid_id: grid_id.to_owned(),
             user,
             user,
             grid_pad,
             grid_pad,
             rev_manager,
             rev_manager,
             block_manager,
             block_manager,
-            filter_service,
-            group_service,
+            filter_service: Arc::new(filter_service),
+            group_service: Arc::new(RwLock::new(group_service)),
         });
         });
 
 
         Ok(editor)
         Ok(editor)
@@ -275,20 +275,8 @@ impl GridRevisionEditor {
     }
     }
 
 
     pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowPB> {
     pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowPB> {
-        let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
-        let block_id = self.block_id().await?;
-
-        // insert empty row below the row whose id is upper_row_id
-        let row_rev = RowRevisionBuilder::new(&block_id, &field_revs).build();
-        let row_order = RowPB::from(&row_rev);
-
-        // insert the row
-        let row_count = self.block_manager.create_row(&block_id, row_rev, start_row_id).await?;
-
-        // update block row count
-        let changeset = GridBlockMetaRevisionChangeset::from_row_count(&block_id, row_count);
-        let _ = self.update_block(changeset).await?;
-        Ok(row_order)
+        let row_rev = self.create_row_rev().await?;
+        self.create_row_pb(row_rev, start_row_id).await
     }
     }
 
 
     pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
     pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowPB>> {
@@ -564,12 +552,40 @@ impl GridRevisionEditor {
         })
         })
     }
     }
 
 
+    pub async fn create_board_card(&self) -> FlowyResult<RowPB> {
+        let mut row_rev = self.create_row_rev().await?;
+        let _ = self.group_service.write().await.create_board_card(&mut row_rev).await;
+        self.create_row_pb(row_rev, None).await
+    }
+
     #[tracing::instrument(level = "trace", skip_all, err)]
     #[tracing::instrument(level = "trace", skip_all, err)]
     pub async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
     pub async fn load_groups(&self) -> FlowyResult<RepeatedGridGroupPB> {
-        let groups = self.group_service.load_groups().await.unwrap_or_default();
+        let groups = self.group_service.write().await.load_groups().await.unwrap_or_default();
         Ok(RepeatedGridGroupPB { items: groups })
         Ok(RepeatedGridGroupPB { items: groups })
     }
     }
 
 
+    async fn create_row_rev(&self) -> FlowyResult<RowRevision> {
+        let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
+        let block_id = self.block_id().await?;
+
+        // insert empty row below the row whose id is upper_row_id
+        let row_rev = RowRevisionBuilder::new(&block_id, &field_revs).build();
+        Ok(row_rev)
+    }
+
+    async fn create_row_pb(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<RowPB> {
+        let row_pb = RowPB::from(&row_rev);
+        let block_id = row_rev.block_id.clone();
+
+        // insert the row
+        let row_count = self.block_manager.create_row(row_rev, start_row_id).await?;
+
+        // update block row count
+        let changeset = GridBlockMetaRevisionChangeset::from_row_count(block_id, row_count);
+        let _ = self.update_block(changeset).await?;
+        Ok(row_pb)
+    }
+
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
     where
         F: for<'a> FnOnce(&'a mut GridRevisionPad) -> FlowyResult<Option<GridChangeset>>,
         F: for<'a> FnOnce(&'a mut GridRevisionPad) -> FlowyResult<Option<GridChangeset>>,

+ 29 - 11
frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs

@@ -1,17 +1,43 @@
-use crate::entities::CheckboxGroupConfigurationPB;
+use crate::entities::{CheckboxGroupConfigurationPB, RowPB};
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::RowRevision;
 
 
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
-use crate::services::group::{Group, GroupAction, GroupCellContentProvider, GroupController, GroupGenerator};
+use crate::services::group::{
+    Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
+};
 
 
 pub type CheckboxGroupController =
 pub type CheckboxGroupController =
     GroupController<CheckboxGroupConfigurationPB, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser>;
     GroupController<CheckboxGroupConfigurationPB, CheckboxTypeOptionPB, CheckboxGroupGenerator, CheckboxCellDataParser>;
 
 
+impl Groupable for CheckboxGroupController {
+    type CellDataType = CheckboxCellData;
+
+    fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
+        false
+    }
+}
+
+impl GroupActionHandler for CheckboxGroupController {
+    fn get_groups(&self) -> Vec<Group> {
+        self.groups()
+    }
+
+    fn group_row(&mut self, row_rev: &RowRevision) -> FlowyResult<()> {
+        self.handle_row(row_rev)
+    }
+
+    fn create_card(&self, row_rev: &mut RowRevision) {
+        todo!()
+    }
+}
+
 pub struct CheckboxGroupGenerator();
 pub struct CheckboxGroupGenerator();
 impl GroupGenerator for CheckboxGroupGenerator {
 impl GroupGenerator for CheckboxGroupGenerator {
     type ConfigurationType = CheckboxGroupConfigurationPB;
     type ConfigurationType = CheckboxGroupConfigurationPB;
     type TypeOptionType = CheckboxTypeOptionPB;
     type TypeOptionType = CheckboxTypeOptionPB;
 
 
-    fn gen_groups(
+    fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         _configuration: &Option<Self::ConfigurationType>,
         _type_option: &Option<Self::TypeOptionType>,
         _type_option: &Option<Self::TypeOptionType>,
         _cell_content_provider: &dyn GroupCellContentProvider,
         _cell_content_provider: &dyn GroupCellContentProvider,
@@ -33,11 +59,3 @@ impl GroupGenerator for CheckboxGroupGenerator {
         vec![check_group, uncheck_group]
         vec![check_group, uncheck_group]
     }
     }
 }
 }
-
-impl GroupAction for CheckboxGroupController {
-    type CellDataType = CheckboxCellData;
-
-    fn should_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool {
-        false
-    }
-}

+ 62 - 44
frontend/rust-lib/flowy-grid/src/services/group/group_generator/generator.rs

@@ -9,12 +9,6 @@ use indexmap::IndexMap;
 use std::marker::PhantomData;
 use std::marker::PhantomData;
 use std::sync::Arc;
 use std::sync::Arc;
 
 
-pub trait GroupAction {
-    type CellDataType;
-
-    fn should_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
-}
-
 pub trait GroupCellContentProvider {
 pub trait GroupCellContentProvider {
     /// We need to group the rows base on the deduplication cell content when the field type is
     /// We need to group the rows base on the deduplication cell content when the field type is
     /// RichText.
     /// RichText.
@@ -27,25 +21,47 @@ pub trait GroupGenerator {
     type ConfigurationType;
     type ConfigurationType;
     type TypeOptionType;
     type TypeOptionType;
 
 
-    fn gen_groups(
+    fn generate_groups(
         configuration: &Option<Self::ConfigurationType>,
         configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
         type_option: &Option<Self::TypeOptionType>,
         cell_content_provider: &dyn GroupCellContentProvider,
         cell_content_provider: &dyn GroupCellContentProvider,
     ) -> Vec<Group>;
     ) -> Vec<Group>;
 }
 }
 
 
+pub trait Groupable {
+    type CellDataType;
+    fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
+}
+
+pub trait GroupActionHandler: Send + Sync {
+    fn get_groups(&self) -> Vec<Group>;
+    fn group_row(&mut self, row_rev: &RowRevision) -> FlowyResult<()>;
+    fn group_rows(&mut self, row_revs: &[Arc<RowRevision>]) -> FlowyResult<()> {
+        for row_rev in row_revs {
+            let _ = self.group_row(row_rev)?;
+        }
+        Ok(())
+    }
+    fn create_card(&self, row_rev: &mut RowRevision);
+}
+
 const DEFAULT_GROUP_ID: &str = "default_group";
 const DEFAULT_GROUP_ID: &str = "default_group";
 
 
-pub struct GroupController<C, T, G, CP> {
+/// C: represents the group configuration structure
+/// T: the type option data deserializer that impl [TypeOptionDataDeserializer]
+/// G: the group container generator
+/// P: the parser that impl [CellBytesParser] for the CellBytes
+pub struct GroupController<C, T, G, P> {
     pub field_rev: Arc<FieldRevision>,
     pub field_rev: Arc<FieldRevision>,
-    pub groups: IndexMap<String, Group>,
-    pub default_group: Group,
+    groups: IndexMap<String, Group>,
+    default_group: Group,
     pub type_option: Option<T>,
     pub type_option: Option<T>,
     pub configuration: Option<C>,
     pub configuration: Option<C>,
     group_action_phantom: PhantomData<G>,
     group_action_phantom: PhantomData<G>,
-    cell_parser_phantom: PhantomData<CP>,
+    cell_parser_phantom: PhantomData<P>,
 }
 }
 
 
+#[derive(Clone)]
 pub struct Group {
 pub struct Group {
     pub id: String,
     pub id: String,
     pub desc: String,
     pub desc: String,
@@ -63,7 +79,7 @@ impl std::convert::From<Group> for GroupPB {
     }
     }
 }
 }
 
 
-impl<C, T, G, CP> GroupController<C, T, G, CP>
+impl<C, T, G, P> GroupController<C, T, G, P>
 where
 where
     C: TryFrom<Bytes, Error = protobuf::ProtobufError>,
     C: TryFrom<Bytes, Error = protobuf::ProtobufError>,
     T: TypeOptionDataDeserializer,
     T: TypeOptionDataDeserializer,
@@ -80,7 +96,7 @@ where
         };
         };
         let field_type_rev = field_rev.field_type_rev;
         let field_type_rev = field_rev.field_type_rev;
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
         let type_option = field_rev.get_type_option_entry::<T>(field_type_rev);
-        let groups = G::gen_groups(&configuration, &type_option, cell_content_provider);
+        let groups = G::generate_groups(&configuration, &type_option, cell_content_provider);
 
 
         let default_group = Group {
         let default_group = Group {
             id: DEFAULT_GROUP_ID.to_owned(),
             id: DEFAULT_GROUP_ID.to_owned(),
@@ -100,9 +116,9 @@ where
         })
         })
     }
     }
 
 
-    pub fn take_groups(self) -> Vec<Group> {
-        let default_group = self.default_group;
-        let mut groups: Vec<Group> = self.groups.into_values().collect();
+    pub fn groups(&self) -> Vec<Group> {
+        let default_group = self.default_group.clone();
+        let mut groups: Vec<Group> = self.groups.values().cloned().collect();
         if !default_group.rows.is_empty() {
         if !default_group.rows.is_empty() {
             groups.push(default_group);
             groups.push(default_group);
         }
         }
@@ -110,48 +126,50 @@ where
     }
     }
 }
 }
 
 
-impl<C, T, G, CP> GroupController<C, T, G, CP>
+impl<C, T, G, P> GroupController<C, T, G, P>
 where
 where
-    CP: CellBytesParser,
-    Self: GroupAction<CellDataType = CP::Object>,
+    P: CellBytesParser,
+    Self: Groupable<CellDataType = P::Object>,
 {
 {
-    pub fn group_rows(&mut self, rows: &[Arc<RowRevision>]) -> FlowyResult<()> {
+    pub fn handle_row(&mut self, row: &RowRevision) -> FlowyResult<()> {
         if self.configuration.is_none() {
         if self.configuration.is_none() {
             return Ok(());
             return Ok(());
         }
         }
-        tracing::debug!("group {} rows", rows.len());
-
-        for row in rows {
-            if let Some(cell_rev) = row.cells.get(&self.field_rev.id) {
-                let mut records: Vec<GroupRecord> = vec![];
-
-                let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), &self.field_rev);
-                let cell_data = cell_bytes.parser::<CP>()?;
-                for group in self.groups.values() {
-                    if self.should_group(&group.content, &cell_data) {
-                        records.push(GroupRecord {
-                            row: row.into(),
-                            group_id: group.id.clone(),
-                        });
-                    }
+        if let Some(cell_rev) = row.cells.get(&self.field_rev.id) {
+            let mut records: Vec<GroupRecord> = vec![];
+            let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), &self.field_rev);
+            let cell_data = cell_bytes.parser::<P>()?;
+            for group in self.groups.values() {
+                if self.can_group(&group.content, &cell_data) {
+                    records.push(GroupRecord {
+                        row: row.into(),
+                        group_id: group.id.clone(),
+                    });
                 }
                 }
+            }
 
 
-                if records.is_empty() {
-                    self.default_group.rows.push(row.into());
-                } else {
-                    for record in records {
-                        if let Some(group) = self.groups.get_mut(&record.group_id) {
-                            group.rows.push(record.row);
-                        }
+            if records.is_empty() {
+                self.default_group.rows.push(row.into());
+            } else {
+                for record in records {
+                    if let Some(group) = self.groups.get_mut(&record.group_id) {
+                        group.rows.push(record.row);
                     }
                     }
                 }
                 }
-            } else {
-                self.default_group.rows.push(row.into());
             }
             }
+        } else {
+            self.default_group.rows.push(row.into());
         }
         }
 
 
         Ok(())
         Ok(())
     }
     }
+
+    pub fn group_rows(&mut self, rows: &[Arc<RowRevision>]) -> FlowyResult<()> {
+        for row in rows {
+            let _ = self.handle_row(row)?;
+        }
+        Ok(())
+    }
 }
 }
 
 
 struct GroupRecord {
 struct GroupRecord {

+ 50 - 18
frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs

@@ -1,9 +1,13 @@
-use crate::entities::SelectOptionGroupConfigurationPB;
+use crate::entities::{RowPB, SelectOptionGroupConfigurationPB};
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::RowRevision;
 
 
 use crate::services::field::{
 use crate::services::field::{
     MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
     MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB,
 };
 };
-use crate::services::group::{Group, GroupAction, GroupCellContentProvider, GroupController, GroupGenerator};
+use crate::services::group::{
+    Group, GroupActionHandler, GroupCellContentProvider, GroupController, GroupGenerator, Groupable,
+};
 
 
 // SingleSelect
 // SingleSelect
 pub type SingleSelectGroupController = GroupController<
 pub type SingleSelectGroupController = GroupController<
@@ -13,11 +17,32 @@ pub type SingleSelectGroupController = GroupController<
     SelectOptionCellDataParser,
     SelectOptionCellDataParser,
 >;
 >;
 
 
+impl Groupable for SingleSelectGroupController {
+    type CellDataType = SelectOptionCellDataPB;
+    fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
+        cell_data.select_options.iter().any(|option| option.id == content)
+    }
+}
+
+impl GroupActionHandler for SingleSelectGroupController {
+    fn get_groups(&self) -> Vec<Group> {
+        self.groups()
+    }
+
+    fn group_row(&mut self, row_rev: &RowRevision) -> FlowyResult<()> {
+        self.handle_row(row_rev)
+    }
+
+    fn create_card(&self, row_rev: &mut RowRevision) {
+        todo!()
+    }
+}
+
 pub struct SingleSelectGroupGenerator();
 pub struct SingleSelectGroupGenerator();
 impl GroupGenerator for SingleSelectGroupGenerator {
 impl GroupGenerator for SingleSelectGroupGenerator {
     type ConfigurationType = SelectOptionGroupConfigurationPB;
     type ConfigurationType = SelectOptionGroupConfigurationPB;
     type TypeOptionType = SingleSelectTypeOptionPB;
     type TypeOptionType = SingleSelectTypeOptionPB;
-    fn gen_groups(
+    fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         _configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
         type_option: &Option<Self::TypeOptionType>,
         _cell_content_provider: &dyn GroupCellContentProvider,
         _cell_content_provider: &dyn GroupCellContentProvider,
@@ -38,13 +63,6 @@ impl GroupGenerator for SingleSelectGroupGenerator {
     }
     }
 }
 }
 
 
-impl GroupAction for SingleSelectGroupController {
-    type CellDataType = SelectOptionCellDataPB;
-    fn should_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
-        cell_data.select_options.iter().any(|option| option.id == content)
-    }
-}
-
 // MultiSelect
 // MultiSelect
 pub type MultiSelectGroupController = GroupController<
 pub type MultiSelectGroupController = GroupController<
     SelectOptionGroupConfigurationPB,
     SelectOptionGroupConfigurationPB,
@@ -53,12 +71,33 @@ pub type MultiSelectGroupController = GroupController<
     SelectOptionCellDataParser,
     SelectOptionCellDataParser,
 >;
 >;
 
 
+impl Groupable for MultiSelectGroupController {
+    type CellDataType = SelectOptionCellDataPB;
+    fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
+        cell_data.select_options.iter().any(|option| option.id == content)
+    }
+}
+
+impl GroupActionHandler for MultiSelectGroupController {
+    fn get_groups(&self) -> Vec<Group> {
+        self.groups()
+    }
+
+    fn group_row(&mut self, row_rev: &RowRevision) -> FlowyResult<()> {
+        self.handle_row(row_rev)
+    }
+
+    fn create_card(&self, row_rev: &mut RowRevision) {
+        todo!()
+    }
+}
+
 pub struct MultiSelectGroupGenerator();
 pub struct MultiSelectGroupGenerator();
 impl GroupGenerator for MultiSelectGroupGenerator {
 impl GroupGenerator for MultiSelectGroupGenerator {
     type ConfigurationType = SelectOptionGroupConfigurationPB;
     type ConfigurationType = SelectOptionGroupConfigurationPB;
     type TypeOptionType = MultiSelectTypeOptionPB;
     type TypeOptionType = MultiSelectTypeOptionPB;
 
 
-    fn gen_groups(
+    fn generate_groups(
         _configuration: &Option<Self::ConfigurationType>,
         _configuration: &Option<Self::ConfigurationType>,
         type_option: &Option<Self::TypeOptionType>,
         type_option: &Option<Self::TypeOptionType>,
         _cell_content_provider: &dyn GroupCellContentProvider,
         _cell_content_provider: &dyn GroupCellContentProvider,
@@ -78,10 +117,3 @@ impl GroupGenerator for MultiSelectGroupGenerator {
         }
         }
     }
     }
 }
 }
-
-impl GroupAction for MultiSelectGroupController {
-    type CellDataType = SelectOptionCellDataPB;
-    fn should_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
-        cell_data.select_options.iter().any(|option| option.id == content)
-    }
-}

+ 67 - 55
frontend/rust-lib/flowy-grid/src/services/group/group_service.rs

@@ -1,12 +1,14 @@
 use crate::services::block_manager::GridBlockManager;
 use crate::services::block_manager::GridBlockManager;
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::group::{
 use crate::services::group::{
-    CheckboxGroupController, Group, GroupCellContentProvider, MultiSelectGroupController, SingleSelectGroupController,
+    CheckboxGroupController, Group, GroupActionHandler, GroupCellContentProvider, MultiSelectGroupController,
+    SingleSelectGroupController,
 };
 };
 
 
 use crate::entities::{
 use crate::entities::{
-    CheckboxGroupConfigurationPB, DateGroupConfigurationPB, FieldType, GroupPB, NumberGroupConfigurationPB,
-    SelectOptionGroupConfigurationPB, TextGroupConfigurationPB, UrlGroupConfigurationPB,
+    CheckboxGroupConfigurationPB, CreateBoardCardParams, DateGroupConfigurationPB, FieldType, GroupPB,
+    NumberGroupConfigurationPB, RowPB, SelectOptionGroupConfigurationPB, TextGroupConfigurationPB,
+    UrlGroupConfigurationPB,
 };
 };
 use bytes::Bytes;
 use bytes::Bytes;
 use flowy_error::FlowyResult;
 use flowy_error::FlowyResult;
@@ -18,10 +20,9 @@ use tokio::sync::RwLock;
 pub(crate) struct GridGroupService {
 pub(crate) struct GridGroupService {
     #[allow(dead_code)]
     #[allow(dead_code)]
     scheduler: Arc<dyn GridServiceTaskScheduler>,
     scheduler: Arc<dyn GridServiceTaskScheduler>,
-    #[allow(dead_code)]
     grid_pad: Arc<RwLock<GridRevisionPad>>,
     grid_pad: Arc<RwLock<GridRevisionPad>>,
-    #[allow(dead_code)]
     block_manager: Arc<GridBlockManager>,
     block_manager: Arc<GridBlockManager>,
+    group_action_handler: Option<Arc<RwLock<dyn GroupActionHandler>>>,
 }
 }
 
 
 impl GridGroupService {
 impl GridGroupService {
@@ -35,14 +36,14 @@ impl GridGroupService {
             scheduler,
             scheduler,
             grid_pad,
             grid_pad,
             block_manager,
             block_manager,
+            group_action_handler: None,
         }
         }
     }
     }
 
 
-    pub(crate) async fn load_groups(&self) -> Option<Vec<GroupPB>> {
-        let grid_pad = self.grid_pad.read().await;
-        let field_rev = find_group_field(grid_pad.fields()).unwrap();
+    pub(crate) async fn load_groups(&mut self) -> Option<Vec<GroupPB>> {
+        let field_rev = find_group_field(self.grid_pad.read().await.fields()).unwrap();
         let field_type: FieldType = field_rev.field_type_rev.into();
         let field_type: FieldType = field_rev.field_type_rev.into();
-        let configuration = self.get_group_configuration(field_rev).await;
+        let configuration = self.get_group_configuration(&field_rev).await;
 
 
         let blocks = self.block_manager.get_block_snapshots(None).await.unwrap();
         let blocks = self.block_manager.get_block_snapshots(None).await.unwrap();
         let row_revs = blocks
         let row_revs = blocks
@@ -51,19 +52,28 @@ impl GridGroupService {
             .flatten()
             .flatten()
             .collect::<Vec<Arc<RowRevision>>>();
             .collect::<Vec<Arc<RowRevision>>>();
 
 
-        match self.build_groups(&field_type, field_rev, row_revs, configuration) {
+        match self
+            .build_groups(&field_type, &field_rev, row_revs, configuration)
+            .await
+        {
             Ok(groups) => Some(groups),
             Ok(groups) => Some(groups),
             Err(_) => None,
             Err(_) => None,
         }
         }
     }
     }
 
 
-    async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
+    pub(crate) async fn create_board_card(&self, row_rev: &mut RowRevision) {
+        if let Some(group_action_handler) = self.group_action_handler.as_ref() {
+            group_action_handler.write().await.create_card(row_rev);
+        }
+    }
+
+    pub(crate) async fn get_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
         let grid_pad = self.grid_pad.read().await;
         let grid_pad = self.grid_pad.read().await;
         let setting = grid_pad.get_setting_rev();
         let setting = grid_pad.get_setting_rev();
         let layout = &setting.layout;
         let layout = &setting.layout;
         let configurations = setting.get_groups(layout, &field_rev.id, &field_rev.field_type_rev);
         let configurations = setting.get_groups(layout, &field_rev.id, &field_rev.field_type_rev);
         match configurations {
         match configurations {
-            None => self.default_group_configuration(field_rev),
+            None => default_group_configuration(field_rev),
             Some(mut configurations) => {
             Some(mut configurations) => {
                 assert_eq!(configurations.len(), 1);
                 assert_eq!(configurations.len(), 1);
                 (&*configurations.pop().unwrap()).clone()
                 (&*configurations.pop().unwrap()).clone()
@@ -71,79 +81,81 @@ impl GridGroupService {
         }
         }
     }
     }
 
 
-    fn default_group_configuration(&self, field_rev: &FieldRevision) -> GroupConfigurationRevision {
-        let field_type: FieldType = field_rev.field_type_rev.clone().into();
-        let bytes: Bytes = match field_type {
-            FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::Number => NumberGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::DateTime => DateGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::SingleSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::MultiSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::Checkbox => CheckboxGroupConfigurationPB::default().try_into().unwrap(),
-            FieldType::URL => UrlGroupConfigurationPB::default().try_into().unwrap(),
-        };
-        GroupConfigurationRevision {
-            id: gen_grid_group_id(),
-            field_id: field_rev.id.clone(),
-            field_type_rev: field_rev.field_type_rev.clone(),
-            content: Some(bytes.to_vec()),
-        }
-    }
-
     #[tracing::instrument(level = "trace", skip_all, err)]
     #[tracing::instrument(level = "trace", skip_all, err)]
-    fn build_groups(
-        &self,
+    async fn build_groups(
+        &mut self,
         field_type: &FieldType,
         field_type: &FieldType,
         field_rev: &Arc<FieldRevision>,
         field_rev: &Arc<FieldRevision>,
         row_revs: Vec<Arc<RowRevision>>,
         row_revs: Vec<Arc<RowRevision>>,
         configuration: GroupConfigurationRevision,
         configuration: GroupConfigurationRevision,
     ) -> FlowyResult<Vec<GroupPB>> {
     ) -> FlowyResult<Vec<GroupPB>> {
-        let groups: Vec<Group> = match field_type {
+        match field_type {
             FieldType::RichText => {
             FieldType::RichText => {
                 // let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
                 // let generator = GroupGenerator::<TextGroupConfigurationPB>::from_configuration(configuration);
-                vec![]
             }
             }
             FieldType::Number => {
             FieldType::Number => {
                 // let generator = GroupGenerator::<NumberGroupConfigurationPB>::from_configuration(configuration);
                 // let generator = GroupGenerator::<NumberGroupConfigurationPB>::from_configuration(configuration);
-                vec![]
             }
             }
             FieldType::DateTime => {
             FieldType::DateTime => {
                 // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
                 // let generator = GroupGenerator::<DateGroupConfigurationPB>::from_configuration(configuration);
-                vec![]
             }
             }
             FieldType::SingleSelect => {
             FieldType::SingleSelect => {
-                let mut group_controller =
-                    SingleSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
-                let _ = group_controller.group_rows(&row_revs)?;
-                group_controller.take_groups()
+                let controller = SingleSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
+                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             }
             FieldType::MultiSelect => {
             FieldType::MultiSelect => {
-                let mut group_controller =
-                    MultiSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
-                let _ = group_controller.group_rows(&row_revs)?;
-                group_controller.take_groups()
+                let controller = MultiSelectGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
+                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             }
             FieldType::Checkbox => {
             FieldType::Checkbox => {
-                let mut group_controller =
-                    CheckboxGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
-                let _ = group_controller.group_rows(&row_revs)?;
-                group_controller.take_groups()
+                let controller = CheckboxGroupController::new(field_rev.clone(), configuration, &self.grid_pad)?;
+                self.group_action_handler = Some(Arc::new(RwLock::new(controller)));
             }
             }
             FieldType::URL => {
             FieldType::URL => {
                 // let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
                 // let generator = GroupGenerator::<UrlGroupConfigurationPB>::from_configuration(configuration);
-                vec![]
             }
             }
         };
         };
 
 
+        let mut groups = vec![];
+        if let Some(group_action_handler) = self.group_action_handler.as_ref() {
+            let mut write_guard = group_action_handler.write().await;
+            let _ = write_guard.group_rows(&row_revs)?;
+            groups = write_guard.get_groups();
+            drop(write_guard);
+        }
+
         Ok(groups.into_iter().map(GroupPB::from).collect())
         Ok(groups.into_iter().map(GroupPB::from).collect())
     }
     }
 }
 }
 
 
-fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<&Arc<FieldRevision>> {
-    field_revs.iter().find(|field_rev| {
-        let field_type: FieldType = field_rev.field_type_rev.into();
-        field_type.can_be_group()
-    })
+fn find_group_field(field_revs: &[Arc<FieldRevision>]) -> Option<Arc<FieldRevision>> {
+    let field_rev = field_revs
+        .iter()
+        .find(|field_rev| {
+            let field_type: FieldType = field_rev.field_type_rev.into();
+            field_type.can_be_group()
+        })
+        .cloned();
+    field_rev
 }
 }
 
 
 impl GroupCellContentProvider for Arc<RwLock<GridRevisionPad>> {}
 impl GroupCellContentProvider for Arc<RwLock<GridRevisionPad>> {}
+
+fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurationRevision {
+    let field_type: FieldType = field_rev.field_type_rev.clone().into();
+    let bytes: Bytes = match field_type {
+        FieldType::RichText => TextGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::Number => NumberGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::DateTime => DateGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::SingleSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::MultiSelect => SelectOptionGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::Checkbox => CheckboxGroupConfigurationPB::default().try_into().unwrap(),
+        FieldType::URL => UrlGroupConfigurationPB::default().try_into().unwrap(),
+    };
+    GroupConfigurationRevision {
+        id: gen_grid_group_id(),
+        field_id: field_rev.id.clone(),
+        field_type_rev: field_rev.field_type_rev.clone(),
+        content: Some(bytes.to_vec()),
+    }
+}

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

@@ -111,6 +111,9 @@ pub enum ErrorCode {
     #[display(fmt = "Field's type option data should not be empty")]
     #[display(fmt = "Field's type option data should not be empty")]
     TypeOptionDataIsEmpty = 450,
     TypeOptionDataIsEmpty = 450,
 
 
+    #[display(fmt = "Group id is empty")]
+    GroupIdIsEmpty = 460,
+
     #[display(fmt = "Invalid date time format")]
     #[display(fmt = "Invalid date time format")]
     InvalidDateTimeFormat = 500,
     InvalidDateTimeFormat = 500,
 
 

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

@@ -88,9 +88,9 @@ pub struct GridBlockMetaRevisionChangeset {
 }
 }
 
 
 impl GridBlockMetaRevisionChangeset {
 impl GridBlockMetaRevisionChangeset {
-    pub fn from_row_count(block_id: &str, row_count: i32) -> Self {
+    pub fn from_row_count(block_id: String, row_count: i32) -> Self {
         Self {
         Self {
-            block_id: block_id.to_string(),
+            block_id,
             start_row_index: None,
             start_row_index: None,
             row_count: Some(row_count),
             row_count: Some(row_count),
         }
         }