Kaynağa Gözat

Merge pull request #1268 from AppFlowy-IO/fix/edit_no_status_card

Fix/edit no status card
Nathan.fooo 2 yıl önce
ebeveyn
işleme
c170b133e6
29 değiştirilmiş dosya ile 296 ekleme ve 210 silme
  1. 1 1
      frontend/rust-lib/flowy-grid/src/services/block_manager.rs
  2. 1 2
      frontend/rust-lib/flowy-grid/src/services/block_manager_trait_impl.rs
  3. 6 2
      frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs
  4. 8 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs
  5. 7 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  6. 7 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs
  7. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs
  8. 17 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs
  9. 4 7
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs
  10. 8 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs
  11. 7 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs
  12. 2 2
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  13. 4 6
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  14. 3 7
      frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs
  15. 71 11
      frontend/rust-lib/flowy-grid/src/services/group/action.rs
  16. 23 13
      frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
  17. 41 75
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  18. 19 15
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
  19. 3 2
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs
  20. 23 13
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
  21. 22 13
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
  22. 1 1
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
  23. 2 6
      frontend/rust-lib/flowy-grid/src/services/group/group_util.rs
  24. 3 3
      frontend/rust-lib/flowy-net/src/http_server/document.rs
  25. 5 17
      frontend/rust-lib/flowy-net/src/http_server/user.rs
  26. 1 1
      frontend/rust-lib/flowy-revision/src/rev_manager.rs
  27. 0 1
      frontend/rust-lib/flowy-revision/src/rev_persistence.rs
  28. 1 2
      frontend/rust-lib/lib-log/src/layer.rs
  29. 4 0
      shared-lib/flowy-grid-data-model/src/revision/grid_block.rs

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

@@ -54,7 +54,7 @@ impl GridBlockManager {
 
     pub(crate) 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_block_editor(&block_id).await?)
+        self.get_block_editor(&block_id).await
     }
 
     #[tracing::instrument(level = "trace", skip(self, start_row_id), err)]

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

@@ -30,8 +30,7 @@ impl GridViewRowDelegate for Arc<GridBlockManager> {
             let blocks = block_manager.get_block_snapshots(None).await.unwrap();
             blocks
                 .into_iter()
-                .map(|block| block.row_revs)
-                .flatten()
+                .flat_map(|block| block.row_revs)
                 .collect::<Vec<Arc<RowRevision>>>()
         })
     }

+ 6 - 2
frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs

@@ -113,11 +113,15 @@ impl AnyCellData {
 /// * Use URLCellData to parse the data when the FieldType is URL.
 /// * Use String to parse the data when the FieldType is RichText, Number, or Checkbox.
 /// * Check out the implementation of CellDataOperation trait for more information.
-#[derive(Default)]
+#[derive(Default, Debug)]
 pub struct CellBytes(pub Bytes);
 
+pub trait CellDataIsEmpty {
+    fn is_empty(&self) -> bool;
+}
+
 pub trait CellBytesParser {
-    type Object;
+    type Object: CellDataIsEmpty;
     fn parser(bytes: &Bytes) -> FlowyResult<Self::Object>;
 }
 

+ 8 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs

@@ -1,4 +1,4 @@
-use crate::services::cell::{CellBytesParser, FromCellString};
+use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellString};
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
 use std::str::FromStr;
@@ -61,6 +61,13 @@ impl ToString for CheckboxCellData {
         self.0.clone()
     }
 }
+
+impl CellDataIsEmpty for CheckboxCellData {
+    fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+}
+
 pub struct CheckboxCellDataParser();
 impl CellBytesParser for CheckboxCellDataParser {
     type Object = CheckboxCellData;

+ 7 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs

@@ -1,6 +1,6 @@
 use crate::entities::CellChangesetPB;
 use crate::entities::{GridCellIdPB, GridCellIdParams};
-use crate::services::cell::{CellBytesParser, FromCellChangeset, FromCellString};
+use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellChangeset, FromCellString};
 use bytes::Bytes;
 
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
@@ -200,6 +200,12 @@ impl std::default::Default for TimeFormat {
     }
 }
 
+impl CellDataIsEmpty for DateCellDataPB {
+    fn is_empty(&self) -> bool {
+        self.date.is_empty()
+    }
+}
+
 pub struct DateCellDataParser();
 impl CellBytesParser for DateCellDataParser {
     type Object = DateCellDataPB;

+ 7 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs

@@ -1,4 +1,4 @@
-use crate::services::cell::{CellBytesCustomParser, CellBytesParser};
+use crate::services::cell::{CellBytesCustomParser, CellBytesParser, CellDataIsEmpty};
 use crate::services::field::number_currency::Currency;
 use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL};
 use bytes::Bytes;
@@ -93,6 +93,12 @@ impl ToString for NumberCellData {
         }
     }
 }
+
+impl CellDataIsEmpty for NumberCellData {
+    fn is_empty(&self) -> bool {
+        self.decimal.is_none()
+    }
+}
 pub struct NumberCellDataParser();
 impl CellBytesParser for NumberCellDataParser {
     type Object = NumberCellData;

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs

@@ -117,12 +117,12 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
         match field_type {
             FieldType::Checkbox => {
                 //Add Yes and No options if it's not exist.
-                if self.0.options.iter().find(|option| option.name == CHECK).is_none() {
+                if !self.0.options.iter().any(|option| option.name == CHECK) {
                     let check_option = SelectOptionPB::with_color(CHECK, SelectOptionColorPB::Green);
                     self.0.options.push(check_option);
                 }
 
-                if self.0.options.iter().find(|option| option.name == UNCHECK).is_none() {
+                if !self.0.options.iter().any(|option| option.name == UNCHECK) {
                     let uncheck_option = SelectOptionPB::with_color(UNCHECK, SelectOptionColorPB::Yellow);
                     self.0.options.push(uncheck_option);
                 }

+ 17 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs

@@ -1,5 +1,7 @@
 use crate::entities::{CellChangesetPB, FieldType, GridCellIdPB, GridCellIdParams};
-use crate::services::cell::{CellBytes, CellBytesParser, CellData, CellDisplayable, FromCellChangeset, FromCellString};
+use crate::services::cell::{
+    CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString,
+};
 use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
@@ -233,7 +235,7 @@ impl ToString for SelectOptionIds {
 impl std::convert::From<Option<String>> for SelectOptionIds {
     fn from(s: Option<String>) -> Self {
         match s {
-            None => Self { 0: vec![] },
+            None => Self(vec![]),
             Some(s) => Self::from(s),
         }
     }
@@ -252,6 +254,13 @@ impl std::ops::DerefMut for SelectOptionIds {
         &mut self.0
     }
 }
+
+impl CellDataIsEmpty for SelectOptionIds {
+    fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+}
+
 pub struct SelectOptionIdsParser();
 impl CellBytesParser for SelectOptionIdsParser {
     type Object = SelectOptionIds;
@@ -263,6 +272,12 @@ impl CellBytesParser for SelectOptionIdsParser {
     }
 }
 
+impl CellDataIsEmpty for SelectOptionCellDataPB {
+    fn is_empty(&self) -> bool {
+        self.select_options.is_empty()
+    }
+}
+
 pub struct SelectOptionCellDataParser();
 impl CellBytesParser for SelectOptionCellDataParser {
     type Object = SelectOptionCellDataPB;

+ 4 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs

@@ -61,7 +61,6 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSel
         _cell_rev: Option<CellRevision>,
     ) -> Result<String, FlowyError> {
         let content_changeset = changeset.try_into_inner()?;
-        let new_cell_data: String;
 
         let mut insert_option_ids = content_changeset
             .insert_option_ids
@@ -73,14 +72,12 @@ impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSel
         // Sometimes, the insert_option_ids may contain list of option ids. For example,
         // copy/paste a ids string.
         if insert_option_ids.is_empty() {
-            new_cell_data = "".to_string()
+            Ok("".to_string())
         } else {
             // Just take the first select option
             let _ = insert_option_ids.drain(1..);
-            new_cell_data = insert_option_ids.pop().unwrap();
+            Ok(insert_option_ids.pop().unwrap())
         }
-
-        Ok(new_cell_data)
     }
 }
 
@@ -109,12 +106,12 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder {
         match field_type {
             FieldType::Checkbox => {
                 //add Yes and No options if it's not exist.
-                if self.0.options.iter().find(|option| option.name == CHECK).is_none() {
+                if !self.0.options.iter().any(|option| option.name == CHECK) {
                     let check_option = SelectOptionPB::with_color(CHECK, SelectOptionColorPB::Green);
                     self.0.options.push(check_option);
                 }
 
-                if self.0.options.iter().find(|option| option.name == UNCHECK).is_none() {
+                if !self.0.options.iter().any(|option| option.name == UNCHECK) {
                     let uncheck_option = SelectOptionPB::with_color(UNCHECK, SelectOptionColorPB::Yellow);
                     self.0.options.push(uncheck_option);
                 }

+ 8 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs

@@ -1,8 +1,8 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{
-    decode_cell_data_to_string, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation,
-    CellDisplayable, FromCellString,
+    decode_cell_data_to_string, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataIsEmpty,
+    CellDataOperation, CellDisplayable, FromCellString,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
@@ -125,6 +125,12 @@ impl ToString for TextCellData {
     }
 }
 
+impl CellDataIsEmpty for TextCellData {
+    fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+}
+
 pub struct TextCellDataParser();
 impl CellBytesParser for TextCellDataParser {
     type Object = TextCellData;

+ 7 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs

@@ -1,4 +1,4 @@
-use crate::services::cell::{CellBytesParser, FromCellString};
+use crate::services::cell::{CellBytesParser, CellDataIsEmpty, FromCellString};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{internal_error, FlowyResult};
@@ -26,6 +26,12 @@ impl URLCellDataPB {
     }
 }
 
+impl CellDataIsEmpty for URLCellDataPB {
+    fn is_empty(&self) -> bool {
+        self.content.is_empty()
+    }
+}
+
 pub struct URLCellDataParser();
 impl CellBytesParser for URLCellDataParser {
     type Object = URLCellDataPB;

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

@@ -410,7 +410,7 @@ impl GridRevisionEditor {
     pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
         let row_id = changeset.row_id.clone();
         let _ = self.block_manager.update_row(changeset).await?;
-        self.view_manager.did_update_row(&row_id).await;
+        self.view_manager.did_update_cell(&row_id).await;
         Ok(())
     }
 
@@ -504,7 +504,7 @@ impl GridRevisionEditor {
                     content,
                 };
                 let _ = self.block_manager.update_cell(cell_changeset).await?;
-                self.view_manager.did_update_cell(&row_id, &field_id).await;
+                self.view_manager.did_update_cell(&row_id).await;
                 Ok(())
             }
         }

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

@@ -123,15 +123,15 @@ impl GridViewRevisionEditor {
             })
             .await;
 
+        tracing::trace!("Delete row in view changeset: {:?}", changesets);
         if let Some(changesets) = changesets {
-            tracing::trace!("{:?}", changesets);
             for changeset in changesets {
                 self.notify_did_update_group(changeset).await;
             }
         }
     }
 
-    pub(crate) async fn did_update_view_row(&self, row_rev: &RowRevision) {
+    pub(crate) async fn did_update_view_cell(&self, row_rev: &RowRevision) {
         let changesets = self
             .mut_group_controller(|group_controller, field_rev| {
                 group_controller.did_update_group_row(row_rev, &field_rev)
@@ -539,11 +539,10 @@ pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<Field
         .map(|filters_by_field_id| {
             filters_by_field_id
                 .into_iter()
-                .map(|(_, v)| {
+                .flat_map(|(_, v)| {
                     let repeated_filter: RepeatedGridFilterConfigurationPB = v.into();
                     repeated_filter.items
                 })
-                .flatten()
                 .collect::<Vec<GridFilterConfigurationPB>>()
         })
         .unwrap_or_default();
@@ -553,11 +552,10 @@ pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<Field
         .map(|groups_by_field_id| {
             groups_by_field_id
                 .into_iter()
-                .map(|(_, v)| {
+                .flat_map(|(_, v)| {
                     let repeated_group: RepeatedGridGroupConfigurationPB = v.into();
                     repeated_group.items
                 })
-                .flatten()
                 .collect::<Vec<GridGroupConfigurationPB>>()
         })
         .unwrap_or_default();

+ 3 - 7
frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs

@@ -77,15 +77,15 @@ impl GridViewManager {
         }
     }
 
-    /// Insert/Delete the group's row if the corresponding data was changed.  
-    pub(crate) async fn did_update_row(&self, row_id: &str) {
+    /// Insert/Delete the group's row if the corresponding cell data was changed.  
+    pub(crate) async fn did_update_cell(&self, row_id: &str) {
         match self.row_delegate.gv_get_row_rev(row_id).await {
             None => {
                 tracing::warn!("Can not find the row in grid view");
             }
             Some(row_rev) => {
                 for view_editor in self.view_editors.iter() {
-                    view_editor.did_update_view_row(&row_rev).await;
+                    view_editor.did_update_view_cell(&row_rev).await;
                 }
             }
         }
@@ -97,10 +97,6 @@ impl GridViewManager {
         Ok(())
     }
 
-    pub(crate) async fn did_update_cell(&self, row_id: &str, _field_id: &str) {
-        self.did_update_row(row_id).await
-    }
-
     pub(crate) async fn did_delete_row(&self, row_rev: Arc<RowRevision>) {
         for view_editor in self.view_editors.iter() {
             view_editor.did_delete_view_row(&row_rev).await;

+ 71 - 11
frontend/rust-lib/flowy-grid/src/services/group/action.rs

@@ -1,19 +1,79 @@
-use crate::entities::GroupChangesetPB;
-
+use crate::entities::{GroupChangesetPB, GroupViewChangesetPB};
+use crate::services::cell::CellDataIsEmpty;
 use crate::services::group::controller::MoveGroupRowContext;
-use flowy_grid_data_model::revision::{CellRevision, RowRevision};
+use crate::services::group::Group;
+use flowy_error::FlowyResult;
+use flowy_grid_data_model::revision::{CellRevision, FieldRevision, RowRevision};
+use std::sync::Arc;
 
-pub trait GroupAction: Send + Sync {
-    type CellDataType;
+/// Using polymorphism to provides the customs action for different group controller.
+///
+/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
+///
+pub trait GroupControllerCustomActions: Send + Sync {
+    type CellDataType: CellDataIsEmpty;
+    /// Returns the a value of the cell, default value is None
+    ///
+    /// Determine which group the row is placed in based on the data of the cell. If the cell data
+    /// is None. The row will be put in to the `No status` group  
+    ///
     fn default_cell_rev(&self) -> Option<CellRevision> {
         None
     }
-    fn use_default_group(&self) -> bool {
-        true
-    }
+
+    /// Returns a bool value to determine whether the group should contain this cell or not.
     fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool;
-    fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>;
-    fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>;
-    // Move row from one group to another
+
+    /// Adds or removes a row if the cell data match the group filter.
+    /// It gets called after editing the cell or row
+    ///
+    fn add_or_remove_row_in_groups_if_match(
+        &mut self,
+        row_rev: &RowRevision,
+        cell_data: &Self::CellDataType,
+    ) -> Vec<GroupChangesetPB>;
+
+    /// Deletes the row from the group
+    fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>;
+
+    /// Move row from one group to another
     fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec<GroupChangesetPB>;
 }
+
+/// Defines the shared actions any group controller can perform.
+pub trait GroupControllerSharedActions: Send + Sync {
+    /// The field that is used for grouping the rows
+    fn field_id(&self) -> &str;
+
+    /// Returns number of groups the current field has
+    fn groups(&self) -> Vec<Group>;
+
+    /// Returns the index and the group data with group_id
+    fn get_group(&self, group_id: &str) -> Option<(usize, Group)>;
+
+    /// Separates the rows into different groups
+    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
+
+    /// Remove the group with from_group_id and insert it to the index with to_group_id
+    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
+
+    /// Insert/Remove the row to the group if the corresponding cell data is changed
+    fn did_update_group_row(
+        &mut self,
+        row_rev: &RowRevision,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<Vec<GroupChangesetPB>>;
+
+    /// Remove the row from the group if the row gets deleted
+    fn did_delete_delete_row(
+        &mut self,
+        row_rev: &RowRevision,
+        field_rev: &FieldRevision,
+    ) -> FlowyResult<Vec<GroupChangesetPB>>;
+
+    /// Move the row from one group to another group
+    fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupChangesetPB>>;
+
+    /// Update the group if the corresponding field is changed
+    fn did_update_group_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>>;
+}

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

@@ -1,5 +1,5 @@
 use crate::entities::{GroupPB, GroupViewChangesetPB};
-use crate::services::group::{default_group_configuration, make_no_status_group, GeneratedGroupConfig, Group};
+use crate::services::group::{default_group_configuration, GeneratedGroupContext, Group};
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::{
     FieldRevision, FieldTypeRevision, GroupConfigurationContentSerde, GroupConfigurationRevision, GroupRevision,
@@ -118,8 +118,16 @@ where
         }
     }
 
-    /// Iterate mut the groups. The default group will be the last one that get mutated.
-    pub(crate) fn iter_mut_all_groups(&mut self, mut each: impl FnMut(&mut Group)) {
+    /// Iterate mut the groups without `No status` group
+    pub(crate) fn iter_mut_status_groups(&mut self, mut each: impl FnMut(&mut Group)) {
+        self.groups_map.iter_mut().for_each(|(_, group)| {
+            if group.id != self.field_rev.id {
+                each(group);
+            }
+        });
+    }
+
+    pub(crate) fn iter_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) {
         self.groups_map.iter_mut().for_each(|(_, group)| {
             each(group);
         });
@@ -172,14 +180,19 @@ where
     /// [GroupConfigurationRevision] as old groups. The old groups and the new groups will be merged
     /// while keeping the order of the old groups.
     ///
-    #[tracing::instrument(level = "trace", skip(self, generated_group_configs), err)]
+    #[tracing::instrument(level = "trace", skip(self, generated_group_context), err)]
     pub(crate) fn init_groups(
         &mut self,
-        generated_group_configs: Vec<GeneratedGroupConfig>,
+        generated_group_context: GeneratedGroupContext,
     ) -> FlowyResult<Option<GroupViewChangesetPB>> {
+        let GeneratedGroupContext {
+            no_status_group,
+            group_configs,
+        } = generated_group_context;
+
         let mut new_groups = vec![];
         let mut filter_content_map = HashMap::new();
-        generated_group_configs.into_iter().for_each(|generate_group| {
+        group_configs.into_iter().for_each(|generate_group| {
             filter_content_map.insert(generate_group.group_rev.id.clone(), generate_group.filter_content);
             new_groups.push(generate_group.group_rev);
         });
@@ -190,12 +203,9 @@ where
             old_groups.clear();
         }
 
-        // When grouping by a new field, there is no `No status` group. So it needs
-        // to insert a `No status` group at index 0
         // The `No status` group index is initialized to 0
-        //
-        if !old_groups.iter().any(|group| group.id == self.field_rev.id) {
-            old_groups.insert(0, make_no_status_group(&self.field_rev));
+        if let Some(no_status_group) = no_status_group {
+            old_groups.insert(0, no_status_group);
         }
 
         // The `all_group_revs` is the combination of the new groups and old groups
@@ -236,9 +246,9 @@ where
                     Some(pos) => {
                         let mut old_group = configuration.groups.get_mut(pos).unwrap();
                         // Take the old group setting
-                        group_rev.update_with_other(&old_group);
+                        group_rev.update_with_other(old_group);
                         if !is_changed {
-                            is_changed = is_group_changed(group_rev, &old_group);
+                            is_changed = is_group_changed(group_rev, old_group);
                         }
                         // Consider the the name of the `group_rev` as the newest.
                         old_group.name = group_rev.name.clone();

+ 41 - 75
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -1,6 +1,6 @@
 use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
-use crate::services::cell::{decode_any_cell_data, CellBytesParser};
-use crate::services::group::action::GroupAction;
+use crate::services::cell::{decode_any_cell_data, CellBytesParser, CellDataIsEmpty};
+use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions};
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::entities::Group;
 use flowy_error::FlowyResult;
@@ -18,7 +18,7 @@ use std::sync::Arc;
 /// If the [FieldType] doesn't implement its group controller, then the [DefaultGroupController] will
 /// be used.
 ///
-pub trait GroupController: GroupControllerActions + Send + Sync {
+pub trait GroupController: GroupControllerSharedActions + Send + Sync {
     fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str);
     fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str);
 }
@@ -29,10 +29,15 @@ pub trait GroupGenerator {
     type TypeOptionType;
 
     fn generate_groups(
-        field_id: &str,
+        field_rev: &FieldRevision,
         group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<GeneratedGroupConfig>;
+    ) -> GeneratedGroupContext;
+}
+
+pub struct GeneratedGroupContext {
+    pub no_status_group: Option<GroupRevision>,
+    pub group_configs: Vec<GeneratedGroupConfig>,
 }
 
 pub struct GeneratedGroupConfig {
@@ -47,45 +52,6 @@ pub struct MoveGroupRowContext<'a> {
     pub to_group_id: &'a str,
     pub to_row_id: Option<String>,
 }
-
-/// Defines the shared actions each group controller can perform.
-pub trait GroupControllerActions: Send + Sync {
-    /// The field that is used for grouping the rows
-    fn field_id(&self) -> &str;
-
-    /// Returns number of groups the current field has
-    fn groups(&self) -> Vec<Group>;
-
-    /// Returns the index and the group data with group_id
-    fn get_group(&self, group_id: &str) -> Option<(usize, Group)>;
-
-    /// Separates the rows into different groups
-    fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()>;
-
-    /// Remove the group with from_group_id and insert it to the index with to_group_id
-    fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
-
-    /// Insert the row to the group if the corresponding cell data is changed
-    fn did_update_group_row(
-        &mut self,
-        row_rev: &RowRevision,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<Vec<GroupChangesetPB>>;
-
-    /// Remove the row from the group if the corresponding cell data is changed
-    fn did_delete_delete_row(
-        &mut self,
-        row_rev: &RowRevision,
-        field_rev: &FieldRevision,
-    ) -> FlowyResult<Vec<GroupChangesetPB>>;
-
-    /// Move the row from one group to another group
-    fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupChangesetPB>>;
-
-    /// Update the group if the corresponding field is changed
-    fn did_update_group_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>>;
-}
-
 /// C: represents the group configuration that impl [GroupConfigurationSerde]
 /// T: the type-option data deserializer that impl [TypeOptionDataDeserializer]
 /// G: the group generator, [GroupGenerator]
@@ -106,8 +72,8 @@ where
 {
     pub async fn new(field_rev: &Arc<FieldRevision>, mut configuration: GroupContext<C>) -> FlowyResult<Self> {
         let type_option = field_rev.get_type_option::<T>(field_rev.ty);
-        let groups = G::generate_groups(&field_rev.id, &configuration, &type_option);
-        let _ = configuration.init_groups(groups)?;
+        let generated_group_context = G::generate_groups(field_rev, &configuration, &type_option);
+        let _ = configuration.init_groups(generated_group_context)?;
 
         Ok(Self {
             field_id: field_rev.id.clone(),
@@ -186,30 +152,21 @@ where
     }
 }
 
-impl<C, T, G, P> GroupControllerActions for GenericGroupController<C, T, G, P>
+impl<C, T, G, P> GroupControllerSharedActions for GenericGroupController<C, T, G, P>
 where
     P: CellBytesParser,
     C: GroupConfigurationContentSerde,
     T: TypeOptionDataDeserializer,
     G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
 
-    Self: GroupAction<CellDataType = P::Object>,
+    Self: GroupControllerCustomActions<CellDataType = P::Object>,
 {
     fn field_id(&self) -> &str {
         &self.field_id
     }
 
     fn groups(&self) -> Vec<Group> {
-        if self.use_default_group() {
-            self.group_ctx.groups().into_iter().cloned().collect()
-        } else {
-            self.group_ctx
-                .groups()
-                .into_iter()
-                .filter(|group| group.id != self.field_id)
-                .cloned()
-                .collect::<Vec<_>>()
-        }
+        self.group_ctx.groups().into_iter().cloned().collect()
     }
 
     fn get_group(&self, group_id: &str) -> Option<(usize, Group)> {
@@ -269,17 +226,14 @@ where
         if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
             let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
             let cell_data = cell_bytes.parser::<P>()?;
-            let mut changesets = self.add_row_if_match(row_rev, &cell_data);
+            let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &cell_data);
 
-            if self.use_default_group() {
-                if let Some(default_group_changeset) = self.update_default_group(row_rev, &changesets) {
-                    tracing::trace!("default_group_changeset: {}", default_group_changeset);
-                    if !default_group_changeset.is_empty() {
-                        changesets.push(default_group_changeset);
-                    }
+            if let Some(default_group_changeset) = self.update_default_group(row_rev, &changesets) {
+                tracing::trace!("default_group_changeset: {}", default_group_changeset);
+                if !default_group_changeset.is_empty() {
+                    changesets.push(default_group_changeset);
                 }
             }
-
             Ok(changesets)
         } else {
             Ok(vec![])
@@ -291,18 +245,30 @@ where
         row_rev: &RowRevision,
         field_rev: &FieldRevision,
     ) -> FlowyResult<Vec<GroupChangesetPB>> {
-        // if the cell_rev is none, then the row must be crated from the default group.
+        // if the cell_rev is none, then the row must in the default group.
         if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
             let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev).1;
             let cell_data = cell_bytes.parser::<P>()?;
-            Ok(self.remove_row_if_match(row_rev, &cell_data))
-        } else if let Some(group) = self.group_ctx.get_no_status_group() {
-            Ok(vec![GroupChangesetPB::delete(
-                group.id.clone(),
-                vec![row_rev.id.clone()],
-            )])
-        } else {
-            Ok(vec![])
+            if !cell_data.is_empty() {
+                tracing::error!("did_delete_delete_row {:?}", cell_rev.data);
+                return Ok(self.delete_row(row_rev, &cell_data));
+            }
+        }
+
+        match self.group_ctx.get_no_status_group() {
+            None => {
+                tracing::error!("Unexpected None value. It should have the no status group");
+                Ok(vec![])
+            }
+            Some(no_status_group) => {
+                if !no_status_group.contains_row(&row_rev.id) {
+                    tracing::error!("The row: {} should be in the no status group", row_rev.id);
+                }
+                Ok(vec![GroupChangesetPB::delete(
+                    no_status_group.id.clone(),
+                    vec![row_rev.id.clone()],
+                )])
+            }
         }
     }
 

+ 19 - 15
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs

@@ -1,13 +1,13 @@
 use crate::entities::{GroupChangesetPB, InsertedRowPB, RowPB};
 use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
-use crate::services::group::action::GroupAction;
+use crate::services::group::action::GroupControllerCustomActions;
 use crate::services::group::configuration::GroupContext;
 use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
 
 use crate::services::cell::insert_checkbox_cell;
-use crate::services::group::{move_group_row, GeneratedGroupConfig};
+use crate::services::group::{move_group_row, GeneratedGroupConfig, GeneratedGroupContext};
 use flowy_grid_data_model::revision::{
     CellRevision, CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision,
 };
@@ -21,16 +21,12 @@ pub type CheckboxGroupController = GenericGroupController<
 
 pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision>;
 
-impl GroupAction for CheckboxGroupController {
+impl GroupControllerCustomActions for CheckboxGroupController {
     type CellDataType = CheckboxCellData;
     fn default_cell_rev(&self) -> Option<CellRevision> {
         Some(CellRevision::new(UNCHECK.to_string()))
     }
 
-    fn use_default_group(&self) -> bool {
-        false
-    }
-
     fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool {
         if cell_data.is_check() {
             content == CHECK
@@ -39,9 +35,13 @@ impl GroupAction for CheckboxGroupController {
         }
     }
 
-    fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn add_or_remove_row_in_groups_if_match(
+        &mut self,
+        row_rev: &RowRevision,
+        cell_data: &Self::CellDataType,
+    ) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_status_groups(|group| {
             let mut changeset = GroupChangesetPB::new(group.id.clone());
             let is_not_contained = !group.contains_row(&row_rev.id);
             if group.id == CHECK {
@@ -81,9 +81,9 @@ impl GroupAction for CheckboxGroupController {
         changesets
     }
 
-    fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             let mut changeset = GroupChangesetPB::new(group.id.clone());
             if group.contains_row(&row_rev.id) {
                 changeset.deleted_rows.push(row_rev.id.clone());
@@ -99,7 +99,7 @@ impl GroupAction for CheckboxGroupController {
 
     fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = move_group_row(group, &mut context) {
                 group_changeset.push(changeset);
             }
@@ -133,10 +133,10 @@ impl GroupGenerator for CheckboxGroupGenerator {
     type TypeOptionType = CheckboxTypeOptionPB;
 
     fn generate_groups(
-        _field_id: &str,
+        _field_rev: &FieldRevision,
         _group_ctx: &Self::Context,
         _type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<GeneratedGroupConfig> {
+    ) -> GeneratedGroupContext {
         let check_group = GeneratedGroupConfig {
             group_rev: GroupRevision::new(CHECK.to_string(), "".to_string()),
             filter_content: CHECK.to_string(),
@@ -146,6 +146,10 @@ impl GroupGenerator for CheckboxGroupGenerator {
             group_rev: GroupRevision::new(UNCHECK.to_string(), "".to_string()),
             filter_content: UNCHECK.to_string(),
         };
-        vec![check_group, uncheck_group]
+
+        GeneratedGroupContext {
+            no_status_group: None,
+            group_configs: vec![check_group, uncheck_group],
+        }
     }
 }

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

@@ -1,5 +1,6 @@
 use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, RowPB};
-use crate::services::group::{Group, GroupController, GroupControllerActions, MoveGroupRowContext};
+use crate::services::group::action::GroupControllerSharedActions;
+use crate::services::group::{Group, GroupController, MoveGroupRowContext};
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
 use std::sync::Arc;
@@ -30,7 +31,7 @@ impl DefaultGroupController {
     }
 }
 
-impl GroupControllerActions for DefaultGroupController {
+impl GroupControllerSharedActions for DefaultGroupController {
     fn field_id(&self) -> &str {
         &self.field_id
     }

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

@@ -1,14 +1,14 @@
 use crate::entities::{GroupChangesetPB, RowPB};
 use crate::services::cell::insert_select_option_cell;
 use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser};
-use crate::services::group::action::GroupAction;
+use crate::services::group::action::GroupControllerCustomActions;
 
 use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
 };
 use crate::services::group::controller_impls::select_option_controller::util::*;
 
-use crate::services::group::GeneratedGroupConfig;
+use crate::services::group::{make_no_status_group, GeneratedGroupContext};
 use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
 
 // MultiSelect
@@ -19,26 +19,30 @@ pub type MultiSelectGroupController = GenericGroupController<
     SelectOptionCellDataParser,
 >;
 
-impl GroupAction for MultiSelectGroupController {
+impl GroupControllerCustomActions 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)
     }
 
-    fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn add_or_remove_row_in_groups_if_match(
+        &mut self,
+        row_rev: &RowRevision,
+        cell_data: &Self::CellDataType,
+    ) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
-            if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
+        self.group_ctx.iter_mut_status_groups(|group| {
+            if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
         changesets
     }
 
-    fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_status_groups(|group| {
             if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -48,7 +52,7 @@ impl GroupAction for MultiSelectGroupController {
 
     fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = move_group_row(group, &mut context) {
                 group_changeset.push(changeset);
             }
@@ -79,14 +83,20 @@ pub struct MultiSelectGroupGenerator();
 impl GroupGenerator for MultiSelectGroupGenerator {
     type Context = SelectOptionGroupContext;
     type TypeOptionType = MultiSelectTypeOptionPB;
+
     fn generate_groups(
-        field_id: &str,
+        field_rev: &FieldRevision,
         group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<GeneratedGroupConfig> {
-        match type_option {
+    ) -> GeneratedGroupContext {
+        let group_configs = match type_option {
             None => vec![],
-            Some(type_option) => generate_select_option_groups(field_id, group_ctx, &type_option.options),
+            Some(type_option) => generate_select_option_groups(&field_rev.id, group_ctx, &type_option.options),
+        };
+
+        GeneratedGroupContext {
+            no_status_group: Some(make_no_status_group(field_rev)),
+            group_configs,
         }
     }
 }

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

@@ -1,7 +1,7 @@
 use crate::entities::{GroupChangesetPB, RowPB};
 use crate::services::cell::insert_select_option_cell;
 use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB};
-use crate::services::group::action::GroupAction;
+use crate::services::group::action::GroupControllerCustomActions;
 
 use crate::services::group::controller::{
     GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
@@ -9,7 +9,7 @@ use crate::services::group::controller::{
 use crate::services::group::controller_impls::select_option_controller::util::*;
 use crate::services::group::entities::Group;
 
-use crate::services::group::GeneratedGroupConfig;
+use crate::services::group::{make_no_status_group, GeneratedGroupContext};
 use flowy_grid_data_model::revision::{FieldRevision, RowRevision, SelectOptionGroupConfigurationRevision};
 
 // SingleSelect
@@ -20,25 +20,29 @@ pub type SingleSelectGroupController = GenericGroupController<
     SelectOptionCellDataParser,
 >;
 
-impl GroupAction for SingleSelectGroupController {
+impl GroupControllerCustomActions 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)
     }
 
-    fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn add_or_remove_row_in_groups_if_match(
+        &mut self,
+        row_rev: &RowRevision,
+        cell_data: &Self::CellDataType,
+    ) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
-            if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
+        self.group_ctx.iter_mut_status_groups(|group| {
+            if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
         });
         changesets
     }
 
-    fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
+    fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
         let mut changesets = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_status_groups(|group| {
             if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
                 changesets.push(changeset);
             }
@@ -48,7 +52,7 @@ impl GroupAction for SingleSelectGroupController {
 
     fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
         let mut group_changeset = vec![];
-        self.group_ctx.iter_mut_all_groups(|group| {
+        self.group_ctx.iter_mut_groups(|group| {
             if let Some(changeset) = move_group_row(group, &mut context) {
                 group_changeset.push(changeset);
             }
@@ -80,13 +84,18 @@ impl GroupGenerator for SingleSelectGroupGenerator {
     type Context = SelectOptionGroupContext;
     type TypeOptionType = SingleSelectTypeOptionPB;
     fn generate_groups(
-        field_id: &str,
+        field_rev: &FieldRevision,
         group_ctx: &Self::Context,
         type_option: &Option<Self::TypeOptionType>,
-    ) -> Vec<GeneratedGroupConfig> {
-        match type_option {
+    ) -> GeneratedGroupContext {
+        let group_configs = match type_option {
             None => vec![],
-            Some(type_option) => generate_select_option_groups(field_id, group_ctx, &type_option.options),
+            Some(type_option) => generate_select_option_groups(&field_rev.id, group_ctx, &type_option.options),
+        };
+
+        GeneratedGroupContext {
+            no_status_group: Some(make_no_status_group(field_rev)),
+            group_configs,
         }
     }
 }

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

@@ -10,7 +10,7 @@ use flowy_grid_data_model::revision::{
 
 pub type SelectOptionGroupContext = GroupContext<SelectOptionGroupConfigurationRevision>;
 
-pub fn add_select_option_row(
+pub fn add_or_remove_select_option_row(
     group: &mut Group,
     cell_data: &SelectOptionCellDataPB,
     row_rev: &RowRevision,

+ 2 - 6
frontend/rust-lib/flowy-grid/src/services/group/group_util.rs

@@ -97,7 +97,7 @@ pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurat
     let field_id = field_rev.id.clone();
     let field_type_rev = field_rev.ty;
     let field_type: FieldType = field_rev.ty.into();
-    let mut group_configuration_rev = match field_type {
+    match field_type {
         FieldType::RichText => {
             GroupConfigurationRevision::new(field_id, field_type_rev, TextGroupConfigurationRevision::default())
                 .unwrap()
@@ -130,11 +130,7 @@ pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurat
         FieldType::URL => {
             GroupConfigurationRevision::new(field_id, field_type_rev, UrlGroupConfigurationRevision::default()).unwrap()
         }
-    };
-
-    // Append the no `status` group
-    group_configuration_rev.groups.push(make_no_status_group(field_rev));
-    group_configuration_rev
+    }
 }
 
 pub fn make_no_status_group(field_rev: &FieldRevision) -> GroupRevision {

+ 3 - 3
frontend/rust-lib/flowy-net/src/http_server/document.rs

@@ -42,7 +42,7 @@ impl TextEditorCloudService for BlockHttpCloudService {
 
 pub async fn create_document_request(token: &str, params: CreateTextBlockParams, url: &str) -> Result<(), FlowyError> {
     let _ = request_builder()
-        .post(&url.to_owned())
+        .post(url)
         .header(HEADER_TOKEN, token)
         .protobuf(params)?
         .send()
@@ -56,7 +56,7 @@ pub async fn read_document_request(
     url: &str,
 ) -> Result<Option<DocumentPB>, FlowyError> {
     let doc = request_builder()
-        .get(&url.to_owned())
+        .get(url)
         .header(HEADER_TOKEN, token)
         .protobuf(params)?
         .option_response()
@@ -67,7 +67,7 @@ pub async fn read_document_request(
 
 pub async fn reset_doc_request(token: &str, params: ResetTextBlockParams, url: &str) -> Result<(), FlowyError> {
     let _ = request_builder()
-        .patch(&url.to_owned())
+        .patch(url)
         .header(HEADER_TOKEN, token)
         .protobuf(params)?
         .send()

+ 5 - 17
frontend/rust-lib/flowy-net/src/http_server/user.rs

@@ -66,35 +66,23 @@ impl UserCloudService for UserHttpCloudService {
 }
 
 pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, ServerError> {
-    let response = request_builder()
-        .post(&url.to_owned())
-        .protobuf(params)?
-        .response()
-        .await?;
+    let response = request_builder().post(url).protobuf(params)?.response().await?;
     Ok(response)
 }
 
 pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, ServerError> {
-    let response = request_builder()
-        .post(&url.to_owned())
-        .protobuf(params)?
-        .response()
-        .await?;
+    let response = request_builder().post(url).protobuf(params)?.response().await?;
     Ok(response)
 }
 
 pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), ServerError> {
-    let _ = request_builder()
-        .delete(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .send()
-        .await?;
+    let _ = request_builder().delete(url).header(HEADER_TOKEN, token).send().await?;
     Ok(())
 }
 
 pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfilePB, ServerError> {
     let user_profile = request_builder()
-        .get(&url.to_owned())
+        .get(url)
         .header(HEADER_TOKEN, token)
         .response()
         .await?;
@@ -107,7 +95,7 @@ pub async fn update_user_profile_request(
     url: &str,
 ) -> Result<(), ServerError> {
     let _ = request_builder()
-        .patch(&url.to_owned())
+        .patch(url)
         .header(HEADER_TOKEN, token)
         .protobuf(params)?
         .send()

+ 1 - 1
frontend/rust-lib/flowy-revision/src/rev_manager.rs

@@ -164,7 +164,7 @@ impl RevisionManager {
     }
 
     pub async fn next_sync_revision(&self) -> FlowyResult<Option<Revision>> {
-        Ok(self.rev_persistence.next_sync_revision().await?)
+        self.rev_persistence.next_sync_revision().await
     }
 
     pub async fn get_revision(&self, rev_id: i64) -> Option<Revision> {

+ 0 - 1
frontend/rust-lib/flowy-revision/src/rev_persistence.rs

@@ -130,7 +130,6 @@ impl RevisionPersistence {
     #[tracing::instrument(level = "trace", skip(self, revisions), err)]
     pub(crate) async fn reset(&self, revisions: Vec<Revision>) -> FlowyResult<()> {
         let records = revisions
-            .to_vec()
             .into_iter()
             .map(|revision| RevisionRecord {
                 revision,

+ 1 - 2
frontend/rust-lib/lib-log/src/layer.rs

@@ -114,11 +114,10 @@ fn format_event_message<S: Subscriber + for<'a> tracing_subscriber::registry::Lo
     let mut message = event_visitor
         .values()
         .get("message")
-        .map(|v| match v {
+        .and_then(|v| match v {
             Value::String(s) => Some(s.as_str()),
             _ => None,
         })
-        .flatten()
         .unwrap_or_else(|| event.metadata().target())
         .to_owned();
 

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

@@ -73,4 +73,8 @@ impl CellRevision {
     pub fn new(data: String) -> Self {
         Self { data }
     }
+
+    pub fn is_empty(&self) -> bool {
+        self.data.is_empty()
+    }
 }