mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidUpdateCalculation,
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl From<URLCellDataPB> for URLCellData {
|
||||
|
||||
impl AsRef<str> for URLCellData {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.url
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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) => {},
|
||||
_ => {},
|
||||
}
|
||||
})
|
||||
|
Reference in New Issue
Block a user