chore: cache filter result

This commit is contained in:
appflowy
2022-06-30 23:00:03 +08:00
parent 4d8f101a42
commit 50d0eff039
23 changed files with 622 additions and 204 deletions

View File

@ -9,7 +9,7 @@ use flowy_grid_data_model::entities::{
CellChangeset, GridRowsChangeset, IndexRowOrder, Row, RowOrder, UpdatedRowOrder, CellChangeset, GridRowsChangeset, IndexRowOrder, Row, RowOrder, UpdatedRowOrder,
}; };
use flowy_grid_data_model::revision::{ use flowy_grid_data_model::revision::{
CellRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
}; };
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence; use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
use flowy_revision::{RevisionManager, RevisionPersistence}; use flowy_revision::{RevisionManager, RevisionPersistence};
@ -23,7 +23,7 @@ pub(crate) struct GridBlockManager {
grid_id: String, grid_id: String,
user: Arc<dyn GridUser>, user: Arc<dyn GridUser>,
persistence: Arc<BlockIndexCache>, persistence: Arc<BlockIndexCache>,
block_editor_map: DashMap<BlockId, Arc<GridBlockRevisionEditor>>, block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
} }
impl GridBlockManager { impl GridBlockManager {
@ -33,13 +33,13 @@ impl GridBlockManager {
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>, block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
persistence: Arc<BlockIndexCache>, persistence: Arc<BlockIndexCache>,
) -> FlowyResult<Self> { ) -> FlowyResult<Self> {
let editor_map = make_block_meta_editor_map(user, block_meta_revs).await?; let block_editors = make_block_editors(user, block_meta_revs).await?;
let user = user.clone(); let user = user.clone();
let grid_id = grid_id.to_owned(); let grid_id = grid_id.to_owned();
let manager = Self { let manager = Self {
grid_id, grid_id,
user, user,
block_editor_map: editor_map, block_editors,
persistence, persistence,
}; };
Ok(manager) Ok(manager)
@ -48,11 +48,11 @@ impl GridBlockManager {
// #[tracing::instrument(level = "trace", skip(self))] // #[tracing::instrument(level = "trace", skip(self))]
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> { pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
debug_assert!(!block_id.is_empty()); debug_assert!(!block_id.is_empty());
match self.block_editor_map.get(block_id) { match self.block_editors.get(block_id) {
None => { None => {
tracing::error!("This is a fatal error, block with id:{} is not exist", block_id); tracing::error!("This is a fatal error, block with id:{} is not exist", block_id);
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?); let editor = Arc::new(make_block_editor(&self.user, block_id).await?);
self.block_editor_map.insert(block_id.to_owned(), editor.clone()); self.block_editors.insert(block_id.to_owned(), editor.clone());
Ok(editor) Ok(editor)
} }
Some(editor) => Ok(editor.clone()), Some(editor) => Ok(editor.clone()),
@ -222,33 +222,31 @@ impl GridBlockManager {
editor.get_row_orders::<&str>(None).await editor.get_row_orders::<&str>(None).await
} }
pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> { pub(crate) async fn get_block_snapshots(
&self,
block_ids: Option<Vec<String>>,
) -> FlowyResult<Vec<GridBlockSnapshot>> {
let mut snapshots = vec![]; let mut snapshots = vec![];
match block_ids {
None => {
for iter in self.block_editors.iter() {
let editor = iter.value();
let block_id = editor.block_id.clone();
let row_revs = editor.get_row_revs::<&str>(None).await?;
snapshots.push(GridBlockSnapshot { block_id, row_revs });
}
}
Some(block_ids) => {
for block_id in block_ids { for block_id in block_ids {
let editor = self.get_editor(&block_id).await?; let editor = self.get_editor(&block_id).await?;
let row_revs = editor.get_row_revs::<&str>(None).await?; let row_revs = editor.get_row_revs::<&str>(None).await?;
snapshots.push(GridBlockSnapshot { block_id, row_revs }); snapshots.push(GridBlockSnapshot { block_id, row_revs });
} }
}
}
Ok(snapshots) Ok(snapshots)
} }
// Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
#[allow(dead_code)]
pub async fn get_cell_revs(
&self,
block_ids: Vec<String>,
field_id: &str,
row_ids: Option<Vec<Cow<'_, String>>>,
) -> FlowyResult<Vec<CellRevision>> {
let mut block_cell_revs = vec![];
for block_id in block_ids {
let editor = self.get_editor(&block_id).await?;
let cell_revs = editor.get_cell_revs(field_id, row_ids.clone()).await?;
block_cell_revs.extend(cell_revs);
}
Ok(block_cell_revs)
}
async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> { async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> {
send_dart_notification(block_id, GridNotification::DidUpdateGridBlock) send_dart_notification(block_id, GridNotification::DidUpdateGridBlock)
.payload(changeset) .payload(changeset)
@ -263,20 +261,20 @@ impl GridBlockManager {
} }
} }
async fn make_block_meta_editor_map( async fn make_block_editors(
user: &Arc<dyn GridUser>, user: &Arc<dyn GridUser>,
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>, block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
) -> FlowyResult<DashMap<String, Arc<GridBlockRevisionEditor>>> { ) -> FlowyResult<DashMap<String, Arc<GridBlockRevisionEditor>>> {
let editor_map = DashMap::new(); let editor_map = DashMap::new();
for block_meta_rev in block_meta_revs { for block_meta_rev in block_meta_revs {
let editor = make_block_meta_editor(user, &block_meta_rev.block_id).await?; let editor = make_block_editor(user, &block_meta_rev.block_id).await?;
editor_map.insert(block_meta_rev.block_id.clone(), Arc::new(editor)); editor_map.insert(block_meta_rev.block_id.clone(), Arc::new(editor));
} }
Ok(editor_map) Ok(editor_map)
} }
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> { async fn make_block_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> {
tracing::trace!("Open block:{} meta editor", block_id); tracing::trace!("Open block:{} meta editor", block_id);
let token = user.token()?; let token = user.token()?;
let user_id = user.user_id()?; let user_id = user.user_id()?;

View File

@ -4,7 +4,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::{FieldType, GridCheckboxFilter};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -40,7 +40,7 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
const YES: &str = "Yes"; const YES: &str = "Yes";
const NO: &str = "No"; const NO: &str = "No";
impl CellDataOperation<String> for CheckboxTypeOption { impl CellDataOperation<String, GridCheckboxFilter> for CheckboxTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -62,6 +62,10 @@ impl CellDataOperation<String> for CheckboxTypeOption {
Ok(DecodedCellData::default()) Ok(DecodedCellData::default())
} }
fn apply_filter(&self, _filter: GridCheckboxFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,

View File

@ -7,7 +7,7 @@ use chrono::format::strftime::StrftimeItems;
use chrono::{NaiveDateTime, Timelike}; use chrono::{NaiveDateTime, Timelike};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{CellChangeset, FieldType}; use flowy_grid_data_model::entities::{CellChangeset, FieldType, GridDateFilter};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum_macros::EnumIter; use strum_macros::EnumIter;
@ -115,7 +115,7 @@ impl DateTypeOption {
} }
} }
impl CellDataOperation<String> for DateTypeOption { impl CellDataOperation<String, GridDateFilter> for DateTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -138,6 +138,10 @@ impl CellDataOperation<String> for DateTypeOption {
DecodedCellData::try_from_bytes(date) DecodedCellData::try_from_bytes(date)
} }
fn apply_filter(&self, _filter: GridDateFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,

View File

@ -6,7 +6,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::{FieldType, GridNumberFilter};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -139,7 +139,7 @@ impl NumberTypeOption {
} }
} }
impl CellDataOperation<String> for NumberTypeOption { impl CellDataOperation<String, GridNumberFilter> for NumberTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -179,6 +179,10 @@ impl CellDataOperation<String> for NumberTypeOption {
} }
} }
fn apply_filter(&self, _filter: GridNumberFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,

View File

@ -6,7 +6,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{CellChangeset, FieldType}; use flowy_grid_data_model::entities::{CellChangeset, FieldType, GridSelectOptionFilter};
use flowy_grid_data_model::parser::NotEmptyStr; use flowy_grid_data_model::parser::NotEmptyStr;
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use nanoid::nanoid; use nanoid::nanoid;
@ -94,7 +94,7 @@ impl SelectOptionOperation for SingleSelectTypeOption {
} }
} }
impl CellDataOperation<String> for SingleSelectTypeOption { impl CellDataOperation<String, GridSelectOptionFilter> for SingleSelectTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -122,6 +122,10 @@ impl CellDataOperation<String> for SingleSelectTypeOption {
DecodedCellData::try_from_bytes(cell_data) DecodedCellData::try_from_bytes(cell_data)
} }
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,
@ -192,7 +196,7 @@ impl SelectOptionOperation for MultiSelectTypeOption {
} }
} }
impl CellDataOperation<String> for MultiSelectTypeOption { impl CellDataOperation<String, GridSelectOptionFilter> for MultiSelectTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -220,6 +224,10 @@ impl CellDataOperation<String> for MultiSelectTypeOption {
DecodedCellData::try_from_bytes(cell_data) DecodedCellData::try_from_bytes(cell_data)
} }
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
todo!()
}
fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
T: Into<CellContentChangeset>, T: Into<CellContentChangeset>,

View File

@ -4,7 +4,7 @@ use crate::services::row::{try_decode_cell_data, CellContentChangeset, CellDataO
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::{FieldType, GridTextFilter};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -30,7 +30,7 @@ pub struct RichTextTypeOption {
} }
impl_type_option!(RichTextTypeOption, FieldType::RichText); impl_type_option!(RichTextTypeOption, FieldType::RichText);
impl CellDataOperation<String> for RichTextTypeOption { impl CellDataOperation<String, GridTextFilter> for RichTextTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -45,13 +45,17 @@ impl CellDataOperation<String> for RichTextTypeOption {
|| decoded_field_type.is_multi_select() || decoded_field_type.is_multi_select()
|| decoded_field_type.is_number() || decoded_field_type.is_number()
{ {
try_decode_cell_data(encoded_data, field_rev, decoded_field_type, decoded_field_type) try_decode_cell_data(encoded_data.into(), field_rev, decoded_field_type, decoded_field_type)
} else { } else {
let cell_data = encoded_data.into(); let cell_data = encoded_data.into();
Ok(DecodedCellData::new(cell_data)) Ok(DecodedCellData::new(cell_data))
} }
} }
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,

View File

@ -5,7 +5,7 @@ use bytes::Bytes;
use fancy_regex::Regex; use fancy_regex::Regex;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::{FieldType, GridTextFilter};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -33,7 +33,7 @@ pub struct URLTypeOption {
} }
impl_type_option!(URLTypeOption, FieldType::URL); impl_type_option!(URLTypeOption, FieldType::URL);
impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption { impl CellDataOperation<EncodedCellData<URLCellData>, GridTextFilter> for URLTypeOption {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -50,6 +50,10 @@ impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
DecodedCellData::try_from_bytes(cell_data) DecodedCellData::try_from_bytes(cell_data)
} }
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
todo!()
}
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError> fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
where where
C: Into<CellContentChangeset>, C: Into<CellContentChangeset>,

View File

@ -1,10 +1,15 @@
use crate::manager::GridTaskSchedulerRwLock;
use crate::services::block_manager::GridBlockManager; use crate::services::block_manager::GridBlockManager;
use crate::services::tasks::Task;
use flowy_error::FlowyResult;
use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::row::GridBlockSnapshot;
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
use flowy_error::FlowyResult;
use flowy_grid_data_model::entities::{
FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter,
GridSettingChangesetParams, GridTextFilter,
};
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use flowy_sync::client_grid::GridRevisionPad; use flowy_sync::client_grid::GridRevisionPad;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -12,47 +17,231 @@ pub(crate) struct GridFilterService {
scheduler: Arc<dyn GridServiceTaskScheduler>, scheduler: Arc<dyn GridServiceTaskScheduler>,
grid_pad: Arc<RwLock<GridRevisionPad>>, grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>, block_manager: Arc<GridBlockManager>,
filter_cache: Arc<RwLock<FilterCache>>,
filter_result: Arc<RwLock<GridFilterResult>>,
} }
impl GridFilterService { impl GridFilterService {
pub fn new<S: GridServiceTaskScheduler>( pub async fn new<S: GridServiceTaskScheduler>(
grid_pad: Arc<RwLock<GridRevisionPad>>, grid_pad: Arc<RwLock<GridRevisionPad>>,
block_manager: Arc<GridBlockManager>, block_manager: Arc<GridBlockManager>,
scheduler: S, scheduler: S,
) -> Self { ) -> Self {
let filter_cache = Arc::new(RwLock::new(FilterCache::from_grid_pad(&grid_pad).await));
let filter_result = Arc::new(RwLock::new(GridFilterResult::default()));
Self { Self {
grid_pad, grid_pad,
block_manager, block_manager,
scheduler: Arc::new(scheduler), scheduler: Arc::new(scheduler),
filter_cache,
filter_result,
} }
} }
pub async fn process_task(&self, _task: Task) -> FlowyResult<()> { pub async fn process(&self, task_context: FilterTaskContext) -> FlowyResult<()> {
let mut filter_result = self.filter_result.write().await;
for block in task_context.blocks {
for row_rev in block.row_revs {
let row_filter_result = RowFilterResult::new(&row_rev);
filter_result.insert(&row_rev.id, row_filter_result);
}
}
Ok(()) Ok(())
} }
pub async fn notify_changed(&self) { pub async fn apply_changeset(&self, changeset: GridFilterChangeset) {
let task_id = self.scheduler.gen_task_id().await; if !changeset.is_changed() {
return;
}
// if let Some(filter_id) = &changeset.insert_filter {
// let grid_pad = self.grid_pad.read().await; let mut cache = self.filter_cache.write().await;
// match grid_pad.get_filters(None) { let field_ids = Some(vec![filter_id.field_id.clone()]);
// None => {} reload_filter_cache(&mut cache, field_ids, &self.grid_pad).await;
// Some(filter_revs) => { }
// filter_revs
// .iter() if let Some(filter_id) = &changeset.delete_filter {
// .for_each(|filter_rev| match grid_pad.get_field_rev(&filter_rev.field_id) { self.filter_cache.write().await.remove(filter_id);
// None => {} }
// Some((_, _field_rev)) => match field_rev.field_type {
// FieldType::RichText => {} match self.block_manager.get_block_snapshots(None).await {
// FieldType::Number => {} Ok(blocks) => {
// FieldType::DateTime => {} let task = self.gen_task(blocks).await;
// FieldType::SingleSelect => {} let _ = self.scheduler.register_task(task).await;
// FieldType::MultiSelect => {} }
// FieldType::Checkbox => {} Err(_) => {}
// FieldType::URL => {} }
// }, }
// });
// } async fn gen_task(&self, blocks: Vec<GridBlockSnapshot>) -> Task {
// } let task_id = self.scheduler.gen_task_id().await;
let handler_id = self.grid_pad.read().await.grid_id();
let context = FilterTaskContext { blocks };
let task = Task {
handler_id,
id: task_id,
content: TaskContent::Filter(context),
};
task
}
}
pub struct GridFilterChangeset {
insert_filter: Option<FilterId>,
delete_filter: Option<FilterId>,
}
impl GridFilterChangeset {
fn is_changed(&self) -> bool {
self.insert_filter.is_some() || self.delete_filter.is_some()
}
}
impl std::convert::From<&GridSettingChangesetParams> for GridFilterChangeset {
fn from(params: &GridSettingChangesetParams) -> Self {
let insert_filter = params.insert_filter.as_ref().map(|insert_filter_params| FilterId {
field_id: insert_filter_params.field_id.clone(),
field_type: insert_filter_params.field_type.clone(),
});
let delete_filter = params.delete_filter.as_ref().map(|delete_filter_params| FilterId {
field_id: delete_filter_params.filter_id.clone(),
field_type: delete_filter_params.field_type.clone(),
});
GridFilterChangeset {
insert_filter,
delete_filter,
}
}
}
#[derive(Default)]
struct GridFilterResult {
rows: HashMap<String, RowFilterResult>,
}
impl GridFilterResult {
fn insert(&mut self, row_id: &str, result: RowFilterResult) {
self.rows.insert(row_id.to_owned(), result);
}
}
#[derive(Default)]
struct RowFilterResult {
cell_by_field_id: HashMap<String, bool>,
}
impl RowFilterResult {
fn new(row_rev: &RowRevision) -> Self {
Self {
cell_by_field_id: row_rev.cells.iter().map(|(k, _)| (k.clone(), true)).collect(),
}
}
fn update_cell(&mut self, cell_id: &str, exist: bool) {
self.cell_by_field_id.insert(cell_id.to_owned(), exist);
}
}
#[derive(Default)]
struct FilterCache {
text_filter: HashMap<FilterId, GridTextFilter>,
url_filter: HashMap<FilterId, GridTextFilter>,
number_filter: HashMap<FilterId, GridNumberFilter>,
date_filter: HashMap<FilterId, GridDateFilter>,
select_option_filter: HashMap<FilterId, GridSelectOptionFilter>,
checkbox_filter: HashMap<FilterId, GridCheckboxFilter>,
}
impl FilterCache {
async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Self {
let mut this = Self::default();
reload_filter_cache(&mut this, None, grid_pad);
this
}
fn remove(&mut self, filter_id: &FilterId) {
let _ = match filter_id.field_type {
FieldType::RichText => {
let _ = self.text_filter.remove(filter_id);
}
FieldType::Number => {
let _ = self.number_filter.remove(filter_id);
}
FieldType::DateTime => {
let _ = self.date_filter.remove(filter_id);
}
FieldType::SingleSelect => {
let _ = self.select_option_filter.remove(filter_id);
}
FieldType::MultiSelect => {
let _ = self.select_option_filter.remove(filter_id);
}
FieldType::Checkbox => {
let _ = self.checkbox_filter.remove(filter_id);
}
FieldType::URL => {
let _ = self.url_filter.remove(filter_id);
}
};
}
}
async fn reload_filter_cache(
cache: &mut FilterCache,
field_ids: Option<Vec<String>>,
grid_pad: &Arc<RwLock<GridRevisionPad>>,
) {
let grid_pad = grid_pad.read().await;
let filters_revs = grid_pad.get_filters(None, field_ids).unwrap_or_default();
for filter_rev in filters_revs {
match grid_pad.get_field_rev(&filter_rev.field_id) {
None => {}
Some((_, field_rev)) => {
let filter_id = FilterId::from(field_rev);
match &field_rev.field_type {
FieldType::RichText => {
let _ = cache.text_filter.insert(filter_id, GridTextFilter::from(filter_rev));
}
FieldType::Number => {
let _ = cache
.number_filter
.insert(filter_id, GridNumberFilter::from(filter_rev));
}
FieldType::DateTime => {
let _ = cache.date_filter.insert(filter_id, GridDateFilter::from(filter_rev));
}
FieldType::SingleSelect | FieldType::MultiSelect => {
let _ = cache
.select_option_filter
.insert(filter_id, GridSelectOptionFilter::from(filter_rev));
}
FieldType::Checkbox => {
let _ = cache
.checkbox_filter
.insert(filter_id, GridCheckboxFilter::from(filter_rev));
}
FieldType::URL => {
let _ = cache.url_filter.insert(filter_id, GridTextFilter::from(filter_rev));
}
}
}
}
}
}
#[derive(Hash, Eq, PartialEq)]
struct FilterId {
field_id: String,
field_type: FieldType,
}
impl std::convert::From<&FieldRevision> for FilterId {
fn from(rev: &FieldRevision) -> Self {
Self {
field_id: rev.id.clone(),
field_type: rev.field_type.clone(),
}
} }
} }

View File

@ -3,7 +3,7 @@ use crate::entities::CellIdentifier;
use crate::manager::{GridTaskSchedulerRwLock, GridUser}; use crate::manager::{GridTaskSchedulerRwLock, GridUser};
use crate::services::block_manager::GridBlockManager; use crate::services::block_manager::GridBlockManager;
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder}; use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
use crate::services::filter::GridFilterService; use crate::services::filter::{GridFilterChangeset, GridFilterService};
use crate::services::persistence::block_index::BlockIndexCache; use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::*; use crate::services::row::*;
@ -53,11 +53,8 @@ impl GridRevisionEditor {
let grid_pad = Arc::new(RwLock::new(grid_pad)); let grid_pad = Arc::new(RwLock::new(grid_pad));
let block_meta_revs = grid_pad.read().await.get_block_meta_revs(); let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?); let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
let filter_service = Arc::new(GridFilterService::new( let filter_service =
grid_pad.clone(), Arc::new(GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
block_manager.clone(),
task_scheduler.clone(),
));
let editor = Arc::new(Self { let editor = Arc::new(Self {
grid_id: grid_id.to_owned(), grid_id: grid_id.to_owned(),
user, user,
@ -454,32 +451,31 @@ impl GridRevisionEditor {
} }
pub async fn get_grid_setting(&self) -> FlowyResult<GridSetting> { pub async fn get_grid_setting(&self) -> FlowyResult<GridSetting> {
let read_guard = self.grid_pad.read().await; // let read_guard = self.grid_pad.read().await;
let grid_setting_rev = read_guard.get_grid_setting_rev(); // let grid_setting_rev = read_guard.get_grid_setting_rev();
Ok(grid_setting_rev.into()) // Ok(grid_setting_rev.into())
todo!()
} }
pub async fn get_grid_filter(&self, layout_type: &GridLayoutType) -> FlowyResult<Vec<GridFilter>> { pub async fn get_grid_filter(&self, layout_type: &GridLayoutType) -> FlowyResult<Vec<GridFilter>> {
let read_guard = self.grid_pad.read().await; let read_guard = self.grid_pad.read().await;
let layout_rev = layout_type.clone().into(); let layout_rev = layout_type.clone().into();
match read_guard.get_filters(Some(&layout_rev)) { match read_guard.get_filters(Some(&layout_rev), None) {
Some(filter_revs) => Ok(filter_revs.iter().map(GridFilter::from).collect::<Vec<GridFilter>>()), Some(filter_revs) => Ok(filter_revs.iter().map(GridFilter::from).collect::<Vec<GridFilter>>()),
None => Ok(vec![]), None => Ok(vec![]),
} }
} }
pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> { pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
let is_filter_changed = params.is_filter_changed(); let filter_changeset = GridFilterChangeset::from(&params);
let _ = self let _ = self
.modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?)) .modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
.await?; .await?;
if is_filter_changed {
let filter_service = self.filter_service.clone(); let filter_service = self.filter_service.clone();
tokio::spawn(async move { tokio::spawn(async move {
filter_service.notify_changed().await; filter_service.apply_changeset(filter_changeset).await;
}); });
}
Ok(()) Ok(())
} }
@ -495,7 +491,7 @@ impl GridRevisionEditor {
.collect::<Vec<String>>(), .collect::<Vec<String>>(),
Some(block_ids) => block_ids, Some(block_ids) => block_ids,
}; };
let snapshots = self.block_manager.make_block_snapshots(block_ids).await?; let snapshots = self.block_manager.get_block_snapshots(Some(block_ids)).await?;
Ok(snapshots) Ok(snapshots)
} }

View File

@ -5,7 +5,7 @@ use flowy_error::FlowyError;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use lib_infra::future::BoxResultFuture; use lib_infra::future::BoxResultFuture;
pub trait GridServiceTaskScheduler: Send + Sync + 'static { pub(crate) trait GridServiceTaskScheduler: Send + Sync + 'static {
fn gen_task_id(&self) -> BoxFuture<TaskId>; fn gen_task_id(&self) -> BoxFuture<TaskId>;
fn register_task(&self, task: Task) -> BoxFuture<()>; fn register_task(&self, task: Task) -> BoxFuture<()>;
} }
@ -17,9 +17,9 @@ impl GridTaskHandler for GridRevisionEditor {
fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError> { fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError> {
Box::pin(async move { Box::pin(async move {
match &task.content { match task.content {
TaskContent::Snapshot { .. } => {} TaskContent::Snapshot => {}
TaskContent::Filter { .. } => self.filter_service.process_task(task).await?, TaskContent::Filter(context) => self.filter_service.process(context).await?,
} }
Ok(()) Ok(())
}) })

View File

@ -2,12 +2,13 @@ use crate::services::field::*;
use bytes::Bytes; use bytes::Bytes;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::FieldType;
use flowy_grid_data_model::revision::{CellRevision, FieldRevision}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, GridFilterRevision};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Formatter; use std::fmt::Formatter;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc;
pub trait CellDataOperation<ED> { pub trait CellDataOperation<D, F> {
fn decode_cell_data<T>( fn decode_cell_data<T>(
&self, &self,
encoded_data: T, encoded_data: T,
@ -15,9 +16,10 @@ pub trait CellDataOperation<ED> {
field_rev: &FieldRevision, field_rev: &FieldRevision,
) -> FlowyResult<DecodedCellData> ) -> FlowyResult<DecodedCellData>
where where
T: Into<ED>; T: Into<D>;
fn apply_filter(&self, filter: F) -> bool;
//
fn apply_changeset<C: Into<CellContentChangeset>>( fn apply_changeset<C: Into<CellContentChangeset>>(
&self, &self,
changeset: C, changeset: C,
@ -113,6 +115,14 @@ impl TypeOptionCellData {
} }
} }
pub fn apply_cell_filter(
_filter_rev: Arc<GridFilterRevision>,
_field_rev: &FieldRevision,
_cell_rev: Option<CellRevision>,
) -> bool {
todo!()
}
/// The changeset will be deserialized into specific data base on the FieldType. /// The changeset will be deserialized into specific data base on the FieldType.
/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect /// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>( pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>(
@ -150,13 +160,12 @@ pub fn decode_cell_data<T: TryInto<TypeOptionCellData>>(data: T, field_rev: &Fie
} }
} }
pub fn try_decode_cell_data<T: Into<String>>( pub fn try_decode_cell_data(
encoded_data: T, encoded_data: String,
field_rev: &FieldRevision, field_rev: &FieldRevision,
s_field_type: &FieldType, s_field_type: &FieldType,
t_field_type: &FieldType, t_field_type: &FieldType,
) -> FlowyResult<DecodedCellData> { ) -> FlowyResult<DecodedCellData> {
let encoded_data = encoded_data.into();
let get_cell_data = || { let get_cell_data = || {
let data = match t_field_type { let data = match t_field_type {
FieldType::RichText => field_rev FieldType::RichText => field_rev

View File

@ -1,4 +1,6 @@
use flowy_grid_data_model::entities::{CreateGridFilterParams, GridLayoutType, GridSettingChangesetParams}; use flowy_grid_data_model::entities::{
CreateGridFilterParams, DeleteFilterParams, GridLayoutType, GridSettingChangesetParams,
};
pub struct GridSettingChangesetBuilder { pub struct GridSettingChangesetBuilder {
params: GridSettingChangesetParams, params: GridSettingChangesetParams,
@ -24,8 +26,8 @@ impl GridSettingChangesetBuilder {
self self
} }
pub fn delete_filter(mut self, filter_id: &str) -> Self { pub fn delete_filter(mut self, params: DeleteFilterParams) -> Self {
self.params.delete_filter = Some(filter_id.to_string()); self.params.delete_filter = Some(params);
self self
} }

View File

@ -21,7 +21,7 @@ impl GridTaskQueue {
pub(crate) fn push(&mut self, task: &Task) { pub(crate) fn push(&mut self, task: &Task) {
let task_type = match task.content { let task_type = match task.content {
TaskContent::Snapshot { .. } => TaskType::Snapshot, TaskContent::Snapshot => TaskType::Snapshot,
TaskContent::Filter { .. } => TaskType::Filter, TaskContent::Filter { .. } => TaskType::Filter,
}; };
let pending_task = PendingTask { let pending_task = PendingTask {

View File

@ -11,7 +11,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{watch, RwLock}; use tokio::sync::{watch, RwLock};
pub trait GridTaskHandler: Send + Sync + 'static { pub(crate) trait GridTaskHandler: Send + Sync + 'static {
fn handler_id(&self) -> &TaskHandlerId; fn handler_id(&self) -> &TaskHandlerId;
fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError>; fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError>;
@ -25,7 +25,7 @@ pub struct GridTaskScheduler {
} }
impl GridTaskScheduler { impl GridTaskScheduler {
pub fn new() -> Arc<RwLock<Self>> { pub(crate) fn new() -> Arc<RwLock<Self>> {
let (notifier, rx) = watch::channel(()); let (notifier, rx) = watch::channel(());
let scheduler = Self { let scheduler = Self {
@ -45,7 +45,7 @@ impl GridTaskScheduler {
scheduler scheduler
} }
pub fn register_handler<T>(&mut self, handler: Arc<T>) pub(crate) fn register_handler<T>(&mut self, handler: Arc<T>)
where where
T: GridTaskHandler, T: GridTaskHandler,
{ {
@ -53,11 +53,11 @@ impl GridTaskScheduler {
self.handlers.insert(handler_id, handler); self.handlers.insert(handler_id, handler);
} }
pub fn unregister_handler<T: AsRef<str>>(&mut self, handler_id: T) { pub(crate) fn unregister_handler<T: AsRef<str>>(&mut self, handler_id: T) {
let _ = self.handlers.remove(handler_id.as_ref()); let _ = self.handlers.remove(handler_id.as_ref());
} }
pub async fn process_next_task(&mut self) -> FlowyResult<()> { pub(crate) async fn process_next_task(&mut self) -> FlowyResult<()> {
let mut get_next_task = || { let mut get_next_task = || {
let pending_task = self.queue.mut_head(|list| list.pop())?; let pending_task = self.queue.mut_head(|list| list.pop())?;
let task = self.store.remove_task(&pending_task.id)?; let task = self.store.remove_task(&pending_task.id)?;
@ -65,7 +65,7 @@ impl GridTaskScheduler {
}; };
if let Some(task) = get_next_task() { if let Some(task) = get_next_task() {
match self.handlers.get(&task.hid) { match self.handlers.get(&task.handler_id) {
None => {} None => {}
Some(handler) => { Some(handler) => {
let _ = handler.process_task(task).await; let _ = handler.process_task(task).await;
@ -75,18 +75,18 @@ impl GridTaskScheduler {
Ok(()) Ok(())
} }
pub fn register_task(&mut self, task: Task) { pub(crate) fn register_task(&mut self, task: Task) {
assert!(!task.is_finished()); assert!(!task.is_finished());
self.queue.push(&task); self.queue.push(&task);
self.store.insert_task(task); self.store.insert_task(task);
self.notify(); self.notify();
} }
pub fn next_task_id(&self) -> TaskId { pub(crate) fn next_task_id(&self) -> TaskId {
self.store.next_task_id() self.store.next_task_id()
} }
pub fn notify(&self) { pub(crate) fn notify(&self) {
let _ = self.notifier.send(()); let _ = self.notifier.send(());
} }
} }

View File

@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::sync::atomic::AtomicU32; use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering::SeqCst; use std::sync::atomic::Ordering::SeqCst;
pub struct GridTaskStore { pub(crate) struct GridTaskStore {
tasks: HashMap<TaskId, Task>, tasks: HashMap<TaskId, Task>,
task_id_counter: AtomicU32, task_id_counter: AtomicU32,
} }
@ -17,15 +17,15 @@ impl GridTaskStore {
} }
} }
pub fn insert_task(&mut self, task: Task) { pub(crate) fn insert_task(&mut self, task: Task) {
self.tasks.insert(task.id, task); self.tasks.insert(task.id, task);
} }
pub fn remove_task(&mut self, task_id: &TaskId) -> Option<Task> { pub(crate) fn remove_task(&mut self, task_id: &TaskId) -> Option<Task> {
self.tasks.remove(task_id) self.tasks.remove(task_id)
} }
pub fn next_task_id(&self) -> TaskId { pub(crate) fn next_task_id(&self) -> TaskId {
let _ = self.task_id_counter.fetch_add(1, SeqCst); let _ = self.task_id_counter.fetch_add(1, SeqCst);
self.task_id_counter.load(SeqCst) self.task_id_counter.load(SeqCst)
} }

View File

@ -1,3 +1,4 @@
use crate::services::row::GridBlockSnapshot;
use crate::services::tasks::queue::TaskHandlerId; use crate::services::tasks::queue::TaskHandlerId;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -49,17 +50,17 @@ impl Ord for PendingTask {
} }
} }
pub struct SnapshotTaskContext {} pub(crate) struct FilterTaskContext {
pub blocks: Vec<GridBlockSnapshot>,
pub struct FilterTaskContext {}
pub enum TaskContent {
Snapshot { context: SnapshotTaskContext },
Filter { context: FilterTaskContext },
} }
pub struct Task { pub(crate) enum TaskContent {
pub hid: TaskHandlerId, Snapshot,
Filter(FilterTaskContext),
}
pub(crate) struct Task {
pub handler_id: TaskHandlerId,
pub id: TaskId, pub id: TaskId,
pub content: TaskContent, pub content: TaskContent,
} }

View File

@ -26,14 +26,17 @@ async fn grid_filter_invalid_condition_panic_test() {
#[tokio::test] #[tokio::test]
async fn grid_filter_delete_test() { async fn grid_filter_delete_test() {
let mut test = GridEditorTest::new().await; let mut test = GridEditorTest::new().await;
let field_rev = test.text_field(); let field_rev = test.text_field().clone();
let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned())); let payload = CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }]; let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
test.run_scripts(scripts).await; test.run_scripts(scripts).await;
let filter = test.grid_filters().await.pop().unwrap(); let filter = test.grid_filters().await.pop().unwrap();
test.run_scripts(vec![ test.run_scripts(vec![
DeleteGridTableFilter { filter_id: filter.id }, DeleteGridTableFilter {
filter_id: filter.id,
field_type: field_rev.field_type.clone(),
},
AssertTableFilterCount { count: 0 }, AssertTableFilterCount { count: 0 },
]) ])
.await; .await;

View File

@ -77,6 +77,7 @@ pub enum EditorScript {
}, },
DeleteGridTableFilter { DeleteGridTableFilter {
filter_id: String, filter_id: String,
field_type: FieldType,
}, },
#[allow(dead_code)] #[allow(dead_code)]
AssertGridSetting { AssertGridSetting {
@ -265,10 +266,10 @@ impl GridEditorTest {
let filters = self.editor.get_grid_filter(&layout_type).await.unwrap(); let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
assert_eq!(count as usize, filters.len()); assert_eq!(count as usize, filters.len());
} }
EditorScript::DeleteGridTableFilter { filter_id } => { EditorScript::DeleteGridTableFilter { filter_id ,field_type} => {
let layout_type = GridLayoutType::Table; let layout_type = GridLayoutType::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type) let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.delete_filter(&filter_id) .delete_filter(DeleteFilterParams { filter_id, field_type })
.build(); .build();
let _ = self.editor.update_grid_setting(params).await.unwrap(); let _ = self.editor.update_grid_setting(params).await.unwrap();
} }

View File

@ -438,6 +438,7 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
Debug, Debug,
Clone, Clone,
PartialEq, PartialEq,
Hash,
Eq, Eq,
ProtoBuf_Enum, ProtoBuf_Enum,
EnumCountMacro, EnumCountMacro,
@ -478,8 +479,7 @@ impl From<&FieldType> for FieldType {
impl FieldType { impl FieldType {
pub fn type_id(&self) -> String { pub fn type_id(&self) -> String {
let ty = self.clone() as u8; (self.clone() as u8).to_string()
ty.to_string()
} }
pub fn default_cell_width(&self) -> i32 { pub fn default_cell_width(&self) -> i32 {

View File

@ -1,10 +1,10 @@
use crate::entities::FieldType;
use crate::parser::NotEmptyStr; use crate::parser::NotEmptyStr;
use crate::revision::{FieldRevision, GridFilterRevision};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error_code::ErrorCode; use flowy_error_code::ErrorCode;
use crate::entities::FieldType;
use crate::revision::{FieldRevision, GridFilterRevision};
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::Arc;
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct GridFilter { pub struct GridFilter {
@ -18,14 +18,14 @@ pub struct RepeatedGridFilter {
pub items: Vec<GridFilter>, pub items: Vec<GridFilter>,
} }
impl std::convert::From<&GridFilterRevision> for GridFilter { impl std::convert::From<&Arc<GridFilterRevision>> for GridFilter {
fn from(rev: &GridFilterRevision) -> Self { fn from(rev: &Arc<GridFilterRevision>) -> Self {
Self { id: rev.id.clone() } Self { id: rev.id.clone() }
} }
} }
impl std::convert::From<&Vec<GridFilterRevision>> for RepeatedGridFilter { impl std::convert::From<&Vec<Arc<GridFilterRevision>>> for RepeatedGridFilter {
fn from(revs: &Vec<GridFilterRevision>) -> Self { fn from(revs: &Vec<Arc<GridFilterRevision>>) -> Self {
RepeatedGridFilter { RepeatedGridFilter {
items: revs.iter().map(|rev| rev.into()).collect(), items: revs.iter().map(|rev| rev.into()).collect(),
} }
@ -38,6 +38,34 @@ impl std::convert::From<Vec<GridFilter>> for RepeatedGridFilter {
} }
} }
#[derive(ProtoBuf, Debug, Default, Clone)]
pub struct DeleteFilterPayload {
#[pb(index = 1)]
pub filter_id: String,
#[pb(index = 2)]
pub field_type: FieldType,
}
pub struct DeleteFilterParams {
pub filter_id: String,
pub field_type: FieldType,
}
impl TryInto<DeleteFilterParams> for DeleteFilterPayload {
type Error = ErrorCode;
fn try_into(self) -> Result<DeleteFilterParams, Self::Error> {
let filter_id = NotEmptyStr::parse(self.filter_id)
.map_err(|_| ErrorCode::UnexpectedEmptyString)?
.0;
Ok(DeleteFilterParams {
filter_id,
field_type: self.field_type,
})
}
}
#[derive(ProtoBuf, Debug, Default, Clone)] #[derive(ProtoBuf, Debug, Default, Clone)]
pub struct CreateGridFilterPayload { pub struct CreateGridFilterPayload {
#[pb(index = 1)] #[pb(index = 1)]
@ -81,9 +109,12 @@ impl TryInto<CreateGridFilterParams> for CreateGridFilterPayload {
.0; .0;
let condition = self.condition as u8; let condition = self.condition as u8;
match self.field_type { match self.field_type {
FieldType::RichText | FieldType::Checkbox | FieldType::URL => { FieldType::RichText | FieldType::URL => {
let _ = TextFilterCondition::try_from(condition)?; let _ = TextFilterCondition::try_from(condition)?;
} }
FieldType::Checkbox => {
let _ = CheckboxCondition::try_from(condition)?;
}
FieldType::Number => { FieldType::Number => {
let _ = NumberFilterCondition::try_from(condition)?; let _ = NumberFilterCondition::try_from(condition)?;
} }
@ -154,11 +185,11 @@ impl std::convert::TryFrom<u8> for TextFilterCondition {
} }
} }
impl std::convert::From<GridFilterRevision> for GridTextFilter { impl std::convert::From<Arc<GridFilterRevision>> for GridTextFilter {
fn from(rev: GridFilterRevision) -> Self { fn from(rev: Arc<GridFilterRevision>) -> Self {
GridTextFilter { GridTextFilter {
condition: TextFilterCondition::try_from(rev.condition).unwrap_or(TextFilterCondition::Is), condition: TextFilterCondition::try_from(rev.condition).unwrap_or(TextFilterCondition::Is),
content: rev.content, content: rev.content.clone(),
} }
} }
} }
@ -213,11 +244,11 @@ impl std::convert::TryFrom<u8> for NumberFilterCondition {
} }
} }
impl std::convert::From<GridFilterRevision> for GridNumberFilter { impl std::convert::From<Arc<GridFilterRevision>> for GridNumberFilter {
fn from(rev: GridFilterRevision) -> Self { fn from(rev: Arc<GridFilterRevision>) -> Self {
GridNumberFilter { GridNumberFilter {
condition: NumberFilterCondition::try_from(rev.condition).unwrap_or(NumberFilterCondition::Equal), condition: NumberFilterCondition::try_from(rev.condition).unwrap_or(NumberFilterCondition::Equal),
content: rev.content, content: rev.content.clone(),
} }
} }
} }
@ -266,11 +297,11 @@ impl std::convert::TryFrom<u8> for SelectOptionCondition {
} }
} }
impl std::convert::From<GridFilterRevision> for GridSelectOptionFilter { impl std::convert::From<Arc<GridFilterRevision>> for GridSelectOptionFilter {
fn from(rev: GridFilterRevision) -> Self { fn from(rev: Arc<GridFilterRevision>) -> Self {
GridSelectOptionFilter { GridSelectOptionFilter {
condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs), condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs),
content: rev.content, content: rev.content.clone(),
} }
} }
} }
@ -318,11 +349,56 @@ impl std::convert::TryFrom<u8> for DateFilterCondition {
} }
} }
} }
impl std::convert::From<GridFilterRevision> for GridDateFilter { impl std::convert::From<Arc<GridFilterRevision>> for GridDateFilter {
fn from(rev: GridFilterRevision) -> Self { fn from(rev: Arc<GridFilterRevision>) -> Self {
GridDateFilter { GridDateFilter {
condition: DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs), condition: DateFilterCondition::try_from(rev.condition).unwrap_or(DateFilterCondition::DateIs),
content: rev.content, content: rev.content.clone(),
}
}
}
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct GridCheckboxFilter {
#[pb(index = 1)]
pub condition: CheckboxCondition,
}
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
#[repr(u8)]
pub enum CheckboxCondition {
IsChecked = 0,
IsUnChecked = 1,
}
impl std::convert::From<CheckboxCondition> for i32 {
fn from(value: CheckboxCondition) -> Self {
value as i32
}
}
impl std::default::Default for CheckboxCondition {
fn default() -> Self {
CheckboxCondition::IsChecked
}
}
impl std::convert::TryFrom<u8> for CheckboxCondition {
type Error = ErrorCode;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(CheckboxCondition::IsChecked),
1 => Ok(CheckboxCondition::IsUnChecked),
_ => Err(ErrorCode::InvalidData),
}
}
}
impl std::convert::From<Arc<GridFilterRevision>> for GridCheckboxFilter {
fn from(rev: Arc<GridFilterRevision>) -> Self {
GridCheckboxFilter {
condition: CheckboxCondition::try_from(rev.condition).unwrap_or(CheckboxCondition::IsChecked),
} }
} }
} }

View File

@ -1,9 +1,10 @@
use crate::entities::{ use crate::entities::{
CreateGridFilterParams, CreateGridFilterPayload, CreateGridGroupParams, CreateGridGroupPayload, CreateGridFilterParams, CreateGridFilterPayload, CreateGridGroupParams, CreateGridGroupPayload,
CreateGridSortParams, CreateGridSortPayload, RepeatedGridFilter, RepeatedGridGroup, RepeatedGridSort, CreateGridSortParams, CreateGridSortPayload, DeleteFilterParams, DeleteFilterPayload, RepeatedGridFilter,
RepeatedGridGroup, RepeatedGridSort,
}; };
use crate::parser::NotEmptyStr; use crate::parser::NotEmptyStr;
use crate::revision::{GridLayoutRevision, GridSettingRevision}; use crate::revision::GridLayoutRevision;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error_code::ErrorCode; use flowy_error_code::ErrorCode;
use std::collections::HashMap; use std::collections::HashMap;
@ -21,34 +22,35 @@ pub struct GridSetting {
pub sorts_by_layout_ty: HashMap<String, RepeatedGridSort>, pub sorts_by_layout_ty: HashMap<String, RepeatedGridSort>,
} }
impl std::convert::From<&GridSettingRevision> for GridSetting { //
fn from(rev: &GridSettingRevision) -> Self { // impl std::convert::From<&GridSettingRevision> for GridSetting {
let filters_by_layout_ty: HashMap<String, RepeatedGridFilter> = rev // fn from(rev: &GridSettingRevision) -> Self {
.filters // let filters_by_layout_ty: HashMap<String, RepeatedGridFilter> = rev
.iter() // .filters
.map(|(layout_rev, filter_revs)| (layout_rev.to_string(), filter_revs.into())) // .iter()
.collect(); // .map(|(layout_rev, filter_revs)| (layout_rev.to_string(), filter_revs.into()))
// .collect();
let groups_by_layout_ty: HashMap<String, RepeatedGridGroup> = rev //
.groups // let groups_by_layout_ty: HashMap<String, RepeatedGridGroup> = rev
.iter() // .groups
.map(|(layout_rev, group_revs)| (layout_rev.to_string(), group_revs.into())) // .iter()
.collect(); // .map(|(layout_rev, group_revs)| (layout_rev.to_string(), group_revs.into()))
// .collect();
let sorts_by_layout_ty: HashMap<String, RepeatedGridSort> = rev //
.sorts // let sorts_by_layout_ty: HashMap<String, RepeatedGridSort> = rev
.iter() // .sorts
.map(|(layout_rev, sort_revs)| (layout_rev.to_string(), sort_revs.into())) // .iter()
.collect(); // .map(|(layout_rev, sort_revs)| (layout_rev.to_string(), sort_revs.into()))
// .collect();
GridSetting { //
filters_by_layout_ty, // GridSetting {
groups_by_layout_ty, // filters_by_layout_ty,
sorts_by_layout_ty, // groups_by_layout_ty,
} // sorts_by_layout_ty,
} // }
} // }
// }
//
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)] #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
#[repr(u8)] #[repr(u8)]
pub enum GridLayoutType { pub enum GridLayoutType {
@ -92,7 +94,7 @@ pub struct GridSettingChangesetPayload {
pub insert_filter: Option<CreateGridFilterPayload>, pub insert_filter: Option<CreateGridFilterPayload>,
#[pb(index = 4, one_of)] #[pb(index = 4, one_of)]
pub delete_filter: Option<String>, pub delete_filter: Option<DeleteFilterPayload>,
#[pb(index = 5, one_of)] #[pb(index = 5, one_of)]
pub insert_group: Option<CreateGridGroupPayload>, pub insert_group: Option<CreateGridGroupPayload>,
@ -111,7 +113,7 @@ pub struct GridSettingChangesetParams {
pub grid_id: String, pub grid_id: String,
pub layout_type: GridLayoutType, pub layout_type: GridLayoutType,
pub insert_filter: Option<CreateGridFilterParams>, pub insert_filter: Option<CreateGridFilterParams>,
pub delete_filter: Option<String>, pub delete_filter: Option<DeleteFilterParams>,
pub insert_group: Option<CreateGridGroupParams>, pub insert_group: Option<CreateGridGroupParams>,
pub delete_group: Option<String>, pub delete_group: Option<String>,
pub insert_sort: Option<CreateGridSortParams>, pub insert_sort: Option<CreateGridSortParams>,
@ -139,7 +141,7 @@ impl TryInto<GridSettingChangesetParams> for GridSettingChangesetPayload {
let delete_filter = match self.delete_filter { let delete_filter = match self.delete_filter {
None => None, None => None,
Some(filter_id) => Some(NotEmptyStr::parse(filter_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?.0), Some(payload) => Some(payload.try_into()?),
}; };
let insert_group = match self.insert_group { let insert_group = match self.insert_group {

View File

@ -1,7 +1,9 @@
use crate::entities::FieldType;
use indexmap::IndexMap; use indexmap::IndexMap;
use nanoid::nanoid; use nanoid::nanoid;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::*; use serde_repr::*;
use std::sync::Arc;
pub fn gen_grid_filter_id() -> String { pub fn gen_grid_filter_id() -> String {
nanoid!(6) nanoid!(6)
@ -18,9 +20,17 @@ pub fn gen_grid_sort_id() -> String {
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
pub struct GridSettingRevision { pub struct GridSettingRevision {
pub layout: GridLayoutRevision, pub layout: GridLayoutRevision,
// layout:
// field_id:
// FieldType: GridFilterRevision
// FieldType: GridFilterRevision
// layout:
// field_id:
// FieldType: GridFilterRevision
// field_id:
// FieldType: GridFilterRevision
#[serde(with = "indexmap::serde_seq")] #[serde(with = "indexmap::serde_seq")]
pub filters: IndexMap<GridLayoutRevision, Vec<GridFilterRevision>>, pub filters: IndexMap<GridLayoutRevision, IndexMap<String, GridFilterRevisionMap>>,
#[serde(skip, with = "indexmap::serde_seq")] #[serde(skip, with = "indexmap::serde_seq")]
pub groups: IndexMap<GridLayoutRevision, Vec<GridGroupRevision>>, pub groups: IndexMap<GridLayoutRevision, Vec<GridGroupRevision>>,
@ -29,6 +39,78 @@ pub struct GridSettingRevision {
pub sorts: IndexMap<GridLayoutRevision, Vec<GridSortRevision>>, pub sorts: IndexMap<GridLayoutRevision, Vec<GridSortRevision>>,
} }
impl GridSettingRevision {
pub fn get_mut_filters(
&mut self,
layout: &GridLayoutRevision,
field_id: &str,
field_type: &FieldType,
) -> Option<&mut Vec<Arc<GridFilterRevision>>> {
self.filters
.get_mut(layout)
.and_then(|filter_rev_map_by_field_id| filter_rev_map_by_field_id.get_mut(field_id))
.and_then(|filter_rev_map| filter_rev_map.get_mut(field_type))
}
pub fn get_filters(
&self,
layout: &GridLayoutRevision,
field_id: &str,
field_type: &FieldType,
) -> Option<Vec<Arc<GridFilterRevision>>> {
self.filters
.get(layout)
.and_then(|filter_rev_map_by_field_id| filter_rev_map_by_field_id.get(field_id))
.and_then(|filter_rev_map| filter_rev_map.get(field_type))
.cloned()
}
pub fn insert_filter(
&mut self,
layout: &GridLayoutRevision,
field_id: &str,
field_type: &FieldType,
filter_rev: GridFilterRevision,
) {
let filter_rev_map_by_field_id = self.filters.entry(layout.clone()).or_insert_with(IndexMap::new);
let filter_rev_map = filter_rev_map_by_field_id
.entry(field_id.to_string())
.or_insert_with(GridFilterRevisionMap::new);
filter_rev_map
.entry(field_type.clone())
.or_insert_with(Vec::new)
.push(Arc::new(filter_rev))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq)]
#[serde(transparent)]
pub struct GridFilterRevisionMap {
#[serde(with = "indexmap::serde_seq")]
pub filter_by_field_type: IndexMap<FieldType, Vec<Arc<GridFilterRevision>>>,
}
impl GridFilterRevisionMap {
pub fn new() -> Self {
GridFilterRevisionMap::default()
}
}
impl std::ops::Deref for GridFilterRevisionMap {
type Target = IndexMap<FieldType, Vec<Arc<GridFilterRevision>>>;
fn deref(&self) -> &Self::Target {
&self.filter_by_field_type
}
}
impl std::ops::DerefMut for GridFilterRevisionMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.filter_by_field_type
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)] #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)] #[repr(u8)]
pub enum GridLayoutRevision { pub enum GridLayoutRevision {
@ -49,7 +131,7 @@ impl std::default::Default for GridLayoutRevision {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)]
pub struct GridFilterRevision { pub struct GridFilterRevision {
pub id: String, pub id: String,
pub field_id: String, pub field_id: String,

View File

@ -27,6 +27,9 @@ pub trait JsonDeserializer {
} }
impl GridRevisionPad { impl GridRevisionPad {
pub fn grid_id(&self) -> String {
self.grid_rev.grid_id.clone()
}
pub async fn duplicate_grid_block_meta(&self) -> (Vec<FieldRevision>, Vec<GridBlockMetaRevision>) { pub async fn duplicate_grid_block_meta(&self) -> (Vec<FieldRevision>, Vec<GridBlockMetaRevision>) {
let fields = self.grid_rev.fields.to_vec(); let fields = self.grid_rev.fields.to_vec();
@ -334,9 +337,35 @@ impl GridRevisionPad {
&self.grid_rev.setting &self.grid_rev.setting
} }
pub fn get_filters(&self, layout: Option<&GridLayoutRevision>) -> Option<&Vec<GridFilterRevision>> { /// If layout is None, then the default layout will be the read from GridSettingRevision
pub fn get_filters(
&self,
layout: Option<&GridLayoutRevision>,
field_ids: Option<Vec<String>>,
) -> Option<Vec<Arc<GridFilterRevision>>> {
let mut filter_revs = vec![];
let layout_ty = layout.unwrap_or(&self.grid_rev.setting.layout); let layout_ty = layout.unwrap_or(&self.grid_rev.setting.layout);
self.grid_rev.setting.filters.get(layout_ty) let field_revs = self.get_field_revs(None).ok()?;
field_revs.iter().for_each(|field_rev| {
let mut is_contain = true;
if let Some(field_ids) = &field_ids {
is_contain = field_ids.contains(&field_rev.id);
}
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)
{
filter_revs.append(&mut t_filter_revs);
}
}
});
Some(filter_revs)
} }
pub fn update_grid_setting_rev( pub fn update_grid_setting_rev(
@ -348,25 +377,27 @@ impl GridRevisionPad {
let layout_rev: GridLayoutRevision = changeset.layout_type.into(); let layout_rev: GridLayoutRevision = changeset.layout_type.into();
if let Some(params) = changeset.insert_filter { if let Some(params) = changeset.insert_filter {
let rev = GridFilterRevision { let filter_rev = GridFilterRevision {
id: gen_grid_filter_id(), id: gen_grid_filter_id(),
field_id: params.field_id, field_id: params.field_id.clone(),
condition: params.condition, condition: params.condition,
content: params.content, content: params.content,
}; };
grid_rev grid_rev
.setting .setting
.filters .insert_filter(&layout_rev, &params.field_id, &params.field_type, filter_rev);
.entry(layout_rev.clone())
.or_insert_with(std::vec::Vec::new)
.push(rev);
is_changed = Some(()) is_changed = Some(())
} }
if let Some(delete_filter_id) = changeset.delete_filter { if let Some(params) = changeset.delete_filter {
match grid_rev.setting.filters.get_mut(&layout_rev) { match grid_rev
Some(filters) => filters.retain(|filter| filter.id != delete_filter_id), .setting
.get_mut_filters(&layout_rev, &params.filter_id, &params.field_type)
{
Some(filters) => {
filters.retain(|filter| filter.id != params.filter_id);
}
None => { None => {
tracing::warn!("Can't find the filter with {:?}", layout_rev); tracing::warn!("Can't find the filter with {:?}", layout_rev);
} }