Selaa lähdekoodia

Merge remote-tracking branch 'upstream/main'

Sean Riley Hawkins 3 vuotta sitten
vanhempi
commit
2cc5f9407e
65 muutettua tiedostoa jossa 1518 lisäystä ja 933 poistoa
  1. 2 2
      .github/workflows/ci.yaml
  2. 1 1
      .github/workflows/dart_lint.yml
  3. 1 1
      .github/workflows/dart_test.yml
  4. 1 1
      .github/workflows/release.yml
  5. 1 1
      .github/workflows/rust_lint.yml
  6. 3 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart
  7. 2 1
      frontend/rust-lib/flowy-grid/Cargo.toml
  8. 0 28
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/checkbox_filter.rs
  9. 91 5
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs
  10. 1 78
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/number_filter.rs
  11. 1 64
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs
  12. 0 86
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs
  13. 13 6
      frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs
  14. 4 3
      frontend/rust-lib/flowy-grid/src/entities/group_entities.rs
  15. 29 33
      frontend/rust-lib/flowy-grid/src/entities/setting_entities.rs
  16. 4 3
      frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
  17. 8 1
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  18. 4 15
      frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs
  19. 4 1
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  20. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/select_option.rs
  21. 2 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option.rs
  22. 30 17
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs
  23. 3 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs
  24. 3 17
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  25. 2 14
      frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs
  26. 3 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option.rs
  27. 4 15
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option.rs
  28. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/util/cell_data_util.rs
  29. 6 6
      frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs
  30. 10 4
      frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs
  31. 52 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/checkbox_filter.rs
  32. 107 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/date_filter.rs
  33. 13 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/mod.rs
  34. 94 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/number_filter.rs
  35. 109 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs
  36. 100 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/text_filter.rs
  37. 15 0
      frontend/rust-lib/flowy-grid/src/services/filter/impls/url_filter.rs
  38. 1 0
      frontend/rust-lib/flowy-grid/src/services/filter/mod.rs
  39. 10 5
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  40. 45 1
      frontend/rust-lib/flowy-grid/src/services/setting/setting_builder.rs
  41. 5 4
      frontend/rust-lib/flowy-grid/tests/grid/block_test/block_test.rs
  42. 5 0
      frontend/rust-lib/flowy-grid/tests/grid/block_test/mod.rs
  43. 17 17
      frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs
  44. 150 0
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  45. 7 6
      frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs
  46. 2 0
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/mod.rs
  47. 61 0
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs
  48. 4 4
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs
  49. 3 0
      frontend/rust-lib/flowy-grid/tests/grid/field_test/mod.rs
  50. 94 0
      frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs
  51. 22 22
      frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs
  52. 0 0
      frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs
  53. 2 0
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs
  54. 97 0
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  55. 17 12
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs
  56. 164 0
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
  57. 2 5
      frontend/rust-lib/flowy-grid/tests/grid/mod.rs
  58. 0 376
      frontend/rust-lib/flowy-grid/tests/grid/script.rs
  59. 1 1
      frontend/rust-lib/lib-dispatch/src/request/payload.rs
  60. 2 2
      frontend/rust-lib/lib-dispatch/src/response/response.rs
  61. 5 0
      shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs
  62. 64 13
      shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs
  63. 6 7
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  64. 1 0
      shared-lib/flowy-sync/src/entities/grid.rs
  65. 5 5
      shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs

+ 2 - 2
.github/workflows/ci.yaml

@@ -16,7 +16,7 @@ jobs:
         os: [ubuntu-latest, macos-latest]
         include:
           - os: ubuntu-latest
-            flutter_profile: development-linux-x86
+            flutter_profile: development-linux-x86_64
           - os: macos-latest
             flutter_profile: development-mac-x86_64
     runs-on: ${{ matrix.os }}
@@ -82,4 +82,4 @@ jobs:
       - name: Build
         working-directory: frontend
         run: |
-          cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
+          cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev

+ 1 - 1
.github/workflows/dart_lint.yml

@@ -42,7 +42,7 @@ jobs:
       - name: Build FlowySDK
         working-directory: frontend
         run: |
-          cargo make --profile development-linux-x86 flowy-sdk-dev
+          cargo make --profile development-linux-x86_64 flowy-sdk-dev
 
       - name: Code Generation
         working-directory: frontend/app_flowy

+ 1 - 1
.github/workflows/dart_test.yml

@@ -56,7 +56,7 @@ jobs:
       - name: Build FlowySDK
         working-directory: frontend
         run: |
-          cargo make --profile development-linux-x86 flowy-sdk-dev
+          cargo make --profile development-linux-x86_64 flowy-sdk-dev
 
       - name: Code Generation
         working-directory: frontend/app_flowy

+ 1 - 1
.github/workflows/release.yml

@@ -67,7 +67,7 @@ jobs:
         working-directory: frontend
         run: |
           flutter config --enable-linux-desktop
-          cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86 appflowy
+          cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86_64 appflowy
 
       - name: Upload Release Asset
         id: upload-release-asset

+ 1 - 1
.github/workflows/rust_lint.yml

@@ -30,7 +30,7 @@ jobs:
       - name: Build FlowySDK
         working-directory: frontend
         run: |
-          cargo make --profile development-linux-x86 flowy-sdk-dev
+          cargo make --profile development-linux-x86_64 flowy-sdk-dev
 
       - run: rustup component add rustfmt
         working-directory: frontend/rust-lib

+ 3 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

@@ -19,7 +19,6 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:window_size/window_size.dart';
 
 class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
   final GridRow rowData;
@@ -35,8 +34,8 @@ class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
   State<RowDetailPage> createState() => _RowDetailPageState();
 
   void show(BuildContext context) async {
-    final window = await getWindowInfo();
-    final size = Size(window.frame.size.width * 0.7, window.frame.size.height * 0.7);
+    final windowSize = MediaQuery.of(context).size;
+    final size = windowSize * 0.7;
     FlowyOverlay.of(context).insertWithRect(
       widget: OverlayContainer(
         child: this,
@@ -44,7 +43,7 @@ class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
       ),
       identifier: RowDetailPage.identifier(),
       anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
-      anchorSize: window.frame.size,
+      anchorSize: windowSize,
       anchorDirection: AnchorDirection.center,
       style: FlowyOverlayStyle(blur: false),
       delegate: this,

+ 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.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,
+    }
+}

+ 5 - 4
frontend/rust-lib/flowy-grid/tests/grid/block_test.rs → frontend/rust-lib/flowy-grid/tests/grid/block_test/block_test.rs

@@ -1,5 +1,6 @@
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
+use crate::grid::block_test::script::GridRowTest;
+use crate::grid::block_test::script::RowScript::*;
+
 use flowy_grid_data_model::revision::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset};
 
 #[tokio::test]
@@ -10,7 +11,7 @@ async fn grid_create_block() {
         CreateBlock { block: block_meta_rev },
         AssertBlockCount(2),
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    GridRowTest::new().await.run_scripts(scripts).await;
 }
 
 #[tokio::test]
@@ -36,5 +37,5 @@ async fn grid_update_block() {
             block: cloned_grid_block,
         },
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    GridRowTest::new().await.run_scripts(scripts).await;
 }

+ 5 - 0
frontend/rust-lib/flowy-grid/tests/grid/block_test/mod.rs

@@ -0,0 +1,5 @@
+#![allow(clippy::module_inception)]
+mod block_test;
+mod row_test;
+mod script;
+pub mod util;

+ 17 - 17
frontend/rust-lib/flowy-grid/tests/grid/row_test.rs → frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs

@@ -1,18 +1,18 @@
-use crate::grid::field_util::*;
-use crate::grid::row_util::GridRowTestBuilder;
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
+use crate::grid::block_test::script::GridRowTest;
+use crate::grid::block_test::script::RowScript::*;
+use crate::grid::block_test::util::GridRowTestBuilder;
 use chrono::NaiveDateTime;
 use flowy_grid::entities::FieldType;
 use flowy_grid::services::cell::decode_any_cell_data;
 use flowy_grid::services::field::select_option::SELECTION_IDS_SEPARATOR;
 use flowy_grid::services::field::{DateCellData, MultiSelectTypeOption, SingleSelectTypeOption};
-use flowy_grid::services::row::CreateRowRevisionBuilder;
+
+use crate::grid::field_test::util::make_date_cell_string;
 use flowy_grid_data_model::revision::RowMetaChangeset;
 
 #[tokio::test]
 async fn grid_create_row_count_test() {
-    let test = GridEditorTest::new().await;
+    let mut test = GridRowTest::new().await;
     let scripts = vec![
         AssertRowCount(3),
         CreateEmptyRow,
@@ -22,12 +22,12 @@ async fn grid_create_row_count_test() {
         },
         AssertRowCount(6),
     ];
-    GridEditorTest::new().await.run_scripts(scripts).await;
+    test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_update_row() {
-    let mut test = GridEditorTest::new().await;
+    let mut test = GridRowTest::new().await;
     let payload = GridRowTestBuilder::new(&test).build();
     let changeset = RowMetaChangeset {
         row_id: payload.row_id.clone(),
@@ -39,14 +39,14 @@ async fn grid_update_row() {
     let scripts = vec![AssertRowCount(3), CreateRow { payload }, UpdateRow { changeset }];
     test.run_scripts(scripts).await;
 
-    let expected_row = (&*test.row_revs.last().cloned().unwrap()).clone();
+    let expected_row = test.last_row().unwrap();
     let scripts = vec![AssertRow { expected_row }, AssertRowCount(4)];
     test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_delete_row() {
-    let mut test = GridEditorTest::new().await;
+    let mut test = GridRowTest::new().await;
     let payload1 = GridRowTestBuilder::new(&test).build();
     let payload2 = GridRowTestBuilder::new(&test).build();
     let row_ids = vec![payload1.row_id.clone(), payload2.row_id.clone()];
@@ -72,9 +72,9 @@ async fn grid_delete_row() {
 
 #[tokio::test]
 async fn grid_row_add_cells_test() {
-    let mut test = GridEditorTest::new().await;
-    let mut builder = CreateRowRevisionBuilder::new(&test.field_revs);
-    for field in &test.field_revs {
+    let mut test = GridRowTest::new().await;
+    let mut builder = test.builder();
+    for field in test.field_revs() {
         let field_type: FieldType = field.field_type_rev.into();
         match field_type {
             FieldType::RichText => {
@@ -112,17 +112,17 @@ async fn grid_row_add_cells_test() {
         }
     }
     let context = builder.build();
-    let scripts = vec![CreateRow { payload: context }, AssertGridRevisionPad];
+    let scripts = vec![CreateRow { payload: context }];
     test.run_scripts(scripts).await;
 }
 
 #[tokio::test]
 async fn grid_row_add_date_cell_test() {
-    let mut test = GridEditorTest::new().await;
-    let mut builder = CreateRowRevisionBuilder::new(&test.field_revs);
+    let mut test = GridRowTest::new().await;
+    let mut builder = test.builder();
     let mut date_field = None;
     let timestamp = 1647390674;
-    for field in &test.field_revs {
+    for field in test.field_revs() {
         let field_type: FieldType = field.field_type_rev.into();
         if field_type == FieldType::DateTime {
             date_field = Some(field.clone());

+ 150 - 0
frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs

@@ -0,0 +1,150 @@
+use crate::grid::grid_editor::GridEditorTest;
+use flowy_grid::entities::RowInfo;
+use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
+use flowy_grid_data_model::revision::{
+    FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
+};
+use std::sync::Arc;
+
+pub enum RowScript {
+    CreateEmptyRow,
+    CreateRow {
+        payload: CreateRowRevisionPayload,
+    },
+    UpdateRow {
+        changeset: RowMetaChangeset,
+    },
+    AssertRow {
+        expected_row: RowRevision,
+    },
+    DeleteRows {
+        row_ids: Vec<String>,
+    },
+    AssertRowCount(usize),
+    CreateBlock {
+        block: GridBlockMetaRevision,
+    },
+    UpdateBlock {
+        changeset: GridBlockMetaRevisionChangeset,
+    },
+    AssertBlockCount(usize),
+    AssertBlock {
+        block_index: usize,
+        row_count: i32,
+        start_row_index: i32,
+    },
+    AssertBlockEqual {
+        block_index: usize,
+        block: GridBlockMetaRevision,
+    },
+}
+
+pub struct GridRowTest {
+    inner: GridEditorTest,
+}
+
+impl GridRowTest {
+    pub async fn new() -> Self {
+        let editor_test = GridEditorTest::new().await;
+        Self { inner: editor_test }
+    }
+
+    pub fn field_revs(&self) -> &Vec<Arc<FieldRevision>> {
+        &self.field_revs
+    }
+
+    pub fn last_row(&self) -> Option<RowRevision> {
+        self.row_revs.last().map(|a| a.clone().as_ref().clone())
+    }
+
+    pub async fn run_scripts(&mut self, scripts: Vec<RowScript>) {
+        for script in scripts {
+            self.run_script(script).await;
+        }
+    }
+
+    pub fn builder(&self) -> CreateRowRevisionBuilder {
+        CreateRowRevisionBuilder::new(&self.field_revs)
+    }
+
+    pub async fn run_script(&mut self, script: RowScript) {
+        match script {
+            RowScript::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_revs = self.get_row_revs().await;
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
+            }
+            RowScript::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_revs = self.get_row_revs().await;
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
+            }
+            RowScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
+            RowScript::DeleteRows { row_ids } => {
+                let row_orders = row_ids
+                    .into_iter()
+                    .map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
+                    .collect::<Vec<RowInfo>>();
+
+                self.editor.delete_rows(row_orders).await.unwrap();
+                self.row_revs = self.get_row_revs().await;
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
+            }
+            RowScript::AssertRow { expected_row } => {
+                let row = &*self
+                    .row_revs
+                    .iter()
+                    .find(|row| row.id == expected_row.id)
+                    .cloned()
+                    .unwrap();
+                assert_eq!(&expected_row, row);
+            }
+            RowScript::AssertRowCount(expected_row_count) => {
+                assert_eq!(expected_row_count, self.row_revs.len());
+            }
+            RowScript::CreateBlock { block } => {
+                self.editor.create_block(block).await.unwrap();
+                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
+            }
+            RowScript::UpdateBlock { changeset: change } => {
+                self.editor.update_block(change).await.unwrap();
+            }
+            RowScript::AssertBlockCount(count) => {
+                assert_eq!(self.editor.get_block_meta_revs().await.unwrap().len(), count);
+            }
+            RowScript::AssertBlock {
+                block_index,
+                row_count,
+                start_row_index,
+            } => {
+                assert_eq!(self.block_meta_revs[block_index].row_count, row_count);
+                assert_eq!(self.block_meta_revs[block_index].start_row_index, start_row_index);
+            }
+            RowScript::AssertBlockEqual { block_index, block } => {
+                let blocks = self.editor.get_block_meta_revs().await.unwrap();
+                let compared_block = blocks[block_index].clone();
+                assert_eq!(compared_block, Arc::new(block));
+            }
+        }
+    }
+}
+
+impl std::ops::Deref for GridRowTest {
+    type Target = GridEditorTest;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl std::ops::DerefMut for GridRowTest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}

+ 7 - 6
frontend/rust-lib/flowy-grid/tests/grid/row_util.rs → frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs

@@ -1,4 +1,5 @@
-use crate::grid::script::GridEditorTest;
+use crate::grid::block_test::script::GridRowTest;
+
 use flowy_grid::entities::FieldType;
 use flowy_grid::services::field::DateCellChangeset;
 use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
@@ -6,15 +7,15 @@ use flowy_grid_data_model::revision::FieldRevision;
 use strum::EnumCount;
 
 pub struct GridRowTestBuilder<'a> {
-    test: &'a GridEditorTest,
+    test: &'a GridRowTest,
     inner_builder: CreateRowRevisionBuilder<'a>,
 }
 
 impl<'a> GridRowTestBuilder<'a> {
-    pub fn new(test: &'a GridEditorTest) -> Self {
-        assert_eq!(test.field_revs.len(), FieldType::COUNT);
+    pub fn new(test: &'a GridRowTest) -> Self {
+        assert_eq!(test.field_revs().len(), FieldType::COUNT);
 
-        let inner_builder = CreateRowRevisionBuilder::new(&test.field_revs);
+        let inner_builder = CreateRowRevisionBuilder::new(test.field_revs());
         Self { test, inner_builder }
     }
     #[allow(dead_code)]
@@ -59,7 +60,7 @@ impl<'a> GridRowTestBuilder<'a> {
 
     pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision {
         self.test
-            .field_revs
+            .field_revs()
             .iter()
             .find(|field_rev| {
                 let t_field_type: FieldType = field_rev.field_type_rev.into();

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

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

+ 61 - 0
frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs

@@ -0,0 +1,61 @@
+use crate::grid::grid_editor::GridEditorTest;
+use flowy_grid::entities::CellChangeset;
+
+pub enum CellScript {
+    UpdateCell { changeset: CellChangeset, is_err: bool },
+}
+
+pub struct GridCellTest {
+    inner: GridEditorTest,
+}
+
+impl GridCellTest {
+    pub async fn new() -> Self {
+        let inner = GridEditorTest::new().await;
+        Self { inner }
+    }
+
+    pub async fn run_scripts(&mut self, scripts: Vec<CellScript>) {
+        for script in scripts {
+            self.run_script(script).await;
+        }
+    }
+
+    pub async fn run_script(&mut self, script: CellScript) {
+        // let grid_manager = self.sdk.grid_manager.clone();
+        // let pool = self.sdk.user_session.db_pool().unwrap();
+        let rev_manager = self.editor.rev_manager();
+        let _cache = rev_manager.revision_cache().await;
+
+        match script {
+            CellScript::UpdateCell { changeset, is_err } => {
+                let result = self.editor.update_cell(changeset).await;
+                if is_err {
+                    assert!(result.is_err())
+                } else {
+                    let _ = result.unwrap();
+                    self.row_revs = self.get_row_revs().await;
+                }
+            } // CellScript::AssertGridRevisionPad => {
+              //     sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
+              //     let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap();
+              //     let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
+              //     println!("{}", grid_pad.delta_str());
+              // }
+        }
+    }
+}
+
+impl std::ops::Deref for GridCellTest {
+    type Target = GridEditorTest;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl std::ops::DerefMut for GridCellTest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}

+ 4 - 4
frontend/rust-lib/flowy-grid/tests/grid/cell_test.rs → frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs

@@ -1,13 +1,13 @@
-use crate::grid::field_util::make_date_cell_string;
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
+use crate::grid::cell_test::script::CellScript::*;
+use crate::grid::cell_test::script::GridCellTest;
+use crate::grid::field_test::util::make_date_cell_string;
 use flowy_grid::entities::{CellChangeset, FieldType};
 use flowy_grid::services::field::select_option::SelectOptionCellChangeset;
 use flowy_grid::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
 
 #[tokio::test]
 async fn grid_cell_update() {
-    let mut test = GridEditorTest::new().await;
+    let mut test = GridCellTest::new().await;
     let field_revs = &test.field_revs;
     let row_revs = &test.row_revs;
     let grid_blocks = &test.block_meta_revs;

+ 3 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_test/mod.rs

@@ -0,0 +1,3 @@
+mod script;
+mod test;
+pub mod util;

+ 94 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs

@@ -0,0 +1,94 @@
+use crate::grid::grid_editor::GridEditorTest;
+use flowy_grid::entities::InsertFieldParams;
+use flowy_grid_data_model::revision::FieldRevision;
+use flowy_sync::entities::grid::FieldChangesetParams;
+
+pub enum FieldScript {
+    CreateField {
+        params: InsertFieldParams,
+    },
+    UpdateField {
+        changeset: FieldChangesetParams,
+    },
+    DeleteField {
+        field_rev: FieldRevision,
+    },
+    AssertFieldCount(usize),
+    AssertFieldEqual {
+        field_index: usize,
+        field_rev: FieldRevision,
+    },
+}
+
+pub struct GridFieldTest {
+    inner: GridEditorTest,
+}
+
+impl GridFieldTest {
+    pub async fn new() -> Self {
+        let editor_test = GridEditorTest::new().await;
+        Self { inner: editor_test }
+    }
+
+    pub fn grid_id(&self) -> String {
+        self.grid_id.clone()
+    }
+
+    pub fn field_count(&self) -> usize {
+        self.field_count
+    }
+
+    pub async fn run_scripts(&mut self, scripts: Vec<FieldScript>) {
+        for script in scripts {
+            self.run_script(script).await;
+        }
+    }
+
+    pub async fn run_script(&mut self, script: FieldScript) {
+        match script {
+            FieldScript::CreateField { params } => {
+                if !self.editor.contain_field(&params.field.id).await {
+                    self.field_count += 1;
+                }
+
+                self.editor.insert_field(params).await.unwrap();
+                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
+                assert_eq!(self.field_count, self.field_revs.len());
+            }
+            FieldScript::UpdateField { changeset: change } => {
+                self.editor.update_field(change).await.unwrap();
+                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
+            }
+            FieldScript::DeleteField { field_rev } => {
+                if self.editor.contain_field(&field_rev.id).await {
+                    self.field_count -= 1;
+                }
+
+                self.editor.delete_field(&field_rev.id).await.unwrap();
+                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
+                assert_eq!(self.field_count, self.field_revs.len());
+            }
+            FieldScript::AssertFieldCount(count) => {
+                assert_eq!(self.editor.get_field_revs(None).await.unwrap().len(), count);
+            }
+            FieldScript::AssertFieldEqual { field_index, field_rev } => {
+                let field_revs = self.editor.get_field_revs(None).await.unwrap();
+                assert_eq!(field_revs[field_index].as_ref(), &field_rev);
+            }
+        }
+    }
+}
+
+impl std::ops::Deref for GridFieldTest {
+    type Target = GridEditorTest;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl std::ops::DerefMut for GridFieldTest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}

+ 22 - 22
frontend/rust-lib/flowy-grid/tests/grid/field_test.rs → frontend/rust-lib/flowy-grid/tests/grid/field_test/test.rs

@@ -1,6 +1,6 @@
-use crate::grid::field_util::*;
-use crate::grid::script::EditorScript::*;
-use crate::grid::script::*;
+use crate::grid::field_test::script::FieldScript::*;
+use crate::grid::field_test::script::GridFieldTest;
+use crate::grid::field_test::util::*;
 use flowy_grid::services::field::select_option::SelectOption;
 use flowy_grid::services::field::SingleSelectTypeOption;
 use flowy_grid_data_model::revision::TypeOptionDataEntry;
@@ -8,23 +8,23 @@ use flowy_sync::entities::grid::FieldChangesetParams;
 
 #[tokio::test]
 async fn grid_create_field() {
-    let mut test = GridEditorTest::new().await;
-    let (params, field_rev) = create_text_field(&test.grid_id);
+    let mut test = GridFieldTest::new().await;
+    let (params, field_rev) = create_text_field(&test.grid_id());
 
     let scripts = vec![
         CreateField { params },
         AssertFieldEqual {
-            field_index: test.field_count,
+            field_index: test.field_count(),
             field_rev,
         },
     ];
     test.run_scripts(scripts).await;
 
-    let (params, field_rev) = create_single_select_field(&test.grid_id);
+    let (params, field_rev) = create_single_select_field(&test.grid_id());
     let scripts = vec![
         CreateField { params },
         AssertFieldEqual {
-            field_index: test.field_count,
+            field_index: test.field_count(),
             field_rev,
         },
     ];
@@ -33,9 +33,9 @@ async fn grid_create_field() {
 
 #[tokio::test]
 async fn grid_create_duplicate_field() {
-    let mut test = GridEditorTest::new().await;
-    let (params, _) = create_text_field(&test.grid_id);
-    let field_count = test.field_count;
+    let mut test = GridFieldTest::new().await;
+    let (params, _) = create_text_field(&test.grid_id());
+    let field_count = test.field_count();
     let expected_field_count = field_count + 1;
     let scripts = vec![
         CreateField { params: params.clone() },
@@ -47,11 +47,11 @@ async fn grid_create_duplicate_field() {
 
 #[tokio::test]
 async fn grid_update_field_with_empty_change() {
-    let mut test = GridEditorTest::new().await;
-    let (params, field_rev) = create_single_select_field(&test.grid_id);
+    let mut test = GridFieldTest::new().await;
+    let (params, field_rev) = create_single_select_field(&test.grid_id());
     let changeset = FieldChangesetParams {
         field_id: field_rev.id.clone(),
-        grid_id: test.grid_id.clone(),
+        grid_id: test.grid_id(),
         ..Default::default()
     };
 
@@ -59,7 +59,7 @@ async fn grid_update_field_with_empty_change() {
         CreateField { params },
         UpdateField { changeset },
         AssertFieldEqual {
-            field_index: test.field_count,
+            field_index: test.field_count(),
             field_rev,
         },
     ];
@@ -68,14 +68,14 @@ async fn grid_update_field_with_empty_change() {
 
 #[tokio::test]
 async fn grid_update_field() {
-    let mut test = GridEditorTest::new().await;
-    let (params, single_select_field) = create_single_select_field(&test.grid_id);
+    let mut test = GridFieldTest::new().await;
+    let (params, single_select_field) = create_single_select_field(&test.grid_id());
 
     let mut single_select_type_option = SingleSelectTypeOption::from(&single_select_field);
     single_select_type_option.options.push(SelectOption::new("Unknown"));
     let changeset = FieldChangesetParams {
         field_id: single_select_field.id.clone(),
-        grid_id: test.grid_id.clone(),
+        grid_id: test.grid_id(),
         frozen: Some(true),
         width: Some(1000),
         type_option_data: Some(single_select_type_option.protobuf_bytes().to_vec()),
@@ -92,7 +92,7 @@ async fn grid_update_field() {
         CreateField { params },
         UpdateField { changeset },
         AssertFieldEqual {
-            field_index: test.field_count,
+            field_index: test.field_count(),
             field_rev: expected_field_rev,
         },
     ];
@@ -101,9 +101,9 @@ async fn grid_update_field() {
 
 #[tokio::test]
 async fn grid_delete_field() {
-    let mut test = GridEditorTest::new().await;
-    let original_field_count = test.field_count;
-    let (params, text_field_rev) = create_text_field(&test.grid_id);
+    let mut test = GridFieldTest::new().await;
+    let original_field_count = test.field_count();
+    let (params, text_field_rev) = create_text_field(&test.grid_id());
     let scripts = vec![
         CreateField { params },
         DeleteField {

+ 0 - 0
frontend/rust-lib/flowy-grid/tests/grid/field_util.rs → frontend/rust-lib/flowy-grid/tests/grid/field_test/util.rs


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

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

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

@@ -0,0 +1,97 @@
+#![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::grid_editor::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 {
+    inner: GridEditorTest,
+}
+
+impl GridFilterTest {
+    pub async fn new() -> Self {
+     let editor_test =  GridEditorTest::new().await;
+        Self {
+            inner: 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.inner
+    }
+}
+
+impl std::ops::DerefMut for GridFilterTest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.inner
+    }
+}

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

+ 164 - 0
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -0,0 +1,164 @@
+#![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_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;
+use std::sync::Arc;
+use std::time::Duration;
+use strum::EnumCount;
+use tokio::time::sleep;
+
+pub struct GridEditorTest {
+    pub sdk: FlowySDKTest,
+    pub grid_id: String,
+    pub editor: Arc<GridRevisionEditor>,
+    pub field_revs: Vec<Arc<FieldRevision>>,
+    pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
+    pub row_revs: Vec<Arc<RowRevision>>,
+    pub field_count: usize,
+    pub row_order_by_row_id: HashMap<String, RowInfo>,
+}
+
+impl GridEditorTest {
+    pub async fn new() -> Self {
+        let sdk = FlowySDKTest::default();
+        let _ = sdk.init_user().await;
+        let build_context = make_all_field_test_grid();
+        let view_data: Bytes = build_context.into();
+        let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
+        let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
+        let field_revs = editor.get_field_revs(None).await.unwrap();
+        let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
+        let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs;
+        assert_eq!(row_revs.len(), 3);
+        assert_eq!(block_meta_revs.len(), 1);
+
+        // It seems like you should add the field in the make_test_grid() function.
+        // Because we assert the initialize count of the fields is equal to FieldType::COUNT.
+        assert_eq!(field_revs.len(), FieldType::COUNT);
+
+        let grid_id = test.view.id;
+        Self {
+            sdk,
+            grid_id,
+            editor,
+            field_revs,
+            block_meta_revs,
+            row_revs,
+            field_count: FieldType::COUNT,
+            row_order_by_row_id: HashMap::default(),
+        }
+    }
+
+    pub(crate) async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
+        self.editor
+            .grid_block_snapshots(None)
+            .await
+            .unwrap()
+            .pop()
+            .unwrap()
+            .row_revs
+    }
+
+    pub async fn grid_filters(&self) -> Vec<GridFilter> {
+        let layout_type = GridLayoutType::Table;
+        self.editor.get_grid_filter(&layout_type).await.unwrap()
+    }
+
+    pub fn text_field(&self) -> &FieldRevision {
+        self.field_revs
+            .iter()
+            .filter(|field_rev| {
+                let t_field_type: FieldType = field_rev.field_type_rev.into();
+                t_field_type == FieldType::RichText
+            })
+            .collect::<Vec<_>>()
+            .pop()
+            .unwrap()
+    }
+}
+
+fn make_all_field_test_grid() -> BuildGridContext {
+    let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
+        .name("Name")
+        .visibility(true)
+        .build();
+
+    // Single Select
+    let single_select = SingleSelectTypeOptionBuilder::default()
+        .option(SelectOption::new("Live"))
+        .option(SelectOption::new("Completed"))
+        .option(SelectOption::new("Planned"))
+        .option(SelectOption::new("Paused"));
+    let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
+
+    // MultiSelect
+    let multi_select = MultiSelectTypeOptionBuilder::default()
+        .option(SelectOption::new("Google"))
+        .option(SelectOption::new("Facebook"))
+        .option(SelectOption::new("Twitter"));
+    let multi_select_field = FieldBuilder::new(multi_select)
+        .name("Platform")
+        .visibility(true)
+        .build();
+
+    // Number
+    let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
+    let number_field = FieldBuilder::new(number).name("Price").visibility(true).build();
+
+    // Date
+    let date = DateTypeOptionBuilder::default()
+        .date_format(DateFormat::US)
+        .time_format(TimeFormat::TwentyFourHour);
+    let date_field = FieldBuilder::new(date).name("Time").visibility(true).build();
+
+    // Checkbox
+    let checkbox = CheckboxTypeOptionBuilder::default();
+    let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build();
+
+    // URL
+    let url = URLTypeOptionBuilder::default();
+    let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
+
+    // for i in 0..3 {
+    //     for field_type in FieldType::iter() {
+    //         let field_type: FieldType = field_type;
+    //         match field_type {
+    //             FieldType::RichText => {}
+    //             FieldType::Number => {}
+    //             FieldType::DateTime => {}
+    //             FieldType::SingleSelect => {}
+    //             FieldType::MultiSelect => {}
+    //             FieldType::Checkbox => {}
+    //             FieldType::URL => {}
+    //         }
+    //     }
+    // }
+
+    GridBuilder::default()
+        .add_field(text_field)
+        .add_field(single_select_field)
+        .add_field(multi_select_field)
+        .add_field(number_field)
+        .add_field(date_field)
+        .add_field(checkbox_field)
+        .add_field(url_field)
+        .add_empty_row()
+        .add_empty_row()
+        .add_empty_row()
+        .build()
+}

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

@@ -1,8 +1,5 @@
 mod block_test;
 mod cell_test;
 mod field_test;
-mod field_util;
-// mod filter_test;
-mod row_test;
-mod row_util;
-mod script;
+mod filter_test;
+mod grid_editor;

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

@@ -1,376 +0,0 @@
-#![cfg_attr(rustfmt, rustfmt::skip)]
-#![allow(clippy::all)]
-#![allow(dead_code)]
-#![allow(unused_imports)]
-use bytes::Bytes;
-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_test::helper::ViewTest;
-use flowy_test::FlowySDKTest;
-use std::collections::HashMap;
-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 {
-        params: InsertFieldParams,
-    },
-    UpdateField {
-        changeset: FieldChangesetParams,
-    },
-    DeleteField {
-        field_rev: FieldRevision,
-    },
-    AssertFieldCount(usize),
-    AssertFieldEqual {
-        field_index: usize,
-        field_rev: FieldRevision,
-    },
-    CreateBlock {
-        block: GridBlockMetaRevision,
-    },
-    UpdateBlock {
-        changeset: GridBlockMetaRevisionChangeset,
-    },
-    AssertBlockCount(usize),
-    AssertBlock {
-        block_index: usize,
-        row_count: i32,
-        start_row_index: i32,
-    },
-    AssertBlockEqual {
-        block_index: usize,
-        block: GridBlockMetaRevision,
-    },
-    CreateEmptyRow,
-    CreateRow {
-        payload: CreateRowRevisionPayload,
-    },
-    UpdateRow {
-        changeset: RowMetaChangeset,
-    },
-    AssertRow {
-        expected_row: RowRevision,
-    },
-    DeleteRows {
-        row_ids: Vec<String>,
-    },
-    UpdateCell {
-        changeset: CellChangeset,
-        is_err: bool,
-    },
-    AssertRowCount(usize),
-    #[allow(dead_code)]
-    UpdateGridSetting {
-        params: GridSettingChangesetParams,
-    },
-    InsertGridTableFilter {
-        payload: CreateGridFilterPayload,
-    },
-    AssertTableFilterCount {
-        count: i32,
-    },
-    DeleteGridTableFilter {
-        filter_id: String,
-        field_type: FieldType,
-    },
-    #[allow(dead_code)]
-    AssertGridSetting {
-        expected_setting: GridSetting,
-    },
-    AssertGridRevisionPad,
-}
-
-pub struct GridEditorTest {
-    pub sdk: FlowySDKTest,
-    pub grid_id: String,
-    pub editor: Arc<GridRevisionEditor>,
-    pub field_revs: Vec<Arc<FieldRevision>>,
-    pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
-    pub row_revs: Vec<Arc<RowRevision>>,
-    pub field_count: usize,
-
-    pub row_order_by_row_id: HashMap<String, RowInfo>,
-}
-
-impl GridEditorTest {
-    pub async fn new() -> Self {
-        let sdk = FlowySDKTest::default();
-        let _ = sdk.init_user().await;
-        let build_context = make_all_field_test_grid();
-        let view_data: Bytes = build_context.into();
-        let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
-        let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
-        let field_revs = editor.get_field_revs(None).await.unwrap();
-        let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
-        let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs;
-        assert_eq!(row_revs.len(), 3);
-        assert_eq!(block_meta_revs.len(), 1);
-
-        // It seems like you should add the field in the make_test_grid() function.
-        // Because we assert the initialize count of the fields is equal to FieldType::COUNT.
-        assert_eq!(field_revs.len(), FieldType::COUNT);
-
-        let grid_id = test.view.id;
-        Self {
-            sdk,
-            grid_id,
-            editor,
-            field_revs,
-            block_meta_revs,
-            row_revs,
-            field_count: FieldType::COUNT,
-            row_order_by_row_id: HashMap::default(),
-        }
-    }
-
-    pub async fn run_scripts(&mut self, scripts: Vec<EditorScript>) {
-        for script in scripts {
-            self.run_script(script).await;
-        }
-    }
-
-    pub async fn run_script(&mut self, script: EditorScript) {
-        let grid_manager = self.sdk.grid_manager.clone();
-        let pool = self.sdk.user_session.db_pool().unwrap();
-        let rev_manager = self.editor.rev_manager();
-        let _cache = rev_manager.revision_cache().await;
-
-        match script {
-            EditorScript::CreateField { params } => {
-                if !self.editor.contain_field(&params.field.id).await {
-                    self.field_count += 1;
-                }
-
-                self.editor.insert_field(params).await.unwrap();
-                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
-                assert_eq!(self.field_count, self.field_revs.len());
-            }
-            EditorScript::UpdateField { changeset: change } => {
-                self.editor.update_field(change).await.unwrap();
-                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
-            }
-            EditorScript::DeleteField { field_rev } => {
-                if self.editor.contain_field(&field_rev.id).await {
-                    self.field_count -= 1;
-                }
-
-                self.editor.delete_field(&field_rev.id).await.unwrap();
-                self.field_revs = self.editor.get_field_revs(None).await.unwrap();
-                assert_eq!(self.field_count, self.field_revs.len());
-            }
-            EditorScript::AssertFieldCount(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();
-                assert_eq!(field_revs[field_index].as_ref(), &field_rev);
-            }
-            EditorScript::CreateBlock { block } => {
-                self.editor.create_block(block).await.unwrap();
-                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
-            }
-            EditorScript::UpdateBlock { changeset: change } => {
-                self.editor.update_block(change).await.unwrap();
-            }
-            EditorScript::AssertBlockCount(count) => {
-                assert_eq!(self.editor.get_block_meta_revs().await.unwrap().len(), count);
-            }
-            EditorScript::AssertBlock {
-                block_index,
-                row_count,
-                start_row_index,
-            } => {
-                assert_eq!(self.block_meta_revs[block_index].row_count, row_count);
-                assert_eq!(self.block_meta_revs[block_index].start_row_index, start_row_index);
-            }
-            EditorScript::AssertBlockEqual { block_index, block } => {
-                let blocks = self.editor.get_block_meta_revs().await.unwrap();
-                let compared_block = blocks[block_index].clone();
-                assert_eq!(compared_block, Arc::new(block));
-            }
-            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_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_revs = self.get_row_revs().await;
-                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
-            }
-            EditorScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
-            EditorScript::DeleteRows { row_ids } => {
-                let row_orders = row_ids
-                    .into_iter()
-                    .map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
-                    .collect::<Vec<RowInfo>>();
-
-                self.editor.delete_rows(row_orders).await.unwrap();
-                self.row_revs = self.get_row_revs().await;
-                self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
-            }
-            EditorScript::AssertRow { expected_row } => {
-                let row = &*self
-                    .row_revs
-                    .iter()
-                    .find(|row| row.id == expected_row.id)
-                    .cloned()
-                    .unwrap();
-                assert_eq!(&expected_row, row);
-                // if let Some(visibility) = changeset.visibility {
-                //     assert_eq!(row.visibility, visibility);
-                // }
-                //
-                // if let Some(height) = changeset.height {
-                //     assert_eq!(row.height, height);
-                // }
-            }
-            EditorScript::UpdateCell { changeset, is_err } => {
-                let result = self.editor.update_cell(changeset).await;
-                if is_err {
-                    assert!(result.is_err())
-                } else {
-                    let _ = result.unwrap();
-                    self.row_revs = self.get_row_revs().await;
-                }
-            }
-            EditorScript::AssertRowCount(expected_row_count) => {
-                assert_eq!(expected_row_count, self.row_revs.len());
-            }
-            EditorScript::UpdateGridSetting { params } => {
-                let _ = self.editor.update_grid_setting(params).await.unwrap();
-            }
-            EditorScript::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();
-            }
-            EditorScript::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());
-            }
-            EditorScript::DeleteGridTableFilter { filter_id ,field_type} => {
-                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() })
-                    .build();
-                let _ = self.editor.update_grid_setting(params).await.unwrap();
-            }
-            EditorScript::AssertGridSetting { expected_setting } => {
-                let setting = self.editor.get_grid_setting().await.unwrap();
-                assert_eq!(expected_setting, setting);
-            }
-            EditorScript::AssertGridRevisionPad => {
-                sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
-                let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap();
-                let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
-                println!("{}", grid_pad.delta_str());
-            }
-        }
-    }
-
-    async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
-        self.editor
-            .grid_block_snapshots(None)
-            .await
-            .unwrap()
-            .pop()
-            .unwrap()
-            .row_revs
-    }
-
-    pub async fn grid_filters(&self) -> Vec<GridFilter> {
-        let layout_type = GridLayoutType::Table;
-        self.editor.get_grid_filter(&layout_type).await.unwrap()
-    }
-
-    pub fn text_field(&self) -> &FieldRevision {
-        self.field_revs
-            .iter()
-            .filter(|field_rev| {
-                let t_field_type: FieldType = field_rev.field_type_rev.into();
-                t_field_type == FieldType::RichText
-            })
-            .collect::<Vec<_>>()
-            .pop()
-            .unwrap()
-    }
-}
-
-fn make_all_field_test_grid() -> BuildGridContext {
-    let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
-        .name("Name")
-        .visibility(true)
-        .build();
-
-    // Single Select
-    let single_select = SingleSelectTypeOptionBuilder::default()
-        .option(SelectOption::new("Live"))
-        .option(SelectOption::new("Completed"))
-        .option(SelectOption::new("Planned"))
-        .option(SelectOption::new("Paused"));
-    let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
-
-    // MultiSelect
-    let multi_select = MultiSelectTypeOptionBuilder::default()
-        .option(SelectOption::new("Google"))
-        .option(SelectOption::new("Facebook"))
-        .option(SelectOption::new("Twitter"));
-    let multi_select_field = FieldBuilder::new(multi_select)
-        .name("Platform")
-        .visibility(true)
-        .build();
-
-    // Number
-    let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
-    let number_field = FieldBuilder::new(number).name("Price").visibility(true).build();
-
-    // Date
-    let date = DateTypeOptionBuilder::default()
-        .date_format(DateFormat::US)
-        .time_format(TimeFormat::TwentyFourHour);
-    let date_field = FieldBuilder::new(date).name("Time").visibility(true).build();
-
-    // Checkbox
-    let checkbox = CheckboxTypeOptionBuilder::default();
-    let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build();
-
-    // URL
-    let url = URLTypeOptionBuilder::default();
-    let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
-
-    GridBuilder::default()
-        .add_field(text_field)
-        .add_field(single_select_field)
-        .add_field(multi_select_field)
-        .add_field(number_field)
-        .add_field(date_field)
-        .add_field(checkbox_field)
-        .add_field(url_field)
-        .add_empty_row()
-        .add_empty_row()
-        .add_empty_row()
-        .build()
-}

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