浏览代码

Merge pull request #600 from AppFlowy-IO/feat/row_filter_test

Feat/row filter test
Nathan.fooo 2 年之前
父节点
当前提交
766af21bc8
共有 46 个文件被更改,包括 985 次插入503 次删除
  1. 2 1
      frontend/rust-lib/flowy-grid/Cargo.toml
  2. 0 28
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/checkbox_filter.rs
  3. 91 5
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs
  4. 1 78
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/number_filter.rs
  5. 1 64
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs
  6. 0 86
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs
  7. 13 6
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs
  8. 4 3
      frontend/rust-lib/flowy-grid/src/entities/group_entities.rs
  9. 29 33
      frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs
  10. 4 3
      frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
  11. 8 1
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  12. 4 15
      frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs
  13. 4 1
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  14. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/select_option.rs
  15. 2 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  16. 30 17
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs
  17. 3 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs
  18. 3 17
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  19. 2 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs
  20. 3 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  21. 4 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs
  22. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs
  23. 6 6
      frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs
  24. 10 4
      frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs
  25. 52 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs
  26. 107 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs
  27. 13 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/mod.rs
  28. 94 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs
  29. 109 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs
  30. 100 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs
  31. 15 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs
  32. 1 0
      frontend/rust-lib/flowy-grid/src/services/filter/mod.rs
  33. 10 5
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  34. 45 1
      frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs
  35. 2 0
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs
  36. 91 0
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  37. 17 12
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs
  38. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/mod.rs
  39. 17 13
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  40. 1 1
      frontend/rust-lib/lib-dispatch/src/request/payload.rs
  41. 2 2
      frontend/rust-lib/lib-dispatch/src/response/response.rs
  42. 5 0
      shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs
  43. 64 13
      shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs
  44. 6 7
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  45. 1 0
      shared-lib/flowy-sync/src/entities/grid.rs
  46. 5 5
      shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs

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

@@ -50,6 +50,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file
 
 
 [features]
-default = []
+default = ["filter"]
 dart = ["lib-infra/dart"]
+filter = []
 flowy_unit_test = ["flowy-revision/flowy_unit_test"]

+ 0 - 28
frontend/rust-lib/flowy-grid/src/entities/filter_entities/checkbox_filter.rs

@@ -1,4 +1,3 @@
-use crate::services::field::CheckboxCellData;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::revision::GridFilterRevision;
@@ -10,16 +9,6 @@ pub struct GridCheckboxFilter {
     pub condition: CheckboxCondition,
 }
 
-impl GridCheckboxFilter {
-    pub fn apply(&self, cell_data: &CheckboxCellData) -> bool {
-        let is_check = cell_data.is_check();
-        match self.condition {
-            CheckboxCondition::IsChecked => is_check,
-            CheckboxCondition::IsUnChecked => !is_check,
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum CheckboxCondition {
@@ -58,20 +47,3 @@ impl std::convert::From<Arc<GridFilterRevision>> for GridCheckboxFilter {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::entities::{CheckboxCondition, GridCheckboxFilter};
-    use crate::services::field::CheckboxCellData;
-
-    #[test]
-    fn checkbox_filter_is_check_test() {
-        let checkbox_filter = GridCheckboxFilter {
-            condition: CheckboxCondition::IsChecked,
-        };
-        for (value, r) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
-            let data = CheckboxCellData(value.to_owned());
-            assert_eq!(checkbox_filter.apply(&data), r);
-        }
-    }
-}

+ 91 - 5
frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs

@@ -1,6 +1,10 @@
+use crate::entities::FieldType;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
+use flowy_grid_data_model::parser::NotEmptyStr;
 use flowy_grid_data_model::revision::GridFilterRevision;
+use serde::{Deserialize, Serialize};
+use std::str::FromStr;
 use std::sync::Arc;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
@@ -9,7 +13,77 @@ pub struct GridDateFilter {
     pub condition: DateFilterCondition,
 
     #[pb(index = 2, one_of)]
-    pub content: Option<String>,
+    pub start: Option<i64>,
+
+    #[pb(index = 3, one_of)]
+    pub end: Option<i64>,
+}
+
+#[derive(ProtoBuf, Default, Clone, Debug)]
+pub struct CreateGridDateFilterPayload {
+    #[pb(index = 1)]
+    pub field_id: String,
+
+    #[pb(index = 2)]
+    pub field_type: FieldType,
+
+    #[pb(index = 3)]
+    pub condition: DateFilterCondition,
+
+    #[pb(index = 4, one_of)]
+    pub start: Option<i64>,
+
+    #[pb(index = 5, one_of)]
+    pub end: Option<i64>,
+}
+
+pub struct CreateGridDateFilterParams {
+    pub field_id: String,
+
+    pub field_type: FieldType,
+
+    pub condition: DateFilterCondition,
+
+    pub start: Option<i64>,
+
+    pub end: Option<i64>,
+}
+
+impl TryInto<CreateGridDateFilterParams> for CreateGridDateFilterPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateGridDateFilterParams, Self::Error> {
+        let field_id = NotEmptyStr::parse(self.field_id)
+            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .0;
+        Ok(CreateGridDateFilterParams {
+            field_id,
+            condition: self.condition,
+            start: self.start,
+            field_type: self.field_type,
+            end: self.end,
+        })
+    }
+}
+
+#[derive(Serialize, Deserialize, Default)]
+struct DateRange {
+    start: Option<i64>,
+    end: Option<i64>,
+}
+
+impl ToString for DateRange {
+    fn to_string(&self) -> String {
+        serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
+    }
+}
+
+impl FromStr for DateRange {
+    type Err = serde_json::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        serde_json::from_str(s)
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
@@ -48,9 +122,21 @@ impl std::convert::TryFrom<u8> for DateFilterCondition {
 }
 impl std::convert::From<Arc<GridFilterRevision>> for GridDateFilter {
     fn from(rev: Arc<GridFilterRevision>) -> Self {
-        GridDateFilter {
-            condition: DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs),
-            content: rev.content.clone(),
-        }
+        let condition = DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs);
+        let mut filter = GridDateFilter {
+            condition,
+            ..Default::default()
+        };
+
+        if let Some(range) = rev
+            .content
+            .as_ref()
+            .and_then(|content| DateRange::from_str(content).ok())
+        {
+            filter.start = range.start;
+            filter.end = range.end;
+        };
+
+        filter
     }
 }

+ 1 - 78
frontend/rust-lib/flowy-grid/src/entities/filter_entities/number_filter.rs

@@ -1,10 +1,7 @@
-use crate::services::field::NumberCellData;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::revision::GridFilterRevision;
-use rust_decimal::prelude::Zero;
-use rust_decimal::Decimal;
-use std::str::FromStr;
+
 use std::sync::Arc;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
@@ -16,31 +13,6 @@ pub struct GridNumberFilter {
     pub content: Option<String>,
 }
 
-impl GridNumberFilter {
-    pub fn apply(&self, num_cell_data: &NumberCellData) -> bool {
-        if self.content.is_none() {
-            return false;
-        }
-
-        let content = self.content.as_ref().unwrap();
-        let zero_decimal = Decimal::zero();
-        let cell_decimal = num_cell_data.decimal().as_ref().unwrap_or(&zero_decimal);
-        match Decimal::from_str(content) {
-            Ok(decimal) => match self.condition {
-                NumberFilterCondition::Equal => cell_decimal == &decimal,
-                NumberFilterCondition::NotEqual => cell_decimal != &decimal,
-                NumberFilterCondition::GreaterThan => cell_decimal > &decimal,
-                NumberFilterCondition::LessThan => cell_decimal < &decimal,
-                NumberFilterCondition::GreaterThanOrEqualTo => cell_decimal >= &decimal,
-                NumberFilterCondition::LessThanOrEqualTo => cell_decimal <= &decimal,
-                NumberFilterCondition::NumberIsEmpty => num_cell_data.is_empty(),
-                NumberFilterCondition::NumberIsNotEmpty => !num_cell_data.is_empty(),
-            },
-            Err(_) => false,
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum NumberFilterCondition {
@@ -91,52 +63,3 @@ impl std::convert::From<Arc<GridFilterRevision>> for GridNumberFilter {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::entities::{GridNumberFilter, NumberFilterCondition};
-
-    use crate::services::field::{NumberCellData, NumberFormat};
-    use std::str::FromStr;
-    #[test]
-    fn number_filter_equal_test() {
-        let number_filter = GridNumberFilter {
-            condition: NumberFilterCondition::Equal,
-            content: Some("123".to_owned()),
-        };
-
-        for (num_str, r) in [("123", true), ("1234", false), ("", false)] {
-            let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
-        }
-
-        let format = NumberFormat::USD;
-        for (num_str, r) in [("$123", true), ("1234", false), ("", false)] {
-            let data = NumberCellData::from_format_str(num_str, true, &format).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
-        }
-    }
-    #[test]
-    fn number_filter_greater_than_test() {
-        let number_filter = GridNumberFilter {
-            condition: NumberFilterCondition::GreaterThan,
-            content: Some("12".to_owned()),
-        };
-        for (num_str, r) in [("123", true), ("10", false), ("30", true), ("", false)] {
-            let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
-        }
-    }
-
-    #[test]
-    fn number_filter_less_than_test() {
-        let number_filter = GridNumberFilter {
-            condition: NumberFilterCondition::LessThan,
-            content: Some("100".to_owned()),
-        };
-        for (num_str, r) in [("12", true), ("1234", false), ("30", true), ("", true)] {
-            let data = NumberCellData::from_str(num_str).unwrap();
-            assert_eq!(number_filter.apply(&data), r);
-        }
-    }
-}

+ 1 - 64
frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs

@@ -1,5 +1,4 @@
-#![allow(clippy::needless_collect)]
-use crate::services::field::select_option::{SelectOptionIds, SelectedSelectOptions};
+use crate::services::field::select_option::SelectOptionIds;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::revision::GridFilterRevision;
@@ -13,36 +12,6 @@ pub struct GridSelectOptionFilter {
     #[pb(index = 2)]
     pub option_ids: Vec<String>,
 }
-
-impl GridSelectOptionFilter {
-    pub fn apply(&self, selected_options: &SelectedSelectOptions) -> bool {
-        let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
-        match self.condition {
-            SelectOptionCondition::OptionIs => {
-                // if selected options equal to filter's options, then the required_options will be empty.
-                let required_options = self
-                    .option_ids
-                    .iter()
-                    .filter(|id| !selected_option_ids.contains(id))
-                    .collect::<Vec<_>>();
-
-                // https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect
-                !required_options.is_empty()
-            }
-            SelectOptionCondition::OptionIsNot => {
-                for option_id in selected_option_ids {
-                    if self.option_ids.contains(option_id) {
-                        return true;
-                    }
-                }
-                false
-            }
-            SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
-            SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum SelectOptionCondition {
@@ -87,35 +56,3 @@ impl std::convert::From<Arc<GridFilterRevision>> for GridSelectOptionFilter {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    #![allow(clippy::all)]
-    use crate::entities::{GridSelectOptionFilter, SelectOptionCondition};
-    use crate::services::field::select_option::{SelectOption, SelectedSelectOptions};
-
-    #[test]
-    fn select_option_filter_is_test() {
-        let option_1 = SelectOption::new("A");
-        let option_2 = SelectOption::new("B");
-
-        let filter_1 = GridSelectOptionFilter {
-            condition: SelectOptionCondition::OptionIs,
-            option_ids: vec![option_1.id.clone(), option_2.id.clone()],
-        };
-
-        assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
-                options: vec![option_1.clone(), option_2.clone()],
-            }),
-            false
-        );
-
-        assert_eq!(
-            filter_1.apply(&SelectedSelectOptions {
-                options: vec![option_1.clone()],
-            }),
-            true,
-        );
-    }
-}

+ 0 - 86
frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs

@@ -12,27 +12,6 @@ pub struct GridTextFilter {
     pub content: Option<String>,
 }
 
-impl GridTextFilter {
-    pub fn apply<T: AsRef<str>>(&self, cell_data: T) -> bool {
-        let cell_data = cell_data.as_ref();
-        let s = cell_data.to_lowercase();
-        if let Some(content) = self.content.as_ref() {
-            match self.condition {
-                TextFilterCondition::Is => &s == content,
-                TextFilterCondition::IsNot => &s != content,
-                TextFilterCondition::Contains => s.contains(content),
-                TextFilterCondition::DoesNotContain => !s.contains(content),
-                TextFilterCondition::StartsWith => s.starts_with(content),
-                TextFilterCondition::EndsWith => s.ends_with(content),
-                TextFilterCondition::TextIsEmpty => s.is_empty(),
-                TextFilterCondition::TextIsNotEmpty => !s.is_empty(),
-            }
-        } else {
-            false
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum TextFilterCondition {
@@ -83,68 +62,3 @@ impl std::convert::From<Arc<GridFilterRevision>> for GridTextFilter {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    #![allow(clippy::all)]
-    use crate::entities::{GridTextFilter, TextFilterCondition};
-
-    #[test]
-    fn text_filter_equal_test() {
-        let text_filter = GridTextFilter {
-            condition: TextFilterCondition::Is,
-            content: Some("appflowy".to_owned()),
-        };
-
-        assert!(text_filter.apply("AppFlowy"));
-        assert_eq!(text_filter.apply("appflowy"), true);
-        assert_eq!(text_filter.apply("Appflowy"), true);
-        assert_eq!(text_filter.apply("AppFlowy.io"), false);
-    }
-    #[test]
-    fn text_filter_start_with_test() {
-        let text_filter = GridTextFilter {
-            condition: TextFilterCondition::StartsWith,
-            content: Some("appflowy".to_owned()),
-        };
-
-        assert_eq!(text_filter.apply("AppFlowy.io"), true);
-        assert_eq!(text_filter.apply(""), false);
-        assert_eq!(text_filter.apply("https"), false);
-    }
-
-    #[test]
-    fn text_filter_end_with_test() {
-        let text_filter = GridTextFilter {
-            condition: TextFilterCondition::EndsWith,
-            content: Some("appflowy".to_owned()),
-        };
-
-        assert_eq!(text_filter.apply("https://github.com/appflowy"), true);
-        assert_eq!(text_filter.apply("App"), false);
-        assert_eq!(text_filter.apply("appflowy.io"), false);
-    }
-    #[test]
-    fn text_filter_empty_test() {
-        let text_filter = GridTextFilter {
-            condition: TextFilterCondition::TextIsEmpty,
-            content: Some("appflowy".to_owned()),
-        };
-
-        assert_eq!(text_filter.apply(""), true);
-        assert_eq!(text_filter.apply("App"), false);
-    }
-    #[test]
-    fn text_filter_contain_test() {
-        let text_filter = GridTextFilter {
-            condition: TextFilterCondition::Contains,
-            content: Some("appflowy".to_owned()),
-        };
-
-        assert_eq!(text_filter.apply("https://github.com/appflowy"), true);
-        assert_eq!(text_filter.apply("AppFlowy"), true);
-        assert_eq!(text_filter.apply("App"), false);
-        assert_eq!(text_filter.apply(""), false);
-        assert_eq!(text_filter.apply("github"), false);
-    }
-}

+ 13 - 6
frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs

@@ -22,16 +22,16 @@ pub struct RepeatedGridFilter {
     pub items: Vec<GridFilter>,
 }
 
-impl std::convert::From<&Arc<GridFilterRevision>> for GridFilter {
-    fn from(rev: &Arc<GridFilterRevision>) -> Self {
+impl std::convert::From<&GridFilterRevision> for GridFilter {
+    fn from(rev: &GridFilterRevision) -> Self {
         Self { id: rev.id.clone() }
     }
 }
 
-impl std::convert::From<&Vec<Arc<GridFilterRevision>>> for RepeatedGridFilter {
-    fn from(revs: &Vec<Arc<GridFilterRevision>>) -> Self {
+impl std::convert::From<Vec<Arc<GridFilterRevision>>> for RepeatedGridFilter {
+    fn from(revs: Vec<Arc<GridFilterRevision>>) -> Self {
         RepeatedGridFilter {
-            items: revs.iter().map(|rev| rev.into()).collect(),
+            items: revs.into_iter().map(|rev| rev.as_ref().into()).collect(),
         }
     }
 }
@@ -45,9 +45,12 @@ impl std::convert::From<Vec<GridFilter>> for RepeatedGridFilter {
 #[derive(ProtoBuf, Debug, Default, Clone)]
 pub struct DeleteFilterPayload {
     #[pb(index = 1)]
-    pub filter_id: String,
+    pub field_id: String,
 
     #[pb(index = 2)]
+    pub filter_id: String,
+
+    #[pb(index = 3)]
     pub field_type: FieldType,
 }
 
@@ -55,10 +58,14 @@ impl TryInto<DeleteFilterParams> for DeleteFilterPayload {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<DeleteFilterParams, Self::Error> {
+        let field_id = NotEmptyStr::parse(self.field_id)
+            .map_err(|_| ErrorCode::FieldIdIsEmpty)?
+            .0;
         let filter_id = NotEmptyStr::parse(self.filter_id)
             .map_err(|_| ErrorCode::UnexpectedEmptyString)?
             .0;
         Ok(DeleteFilterParams {
+            field_id,
             filter_id,
             field_type_rev: self.field_type.into(),
         })

+ 4 - 3
frontend/rust-lib/flowy-grid/src/entities/group_entities.rs

@@ -4,6 +4,7 @@ use flowy_grid_data_model::parser::NotEmptyStr;
 use flowy_grid_data_model::revision::GridGroupRevision;
 use flowy_sync::entities::grid::CreateGridGroupParams;
 use std::convert::TryInto;
+use std::sync::Arc;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GridGroup {
@@ -39,10 +40,10 @@ impl std::convert::From<Vec<GridGroup>> for RepeatedGridGroup {
     }
 }
 
-impl std::convert::From<&Vec<GridGroupRevision>> for RepeatedGridGroup {
-    fn from(revs: &Vec<GridGroupRevision>) -> Self {
+impl std::convert::From<Vec<Arc<GridGroupRevision>>> for RepeatedGridGroup {
+    fn from(revs: Vec<Arc<GridGroupRevision>>) -> Self {
         RepeatedGridGroup {
-            items: revs.iter().map(|rev| rev.into()).collect(),
+            items: revs.iter().map(|rev| rev.as_ref().into()).collect(),
         }
     }
 }

+ 29 - 33
frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs

@@ -9,49 +9,45 @@ use flowy_grid_data_model::revision::GridLayoutRevision;
 use flowy_sync::entities::grid::GridSettingChangesetParams;
 use std::collections::HashMap;
 use std::convert::TryInto;
+use strum::IntoEnumIterator;
+use strum_macros::EnumIter;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GridSetting {
     #[pb(index = 1)]
-    pub filters_by_layout_ty: HashMap<String, RepeatedGridFilter>,
+    pub layouts: Vec<GridLayout>,
 
     #[pb(index = 2)]
-    pub groups_by_layout_ty: HashMap<String, RepeatedGridGroup>,
+    pub current_layout_type: GridLayoutType,
 
     #[pb(index = 3)]
-    pub sorts_by_layout_ty: HashMap<String, RepeatedGridSort>,
+    pub filters_by_field_id: HashMap<String, RepeatedGridFilter>,
+
+    #[pb(index = 4)]
+    pub groups_by_field_id: HashMap<String, RepeatedGridGroup>,
+
+    #[pb(index = 5)]
+    pub sorts_by_field_id: HashMap<String, RepeatedGridSort>,
+}
+
+#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
+pub struct GridLayout {
+    #[pb(index = 1)]
+    ty: GridLayoutType,
+}
+
+impl GridLayout {
+    pub fn all() -> Vec<GridLayout> {
+        let mut layouts = vec![];
+        for layout_ty in GridLayoutType::iter() {
+            layouts.push(GridLayout { ty: layout_ty })
+        }
+
+        layouts
+    }
 }
 
-//
-// impl std::convert::From<&GridSettingRevision> for GridSetting {
-//     fn from(rev: &GridSettingRevision) -> Self {
-//         let filters_by_layout_ty: HashMap<String, RepeatedGridFilter> = rev
-//             .filters
-//             .iter()
-//             .map(|(layout_rev, filter_revs)| (layout_rev.to_string(), filter_revs.into()))
-//             .collect();
-//
-//         let groups_by_layout_ty: HashMap<String, RepeatedGridGroup> = rev
-//             .groups
-//             .iter()
-//             .map(|(layout_rev, group_revs)| (layout_rev.to_string(), group_revs.into()))
-//             .collect();
-//
-//         let sorts_by_layout_ty: HashMap<String, RepeatedGridSort> = rev
-//             .sorts
-//             .iter()
-//             .map(|(layout_rev, sort_revs)| (layout_rev.to_string(), sort_revs.into()))
-//             .collect();
-//
-//         GridSetting {
-//             filters_by_layout_ty,
-//             groups_by_layout_ty,
-//             sorts_by_layout_ty,
-//         }
-//     }
-// }
-//
-#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
+#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum, EnumIter)]
 #[repr(u8)]
 pub enum GridLayoutType {
     Table = 0,

+ 4 - 3
frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs

@@ -4,6 +4,7 @@ use flowy_grid_data_model::parser::NotEmptyStr;
 use flowy_grid_data_model::revision::GridSortRevision;
 use flowy_sync::entities::grid::CreateGridSortParams;
 use std::convert::TryInto;
+use std::sync::Arc;
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GridSort {
@@ -30,10 +31,10 @@ pub struct RepeatedGridSort {
     pub items: Vec<GridSort>,
 }
 
-impl std::convert::From<&Vec<GridSortRevision>> for RepeatedGridSort {
-    fn from(revs: &Vec<GridSortRevision>) -> Self {
+impl std::convert::From<Vec<Arc<GridSortRevision>>> for RepeatedGridSort {
+    fn from(revs: Vec<Arc<GridSortRevision>>) -> Self {
         RepeatedGridSort {
-            items: revs.iter().map(|rev| rev.into()).collect(),
+            items: revs.into_iter().map(|rev| rev.as_ref().into()).collect(),
         }
     }
 }

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

@@ -363,9 +363,16 @@ pub(crate) async fn get_select_option_handler(
             data_result(SelectOptionCellData::default())
         }
         Some(field_rev) => {
+            //
             let cell_rev = editor.get_cell_rev(&params.row_id, &params.field_id).await?;
             let type_option = select_option_operation(&field_rev)?;
-            let any_cell_data: AnyCellData = cell_rev.try_into()?;
+            let any_cell_data: AnyCellData = match cell_rev {
+                None => AnyCellData {
+                    data: "".to_string(),
+                    field_type: field_rev.field_type_rev.clone().into(),
+                },
+                Some(cell_rev) => cell_rev.try_into()?,
+            };
             let option_context = type_option.selected_select_option(any_cell_data);
             data_result(option_context)
         }

+ 4 - 15
frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs

@@ -9,7 +9,7 @@ use std::str::FromStr;
 /// So it will return an empty data. You could check the CellDataOperation trait for more information.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct AnyCellData {
-    pub cell_data: String,
+    pub data: String,
     pub field_type: FieldType,
 }
 
@@ -38,21 +38,10 @@ impl std::convert::TryFrom<&CellRevision> for AnyCellData {
     }
 }
 
-impl std::convert::TryFrom<&Option<CellRevision>> for AnyCellData {
+impl std::convert::TryFrom<CellRevision> for AnyCellData {
     type Error = FlowyError;
 
-    fn try_from(value: &Option<CellRevision>) -> Result<Self, Self::Error> {
-        match value {
-            None => Err(FlowyError::invalid_data().context("Expected CellRevision, but receive None")),
-            Some(cell_rev) => AnyCellData::try_from(cell_rev),
-        }
-    }
-}
-
-impl std::convert::TryFrom<Option<CellRevision>> for AnyCellData {
-    type Error = FlowyError;
-
-    fn try_from(value: Option<CellRevision>) -> Result<Self, Self::Error> {
+    fn try_from(value: CellRevision) -> Result<Self, Self::Error> {
         Self::try_from(&value)
     }
 }
@@ -60,7 +49,7 @@ impl std::convert::TryFrom<Option<CellRevision>> for AnyCellData {
 impl AnyCellData {
     pub fn new(content: String, field_type: FieldType) -> Self {
         AnyCellData {
-            cell_data: content,
+            data: content,
             field_type,
         }
     }

+ 4 - 1
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -51,7 +51,10 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
 
 pub fn decode_any_cell_data<T: TryInto<AnyCellData>>(data: T, field_rev: &FieldRevision) -> DecodedCellData {
     if let Ok(any_cell_data) = data.try_into() {
-        let AnyCellData { cell_data, field_type } = any_cell_data;
+        let AnyCellData {
+            data: cell_data,
+            field_type,
+        } = any_cell_data;
         let to_field_type = field_rev.field_type_rev.into();
         match try_decode_cell_data(CellData(Some(cell_data)), field_rev, &field_type, &to_field_type) {
             Ok(cell_data) => cell_data,

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

@@ -65,7 +65,7 @@ pub fn make_selected_select_options<T: TryInto<AnyCellData>>(
     options: &[SelectOption],
 ) -> Vec<SelectOption> {
     if let Ok(type_option_cell_data) = any_cell_data.try_into() {
-        let ids = SelectOptionIds::from(type_option_cell_data.cell_data);
+        let ids = SelectOptionIds::from(type_option_cell_data.data);
         ids.iter()
             .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
             .collect()
@@ -151,7 +151,7 @@ impl std::convert::TryFrom<AnyCellData> for SelectOptionIds {
     type Error = FlowyError;
 
     fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
-        Ok(Self::from(value.cell_data))
+        Ok(Self::from(value.data))
     }
 }
 

+ 2 - 14
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs

@@ -1,8 +1,6 @@
-use crate::entities::{FieldType, GridCheckboxFilter};
+use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
+use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData};
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -42,16 +40,6 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
 const YES: &str = "Yes";
 const NO: &str = "No";
 
-impl CellFilterOperation<GridCheckboxFilter> for CheckboxTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridCheckboxFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_checkbox() {
-            return Ok(true);
-        }
-        let checkbox_cell_data: CheckboxCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(&checkbox_cell_data))
-    }
-}
-
 impl CellDataOperation<String, String> for CheckboxTypeOption {
     fn decode_cell_data(
         &self,

+ 30 - 17
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -1,9 +1,8 @@
-use crate::entities::{CellChangeset, FieldType, GridDateFilter};
+use crate::entities::{CellChangeset, FieldType};
 use crate::entities::{CellIdentifier, CellIdentifierPayload};
 use crate::impl_type_option;
 use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-    FromCellChangeset, FromCellString,
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, FromCellChangeset, FromCellString,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
@@ -110,6 +109,10 @@ impl DateTypeOption {
 
     fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
         let native = NaiveDateTime::from_timestamp(timestamp, 0);
+        let native2 = NaiveDateTime::from_timestamp(timestamp, 0);
+
+        if native > native2 {}
+
         self.utc_date_time_from_native(native)
     }
 
@@ -118,19 +121,10 @@ impl DateTypeOption {
     }
 }
 
-impl CellFilterOperation<GridDateFilter> for DateTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, _filter: &GridDateFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_date() {
-            return Ok(true);
-        }
-        Ok(false)
-    }
-}
-
-impl CellDataOperation<TimestampParser, DateCellChangeset> for DateTypeOption {
+impl CellDataOperation<DateTimestamp, DateCellChangeset> for DateTypeOption {
     fn decode_cell_data(
         &self,
-        cell_data: CellData<TimestampParser>,
+        cell_data: CellData<DateTimestamp>,
         decoded_field_type: &FieldType,
         _field_rev: &FieldRevision,
     ) -> FlowyResult<DecodedCellData> {
@@ -168,17 +162,36 @@ impl CellDataOperation<TimestampParser, DateCellChangeset> for DateTypeOption {
     }
 }
 
-pub struct TimestampParser(i64);
+pub struct DateTimestamp(i64);
+impl AsRef<i64> for DateTimestamp {
+    fn as_ref(&self) -> &i64 {
+        &self.0
+    }
+}
+
+impl std::convert::From<DateTimestamp> for i64 {
+    fn from(timestamp: DateTimestamp) -> Self {
+        timestamp.0
+    }
+}
 
-impl FromCellString for TimestampParser {
+impl FromCellString for DateTimestamp {
     fn from_cell_str(s: &str) -> FlowyResult<Self>
     where
         Self: Sized,
     {
         let num = s.parse::<i64>().unwrap_or(0);
-        Ok(TimestampParser(num))
+        Ok(DateTimestamp(num))
     }
 }
+
+impl std::convert::From<AnyCellData> for DateTimestamp {
+    fn from(data: AnyCellData) -> Self {
+        let num = data.data.parse::<i64>().unwrap_or(0);
+        DateTimestamp(num)
+    }
+}
+
 #[derive(Default)]
 pub struct DateTypeOptionBuilder(DateTypeOption);
 impl_into_box_type_option_builder!(DateTypeOptionBuilder);

+ 3 - 14
frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs

@@ -1,12 +1,10 @@
-use crate::entities::{FieldType, GridSelectOptionFilter};
+use crate::entities::FieldType;
 
 use crate::impl_type_option;
-use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
+use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData};
 use crate::services::field::select_option::{
     make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds,
-    SelectOptionOperation, SelectedSelectOptions, SELECTION_IDS_SEPARATOR,
+    SelectOptionOperation, SELECTION_IDS_SEPARATOR,
 };
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
@@ -46,16 +44,7 @@ impl SelectOptionOperation for MultiSelectTypeOption {
         &mut self.options
     }
 }
-impl CellFilterOperation<GridSelectOptionFilter> for MultiSelectTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_multi_select() {
-            return Ok(true);
-        }
 
-        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
-        Ok(filter.apply(&selected_options))
-    }
-}
 impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for MultiSelectTypeOption {
     fn decode_cell_data(
         &self,

+ 3 - 17
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

@@ -1,9 +1,7 @@
 use crate::impl_type_option;
 
-use crate::entities::{FieldType, GridNumberFilter};
-use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
+use crate::entities::FieldType;
+use crate::services::cell::{CellData, CellDataChangeset, CellDataOperation, DecodedCellData};
 use crate::services::field::number_currency::Currency;
 use crate::services::field::type_options::number_type_option::format::*;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
@@ -79,7 +77,7 @@ impl NumberTypeOption {
         Self::default()
     }
 
-    fn format_cell_data(&self, s: &str) -> FlowyResult<NumberCellData> {
+    pub(crate) fn format_cell_data(&self, s: &str) -> FlowyResult<NumberCellData> {
         match self.format {
             NumberFormat::Num | NumberFormat::Percent => match Decimal::from_str(s) {
                 Ok(value, ..) => Ok(NumberCellData::from_decimal(value)),
@@ -105,18 +103,6 @@ pub(crate) fn strip_currency_symbol<T: ToString>(s: T) -> String {
     }
     s
 }
-impl CellFilterOperation<GridNumberFilter> for NumberTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridNumberFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_number() {
-            return Ok(true);
-        }
-
-        let cell_data = any_cell_data.cell_data;
-        let num_cell_data = self.format_cell_data(&cell_data)?;
-
-        Ok(filter.apply(&num_cell_data))
-    }
-}
 
 impl CellDataOperation<String, String> for NumberTypeOption {
     fn decode_cell_data(

+ 2 - 14
frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs

@@ -1,8 +1,6 @@
-use crate::entities::{FieldType, GridSelectOptionFilter};
+use crate::entities::FieldType;
 use crate::impl_type_option;
-use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
-};
+use crate::services::cell::{AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData};
 use crate::services::field::select_option::{
     make_selected_select_options, SelectOption, SelectOptionCellChangeset, SelectOptionCellData, SelectOptionIds,
     SelectOptionOperation,
@@ -43,16 +41,6 @@ impl SelectOptionOperation for SingleSelectTypeOption {
     }
 }
 
-impl CellFilterOperation<GridSelectOptionFilter> for SingleSelectTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, _filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_single_select() {
-            return Ok(true);
-        }
-        let _ids: SelectOptionIds = any_cell_data.try_into()?;
-        Ok(false)
-    }
-}
-
 impl CellDataOperation<SelectOptionIds, SelectOptionCellChangeset> for SingleSelectTypeOption {
     fn decode_cell_data(
         &self,

+ 3 - 15
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs

@@ -1,8 +1,7 @@
-use crate::entities::{FieldType, GridTextFilter};
+use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{
-    try_decode_cell_data, AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation,
-    DecodedCellData,
+    try_decode_cell_data, AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData,
 };
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
@@ -33,17 +32,6 @@ pub struct RichTextTypeOption {
 }
 impl_type_option!(RichTextTypeOption, FieldType::RichText);
 
-impl CellFilterOperation<GridTextFilter> for RichTextTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridTextFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_text() {
-            return Ok(true);
-        }
-
-        let text_cell_data: TextCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(text_cell_data))
-    }
-}
-
 impl CellDataOperation<String, String> for RichTextTypeOption {
     fn decode_cell_data(
         &self,
@@ -88,7 +76,7 @@ impl std::convert::TryFrom<AnyCellData> for TextCellData {
     type Error = FlowyError;
 
     fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
-        Ok(TextCellData(value.cell_data))
+        Ok(TextCellData(value.data))
     }
 }
 

+ 4 - 15
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs

@@ -1,9 +1,9 @@
-use crate::entities::{FieldType, GridTextFilter};
+use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{
-    AnyCellData, CellData, CellDataChangeset, CellDataOperation, CellFilterOperation, DecodedCellData, FromCellString,
+    AnyCellData, CellData, CellDataChangeset, CellDataOperation, DecodedCellData, FromCellString,
 };
-use crate::services::field::{BoxTypeOptionBuilder, TextCellData, TypeOptionBuilder};
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use fancy_regex::Regex;
 use flowy_derive::ProtoBuf;
@@ -34,17 +34,6 @@ pub struct URLTypeOption {
 }
 impl_type_option!(URLTypeOption, FieldType::URL);
 
-impl CellFilterOperation<GridTextFilter> for URLTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridTextFilter) -> FlowyResult<bool> {
-        if !any_cell_data.is_url() {
-            return Ok(true);
-        }
-
-        let text_cell_data: TextCellData = any_cell_data.try_into()?;
-        Ok(filter.apply(&text_cell_data))
-    }
-}
-
 impl CellDataOperation<URLCellData, String> for URLTypeOption {
     fn decode_cell_data(
         &self,
@@ -125,7 +114,7 @@ impl std::convert::TryFrom<AnyCellData> for URLCellData {
     type Error = FlowyError;
 
     fn try_from(data: AnyCellData) -> Result<Self, Self::Error> {
-        serde_json::from_str::<URLCellData>(&data.cell_data).map_err(internal_error)
+        serde_json::from_str::<URLCellData>(&data.data).map_err(internal_error)
     }
 }
 

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs

@@ -4,7 +4,7 @@ use std::str::FromStr;
 
 pub fn get_cell_data(cell_rev: &CellRevision) -> String {
     match AnyCellData::from_str(&cell_rev.data) {
-        Ok(type_option) => type_option.cell_data,
+        Ok(type_option) => type_option.data,
         Err(_) => String::new(),
     }
 }

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

@@ -1,20 +1,19 @@
 use crate::entities::{
     FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter, GridTextFilter,
 };
-
 use dashmap::DashMap;
-
 use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
 use flowy_sync::client_grid::GridRevisionPad;
-
 use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
+type RowId = String;
+
 #[derive(Default)]
 pub(crate) struct FilterResultCache {
     // key: row id
-    inner: DashMap<String, FilterResult>,
+    inner: DashMap<RowId, FilterResult>,
 }
 
 impl FilterResultCache {
@@ -70,7 +69,7 @@ pub(crate) struct FilterCache {
 impl FilterCache {
     pub(crate) async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Arc<Self> {
         let this = Arc::new(Self::default());
-        let _ = reload_filter_cache(this.clone(), None, grid_pad).await;
+        let _ = refresh_filter_cache(this.clone(), None, grid_pad).await;
         this
     }
 
@@ -101,7 +100,8 @@ impl FilterCache {
     }
 }
 
-pub(crate) async fn reload_filter_cache(
+/// Refresh the filter according to the field id.
+pub(crate) async fn refresh_filter_cache(
     cache: Arc<FilterCache>,
     field_ids: Option<Vec<String>>,
     grid_pad: &Arc<RwLock<GridRevisionPad>>,

+ 10 - 4
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -7,7 +7,7 @@ use crate::services::field::{
     SingleSelectTypeOption, URLTypeOption,
 };
 use crate::services::filter::filter_cache::{
-    reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
+    refresh_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
 };
 use crate::services::grid_editor_task::GridServiceTaskScheduler;
 use crate::services::row::GridBlockSnapshot;
@@ -62,6 +62,7 @@ impl GridFilterService {
 
         let mut changesets = vec![];
         for (index, block) in task_context.blocks.into_iter().enumerate() {
+            // The row_ids contains the row that its visibility was changed.
             let row_ids = block
                 .row_revs
                 .par_iter()
@@ -74,6 +75,8 @@ impl GridFilterService {
 
             let mut visible_rows = vec![];
             let mut hide_rows = vec![];
+
+            // Query the filter result from the cache
             for row_id in row_ids {
                 if self
                     .filter_result_cache
@@ -93,8 +96,11 @@ impl GridFilterService {
                 visible_rows,
                 ..Default::default()
             };
+
+            // Save the changeset for each block
             changesets.push(changeset);
         }
+
         self.notify(changesets).await;
         Ok(())
     }
@@ -106,7 +112,7 @@ impl GridFilterService {
 
         if let Some(filter_id) = &changeset.insert_filter {
             let field_ids = Some(vec![filter_id.field_id.clone()]);
-            reload_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
+            refresh_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
         }
 
         if let Some(filter_id) = &changeset.delete_filter {
@@ -179,7 +185,7 @@ fn filter_cell(
         field_type,
     };
     let any_cell_data = AnyCellData::try_from(cell_rev).ok()?;
-    let is_hidden = match &filter_id.field_type {
+    let is_visible = match &filter_id.field_type {
         FieldType::RichText => filter_cache.text_filter.get(&filter_id).and_then(|filter| {
             Some(
                 field_rev
@@ -238,7 +244,7 @@ fn filter_cell(
         }),
     }?;
 
-    let is_visible = !is_hidden.unwrap_or(false);
+    let is_visible = !is_visible.unwrap_or(true);
     match filter_result.visible_by_field_id.get(&filter_id) {
         None => {
             if is_visible {

+ 52 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs

@@ -0,0 +1,52 @@
+use crate::entities::{CheckboxCondition, GridCheckboxFilter};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{CheckboxCellData, CheckboxTypeOption};
+use flowy_error::FlowyResult;
+
+impl GridCheckboxFilter {
+    pub fn is_visible(&self, cell_data: &CheckboxCellData) -> bool {
+        let is_check = cell_data.is_check();
+        match self.condition {
+            CheckboxCondition::IsChecked => is_check,
+            CheckboxCondition::IsUnChecked => !is_check,
+        }
+    }
+}
+
+impl CellFilterOperation<GridCheckboxFilter> for CheckboxTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridCheckboxFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_checkbox() {
+            return Ok(true);
+        }
+        let checkbox_cell_data: CheckboxCellData = any_cell_data.try_into()?;
+        Ok(filter.is_visible(&checkbox_cell_data))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::entities::{CheckboxCondition, GridCheckboxFilter};
+    use crate::services::field::CheckboxCellData;
+
+    #[test]
+    fn checkbox_filter_is_check_test() {
+        let checkbox_filter = GridCheckboxFilter {
+            condition: CheckboxCondition::IsChecked,
+        };
+        for (value, visible) in [("true", true), ("yes", true), ("false", false), ("no", false)] {
+            let data = CheckboxCellData(value.to_owned());
+            assert_eq!(checkbox_filter.is_visible(&data), visible);
+        }
+    }
+
+    #[test]
+    fn checkbox_filter_is_uncheck_test() {
+        let checkbox_filter = GridCheckboxFilter {
+            condition: CheckboxCondition::IsUnChecked,
+        };
+        for (value, visible) in [("false", true), ("no", true), ("true", false), ("yes", false)] {
+            let data = CheckboxCellData(value.to_owned());
+            assert_eq!(checkbox_filter.is_visible(&data), visible);
+        }
+    }
+}

+ 107 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs

@@ -0,0 +1,107 @@
+use crate::entities::{DateFilterCondition, GridDateFilter};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{DateTimestamp, DateTypeOption};
+use flowy_error::FlowyResult;
+
+impl GridDateFilter {
+    pub fn is_visible<T: Into<i64>>(&self, cell_timestamp: T) -> bool {
+        if self.start.is_none() {
+            return false;
+        }
+        let cell_timestamp = cell_timestamp.into();
+        let start_timestamp = *self.start.as_ref().unwrap();
+        // We assume that the cell_timestamp doesn't contain hours, just day.
+        match self.condition {
+            DateFilterCondition::DateIs => cell_timestamp == start_timestamp,
+            DateFilterCondition::DateBefore => cell_timestamp < start_timestamp,
+            DateFilterCondition::DateAfter => cell_timestamp > start_timestamp,
+            DateFilterCondition::DateOnOrBefore => cell_timestamp <= start_timestamp,
+            DateFilterCondition::DateOnOrAfter => cell_timestamp >= start_timestamp,
+            DateFilterCondition::DateWithIn => {
+                if let Some(end_timestamp) = self.end.as_ref() {
+                    cell_timestamp >= start_timestamp && cell_timestamp <= *end_timestamp
+                } else {
+                    false
+                }
+            }
+            DateFilterCondition::DateIsEmpty => cell_timestamp == 0_i64,
+        }
+    }
+}
+
+impl CellFilterOperation<GridDateFilter> for DateTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridDateFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_date() {
+            return Ok(true);
+        }
+        let timestamp: DateTimestamp = any_cell_data.into();
+        Ok(filter.is_visible(timestamp))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #![allow(clippy::all)]
+    use crate::entities::{DateFilterCondition, GridDateFilter};
+
+    #[test]
+    fn date_filter_is_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateIs,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, true), (12, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_before_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateBefore,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, false), (122, true)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_before_or_on_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateOnOrBefore,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(123, true), (122, true)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_after_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateAfter,
+            start: Some(123),
+            end: None,
+        };
+
+        for (val, visible) in vec![(1234, true), (122, false), (0, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+    #[test]
+    fn date_filter_within_test() {
+        let filter = GridDateFilter {
+            condition: DateFilterCondition::DateWithIn,
+            start: Some(123),
+            end: Some(130),
+        };
+
+        for (val, visible) in vec![(123, true), (130, true), (132, false)] {
+            assert_eq!(filter.is_visible(val as i64), visible);
+        }
+    }
+}

+ 13 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/mod.rs

@@ -0,0 +1,13 @@
+mod checkbox_filter;
+mod date_filter;
+mod number_filter;
+mod select_option_filter;
+mod text_filter;
+mod url_filter;
+
+pub use checkbox_filter::*;
+pub use date_filter::*;
+pub use number_filter::*;
+pub use select_option_filter::*;
+pub use text_filter::*;
+pub use url_filter::*;

+ 94 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs

@@ -0,0 +1,94 @@
+use crate::entities::{GridNumberFilter, NumberFilterCondition};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{NumberCellData, NumberTypeOption};
+use flowy_error::FlowyResult;
+use rust_decimal::prelude::Zero;
+use rust_decimal::Decimal;
+use std::str::FromStr;
+
+impl GridNumberFilter {
+    pub fn is_visible(&self, num_cell_data: &NumberCellData) -> bool {
+        if self.content.is_none() {
+            return false;
+        }
+
+        let content = self.content.as_ref().unwrap();
+        let zero_decimal = Decimal::zero();
+        let cell_decimal = num_cell_data.decimal().as_ref().unwrap_or(&zero_decimal);
+        match Decimal::from_str(content) {
+            Ok(decimal) => match self.condition {
+                NumberFilterCondition::Equal => cell_decimal == &decimal,
+                NumberFilterCondition::NotEqual => cell_decimal != &decimal,
+                NumberFilterCondition::GreaterThan => cell_decimal > &decimal,
+                NumberFilterCondition::LessThan => cell_decimal < &decimal,
+                NumberFilterCondition::GreaterThanOrEqualTo => cell_decimal >= &decimal,
+                NumberFilterCondition::LessThanOrEqualTo => cell_decimal <= &decimal,
+                NumberFilterCondition::NumberIsEmpty => num_cell_data.is_empty(),
+                NumberFilterCondition::NumberIsNotEmpty => !num_cell_data.is_empty(),
+            },
+            Err(_) => false,
+        }
+    }
+}
+
+impl CellFilterOperation<GridNumberFilter> for NumberTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridNumberFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_number() {
+            return Ok(true);
+        }
+
+        let cell_data = any_cell_data.data;
+        let num_cell_data = self.format_cell_data(&cell_data)?;
+
+        Ok(filter.is_visible(&num_cell_data))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::entities::{GridNumberFilter, NumberFilterCondition};
+
+    use crate::services::field::{NumberCellData, NumberFormat};
+    use std::str::FromStr;
+    #[test]
+    fn number_filter_equal_test() {
+        let number_filter = GridNumberFilter {
+            condition: NumberFilterCondition::Equal,
+            content: Some("123".to_owned()),
+        };
+
+        for (num_str, visible) in [("123", true), ("1234", false), ("", false)] {
+            let data = NumberCellData::from_str(num_str).unwrap();
+            assert_eq!(number_filter.is_visible(&data), visible);
+        }
+
+        let format = NumberFormat::USD;
+        for (num_str, visible) in [("$123", true), ("1234", false), ("", false)] {
+            let data = NumberCellData::from_format_str(num_str, true, &format).unwrap();
+            assert_eq!(number_filter.is_visible(&data), visible);
+        }
+    }
+    #[test]
+    fn number_filter_greater_than_test() {
+        let number_filter = GridNumberFilter {
+            condition: NumberFilterCondition::GreaterThan,
+            content: Some("12".to_owned()),
+        };
+        for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
+            let data = NumberCellData::from_str(num_str).unwrap();
+            assert_eq!(number_filter.is_visible(&data), visible);
+        }
+    }
+
+    #[test]
+    fn number_filter_less_than_test() {
+        let number_filter = GridNumberFilter {
+            condition: NumberFilterCondition::LessThan,
+            content: Some("100".to_owned()),
+        };
+        for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", true)] {
+            let data = NumberCellData::from_str(num_str).unwrap();
+            assert_eq!(number_filter.is_visible(&data), visible);
+        }
+    }
+}

+ 109 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs

@@ -0,0 +1,109 @@
+#![allow(clippy::needless_collect)]
+
+use crate::entities::{GridSelectOptionFilter, SelectOptionCondition};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::select_option::{SelectOptionOperation, SelectedSelectOptions};
+use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
+use flowy_error::FlowyResult;
+
+impl GridSelectOptionFilter {
+    pub fn is_visible(&self, selected_options: &SelectedSelectOptions) -> bool {
+        let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
+        match self.condition {
+            SelectOptionCondition::OptionIs => {
+                if self.option_ids.len() != selected_option_ids.len() {
+                    return true;
+                }
+
+                // if selected options equal to filter's options, then the required_options will be empty.
+                let required_options = self
+                    .option_ids
+                    .iter()
+                    .filter(|id| !selected_option_ids.contains(id))
+                    .collect::<Vec<_>>();
+
+                // https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect
+                !required_options.is_empty()
+            }
+            SelectOptionCondition::OptionIsNot => {
+                for option_id in selected_option_ids {
+                    if self.option_ids.contains(option_id) {
+                        return true;
+                    }
+                }
+                false
+            }
+            SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
+            SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
+        }
+    }
+}
+
+impl CellFilterOperation<GridSelectOptionFilter> for MultiSelectTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_multi_select() {
+            return Ok(true);
+        }
+
+        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
+        Ok(filter.is_visible(&selected_options))
+    }
+}
+
+impl CellFilterOperation<GridSelectOptionFilter> for SingleSelectTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_single_select() {
+            return Ok(true);
+        }
+        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
+        Ok(filter.is_visible(&selected_options))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #![allow(clippy::all)]
+    use crate::entities::{GridSelectOptionFilter, SelectOptionCondition};
+    use crate::services::field::select_option::{SelectOption, SelectedSelectOptions};
+
+    #[test]
+    fn select_option_filter_is_test() {
+        let option_1 = SelectOption::new("A");
+        let option_2 = SelectOption::new("B");
+        let option_3 = SelectOption::new("C");
+
+        let filter_1 = GridSelectOptionFilter {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![option_1.id.clone(), option_2.id.clone()],
+        };
+
+        assert_eq!(
+            filter_1.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_2.clone()],
+            }),
+            false
+        );
+
+        assert_eq!(
+            filter_1.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_2.clone(), option_3.clone()],
+            }),
+            true
+        );
+
+        assert_eq!(
+            filter_1.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_3.clone()],
+            }),
+            true
+        );
+
+        assert_eq!(filter_1.is_visible(&SelectedSelectOptions { options: vec![] }), true);
+        assert_eq!(
+            filter_1.is_visible(&SelectedSelectOptions {
+                options: vec![option_1.clone()],
+            }),
+            true,
+        );
+    }
+}

+ 100 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs

@@ -0,0 +1,100 @@
+use crate::entities::{GridTextFilter, TextFilterCondition};
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{RichTextTypeOption, TextCellData};
+use flowy_error::FlowyResult;
+
+impl GridTextFilter {
+    pub fn is_visible<T: AsRef<str>>(&self, cell_data: T) -> bool {
+        let cell_data = cell_data.as_ref();
+        let s = cell_data.to_lowercase();
+        if let Some(content) = self.content.as_ref() {
+            match self.condition {
+                TextFilterCondition::Is => &s == content,
+                TextFilterCondition::IsNot => &s != content,
+                TextFilterCondition::Contains => s.contains(content),
+                TextFilterCondition::DoesNotContain => !s.contains(content),
+                TextFilterCondition::StartsWith => s.starts_with(content),
+                TextFilterCondition::EndsWith => s.ends_with(content),
+                TextFilterCondition::TextIsEmpty => s.is_empty(),
+                TextFilterCondition::TextIsNotEmpty => !s.is_empty(),
+            }
+        } else {
+            false
+        }
+    }
+}
+
+impl CellFilterOperation<GridTextFilter> for RichTextTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridTextFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_text() {
+            return Ok(true);
+        }
+
+        let text_cell_data: TextCellData = any_cell_data.try_into()?;
+        Ok(filter.is_visible(text_cell_data))
+    }
+}
+#[cfg(test)]
+mod tests {
+    #![allow(clippy::all)]
+    use crate::entities::{GridTextFilter, TextFilterCondition};
+
+    #[test]
+    fn text_filter_equal_test() {
+        let text_filter = GridTextFilter {
+            condition: TextFilterCondition::Is,
+            content: Some("appflowy".to_owned()),
+        };
+
+        assert!(text_filter.is_visible("AppFlowy"));
+        assert_eq!(text_filter.is_visible("appflowy"), true);
+        assert_eq!(text_filter.is_visible("Appflowy"), true);
+        assert_eq!(text_filter.is_visible("AppFlowy.io"), false);
+    }
+    #[test]
+    fn text_filter_start_with_test() {
+        let text_filter = GridTextFilter {
+            condition: TextFilterCondition::StartsWith,
+            content: Some("appflowy".to_owned()),
+        };
+
+        assert_eq!(text_filter.is_visible("AppFlowy.io"), true);
+        assert_eq!(text_filter.is_visible(""), false);
+        assert_eq!(text_filter.is_visible("https"), false);
+    }
+
+    #[test]
+    fn text_filter_end_with_test() {
+        let text_filter = GridTextFilter {
+            condition: TextFilterCondition::EndsWith,
+            content: Some("appflowy".to_owned()),
+        };
+
+        assert_eq!(text_filter.is_visible("https://github.com/appflowy"), true);
+        assert_eq!(text_filter.is_visible("App"), false);
+        assert_eq!(text_filter.is_visible("appflowy.io"), false);
+    }
+    #[test]
+    fn text_filter_empty_test() {
+        let text_filter = GridTextFilter {
+            condition: TextFilterCondition::TextIsEmpty,
+            content: Some("appflowy".to_owned()),
+        };
+
+        assert_eq!(text_filter.is_visible(""), true);
+        assert_eq!(text_filter.is_visible("App"), false);
+    }
+    #[test]
+    fn text_filter_contain_test() {
+        let text_filter = GridTextFilter {
+            condition: TextFilterCondition::Contains,
+            content: Some("appflowy".to_owned()),
+        };
+
+        assert_eq!(text_filter.is_visible("https://github.com/appflowy"), true);
+        assert_eq!(text_filter.is_visible("AppFlowy"), true);
+        assert_eq!(text_filter.is_visible("App"), false);
+        assert_eq!(text_filter.is_visible(""), false);
+        assert_eq!(text_filter.is_visible("github"), false);
+    }
+}

+ 15 - 0
frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs

@@ -0,0 +1,15 @@
+use crate::entities::GridTextFilter;
+use crate::services::cell::{AnyCellData, CellFilterOperation};
+use crate::services::field::{TextCellData, URLTypeOption};
+use flowy_error::FlowyResult;
+
+impl CellFilterOperation<GridTextFilter> for URLTypeOption {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridTextFilter) -> FlowyResult<bool> {
+        if !any_cell_data.is_url() {
+            return Ok(true);
+        }
+
+        let text_cell_data: TextCellData = any_cell_data.try_into()?;
+        Ok(filter.is_visible(&text_cell_data))
+    }
+}

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

@@ -1,4 +1,5 @@
 mod filter_cache;
 mod filter_service;
+mod impls;
 
 pub(crate) use filter_service::*;

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

@@ -11,6 +11,7 @@ use crate::services::row::{
     make_grid_blocks, make_row_from_row_rev, make_row_rev_from_context, make_rows_from_row_revs,
     CreateRowRevisionBuilder, CreateRowRevisionPayload, GridBlockSnapshot,
 };
+use crate::services::setting::make_grid_setting;
 use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::*;
@@ -453,17 +454,21 @@ impl GridRevisionEditor {
     }
 
     pub async fn get_grid_setting(&self) -> FlowyResult<GridSetting> {
-        // let read_guard = self.grid_pad.read().await;
-        // let grid_setting_rev = read_guard.get_grid_setting_rev();
-        // Ok(grid_setting_rev.into())
-        todo!()
+        let read_guard = self.grid_pad.read().await;
+        let grid_setting_rev = read_guard.get_grid_setting_rev();
+        let field_revs = read_guard.get_field_revs(None)?;
+        let grid_setting = make_grid_setting(grid_setting_rev, &field_revs);
+        Ok(grid_setting)
     }
 
     pub async fn get_grid_filter(&self, layout_type: &GridLayoutType) -> FlowyResult<Vec<GridFilter>> {
         let read_guard = self.grid_pad.read().await;
         let layout_rev = layout_type.clone().into();
         match read_guard.get_filters(Some(&layout_rev), None) {
-            Some(filter_revs) => Ok(filter_revs.iter().map(GridFilter::from).collect::<Vec<GridFilter>>()),
+            Some(filter_revs) => Ok(filter_revs
+                .iter()
+                .map(|filter_rev| filter_rev.as_ref().into())
+                .collect::<Vec<GridFilter>>()),
             None => Ok(vec![]),
         }
     }

+ 45 - 1
frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs

@@ -1,5 +1,10 @@
-use crate::entities::GridLayoutType;
+use crate::entities::{
+    GridLayout, GridLayoutType, GridSetting, RepeatedGridFilter, RepeatedGridGroup, RepeatedGridSort,
+};
+use flowy_grid_data_model::revision::{FieldRevision, GridSettingRevision};
 use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
+use std::collections::HashMap;
+use std::sync::Arc;
 
 pub struct GridSettingChangesetBuilder {
     params: GridSettingChangesetParams,
@@ -34,3 +39,42 @@ impl GridSettingChangesetBuilder {
         self.params
     }
 }
+
+pub fn make_grid_setting(grid_setting_rev: &GridSettingRevision, field_revs: &[Arc<FieldRevision>]) -> GridSetting {
+    let current_layout_type: GridLayoutType = grid_setting_rev.layout.clone().into();
+    let filters_by_field_id = grid_setting_rev
+        .get_all_filter(field_revs)
+        .map(|filters_by_field_id| {
+            filters_by_field_id
+                .into_iter()
+                .map(|(k, v)| (k, v.into()))
+                .collect::<HashMap<String, RepeatedGridFilter>>()
+        })
+        .unwrap_or_default();
+    let groups_by_field_id = grid_setting_rev
+        .get_all_group()
+        .map(|groups_by_field_id| {
+            groups_by_field_id
+                .into_iter()
+                .map(|(k, v)| (k, v.into()))
+                .collect::<HashMap<String, RepeatedGridGroup>>()
+        })
+        .unwrap_or_default();
+    let sorts_by_field_id = grid_setting_rev
+        .get_all_sort()
+        .map(|sorts_by_field_id| {
+            sorts_by_field_id
+                .into_iter()
+                .map(|(k, v)| (k, v.into()))
+                .collect::<HashMap<String, RepeatedGridSort>>()
+        })
+        .unwrap_or_default();
+
+    GridSetting {
+        layouts: GridLayout::all(),
+        current_layout_type,
+        filters_by_field_id,
+        groups_by_field_id,
+        sorts_by_field_id,
+    }
+}

+ 2 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs

@@ -0,0 +1,2 @@
+mod script;
+mod text_filter_test;

+ 91 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -0,0 +1,91 @@
+#![cfg_attr(rustfmt, rustfmt::skip)]
+#![allow(clippy::all)]
+#![allow(dead_code)]
+#![allow(unused_imports)]
+
+use flowy_grid::entities::{CreateGridFilterPayload, GridLayoutType, GridSetting};
+use flowy_grid::services::setting::GridSettingChangesetBuilder;
+use flowy_grid_data_model::revision::{FieldRevision, FieldTypeRevision};
+use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
+use crate::grid::script::GridEditorTest;
+
+pub enum FilterScript {
+    #[allow(dead_code)]
+    UpdateGridSetting {
+        params: GridSettingChangesetParams,
+    },
+    InsertGridTableFilter {
+        payload: CreateGridFilterPayload,
+    },
+    AssertTableFilterCount {
+        count: i32,
+    },
+    DeleteGridTableFilter {
+        filter_id: String,
+        field_rev: FieldRevision,
+    },
+    #[allow(dead_code)]
+    AssertGridSetting {
+        expected_setting: GridSetting,
+    },
+}
+
+pub struct GridFilterTest {
+    pub editor_test: GridEditorTest,
+}
+
+impl GridFilterTest {
+    pub async fn new() -> Self {
+     let editor_test =  GridEditorTest::new().await;
+        Self {
+            editor_test
+        }
+    }
+
+    pub async fn run_scripts(&mut self, scripts: Vec<FilterScript>) {
+        for script in scripts {
+            self.run_script(script).await;
+        }
+    }
+
+    pub async fn run_script(&mut self, script: FilterScript) {
+
+        match script {
+            FilterScript::UpdateGridSetting { params } => {
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
+            }
+            FilterScript::InsertGridTableFilter { payload } => {
+                let params: CreateGridFilterParams = payload.try_into().unwrap();
+                let layout_type = GridLayoutType::Table;
+                let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
+                    .insert_filter(params)
+                    .build();
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
+            }
+            FilterScript::AssertTableFilterCount { count } => {
+                let layout_type = GridLayoutType::Table;
+                let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
+                assert_eq!(count as usize, filters.len());
+            }
+            FilterScript::DeleteGridTableFilter { filter_id, field_rev} => {
+                let layout_type = GridLayoutType::Table;
+                let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
+                    .delete_filter(DeleteFilterParams { field_id: field_rev.id, filter_id, field_type_rev: field_rev.field_type_rev })
+                    .build();
+                let _ = self.editor.update_grid_setting(params).await.unwrap();
+            }
+            FilterScript::AssertGridSetting { expected_setting } => {
+                let setting = self.editor.get_grid_setting().await.unwrap();
+                assert_eq!(expected_setting, setting);
+            }
+        }
+    }
+}
+
+impl std::ops::Deref for GridFilterTest {
+    type Target = GridEditorTest;
+
+    fn deref(&self) -> &Self::Target {
+        &self.editor_test
+    }
+}

+ 17 - 12
frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs → frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs

@@ -1,33 +1,34 @@
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
-use flowy_grid::entities::CreateGridFilterPayload;
+use crate::grid::filter_test::script::FilterScript::*;
+use crate::grid::filter_test::script::*;
+use flowy_grid::entities::{CreateGridFilterPayload, TextFilterCondition};
+use flowy_grid_data_model::revision::FieldRevision;
 
 #[tokio::test]
 async fn grid_filter_create_test() {
-    let test = GridEditorTest::new().await;
+    let mut test = GridFilterTest::new().await;
     let field_rev = test.text_field();
     let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
     let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 #[should_panic]
 async fn grid_filter_invalid_condition_panic_test() {
-    let test = GridEditorTest::new().await;
-    let field_rev = test.text_field();
+    let mut test = GridFilterTest::new().await;
+    let field_rev = test.text_field().clone();
 
     // 100 is not a valid condition, so this test should be panic.
-    let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned()));
+    let payload = CreateGridFilterPayload::new(&field_rev, 100, Some("".to_owned()));
     let scripts = vec![InsertGridTableFilter { payload }];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_filter_delete_test() {
-    let mut test = GridEditorTest::new().await;
+    let mut test = GridFilterTest::new().await;
     let field_rev = test.text_field().clone();
-    let payload = CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
+    let payload = create_filter(&field_rev, TextFilterCondition::TextIsEmpty, "abc");
     let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
     test.run_scripts(scripts).await;
 
@@ -35,7 +36,7 @@ async fn grid_filter_delete_test() {
     test.run_scripts(vec![
         DeleteGridTableFilter {
             filter_id: filter.id,
-            field_type: field_rev.field_type.clone(),
+            field_rev,
         },
         AssertTableFilterCount { count: 0 },
     ])
@@ -44,3 +45,7 @@ async fn grid_filter_delete_test() {
 
 #[tokio::test]
 async fn grid_filter_get_rows_test() {}
+
+fn create_filter(field_rev: &FieldRevision, condition: TextFilterCondition, s: &str) -> CreateGridFilterPayload {
+    CreateGridFilterPayload::new(field_rev, condition, Some(s.to_owned()))
+}

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

@@ -2,7 +2,7 @@ mod block_test;
 mod cell_test;
 mod field_test;
 mod field_util;
-// mod filter_test;
+mod filter_test;
 mod row_test;
 mod row_util;
 mod script;

+ 17 - 13
frontend/rust-lib/flowy-grid/tests/grid/script.rs

@@ -1,16 +1,19 @@
-#![cfg_attr(rustfmt, rustfmt::skip)]
 #![allow(clippy::all)]
 #![allow(dead_code)]
 #![allow(unused_imports)]
 use bytes::Bytes;
+use flowy_grid::entities::*;
+use flowy_grid::services::field::select_option::SelectOption;
 use flowy_grid::services::field::*;
 use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor};
 use flowy_grid::services::row::CreateRowRevisionPayload;
 use flowy_grid::services::setting::GridSettingChangesetBuilder;
-use flowy_grid::entities::*;
 use flowy_grid_data_model::revision::*;
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_sync::client_grid::GridBuilder;
+use flowy_sync::entities::grid::{
+    CreateGridFilterParams, DeleteFilterParams, FieldChangesetParams, GridSettingChangesetParams,
+};
 use flowy_test::helper::ViewTest;
 use flowy_test::FlowySDKTest;
 use std::collections::HashMap;
@@ -18,8 +21,6 @@ use std::sync::Arc;
 use std::time::Duration;
 use strum::EnumCount;
 use tokio::time::sleep;
-use flowy_grid::services::field::select_option::SelectOption;
-use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, FieldChangesetParams, GridSettingChangesetParams};
 
 pub enum EditorScript {
     CreateField {
@@ -82,7 +83,7 @@ pub enum EditorScript {
     },
     DeleteGridTableFilter {
         filter_id: String,
-        field_type: FieldType,
+        field_rev: FieldRevision,
     },
     #[allow(dead_code)]
     AssertGridSetting {
@@ -170,10 +171,7 @@ impl GridEditorTest {
                 assert_eq!(self.field_count, self.field_revs.len());
             }
             EditorScript::AssertFieldCount(count) => {
-                assert_eq!(
-                    self.editor.get_field_revs(None).await.unwrap().len(),
-                    count
-                );
+                assert_eq!(self.editor.get_field_revs(None).await.unwrap().len(), count);
             }
             EditorScript::AssertFieldEqual { field_index, field_rev } => {
                 let field_revs = self.editor.get_field_revs(None).await.unwrap();
@@ -204,14 +202,16 @@ impl GridEditorTest {
             }
             EditorScript::CreateEmptyRow => {
                 let row_order = self.editor.create_row(None).await.unwrap();
-                self.row_order_by_row_id.insert(row_order.row_id().to_owned(), row_order);
+                self.row_order_by_row_id
+                    .insert(row_order.row_id().to_owned(), row_order);
                 self.row_revs = self.get_row_revs().await;
                 self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
             }
             EditorScript::CreateRow { payload: context } => {
                 let row_orders = self.editor.insert_rows(vec![context]).await.unwrap();
                 for row_order in row_orders {
-                    self.row_order_by_row_id.insert(row_order.row_id().to_owned(), row_order);
+                    self.row_order_by_row_id
+                        .insert(row_order.row_id().to_owned(), row_order);
                 }
                 self.row_revs = self.get_row_revs().await;
                 self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
@@ -271,10 +271,14 @@ impl GridEditorTest {
                 let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
                 assert_eq!(count as usize, filters.len());
             }
-            EditorScript::DeleteGridTableFilter { filter_id ,field_type} => {
+            EditorScript::DeleteGridTableFilter { filter_id, field_rev } => {
                 let layout_type = GridLayoutType::Table;
                 let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
-                    .delete_filter(DeleteFilterParams { filter_id, field_type_rev: field_type.into() })
+                    .delete_filter(DeleteFilterParams {
+                        field_id: field_rev.id,
+                        filter_id,
+                        field_type_rev: field_rev.field_type_rev,
+                    })
                     .build();
                 let _ = self.editor.update_grid_setting(params).await.unwrap();
             }

+ 1 - 1
frontend/rust-lib/lib-dispatch/src/request/payload.rs

@@ -5,7 +5,7 @@ pub enum PayloadError {}
 
 // TODO: support stream data
 #[derive(Clone)]
-#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
 pub enum Payload {
     None,
     Bytes(Bytes),

+ 2 - 2
frontend/rust-lib/lib-dispatch/src/response/response.rs

@@ -9,7 +9,7 @@ use derivative::*;
 use std::{convert::TryFrom, fmt, fmt::Formatter};
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
 pub enum StatusCode {
     Ok = 0,
     Err = 1,
@@ -18,7 +18,7 @@ pub enum StatusCode {
 
 // serde user guide: https://serde.rs/field-attrs.html
 #[derive(Debug, Clone, Derivative)]
-#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
 pub struct EventResponse {
     #[derivative(Debug = "ignore")]
     pub payload: Payload,

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

@@ -31,6 +31,11 @@ pub struct GridRevision {
     pub fields: Vec<Arc<FieldRevision>>,
     pub blocks: Vec<Arc<GridBlockMetaRevision>>,
 
+    #[cfg(feature = "filter")]
+    #[serde(default)]
+    pub setting: GridSettingRevision,
+
+    #[cfg(not(feature = "filter"))]
     #[serde(default, skip)]
     pub setting: GridSettingRevision,
 }

+ 64 - 13
shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs

@@ -1,8 +1,9 @@
-use crate::revision::FieldTypeRevision;
+use crate::revision::{FieldRevision, FieldTypeRevision};
 use indexmap::IndexMap;
 use nanoid::nanoid;
 use serde::{Deserialize, Serialize};
 use serde_repr::*;
+use std::collections::HashMap;
 use std::sync::Arc;
 
 pub fn gen_grid_filter_id() -> String {
@@ -17,20 +18,33 @@ pub fn gen_grid_sort_id() -> String {
     nanoid!(6)
 }
 
+/// Each layout contains multiple key/value.
+/// Key:    field_id
+/// Value:  this value also contains key/value.
+///         Key: FieldType,
+///         Value: the corresponding filter.
+///
+/// This overall struct is described below:
+/// GridSettingRevision
+///     layout:
+///           field_id:
+///                   FieldType: GridFilterRevision
+///                   FieldType: GridFilterRevision
+///           field_id:
+///                   FieldType: GridFilterRevision
+///                   FieldType: GridFilterRevision
+///     layout:
+///           field_id:
+///                   FieldType: GridFilterRevision
+///                   FieldType: GridFilterRevision
+///
+/// Group and sorts will be the same structure as filters.
 #[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
 pub struct GridSettingRevision {
     pub layout: GridLayoutRevision,
-    // layout:
-    //       field_id:
-    //               FieldType: GridFilterRevision
-    //               FieldType: GridFilterRevision
-    // layout:
-    //       field_id:
-    //               FieldType: GridFilterRevision
-    //       field_id:
-    //               FieldType: GridFilterRevision
+
     #[serde(with = "indexmap::serde_seq")]
-    pub filters: IndexMap<GridLayoutRevision, IndexMap<String, GridFilterRevisionMap>>,
+    filters: IndexMap<GridLayoutRevision, IndexMap<String, GridFilterRevisionMap>>,
 
     #[serde(skip, with = "indexmap::serde_seq")]
     pub groups: IndexMap<GridLayoutRevision, Vec<GridGroupRevision>>,
@@ -39,7 +53,44 @@ pub struct GridSettingRevision {
     pub sorts: IndexMap<GridLayoutRevision, Vec<GridSortRevision>>,
 }
 
+pub type FiltersByFieldId = HashMap<String, Vec<Arc<GridFilterRevision>>>;
+pub type GroupsByFieldId = HashMap<String, Vec<Arc<GridGroupRevision>>>;
+pub type SortsByFieldId = HashMap<String, Vec<Arc<GridSortRevision>>>;
 impl GridSettingRevision {
+    pub fn get_all_group(&self) -> Option<GroupsByFieldId> {
+        None
+    }
+
+    pub fn get_all_sort(&self) -> Option<SortsByFieldId> {
+        None
+    }
+
+    /// Return the Filters of the current layout
+    pub fn get_all_filter(&self, field_revs: &[Arc<FieldRevision>]) -> Option<FiltersByFieldId> {
+        let layout = &self.layout;
+        // Acquire the read lock of the filters.
+        let filter_rev_map_by_field_id = self.filters.get(layout)?;
+        // Get the filters according to the FieldType, so we need iterate the field_revs.
+        let filters_by_field_id = field_revs
+            .iter()
+            .flat_map(|field_rev| {
+                let field_type = &field_rev.field_type_rev;
+                let field_id = &field_rev.id;
+
+                let filter_rev_map: &GridFilterRevisionMap = filter_rev_map_by_field_id.get(field_id)?;
+                let filters: Vec<Arc<GridFilterRevision>> = filter_rev_map.get(field_type)?.clone();
+                Some((field_rev.id.clone(), filters))
+            })
+            .collect::<FiltersByFieldId>();
+        Some(filters_by_field_id)
+    }
+
+    #[allow(dead_code)]
+    fn get_filter_rev_map(&self, layout: &GridLayoutRevision, field_id: &str) -> Option<&GridFilterRevisionMap> {
+        let filter_rev_map_by_field_id = self.filters.get(layout)?;
+        filter_rev_map_by_field_id.get(field_id)
+    }
+
     pub fn get_mut_filters(
         &mut self,
         layout: &GridLayoutRevision,
@@ -56,12 +107,12 @@ impl GridSettingRevision {
         &self,
         layout: &GridLayoutRevision,
         field_id: &str,
-        field_type: &FieldTypeRevision,
+        field_type_rev: &FieldTypeRevision,
     ) -> Option<Vec<Arc<GridFilterRevision>>> {
         self.filters
             .get(layout)
             .and_then(|filter_rev_map_by_field_id| filter_rev_map_by_field_id.get(field_id))
-            .and_then(|filter_rev_map| filter_rev_map.get(field_type))
+            .and_then(|filter_rev_map| filter_rev_map.get(field_type_rev))
             .cloned()
     }
 

+ 6 - 7
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -17,8 +17,8 @@ pub type GridRevisionDelta = PlainTextDelta;
 pub type GridRevisionDeltaBuilder = PlainTextDeltaBuilder;
 
 pub struct GridRevisionPad {
-    pub(crate) grid_rev: Arc<GridRevision>,
-    pub(crate) delta: GridRevisionDelta,
+    grid_rev: Arc<GridRevision>,
+    delta: GridRevisionDelta,
 }
 
 pub trait JsonDeserializer {
@@ -358,10 +358,9 @@ impl GridRevisionPad {
 
             if is_contain {
                 // Only return the filters for the current fields' type.
-                if let Some(mut t_filter_revs) =
-                    self.grid_rev
-                        .setting
-                        .get_filters(layout_ty, &field_rev.id, &field_rev.field_type_rev)
+                let field_id = &field_rev.id;
+                let field_type_rev = &field_rev.field_type_rev;
+                if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(layout_ty, field_id, field_type_rev)
                 {
                     filter_revs.append(&mut t_filter_revs);
                 }
@@ -396,7 +395,7 @@ impl GridRevisionPad {
             if let Some(params) = changeset.delete_filter {
                 match grid_rev
                     .setting
-                    .get_mut_filters(&layout_rev, &params.filter_id, &params.field_type_rev)
+                    .get_mut_filters(&layout_rev, &params.field_id, &params.field_type_rev)
                 {
                     Some(filters) => {
                         filters.retain(|filter| filter.id != params.filter_id);

+ 1 - 0
shared-lib/flowy-sync/src/entities/grid.rs

@@ -24,6 +24,7 @@ pub struct CreateGridFilterParams {
 }
 
 pub struct DeleteFilterParams {
+    pub field_id: String,
     pub filter_id: String,
     pub field_type_rev: FieldTypeRevision,
 }

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

@@ -114,12 +114,12 @@ fn generate_dart_protobuf_files(
     check_pb_dart_plugin();
     let protoc_bin_path = protoc_bin_path.to_str().unwrap().to_owned();
     paths.iter().for_each(|path| {
-        if cmd_lib::run_cmd! {
+        let result = cmd_lib::run_cmd! {
             ${protoc_bin_path} --dart_out=${output} --proto_path=${proto_file_output_path} ${path}
-        }
-        .is_err()
-        {
-            panic!("Generate dart pb file failed with: {}", path)
+        };
+
+        if result.is_err() {
+            panic!("Generate dart pb file failed with: {}, {:?}", path, result)
         };
     });