chore: kanban filter mvp (#4935)

* chore: add filter controller in group controller

* chore: enable url filter

* chore: bump collab rev

* chore: fix test

* chore: bump collab rev
This commit is contained in:
Richard Shiue
2024-03-21 12:54:56 +08:00
committed by GitHub
parent ef9891abfe
commit 37f521ae57
40 changed files with 661 additions and 429 deletions

View File

@ -23,19 +23,19 @@ pub struct DateFilterPB {
}
#[derive(Deserialize, Serialize, Default, Clone, Debug)]
pub struct DateFilterContentPB {
pub struct DateFilterContent {
pub start: Option<i64>,
pub end: Option<i64>,
pub timestamp: Option<i64>,
}
impl ToString for DateFilterContentPB {
impl ToString for DateFilterContent {
fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
impl FromStr for DateFilterContentPB {
impl FromStr for DateFilterContent {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -89,7 +89,7 @@ impl ParseFilterData for DateFilterPB {
..Default::default()
};
if let Ok(content) = DateFilterContentPB::from_str(&content) {
if let Ok(content) = DateFilterContent::from_str(&content) {
date_filter.start = content.start;
date_filter.end = content.end;
date_filter.timestamp = content.timestamp;

View File

@ -6,6 +6,7 @@ use collab_database::views::RowOrder;
use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use lib_infra::validator_fn::required_not_empty_str;
use serde::{Deserialize, Serialize};
use validator::Validate;
use crate::entities::parser::NotEmptyStr;
@ -49,7 +50,7 @@ impl From<RowOrder> for RowPB {
}
}
#[derive(Debug, Default, Clone, ProtoBuf)]
#[derive(Debug, Default, Clone, ProtoBuf, Serialize, Deserialize)]
pub struct RowMetaPB {
#[pb(index = 1)]
pub id: String,

View File

@ -1078,7 +1078,7 @@ impl DatabaseEditor {
pub async fn group_by_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
let view = self.database_views.get_view_editor(view_id).await?;
view.v_grouping_by_field(field_id).await?;
view.v_group_by_field(field_id).await?;
Ok(())
}

View File

@ -1,12 +1,12 @@
#![allow(clippy::while_let_loop)]
use crate::entities::{
CalculationChangesetNotificationPB, DatabaseViewSettingPB, FilterChangesetNotificationPB,
GroupChangesPB, GroupRowsNotificationPB, ReorderAllRowsPB, ReorderSingleRowPB,
RowsVisibilityChangePB, SortChangesetNotificationPB,
GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB, ReorderAllRowsPB, ReorderSingleRowPB,
RowMetaPB, RowsChangePB, RowsVisibilityChangePB, SortChangesetNotificationPB,
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::filter::FilterResultNotification;
use crate::services::sort::{InsertSortedRowResult, ReorderAllRowsResult, ReorderSingleRowResult};
use crate::services::sort::{InsertRowResult, ReorderAllRowsResult, ReorderSingleRowResult};
use async_stream::stream;
use futures::stream::StreamExt;
use tokio::sync::broadcast;
@ -16,7 +16,7 @@ pub enum DatabaseViewChanged {
FilterNotification(FilterResultNotification),
ReorderAllRowsNotification(ReorderAllRowsResult),
ReorderSingleRowNotification(ReorderSingleRowResult),
InsertSortedRowNotification(InsertSortedRowResult),
InsertRowNotification(InsertRowResult),
CalculationValueNotification(CalculationChangesetNotificationPB),
}
@ -79,7 +79,17 @@ impl DatabaseViewChangedReceiverRunner {
.payload(reorder_row)
.send()
},
DatabaseViewChanged::InsertSortedRowNotification(_result) => {},
DatabaseViewChanged::InsertRowNotification(result) => {
let inserted_row = InsertedRowPB {
row_meta: RowMetaPB::from(result.row),
index: Some(result.index as i32),
is_new: true,
};
let changes = RowsChangePB::from_insert(inserted_row);
send_notification(&result.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
.send();
},
DatabaseViewChanged::CalculationValueNotification(notification) => send_notification(
&notification.view_id,
DatabaseNotification::DidUpdateCalculation,

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use collab_database::database::{gen_database_calculation_id, gen_database_sort_id, gen_row_id};
use collab_database::fields::{Field, TypeOptionData};
use collab_database::fields::Field;
use collab_database::rows::{Cells, Row, RowDetail, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView};
use lib_infra::util::timestamp;
@ -16,9 +16,9 @@ use lib_dispatch::prelude::af_spawn;
use crate::entities::{
CalendarEventPB, CreateRowParams, CreateRowPayloadPB, DatabaseLayoutMetaPB,
DatabaseLayoutSettingPB, DeleteSortPayloadPB, FieldType, FieldVisibility, GroupChangesPB,
GroupPB, InsertedRowPB, LayoutSettingChangeset, LayoutSettingParams,
RemoveCalculationChangesetPB, ReorderSortPayloadPB, RowMetaPB, RowsChangePB,
SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB,
GroupPB, LayoutSettingChangeset, LayoutSettingParams, RemoveCalculationChangesetPB,
ReorderSortPayloadPB, RowMetaPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
UpdateCalculationChangesetPB, UpdateSortPayloadPB,
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController};
@ -26,7 +26,7 @@ use crate::services::cell::{CellBuilder, CellCache};
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow};
use crate::services::database_view::view_filter::make_filter_controller;
use crate::services::database_view::view_group::{
get_cell_for_row, get_cells_for_field, new_group_controller, new_group_controller_with_field,
get_cell_for_row, get_cells_for_field, new_group_controller,
};
use crate::services::database_view::view_operation::DatabaseViewOperation;
use crate::services::database_view::view_sort::make_sort_controller;
@ -68,10 +68,6 @@ impl DatabaseViewEditor {
) -> FlowyResult<Self> {
let (notifier, _) = broadcast::channel(100);
af_spawn(DatabaseViewChangedReceiverRunner(Some(notifier.subscribe())).run());
// Group
let group_controller = Arc::new(RwLock::new(
new_group_controller(view_id.clone(), delegate.clone()).await?,
));
// Filter
let filter_controller = make_filter_controller(
@ -92,6 +88,17 @@ impl DatabaseViewEditor {
)
.await;
// Group
let group_controller = Arc::new(RwLock::new(
new_group_controller(
view_id.clone(),
delegate.clone(),
filter_controller.clone(),
None,
)
.await?,
));
// Calculations
let calculations_controller =
make_calculations_controller(&view_id, delegate.clone(), notifier.clone()).await;
@ -142,7 +149,7 @@ impl DatabaseViewEditor {
if let Some(controller) = self.group_controller.read().await.as_ref() {
let field = self
.delegate
.get_field(controller.field_id())
.get_field(controller.get_grouping_field_id())
.ok_or_else(|| FlowyError::internal().with_context("Failed to get grouping field"))?;
controller.will_create_row(&mut cells, &field, &group_id);
}
@ -168,24 +175,20 @@ impl DatabaseViewEditor {
pub async fn v_did_create_row(&self, row_detail: &RowDetail, index: usize) {
// Send the group notification if the current view has groups
if let Some(controller) = self.group_controller.write().await.as_mut() {
let changesets = controller.did_create_row(row_detail, index);
let mut row_details = vec![Arc::new(row_detail.clone())];
self.v_filter_rows(&mut row_details).await;
for changeset in changesets {
notify_did_update_group_rows(changeset).await;
if let Some(row_detail) = row_details.pop() {
let changesets = controller.did_create_row(&row_detail, index);
for changeset in changesets {
notify_did_update_group_rows(changeset).await;
}
}
}
let inserted_row = InsertedRowPB {
row_meta: RowMetaPB::from(row_detail),
index: Some(index as i32),
is_new: true,
};
let changes = RowsChangePB::from_insert(inserted_row);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
.send();
self
.gen_did_create_row_view_tasks(row_detail.row.clone())
.gen_did_create_row_view_tasks(index, row_detail.clone())
.await;
}
@ -239,34 +242,41 @@ impl DatabaseViewEditor {
row_detail: &RowDetail,
field_id: String,
) {
let result = self
.mut_group_controller(|group_controller, field| {
Ok(group_controller.did_update_group_row(old_row, row_detail, &field))
})
.await;
if let Some(controller) = self.group_controller.write().await.as_mut() {
let field = self.delegate.get_field(controller.get_grouping_field_id());
if let Some(Ok(result)) = result {
let mut group_changes = GroupChangesPB {
view_id: self.view_id.clone(),
..Default::default()
};
if let Some(inserted_group) = result.inserted_group {
tracing::trace!("Create group after editing the row: {:?}", inserted_group);
group_changes.inserted_groups.push(inserted_group);
}
if let Some(delete_group) = result.deleted_group {
tracing::trace!("Delete group after editing the row: {:?}", delete_group);
group_changes.deleted_groups.push(delete_group.group_id);
}
if let Some(field) = field {
let mut row_details = vec![Arc::new(row_detail.clone())];
self.v_filter_rows(&mut row_details).await;
if !group_changes.is_empty() {
notify_did_update_num_of_groups(&self.view_id, group_changes).await;
}
if let Some(row_detail) = row_details.pop() {
let result = controller.did_update_group_row(old_row, &row_detail, &field);
for changeset in result.row_changesets {
if !changeset.is_empty() {
tracing::trace!("Group change after editing the row: {:?}", changeset);
notify_did_update_group_rows(changeset).await;
if let Ok(result) = result {
let mut group_changes = GroupChangesPB {
view_id: self.view_id.clone(),
..Default::default()
};
if let Some(inserted_group) = result.inserted_group {
tracing::trace!("Create group after editing the row: {:?}", inserted_group);
group_changes.inserted_groups.push(inserted_group);
}
if let Some(delete_group) = result.deleted_group {
tracing::trace!("Delete group after editing the row: {:?}", delete_group);
group_changes.deleted_groups.push(delete_group.group_id);
}
if !group_changes.is_empty() {
notify_did_update_num_of_groups(&self.view_id, group_changes).await;
}
for changeset in result.row_changesets {
if !changeset.is_empty() {
tracing::trace!("Group change after editing the row: {:?}", changeset);
notify_did_update_group_rows(changeset).await;
}
}
}
}
}
}
@ -376,7 +386,7 @@ impl DatabaseViewEditor {
pub async fn is_grouping_field(&self, field_id: &str) -> bool {
match self.group_controller.read().await.as_ref() {
Some(group_controller) => group_controller.field_id() == field_id,
Some(group_controller) => group_controller.get_grouping_field_id() == field_id,
None => false,
}
}
@ -385,7 +395,7 @@ impl DatabaseViewEditor {
pub async fn v_initialize_new_group(&self, field_id: &str) -> FlowyResult<()> {
let is_grouping_field = self.is_grouping_field(field_id).await;
if !is_grouping_field {
self.v_grouping_by_field(field_id).await?;
self.v_group_by_field(field_id).await?;
if let Some(view) = self.delegate.get_view(&self.view_id).await {
let setting = database_view_setting_pb_from_view(view);
@ -399,7 +409,7 @@ impl DatabaseViewEditor {
let mut old_field: Option<Field> = None;
let result = if let Some(controller) = self.group_controller.write().await.as_mut() {
let create_group_results = controller.create_group(name.to_string())?;
old_field = self.delegate.get_field(controller.field_id());
old_field = self.delegate.get_field(controller.get_grouping_field_id());
create_group_results
} else {
(None, None)
@ -432,7 +442,7 @@ impl DatabaseViewEditor {
None => return Ok(RowsChangePB::default()),
};
let old_field = self.delegate.get_field(controller.field_id());
let old_field = self.delegate.get_field(controller.get_grouping_field_id());
let (row_ids, type_option_data) = controller.delete_group(group_id)?;
drop(group_controller);
@ -462,12 +472,15 @@ impl DatabaseViewEditor {
}
pub async fn v_update_group(&self, changeset: Vec<GroupChangeset>) -> FlowyResult<()> {
let mut type_option_data = TypeOptionData::new();
let mut type_option_data = None;
let (old_field, updated_groups) =
if let Some(controller) = self.group_controller.write().await.as_mut() {
let old_field = self.delegate.get_field(controller.field_id());
let old_field = self.delegate.get_field(controller.get_grouping_field_id());
let (updated_groups, new_type_option) = controller.apply_group_changeset(&changeset)?;
type_option_data.extend(new_type_option);
if new_type_option.is_some() {
type_option_data = new_type_option;
}
(old_field, updated_groups)
} else {
@ -475,7 +488,7 @@ impl DatabaseViewEditor {
};
if let Some(old_field) = old_field {
if !type_option_data.is_empty() {
if let Some(type_option_data) = type_option_data {
self
.delegate
.update_field(type_option_data, old_field)
@ -644,15 +657,20 @@ impl DatabaseViewEditor {
#[tracing::instrument(level = "trace", skip(self), err)]
pub async fn v_modify_filters(&self, changeset: FilterChangeset) -> FlowyResult<()> {
let filter_controller = self.filter_controller.clone();
// self.delegate.insert_filter(&self.view_id, filter.clone());
let notification = filter_controller.apply_changeset(changeset).await;
drop(filter_controller);
let notification = self.filter_controller.apply_changeset(changeset).await;
notify_did_update_filter(notification).await;
let group_controller_read_guard = self.group_controller.read().await;
let grouping_field_id = group_controller_read_guard
.as_ref()
.map(|controller| controller.get_grouping_field_id().to_string());
drop(group_controller_read_guard);
if let Some(field_id) = grouping_field_id {
self.v_group_by_field(&field_id).await?;
}
Ok(())
}
@ -786,11 +804,6 @@ impl DatabaseViewEditor {
#[tracing::instrument(level = "trace", skip_all, err)]
pub async fn v_did_update_field_type_option(&self, old_field: &Field) -> FlowyResult<()> {
let field_id = &old_field.id;
// If the id of the grouping field is equal to the updated field's id, then we need to
// update the group setting
if self.is_grouping_field(field_id).await {
self.v_grouping_by_field(field_id).await?;
}
if let Some(field) = self.delegate.get_field(field_id) {
self
@ -808,36 +821,58 @@ impl DatabaseViewEditor {
notify_did_update_filter(notification).await;
}
}
// If the id of the grouping field is equal to the updated field's id, then we need to
// update the group setting
if self.is_grouping_field(field_id).await {
self.v_group_by_field(field_id).await?;
}
Ok(())
}
/// Called when a grouping field is updated.
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn v_grouping_by_field(&self, field_id: &str) -> FlowyResult<()> {
pub async fn v_group_by_field(&self, field_id: &str) -> FlowyResult<()> {
if let Some(field) = self.delegate.get_field(field_id) {
let new_group_controller =
new_group_controller_with_field(self.view_id.clone(), self.delegate.clone(), field).await?;
tracing::trace!("create new group controller");
let new_groups = new_group_controller
.get_all_groups()
.into_iter()
.map(|group| GroupPB::from(group.clone()))
.collect();
let new_group_controller = new_group_controller(
self.view_id.clone(),
self.delegate.clone(),
self.filter_controller.clone(),
Some(field),
)
.await?;
*self.group_controller.write().await = Some(new_group_controller);
let changeset = GroupChangesPB {
view_id: self.view_id.clone(),
initial_groups: new_groups,
..Default::default()
};
if let Some(controller) = &new_group_controller {
let new_groups = controller
.get_all_groups()
.into_iter()
.map(|group| GroupPB::from(group.clone()))
.collect();
debug_assert!(!changeset.is_empty());
if !changeset.is_empty() {
send_notification(&changeset.view_id, DatabaseNotification::DidGroupByField)
.payload(changeset)
.send();
let changeset = GroupChangesPB {
view_id: self.view_id.clone(),
initial_groups: new_groups,
..Default::default()
};
tracing::trace!("notify did group by field1");
debug_assert!(!changeset.is_empty());
if !changeset.is_empty() {
send_notification(&changeset.view_id, DatabaseNotification::DidGroupByField)
.payload(changeset)
.send();
}
}
tracing::trace!("notify did group by field2");
*self.group_controller.write().await = new_group_controller;
tracing::trace!("did write group_controller to cache");
}
Ok(())
}
@ -958,8 +993,13 @@ impl DatabaseViewEditor {
}
// initialize the group controller if the current layout support grouping
*self.group_controller.write().await =
new_group_controller(self.view_id.clone(), self.delegate.clone()).await?;
*self.group_controller.write().await = new_group_controller(
self.view_id.clone(),
self.delegate.clone(),
self.filter_controller.clone(),
None,
)
.await?;
let payload = DatabaseLayoutMetaPB {
view_id: self.view_id.clone(),
@ -1019,7 +1059,7 @@ impl DatabaseViewEditor {
.read()
.await
.as_ref()
.map(|group| group.field_id().to_owned())?;
.map(|controller| controller.get_grouping_field_id().to_owned())?;
let field = self.delegate.get_field(&group_field_id)?;
let mut write_guard = self.group_controller.write().await;
if let Some(group_controller) = &mut *write_guard {
@ -1054,7 +1094,7 @@ impl DatabaseViewEditor {
});
}
async fn gen_did_create_row_view_tasks(&self, row: Row) {
async fn gen_did_create_row_view_tasks(&self, preliminary_index: usize, row_detail: RowDetail) {
let weak_sort_controller = Arc::downgrade(&self.sort_controller);
let weak_calculations_controller = Arc::downgrade(&self.calculations_controller);
af_spawn(async move {
@ -1062,12 +1102,14 @@ impl DatabaseViewEditor {
sort_controller
.read()
.await
.did_create_row(row.id.clone())
.did_create_row(preliminary_index, &row_detail)
.await;
}
if let Some(calculations_controller) = weak_calculations_controller.upgrade() {
calculations_controller.did_receive_row_changed(row).await;
calculations_controller
.did_receive_row_changed(row_detail.row.clone())
.await;
}
});
}

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::RowId;
use collab_database::rows::{RowDetail, RowId};
use flowy_error::FlowyResult;
use lib_infra::future::{to_fut, Fut};
@ -9,60 +9,61 @@ use lib_infra::future::{to_fut, Fut};
use crate::entities::FieldType;
use crate::services::database_view::DatabaseViewOperation;
use crate::services::field::RowSingleCellData;
use crate::services::filter::FilterController;
use crate::services::group::{
find_suitable_grouping_field, make_group_controller, GroupContextDelegate, GroupController,
GroupControllerDelegate, GroupSetting,
make_group_controller, GroupContextDelegate, GroupController, GroupControllerDelegate,
GroupSetting,
};
pub async fn new_group_controller_with_field(
view_id: String,
delegate: Arc<dyn DatabaseViewOperation>,
grouping_field: Field,
) -> FlowyResult<Box<dyn GroupController>> {
let configuration_delegate = GroupControllerDelegateImpl(delegate.clone());
let rows = delegate.get_rows(&view_id).await;
make_group_controller(view_id, grouping_field, rows, configuration_delegate).await
}
pub async fn new_group_controller(
view_id: String,
delegate: Arc<dyn DatabaseViewOperation>,
filter_controller: Arc<FilterController>,
grouping_field: Option<Field>,
) -> FlowyResult<Option<Box<dyn GroupController>>> {
let fields = delegate.get_fields(&view_id, None).await;
let controller_delegate = GroupControllerDelegateImpl(delegate.clone());
// Read the grouping field or find a new grouping field
let mut grouping_field = controller_delegate
.get_group_setting(&view_id)
.await
.and_then(|setting| {
fields
.iter()
.find(|field| field.id == setting.field_id)
.cloned()
});
let layout = delegate.get_layout_for_view(&view_id);
// If the view is a board and the grouping field is empty, we need to find a new grouping field
if layout.is_board() && grouping_field.is_none() {
grouping_field = find_suitable_grouping_field(&fields);
if !delegate.get_layout_for_view(&view_id).is_board() {
return Ok(None);
}
if let Some(grouping_field) = grouping_field {
let rows = delegate.get_rows(&view_id).await;
Ok(Some(
make_group_controller(view_id, grouping_field, rows, controller_delegate).await?,
))
} else {
Ok(None)
}
let controller_delegate = GroupControllerDelegateImpl {
delegate: delegate.clone(),
filter_controller: filter_controller.clone(),
};
let grouping_field = match grouping_field {
Some(field) => Some(field),
None => {
let group_setting = controller_delegate.get_group_setting(&view_id).await;
let fields = delegate.get_fields(&view_id, None).await;
group_setting
.and_then(|setting| {
fields
.iter()
.find(|field| field.id == setting.field_id)
.cloned()
})
.or_else(|| find_suitable_grouping_field(&fields))
},
};
let controller = match grouping_field {
Some(field) => Some(make_group_controller(&view_id, field, controller_delegate).await?),
None => None,
};
Ok(controller)
}
pub(crate) struct GroupControllerDelegateImpl(pub Arc<dyn DatabaseViewOperation>);
pub(crate) struct GroupControllerDelegateImpl {
delegate: Arc<dyn DatabaseViewOperation>,
filter_controller: Arc<FilterController>,
}
impl GroupContextDelegate for GroupControllerDelegateImpl {
fn get_group_setting(&self, view_id: &str) -> Fut<Option<Arc<GroupSetting>>> {
let mut settings = self.0.get_group_setting(view_id);
let mut settings = self.delegate.get_group_setting(view_id);
to_fut(async move {
if settings.is_empty() {
None
@ -75,19 +76,30 @@ impl GroupContextDelegate for GroupControllerDelegateImpl {
fn get_configuration_cells(&self, view_id: &str, field_id: &str) -> Fut<Vec<RowSingleCellData>> {
let field_id = field_id.to_owned();
let view_id = view_id.to_owned();
let delegate = self.0.clone();
let delegate = self.delegate.clone();
to_fut(async move { get_cells_for_field(delegate, &view_id, &field_id).await })
}
fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut<FlowyResult<()>> {
self.0.insert_group_setting(view_id, group_setting);
self.delegate.insert_group_setting(view_id, group_setting);
to_fut(async move { Ok(()) })
}
}
impl GroupControllerDelegate for GroupControllerDelegateImpl {
fn get_field(&self, field_id: &str) -> Option<Field> {
self.0.get_field(field_id)
self.delegate.get_field(field_id)
}
fn get_all_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>> {
let view_id = view_id.to_string();
let delegate = self.delegate.clone();
let filter_controller = self.filter_controller.clone();
to_fut(async move {
let mut row_details = delegate.get_rows(&view_id).await;
filter_controller.filter_rows(&mut row_details).await;
row_details
})
}
}
@ -143,3 +155,15 @@ pub(crate) async fn get_cells_for_field(
vec![]
}
fn find_suitable_grouping_field(fields: &[Field]) -> Option<Field> {
let groupable_field = fields
.iter()
.find(|field| FieldType::from(field.field_type).can_be_group());
if let Some(field) = groupable_field {
Some(field.clone())
} else {
fields.iter().find(|field| field.is_primary).cloned()
}
}

View File

@ -70,6 +70,16 @@ impl SortDelegate for DatabaseViewSortDelegateImpl {
})
}
fn filter_row(&self, row_detail: &RowDetail) -> Fut<bool> {
let filter_controller = self.filter_controller.clone();
let row_detail = row_detail.clone();
to_fut(async move {
let mut row_details = vec![Arc::new(row_detail)];
filter_controller.filter_rows(&mut row_details).await;
!row_details.is_empty()
})
}
fn get_field(&self, field_id: &str) -> Option<Field> {
self.delegate.get_field(field_id)
}

View File

@ -71,7 +71,7 @@ impl From<URLCellDataPB> for URLCellData {
impl AsRef<str> for URLCellData {
fn as_ref(&self) -> &str {
&self.url
&self.data
}
}

View File

@ -10,7 +10,7 @@ use flowy_error::{FlowyError, FlowyResult};
use lib_infra::box_any::BoxAny;
use crate::entities::{
CheckboxFilterPB, ChecklistFilterPB, DateFilterContentPB, DateFilterPB, FieldType, FilterType,
CheckboxFilterPB, ChecklistFilterPB, DateFilterContent, DateFilterPB, FieldType, FilterType,
InsertedRowPB, NumberFilterPB, RelationFilterPB, SelectOptionFilterPB, TextFilterPB,
};
use crate::services::field::SelectOptionIds;
@ -337,7 +337,7 @@ impl<'a> From<&'a Filter> for FilterMap {
},
FieldType::DateTime | FieldType::LastEditedTime | FieldType::CreatedTime => {
let filter = condition_and_content.cloned::<DateFilterPB>()?;
let content = DateFilterContentPB {
let content = DateFilterContent {
start: filter.start,
end: filter.end,
timestamp: filter.timestamp,

View File

@ -79,8 +79,9 @@ pub trait GroupCustomize: Send + Sync {
fn update_type_option_when_update_group(
&mut self,
_changeset: &GroupChangeset,
_type_option: &mut Self::GroupTypeOption,
) {
_type_option: &Self::GroupTypeOption,
) -> Option<Self::GroupTypeOption> {
None
}
fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str);
@ -97,7 +98,7 @@ pub trait GroupCustomize: Send + Sync {
///
pub trait GroupController: Send + Sync {
/// Returns the id of field that is being used to group the rows
fn field_id(&self) -> &str;
fn get_grouping_field_id(&self) -> &str;
/// Returns all of the groups currently managed by the controller
fn get_all_groups(&self) -> Vec<&GroupData>;
@ -189,7 +190,7 @@ pub trait GroupController: Send + Sync {
fn apply_group_changeset(
&mut self,
changesets: &[GroupChangeset],
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)>;
) -> FlowyResult<(Vec<GroupPB>, Option<TypeOptionData>)>;
/// Called before the row was created.
fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str);

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cells, Row, RowDetail, RowId};
use futures::executor::block_on;
use lib_infra::future::Fut;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -24,6 +25,8 @@ use crate::services::group::{GroupChangeset, GroupsBuilder, MoveGroupRowContext}
pub trait GroupControllerDelegate: Send + Sync + 'static {
fn get_field(&self, field_id: &str) -> Option<Field>;
fn get_all_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
}
/// [BaseGroupController] is a generic group controller that provides customized implementations
@ -159,7 +162,7 @@ where
G: GroupsBuilder<Context = GroupControllerContext<C>, GroupTypeOption = T>,
Self: GroupCustomize<GroupTypeOption = T>,
{
fn field_id(&self) -> &str {
fn get_grouping_field_id(&self) -> &str {
&self.grouping_field_id
}
@ -228,12 +231,13 @@ where
row_detail: &RowDetail,
index: usize,
) -> Vec<GroupRowsNotificationPB> {
let mut changesets: Vec<GroupRowsNotificationPB> = vec![];
let cell = match row_detail.row.cells.get(&self.grouping_field_id) {
None => self.placeholder_cell(),
Some(cell) => Some(cell.clone()),
};
let mut changesets: Vec<GroupRowsNotificationPB> = vec![];
if let Some(cell) = cell {
let cell_data = <T as TypeOption>::CellData::from(&cell);
@ -245,7 +249,7 @@ where
let changeset = GroupRowsNotificationPB::insert(
group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
row_meta: (*row_detail).clone().into(),
index: Some(index as i32),
is_new: true,
}],
@ -256,15 +260,15 @@ where
if !suitable_group_ids.is_empty() {
for group_id in suitable_group_ids.iter() {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row_detail.clone());
group.add_row((*row_detail).clone());
}
}
} else if let Some(no_status_group) = self.context.get_mut_no_status_group() {
no_status_group.add_row(row_detail.clone());
no_status_group.add_row((*row_detail).clone());
let changeset = GroupRowsNotificationPB::insert(
no_status_group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
row_meta: (*row_detail).clone().into(),
index: Some(index as i32),
is_new: true,
}],
@ -282,18 +286,12 @@ where
row_detail: &RowDetail,
field: &Field,
) -> FlowyResult<DidUpdateGroupRowResult> {
// let cell_data = row_rev.cells.get(&self.field_id).and_then(|cell_rev| {
// let cell_data: Option<P> = get_type_cell_data(cell_rev, field_rev, None);
// cell_data
// });
let mut result = DidUpdateGroupRowResult {
inserted_group: None,
deleted_group: None,
row_changesets: vec![],
};
if let Some(cell_data) = get_cell_data_from_row::<P>(Some(&row_detail.row), field) {
let _old_row = old_row_detail.as_ref();
let old_cell_data =
get_cell_data_from_row::<P>(old_row_detail.as_ref().map(|detail| &detail.row), field);
if let Ok((insert, delete)) = self.create_or_delete_group_when_cell_changed(
@ -376,7 +374,7 @@ where
}
fn delete_group(&mut self, group_id: &str) -> FlowyResult<(Vec<RowId>, Option<TypeOptionData>)> {
let group = if group_id != self.field_id() {
let group = if group_id != self.get_grouping_field_id() {
self.get_group(group_id)
} else {
None
@ -399,17 +397,26 @@ where
fn apply_group_changeset(
&mut self,
changeset: &[GroupChangeset],
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)> {
) -> FlowyResult<(Vec<GroupPB>, Option<TypeOptionData>)> {
// update group visibility
for group_changeset in changeset.iter() {
self.context.update_group(group_changeset)?;
}
let mut type_option = self.get_grouping_field_type_option().ok_or_else(|| {
// update group name
let type_option = self.get_grouping_field_type_option().ok_or_else(|| {
FlowyError::internal().with_context("Failed to get grouping field type option")
})?;
let mut updated_type_option = None;
for group_changeset in changeset.iter() {
self.update_type_option_when_update_group(group_changeset, &mut type_option);
if let Some(type_option) =
self.update_type_option_when_update_group(group_changeset, &type_option)
{
updated_type_option = Some(type_option);
break;
}
}
let updated_groups = changeset
@ -421,7 +428,10 @@ where
})
.collect::<Vec<_>>();
Ok((updated_groups, type_option.into()))
Ok((
updated_groups,
updated_type_option.map(|type_option| type_option.into()),
))
}
fn will_create_row(&self, cells: &mut Cells, field: &Field, group_id: &str) {

View File

@ -1,4 +1,5 @@
use async_trait::async_trait;
use std::sync::Arc;
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cells, Row, RowDetail, RowId};
@ -10,7 +11,9 @@ use crate::entities::{
use crate::services::group::action::{
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupController,
};
use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext};
use crate::services::group::{
GroupChangeset, GroupControllerDelegate, GroupData, MoveGroupRowContext,
};
/// A [DefaultGroupController] is used to handle the group actions for the [FieldType] that doesn't
/// implement its own group controller. The default group controller only contains one group, which
@ -19,23 +22,24 @@ use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext};
pub struct DefaultGroupController {
pub field_id: String,
pub group: GroupData,
pub delegate: Arc<dyn GroupControllerDelegate>,
}
const DEFAULT_GROUP_CONTROLLER: &str = "DefaultGroupController";
impl DefaultGroupController {
pub fn new(field: &Field) -> Self {
pub fn new(field: &Field, delegate: Arc<dyn GroupControllerDelegate>) -> Self {
let group = GroupData::new(DEFAULT_GROUP_CONTROLLER.to_owned(), field.id.clone(), true);
Self {
field_id: field.id.clone(),
group,
delegate,
}
}
}
#[async_trait]
impl GroupController for DefaultGroupController {
fn field_id(&self) -> &str {
fn get_grouping_field_id(&self) -> &str {
&self.field_id
}
@ -70,12 +74,12 @@ impl GroupController for DefaultGroupController {
row_detail: &RowDetail,
index: usize,
) -> Vec<GroupRowsNotificationPB> {
self.group.add_row(row_detail.clone());
self.group.add_row((*row_detail).clone());
vec![GroupRowsNotificationPB::insert(
self.group.id.clone(),
vec![InsertedRowPB {
row_meta: row_detail.into(),
row_meta: (*row_detail).clone().into(),
index: Some(index as i32),
is_new: true,
}],
@ -128,8 +132,8 @@ impl GroupController for DefaultGroupController {
fn apply_group_changeset(
&mut self,
_changeset: &[GroupChangeset],
) -> FlowyResult<(Vec<GroupPB>, TypeOptionData)> {
Ok((Vec::new(), TypeOptionData::default()))
) -> FlowyResult<(Vec<GroupPB>, Option<TypeOptionData>)> {
Ok((Vec::new(), None))
}
fn will_create_row(&self, _cells: &mut Cells, _field: &Field, _group_id: &str) {}

View File

@ -124,10 +124,11 @@ impl GroupCustomize for MultiSelectGroupController {
fn update_type_option_when_update_group(
&mut self,
changeset: &GroupChangeset,
type_option: &mut Self::GroupTypeOption,
) {
type_option: &Self::GroupTypeOption,
) -> Option<Self::GroupTypeOption> {
if let Some(name) = &changeset.name {
let mut new_type_option = type_option.clone();
let select_option = type_option
.options
.iter()
@ -139,6 +140,10 @@ impl GroupCustomize for MultiSelectGroupController {
..select_option.to_owned()
};
new_type_option.insert_option(new_select_option);
Some(new_type_option)
} else {
None
}
}

View File

@ -126,10 +126,11 @@ impl GroupCustomize for SingleSelectGroupController {
fn update_type_option_when_update_group(
&mut self,
changeset: &GroupChangeset,
type_option: &mut Self::GroupTypeOption,
) {
type_option: &Self::GroupTypeOption,
) -> Option<Self::GroupTypeOption> {
if let Some(name) = &changeset.name {
let mut new_type_option = type_option.clone();
let select_option = type_option
.options
.iter()
@ -141,6 +142,10 @@ impl GroupCustomize for SingleSelectGroupController {
..select_option.to_owned()
};
new_type_option.insert_option(new_select_option);
Some(new_type_option)
} else {
None
}
}

View File

@ -86,9 +86,8 @@ impl RowChangeset {
err
)]
pub async fn make_group_controller<D>(
view_id: String,
view_id: &str,
grouping_field: Field,
row_details: Vec<Arc<RowDetail>>,
delegate: D,
) -> FlowyResult<Box<dyn GroupController>>
where
@ -102,68 +101,81 @@ where
match grouping_field_type {
FieldType::SingleSelect => {
let configuration =
SingleSelectGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone())
.await?;
let configuration = SingleSelectGroupControllerContext::new(
view_id.to_string(),
grouping_field.clone(),
delegate.clone(),
)
.await?;
let controller =
SingleSelectGroupController::new(&grouping_field, configuration, delegate).await?;
SingleSelectGroupController::new(&grouping_field, configuration, delegate.clone()).await?;
group_controller = Box::new(controller);
},
FieldType::MultiSelect => {
let configuration =
MultiSelectGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone())
.await?;
let configuration = MultiSelectGroupControllerContext::new(
view_id.to_string(),
grouping_field.clone(),
delegate.clone(),
)
.await?;
let controller =
MultiSelectGroupController::new(&grouping_field, configuration, delegate).await?;
MultiSelectGroupController::new(&grouping_field, configuration, delegate.clone()).await?;
group_controller = Box::new(controller);
},
FieldType::Checkbox => {
let configuration =
CheckboxGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone())
.await?;
let configuration = CheckboxGroupControllerContext::new(
view_id.to_string(),
grouping_field.clone(),
delegate.clone(),
)
.await?;
let controller =
CheckboxGroupController::new(&grouping_field, configuration, delegate).await?;
CheckboxGroupController::new(&grouping_field, configuration, delegate.clone()).await?;
group_controller = Box::new(controller);
},
FieldType::URL => {
let configuration =
URLGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()).await?;
let controller = URLGroupController::new(&grouping_field, configuration, delegate).await?;
let configuration = URLGroupControllerContext::new(
view_id.to_string(),
grouping_field.clone(),
delegate.clone(),
)
.await?;
let controller =
URLGroupController::new(&grouping_field, configuration, delegate.clone()).await?;
group_controller = Box::new(controller);
},
FieldType::DateTime => {
let configuration =
DateGroupControllerContext::new(view_id, grouping_field.clone(), delegate.clone()).await?;
let controller = DateGroupController::new(&grouping_field, configuration, delegate).await?;
let configuration = DateGroupControllerContext::new(
view_id.to_string(),
grouping_field.clone(),
delegate.clone(),
)
.await?;
let controller =
DateGroupController::new(&grouping_field, configuration, delegate.clone()).await?;
group_controller = Box::new(controller);
},
_ => {
group_controller = Box::new(DefaultGroupController::new(&grouping_field));
group_controller = Box::new(DefaultGroupController::new(
&grouping_field,
delegate.clone(),
));
},
}
// Separates the rows into different groups
let row_details = delegate.get_all_rows(view_id).await;
let rows = row_details
.iter()
.map(|row| row.as_ref())
.collect::<Vec<_>>();
group_controller.fill_groups(rows.as_slice(), &grouping_field)?;
Ok(group_controller)
}
#[tracing::instrument(level = "debug", skip_all)]
pub fn find_suitable_grouping_field(fields: &[Field]) -> Option<Field> {
let groupable_field = fields
.iter()
.find(|field| FieldType::from(field.field_type).can_be_group());
if let Some(field) = groupable_field {
Some(field.clone())
} else {
fields.iter().find(|field| field.is_primary).cloned()
}
}
/// Returns a `default` group configuration for the [Field]
///
/// # Arguments

View File

@ -21,14 +21,14 @@ use crate::services::field::{
default_order, TimestampCellData, TimestampCellDataWrapper, TypeOptionCellExt,
};
use crate::services::sort::{
InsertSortedRowResult, ReorderAllRowsResult, ReorderSingleRowResult, Sort, SortChangeset,
SortCondition,
InsertRowResult, ReorderAllRowsResult, ReorderSingleRowResult, Sort, SortChangeset, SortCondition,
};
pub trait SortDelegate: Send + Sync {
fn get_sort(&self, view_id: &str, sort_id: &str) -> Fut<Option<Arc<Sort>>>;
/// Returns all the rows after applying grid's filter
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
fn filter_row(&self, row_detail: &RowDetail) -> Fut<bool>;
fn get_field(&self, field_id: &str) -> Option<Field>;
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Field>>;
}
@ -94,14 +94,27 @@ impl SortController {
}
}
pub async fn did_create_row(&self, row_id: RowId) {
pub async fn did_create_row(&self, preliminary_index: usize, row_detail: &RowDetail) {
if !self.delegate.filter_row(row_detail).await {
return;
}
if !self.sorts.is_empty() {
self
.gen_task(
SortEvent::NewRowInserted(row_id),
SortEvent::NewRowInserted(row_detail.clone()),
QualityOfService::Background,
)
.await;
} else {
let result = InsertRowResult {
view_id: self.view_id.clone(),
row: row_detail.clone(),
index: preliminary_index,
};
let _ = self
.notifier
.send(DatabaseViewChanged::InsertRowNotification(result));
}
}
@ -162,22 +175,20 @@ impl SortController {
_ => tracing::trace!("The row index cache is outdated"),
}
},
SortEvent::NewRowInserted(row_id) => {
SortEvent::NewRowInserted(row_detail) => {
self.sort_rows(&mut row_details).await;
let row_index = self.row_index_cache.get(&row_id).cloned();
let row_index = self.row_index_cache.get(&row_detail.row.id).cloned();
match row_index {
Some(row_index) => {
let notification = InsertSortedRowResult {
row_id: row_id.clone(),
let notification = InsertRowResult {
view_id: self.view_id.clone(),
row: row_detail.clone(),
index: row_index,
};
self.row_index_cache.insert(row_id, row_index);
self.row_index_cache.insert(row_detail.row.id, row_index);
let _ = self
.notifier
.send(DatabaseViewChanged::InsertSortedRowNotification(
notification,
));
.send(DatabaseViewChanged::InsertRowNotification(notification));
},
_ => tracing::trace!("The row index cache is outdated"),
}
@ -353,7 +364,7 @@ fn cmp_cell(
enum SortEvent {
SortDidChanged,
RowDidChanged(RowId),
NewRowInserted(RowId),
NewRowInserted(RowDetail),
DeleteAllSorts,
}

View File

@ -2,7 +2,7 @@ use std::cmp::Ordering;
use anyhow::bail;
use collab::core::any_map::AnyMapExtension;
use collab_database::rows::RowId;
use collab_database::rows::{RowDetail, RowId};
use collab_database::views::{SortMap, SortMapBuilder};
#[derive(Debug, Clone)]
@ -107,9 +107,9 @@ pub struct ReorderSingleRowResult {
}
#[derive(Clone)]
pub struct InsertSortedRowResult {
pub struct InsertRowResult {
pub view_id: String,
pub row_id: RowId,
pub row: RowDetail,
pub index: usize,
}

View File

@ -212,7 +212,7 @@ async fn assert_sort_changed(
old_row_orders.insert(changed.new_index, old);
assert_eq!(old_row_orders, new_row_orders);
},
DatabaseViewChanged::InsertSortedRowNotification(_changed) => {},
DatabaseViewChanged::InsertRowNotification(_changed) => {},
_ => {},
}
})