Browse Source

Feat/sort after change (#1607)

* chore: generate task after row was changed

* chore: config task

* chore: add task test

Co-authored-by: nathan <[email protected]>
Nathan.fooo 2 years ago
parent
commit
f5b7d3951f
41 changed files with 967 additions and 572 deletions
  1. 2 0
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  2. 49 1
      frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs
  3. 35 19
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  4. 7 2
      frontend/rust-lib/flowy-grid/src/services/block_editor.rs
  5. 23 12
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  6. 0 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_filter.rs
  7. 8 6
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  8. 13 26
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs
  9. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/type_option.rs
  10. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/type_option_cell.rs
  11. 4 8
      frontend/rust-lib/flowy-grid/src/services/filter/controller.rs
  12. 5 1
      frontend/rust-lib/flowy-grid/src/services/filter/task.rs
  13. 34 36
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  14. 1 1
      frontend/rust-lib/flowy-grid/src/services/persistence/rev_sqlite/grid_snapshot_sqlite_impl.rs
  15. 141 40
      frontend/rust-lib/flowy-grid/src/services/sort/controller.rs
  16. 19 1
      frontend/rust-lib/flowy-grid/src/services/sort/entities.rs
  17. 17 2
      frontend/rust-lib/flowy-grid/src/services/sort/task.rs
  18. 23 3
      frontend/rust-lib/flowy-grid/src/services/view_editor/changed_notifier.rs
  19. 27 20
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs
  20. 2 13
      frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs
  21. 12 1
      frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs
  22. 8 8
      frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs
  23. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  24. 6 4
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs
  25. 7 3
      frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs
  26. 2 2
      frontend/rust-lib/flowy-grid/tests/grid/field_test/script.rs
  27. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs
  28. 2 2
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/date_filter_test.rs
  29. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/number_filter_test.rs
  30. 12 42
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs
  31. 4 3
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/select_option_filter_test.rs
  32. 9 6
      frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs
  33. 63 6
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
  34. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs
  35. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/snapshot_test/script.rs
  36. 2 1
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/mod.rs
  37. 46 0
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/multi_sort_test.rs
  38. 103 11
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/script.rs
  39. 257 0
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/single_sort_test.rs
  40. 0 279
      frontend/rust-lib/flowy-grid/tests/grid/sort_test/sort_test.rs
  41. 14 2
      frontend/rust-lib/flowy-task/src/scheduler.rs

+ 2 - 0
frontend/rust-lib/flowy-grid/src/dart_notification.rs

@@ -17,6 +17,8 @@ pub enum GridDartNotification {
     DidGroupByNewField = 62,
     DidUpdateFilter = 63,
     DidUpdateSort = 64,
+    DidReorderRows = 65,
+    DidReorderSingleRow = 66,
     DidUpdateGridSetting = 70,
 }
 

+ 49 - 1
frontend/rust-lib/flowy-grid/src/entities/sort_entities.rs

@@ -4,7 +4,7 @@ use crate::services::sort::SortType;
 
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
-use grid_rev_model::FieldTypeRevision;
+use grid_rev_model::{FieldTypeRevision, SortCondition, SortRevision};
 
 #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
 pub struct GridSortPB {
@@ -21,6 +21,17 @@ pub struct GridSortPB {
     pub condition: GridSortConditionPB,
 }
 
+impl std::convert::From<&SortRevision> for GridSortPB {
+    fn from(sort_rev: &SortRevision) -> Self {
+        Self {
+            id: sort_rev.id.clone(),
+            field_id: sort_rev.field_id.clone(),
+            field_type: sort_rev.field_type.into(),
+            condition: sort_rev.condition.clone().into(),
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum GridSortConditionPB {
@@ -32,6 +43,16 @@ impl std::default::Default for GridSortConditionPB {
         Self::Ascending
     }
 }
+
+impl std::convert::From<SortCondition> for GridSortConditionPB {
+    fn from(condition: SortCondition) -> Self {
+        match condition {
+            SortCondition::Ascending => GridSortConditionPB::Ascending,
+            SortCondition::Descending => GridSortConditionPB::Descending,
+        }
+    }
+}
+
 #[derive(ProtoBuf, Debug, Default, Clone)]
 pub struct AlterSortPayloadPB {
     #[pb(index = 1)]
@@ -151,3 +172,30 @@ pub struct SortChangesetNotificationPB {
     #[pb(index = 4)]
     pub update_sorts: Vec<GridSortPB>,
 }
+
+impl SortChangesetNotificationPB {
+    pub fn extend(&mut self, other: SortChangesetNotificationPB) {
+        self.insert_sorts.extend(other.insert_sorts);
+        self.delete_sorts.extend(other.delete_sorts);
+        self.update_sorts.extend(other.update_sorts);
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.insert_sorts.is_empty() && self.delete_sorts.is_empty() && self.update_sorts.is_empty()
+    }
+}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct ReorderAllRowsPB {
+    #[pb(index = 1)]
+    pub row_orders: Vec<String>,
+}
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct ReorderSingleRowPB {
+    #[pb(index = 1)]
+    pub old_index: i32,
+
+    #[pb(index = 2)]
+    pub new_index: i32,
+}

+ 35 - 19
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -1,6 +1,6 @@
 use crate::entities::*;
 use crate::manager::GridManager;
-use crate::services::cell::{FromCellString, TypeCellData};
+use crate::services::cell::{FromCellString, ToCellChangesetString, TypeCellData};
 use crate::services::field::{
     default_type_option_builder_from_type, select_type_option_from_field_rev, type_option_builder_from_json_str,
     DateCellChangeset, DateChangesetPB, SelectOptionCellChangeset, SelectOptionCellChangesetPB,
@@ -317,7 +317,9 @@ pub(crate) async fn update_cell_handler(
 ) -> Result<(), FlowyError> {
     let changeset: CellChangesetPB = data.into_inner();
     let editor = manager.get_grid_editor(&changeset.grid_id).await?;
-    let _ = editor.update_cell_with_changeset(changeset).await?;
+    let _ = editor
+        .update_cell_with_changeset(&changeset.row_id, &changeset.field_id, changeset.type_cell_data)
+        .await?;
     Ok(())
 }
 
@@ -344,16 +346,17 @@ pub(crate) async fn update_select_option_handler(
     manager: AFPluginState<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
     let changeset: SelectOptionChangeset = data.into_inner().try_into()?;
-    let editor = manager.get_grid_editor(&changeset.cell_identifier.view_id).await?;
-
+    let editor = manager.get_grid_editor(&changeset.cell_path.view_id).await?;
+    let field_id = changeset.cell_path.field_id.clone();
     let _ = editor
-        .modify_field_rev(&changeset.cell_identifier.field_id, |field_rev| {
+        .modify_field_rev(&field_id, |field_rev| {
             let mut type_option = select_type_option_from_field_rev(field_rev)?;
-            let mut cell_content_changeset = None;
+            let mut cell_changeset_str = None;
             let mut is_changed = None;
 
             for option in changeset.insert_options {
-                cell_content_changeset = Some(SelectOptionCellChangeset::from_insert_option_id(&option.id).to_str());
+                cell_changeset_str =
+                    Some(SelectOptionCellChangeset::from_insert_option_id(&option.id).to_cell_changeset_str());
                 type_option.insert_option(option);
                 is_changed = Some(());
             }
@@ -364,7 +367,8 @@ pub(crate) async fn update_select_option_handler(
             }
 
             for option in changeset.delete_options {
-                cell_content_changeset = Some(SelectOptionCellChangeset::from_delete_option_id(&option.id).to_str());
+                cell_changeset_str =
+                    Some(SelectOptionCellChangeset::from_delete_option_id(&option.id).to_cell_changeset_str());
                 type_option.delete_option(option);
                 is_changed = Some(());
             }
@@ -373,16 +377,17 @@ pub(crate) async fn update_select_option_handler(
                 field_rev.insert_type_option(&*type_option);
             }
 
-            if let Some(cell_content_changeset) = cell_content_changeset {
-                let changeset = CellChangesetPB {
-                    grid_id: changeset.cell_identifier.view_id,
-                    row_id: changeset.cell_identifier.row_id,
-                    field_id: changeset.cell_identifier.field_id.clone(),
-                    type_cell_data: cell_content_changeset,
-                };
+            if let Some(cell_changeset_str) = cell_changeset_str {
                 let cloned_editor = editor.clone();
                 tokio::spawn(async move {
-                    match cloned_editor.update_cell_with_changeset(changeset).await {
+                    match cloned_editor
+                        .update_cell_with_changeset(
+                            &changeset.cell_path.row_id,
+                            &changeset.cell_path.field_id,
+                            cell_changeset_str,
+                        )
+                        .await
+                    {
                         Ok(_) => {}
                         Err(e) => tracing::error!("{}", e),
                     }
@@ -432,7 +437,18 @@ pub(crate) async fn update_select_option_cell_handler(
 ) -> Result<(), FlowyError> {
     let params: SelectOptionCellChangesetParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.cell_identifier.view_id).await?;
-    let _ = editor.update_cell_with_changeset(params.into()).await?;
+    let changeset = SelectOptionCellChangeset {
+        insert_option_ids: params.insert_option_ids,
+        delete_option_ids: params.delete_option_ids,
+    };
+
+    let _ = editor
+        .update_cell_with_changeset(
+            &params.cell_identifier.row_id,
+            &params.cell_identifier.field_id,
+            changeset,
+        )
+        .await?;
     Ok(())
 }
 
@@ -443,7 +459,7 @@ pub(crate) async fn update_date_cell_handler(
 ) -> Result<(), FlowyError> {
     let data = data.into_inner();
     let cell_path: CellPathParams = data.cell_path.try_into()?;
-    let content = DateCellChangeset {
+    let cell_changeset = DateCellChangeset {
         date: data.date,
         time: data.time,
         is_utc: data.is_utc,
@@ -451,7 +467,7 @@ pub(crate) async fn update_date_cell_handler(
 
     let editor = manager.get_grid_editor(&cell_path.view_id).await?;
     let _ = editor
-        .update_cell(cell_path.view_id, cell_path.row_id, cell_path.field_id, content)
+        .update_cell(cell_path.row_id, cell_path.field_id, cell_changeset)
         .await?;
     Ok(())
 }

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

@@ -118,8 +118,13 @@ impl GridBlockRevisionEditor {
     }
 
     pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<(usize, Arc<RowRevision>)>> {
-        let row_rev = self.pad.read().await.get_row_rev(row_id);
-        Ok(row_rev)
+        if self.pad.try_read().is_err() {
+            tracing::error!("Required GridBlockRevisionPad's read lock failed");
+            Ok(None)
+        } else {
+            let row_rev = self.pad.read().await.get_row_rev(row_id);
+            Ok(row_rev)
+        }
     }
 
     pub async fn get_row_revs<T>(&self, row_ids: Option<Vec<Cow<'_, T>>>) -> FlowyResult<Vec<Arc<RowRevision>>>

+ 23 - 12
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -34,7 +34,7 @@ pub trait CellDataDecoder: TypeOption {
 
 pub trait CellDataChangeset: TypeOption {
     /// The changeset is able to parse into the concrete data struct if `TypeOption::CellChangeset`
-    /// implements the `FromCellChangeset` trait.
+    /// implements the `FromCellChangesetString` trait.
     /// For example,the SelectOptionCellChangeset,DateCellChangeset. etc.
     ///  
     fn apply_changeset(
@@ -50,14 +50,14 @@ pub trait CellDataChangeset: TypeOption {
 ///         FieldType::SingleSelect => SelectOptionChangeset
 ///
 /// cell_rev: It will be None if the cell does not contain any data.
-pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
+pub fn apply_cell_data_changeset<C: ToCellChangesetString, T: AsRef<FieldRevision>>(
     changeset: C,
     cell_rev: Option<CellRevision>,
     field_rev: T,
     cell_data_cache: Option<AtomicCellDataCache>,
 ) -> Result<String, FlowyError> {
     let field_rev = field_rev.as_ref();
-    let changeset = changeset.to_string();
+    let changeset = changeset.to_cell_changeset_str();
     let field_type: FieldType = field_rev.ty.into();
 
     let type_cell_data = cell_rev.and_then(|cell_rev| match TypeCellData::try_from(cell_rev) {
@@ -108,7 +108,8 @@ pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debu
 ///
 /// # Arguments
 ///
-/// * `cell_str`: the opaque cell string
+/// * `cell_str`: the opaque cell string that can be decoded by corresponding structs that implement the
+/// `FromCellString` trait.
 /// * `from_field_type`: the original field type of the passed-in cell data. Check the `TypeCellData`
 /// that is used to save the origin field type of the cell data.
 /// * `to_field_type`: decode the passed-in cell data to this field type. It will use the to_field_type's
@@ -155,7 +156,7 @@ pub fn insert_text_cell(s: String, field_rev: &FieldRevision) -> CellRevision {
 }
 
 pub fn insert_number_cell(num: i64, field_rev: &FieldRevision) -> CellRevision {
-    let data = apply_cell_data_changeset(num, None, field_rev, None).unwrap();
+    let data = apply_cell_data_changeset(num.to_string(), None, field_rev, None).unwrap();
     CellRevision::new(data)
 }
 
@@ -186,14 +187,14 @@ pub fn insert_date_cell(timestamp: i64, field_rev: &FieldRevision) -> CellRevisi
 }
 
 pub fn insert_select_option_cell(option_ids: Vec<String>, field_rev: &FieldRevision) -> CellRevision {
-    let cell_data = SelectOptionCellChangeset::from_insert_options(option_ids).to_str();
-    let data = apply_cell_data_changeset(cell_data, None, field_rev, None).unwrap();
+    let changeset = SelectOptionCellChangeset::from_insert_options(option_ids).to_cell_changeset_str();
+    let data = apply_cell_data_changeset(changeset, None, field_rev, None).unwrap();
     CellRevision::new(data)
 }
 
 pub fn delete_select_option_cell(option_ids: Vec<String>, field_rev: &FieldRevision) -> CellRevision {
-    let cell_data = SelectOptionCellChangeset::from_delete_options(option_ids).to_str();
-    let data = apply_cell_data_changeset(cell_data, None, field_rev, None).unwrap();
+    let changeset = SelectOptionCellChangeset::from_delete_options(option_ids).to_cell_changeset_str();
+    let data = apply_cell_data_changeset(changeset, None, field_rev, None).unwrap();
     CellRevision::new(data)
 }
 
@@ -252,13 +253,13 @@ impl std::convert::From<IntoCellData<String>> for String {
 
 /// If the changeset applying to the cell is not String type, it should impl this trait.
 /// Deserialize the string into cell specific changeset.
-pub trait FromCellChangeset {
+pub trait FromCellChangesetString {
     fn from_changeset(changeset: String) -> FlowyResult<Self>
     where
         Self: Sized;
 }
 
-impl FromCellChangeset for String {
+impl FromCellChangesetString for String {
     fn from_changeset(changeset: String) -> FlowyResult<Self>
     where
         Self: Sized,
@@ -267,6 +268,16 @@ impl FromCellChangeset for String {
     }
 }
 
+pub trait ToCellChangesetString: Debug {
+    fn to_cell_changeset_str(&self) -> String;
+}
+
+impl ToCellChangesetString for String {
+    fn to_cell_changeset_str(&self) -> String {
+        self.clone()
+    }
+}
+
 pub struct AnyCellChangeset<T>(pub Option<T>);
 
 impl<T> AnyCellChangeset<T> {
@@ -280,7 +291,7 @@ impl<T> AnyCellChangeset<T> {
 
 impl<T, C: ToString> std::convert::From<C> for AnyCellChangeset<T>
 where
-    T: FromCellChangeset,
+    T: FromCellChangesetString,
 {
     fn from(changeset: C) -> Self {
         match T::from_changeset(changeset.to_string()) {

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

@@ -1,5 +1,4 @@
 use crate::entities::{DateFilterConditionPB, DateFilterPB};
-
 use chrono::NaiveDateTime;
 
 impl DateFilterPB {

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

@@ -1,5 +1,7 @@
 use crate::entities::CellPathPB;
-use crate::services::cell::{CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString};
+use crate::services::cell::{
+    CellProtobufBlobParser, DecodedCellData, FromCellChangesetString, FromCellString, ToCellChangesetString,
+};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{internal_error, FlowyResult};
@@ -33,7 +35,7 @@ pub struct DateChangesetPB {
     pub is_utc: bool,
 }
 
-#[derive(Clone, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct DateCellChangeset {
     pub date: Option<String>,
     pub time: Option<String>,
@@ -53,7 +55,7 @@ impl DateCellChangeset {
     }
 }
 
-impl FromCellChangeset for DateCellChangeset {
+impl FromCellChangesetString for DateCellChangeset {
     fn from_changeset(changeset: String) -> FlowyResult<Self>
     where
         Self: Sized,
@@ -62,9 +64,9 @@ impl FromCellChangeset for DateCellChangeset {
     }
 }
 
-impl ToString for DateCellChangeset {
-    fn to_string(&self) -> String {
-        serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
+impl ToCellChangesetString for DateCellChangeset {
+    fn to_cell_changeset_str(&self) -> String {
+        serde_json::to_string(self).unwrap_or_default()
     }
 }
 

+ 13 - 26
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs

@@ -1,7 +1,8 @@
 use crate::entities::parser::NotEmptyStr;
-use crate::entities::{CellChangesetPB, CellPathPB, CellPathParams, FieldType};
+use crate::entities::{CellPathPB, CellPathParams, FieldType};
 use crate::services::cell::{
-    CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangeset, FromCellString,
+    CellDataDecoder, CellProtobufBlobParser, DecodedCellData, FromCellChangesetString, FromCellString,
+    ToCellChangesetString,
 };
 
 use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
@@ -379,22 +380,6 @@ pub struct SelectOptionCellChangesetParams {
     pub delete_option_ids: Vec<String>,
 }
 
-impl std::convert::From<SelectOptionCellChangesetParams> for CellChangesetPB {
-    fn from(params: SelectOptionCellChangesetParams) -> Self {
-        let changeset = SelectOptionCellChangeset {
-            insert_option_ids: params.insert_option_ids,
-            delete_option_ids: params.delete_option_ids,
-        };
-        let content = serde_json::to_string(&changeset).unwrap();
-        CellChangesetPB {
-            grid_id: params.cell_identifier.view_id,
-            row_id: params.cell_identifier.row_id,
-            field_id: params.cell_identifier.field_id,
-            type_cell_data: content,
-        }
-    }
-}
-
 impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPB {
     type Error = ErrorCode;
 
@@ -432,13 +417,13 @@ impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPB {
     }
 }
 
-#[derive(Clone, Serialize, Deserialize)]
+#[derive(Clone, Serialize, Deserialize, Debug)]
 pub struct SelectOptionCellChangeset {
     pub insert_option_ids: Vec<String>,
     pub delete_option_ids: Vec<String>,
 }
 
-impl FromCellChangeset for SelectOptionCellChangeset {
+impl FromCellChangesetString for SelectOptionCellChangeset {
     fn from_changeset(changeset: String) -> FlowyResult<Self>
     where
         Self: Sized,
@@ -447,6 +432,12 @@ impl FromCellChangeset for SelectOptionCellChangeset {
     }
 }
 
+impl ToCellChangesetString for SelectOptionCellChangeset {
+    fn to_cell_changeset_str(&self) -> String {
+        serde_json::to_string(self).unwrap_or_default()
+    }
+}
+
 impl SelectOptionCellChangeset {
     pub fn from_insert_option_id(option_id: &str) -> Self {
         SelectOptionCellChangeset {
@@ -475,10 +466,6 @@ impl SelectOptionCellChangeset {
             delete_option_ids: option_ids,
         }
     }
-
-    pub fn to_str(&self) -> String {
-        serde_json::to_string(self).unwrap()
-    }
 }
 
 /// [SelectOptionCellDataPB] contains a list of user's selected options and a list of all the options
@@ -512,7 +499,7 @@ pub struct SelectOptionChangesetPB {
 }
 
 pub struct SelectOptionChangeset {
-    pub cell_identifier: CellPathParams,
+    pub cell_path: CellPathParams,
     pub insert_options: Vec<SelectOptionPB>,
     pub update_options: Vec<SelectOptionPB>,
     pub delete_options: Vec<SelectOptionPB>,
@@ -524,7 +511,7 @@ impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPB {
     fn try_into(self) -> Result<SelectOptionChangeset, Self::Error> {
         let cell_identifier = self.cell_identifier.try_into()?;
         Ok(SelectOptionChangeset {
-            cell_identifier,
+            cell_path: cell_identifier,
             insert_options: self.insert_options,
             update_options: self.update_options,
             delete_options: self.delete_options,

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

@@ -1,5 +1,5 @@
 use crate::entities::FieldType;
-use crate::services::cell::{CellDataDecoder, FromCellChangeset, FromCellString};
+use crate::services::cell::{CellDataDecoder, FromCellChangesetString, FromCellString, ToCellChangesetString};
 
 use crate::services::filter::FromFilterString;
 use bytes::Bytes;
@@ -23,10 +23,10 @@ pub trait TypeOption {
     type CellData: FromCellString + ToString + Default + Send + Sync + Clone + 'static;
 
     /// Represents as the corresponding field type cell changeset.
-    /// The changeset must implements the `FromCellChangeset` trait. The `CellChangeset` is implemented
-    /// for `String`.
+    /// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.
+    /// These two traits are auto implemented for `String`.
     ///  
-    type CellChangeset: FromCellChangeset;
+    type CellChangeset: FromCellChangesetString + ToCellChangesetString;
 
     ///  For the moment, the protobuf type only be used in the FFI of `Dart`. If the decoded cell
     /// struct is just a `String`, then use the `StrCellData` as its `CellProtobufType`.

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

@@ -1,7 +1,7 @@
 use crate::entities::FieldType;
 use crate::services::cell::{
     AtomicCellDataCache, AtomicCellFilterCache, CellDataChangeset, CellDataDecoder, CellProtobufBlob,
-    FromCellChangeset, FromCellString, TypeCellData,
+    FromCellChangesetString, FromCellString, TypeCellData,
 };
 use crate::services::field::{
     default_order, CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateTypeOptionPB, MultiSelectTypeOptionPB,

+ 4 - 8
frontend/rust-lib/flowy-grid/src/services/filter/controller.rs

@@ -77,7 +77,7 @@ impl FilterController {
     }
 
     #[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))]
-    async fn gen_task(&mut self, task_type: FilterEvent, qos: QualityOfService) {
+    async fn gen_task(&self, task_type: FilterEvent, qos: QualityOfService) {
         let task_id = self.task_scheduler.read().await.next_task_id();
         let task = Task::new(&self.handler_id, task_id, TaskContent::Text(task_type.to_string()), qos);
         self.task_scheduler.write().await.add_task(task);
@@ -148,9 +148,7 @@ impl FilterController {
                 }
             }
 
-            let _ = self
-                .notifier
-                .send(GridViewChanged::DidReceiveFilterResult(notification));
+            let _ = self.notifier.send(GridViewChanged::FilterNotification(notification));
         }
         Ok(())
     }
@@ -186,14 +184,12 @@ impl FilterController {
                 visible_rows,
             };
             tracing::Span::current().record("filter_result", &format!("{:?}", &notification).as_str());
-            let _ = self
-                .notifier
-                .send(GridViewChanged::DidReceiveFilterResult(notification));
+            let _ = self.notifier.send(GridViewChanged::FilterNotification(notification));
         }
         Ok(())
     }
 
-    pub async fn did_receive_row_changed(&mut self, row_id: &str) {
+    pub async fn did_receive_row_changed(&self, row_id: &str) {
         self.gen_task(
             FilterEvent::RowDidChanged(row_id.to_string()),
             QualityOfService::UserInteractive,

+ 5 - 1
frontend/rust-lib/flowy-grid/src/services/filter/task.rs

@@ -24,6 +24,10 @@ impl TaskHandler for FilterTaskHandler {
         &self.handler_id
     }
 
+    fn handler_name(&self) -> &str {
+        "FilterTaskHandler"
+    }
+
     fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
         let filter_controller = self.filter_controller.clone();
         Box::pin(async move {
@@ -33,7 +37,7 @@ impl TaskHandler for FilterTaskHandler {
                     .await
                     .process(&predicate)
                     .await
-                    .map_err(anyhow::Error::from);
+                    .map_err(anyhow::Error::from)?;
             }
             Ok(())
         })

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

@@ -5,7 +5,7 @@ use crate::manager::GridUser;
 use crate::services::block_manager::GridBlockManager;
 use crate::services::cell::{
     apply_cell_data_changeset, decode_type_cell_data, stringify_cell_data, AnyTypeCache, AtomicCellDataCache,
-    CellProtobufBlob, TypeCellData,
+    CellProtobufBlob, ToCellChangesetString, TypeCellData,
 };
 use crate::services::field::{
     default_type_option_builder_from_type, transform_type_option, type_option_builder_from_bytes, FieldBuilder,
@@ -31,6 +31,7 @@ use grid_rev_model::*;
 use lib_infra::future::{to_fut, FutureResult};
 use lib_ot::core::EmptyAttributes;
 use std::collections::HashMap;
+
 use std::sync::Arc;
 use tokio::sync::{broadcast, RwLock};
 
@@ -405,16 +406,20 @@ impl GridRevisionEditor {
     /// Returns all the rows in this block.
     pub async fn get_row_pbs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<RowPB>> {
         let rows = self.view_manager.get_row_revs(view_id, block_id).await?;
-        let rows = self
-            .view_manager
-            .filter_rows(view_id, block_id, rows)
-            .await?
-            .into_iter()
-            .map(|row_rev| RowPB::from(&row_rev))
-            .collect();
+        let rows = rows.into_iter().map(|row_rev| RowPB::from(&row_rev)).collect();
         Ok(rows)
     }
 
+    pub async fn get_all_row_revs(&self, view_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
+        let mut all_rows = vec![];
+        let blocks = self.block_manager.get_blocks(None).await?;
+        for block in blocks {
+            let rows = self.view_manager.get_row_revs(view_id, &block.block_id).await?;
+            all_rows.extend(rows);
+        }
+        Ok(all_rows)
+    }
+
     pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<Arc<RowRevision>>> {
         match self.block_manager.get_row_rev(row_id).await? {
             None => Ok(None),
@@ -458,7 +463,7 @@ impl GridRevisionEditor {
             ))
         };
 
-        display_str().await.unwrap_or("".to_string())
+        display_str().await.unwrap_or_else(|| "".to_string())
     }
 
     pub async fn get_cell_bytes(&self, params: &CellPathParams) -> Option<CellProtobufBlob> {
@@ -488,52 +493,45 @@ impl GridRevisionEditor {
     }
 
     #[tracing::instrument(level = "trace", skip_all, err)]
-    pub async fn update_cell_with_changeset(&self, cell_changeset: CellChangesetPB) -> FlowyResult<()> {
-        let CellChangesetPB {
-            grid_id,
-            row_id,
-            field_id,
-            type_cell_data: mut content,
-        } = cell_changeset;
-
-        match self.grid_pad.read().await.get_field_rev(&field_id) {
+    pub async fn update_cell_with_changeset<T: ToCellChangesetString>(
+        &self,
+        row_id: &str,
+        field_id: &str,
+        cell_changeset: T,
+    ) -> FlowyResult<()> {
+        match self.grid_pad.read().await.get_field_rev(field_id) {
             None => {
                 let msg = format!("Field:{} not found", &field_id);
                 Err(FlowyError::internal().context(msg))
             }
             Some((_, field_rev)) => {
-                tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, content);
-                let cell_rev = self.get_cell_rev(&row_id, &field_id).await?;
+                tracing::trace!("Cell changeset: id:{} / value:{:?}", &field_id, cell_changeset);
+                let cell_rev = self.get_cell_rev(row_id, field_id).await?;
                 // Update the changeset.data property with the return value.
-                content = apply_cell_data_changeset(content, cell_rev, field_rev, Some(self.cell_data_cache.clone()))?;
+                let type_cell_data =
+                    apply_cell_data_changeset(cell_changeset, cell_rev, field_rev, Some(self.cell_data_cache.clone()))?;
                 let cell_changeset = CellChangesetPB {
-                    grid_id,
-                    row_id: row_id.clone(),
-                    field_id: field_id.clone(),
-                    type_cell_data: content,
+                    grid_id: self.grid_id.clone(),
+                    row_id: row_id.to_owned(),
+                    field_id: field_id.to_owned(),
+                    type_cell_data,
                 };
                 let _ = self.block_manager.update_cell(cell_changeset).await?;
-                self.view_manager.did_update_cell(&row_id).await;
+                self.view_manager.did_update_cell(row_id).await;
                 Ok(())
             }
         }
     }
 
     #[tracing::instrument(level = "trace", skip_all, err)]
-    pub async fn update_cell<T: ToString>(
+    pub async fn update_cell<T: ToCellChangesetString>(
         &self,
-        grid_id: String,
         row_id: String,
         field_id: String,
-        content: T,
+        cell_changeset: T,
     ) -> FlowyResult<()> {
-        self.update_cell_with_changeset(CellChangesetPB {
-            grid_id,
-            row_id,
-            field_id,
-            type_cell_data: content.to_string(),
-        })
-        .await
+        self.update_cell_with_changeset(&row_id, &field_id, cell_changeset)
+            .await
     }
 
     pub async fn get_block_meta_revs(&self) -> FlowyResult<Vec<Arc<GridBlockMetaRevision>>> {

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/persistence/rev_sqlite/grid_snapshot_sqlite_impl.rs

@@ -92,7 +92,7 @@ impl RevisionSnapshotDiskCache for SQLiteGridRevisionSnapshotPersistence {
         Ok(Some(latest_record.into()))
     }
 }
-//noinspection ALL
+
 #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
 #[table_name = "grid_rev_snapshot"]
 #[primary_key("snapshot_id")]

+ 141 - 40
frontend/rust-lib/flowy-grid/src/services/sort/controller.rs

@@ -1,52 +1,61 @@
-#![allow(clippy::all)]
-
 use crate::entities::FieldType;
-#[allow(unused_attributes)]
 use crate::entities::SortChangesetNotificationPB;
-use crate::services::sort::{SortChangeset, SortType};
-use flowy_task::TaskDispatcher;
-use grid_rev_model::{CellRevision, FieldRevision, RowRevision, SortCondition, SortRevision};
-use lib_infra::future::Fut;
-
 use crate::services::cell::{AtomicCellDataCache, TypeCellData};
 use crate::services::field::{default_order, TypeOptionCellExt};
+use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult, SortChangeset, SortType};
+use crate::services::view_editor::{GridViewChanged, GridViewChangedNotifier};
+use flowy_error::FlowyResult;
+use flowy_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
+use grid_rev_model::{CellRevision, FieldRevision, RowRevision, SortCondition, SortRevision};
+use lib_infra::future::Fut;
 use rayon::prelude::ParallelSliceMut;
+use serde::{Deserialize, Serialize};
 use std::cmp::Ordering;
-
+use std::collections::HashMap;
+use std::str::FromStr;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
 pub trait SortDelegate: Send + Sync {
     fn get_sort_rev(&self, sort_type: SortType) -> Fut<Option<Arc<SortRevision>>>;
+    /// Returns all the rows after applying grid's filter
+    fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>>;
     fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>>;
     fn get_field_revs(&self, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<FieldRevision>>>;
 }
 
 pub struct SortController {
+    view_id: String,
     handler_id: String,
     delegate: Box<dyn SortDelegate>,
     task_scheduler: Arc<RwLock<TaskDispatcher>>,
     sorts: Vec<Arc<SortRevision>>,
     cell_data_cache: AtomicCellDataCache,
+    row_index_cache: HashMap<String, usize>,
+    notifier: GridViewChangedNotifier,
 }
 
 impl SortController {
     pub fn new<T>(
-        _view_id: &str,
+        view_id: &str,
         handler_id: &str,
         delegate: T,
         task_scheduler: Arc<RwLock<TaskDispatcher>>,
         cell_data_cache: AtomicCellDataCache,
+        notifier: GridViewChangedNotifier,
     ) -> Self
     where
         T: SortDelegate + 'static,
     {
         Self {
+            view_id: view_id.to_string(),
             handler_id: handler_id.to_string(),
             delegate: Box::new(delegate),
             task_scheduler,
             sorts: vec![],
             cell_data_cache,
+            row_index_cache: Default::default(),
+            notifier,
         }
     }
 
@@ -58,65 +67,139 @@ impl SortController {
             .await;
     }
 
-    pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
+    pub async fn did_receive_row_changed(&self, row_id: &str) {
+        let task_type = SortEvent::RowDidChanged(row_id.to_string());
+        self.gen_task(task_type, QualityOfService::Background).await;
+    }
+
+    #[tracing::instrument(name = "receive_sort_task_result", level = "trace", skip_all, err)]
+    pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
+        let event_type = SortEvent::from_str(predicate).unwrap();
+        let mut row_revs = self.delegate.get_row_revs().await;
+        match event_type {
+            SortEvent::SortDidChanged => {
+                self.sort_rows(&mut row_revs).await;
+                let row_orders = row_revs
+                    .iter()
+                    .map(|row_rev| row_rev.id.clone())
+                    .collect::<Vec<String>>();
+
+                let notification = ReorderAllRowsResult {
+                    view_id: self.view_id.clone(),
+                    row_orders,
+                };
+
+                let _ = self
+                    .notifier
+                    .send(GridViewChanged::ReorderAllRowsNotification(notification));
+            }
+            SortEvent::RowDidChanged(row_id) => {
+                let old_row_index = self.row_index_cache.get(&row_id).cloned();
+                self.sort_rows(&mut row_revs).await;
+                let new_row_index = self.row_index_cache.get(&row_id).cloned();
+                match (old_row_index, new_row_index) {
+                    (Some(old_row_index), Some(new_row_index)) => {
+                        if old_row_index == new_row_index {
+                            return Ok(());
+                        }
+                        let notification = ReorderSingleRowResult {
+                            view_id: self.view_id.clone(),
+                            old_index: old_row_index,
+                            new_index: new_row_index,
+                        };
+                        let _ = self
+                            .notifier
+                            .send(GridViewChanged::ReorderSingleRowNotification(notification));
+                    }
+                    _ => tracing::trace!("The row index cache is outdated"),
+                }
+            }
+        }
+        Ok(())
+    }
+
+    #[tracing::instrument(name = "schedule_sort_task", level = "trace", skip(self))]
+    async fn gen_task(&self, task_type: SortEvent, qos: QualityOfService) {
+        if self.sorts.is_empty() {
+            return;
+        }
+        let task_id = self.task_scheduler.read().await.next_task_id();
+        let task = Task::new(&self.handler_id, task_id, TaskContent::Text(task_type.to_string()), qos);
+        self.task_scheduler.write().await.add_task(task);
+    }
+
+    pub async fn sort_rows(&mut self, rows: &mut Vec<Arc<RowRevision>>) {
+        if self.sorts.is_empty() {
+            return;
+        }
+
         let field_revs = self.delegate.get_field_revs(None).await;
-        rows.par_sort_by(|left, right| cmp_row(left, right, &self.sorts, &field_revs, &self.cell_data_cache));
+        for sort in self.sorts.iter() {
+            rows.par_sort_by(|left, right| cmp_row(left, right, sort, &field_revs, &self.cell_data_cache));
+        }
+        rows.iter().enumerate().for_each(|(index, row)| {
+            self.row_index_cache.insert(row.id.to_string(), index);
+        });
     }
 
     #[tracing::instrument(level = "trace", skip(self))]
-    pub async fn did_receive_changes(&mut self, changeset: SortChangeset) -> Option<SortChangesetNotificationPB> {
+    pub async fn did_receive_changes(&mut self, changeset: SortChangeset) -> SortChangesetNotificationPB {
+        let mut notification = SortChangesetNotificationPB::default();
         if let Some(insert_sort) = changeset.insert_sort {
             if let Some(sort) = self.delegate.get_sort_rev(insert_sort).await {
+                notification.insert_sorts.push(sort.as_ref().into());
                 self.sorts.push(sort);
             }
         }
 
         if let Some(delete_sort_type) = changeset.delete_sort {
             if let Some(index) = self.sorts.iter().position(|sort| sort.id == delete_sort_type.sort_id) {
-                self.sorts.remove(index);
+                let sort = self.sorts.remove(index);
+                notification.delete_sorts.push(sort.as_ref().into());
             }
         }
 
-        if let Some(_update_sort) = changeset.update_sort {
-            //
+        if let Some(update_sort) = changeset.update_sort {
+            if let Some(updated_sort) = self.delegate.get_sort_rev(update_sort).await {
+                notification.update_sorts.push(updated_sort.as_ref().into());
+                if let Some(index) = self.sorts.iter().position(|sort| sort.id == updated_sort.id) {
+                    self.sorts[index] = updated_sort;
+                }
+            }
         }
 
-        None
+        if !notification.insert_sorts.is_empty() || !notification.delete_sorts.is_empty() {
+            self.gen_task(SortEvent::SortDidChanged, QualityOfService::Background)
+                .await;
+        }
+        notification
     }
 }
 
 fn cmp_row(
     left: &Arc<RowRevision>,
     right: &Arc<RowRevision>,
-    sorts: &[Arc<SortRevision>],
+    sort: &Arc<SortRevision>,
     field_revs: &[Arc<FieldRevision>],
     cell_data_cache: &AtomicCellDataCache,
 ) -> Ordering {
-    let mut order = default_order();
-    for sort in sorts.iter() {
-        let cmp_order = match (left.cells.get(&sort.field_id), right.cells.get(&sort.field_id)) {
-            (Some(left_cell), Some(right_cell)) => {
-                let field_type: FieldType = sort.field_type.into();
-                match field_revs.iter().find(|field_rev| field_rev.id == sort.field_id) {
-                    None => default_order(),
-                    Some(field_rev) => cmp_cell(left_cell, right_cell, field_rev, field_type, cell_data_cache),
-                }
+    let order = match (left.cells.get(&sort.field_id), right.cells.get(&sort.field_id)) {
+        (Some(left_cell), Some(right_cell)) => {
+            let field_type: FieldType = sort.field_type.into();
+            match field_revs.iter().find(|field_rev| field_rev.id == sort.field_id) {
+                None => default_order(),
+                Some(field_rev) => cmp_cell(left_cell, right_cell, field_rev, field_type, cell_data_cache),
             }
-            (Some(_), None) => Ordering::Greater,
-            (None, Some(_)) => Ordering::Less,
-            _ => default_order(),
-        };
-
-        if cmp_order.is_ne() {
-            // If the cmp_order is not Ordering::Equal, then break the loop.
-            order = match sort.condition {
-                SortCondition::Ascending => cmp_order,
-                SortCondition::Descending => cmp_order.reverse(),
-            };
-            break;
         }
+        (Some(_), None) => Ordering::Greater,
+        (None, Some(_)) => Ordering::Less,
+        _ => default_order(),
+    };
+
+    match sort.condition {
+        SortCondition::Ascending => order,
+        SortCondition::Descending => order.reverse(),
     }
-    order
 }
 
 fn cmp_cell(
@@ -142,3 +225,21 @@ fn cmp_cell(
         }
     }
 }
+#[derive(Serialize, Deserialize, Clone, Debug)]
+enum SortEvent {
+    SortDidChanged,
+    RowDidChanged(String),
+}
+
+impl ToString for SortEvent {
+    fn to_string(&self) -> String {
+        serde_json::to_string(self).unwrap()
+    }
+}
+
+impl FromStr for SortEvent {
+    type Err = serde_json::Error;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        serde_json::from_str(s)
+    }
+}

+ 19 - 1
frontend/rust-lib/flowy-grid/src/services/sort/entities.rs

@@ -32,7 +32,25 @@ impl std::convert::From<&Arc<FieldRevision>> for SortType {
     }
 }
 
-#[allow(dead_code)]
+#[derive(Clone)]
+pub struct ReorderAllRowsResult {
+    pub view_id: String,
+    pub row_orders: Vec<String>,
+}
+
+impl ReorderAllRowsResult {
+    pub fn new(view_id: String, row_orders: Vec<String>) -> Self {
+        Self { view_id, row_orders }
+    }
+}
+
+#[derive(Clone)]
+pub struct ReorderSingleRowResult {
+    pub view_id: String,
+    pub old_index: usize,
+    pub new_index: usize,
+}
+
 #[derive(Debug)]
 pub struct SortChangeset {
     pub(crate) insert_sort: Option<SortType>,

+ 17 - 2
frontend/rust-lib/flowy-grid/src/services/sort/task.rs

@@ -24,7 +24,22 @@ impl TaskHandler for SortTaskHandler {
         &self.handler_id
     }
 
-    fn run(&self, _content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
-        todo!();
+    fn handler_name(&self) -> &str {
+        "SortTaskHandler"
+    }
+
+    fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> {
+        let sort_controller = self.sort_controller.clone();
+        Box::pin(async move {
+            if let TaskContent::Text(predicate) = content {
+                let _ = sort_controller
+                    .write()
+                    .await
+                    .process(&predicate)
+                    .await
+                    .map_err(anyhow::Error::from)?;
+            }
+            Ok(())
+        })
     }
 }

+ 23 - 3
frontend/rust-lib/flowy-grid/src/services/view_editor/changed_notifier.rs

@@ -1,13 +1,16 @@
 use crate::dart_notification::{send_dart_notification, GridDartNotification};
-use crate::entities::GridRowsVisibilityChangesetPB;
+use crate::entities::{GridRowsVisibilityChangesetPB, ReorderAllRowsPB, ReorderSingleRowPB};
 use crate::services::filter::FilterResultNotification;
+use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult};
 use async_stream::stream;
 use futures::stream::StreamExt;
 use tokio::sync::broadcast;
 
 #[derive(Clone)]
 pub enum GridViewChanged {
-    DidReceiveFilterResult(FilterResultNotification),
+    FilterNotification(FilterResultNotification),
+    ReorderAllRowsNotification(ReorderAllRowsResult),
+    ReorderSingleRowNotification(ReorderSingleRowResult),
 }
 
 pub type GridViewChangedNotifier = broadcast::Sender<GridViewChanged>;
@@ -27,7 +30,7 @@ impl GridViewChangedReceiverRunner {
         stream
             .for_each(|changed| async {
                 match changed {
-                    GridViewChanged::DidReceiveFilterResult(notification) => {
+                    GridViewChanged::FilterNotification(notification) => {
                         let changeset = GridRowsVisibilityChangesetPB {
                             view_id: notification.view_id,
                             visible_rows: notification.visible_rows,
@@ -41,6 +44,23 @@ impl GridViewChangedReceiverRunner {
                         .payload(changeset)
                         .send()
                     }
+                    GridViewChanged::ReorderAllRowsNotification(notification) => {
+                        let row_orders = ReorderAllRowsPB {
+                            row_orders: notification.row_orders,
+                        };
+                        send_dart_notification(&notification.view_id, GridDartNotification::DidReorderRows)
+                            .payload(row_orders)
+                            .send()
+                    }
+                    GridViewChanged::ReorderSingleRowNotification(notification) => {
+                        let reorder_row = ReorderSingleRowPB {
+                            old_index: notification.old_index as i32,
+                            new_index: notification.new_index as i32,
+                        };
+                        send_dart_notification(&notification.view_id, GridDartNotification::DidReorderSingleRow)
+                            .payload(reorder_row)
+                            .send()
+                    }
                 }
             })
             .await;

+ 27 - 20
frontend/rust-lib/flowy-grid/src/services/view_editor/editor.rs

@@ -112,22 +112,24 @@ impl GridViewRevisionEditor {
         )
         .await?;
 
-        let sort_controller = make_sort_controller(
+        let user_id = user_id.to_owned();
+        let group_controller = Arc::new(RwLock::new(group_controller));
+        let filter_controller = make_filter_controller(
             &view_id,
             delegate.clone(),
-            view_rev_pad.clone(),
+            notifier.clone(),
             cell_data_cache.clone(),
+            view_rev_pad.clone(),
         )
         .await;
 
-        let user_id = user_id.to_owned();
-        let group_controller = Arc::new(RwLock::new(group_controller));
-        let filter_controller = make_filter_controller(
+        let sort_controller = make_sort_controller(
             &view_id,
             delegate.clone(),
             notifier.clone(),
-            cell_data_cache,
+            filter_controller.clone(),
             view_rev_pad.clone(),
+            cell_data_cache,
         )
         .await;
         Ok(Self {
@@ -181,12 +183,11 @@ impl GridViewRevisionEditor {
     }
 
     pub async fn sort_rows(&self, rows: &mut Vec<Arc<RowRevision>>) {
-        self.sort_controller.read().await.sort_rows(rows).await
+        self.sort_controller.write().await.sort_rows(rows).await
     }
 
-    pub async fn filter_rows(&self, _block_id: &str, mut rows: Vec<Arc<RowRevision>>) -> Vec<Arc<RowRevision>> {
-        self.filter_controller.write().await.filter_row_revs(&mut rows).await;
-        rows
+    pub async fn filter_rows(&self, _block_id: &str, rows: &mut Vec<Arc<RowRevision>>) {
+        self.filter_controller.write().await.filter_row_revs(rows).await;
     }
 
     pub async fn duplicate_view_data(&self) -> FlowyResult<String> {
@@ -260,9 +261,11 @@ impl GridViewRevisionEditor {
         }
 
         let filter_controller = self.filter_controller.clone();
+        let sort_controller = self.sort_controller.clone();
         let row_id = row_rev.id.clone();
         tokio::spawn(async move {
-            filter_controller.write().await.did_receive_row_changed(&row_id).await;
+            filter_controller.read().await.did_receive_row_changed(&row_id).await;
+            sort_controller.read().await.did_receive_row_changed(&row_id).await;
         });
     }
 
@@ -423,10 +426,9 @@ impl GridViewRevisionEditor {
                 .did_receive_changes(SortChangeset::from_insert(sort_type))
                 .await
         };
+        drop(sort_controller);
 
-        if let Some(changeset) = changeset {
-            self.notify_did_update_sort(changeset).await;
-        }
+        self.notify_did_update_sort(changeset).await;
         Ok(sort_rev)
     }
 
@@ -446,9 +448,7 @@ impl GridViewRevisionEditor {
             })
             .await?;
 
-        if changeset.is_some() {
-            self.notify_did_update_sort(changeset.unwrap()).await;
-        }
+        self.notify_did_update_sort(changeset).await;
         Ok(())
     }
 
@@ -495,6 +495,7 @@ impl GridViewRevisionEditor {
                 .did_receive_changes(FilterChangeset::from_insert(filter_type))
                 .await
         };
+        drop(filter_controller);
 
         if let Some(changeset) = changeset {
             self.notify_did_update_filter(changeset).await;
@@ -612,9 +613,11 @@ impl GridViewRevisionEditor {
     }
 
     pub async fn notify_did_update_sort(&self, changeset: SortChangesetNotificationPB) {
-        send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
-            .payload(changeset)
-            .send();
+        if !changeset.is_empty() {
+            send_dart_notification(&changeset.view_id, GridDartNotification::DidUpdateSort)
+                .payload(changeset)
+                .send();
+        }
     }
 
     async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
@@ -756,6 +759,8 @@ async fn make_filter_controller(
 async fn make_sort_controller(
     view_id: &str,
     delegate: Arc<dyn GridViewEditorDelegate>,
+    notifier: GridViewChangedNotifier,
+    filter_controller: Arc<RwLock<FilterController>>,
     pad: Arc<RwLock<GridViewRevisionPad>>,
     cell_data_cache: AtomicCellDataCache,
 ) -> Arc<RwLock<SortController>> {
@@ -763,6 +768,7 @@ async fn make_sort_controller(
     let sort_delegate = GridViewSortDelegateImpl {
         editor_delegate: delegate.clone(),
         view_revision_pad: pad,
+        filter_controller,
     };
     let task_scheduler = delegate.get_task_scheduler();
     let sort_controller = Arc::new(RwLock::new(SortController::new(
@@ -771,6 +777,7 @@ async fn make_sort_controller(
         sort_delegate,
         task_scheduler.clone(),
         cell_data_cache,
+        notifier,
     )));
     task_scheduler
         .write()

+ 2 - 13
frontend/rust-lib/flowy-grid/src/services/view_editor/editor_manager.rs

@@ -60,22 +60,11 @@ impl GridViewManager {
     pub async fn get_row_revs(&self, view_id: &str, block_id: &str) -> FlowyResult<Vec<Arc<RowRevision>>> {
         let mut row_revs = self.delegate.get_row_revs(Some(vec![block_id.to_owned()])).await;
         if let Ok(view_editor) = self.get_view_editor(view_id).await {
+            view_editor.filter_rows(block_id, &mut row_revs).await;
             view_editor.sort_rows(&mut row_revs).await;
         }
-        Ok(row_revs)
-    }
 
-    pub async fn filter_rows(
-        &self,
-        view_id: &str,
-        block_id: &str,
-        rows: Vec<Arc<RowRevision>>,
-    ) -> FlowyResult<Vec<Arc<RowRevision>>> {
-        let rows = match self.get_view_editor(view_id).await {
-            Ok(view_editor) => view_editor.filter_rows(block_id, rows).await,
-            Err(_) => rows,
-        };
-        Ok(rows)
+        Ok(row_revs)
     }
 
     pub async fn duplicate_grid_view(&self) -> FlowyResult<String> {

+ 12 - 1
frontend/rust-lib/flowy-grid/src/services/view_editor/trait_impl.rs

@@ -1,5 +1,5 @@
 use crate::entities::{GridLayout, GridLayoutPB, GridSettingPB};
-use crate::services::filter::{FilterDelegate, FilterType};
+use crate::services::filter::{FilterController, FilterDelegate, FilterType};
 use crate::services::group::{GroupConfigurationReader, GroupConfigurationWriter};
 use crate::services::row::GridBlockRowRevision;
 use crate::services::sort::{SortDelegate, SortType};
@@ -169,6 +169,7 @@ impl FilterDelegate for GridViewFilterDelegateImpl {
 pub(crate) struct GridViewSortDelegateImpl {
     pub(crate) editor_delegate: Arc<dyn GridViewEditorDelegate>,
     pub(crate) view_revision_pad: Arc<RwLock<GridViewRevisionPad>>,
+    pub(crate) filter_controller: Arc<RwLock<FilterController>>,
 }
 
 impl SortDelegate for GridViewSortDelegateImpl {
@@ -187,6 +188,16 @@ impl SortDelegate for GridViewSortDelegateImpl {
         })
     }
 
+    fn get_row_revs(&self) -> Fut<Vec<Arc<RowRevision>>> {
+        let filter_controller = self.filter_controller.clone();
+        let editor_delegate = self.editor_delegate.clone();
+        to_fut(async move {
+            let mut row_revs = editor_delegate.get_row_revs(None).await;
+            filter_controller.write().await.filter_row_revs(&mut row_revs).await;
+            row_revs
+        })
+    }
+
     fn get_field_rev(&self, field_id: &str) -> Fut<Option<Arc<FieldRevision>>> {
         self.editor_delegate.get_field_rev(field_id)
     }

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

@@ -9,13 +9,13 @@ use grid_rev_model::RowChangeset;
 async fn grid_create_row_count_test() {
     let mut test = GridRowTest::new().await;
     let scripts = vec![
-        AssertRowCount(5),
+        AssertRowCount(6),
         CreateEmptyRow,
         CreateEmptyRow,
         CreateRow {
             row_rev: test.row_builder().build(),
         },
-        AssertRowCount(8),
+        AssertRowCount(9),
     ];
     test.run_scripts(scripts).await;
 }
@@ -30,12 +30,12 @@ async fn grid_update_row() {
         visibility: None,
         cell_by_field_id: Default::default(),
     };
-
-    let scripts = vec![AssertRowCount(5), CreateRow { row_rev }, UpdateRow { changeset }];
+    let row_count = test.row_revs.len();
+    let scripts = vec![CreateRow { row_rev }, UpdateRow { changeset }];
     test.run_scripts(scripts).await;
 
     let expected_row = test.last_row().unwrap();
-    let scripts = vec![AssertRow { expected_row }, AssertRowCount(6)];
+    let scripts = vec![AssertRow { expected_row }, AssertRowCount(row_count + 1)];
     test.run_scripts(scripts).await;
 }
 
@@ -45,20 +45,20 @@ async fn grid_delete_row() {
     let row_1 = test.row_builder().build();
     let row_2 = test.row_builder().build();
     let row_ids = vec![row_1.id.clone(), row_2.id.clone()];
+    let row_count = test.row_revs.len() as i32;
     let scripts = vec![
-        AssertRowCount(5),
         CreateRow { row_rev: row_1 },
         CreateRow { row_rev: row_2 },
         AssertBlockCount(1),
         AssertBlock {
             block_index: 0,
-            row_count: 7,
+            row_count: row_count + 2,
             start_row_index: 0,
         },
         DeleteRows { row_ids },
         AssertBlock {
             block_index: 0,
-            row_count: 5,
+            row_count,
             start_row_index: 0,
         },
     ];

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

@@ -113,7 +113,7 @@ impl GridRowTest {
                 expected,
             } => {
                 let id = CellPathParams {
-                    view_id: self.grid_id.clone(),
+                    view_id: self.view_id.clone(),
                     field_id,
                     row_id,
                 };

+ 6 - 4
frontend/rust-lib/flowy-grid/tests/grid/cell_test/script.rs

@@ -24,17 +24,19 @@ impl GridCellTest {
     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;
+        // 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_with_changeset(changeset).await;
+                let result = self
+                    .editor
+                    .update_cell_with_changeset(&changeset.row_id, &changeset.field_id, changeset.type_cell_data)
+                    .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;

+ 7 - 3
frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs

@@ -2,6 +2,7 @@ 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::{CellChangesetPB, FieldType};
+use flowy_grid::services::cell::ToCellChangesetString;
 use flowy_grid::services::field::selection_type_option::SelectOptionCellChangeset;
 use flowy_grid::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
 
@@ -25,15 +26,18 @@ async fn grid_cell_update() {
                 FieldType::DateTime => make_date_cell_string("123"),
                 FieldType::SingleSelect => {
                     let type_option = SingleSelectTypeOptionPB::from(field_rev);
-                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
+                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
+                        .to_cell_changeset_str()
                 }
                 FieldType::MultiSelect => {
                     let type_option = MultiSelectTypeOptionPB::from(field_rev);
-                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
+                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
+                        .to_cell_changeset_str()
                 }
                 FieldType::Checklist => {
                     let type_option = ChecklistTypeOptionPB::from(field_rev);
-                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str()
+                    SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id)
+                        .to_cell_changeset_str()
                 }
                 FieldType::Checkbox => "1".to_string(),
                 FieldType::URL => "1".to_string(),

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

@@ -49,7 +49,7 @@ impl GridFieldTest {
     }
 
     pub fn view_id(&self) -> String {
-        self.grid_id.clone()
+        self.view_id.clone()
     }
 
     pub fn field_count(&self) -> usize {
@@ -100,7 +100,7 @@ impl GridFieldTest {
             FieldScript::UpdateTypeOption { field_id, type_option } => {
                 //
                 self.editor
-                    .update_field_type_option(&self.grid_id, &field_id, type_option, None)
+                    .update_field_type_option(&self.view_id, &field_id, type_option, None)
                     .await
                     .unwrap();
                 self.field_revs = self.editor.get_field_revs(None).await.unwrap();

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

@@ -9,7 +9,7 @@ async fn grid_filter_checklist_is_incomplete_test() {
         CreateChecklistFilter {
             condition: ChecklistFilterConditionPB::IsIncomplete,
         },
-        AssertNumberOfVisibleRows { expected: 4 },
+        AssertNumberOfVisibleRows { expected: 5 },
     ];
     test.run_scripts(scripts).await;
 }

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

@@ -27,7 +27,7 @@ async fn grid_filter_date_after_test() {
             end: None,
             timestamp: Some(1647251762),
         },
-        AssertNumberOfVisibleRows { expected: 2 },
+        AssertNumberOfVisibleRows { expected: 3 },
     ];
     test.run_scripts(scripts).await;
 }
@@ -42,7 +42,7 @@ async fn grid_filter_date_on_or_after_test() {
             end: None,
             timestamp: Some(1668359085),
         },
-        AssertNumberOfVisibleRows { expected: 2 },
+        AssertNumberOfVisibleRows { expected: 3 },
     ];
     test.run_scripts(scripts).await;
 }

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

@@ -76,7 +76,7 @@ async fn grid_filter_number_is_not_empty_test() {
             condition: NumberFilterConditionPB::NumberIsNotEmpty,
             content: "".to_string(),
         },
-        AssertNumberOfVisibleRows { expected: 4 },
+        AssertNumberOfVisibleRows { expected: 5 },
     ];
     test.run_scripts(scripts).await;
 }

+ 12 - 42
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -19,11 +19,11 @@ use crate::grid::grid_editor::GridEditorTest;
 
 pub enum FilterScript {
     UpdateTextCell {
-        row_index: usize,
+        row_id: String,
         text: String,
     },
     UpdateSingleSelectCell {
-        row_index: usize,
+        row_id: String,
         option_id: String,
     },
     InsertFilter {
@@ -103,7 +103,7 @@ impl GridFilterTest {
     }
 
      pub fn view_id(&self) -> String {
-        self.grid_id.clone()
+        self.view_id.clone()
     }
 
     pub async fn get_all_filters(&self) -> Vec<FilterPB> {
@@ -118,13 +118,13 @@ impl GridFilterTest {
 
     pub async fn run_script(&mut self, script: FilterScript) {
         match script {
-            FilterScript::UpdateTextCell { row_index, text} => {
+            FilterScript::UpdateTextCell { row_id, text} => {
                 self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
-                self.update_text_cell(row_index, &text).await;
+                self.update_text_cell(row_id, &text).await;
             }
-            FilterScript::UpdateSingleSelectCell { row_index, option_id} => {
+            FilterScript::UpdateSingleSelectCell { row_id, option_id} => {
                 self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
-                self.update_single_select_cell(row_index, &option_id).await;
+                self.update_single_select_cell(row_id, &option_id).await;
             }
             FilterScript::InsertFilter { payload } => {
                 self.recv = Some(self.editor.subscribe_view_changed(&self.view_id()).await.unwrap());
@@ -241,13 +241,15 @@ impl GridFilterTest {
                     match tokio::time::timeout(Duration::from_secs(2), receiver.recv()).await {
                         Ok(changed) =>  {
                             //
-                            match changed.unwrap() { GridViewChanged::DidReceiveFilterResult(changed) => {
+                            match changed.unwrap() { GridViewChanged::FilterNotification(changed) => {
                                 assert_eq!(changed.visible_rows.len(), visible_row_len, "visible rows not match");
                                 assert_eq!(changed.invisible_rows.len(), hide_row_len, "invisible rows not match");
-                            } }
+                            }
+                                _ => {}
+                            }
                         },
                         Err(e) => {
-                            panic!("Process task timeout: {:?}", e);
+                            panic!("Process filter task timeout: {:?}", e);
                         }
                     }
                 }
@@ -268,38 +270,6 @@ impl GridFilterTest {
         let _ = self.editor.create_or_update_filter(params).await.unwrap();
     }
 
-    async fn update_text_cell(&self, row_index: usize, content: &str) {
-        let row_rev = &self.inner.row_revs[row_index];
-        let field_rev = self.inner.field_revs.iter().find(|field_rev| {
-            let field_type: FieldType = field_rev.ty.into();
-            field_type == FieldType::RichText
-        }).unwrap();
-        let changeset =CellChangesetPB {
-            grid_id: self.grid_id.clone(),
-            row_id: row_rev.id.clone(),
-            field_id: field_rev.id.clone(),
-            type_cell_data: content.to_string(),
-        };
-        self.editor.update_cell_with_changeset(changeset).await.unwrap();
-
-    }
-    async fn update_single_select_cell(&self, row_index: usize, option_id: &str) {
-        let row_rev = &self.inner.row_revs[row_index];
-        let field_rev = self.inner.field_revs.iter().find(|field_rev| {
-            let field_type: FieldType = field_rev.ty.into();
-            field_type == FieldType::SingleSelect
-        }).unwrap();
-
-        let content = SelectOptionCellChangeset::from_insert_option_id(&option_id).to_str();
-        let changeset =CellChangesetPB {
-            grid_id: self.grid_id.clone(),
-            row_id: row_rev.id.clone(),
-            field_id: field_rev.id.clone(),
-            type_cell_data: content,
-        };
-        self.editor.update_cell_with_changeset(changeset).await.unwrap();
-
-    }
 }
 
 

+ 4 - 3
frontend/rust-lib/flowy-grid/tests/grid/filter_test/select_option_filter_test.rs

@@ -10,7 +10,7 @@ async fn grid_filter_multi_select_is_empty_test() {
             condition: SelectOptionConditionPB::OptionIsEmpty,
             option_ids: vec![],
         },
-        AssertNumberOfVisibleRows { expected: 2 },
+        AssertNumberOfVisibleRows { expected: 3 },
     ];
     test.run_scripts(scripts).await;
 }
@@ -90,6 +90,7 @@ async fn grid_filter_single_select_is_test() {
 async fn grid_filter_single_select_is_test2() {
     let mut test = GridFilterTest::new().await;
     let field_rev = test.get_first_field_rev(FieldType::SingleSelect);
+    let row_revs = test.get_row_revs().await;
     let mut options = test.get_single_select_type_option(&field_rev.id).options;
     let option = options.remove(0);
     let scripts = vec![
@@ -99,12 +100,12 @@ async fn grid_filter_single_select_is_test2() {
         },
         AssertNumberOfVisibleRows { expected: 2 },
         UpdateSingleSelectCell {
-            row_index: 1,
+            row_id: row_revs[1].id.clone(),
             option_id: option.id.clone(),
         },
         AssertNumberOfVisibleRows { expected: 3 },
         UpdateSingleSelectCell {
-            row_index: 1,
+            row_id: row_revs[1].id.clone(),
             option_id: "".to_string(),
         },
         AssertFilterChanged {

+ 9 - 6
frontend/rust-lib/flowy-grid/tests/grid/filter_test/text_filter_test.rs

@@ -14,7 +14,7 @@ async fn grid_filter_text_is_empty_test() {
         AssertFilterCount { count: 1 },
         AssertFilterChanged {
             visible_row_len: 0,
-            hide_row_len: 4,
+            hide_row_len: 5,
         },
     ];
     test.run_scripts(scripts).await;
@@ -65,7 +65,7 @@ async fn grid_filter_is_text_test() {
         },
         AssertFilterChanged {
             visible_row_len: 0,
-            hide_row_len: 4,
+            hide_row_len: 5,
         },
     ];
     test.run_scripts(scripts).await;
@@ -90,6 +90,8 @@ async fn grid_filter_contain_text_test() {
 #[tokio::test]
 async fn grid_filter_contain_text_test2() {
     let mut test = GridFilterTest::new().await;
+    let row_revs = test.row_revs.clone();
+
     let scripts = vec![
         CreateTextFilter {
             condition: TextFilterConditionPB::Contains,
@@ -100,7 +102,7 @@ async fn grid_filter_contain_text_test2() {
             hide_row_len: 2,
         },
         UpdateTextCell {
-            row_index: 1,
+            row_id: row_revs[1].id.clone(),
             text: "ABC".to_string(),
         },
         AssertFilterChanged {
@@ -206,7 +208,7 @@ async fn grid_filter_delete_test() {
             filter_type: FilterType::from(&field_rev),
         },
         AssertFilterCount { count: 0 },
-        AssertNumberOfVisibleRows { expected: 5 },
+        AssertNumberOfVisibleRows { expected: 6 },
     ])
     .await;
 }
@@ -214,6 +216,7 @@ async fn grid_filter_delete_test() {
 #[tokio::test]
 async fn grid_filter_update_empty_text_cell_test() {
     let mut test = GridFilterTest::new().await;
+    let row_revs = test.row_revs.clone();
     let scripts = vec![
         CreateTextFilter {
             condition: TextFilterConditionPB::TextIsEmpty,
@@ -222,10 +225,10 @@ async fn grid_filter_update_empty_text_cell_test() {
         AssertFilterCount { count: 1 },
         AssertFilterChanged {
             visible_row_len: 0,
-            hide_row_len: 4,
+            hide_row_len: 5,
         },
         UpdateTextCell {
-            row_index: 0,
+            row_id: row_revs[0].id.clone(),
             text: "".to_string(),
         },
         AssertFilterChanged {

+ 63 - 6
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -5,6 +5,7 @@ use crate::grid::block_test::util::GridRowTestBuilder;
 use bytes::Bytes;
 use flowy_error::FlowyResult;
 use flowy_grid::entities::*;
+use flowy_grid::services::cell::ToCellChangesetString;
 use flowy_grid::services::field::SelectOptionPB;
 use flowy_grid::services::field::*;
 use flowy_grid::services::grid_editor::{GridRevisionEditor, GridRevisionSerde};
@@ -24,7 +25,7 @@ use tokio::time::sleep;
 
 pub struct GridEditorTest {
     pub sdk: FlowySDKTest,
-    pub grid_id: String,
+    pub view_id: String,
     pub editor: Arc<GridRevisionEditor>,
     pub field_revs: Vec<Arc<FieldRevision>>,
     pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
@@ -61,7 +62,7 @@ impl GridEditorTest {
         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.get_blocks(None).await.unwrap().pop().unwrap().row_revs;
+        let row_pbs = editor.get_all_row_revs(&test.view.id).await.unwrap();
         assert_eq!(block_meta_revs.len(), 1);
 
         // It seems like you should add the field in the make_test_grid() function.
@@ -71,18 +72,18 @@ impl GridEditorTest {
         let grid_id = test.view.id;
         Self {
             sdk,
-            grid_id,
+            view_id: grid_id,
             editor,
             field_revs,
             block_meta_revs,
-            row_revs,
+            row_revs: row_pbs,
             field_count: FieldType::COUNT,
             row_by_row_id: HashMap::default(),
         }
     }
 
     pub async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
-        self.editor.get_blocks(None).await.unwrap().pop().unwrap().row_revs
+        self.editor.get_all_row_revs(&self.view_id).await.unwrap()
     }
 
     pub async fn grid_filters(&self) -> Vec<FilterPB> {
@@ -153,6 +154,48 @@ impl GridEditorTest {
     pub fn block_id(&self) -> &str {
         &self.block_meta_revs.last().unwrap().block_id
     }
+
+    pub async fn update_cell<T: ToCellChangesetString>(&mut self, field_id: &str, row_id: String, cell_changeset: T) {
+        let field_rev = self
+            .field_revs
+            .iter()
+            .find(|field_rev| field_rev.id == field_id)
+            .unwrap();
+
+        self.editor
+            .update_cell_with_changeset(&row_id, &field_rev.id, cell_changeset)
+            .await
+            .unwrap();
+    }
+
+    pub(crate) async fn update_text_cell(&mut self, row_id: String, content: &str) {
+        let field_rev = self
+            .field_revs
+            .iter()
+            .find(|field_rev| {
+                let field_type: FieldType = field_rev.ty.into();
+                field_type == FieldType::RichText
+            })
+            .unwrap()
+            .clone();
+
+        self.update_cell(&field_rev.id, row_id, content.to_string()).await;
+    }
+
+    pub(crate) async fn update_single_select_cell(&mut self, row_id: String, option_id: &str) {
+        let field_rev = self
+            .field_revs
+            .iter()
+            .find(|field_rev| {
+                let field_type: FieldType = field_rev.ty.into();
+                field_type == FieldType::SingleSelect
+            })
+            .unwrap()
+            .clone();
+
+        let cell_changeset = SelectOptionCellChangeset::from_insert_option_id(&option_id);
+        self.update_cell(&field_rev.id, row_id, cell_changeset).await;
+    }
 }
 
 pub const GOOGLE: &str = "Google";
@@ -243,7 +286,7 @@ fn make_test_grid() -> BuildGridContext {
         }
     }
 
-    for i in 0..5 {
+    for i in 0..6 {
         let block_id = grid_builder.block_id().to_owned();
         let field_revs = grid_builder.field_revs();
         let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs);
@@ -321,6 +364,20 @@ fn make_test_grid() -> BuildGridContext {
                     };
                 }
             }
+            5 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("AE"),
+                        FieldType::Number => row_builder.insert_number_cell("5"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1671938394"),
+                        FieldType::SingleSelect => {
+                            row_builder.insert_single_select_cell(|mut options| options.remove(1))
+                        }
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
             _ => {}
         }
 

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

@@ -90,7 +90,7 @@ impl GridGroupTest {
                 let to_group = groups.get(to_group_index).unwrap();
                 let to_row = to_group.rows.get(to_row_index).unwrap();
                 let params = MoveGroupRowParams {
-                    view_id: self.inner.grid_id.clone(),
+                    view_id: self.inner.view_id.clone(),
                     from_row_id: from_row.id.clone(),
                     to_group_id: to_group.group_id.clone(),
                     to_row_id: Some(to_row.id.clone()),

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

@@ -43,7 +43,7 @@ impl GridSnapshotTest {
     }
 
     pub fn grid_id(&self) -> String {
-        self.grid_id.clone()
+        self.view_id.clone()
     }
 
     pub async fn grid_pad(&self) -> GridRevisionPad {

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

@@ -1,2 +1,3 @@
+mod multi_sort_test;
 mod script;
-mod sort_test;
+mod single_sort_test;

+ 46 - 0
frontend/rust-lib/flowy-grid/tests/grid/sort_test/multi_sort_test.rs

@@ -0,0 +1,46 @@
+use crate::grid::sort_test::script::GridSortTest;
+use crate::grid::sort_test::script::SortScript::*;
+use flowy_grid::entities::FieldType;
+use grid_rev_model::SortCondition;
+
+#[tokio::test]
+async fn sort_text_with_checkbox_by_ascending_test() {
+    let mut test = GridSortTest::new().await;
+    let text_field = test.get_first_field_rev(FieldType::RichText).clone();
+    let checkbox_field = test.get_first_field_rev(FieldType::Checkbox).clone();
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["A", "", "C", "DA", "AE", "AE"],
+        },
+        AssertCellContentOrder {
+            field_id: checkbox_field.id.clone(),
+            orders: vec!["Yes", "Yes", "No", "No", "No"],
+        },
+        InsertSort {
+            field_rev: text_field.clone(),
+            condition: SortCondition::Ascending,
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["", "A", "AE", "AE", "C", "DA"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+
+    let scripts = vec![
+        InsertSort {
+            field_rev: checkbox_field.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["", "A", "AE", "AE", "C", "DA"],
+        },
+        AssertCellContentOrder {
+            field_id: checkbox_field.id.clone(),
+            orders: vec!["Yes", "Yes", "Yes", "No", "No"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}

+ 103 - 11
frontend/rust-lib/flowy-grid/tests/grid/sort_test/script.rs

@@ -1,23 +1,45 @@
 use crate::grid::grid_editor::GridEditorTest;
+use async_stream::stream;
 use flowy_grid::entities::{AlterSortParams, CellPathParams, DeleteSortParams};
-use grid_rev_model::SortRevision;
+use flowy_grid::services::sort::SortType;
+use flowy_grid::services::view_editor::GridViewChanged;
+use futures::stream::StreamExt;
+use grid_rev_model::{FieldRevision, SortCondition, SortRevision};
+use std::cmp::min;
+use std::sync::Arc;
+use std::time::Duration;
+use tokio::sync::broadcast::Receiver;
 
 pub enum SortScript {
     InsertSort {
-        params: AlterSortParams,
+        field_rev: Arc<FieldRevision>,
+        condition: SortCondition,
     },
     DeleteSort {
-        params: DeleteSortParams,
+        field_rev: Arc<FieldRevision>,
+        sort_id: String,
     },
-    AssertTextOrder {
+    AssertCellContentOrder {
         field_id: String,
         orders: Vec<&'static str>,
     },
+    UpdateTextCell {
+        row_id: String,
+        text: String,
+    },
+    AssertSortChanged {
+        old_row_orders: Vec<&'static str>,
+        new_row_orders: Vec<&'static str>,
+    },
+    Wait {
+        millis: u64,
+    },
 }
 
 pub struct GridSortTest {
     inner: GridEditorTest,
     pub current_sort_rev: Option<SortRevision>,
+    recv: Option<Receiver<GridViewChanged>>,
 }
 
 impl GridSortTest {
@@ -26,6 +48,7 @@ impl GridSortTest {
         Self {
             inner: editor_test,
             current_sort_rev: None,
+            recv: None,
         }
     }
     pub async fn run_scripts(&mut self, scripts: Vec<SortScript>) {
@@ -36,32 +59,101 @@ impl GridSortTest {
 
     pub async fn run_script(&mut self, script: SortScript) {
         match script {
-            SortScript::InsertSort { params } => {
+            SortScript::InsertSort { condition, field_rev } => {
+                self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
+                let params = AlterSortParams {
+                    view_id: self.view_id.clone(),
+                    field_id: field_rev.id.clone(),
+                    sort_id: None,
+                    field_type: field_rev.ty,
+                    condition: condition.into(),
+                };
                 let sort_rev = self.editor.create_or_update_sort(params).await.unwrap();
                 self.current_sort_rev = Some(sort_rev);
             }
-            SortScript::DeleteSort { params } => {
-                //
+            SortScript::DeleteSort { field_rev, sort_id } => {
+                self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
+                let params = DeleteSortParams {
+                    view_id: self.view_id.clone(),
+                    sort_type: SortType::from(&field_rev),
+                    sort_id,
+                };
                 self.editor.delete_sort(params).await.unwrap();
+                self.current_sort_rev = None;
             }
-            SortScript::AssertTextOrder { field_id, orders } => {
+            SortScript::AssertCellContentOrder { field_id, orders } => {
                 let mut cells = vec![];
-                let rows = self.editor.get_grid(&self.grid_id).await.unwrap().rows;
+                let rows = self.editor.get_grid(&self.view_id).await.unwrap().rows;
                 for row in rows {
                     let params = CellPathParams {
-                        view_id: self.grid_id.clone(),
+                        view_id: self.view_id.clone(),
                         field_id: field_id.clone(),
                         row_id: row.id,
                     };
                     let cell = self.editor.get_cell_display_str(&params).await;
                     cells.push(cell);
                 }
-                assert_eq!(cells, orders)
+                if orders.is_empty() {
+                    assert_eq!(cells, orders);
+                } else {
+                    let len = min(cells.len(), orders.len());
+                    assert_eq!(cells.split_at(len).0, orders);
+                }
+            }
+            SortScript::UpdateTextCell { row_id, text } => {
+                self.recv = Some(self.editor.subscribe_view_changed(&self.view_id).await.unwrap());
+                self.update_text_cell(row_id, &text).await;
+            }
+            SortScript::AssertSortChanged {
+                new_row_orders,
+                old_row_orders,
+            } => {
+                if let Some(receiver) = self.recv.take() {
+                    assert_sort_changed(
+                        receiver,
+                        new_row_orders.into_iter().map(|order| order.to_owned()).collect(),
+                        old_row_orders.into_iter().map(|order| order.to_owned()).collect(),
+                    )
+                    .await;
+                }
+            }
+            SortScript::Wait { millis } => {
+                tokio::time::sleep(Duration::from_millis(millis)).await;
             }
         }
     }
 }
 
+async fn assert_sort_changed(
+    mut receiver: Receiver<GridViewChanged>,
+    new_row_orders: Vec<String>,
+    old_row_orders: Vec<String>,
+) {
+    let stream = stream! {
+       loop {
+        tokio::select! {
+            changed = receiver.recv() => yield changed.unwrap(),
+            _ = tokio::time::sleep(Duration::from_secs(2)) => break,
+        };
+        }
+    };
+
+    stream
+        .for_each(|changed| async {
+            match changed {
+                GridViewChanged::ReorderAllRowsNotification(_changed) => {}
+                GridViewChanged::ReorderSingleRowNotification(changed) => {
+                    let mut old_row_orders = old_row_orders.clone();
+                    let old = old_row_orders.remove(changed.old_index);
+                    old_row_orders.insert(changed.new_index, old);
+                    assert_eq!(old_row_orders, new_row_orders);
+                }
+                _ => {}
+            }
+        })
+        .await;
+}
+
 impl std::ops::Deref for GridSortTest {
     type Target = GridEditorTest;
 

+ 257 - 0
frontend/rust-lib/flowy-grid/tests/grid/sort_test/single_sort_test.rs

@@ -0,0 +1,257 @@
+use crate::grid::sort_test::script::{GridSortTest, SortScript::*};
+use flowy_grid::entities::FieldType;
+use grid_rev_model::SortCondition;
+
+#[tokio::test]
+async fn sort_text_by_ascending_test() {
+    let mut test = GridSortTest::new().await;
+    let text_field = test.get_first_field_rev(FieldType::RichText);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["A", "", "C", "DA", "AE", "AE"],
+        },
+        InsertSort {
+            field_rev: text_field.clone(),
+            condition: SortCondition::Ascending,
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["", "A", "AE", "AE", "C", "DA"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_change_notification_by_update_text_test() {
+    let mut test = GridSortTest::new().await;
+    let text_field = test.get_first_field_rev(FieldType::RichText).clone();
+    let scripts = vec![
+        InsertSort {
+            field_rev: text_field.clone(),
+            condition: SortCondition::Ascending,
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["", "A", "AE", "AE", "C", "DA"],
+        },
+        // Wait the insert task to finish. The cost of time should be less than 200 milliseconds.
+        Wait { millis: 200 },
+    ];
+    test.run_scripts(scripts).await;
+
+    let row_revs = test.get_row_revs().await;
+    let scripts = vec![
+        UpdateTextCell {
+            row_id: row_revs[2].id.clone(),
+            text: "E".to_string(),
+        },
+        AssertSortChanged {
+            old_row_orders: vec!["", "A", "E", "AE", "C", "DA"],
+            new_row_orders: vec!["", "A", "AE", "C", "DA", "E"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_text_by_ascending_and_delete_sort_test() {
+    let mut test = GridSortTest::new().await;
+    let text_field = test.get_first_field_rev(FieldType::RichText).clone();
+    let scripts = vec![InsertSort {
+        field_rev: text_field.clone(),
+        condition: SortCondition::Ascending,
+    }];
+    test.run_scripts(scripts).await;
+    let sort_rev = test.current_sort_rev.as_ref().unwrap();
+    let scripts = vec![
+        DeleteSort {
+            field_rev: text_field.clone(),
+            sort_id: sort_rev.id.clone(),
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["A", "", "C", "DA", "AE"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_text_by_descending_test() {
+    let mut test = GridSortTest::new().await;
+    let text_field = test.get_first_field_rev(FieldType::RichText);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["A", "", "C", "DA", "AE", "AE"],
+        },
+        InsertSort {
+            field_rev: text_field.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: text_field.id.clone(),
+            orders: vec!["DA", "C", "AE", "AE", "A", ""],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_checkbox_by_ascending_test() {
+    let mut test = GridSortTest::new().await;
+    let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: checkbox_field.id.clone(),
+            orders: vec!["Yes", "Yes", "No", "No", "No"],
+        },
+        InsertSort {
+            field_rev: checkbox_field.clone(),
+            condition: SortCondition::Ascending,
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_checkbox_by_descending_test() {
+    let mut test = GridSortTest::new().await;
+    let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: checkbox_field.id.clone(),
+            orders: vec!["Yes", "Yes", "No", "No", "No", "Yes"],
+        },
+        InsertSort {
+            field_rev: checkbox_field.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: checkbox_field.id.clone(),
+            orders: vec!["Yes", "Yes", "Yes", "No", "No", "No"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_date_by_ascending_test() {
+    let mut test = GridSortTest::new().await;
+    let date_field = test.get_first_field_rev(FieldType::DateTime);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: date_field.id.clone(),
+            orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
+        },
+        InsertSort {
+            field_rev: date_field.clone(),
+            condition: SortCondition::Ascending,
+        },
+        AssertCellContentOrder {
+            field_id: date_field.id.clone(),
+            orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/13", "2022/11/17"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_date_by_descending_test() {
+    let mut test = GridSortTest::new().await;
+    let date_field = test.get_first_field_rev(FieldType::DateTime);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: date_field.id.clone(),
+            orders: vec![
+                "2022/03/14",
+                "2022/03/14",
+                "2022/03/14",
+                "2022/11/17",
+                "2022/11/13",
+                "2022/12/25",
+            ],
+        },
+        InsertSort {
+            field_rev: date_field.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: date_field.id.clone(),
+            orders: vec![
+                "2022/12/25",
+                "2022/11/17",
+                "2022/11/13",
+                "2022/03/14",
+                "2022/03/14",
+                "2022/03/14",
+            ],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_number_by_descending_test() {
+    let mut test = GridSortTest::new().await;
+    let number_field = test.get_first_field_rev(FieldType::Number);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: number_field.id.clone(),
+            orders: vec!["$1", "$2", "$3", "$4", "", "$5"],
+        },
+        InsertSort {
+            field_rev: number_field.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: number_field.id.clone(),
+            orders: vec!["$5", "$4", "$3", "$2", "$1", ""],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_single_select_by_descending_test() {
+    let mut test = GridSortTest::new().await;
+    let single_select = test.get_first_field_rev(FieldType::SingleSelect);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: single_select.id.clone(),
+            orders: vec!["", "", "Completed", "Completed", "Planned", "Planned"],
+        },
+        InsertSort {
+            field_rev: single_select.clone(),
+            condition: SortCondition::Descending,
+        },
+        AssertCellContentOrder {
+            field_id: single_select.id.clone(),
+            orders: vec!["Planned", "Planned", "Completed", "Completed", "", ""],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn sort_multi_select_by_ascending_test() {
+    let mut test = GridSortTest::new().await;
+    let multi_select = test.get_first_field_rev(FieldType::MultiSelect);
+    let scripts = vec![
+        AssertCellContentOrder {
+            field_id: multi_select.id.clone(),
+            orders: vec!["Google,Facebook", "Google,Twitter", "Facebook", "", "", ""],
+        },
+        InsertSort {
+            field_rev: multi_select.clone(),
+            condition: SortCondition::Ascending,
+        },
+        AssertCellContentOrder {
+            field_id: multi_select.id.clone(),
+            orders: vec!["", "", "", "Facebook", "Google,Facebook", "Google,Twitter"],
+        },
+    ];
+    test.run_scripts(scripts).await;
+}

+ 0 - 279
frontend/rust-lib/flowy-grid/tests/grid/sort_test/sort_test.rs

@@ -1,279 +0,0 @@
-use crate::grid::sort_test::script::{GridSortTest, SortScript::*};
-use flowy_grid::entities::{AlterSortParams, DeleteSortParams, FieldType};
-use flowy_grid::services::sort::SortType;
-use grid_rev_model::SortCondition;
-
-#[tokio::test]
-async fn sort_text_by_ascending_test() {
-    let mut test = GridSortTest::new().await;
-    let text_field = test.get_first_field_rev(FieldType::RichText);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: text_field.id.clone(),
-            orders: vec!["A", "", "C", "DA", "AE"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: text_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::RichText.into(),
-                condition: SortCondition::Ascending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: text_field.id.clone(),
-            orders: vec!["", "A", "AE", "C", "DA"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_text_by_ascending_and_delete_sort_test() {
-    let mut test = GridSortTest::new().await;
-    let text_field = test.get_first_field_rev(FieldType::RichText).clone();
-    let view_id = test.grid_id.clone();
-    let scripts = vec![InsertSort {
-        params: AlterSortParams {
-            view_id: view_id.clone(),
-            field_id: text_field.id.clone(),
-            sort_id: None,
-            field_type: FieldType::RichText.into(),
-            condition: SortCondition::Ascending.into(),
-        },
-    }];
-    test.run_scripts(scripts).await;
-    let sort_rev = test.current_sort_rev.as_ref().unwrap();
-    let scripts = vec![
-        DeleteSort {
-            params: DeleteSortParams {
-                view_id,
-                sort_type: SortType::from(&text_field),
-                sort_id: sort_rev.id.clone(),
-            },
-        },
-        AssertTextOrder {
-            field_id: text_field.id.clone(),
-            orders: vec!["A", "", "C", "DA", "AE"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_text_by_descending_test() {
-    let mut test = GridSortTest::new().await;
-    let text_field = test.get_first_field_rev(FieldType::RichText);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: text_field.id.clone(),
-            orders: vec!["A", "", "C", "DA", "AE"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: text_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::RichText.into(),
-                condition: SortCondition::Descending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: text_field.id.clone(),
-            orders: vec!["DA", "C", "AE", "A", ""],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_checkbox_by_ascending_test() {
-    let mut test = GridSortTest::new().await;
-    let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: checkbox_field.id.clone(),
-            orders: vec!["Yes", "Yes", "No", "No", "No"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: checkbox_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::Checkbox.into(),
-                condition: SortCondition::Ascending.into(),
-            },
-        },
-        // AssertTextOrder {
-        //     field_id: checkbox_field.id.clone(),
-        //     orders: vec!["No", "No", "No", "Yes", "Yes"],
-        // },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_checkbox_by_descending_test() {
-    let mut test = GridSortTest::new().await;
-    let checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: checkbox_field.id.clone(),
-            orders: vec!["Yes", "Yes", "No", "No", "No"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: checkbox_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::Checkbox.into(),
-                condition: SortCondition::Descending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: checkbox_field.id.clone(),
-            orders: vec!["Yes", "Yes", "No", "No", "No"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_date_by_ascending_test() {
-    let mut test = GridSortTest::new().await;
-    let date_field = test.get_first_field_rev(FieldType::DateTime);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: date_field.id.clone(),
-            orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: date_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::DateTime.into(),
-                condition: SortCondition::Ascending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: date_field.id.clone(),
-            orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/13", "2022/11/17"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_date_by_descending_test() {
-    let mut test = GridSortTest::new().await;
-    let date_field = test.get_first_field_rev(FieldType::DateTime);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: date_field.id.clone(),
-            orders: vec!["2022/03/14", "2022/03/14", "2022/03/14", "2022/11/17", "2022/11/13"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: date_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::DateTime.into(),
-                condition: SortCondition::Descending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: date_field.id.clone(),
-            orders: vec!["2022/11/17", "2022/11/13", "2022/03/14", "2022/03/14", "2022/03/14"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_number_by_descending_test() {
-    let mut test = GridSortTest::new().await;
-    let number_field = test.get_first_field_rev(FieldType::Number);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: number_field.id.clone(),
-            orders: vec!["$1", "$2", "$3", "$4", ""],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: number_field.id.clone(),
-                sort_id: None,
-                field_type: FieldType::Number.into(),
-                condition: SortCondition::Descending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: number_field.id.clone(),
-            orders: vec!["$4", "$3", "$2", "$1", ""],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_single_select_by_descending_test() {
-    let mut test = GridSortTest::new().await;
-    let single_select = test.get_first_field_rev(FieldType::SingleSelect);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: single_select.id.clone(),
-            orders: vec!["", "", "Completed", "Completed", "Planned"],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: single_select.id.clone(),
-                sort_id: None,
-                field_type: FieldType::SingleSelect.into(),
-                condition: SortCondition::Descending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: single_select.id.clone(),
-            orders: vec!["Planned", "Completed", "Completed", "", ""],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}
-
-#[tokio::test]
-async fn sort_multi_select_by_ascending_test() {
-    let mut test = GridSortTest::new().await;
-    let multi_select = test.get_first_field_rev(FieldType::MultiSelect);
-    let view_id = test.grid_id.clone();
-    let scripts = vec![
-        AssertTextOrder {
-            field_id: multi_select.id.clone(),
-            orders: vec!["Google,Facebook", "Google,Twitter", "Facebook", "", ""],
-        },
-        InsertSort {
-            params: AlterSortParams {
-                view_id,
-                field_id: multi_select.id.clone(),
-                sort_id: None,
-                field_type: FieldType::MultiSelect.into(),
-                condition: SortCondition::Ascending.into(),
-            },
-        },
-        AssertTextOrder {
-            field_id: multi_select.id.clone(),
-            orders: vec!["", "", "Facebook", "Google,Facebook", "Google,Twitter"],
-        },
-    ];
-    test.run_scripts(scripts).await;
-}

+ 14 - 2
frontend/rust-lib/flowy-task/src/scheduler.rs

@@ -71,12 +71,12 @@ impl TaskDispatcher {
                 Ok(result) => match result {
                     Ok(_) => task.set_state(TaskState::Done),
                     Err(e) => {
-                        tracing::error!("Process {} task failed: {:?}", handler.handler_id(), e);
+                        tracing::error!("Process {} task failed: {:?}", handler.handler_name(), e);
                         task.set_state(TaskState::Failure);
                     }
                 },
                 Err(e) => {
-                    tracing::error!("Process {} task timeout: {:?}", handler.handler_id(), e);
+                    tracing::error!("Process {} task timeout: {:?}", handler.handler_name(), e);
                     task.set_state(TaskState::Timeout);
                 }
             }
@@ -144,6 +144,10 @@ impl TaskRunner {
 pub trait TaskHandler: Send + Sync + 'static {
     fn handler_id(&self) -> &str;
 
+    fn handler_name(&self) -> &str {
+        ""
+    }
+
     fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error>;
 }
 
@@ -155,6 +159,10 @@ where
         (**self).handler_id()
     }
 
+    fn handler_name(&self) -> &str {
+        (**self).handler_name()
+    }
+
     fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> {
         (**self).run(content)
     }
@@ -168,6 +176,10 @@ where
         (**self).handler_id()
     }
 
+    fn handler_name(&self) -> &str {
+        (**self).handler_name()
+    }
+
     fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> {
         (**self).run(content)
     }