|
@@ -1,14 +1,23 @@
|
|
-use crate::entities::{
|
|
|
|
- FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter, GridTextFilter,
|
|
|
|
-};
|
|
|
|
|
|
+use crate::dart_notification::{send_dart_notification, GridNotification};
|
|
|
|
+use crate::entities::{FieldType, GridBlockChangeset};
|
|
use crate::services::block_manager::GridBlockManager;
|
|
use crate::services::block_manager::GridBlockManager;
|
|
|
|
+use crate::services::field::{
|
|
|
|
+ CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption,
|
|
|
|
+ SingleSelectTypeOption, URLTypeOption,
|
|
|
|
+};
|
|
|
|
+use crate::services::filter::filter_cache::{
|
|
|
|
+ reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
|
|
|
|
+};
|
|
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
|
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
|
-use crate::services::row::GridBlockSnapshot;
|
|
|
|
|
|
+use crate::services::row::{AnyCellData, CellFilterOperation, GridBlockSnapshot};
|
|
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
|
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
|
|
|
+
|
|
use flowy_error::FlowyResult;
|
|
use flowy_error::FlowyResult;
|
|
use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
|
|
use flowy_grid_data_model::revision::{CellRevision, FieldId, FieldRevision, RowRevision};
|
|
use flowy_sync::client_grid::GridRevisionPad;
|
|
use flowy_sync::client_grid::GridRevisionPad;
|
|
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
|
use flowy_sync::entities::grid::GridSettingChangesetParams;
|
|
|
|
+use rayon::prelude::*;
|
|
|
|
+
|
|
use std::collections::HashMap;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
use tokio::sync::RwLock;
|
|
@@ -19,8 +28,8 @@ 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_cache: Arc<RwLock<FilterResultCache>>,
|
|
|
|
|
|
+ filter_cache: Arc<FilterCache>,
|
|
|
|
+ filter_result_cache: Arc<FilterResultCache>,
|
|
}
|
|
}
|
|
impl GridFilterService {
|
|
impl GridFilterService {
|
|
pub async fn new<S: GridServiceTaskScheduler>(
|
|
pub async fn new<S: GridServiceTaskScheduler>(
|
|
@@ -29,13 +38,14 @@ impl GridFilterService {
|
|
scheduler: S,
|
|
scheduler: S,
|
|
) -> Self {
|
|
) -> Self {
|
|
let grid_id = grid_pad.read().await.grid_id();
|
|
let grid_id = grid_pad.read().await.grid_id();
|
|
- let filter_cache = Arc::new(RwLock::new(FilterCache::from_grid_pad(&grid_pad).await));
|
|
|
|
- let filter_result_cache = Arc::new(RwLock::new(FilterResultCache::default()));
|
|
|
|
|
|
+ let scheduler = Arc::new(scheduler);
|
|
|
|
+ let filter_cache = FilterCache::from_grid_pad(&grid_pad).await;
|
|
|
|
+ let filter_result_cache = FilterResultCache::new();
|
|
Self {
|
|
Self {
|
|
grid_id,
|
|
grid_id,
|
|
grid_pad,
|
|
grid_pad,
|
|
block_manager,
|
|
block_manager,
|
|
- scheduler: Arc::new(scheduler),
|
|
|
|
|
|
+ scheduler,
|
|
filter_cache,
|
|
filter_cache,
|
|
filter_result_cache,
|
|
filter_result_cache,
|
|
}
|
|
}
|
|
@@ -51,20 +61,42 @@ impl GridFilterService {
|
|
.map(|field_rev| (field_rev.id.clone(), field_rev))
|
|
.map(|field_rev| (field_rev.id.clone(), field_rev))
|
|
.collect::<HashMap<String, Arc<FieldRevision>>>();
|
|
.collect::<HashMap<String, Arc<FieldRevision>>>();
|
|
|
|
|
|
- let mut show_rows = vec![];
|
|
|
|
- let mut hide_rows = vec![];
|
|
|
|
- for block in task_context.blocks {
|
|
|
|
- block.row_revs.iter().for_each(|row_rev| {
|
|
|
|
- let result = filter_row(row_rev, &self.filter_cache, &self.filter_result_cache, &field_revs);
|
|
|
|
|
|
+ let mut changesets = vec![];
|
|
|
|
+ for (index, block) in task_context.blocks.into_iter().enumerate() {
|
|
|
|
+ let row_ids = block
|
|
|
|
+ .row_revs
|
|
|
|
+ .par_iter()
|
|
|
|
+ .flat_map(|row_rev| {
|
|
|
|
+ let filter_result_cache = self.filter_result_cache.clone();
|
|
|
|
+ let filter_cache = self.filter_cache.clone();
|
|
|
|
+ filter_row(index, row_rev, filter_cache, filter_result_cache, &field_revs)
|
|
|
|
+ })
|
|
|
|
+ .collect::<Vec<String>>();
|
|
|
|
|
|
- if result.is_row_hidden() {
|
|
|
|
- hide_rows.push(result.row_id);
|
|
|
|
|
|
+ let mut visible_rows = vec![];
|
|
|
|
+ let mut hide_rows = vec![];
|
|
|
|
+ for row_id in row_ids {
|
|
|
|
+ if self
|
|
|
|
+ .filter_result_cache
|
|
|
|
+ .get(&row_id)
|
|
|
|
+ .map(|result| result.is_visible())
|
|
|
|
+ .unwrap_or(false)
|
|
|
|
+ {
|
|
|
|
+ visible_rows.push(row_id);
|
|
} else {
|
|
} else {
|
|
- show_rows.push(result.row_id);
|
|
|
|
|
|
+ hide_rows.push(row_id);
|
|
}
|
|
}
|
|
- });
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let changeset = GridBlockChangeset {
|
|
|
|
+ block_id: block.block_id,
|
|
|
|
+ hide_rows,
|
|
|
|
+ visible_rows,
|
|
|
|
+ ..Default::default()
|
|
|
|
+ };
|
|
|
|
+ changesets.push(changeset);
|
|
}
|
|
}
|
|
- self.notify(hide_rows, show_rows).await;
|
|
|
|
|
|
+ self.notify(changesets).await;
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
@@ -74,18 +106,17 @@ impl GridFilterService {
|
|
}
|
|
}
|
|
|
|
|
|
if let Some(filter_id) = &changeset.insert_filter {
|
|
if let Some(filter_id) = &changeset.insert_filter {
|
|
- let mut cache = self.filter_cache.write().await;
|
|
|
|
let field_ids = Some(vec![filter_id.field_id.clone()]);
|
|
let field_ids = Some(vec![filter_id.field_id.clone()]);
|
|
- reload_filter_cache(&mut cache, field_ids, &self.grid_pad).await;
|
|
|
|
|
|
+ reload_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
|
|
}
|
|
}
|
|
|
|
|
|
if let Some(filter_id) = &changeset.delete_filter {
|
|
if let Some(filter_id) = &changeset.delete_filter {
|
|
- self.filter_cache.write().await.remove(filter_id);
|
|
|
|
|
|
+ self.filter_cache.remove(filter_id);
|
|
}
|
|
}
|
|
|
|
|
|
if let Ok(blocks) = self.block_manager.get_block_snapshots(None).await {
|
|
if let Ok(blocks) = self.block_manager.get_block_snapshots(None).await {
|
|
- let task = self.gen_task(blocks).await;
|
|
|
|
- let _ = self.scheduler.register_task(task).await;
|
|
|
|
|
|
+ let _task = self.gen_task(blocks).await;
|
|
|
|
+ // let _ = self.scheduler.register_task(task).await;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -101,25 +132,125 @@ impl GridFilterService {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async fn notify(&self, _hide_rows: Vec<String>, _show_rows: Vec<String>) {
|
|
|
|
- // let notification = GridNotification {};
|
|
|
|
- // send_dart_notification(grid_id, GridNotification::DidUpdateGridBlock)
|
|
|
|
- // .payload(notification)
|
|
|
|
- // .send();
|
|
|
|
|
|
+ async fn notify(&self, changesets: Vec<GridBlockChangeset>) {
|
|
|
|
+ for changeset in changesets {
|
|
|
|
+ send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridBlock)
|
|
|
|
+ .payload(changeset)
|
|
|
|
+ .send();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// Return None if there is no change in this row after applying the filter
|
|
fn filter_row(
|
|
fn filter_row(
|
|
|
|
+ index: usize,
|
|
row_rev: &Arc<RowRevision>,
|
|
row_rev: &Arc<RowRevision>,
|
|
- _filter_cache: &Arc<RwLock<FilterCache>>,
|
|
|
|
- _filter_result_cache: &Arc<RwLock<FilterResultCache>>,
|
|
|
|
- _field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
|
|
|
-) -> FilterResult {
|
|
|
|
- let filter_result = FilterResult::new(row_rev);
|
|
|
|
- row_rev.cells.iter().for_each(|(_k, cell_rev)| {
|
|
|
|
- let _cell_rev: &CellRevision = cell_rev;
|
|
|
|
- });
|
|
|
|
- filter_result
|
|
|
|
|
|
+ filter_cache: Arc<FilterCache>,
|
|
|
|
+ filter_result_cache: Arc<FilterResultCache>,
|
|
|
|
+ field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
|
|
|
+) -> Option<String> {
|
|
|
|
+ let mut result = filter_result_cache
|
|
|
|
+ .entry(row_rev.id.clone())
|
|
|
|
+ .or_insert(FilterResult::new(index as i32, row_rev));
|
|
|
|
+
|
|
|
|
+ for (field_id, cell_rev) in row_rev.cells.iter() {
|
|
|
|
+ match filter_cell(field_revs, result.value_mut(), &filter_cache, field_id, cell_rev) {
|
|
|
|
+ None => {}
|
|
|
|
+ Some(_) => {
|
|
|
|
+ return Some(row_rev.id.clone());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ None
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Return None if there is no change in this cell after applying the filter
|
|
|
|
+fn filter_cell(
|
|
|
|
+ field_revs: &HashMap<FieldId, Arc<FieldRevision>>,
|
|
|
|
+ filter_result: &mut FilterResult,
|
|
|
|
+ filter_cache: &Arc<FilterCache>,
|
|
|
|
+ field_id: &str,
|
|
|
|
+ cell_rev: &CellRevision,
|
|
|
|
+) -> Option<()> {
|
|
|
|
+ let field_rev = field_revs.get(field_id)?;
|
|
|
|
+ let field_type = FieldType::from(field_rev.field_type_rev);
|
|
|
|
+ let field_type_rev = field_type.clone().into();
|
|
|
|
+ let filter_id = FilterId {
|
|
|
|
+ field_id: field_id.to_owned(),
|
|
|
|
+ field_type,
|
|
|
|
+ };
|
|
|
|
+ let any_cell_data = AnyCellData::try_from(cell_rev).ok()?;
|
|
|
|
+ let is_hidden = match &filter_id.field_type {
|
|
|
|
+ FieldType::RichText => filter_cache.text_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<RichTextTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data.into(), filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::Number => filter_cache.number_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<NumberTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data, filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::DateTime => filter_cache.date_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<DateTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data, filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::SingleSelect => filter_cache.select_option_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<SingleSelectTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data.into(), filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::MultiSelect => filter_cache.select_option_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<MultiSelectTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data.into(), filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::Checkbox => filter_cache.checkbox_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<CheckboxTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data.into(), filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ FieldType::URL => filter_cache.url_filter.get(&filter_id).and_then(|filter| {
|
|
|
|
+ Some(
|
|
|
|
+ field_rev
|
|
|
|
+ .get_type_option_entry::<URLTypeOption>(field_type_rev)?
|
|
|
|
+ .apply_filter(any_cell_data.into(), filter.value()),
|
|
|
|
+ )
|
|
|
|
+ }),
|
|
|
|
+ }?;
|
|
|
|
+
|
|
|
|
+ let is_visible = !is_hidden;
|
|
|
|
+ match filter_result.visible_by_field_id.get(&filter_id) {
|
|
|
|
+ None => {
|
|
|
|
+ if is_visible {
|
|
|
|
+ None
|
|
|
|
+ } else {
|
|
|
|
+ filter_result.visible_by_field_id.insert(filter_id, is_visible);
|
|
|
|
+ Some(())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Some(old_is_visible) => {
|
|
|
|
+ if old_is_visible != &is_visible {
|
|
|
|
+ filter_result.visible_by_field_id.insert(filter_id, is_visible);
|
|
|
|
+ Some(())
|
|
|
|
+ } else {
|
|
|
|
+ None
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
pub struct GridFilterChangeset {
|
|
pub struct GridFilterChangeset {
|
|
@@ -150,144 +281,3 @@ impl std::convert::From<&GridSettingChangesetParams> for GridFilterChangeset {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
-#[derive(Default)]
|
|
|
|
-struct FilterResultCache {
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
- rows: HashMap<String, FilterResult>,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl FilterResultCache {
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
- fn insert(&mut self, row_id: &str, result: FilterResult) {
|
|
|
|
- self.rows.insert(row_id.to_owned(), result);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#[derive(Default)]
|
|
|
|
-struct FilterResult {
|
|
|
|
- row_id: String,
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
- cell_by_field_id: HashMap<String, bool>,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl FilterResult {
|
|
|
|
- fn new(row_rev: &RowRevision) -> Self {
|
|
|
|
- Self {
|
|
|
|
- row_id: row_rev.id.clone(),
|
|
|
|
- cell_by_field_id: row_rev.cells.iter().map(|(k, _)| (k.clone(), true)).collect(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
- fn update_cell(&mut self, cell_id: &str, exist: bool) {
|
|
|
|
- self.cell_by_field_id.insert(cell_id.to_owned(), exist);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fn is_row_hidden(&self) -> bool {
|
|
|
|
- todo!()
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#[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();
|
|
|
|
- let _ = reload_filter_cache(&mut this, None, grid_pad).await;
|
|
|
|
- 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);
|
|
|
|
- let field_type: FieldType = field_rev.field_type_rev.into();
|
|
|
|
- match &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<&Arc<FieldRevision>> for FilterId {
|
|
|
|
- fn from(rev: &Arc<FieldRevision>) -> Self {
|
|
|
|
- Self {
|
|
|
|
- field_id: rev.id.clone(),
|
|
|
|
- field_type: rev.field_type_rev.into(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|