diff --git a/frontend/rust-lib/flowy-grid/Cargo.toml b/frontend/rust-lib/flowy-grid/Cargo.toml index ba3702038e..e09e156772 100644 --- a/frontend/rust-lib/flowy-grid/Cargo.toml +++ b/frontend/rust-lib/flowy-grid/Cargo.toml @@ -50,6 +50,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file [features] -default = [] +default = ["filter"] dart = ["lib-infra/dart"] +filter = [] flowy_unit_test = ["flowy-revision/flowy_unit_test"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs index 6df4c03f7d..eed2fa56bd 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs @@ -1,5 +1,7 @@ +use crate::entities::FieldType; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; +use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::revision::GridFilterRevision; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -17,6 +19,53 @@ pub struct GridDateFilter { pub end: Option, } +#[derive(ProtoBuf, Default, Clone, Debug)] +pub struct CreateGridDateFilterPayload { + #[pb(index = 1)] + pub field_id: String, + + #[pb(index = 2)] + pub field_type: FieldType, + + #[pb(index = 3)] + pub condition: DateFilterCondition, + + #[pb(index = 4, one_of)] + pub start: Option, + + #[pb(index = 5, one_of)] + pub end: Option, +} + +pub struct CreateGridDateFilterParams { + pub field_id: String, + + pub field_type: FieldType, + + pub condition: DateFilterCondition, + + pub start: Option, + + pub end: Option, +} + +impl TryInto for CreateGridDateFilterPayload { + type Error = ErrorCode; + + fn try_into(self) -> Result { + let field_id = NotEmptyStr::parse(self.field_id) + .map_err(|_| ErrorCode::FieldIdIsEmpty)? + .0; + Ok(CreateGridDateFilterParams { + field_id, + condition: self.condition, + start: self.start, + field_type: self.field_type, + end: self.end, + }) + } +} + #[derive(Serialize, Deserialize, Default)] struct DateRange { start: Option, diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs b/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs index 5dcd16e448..a1f6d4cbbf 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs @@ -8,10 +8,12 @@ use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; +type RowId = String; + #[derive(Default)] pub(crate) struct FilterResultCache { // key: row id - inner: DashMap, + inner: DashMap, } impl FilterResultCache { @@ -67,7 +69,7 @@ pub(crate) struct FilterCache { impl FilterCache { pub(crate) async fn from_grid_pad(grid_pad: &Arc>) -> Arc { let this = Arc::new(Self::default()); - let _ = reload_filter_cache(this.clone(), None, grid_pad).await; + let _ = refresh_filter_cache(this.clone(), None, grid_pad).await; this } @@ -98,7 +100,8 @@ impl FilterCache { } } -pub(crate) async fn reload_filter_cache( +/// Refresh the filter according to the field id. +pub(crate) async fn refresh_filter_cache( cache: Arc, field_ids: Option>, grid_pad: &Arc>, diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs index 261b5c251f..6442dd4710 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs @@ -7,7 +7,7 @@ use crate::services::field::{ SingleSelectTypeOption, URLTypeOption, }; use crate::services::filter::filter_cache::{ - reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache, + refresh_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache, }; use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::row::GridBlockSnapshot; @@ -62,6 +62,7 @@ impl GridFilterService { let mut changesets = vec![]; for (index, block) in task_context.blocks.into_iter().enumerate() { + // The row_ids contains the row that its visibility was changed. let row_ids = block .row_revs .par_iter() @@ -74,6 +75,8 @@ impl GridFilterService { let mut visible_rows = vec![]; let mut hide_rows = vec![]; + + // Query the filter result from the cache for row_id in row_ids { if self .filter_result_cache @@ -93,8 +96,11 @@ impl GridFilterService { visible_rows, ..Default::default() }; + + // Save the changeset for each block changesets.push(changeset); } + self.notify(changesets).await; Ok(()) } @@ -106,7 +112,7 @@ impl GridFilterService { if let Some(filter_id) = &changeset.insert_filter { let field_ids = Some(vec![filter_id.field_id.clone()]); - reload_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await; + refresh_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await; } if let Some(filter_id) = &changeset.delete_filter { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs new file mode 100644 index 0000000000..63d424afaf --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs @@ -0,0 +1,2 @@ +mod script; +mod test; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs new file mode 100644 index 0000000000..c63ebc0d9c --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs @@ -0,0 +1,91 @@ +#![cfg_attr(rustfmt, rustfmt::skip)] +#![allow(clippy::all)] +#![allow(dead_code)] +#![allow(unused_imports)] + +use flowy_grid::entities::{CreateGridFilterPayload, GridLayoutType, GridSetting}; +use flowy_grid::services::setting::GridSettingChangesetBuilder; +use flowy_grid_data_model::revision::FieldTypeRevision; +use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams}; +use crate::grid::script::GridEditorTest; + +pub enum FilterScript { + #[allow(dead_code)] + UpdateGridSetting { + params: GridSettingChangesetParams, + }, + InsertGridTableFilter { + payload: CreateGridFilterPayload, + }, + AssertTableFilterCount { + count: i32, + }, + DeleteGridTableFilter { + filter_id: String, + field_type_rev: FieldTypeRevision, + }, + #[allow(dead_code)] + AssertGridSetting { + expected_setting: GridSetting, + }, +} + +pub struct GridFilterTest { + pub editor_test: GridEditorTest, +} + +impl GridFilterTest { + pub async fn new() -> Self { + let editor_test = GridEditorTest::new().await; + Self { + editor_test + } + } + + pub async fn run_scripts(&mut self, scripts: Vec) { + for script in scripts { + self.run_script(script).await; + } + } + + pub async fn run_script(&mut self, script: FilterScript) { + + match script { + FilterScript::UpdateGridSetting { params } => { + let _ = self.editor.update_grid_setting(params).await.unwrap(); + } + FilterScript::InsertGridTableFilter { payload } => { + let params: CreateGridFilterParams = payload.try_into().unwrap(); + let layout_type = GridLayoutType::Table; + let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) + .insert_filter(params) + .build(); + let _ = self.editor.update_grid_setting(params).await.unwrap(); + } + FilterScript::AssertTableFilterCount { count } => { + let layout_type = GridLayoutType::Table; + let filters = self.editor.get_grid_filter(&layout_type).await.unwrap(); + assert_eq!(count as usize, filters.len()); + } + FilterScript::DeleteGridTableFilter { filter_id ,field_type_rev} => { + let layout_type = GridLayoutType::Table; + let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) + .delete_filter(DeleteFilterParams { filter_id, field_type_rev }) + .build(); + let _ = self.editor.update_grid_setting(params).await.unwrap(); + } + FilterScript::AssertGridSetting { expected_setting } => { + let setting = self.editor.get_grid_setting().await.unwrap(); + assert_eq!(expected_setting, setting); + } + } + } +} + +impl std::ops::Deref for GridFilterTest { + type Target = GridEditorTest; + + fn deref(&self) -> &Self::Target { + &self.editor_test + } +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/test.rs similarity index 54% rename from frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs rename to frontend/rust-lib/flowy-grid/tests/grid/filter_test/test.rs index aad4cb1e7d..34e1ef69b0 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/test.rs @@ -1,41 +1,41 @@ -use crate::grid::script::EditorScript::*; -use crate::grid::script::*; -use flowy_grid::entities::CreateGridFilterPayload; +use crate::grid::filter_test::script::FilterScript::*; +use crate::grid::filter_test::script::*; +use flowy_grid::entities::{CreateGridFilterPayload, TextFilterCondition}; +use flowy_grid_data_model::revision::FieldRevision; #[tokio::test] async fn grid_filter_create_test() { - let test = GridEditorTest::new().await; + let mut test = GridFilterTest::new().await; let field_rev = test.text_field(); let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned())); let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }]; - GridEditorTest::new().await.run_scripts(scripts).await; + test.run_scripts(scripts).await; } #[tokio::test] #[should_panic] async fn grid_filter_invalid_condition_panic_test() { - let test = GridEditorTest::new().await; - let field_rev = test.text_field(); + let mut test = GridFilterTest::new().await; // 100 is not a valid condition, so this test should be panic. - let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned())); + let payload = create_filter(&test, "abc"); let scripts = vec![InsertGridTableFilter { payload }]; - GridEditorTest::new().await.run_scripts(scripts).await; + test.run_scripts(scripts).await; } #[tokio::test] async fn grid_filter_delete_test() { - let mut test = GridEditorTest::new().await; - let field_rev = test.text_field().clone(); - let payload = CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned())); + let mut test = GridFilterTest::new().await; + let payload = create_filter(&test, "abc"); let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }]; test.run_scripts(scripts).await; let filter = test.grid_filters().await.pop().unwrap(); + test.run_scripts(vec![ DeleteGridTableFilter { filter_id: filter.id, - field_type: field_rev.field_type.clone(), + field_type_rev: field_rev.field_type_rev.clone(), }, AssertTableFilterCount { count: 0 }, ]) @@ -44,3 +44,8 @@ async fn grid_filter_delete_test() { #[tokio::test] async fn grid_filter_get_rows_test() {} + +fn create_filter(grid_filter_test: &GridFilterTest, s: &str) -> CreateGridFilterPayload { + let field_rev = grid_filter_test.text_field(); + CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some(s.to_owned())) +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/mod.rs b/frontend/rust-lib/flowy-grid/tests/grid/mod.rs index 38cdb25b99..4d746661eb 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/mod.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/mod.rs @@ -2,7 +2,7 @@ mod block_test; mod cell_test; mod field_test; mod field_util; -// mod filter_test; +mod filter_test; mod row_test; mod row_util; mod script; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/script.rs index e0ec211ba3..f25e859450 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/script.rs @@ -82,7 +82,7 @@ pub enum EditorScript { }, DeleteGridTableFilter { filter_id: String, - field_type: FieldType, + field_type_rev: FieldTypeRevision, }, #[allow(dead_code)] AssertGridSetting { @@ -271,10 +271,10 @@ impl GridEditorTest { let filters = self.editor.get_grid_filter(&layout_type).await.unwrap(); assert_eq!(count as usize, filters.len()); } - EditorScript::DeleteGridTableFilter { filter_id ,field_type} => { + EditorScript::DeleteGridTableFilter { filter_id ,field_type_rev} => { let layout_type = GridLayoutType::Table; let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) - .delete_filter(DeleteFilterParams { filter_id, field_type_rev: field_type.into() }) + .delete_filter(DeleteFilterParams { filter_id, field_type_rev }) .build(); let _ = self.editor.update_grid_setting(params).await.unwrap(); } diff --git a/frontend/rust-lib/lib-dispatch/src/request/payload.rs b/frontend/rust-lib/lib-dispatch/src/request/payload.rs index c5d67e8413..59edcf79d2 100644 --- a/frontend/rust-lib/lib-dispatch/src/request/payload.rs +++ b/frontend/rust-lib/lib-dispatch/src/request/payload.rs @@ -5,7 +5,7 @@ pub enum PayloadError {} // TODO: support stream data #[derive(Clone)] -#[cfg_attr(feature = "user_serde", derive(serde::Serialize))] +#[cfg_attr(feature = "use_serde", derive(serde::Serialize))] pub enum Payload { None, Bytes(Bytes), diff --git a/frontend/rust-lib/lib-dispatch/src/response/response.rs b/frontend/rust-lib/lib-dispatch/src/response/response.rs index cfad0e6f85..a745f8ee1a 100644 --- a/frontend/rust-lib/lib-dispatch/src/response/response.rs +++ b/frontend/rust-lib/lib-dispatch/src/response/response.rs @@ -9,7 +9,7 @@ use derivative::*; use std::{convert::TryFrom, fmt, fmt::Formatter}; #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "user_serde", derive(serde::Serialize))] +#[cfg_attr(feature = "use_serde", derive(serde::Serialize))] pub enum StatusCode { Ok = 0, Err = 1, @@ -18,7 +18,7 @@ pub enum StatusCode { // serde user guide: https://serde.rs/field-attrs.html #[derive(Debug, Clone, Derivative)] -#[cfg_attr(feature = "user_serde", derive(serde::Serialize))] +#[cfg_attr(feature = "use_serde", derive(serde::Serialize))] pub struct EventResponse { #[derivative(Debug = "ignore")] pub payload: Payload, diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs index 1350f6acf5..edceb25d9c 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs @@ -31,6 +31,11 @@ pub struct GridRevision { pub fields: Vec>, pub blocks: Vec>, + #[cfg(feature = "filter")] + #[serde(default)] + pub setting: GridSettingRevision, + + #[cfg(not(feature = "filter"))] #[serde(default, skip)] pub setting: GridSettingRevision, } diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs index 3ffcedb1c4..7bd18024ca 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs @@ -56,12 +56,12 @@ impl GridSettingRevision { &self, layout: &GridLayoutRevision, field_id: &str, - field_type: &FieldTypeRevision, + field_type_rev: &FieldTypeRevision, ) -> Option>> { self.filters .get(layout) .and_then(|filter_rev_map_by_field_id| filter_rev_map_by_field_id.get(field_id)) - .and_then(|filter_rev_map| filter_rev_map.get(field_type)) + .and_then(|filter_rev_map| filter_rev_map.get(field_type_rev)) .cloned() } diff --git a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs index 8b724d8133..839102c1fb 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs @@ -17,8 +17,8 @@ pub type GridRevisionDelta = PlainTextDelta; pub type GridRevisionDeltaBuilder = PlainTextDeltaBuilder; pub struct GridRevisionPad { - pub(crate) grid_rev: Arc, - pub(crate) delta: GridRevisionDelta, + grid_rev: Arc, + delta: GridRevisionDelta, } pub trait JsonDeserializer { @@ -358,10 +358,9 @@ impl GridRevisionPad { if is_contain { // Only return the filters for the current fields' type. - if let Some(mut t_filter_revs) = - self.grid_rev - .setting - .get_filters(layout_ty, &field_rev.id, &field_rev.field_type_rev) + let field_id = &field_rev.id; + let field_type_rev = &field_rev.field_type_rev; + if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(layout_ty, field_id, &field_type_rev) { filter_revs.append(&mut t_filter_revs); }