Browse Source

fix: edit no status card

appflowy 2 years ago
parent
commit
3511737bb3
27 changed files with 221 additions and 149 deletions
  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. 3 5
      frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs
  13. 10 11
      frontend/rust-lib/flowy-grid/src/services/group/action.rs
  14. 23 13
      frontend/rust-lib/flowy-grid/src/services/group/configuration.rs
  15. 40 31
      frontend/rust-lib/flowy-grid/src/services/group/controller.rs
  16. 17 13
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs
  17. 2 1
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs
  18. 21 11
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs
  19. 20 11
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs
  20. 1 1
      frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs
  21. 2 6
      frontend/rust-lib/flowy-grid/src/services/group/group_util.rs
  22. 3 3
      frontend/rust-lib/flowy-net/src/http_server/document.rs
  23. 5 17
      frontend/rust-lib/flowy-net/src/http_server/user.rs
  24. 1 1
      frontend/rust-lib/flowy-revision/src/rev_manager.rs
  25. 0 1
      frontend/rust-lib/flowy-revision/src/rev_persistence.rs
  26. 1 2
      frontend/rust-lib/lib-log/src/layer.rs
  27. 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;

+ 3 - 5
frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs

@@ -123,8 +123,8 @@ 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;
             }
@@ -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();

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

@@ -1,4 +1,5 @@
 use crate::entities::{GroupChangesetPB, GroupViewChangesetPB};
+use crate::services::cell::CellDataIsEmpty;
 use crate::services::group::controller::MoveGroupRowContext;
 use crate::services::group::Group;
 use flowy_error::FlowyResult;
@@ -10,7 +11,7 @@ use std::sync::Arc;
 /// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
 ///
 pub trait GroupControllerCustomActions: Send + Sync {
-    type CellDataType;
+    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
@@ -20,22 +21,20 @@ pub trait GroupControllerCustomActions: Send + Sync {
         None
     }
 
-    /// Returns a bool value to determine the `No status` group should show or hide.
-    ///
-    fn use_no_status_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;
 
-    /// Adding a new row to the group if the cell data match the group filter.
+    /// Adds or removes a row if the cell data match the group filter.
     /// It gets called after editing the cell or row
     ///
-    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>;
 
-    ///
-    fn remove_row_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>;

+ 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();

+ 40 - 31
frontend/rust-lib/flowy-grid/src/services/group/controller.rs

@@ -1,5 +1,5 @@
 use crate::entities::{GroupChangesetPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
-use crate::services::cell::{decode_any_cell_data, CellBytesParser};
+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;
@@ -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 {
@@ -67,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(),
@@ -161,16 +166,7 @@ where
     }
 
     fn groups(&self) -> Vec<Group> {
-        if self.use_no_status_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)> {
@@ -230,13 +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);
-            // 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);
-            //     }
-            // }
+            let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &cell_data);
+
+            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![])
@@ -248,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()],
+                )])
+            }
         }
     }
 

+ 17 - 13
frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs

@@ -7,7 +7,7 @@ use crate::services::group::controller::{
 };
 
 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,
 };
@@ -27,10 +27,6 @@ impl GroupControllerCustomActions for CheckboxGroupController {
         Some(CellRevision::new(UNCHECK.to_string()))
     }
 
-    fn use_no_status_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 GroupControllerCustomActions 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 GroupControllerCustomActions 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 GroupControllerCustomActions 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],
+        }
     }
 }

+ 2 - 1
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, GroupControllerSharedActions, 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;

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

@@ -8,7 +8,7 @@ use crate::services::group::controller::{
 };
 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
@@ -26,19 +26,23 @@ impl GroupControllerCustomActions for MultiSelectGroupController {
         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 GroupControllerCustomActions 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,
         }
     }
 }

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

@@ -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
@@ -26,19 +26,23 @@ impl GroupControllerCustomActions for SingleSelectGroupController {
         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 GroupControllerCustomActions 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()
+    }
 }