feat: row document (#2792)

* chore: create orphan view handler

* feat: save icon url and cover url in view

* feat: implement emoji picker UI

* chore: config ui

* chore: config ui again

* chore: replace RowPB with RowMetaPB to exposing more row information

* fix: compile error

* feat: show emoji in row

* chore: update

* test: insert emoji test

* test: add update emoji test

* test: add remove emoji test

* test: add create field tests

* test: add create row and delete row integration tests

* test: add create row from row menu

* test: document in row detail page

* test: delete, duplicate row in row detail page

* test: check the row count displayed in grid page

* test: rename existing field in grid page

* test: update field type of exisiting field in grid page

* test: delete field test

* test: add duplicate field test

* test: add hide field test

* test: add edit text cell test

* test: add insert text to text cell test

* test: add edit number cell test

* test: add edit multiple number cells

* test: add edit checkbox cell test

* feat: integrate editor into database row

* test: add edit create time and last edit time cell test

* test: add edit date cell by selecting a date test

* chore: remove unused code

* chore: update checklist bg color

* test: add update database layout test

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo
2023-06-14 22:16:33 +08:00
committed by GitHub
parent b8983e4466
commit 27dd719aa8
145 changed files with 4414 additions and 1366 deletions

View File

@ -2,6 +2,7 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use crate::entities::parser::NotEmptyStr;
use crate::entities::RowMetaPB;
use crate::services::setting::{CalendarLayout, CalendarLayoutSetting};
use super::CellIdPB;
@ -99,7 +100,7 @@ impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB {
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct CalendarEventPB {
#[pb(index = 1)]
pub row_id: String,
pub row_meta: RowMetaPB,
#[pb(index = 2)]
pub date_field_id: String,

View File

@ -6,7 +6,7 @@ use flowy_derive::ProtoBuf;
use flowy_error::{ErrorCode, FlowyError};
use crate::entities::parser::NotEmptyStr;
use crate::entities::{DatabaseLayoutPB, FieldIdPB, RowPB};
use crate::entities::{DatabaseLayoutPB, FieldIdPB, RowMetaPB};
use crate::services::database::CreateDatabaseViewParams;
/// [DatabasePB] describes how many fields and blocks the grid has
@ -19,7 +19,7 @@ pub struct DatabasePB {
pub fields: Vec<FieldIdPB>,
#[pb(index = 3)]
pub rows: Vec<RowPB>,
pub rows: Vec<RowMetaPB>,
#[pb(index = 4)]
pub layout_type: DatabaseLayoutPB,

View File

@ -4,7 +4,7 @@ use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use crate::entities::parser::NotEmptyStr;
use crate::entities::{FieldType, RowPB};
use crate::entities::{FieldType, RowMetaPB};
use crate::services::group::{GroupChangeset, GroupData, GroupSetting};
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
@ -79,7 +79,7 @@ pub struct GroupPB {
pub group_name: String,
#[pb(index = 4)]
pub rows: Vec<RowPB>,
pub rows: Vec<RowMetaPB>,
#[pb(index = 5)]
pub is_default: bool,
@ -94,7 +94,11 @@ impl std::convert::From<GroupData> for GroupPB {
field_id: group_data.field_id,
group_id: group_data.id,
group_name: group_data.name,
rows: group_data.rows.into_iter().map(RowPB::from).collect(),
rows: group_data
.rows
.into_iter()
.map(|row_detail| RowMetaPB::from(row_detail.meta))
.collect(),
is_default: group_data.is_default,
is_visible: group_data.is_visible,
}

View File

@ -4,7 +4,7 @@ use flowy_derive::ProtoBuf;
use flowy_error::ErrorCode;
use crate::entities::parser::NotEmptyStr;
use crate::entities::{GroupPB, InsertedRowPB, RowPB};
use crate::entities::{GroupPB, InsertedRowPB, RowMetaPB};
#[derive(Debug, Default, ProtoBuf)]
pub struct GroupRowsNotificationPB {
@ -21,7 +21,7 @@ pub struct GroupRowsNotificationPB {
pub deleted_rows: Vec<String>,
#[pb(index = 5)]
pub updated_rows: Vec<RowPB>,
pub updated_rows: Vec<RowMetaPB>,
}
impl std::fmt::Display for GroupRowsNotificationPB {
@ -29,7 +29,7 @@ impl std::fmt::Display for GroupRowsNotificationPB {
for inserted_row in &self.inserted_rows {
f.write_fmt(format_args!(
"Insert: {} row at {:?}",
inserted_row.row.id, inserted_row.index
inserted_row.row_meta.id, inserted_row.index
))?;
}
@ -80,7 +80,7 @@ impl GroupRowsNotificationPB {
}
}
pub fn update(group_id: String, updated_rows: Vec<RowPB>) -> Self {
pub fn update(group_id: String, updated_rows: Vec<RowMetaPB>) -> Self {
Self {
group_id,
updated_rows,

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use collab_database::rows::{Row, RowId};
use collab_database::rows::{Row, RowId, RowMeta};
use collab_database::views::RowOrder;
use flowy_derive::ProtoBuf;
@ -36,6 +36,7 @@ impl std::convert::From<Row> for RowPB {
}
}
}
impl From<RowOrder> for RowPB {
fn from(data: RowOrder) -> Self {
Self {
@ -45,6 +46,153 @@ impl From<RowOrder> for RowPB {
}
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct RowMetaPB {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub document_id: String,
#[pb(index = 3, one_of)]
pub icon: Option<String>,
#[pb(index = 4, one_of)]
pub cover: Option<String>,
}
impl std::convert::From<&RowMeta> for RowMetaPB {
fn from(row_meta: &RowMeta) -> Self {
Self {
id: row_meta.row_id.clone(),
document_id: row_meta.document_id.clone(),
icon: row_meta.icon_url.clone(),
cover: row_meta.cover_url.clone(),
}
}
}
impl std::convert::From<RowMeta> for RowMetaPB {
fn from(row_meta: RowMeta) -> Self {
Self {
id: row_meta.row_id,
document_id: row_meta.document_id,
icon: row_meta.icon_url,
cover: row_meta.cover_url,
}
}
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct UpdateRowMetaChangesetPB {
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub view_id: String,
#[pb(index = 3, one_of)]
pub icon_url: Option<String>,
#[pb(index = 4, one_of)]
pub cover_url: Option<String>,
}
#[derive(Debug)]
pub struct UpdateRowMetaParams {
pub id: String,
pub view_id: String,
pub icon_url: Option<String>,
pub cover_url: Option<String>,
}
impl TryInto<UpdateRowMetaParams> for UpdateRowMetaChangesetPB {
type Error = ErrorCode;
fn try_into(self) -> Result<UpdateRowMetaParams, Self::Error> {
let row_id = NotEmptyStr::parse(self.id)
.map_err(|_| ErrorCode::RowIdIsEmpty)?
.0;
let view_id = NotEmptyStr::parse(self.view_id)
.map_err(|_| ErrorCode::ViewIdIsInvalid)?
.0;
Ok(UpdateRowMetaParams {
id: row_id,
view_id,
icon_url: self.icon_url,
cover_url: self.cover_url,
})
}
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct UpdateRowPayloadPB {
#[pb(index = 1)]
pub row_id: String,
#[pb(index = 2, one_of)]
pub insert_document: Option<bool>,
#[pb(index = 3, one_of)]
pub insert_comment: Option<RowCommentPayloadPB>,
}
#[derive(Debug, Default, Clone)]
pub struct UpdateRowParams {
pub row_id: String,
pub insert_comment: Option<RowCommentParams>,
}
impl TryInto<UpdateRowParams> for UpdateRowPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<UpdateRowParams, Self::Error> {
let row_id = NotEmptyStr::parse(self.row_id)
.map_err(|_| ErrorCode::RowIdIsEmpty)?
.0;
let insert_comment = self
.insert_comment
.map(|comment| comment.try_into())
.transpose()?;
Ok(UpdateRowParams {
row_id,
insert_comment,
})
}
}
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct RowCommentPayloadPB {
#[pb(index = 1)]
pub uid: String,
#[pb(index = 2)]
pub comment: String,
}
#[derive(Debug, Default, Clone)]
pub struct RowCommentParams {
pub uid: String,
pub comment: String,
}
impl TryInto<RowCommentParams> for RowCommentPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<RowCommentParams, Self::Error> {
let uid = NotEmptyStr::parse(self.uid)
.map_err(|_| ErrorCode::RowIdIsEmpty)?
.0;
let comment = NotEmptyStr::parse(self.comment)
.map_err(|_| ErrorCode::RowIdIsEmpty)?
.0;
Ok(RowCommentParams { uid, comment })
}
}
#[derive(Debug, Default, ProtoBuf)]
pub struct OptionalRowPB {
#[pb(index = 1, one_of)]
@ -66,7 +214,7 @@ impl std::convert::From<Vec<RowPB>> for RepeatedRowPB {
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct InsertedRowPB {
#[pb(index = 1)]
pub row: RowPB,
pub row_meta: RowMetaPB,
#[pb(index = 2, one_of)]
pub index: Option<i32>,
@ -76,9 +224,9 @@ pub struct InsertedRowPB {
}
impl InsertedRowPB {
pub fn new(row: RowPB) -> Self {
pub fn new(row_meta: RowMetaPB) -> Self {
Self {
row,
row_meta,
index: None,
is_new: false,
}
@ -90,26 +238,20 @@ impl InsertedRowPB {
}
}
impl std::convert::From<RowPB> for InsertedRowPB {
fn from(row: RowPB) -> Self {
impl std::convert::From<RowMetaPB> for InsertedRowPB {
fn from(row_meta: RowMetaPB) -> Self {
Self {
row,
row_meta,
index: None,
is_new: false,
}
}
}
impl std::convert::From<&Row> for InsertedRowPB {
fn from(row: &Row) -> Self {
Self::from(RowPB::from(row))
}
}
impl From<InsertedRow> for InsertedRowPB {
fn from(data: InsertedRow) -> Self {
Self {
row: data.row.into(),
row_meta: data.row_meta.into(),
index: data.index,
is_new: data.is_new,
}
@ -119,18 +261,24 @@ impl From<InsertedRow> for InsertedRowPB {
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct UpdatedRowPB {
#[pb(index = 1)]
pub row: RowPB,
pub row_id: String,
// Indicates the field ids of the cells that were updated in this row.
#[pb(index = 2)]
pub field_ids: Vec<String>,
/// The meta of row was updated if this is Some.
#[pb(index = 3, one_of)]
pub row_meta: Option<RowMetaPB>,
}
impl From<UpdatedRow> for UpdatedRowPB {
fn from(data: UpdatedRow) -> Self {
let row_meta = data.row_meta.map(RowMetaPB::from);
Self {
row: data.row.into(),
row_id: data.row_id,
field_ids: data.field_ids,
row_meta,
}
}
}

View File

@ -133,6 +133,34 @@ pub(crate) async fn get_fields_handler(
data_result_ok(fields)
}
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_primary_field_handler(
data: AFPluginData<DatabaseViewIdPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> DataResult<FieldPB, FlowyError> {
let view_id = data.into_inner().value;
let database_editor = manager.get_database_with_view_id(&view_id).await?;
let mut fields = database_editor
.get_fields(&view_id, None)
.into_iter()
.filter(|field| field.is_primary)
.map(FieldPB::from)
.collect::<Vec<FieldPB>>();
if fields.is_empty() {
// The primary field should not be empty. Because it is created when the database is created.
// If it is empty, it must be a bug.
Err(FlowyError::record_not_found())
} else {
if fields.len() > 1 {
// The primary field should not be more than one. If it is more than one,
// it must be a bug.
tracing::error!("The primary field is more than one");
}
data_result_ok(fields.remove(0))
}
}
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn update_field_handler(
data: AFPluginData<FieldChangesetPB>,
@ -300,6 +328,29 @@ pub(crate) async fn get_row_handler(
data_result_ok(OptionalRowPB { row })
}
pub(crate) async fn get_row_meta_handler(
data: AFPluginData<RowIdPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> DataResult<RowMetaPB, FlowyError> {
let params: RowIdParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
match database_editor.get_row_meta(&params.view_id, &params.row_id) {
None => Err(FlowyError::record_not_found()),
Some(row) => data_result_ok(row),
}
}
pub(crate) async fn update_row_meta_handler(
data: AFPluginData<UpdateRowMetaChangesetPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> FlowyResult<()> {
let params: UpdateRowMetaParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let row_id = RowId::from(params.id.clone());
database_editor.update_row_meta(&row_id, params).await;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn delete_row_handler(
data: AFPluginData<RowIdPB>,
@ -341,7 +392,7 @@ pub(crate) async fn move_row_handler(
pub(crate) async fn create_row_handler(
data: AFPluginData<CreateRowPayloadPB>,
manager: AFPluginState<Arc<DatabaseManager2>>,
) -> DataResult<RowPB, FlowyError> {
) -> DataResult<RowMetaPB, FlowyError> {
let params: CreateRowParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let fields = database_editor.get_fields(&params.view_id, None);
@ -362,7 +413,7 @@ pub(crate) async fn create_row_handler(
.await?
{
None => Err(FlowyError::internal().context("Create row fail")),
Some(row) => data_result_ok(RowPB::from(row)),
Some(row) => data_result_ok(RowMetaPB::from(row.meta)),
}
}

View File

@ -22,6 +22,7 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
.event(DatabaseEvent::DeleteAllSorts, delete_all_sorts_handler)
// Field
.event(DatabaseEvent::GetFields, get_fields_handler)
.event(DatabaseEvent::GetPrimaryField, get_primary_field_handler)
.event(DatabaseEvent::UpdateField, update_field_handler)
.event(DatabaseEvent::UpdateFieldTypeOption, update_field_type_option_handler)
.event(DatabaseEvent::DeleteField, delete_field_handler)
@ -33,6 +34,8 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
// Row
.event(DatabaseEvent::CreateRow, create_row_handler)
.event(DatabaseEvent::GetRow, get_row_handler)
.event(DatabaseEvent::GetRowMeta, get_row_meta_handler)
.event(DatabaseEvent::UpdateRowMeta, update_row_meta_handler)
.event(DatabaseEvent::DeleteRow, delete_row_handler)
.event(DatabaseEvent::DuplicateRow, duplicate_row_handler)
.event(DatabaseEvent::MoveRow, move_row_handler)
@ -172,6 +175,9 @@ pub enum DatabaseEvent {
#[event(input = "CreateFieldPayloadPB", output = "TypeOptionPB")]
CreateTypeOption = 24,
#[event(input = "DatabaseViewIdPB", output = "FieldPB")]
GetPrimaryField = 25,
/// [CreateSelectOption] event is used to create a new select option. Returns a [SelectOptionPB] if
/// there are no errors.
#[event(input = "CreateSelectOptionPayloadPB", output = "SelectOptionPB")]
@ -195,7 +201,7 @@ pub enum DatabaseEvent {
#[event(input = "RepeatedSelectOptionPayload")]
DeleteSelectOption = 33,
#[event(input = "CreateRowPayloadPB", output = "RowPB")]
#[event(input = "CreateRowPayloadPB", output = "RowMetaPB")]
CreateRow = 50,
/// [GetRow] event is used to get the row data,[RowPB]. [OptionalRowPB] is a wrapper that enables
@ -212,6 +218,12 @@ pub enum DatabaseEvent {
#[event(input = "MoveRowPayloadPB")]
MoveRow = 54,
#[event(input = "RowIdPB", output = "RowMetaPB")]
GetRowMeta = 55,
#[event(input = "UpdateRowMetaChangesetPB")]
UpdateRowMeta = 56,
#[event(input = "CellIdPB", output = "CellPB")]
GetCell = 70,

View File

@ -31,6 +31,8 @@ pub enum DatabaseNotification {
DidReorderRows = 65,
/// Trigger after editing the row that hit the sort rule
DidReorderSingleRow = 66,
/// Trigger after updating the row meta
DidUpdateRowMeta = 67,
/// Trigger when the settings of the database are changed
DidUpdateSettings = 70,
// Trigger when the layout setting of the database is updated

View File

@ -14,19 +14,13 @@ use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_task::TaskDispatcher;
use lib_infra::future::{to_fut, Fut};
use crate::entities::{
CalendarEventPB, CellChangesetNotifyPB, CellPB, ChecklistCellDataPB, DatabaseFieldChangesetPB,
DatabasePB, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams,
FieldChangesetParams, FieldIdPB, FieldPB, FieldType, GroupPB, IndexFieldPB, InsertedRowPB,
LayoutSettingParams, NoDateCalendarEventPB, RepeatedFilterPB, RepeatedGroupPB, RepeatedSortPB,
RowPB, RowsChangePB, SelectOptionCellDataPB, SelectOptionPB, UpdateFilterParams,
UpdateSortParams, UpdatedRowPB,
};
use crate::entities::*;
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::{
apply_cell_changeset, get_cell_protobuf, AnyTypeCache, CellCache, ToCellChangeset,
};
use crate::services::database::util::database_view_setting_pb_from_view;
use crate::services::database::{RowDetail, UpdatedRow};
use crate::services::database_view::{DatabaseViewChanged, DatabaseViewData, DatabaseViews};
use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData};
use crate::services::field::{
@ -376,8 +370,8 @@ impl DatabaseEditor {
pub async fn move_row(&self, view_id: &str, from: RowId, to: RowId) {
let database = self.database.lock();
if let (Some(row), Some(from_index), Some(to_index)) = (
database.get_row(&from),
if let (Some(row_meta), Some(from_index), Some(to_index)) = (
database.get_row_meta(&from),
database.index_of_row(view_id, &from),
database.index_of_row(view_id, &to),
) {
@ -387,7 +381,7 @@ impl DatabaseEditor {
drop(database);
let delete_row_id = from.into_inner();
let insert_row = InsertedRowPB::from(&row).with_index(to_index as i32);
let insert_row = InsertedRowPB::new(RowMetaPB::from(&row_meta)).with_index(to_index as i32);
let changes =
RowsChangePB::from_move(view_id.to_string(), vec![delete_row_id], vec![insert_row]);
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
@ -401,7 +395,7 @@ impl DatabaseEditor {
view_id: &str,
group_id: Option<String>,
mut params: CreateRowParams,
) -> FlowyResult<Option<Row>> {
) -> FlowyResult<Option<RowDetail>> {
for view in self.database_views.editors().await {
view.v_will_create_row(&mut params.cells, &group_id).await;
}
@ -409,11 +403,13 @@ impl DatabaseEditor {
if let Some((index, row_order)) = result {
tracing::trace!("create row: {:?} at {}", row_order, index);
let row = self.database.lock().get_row(&row_order.id);
if let Some(row) = row {
let row_meta = self.database.lock().get_row_meta(&row_order.id);
if let (Some(row), Some(meta)) = (row, row_meta) {
let row_detail = RowDetail { row, meta };
for view in self.database_views.editors().await {
view.v_did_create_row(&row, &group_id, index).await;
view.v_did_create_row(&row_detail, &group_id, index).await;
}
return Ok(Some(row));
return Ok(Some(row_detail));
}
}
@ -491,16 +487,42 @@ impl DatabaseEditor {
Ok(())
}
pub async fn get_rows(&self, view_id: &str) -> FlowyResult<Vec<Arc<Row>>> {
pub async fn get_rows(&self, view_id: &str) -> FlowyResult<Vec<Arc<RowDetail>>> {
let view_editor = self.database_views.get_view_editor(view_id).await?;
Ok(view_editor.v_get_rows().await)
}
pub fn get_row(&self, view_id: &str, row_id: &RowId) -> Option<Row> {
if self.database.lock().views.is_row_exist(view_id, row_id) {
return None;
} else {
self.database.lock().get_row(row_id)
} else {
None
}
}
pub fn get_row_meta(&self, view_id: &str, row_id: &RowId) -> Option<RowMetaPB> {
if self.database.lock().views.is_row_exist(view_id, row_id) {
let row_meta = self.database.lock().get_row_meta(row_id)?;
Some(RowMetaPB {
id: row_id.clone().into_inner(),
document_id: row_meta.document_id,
icon: row_meta.icon_url,
cover: row_meta.cover_url,
})
} else {
tracing::warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id);
None
}
}
pub fn get_row_detail(&self, view_id: &str, row_id: &RowId) -> Option<RowDetail> {
if self.database.lock().views.is_row_exist(view_id, row_id) {
let meta = self.database.lock().get_row_meta(row_id)?;
let row = self.database.lock().get_row(row_id)?;
Some(RowDetail { row, meta })
} else {
tracing::warn!("the row:{} is exist in view:{}", row_id.as_str(), view_id);
None
}
}
@ -514,6 +536,28 @@ impl DatabaseEditor {
}
}
#[tracing::instrument(level = "trace", skip_all)]
pub async fn update_row_meta(&self, row_id: &RowId, changeset: UpdateRowMetaParams) {
self.database.lock().update_row_meta(row_id, |meta_update| {
meta_update
.insert_cover_if_not_none(changeset.cover_url)
.insert_icon_if_not_none(changeset.icon_url);
});
// Use the temporary row meta to get rid of the lock that not implement the `Send` or 'Sync' trait.
let row_meta = self.database.lock().get_row_meta(row_id);
if let Some(row_meta) = row_meta {
for view in self.database_views.editors().await {
view.v_did_update_row_meta(row_id, &row_meta).await;
}
// Notifies the client that the row meta has been updated.
send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta)
.payload(RowMetaPB::from(&row_meta))
.send();
}
}
pub async fn get_cell(&self, field_id: &str, row_id: &RowId) -> Option<Cell> {
let database = self.database.lock();
let field = database.fields.get_field(field_id)?;
@ -630,7 +674,7 @@ impl DatabaseEditor {
new_cell: Cell,
) -> FlowyResult<()> {
// Get the old row before updating the cell. It would be better to get the old cell
let old_row = { self.database.lock().get_row(&row_id) };
let old_row = { self.get_row_detail(view_id, &row_id) };
// Get all auto updated fields. It will be used to notify the frontend
// that the fields have been updated.
@ -642,19 +686,19 @@ impl DatabaseEditor {
});
});
let option_row = self.database.lock().get_row(&row_id);
if let Some(new_row) = option_row {
let updated_row = UpdatedRowPB {
row: RowPB::from(&new_row),
field_ids: vec![field_id.to_string()],
};
let changes = RowsChangePB::from_update(view_id.to_string(), updated_row);
let option_row = self.get_row_detail(view_id, &row_id);
if let Some(new_row_detail) = option_row {
let updated_row =
UpdatedRow::new(&new_row_detail.row.id).with_field_ids(vec![field_id.to_string()]);
let changes = RowsChangePB::from_update(view_id.to_string(), updated_row.into());
send_notification(view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
.send();
for view in self.database_views.editors().await {
view.v_did_update_row(&old_row, &new_row, field_id).await;
view
.v_did_update_row(&old_row, &new_row_detail, field_id)
.await;
}
}
@ -854,23 +898,23 @@ impl DatabaseEditor {
from_row: RowId,
to_row: Option<RowId>,
) -> FlowyResult<()> {
let row = self.database.lock().get_row(&from_row);
match row {
let row_detail = self.get_row_detail(view_id, &from_row);
match row_detail {
None => {
tracing::warn!(
"Move row between group failed, can not find the row:{}",
from_row
)
},
Some(row) => {
let mut row_changeset = RowChangeset::new(row.id.clone());
Some(row_detail) => {
let mut row_changeset = RowChangeset::new(row_detail.row.id.clone());
let view = self.database_views.get_view_editor(view_id).await?;
view
.v_move_group_row(&row, &mut row_changeset, to_group, to_row)
.v_move_group_row(&row_detail, &mut row_changeset, to_group, to_row)
.await;
tracing::trace!("Row data changed: {:?}", row_changeset);
self.database.lock().update_row(&row.id, |row| {
self.database.lock().update_row(&row_detail.row.id, |row| {
row.set_cells(Cells::from(row_changeset.cell_by_field_id.clone()));
});
@ -1012,8 +1056,8 @@ impl DatabaseEditor {
let rows = rows
.into_iter()
.map(|row| RowPB::from(row.as_ref()))
.collect::<Vec<RowPB>>();
.map(|row_detail| RowMetaPB::from(&row_detail.meta))
.collect::<Vec<RowMetaPB>>();
Ok(DatabasePB {
id: database_id,
fields,
@ -1150,20 +1194,37 @@ impl DatabaseViewData for DatabaseViewDataImpl {
to_fut(async move { index })
}
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<Row>)>> {
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>> {
let index = self.database.lock().index_of_row(view_id, row_id);
let row = self.database.lock().get_row(row_id);
let row_meta = self.database.lock().get_row_meta(row_id);
to_fut(async move {
match (index, row) {
(Some(index), Some(row)) => Some((index, Arc::new(row))),
match (index, row, row_meta) {
(Some(index), Some(row), Some(row_meta)) => {
let row_detail = RowDetail {
row,
meta: row_meta,
};
Some((index, Arc::new(row_detail)))
},
_ => None,
}
})
}
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>> {
let rows = self.database.lock().get_rows_for_view(view_id);
to_fut(async move { rows.into_iter().map(Arc::new).collect() })
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>> {
let database = self.database.lock();
let rows = database.get_rows_for_view(view_id);
let row_details = rows
.into_iter()
.flat_map(|row| {
database
.get_row_meta(&row.id)
.map(|meta| RowDetail { row, meta })
})
.collect::<Vec<RowDetail>>();
to_fut(async move { row_details.into_iter().map(Arc::new).collect() })
}
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>> {

View File

@ -1,5 +1,5 @@
use collab_database::rows::RowId;
use collab_database::views::{DatabaseLayout, RowOrder};
use collab_database::rows::{Row, RowId, RowMeta};
use collab_database::views::DatabaseLayout;
#[derive(Debug, Clone)]
pub enum DatabaseRowEvent {
@ -14,16 +14,48 @@ pub enum DatabaseRowEvent {
#[derive(Debug, Clone)]
pub struct InsertedRow {
pub row: RowOrder,
pub row_meta: RowMeta,
pub index: Option<i32>,
pub is_new: bool,
}
#[derive(Debug, Clone)]
pub struct UpdatedRow {
pub row: RowOrder,
// represents as the cells that were updated in this row.
pub row_id: String,
pub height: Option<i32>,
/// Indicates which cells were updated.
pub field_ids: Vec<String>,
/// The meta of row was updated if this is Some.
pub row_meta: Option<RowMeta>,
}
impl UpdatedRow {
pub fn new(row_id: &str) -> Self {
Self {
row_id: row_id.to_string(),
height: None,
field_ids: vec![],
row_meta: None,
}
}
pub fn with_field_ids(mut self, field_ids: Vec<String>) -> Self {
self.field_ids = field_ids;
self
}
pub fn with_height(mut self, height: i32) -> Self {
self.height = Some(height);
self
}
pub fn with_row_meta(mut self, row_meta: RowMeta) -> Self {
self.row_meta = Some(row_meta);
self
}
}
#[derive(Debug, Clone)]
@ -32,3 +64,9 @@ pub struct CreateDatabaseViewParams {
pub view_id: String,
pub layout_type: DatabaseLayout,
}
#[derive(Debug, Clone)]
pub struct RowDetail {
pub row: Row,
pub meta: RowMeta,
}

View File

@ -4,8 +4,8 @@ use std::sync::Arc;
use collab_database::database::{gen_database_filter_id, gen_database_sort_id};
use collab_database::fields::{Field, TypeOptionData};
use collab_database::rows::{Cells, Row, RowCell, RowId};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, RowOrder};
use collab_database::rows::{Cells, Row, RowCell, RowId, RowMeta};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
use tokio::sync::{broadcast, RwLock};
use flowy_error::{FlowyError, FlowyResult};
@ -15,12 +15,14 @@ use lib_infra::future::Fut;
use crate::entities::{
CalendarEventPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, DeleteFilterParams,
DeleteGroupParams, DeleteSortParams, FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB,
InsertedRowPB, LayoutSettingParams, RowPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
InsertedRowPB, LayoutSettingParams, RowMetaPB, RowsChangePB, SortChangesetNotificationPB, SortPB,
UpdateFilterParams, UpdateSortParams,
};
use crate::notification::{send_notification, DatabaseNotification};
use crate::services::cell::CellCache;
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow};
use crate::services::database::{
database_view_setting_pb_from_view, DatabaseRowEvent, RowDetail, 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,
@ -63,10 +65,10 @@ pub trait DatabaseViewData: Send + Sync + 'static {
fn index_of_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<usize>>;
/// Returns the `index` and `RowRevision` with row_id
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<Row>)>>;
fn get_row(&self, view_id: &str, row_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
/// Returns all the rows in the view
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>>;
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Fut<Vec<Arc<RowCell>>>;
@ -198,11 +200,24 @@ impl DatabaseViewEditor {
.await;
}
pub async fn v_did_create_row(&self, row: &Row, group_id: &Option<String>, index: usize) {
pub async fn v_did_update_row_meta(&self, row_id: &RowId, row_meta: &RowMeta) {
let update_row = UpdatedRow::new(row_id.as_str()).with_row_meta(row_meta.clone());
let changeset = RowsChangePB::from_update(self.view_id.clone(), update_row.into());
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changeset)
.send();
}
pub async fn v_did_create_row(
&self,
row_detail: &RowDetail,
group_id: &Option<String>,
index: usize,
) {
// Send the group notification if the current view has groups
match group_id.as_ref() {
None => {
let row = InsertedRowPB::from(row).with_index(index as i32);
let row = InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)).with_index(index as i32);
let changes = RowsChangePB::from_insert(self.view_id.clone(), row);
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changes)
@ -211,13 +226,13 @@ impl DatabaseViewEditor {
Some(group_id) => {
self
.mut_group_controller(|group_controller, _| {
group_controller.did_create_row(row, group_id);
group_controller.did_create_row(row_detail, group_id);
Ok(())
})
.await;
let inserted_row = InsertedRowPB {
row: RowPB::from(row),
row_meta: RowMetaPB::from(&row_detail.meta),
index: Some(index as i32),
is_new: true,
};
@ -251,10 +266,15 @@ impl DatabaseViewEditor {
/// Notify the view that the row has been updated. If the view has groups,
/// send the group notification with [GroupRowsNotificationPB]. Otherwise,
/// send the view notification with [RowsChangePB]
pub async fn v_did_update_row(&self, old_row: &Option<Row>, row: &Row, field_id: &str) {
pub async fn v_did_update_row(
&self,
old_row: &Option<RowDetail>,
row_detail: &RowDetail,
field_id: &str,
) {
let result = self
.mut_group_controller(|group_controller, field| {
Ok(group_controller.did_update_group_row(old_row, row, &field))
Ok(group_controller.did_update_group_row(old_row, row_detail, &field))
})
.await;
@ -283,10 +303,8 @@ impl DatabaseViewEditor {
}
}
} else {
let update_row = UpdatedRow {
row: RowOrder::from(row),
field_ids: vec![field_id.to_string()],
};
let update_row =
UpdatedRow::new(&row_detail.row.id).with_field_ids(vec![field_id.to_string()]);
let changeset = RowsChangePB::from_update(self.view_id.clone(), update_row.into());
send_notification(&self.view_id, DatabaseNotification::DidUpdateViewRows)
.payload(changeset)
@ -295,7 +313,7 @@ impl DatabaseViewEditor {
// Each row update will trigger a filter and sort operation. We don't want
// to block the main thread, so we spawn a new task to do the work.
let row_id = row.id.clone();
let row_id = row_detail.row.id.clone();
let weak_filter_controller = Arc::downgrade(&self.filter_controller);
let weak_sort_controller = Arc::downgrade(&self.sort_controller);
tokio::spawn(async move {
@ -314,15 +332,20 @@ impl DatabaseViewEditor {
});
}
pub async fn v_filter_rows(&self, rows: &mut Vec<Arc<Row>>) {
self.filter_controller.filter_rows(rows).await
pub async fn v_filter_rows(&self, row_details: &mut Vec<Arc<RowDetail>>) {
self.filter_controller.filter_rows(row_details).await
}
pub async fn v_sort_rows(&self, rows: &mut Vec<Arc<Row>>) {
self.sort_controller.write().await.sort_rows(rows).await
pub async fn v_sort_rows(&self, row_details: &mut Vec<Arc<RowDetail>>) {
self
.sort_controller
.write()
.await
.sort_rows(row_details)
.await
}
pub async fn v_get_rows(&self) -> Vec<Arc<Row>> {
pub async fn v_get_rows(&self) -> Vec<Arc<RowDetail>> {
let mut rows = self.delegate.get_rows(&self.view_id).await;
self.v_filter_rows(&mut rows).await;
self.v_sort_rows(&mut rows).await;
@ -331,7 +354,7 @@ impl DatabaseViewEditor {
pub async fn v_move_group_row(
&self,
row: &Row,
row_detail: &RowDetail,
row_changeset: &mut RowChangeset,
to_group_id: &str,
to_row_id: Option<RowId>,
@ -339,7 +362,7 @@ impl DatabaseViewEditor {
let result = self
.mut_group_controller(|group_controller, field| {
let move_row_context = MoveGroupRowContext {
row,
row_detail,
row_changeset,
field: field.as_ref(),
to_group_id,
@ -741,8 +764,9 @@ impl DatabaseViewEditor {
.timestamp
.unwrap_or_default();
let (_, row_detail) = self.delegate.get_row(&self.view_id, &row_id).await?;
Some(CalendarEventPB {
row_id: row_id.into_inner(),
row_meta: RowMetaPB::from(&row_detail.meta),
date_field_id: date_field.id.clone(),
title,
timestamp,
@ -792,8 +816,9 @@ impl DatabaseViewEditor {
.unwrap_or_default()
.into();
let (_, row_detail) = self.delegate.get_row(&self.view_id, &row_id).await?;
let event = CalendarEventPB {
row_id: row_id.into_inner(),
row_meta: RowMetaPB::from(&row_detail.meta),
date_field_id: calendar_setting.field_id.clone(),
title,
timestamp,

View File

@ -1,12 +1,16 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::RowId;
use lib_infra::future::{to_fut, Fut};
use crate::services::cell::CellCache;
use crate::services::database::RowDetail;
use crate::services::database_view::{
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData,
};
use crate::services::filter::{Filter, FilterController, FilterDelegate, FilterTaskHandler};
use collab_database::fields::Field;
use collab_database::rows::{Row, RowId};
use lib_infra::future::{to_fut, Fut};
use std::sync::Arc;
pub async fn make_filter_controller(
view_id: &str,
@ -56,11 +60,11 @@ impl FilterDelegate for DatabaseViewFilterDelegateImpl {
self.0.get_fields(view_id, field_ids)
}
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>> {
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>> {
self.0.get_rows(view_id)
}
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<Row>)>> {
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>> {
self.0.get_row(view_id, rows_id)
}
}

View File

@ -1,14 +1,17 @@
use std::sync::Arc;
use collab_database::fields::Field;
use tokio::sync::RwLock;
use lib_infra::future::{to_fut, Fut};
use crate::services::cell::CellCache;
use crate::services::database::RowDetail;
use crate::services::database_view::{
gen_handler_id, DatabaseViewChangedNotifier, DatabaseViewData,
};
use crate::services::filter::FilterController;
use crate::services::sort::{Sort, SortController, SortDelegate, SortTaskHandler};
use collab_database::fields::Field;
use collab_database::rows::Row;
use lib_infra::future::{to_fut, Fut};
use std::sync::Arc;
use tokio::sync::RwLock;
pub(crate) async fn make_sort_controller(
view_id: &str,
@ -56,14 +59,14 @@ impl SortDelegate for DatabaseViewSortDelegateImpl {
to_fut(async move { sort })
}
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>> {
fn get_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 rows = delegate.get_rows(&view_id).await;
filter_controller.filter_rows(&mut rows).await;
rows
let mut row_details = delegate.get_rows(&view_id).await;
filter_controller.filter_rows(&mut row_details).await;
row_details
})
}

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::{Row, RowId};
use collab_database::rows::RowId;
use nanoid::nanoid;
use tokio::sync::{broadcast, RwLock};
@ -10,7 +10,7 @@ use flowy_error::FlowyResult;
use lib_infra::future::Fut;
use crate::services::cell::CellCache;
use crate::services::database::{DatabaseRowEvent, MutexDatabase};
use crate::services::database::{DatabaseRowEvent, MutexDatabase, RowDetail};
use crate::services::database_view::{DatabaseViewData, DatabaseViewEditor};
use crate::services::group::RowChangeset;
@ -58,15 +58,15 @@ impl DatabaseViews {
pub async fn move_group_row(
&self,
view_id: &str,
row: Arc<Row>,
row_detail: Arc<RowDetail>,
to_group_id: String,
to_row_id: Option<RowId>,
recv_row_changeset: impl FnOnce(RowChangeset) -> Fut<()>,
) -> FlowyResult<()> {
let view_editor = self.get_view_editor(view_id).await?;
let mut row_changeset = RowChangeset::new(row.id.clone());
let mut row_changeset = RowChangeset::new(row_detail.row.id.clone());
view_editor
.v_move_group_row(&row, &mut row_changeset, &to_group_id, to_row_id)
.v_move_group_row(&row_detail, &mut row_changeset, &to_group_id, to_row_id)
.await;
if !row_changeset.is_empty() {

View File

@ -13,8 +13,9 @@ use flowy_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
use lib_infra::future::Fut;
use crate::entities::filter_entities::*;
use crate::entities::{FieldType, InsertedRowPB, RowPB};
use crate::entities::{FieldType, InsertedRowPB, RowMetaPB};
use crate::services::cell::{AnyTypeCache, CellCache, CellFilterCache};
use crate::services::database::RowDetail;
use crate::services::database_view::{DatabaseViewChanged, DatabaseViewChangedNotifier};
use crate::services::field::*;
use crate::services::filter::{Filter, FilterChangeset, FilterResult, FilterResultNotification};
@ -23,8 +24,8 @@ pub trait FilterDelegate: Send + Sync + 'static {
fn get_filter(&self, view_id: &str, filter_id: &str) -> Fut<Option<Arc<Filter>>>;
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<Row>>>;
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<Row>)>>;
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
fn get_row(&self, view_id: &str, rows_id: &RowId) -> Fut<Option<(usize, Arc<RowDetail>)>>;
}
pub trait FromFilterString {
@ -98,14 +99,14 @@ impl FilterController {
self.task_scheduler.write().await.add_task(task);
}
pub async fn filter_rows(&self, rows: &mut Vec<Arc<Row>>) {
pub async fn filter_rows(&self, rows: &mut Vec<Arc<RowDetail>>) {
if self.cell_filter_cache.read().is_empty() {
return;
}
let field_by_field_id = self.get_field_map().await;
rows.iter().for_each(|row| {
rows.iter().for_each(|row_detail| {
let _ = filter_row(
row,
&row_detail.row,
&self.result_by_row_id,
&field_by_field_id,
&self.cell_cache,
@ -113,10 +114,10 @@ impl FilterController {
);
});
rows.retain(|row| {
rows.retain(|row_detail| {
self
.result_by_row_id
.get(&row.id)
.get(&row_detail.row.id)
.map(|result| result.is_visible())
.unwrap_or(false)
});
@ -149,22 +150,21 @@ impl FilterController {
}
async fn filter_row(&self, row_id: RowId) -> FlowyResult<()> {
if let Some((_, row)) = self.delegate.get_row(&self.view_id, &row_id).await {
if let Some((_, row_detail)) = self.delegate.get_row(&self.view_id, &row_id).await {
let field_by_field_id = self.get_field_map().await;
let mut notification = FilterResultNotification::new(self.view_id.clone());
if let Some((row_id, is_visible)) = filter_row(
&row,
&row_detail.row,
&self.result_by_row_id,
&field_by_field_id,
&self.cell_cache,
&self.cell_filter_cache,
) {
if is_visible {
if let Some((index, row)) = self.delegate.get_row(&self.view_id, &row_id).await {
let row_pb = RowPB::from(row.as_ref());
if let Some((index, _row)) = self.delegate.get_row(&self.view_id, &row_id).await {
notification
.visible_rows
.push(InsertedRowPB::new(row_pb).with_index(index as i32))
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)).with_index(index as i32))
}
} else {
notification.invisible_rows.push(row_id);
@ -183,7 +183,7 @@ impl FilterController {
let mut visible_rows = vec![];
let mut invisible_rows = vec![];
for (index, row) in self
for (index, row_detail) in self
.delegate
.get_rows(&self.view_id)
.await
@ -191,15 +191,15 @@ impl FilterController {
.enumerate()
{
if let Some((row_id, is_visible)) = filter_row(
&row,
&row_detail.row,
&self.result_by_row_id,
&field_by_field_id,
&self.cell_cache,
&self.cell_filter_cache,
) {
if is_visible {
let row_pb = RowPB::from(row.as_ref());
visible_rows.push(InsertedRowPB::new(row_pb).with_index(index as i32))
let row_meta = RowMetaPB::from(&row_detail.meta);
visible_rows.push(InsertedRowPB::new(row_meta).with_index(index as i32))
} else {
invisible_rows.push(row_id);
}

View File

@ -1,12 +1,14 @@
use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::DecodedCellData;
use crate::services::group::controller::MoveGroupRowContext;
use crate::services::group::{GroupData, GroupSettingChangeset};
use collab_database::fields::Field;
use collab_database::rows::{Cell, Row};
use flowy_error::FlowyResult;
use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
use crate::services::cell::DecodedCellData;
use crate::services::database::RowDetail;
use crate::services::group::controller::MoveGroupRowContext;
use crate::services::group::{GroupData, GroupSettingChangeset};
/// Using polymorphism to provides the customs action for different group controller.
///
/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
@ -28,7 +30,7 @@ pub trait GroupCustomize: Send + Sync {
fn create_or_delete_group_when_cell_changed(
&mut self,
_row: &Row,
_row_detail: &RowDetail,
_old_cell_data: Option<&Self::CellData>,
_cell_data: &Self::CellData,
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
@ -40,7 +42,7 @@ pub trait GroupCustomize: Send + Sync {
///
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
cell_data: &Self::CellData,
) -> Vec<GroupRowsNotificationPB>;
@ -76,7 +78,7 @@ pub trait GroupControllerOperation: Send + Sync {
fn get_group(&self, group_id: &str) -> Option<(usize, GroupData)>;
/// Separates the rows into different groups
fn fill_groups(&mut self, rows: &[&Row], field: &Field) -> FlowyResult<()>;
fn fill_groups(&mut self, rows: &[&RowDetail], field: &Field) -> FlowyResult<()>;
/// Remove the group with from_group_id and insert it to the index with to_group_id
fn move_group(&mut self, from_group_id: &str, to_group_id: &str) -> FlowyResult<()>;
@ -84,8 +86,8 @@ pub trait GroupControllerOperation: Send + Sync {
/// Insert/Remove the row to the group if the corresponding cell data is changed
fn did_update_group_row(
&mut self,
old_row: &Option<Row>,
row: &Row,
old_row_detail: &Option<RowDetail>,
row_detail: &RowDetail,
field: &Field,
) -> FlowyResult<DidUpdateGroupRowResult>;

View File

@ -9,8 +9,11 @@ use serde::Serialize;
use flowy_error::FlowyResult;
use crate::entities::{FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB};
use crate::entities::{
FieldType, GroupChangesPB, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB,
};
use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser, DecodedCellData};
use crate::services::database::RowDetail;
use crate::services::group::action::{
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation, GroupCustomize,
};
@ -36,7 +39,7 @@ pub trait GroupController: GroupControllerOperation + Send + Sync {
fn will_create_row(&mut self, cells: &mut Cells, field: &Field, group_id: &str);
/// Called after the row was created.
fn did_create_row(&mut self, row: &Row, group_id: &str);
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str);
}
/// The [GroupsBuilder] trait is used to generate the groups for different [FieldType]
@ -62,7 +65,7 @@ pub struct GeneratedGroupConfig {
}
pub struct MoveGroupRowContext<'a> {
pub row: &'a Row,
pub row_detail: &'a RowDetail,
pub row_changeset: &'a mut RowChangeset,
pub field: &'a Field,
pub to_group_id: &'a str,
@ -134,7 +137,7 @@ where
#[allow(clippy::needless_collect)]
fn update_no_status_group(
&mut self,
row: &Row,
row_detail: &RowDetail,
other_group_changesets: &[GroupRowsNotificationPB],
) -> Option<GroupRowsNotificationPB> {
let no_status_group = self.context.get_mut_no_status_group()?;
@ -155,14 +158,16 @@ where
// which means the row should not move to the default group.
!other_group_inserted_row
.iter()
.any(|inserted_row| &inserted_row.row.id == row_id)
.any(|inserted_row| &inserted_row.row_meta.id == row_id)
})
.collect::<Vec<String>>();
let mut changeset = GroupRowsNotificationPB::new(no_status_group.id.clone());
if !no_status_group_rows.is_empty() {
changeset.inserted_rows.push(InsertedRowPB::new(row.into()));
no_status_group.add_row(row.clone());
changeset
.inserted_rows
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)));
no_status_group.add_row(row_detail.clone());
}
// [other_group_delete_rows] contains all the deleted rows except the default group.
@ -180,23 +185,23 @@ where
// out from the default_group.
!other_group_delete_rows
.iter()
.any(|row_id| &inserted_row.row.id == row_id)
.any(|row_id| &inserted_row.row_meta.id == row_id)
})
.collect::<Vec<&InsertedRowPB>>();
let mut deleted_row_ids = vec![];
for row in &no_status_group.rows {
let row_id = row.id.clone().into_inner();
for row_detail in &no_status_group.rows {
let row_id = row_detail.meta.row_id.clone();
if default_group_deleted_rows
.iter()
.any(|deleted_row| deleted_row.row.id == row_id)
.any(|deleted_row| deleted_row.row_meta.id == row_id)
{
deleted_row_ids.push(row_id);
}
}
no_status_group
.rows
.retain(|row| !deleted_row_ids.contains(&row.id));
.retain(|row_detail| !deleted_row_ids.contains(&row_detail.meta.row_id));
changeset.deleted_rows.extend(deleted_row_ids);
Some(changeset)
}
@ -225,9 +230,9 @@ where
}
#[tracing::instrument(level = "trace", skip_all, fields(row_count=%rows.len(), group_result))]
fn fill_groups(&mut self, rows: &[&Row], field: &Field) -> FlowyResult<()> {
for row in rows {
let cell = match row.cells.get(&self.grouping_field_id) {
fn fill_groups(&mut self, rows: &[&RowDetail], field: &Field) -> FlowyResult<()> {
for row_detail in rows {
let cell = match row_detail.row.cells.get(&self.grouping_field_id) {
None => self.placeholder_cell(),
Some(cell) => Some(cell.clone()),
};
@ -239,7 +244,7 @@ where
for group in self.context.groups() {
if self.can_group(&group.filter_content, &cell_data) {
grouped_rows.push(GroupedRow {
row: (*row).clone(),
row_detail: (*row_detail).clone(),
group_id: group.id.clone(),
});
}
@ -248,7 +253,7 @@ where
if !grouped_rows.is_empty() {
for group_row in grouped_rows {
if let Some(group) = self.context.get_mut_group(&group_row.group_id) {
group.add_row(group_row.row);
group.add_row(group_row.row_detail);
}
}
continue;
@ -257,7 +262,7 @@ where
match self.context.get_mut_no_status_group() {
None => {},
Some(no_status_group) => no_status_group.add_row((*row).clone()),
Some(no_status_group) => no_status_group.add_row((*row_detail).clone()),
}
}
@ -271,8 +276,8 @@ where
fn did_update_group_row(
&mut self,
old_row: &Option<Row>,
row: &Row,
old_row_detail: &Option<RowDetail>,
row_detail: &RowDetail,
field: &Field,
) -> FlowyResult<DidUpdateGroupRowResult> {
// let cell_data = row_rev.cells.get(&self.field_id).and_then(|cell_rev| {
@ -285,18 +290,21 @@ where
row_changesets: vec![],
};
if let Some(cell_data) = get_cell_data_from_row::<P>(Some(row), field) {
let old_row = old_row.as_ref();
let old_cell_data = get_cell_data_from_row::<P>(old_row, field);
if let Ok((insert, delete)) =
self.create_or_delete_group_when_cell_changed(row, old_cell_data.as_ref(), &cell_data)
{
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(
row_detail,
old_cell_data.as_ref(),
&cell_data,
) {
result.inserted_group = insert;
result.deleted_group = delete;
}
let mut changesets = self.add_or_remove_row_when_cell_changed(row, &cell_data);
if let Some(changeset) = self.update_no_status_group(row, &changesets) {
let mut changesets = self.add_or_remove_row_when_cell_changed(row_detail, &cell_data);
if let Some(changeset) = self.update_no_status_group(row_detail, &changesets) {
if !changeset.is_empty() {
changesets.push(changeset);
}
@ -350,7 +358,7 @@ where
deleted_group: None,
row_changesets: vec![],
};
let cell = match context.row.cells.get(&self.grouping_field_id) {
let cell = match context.row_detail.row.cells.get(&self.grouping_field_id) {
Some(cell) => Some(cell.clone()),
None => self.placeholder_cell(),
};
@ -358,7 +366,7 @@ where
if let Some(cell) = cell {
let cell_bytes = get_cell_protobuf(&cell, context.field, None);
let cell_data = cell_bytes.parser::<P>()?;
result.deleted_group = self.delete_group_when_move_row(context.row, &cell_data);
result.deleted_group = self.delete_group_when_move_row(&context.row_detail.row, &cell_data);
result.row_changesets = self.move_row(&cell_data, context);
} else {
tracing::warn!("Unexpected moving group row, changes should not be empty");
@ -381,7 +389,7 @@ where
}
struct GroupedRow {
row: Row,
row_detail: RowDetail,
group_id: String,
}

View File

@ -1,10 +1,12 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowPB};
use crate::entities::{FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB};
use crate::services::cell::insert_checkbox_cell;
use crate::services::database::RowDetail;
use crate::services::field::{
CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOption, CHECK, UNCHECK,
};
@ -49,25 +51,27 @@ impl GroupCustomize for CheckboxGroupController {
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
cell_data: &Self::CellData,
) -> Vec<GroupRowsNotificationPB> {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
let is_not_contained = !group.contains_row(&row.id);
let is_not_contained = !group.contains_row(&row_detail.row.id);
if group.id == CHECK {
if cell_data.is_uncheck() {
// Remove the row if the group.id is CHECK but the cell_data is UNCHECK
changeset.deleted_rows.push(row.id.clone().into_inner());
group.remove_row(&row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
group.remove_row(&row_detail.row.id);
} else {
// Add the row to the group if the group didn't contain the row
if is_not_contained {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowPB::from(row)));
group.add_row(row.clone());
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)));
group.add_row(row_detail.clone());
}
}
}
@ -75,15 +79,17 @@ impl GroupCustomize for CheckboxGroupController {
if group.id == UNCHECK {
if cell_data.is_check() {
// Remove the row if the group.id is UNCHECK but the cell_data is CHECK
changeset.deleted_rows.push(row.id.clone().into_inner());
group.remove_row(&row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
group.remove_row(&row_detail.row.id);
} else {
// Add the row to the group if the group didn't contain the row
if is_not_contained {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowPB::from(row)));
group.add_row(row.clone());
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)));
group.add_row(row_detail.clone());
}
}
}
@ -142,9 +148,9 @@ impl GroupController for CheckboxGroupController {
}
}
fn did_create_row(&mut self, row: &Row, group_id: &str) {
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row.clone())
group.add_row(row_detail.clone())
}
}
}

View File

@ -6,6 +6,7 @@ use collab_database::rows::{Cells, Row};
use flowy_error::FlowyResult;
use crate::entities::GroupChangesPB;
use crate::services::database::RowDetail;
use crate::services::group::action::{
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerOperation,
};
@ -52,7 +53,7 @@ impl GroupControllerOperation for DefaultGroupController {
Some((0, self.group.clone()))
}
fn fill_groups(&mut self, rows: &[&Row], _field: &Field) -> FlowyResult<()> {
fn fill_groups(&mut self, rows: &[&RowDetail], _field: &Field) -> FlowyResult<()> {
rows.iter().for_each(|row| {
self.group.add_row((*row).clone());
});
@ -65,8 +66,8 @@ impl GroupControllerOperation for DefaultGroupController {
fn did_update_group_row(
&mut self,
_old_row: &Option<Row>,
_row: &Row,
_old_row_detail: &Option<RowDetail>,
_row_detail: &RowDetail,
_field: &Field,
) -> FlowyResult<DidUpdateGroupRowResult> {
Ok(DidUpdateGroupRowResult {
@ -116,5 +117,5 @@ impl GroupController for DefaultGroupController {
fn will_create_row(&mut self, _cells: &mut Cells, _field: &Field, _group_id: &str) {}
fn did_create_row(&mut self, _row: &Row, _group_id: &str) {}
fn did_create_row(&mut self, _row_detail: &RowDetail, _group_id: &str) {}
}

View File

@ -1,5 +1,12 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::database::RowDetail;
use crate::services::field::{MultiSelectTypeOption, SelectOptionCellDataParser};
use crate::services::group::action::GroupCustomize;
use crate::services::group::controller::{
@ -9,11 +16,6 @@ use crate::services::group::{
add_or_remove_select_option_row, generate_select_option_groups, make_no_status_group,
move_group_row, remove_select_option_row, GeneratedGroups, GroupContext,
};
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use std::sync::Arc;
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
pub struct MultiSelectGroupConfiguration {
@ -49,12 +51,12 @@ impl GroupCustomize for MultiSelectGroupController {
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
cell_data: &Self::CellData,
) -> Vec<GroupRowsNotificationPB> {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row) {
if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row_detail) {
changesets.push(changeset);
}
});
@ -99,9 +101,9 @@ impl GroupController for MultiSelectGroupController {
}
}
fn did_create_row(&mut self, row: &Row, group_id: &str) {
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row.clone())
group.add_row(row_detail.clone())
}
}
}

View File

@ -1,11 +1,14 @@
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption};
use crate::services::group::action::GroupCustomize;
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use serde::{Deserialize, Serialize};
use crate::entities::{FieldType, GroupRowsNotificationPB, SelectOptionCellDataPB};
use crate::services::cell::insert_select_option_cell;
use crate::services::database::RowDetail;
use crate::services::field::{SelectOptionCellDataParser, SingleSelectTypeOption};
use crate::services::group::action::GroupCustomize;
use crate::services::group::controller::{
BaseGroupController, GroupController, GroupsBuilder, MoveGroupRowContext,
};
@ -13,8 +16,6 @@ use crate::services::group::controller_impls::select_option_controller::util::*;
use crate::services::group::entities::GroupData;
use crate::services::group::{make_no_status_group, GeneratedGroups, GroupContext};
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
pub struct SingleSelectGroupConfiguration {
pub hide_empty: bool,
@ -49,12 +50,12 @@ impl GroupCustomize for SingleSelectGroupController {
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
cell_data: &Self::CellData,
) -> Vec<GroupRowsNotificationPB> {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row) {
if let Some(changeset) = add_or_remove_select_option_row(group, cell_data, row_detail) {
changesets.push(changeset);
}
});
@ -99,9 +100,9 @@ impl GroupController for SingleSelectGroupController {
},
}
}
fn did_create_row(&mut self, row: &Row, group_id: &str) {
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row.clone())
group.add_row(row_detail.clone())
}
}
}

View File

@ -2,9 +2,10 @@ use collab_database::fields::Field;
use collab_database::rows::{Cell, Row};
use crate::entities::{
FieldType, GroupRowsNotificationPB, InsertedRowPB, RowPB, SelectOptionCellDataPB,
FieldType, GroupRowsNotificationPB, InsertedRowPB, RowMetaPB, SelectOptionCellDataPB,
};
use crate::services::cell::{insert_checkbox_cell, insert_select_option_cell, insert_url_cell};
use crate::services::database::RowDetail;
use crate::services::field::{SelectOption, CHECK};
use crate::services::group::controller::MoveGroupRowContext;
use crate::services::group::{GeneratedGroupConfig, Group, GroupData};
@ -12,26 +13,30 @@ use crate::services::group::{GeneratedGroupConfig, Group, GroupData};
pub fn add_or_remove_select_option_row(
group: &mut GroupData,
cell_data: &SelectOptionCellDataPB,
row: &Row,
row_detail: &RowDetail,
) -> Option<GroupRowsNotificationPB> {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
if cell_data.select_options.is_empty() {
if group.contains_row(&row.id) {
group.remove_row(&row.id);
changeset.deleted_rows.push(row.id.clone().into_inner());
if group.contains_row(&row_detail.row.id) {
group.remove_row(&row_detail.row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
}
} else {
cell_data.select_options.iter().for_each(|option| {
if option.id == group.id {
if !group.contains_row(&row.id) {
if !group.contains_row(&row_detail.row.id) {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowPB::from(row)));
group.add_row(row.clone());
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)));
group.add_row(row_detail.clone());
}
} else if group.contains_row(&row.id) {
group.remove_row(&row.id);
changeset.deleted_rows.push(row.id.clone().into_inner());
} else if group.contains_row(&row_detail.row.id) {
group.remove_row(&row_detail.row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
}
});
}
@ -69,14 +74,14 @@ pub fn move_group_row(
) -> Option<GroupRowsNotificationPB> {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
let MoveGroupRowContext {
row,
row_detail,
row_changeset,
field,
to_group_id,
to_row_id,
} = context;
let from_index = group.index_of_row(&row.id);
let from_index = group.index_of_row(&row_detail.row.id);
let to_index = match to_row_id {
None => None,
Some(to_row_id) => group.index_of_row(to_row_id),
@ -84,28 +89,40 @@ pub fn move_group_row(
// Remove the row in which group contains it
if let Some(from_index) = &from_index {
changeset.deleted_rows.push(row.id.clone().into_inner());
tracing::debug!("Group:{} remove {} at {}", group.id, row.id, from_index);
group.remove_row(&row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
tracing::debug!(
"Group:{} remove {} at {}",
group.id,
row_detail.row.id,
from_index
);
group.remove_row(&row_detail.row.id);
}
if group.id == *to_group_id {
let mut inserted_row = InsertedRowPB::new(RowPB::from(*row));
let mut inserted_row = InsertedRowPB::new(RowMetaPB::from(&row_detail.meta));
match to_index {
None => {
changeset.inserted_rows.push(inserted_row);
tracing::debug!("Group:{} append row:{}", group.id, row.id);
group.add_row(row.clone());
tracing::debug!("Group:{} append row:{}", group.id, row_detail.row.id);
group.add_row(row_detail.clone());
},
Some(to_index) => {
if to_index < group.number_of_row() {
tracing::debug!("Group:{} insert {} at {} ", group.id, row.id, to_index);
tracing::debug!(
"Group:{} insert {} at {} ",
group.id,
row_detail.row.id,
to_index
);
inserted_row.index = Some(to_index as i32);
group.insert_row(to_index, (*row).clone());
group.insert_row(to_index, (*row_detail).clone());
} else {
tracing::warn!("Move to index: {} is out of bounds", to_index);
tracing::debug!("Group:{} append row:{}", group.id, row.id);
group.add_row((*row).clone());
tracing::debug!("Group:{} append row:{}", group.id, row_detail.row.id);
group.add_row((*row_detail).clone());
}
changeset.inserted_rows.push(inserted_row);
},
@ -119,7 +136,7 @@ pub fn move_group_row(
if let Some(cell) = cell {
tracing::debug!(
"Update content of the cell in the row:{} to group:{}",
row.id,
row_detail.row.id,
group.id
);
row_changeset

View File

@ -1,14 +1,17 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::rows::{new_cell_builder, Cell, Cells, Row};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use flowy_error::FlowyResult;
use crate::entities::{
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowPB, URLCellDataPB,
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
URLCellDataPB,
};
use crate::services::cell::insert_url_cell;
use crate::services::database::RowDetail;
use crate::services::field::{URLCellData, URLCellDataParser, URLTypeOption};
use crate::services::group::action::GroupCustomize;
use crate::services::group::configuration::GroupContext;
@ -46,7 +49,7 @@ impl GroupCustomize for URLGroupController {
fn create_or_delete_group_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
_old_cell_data: Option<&Self::CellData>,
_cell_data: &Self::CellData,
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
@ -56,7 +59,7 @@ impl GroupCustomize for URLGroupController {
let cell_data: URLCellData = _cell_data.clone().into();
let group = make_group_from_url_cell(&cell_data);
let mut new_group = self.context.add_new_group(group)?;
new_group.group.rows.push(RowPB::from(row));
new_group.group.rows.push(RowMetaPB::from(&row_detail.meta));
inserted_group = Some(new_group);
}
@ -87,22 +90,24 @@ impl GroupCustomize for URLGroupController {
fn add_or_remove_row_when_cell_changed(
&mut self,
row: &Row,
row_detail: &RowDetail,
cell_data: &Self::CellData,
) -> Vec<GroupRowsNotificationPB> {
let mut changesets = vec![];
self.context.iter_mut_status_groups(|group| {
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
if group.id == cell_data.content {
if !group.contains_row(&row.id) {
if !group.contains_row(&row_detail.row.id) {
changeset
.inserted_rows
.push(InsertedRowPB::new(RowPB::from(row)));
group.add_row(row.clone());
.push(InsertedRowPB::new(RowMetaPB::from(&row_detail.meta)));
group.add_row(row_detail.clone());
}
} else if group.contains_row(&row.id) {
group.remove_row(&row.id);
changeset.deleted_rows.push(row.id.clone().into_inner());
} else if group.contains_row(&row_detail.row.id) {
group.remove_row(&row_detail.row.id);
changeset
.deleted_rows
.push(row_detail.row.id.clone().into_inner());
}
if !changeset.is_empty() {
@ -175,9 +180,9 @@ impl GroupController for URLGroupController {
}
}
fn did_create_row(&mut self, row: &Row, group_id: &str) {
fn did_create_row(&mut self, row_detail: &RowDetail, group_id: &str) {
if let Some(group) = self.context.get_mut_group(group_id) {
group.add_row(row.clone())
group.add_row(row_detail.clone())
}
}
}

View File

@ -1,10 +1,12 @@
use anyhow::bail;
use collab::core::any_map::AnyMapExtension;
use collab_database::database::gen_database_group_id;
use collab_database::rows::{Row, RowId};
use collab_database::rows::RowId;
use collab_database::views::{GroupMap, GroupMapBuilder, GroupSettingBuilder, GroupSettingMap};
use serde::{Deserialize, Serialize};
use crate::services::database::RowDetail;
#[derive(Debug, Clone, Default)]
pub struct GroupSetting {
pub id: String,
@ -133,7 +135,7 @@ pub struct GroupData {
pub name: String,
pub is_default: bool,
pub is_visible: bool,
pub(crate) rows: Vec<Row>,
pub(crate) rows: Vec<RowDetail>,
/// [filter_content] is used to determine which group the cell belongs to.
pub filter_content: String,
@ -154,11 +156,18 @@ impl GroupData {
}
pub fn contains_row(&self, row_id: &RowId) -> bool {
self.rows.iter().any(|row| &row.id == row_id)
self
.rows
.iter()
.any(|row_detail| &row_detail.row.id == row_id)
}
pub fn remove_row(&mut self, row_id: &RowId) {
match self.rows.iter().position(|row| &row.id == row_id) {
match self
.rows
.iter()
.position(|row_detail| &row_detail.row.id == row_id)
{
None => {},
Some(pos) => {
self.rows.remove(pos);
@ -166,18 +175,18 @@ impl GroupData {
}
}
pub fn add_row(&mut self, row: Row) {
match self.rows.iter().find(|r| r.id == row.id) {
pub fn add_row(&mut self, row_detail: RowDetail) {
match self.rows.iter().find(|r| r.row.id == row_detail.row.id) {
None => {
self.rows.push(row);
self.rows.push(row_detail);
},
Some(_) => {},
}
}
pub fn insert_row(&mut self, index: usize, row: Row) {
pub fn insert_row(&mut self, index: usize, row_detail: RowDetail) {
if index < self.rows.len() {
self.rows.insert(index, row);
self.rows.insert(index, row_detail);
} else {
tracing::error!(
"Insert row index:{} beyond the bounds:{},",
@ -188,7 +197,10 @@ impl GroupData {
}
pub fn index_of_row(&self, row_id: &RowId) -> Option<usize> {
self.rows.iter().position(|row| &row.id == row_id)
self
.rows
.iter()
.position(|row_detail| &row_detail.row.id == row_id)
}
pub fn number_of_row(&self) -> usize {

View File

@ -1,4 +1,12 @@
use std::sync::Arc;
use collab_database::fields::Field;
use collab_database::views::DatabaseLayout;
use flowy_error::FlowyResult;
use crate::entities::FieldType;
use crate::services::database::RowDetail;
use crate::services::group::configuration::GroupSettingReader;
use crate::services::group::controller::GroupController;
use crate::services::group::{
@ -6,12 +14,6 @@ use crate::services::group::{
GroupSettingWriter, MultiSelectGroupController, MultiSelectOptionGroupContext,
SingleSelectGroupController, SingleSelectOptionGroupContext, URLGroupContext, URLGroupController,
};
use collab_database::fields::Field;
use collab_database::rows::Row;
use collab_database::views::DatabaseLayout;
use flowy_error::FlowyResult;
use std::sync::Arc;
/// Returns a group controller.
///
@ -33,7 +35,7 @@ use std::sync::Arc;
pub async fn make_group_controller<R, W>(
view_id: String,
grouping_field: Arc<Field>,
rows: Vec<Arc<Row>>,
row_details: Vec<Arc<RowDetail>>,
setting_reader: R,
setting_writer: W,
) -> FlowyResult<Box<dyn GroupController>>
@ -99,7 +101,10 @@ where
}
// Separates the rows into different groups
let rows = rows.iter().map(|row| row.as_ref()).collect::<Vec<&Row>>();
let rows = row_details
.iter()
.map(|row| row.as_ref())
.collect::<Vec<&RowDetail>>();
group_controller.fill_groups(rows.as_slice(), &grouping_field)?;
Ok(group_controller)
}

View File

@ -16,6 +16,7 @@ use lib_infra::future::Fut;
use crate::entities::FieldType;
use crate::entities::SortChangesetNotificationPB;
use crate::services::cell::CellCache;
use crate::services::database::RowDetail;
use crate::services::database_view::{DatabaseViewChanged, DatabaseViewChangedNotifier};
use crate::services::field::{default_order, TypeOptionCellExt};
use crate::services::sort::{
@ -25,7 +26,7 @@ use crate::services::sort::{
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<Row>>>;
fn get_rows(&self, view_id: &str) -> Fut<Vec<Arc<RowDetail>>>;
fn get_field(&self, field_id: &str) -> Fut<Option<Arc<Field>>>;
fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Fut<Vec<Arc<Field>>>;
}
@ -90,13 +91,13 @@ impl SortController {
// #[tracing::instrument(name = "process_sort_task", level = "trace", skip_all, err)]
pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
let event_type = SortEvent::from_str(predicate).unwrap();
let mut rows = self.delegate.get_rows(&self.view_id).await;
let mut row_details = self.delegate.get_rows(&self.view_id).await;
match event_type {
SortEvent::SortDidChanged | SortEvent::DeleteAllSorts => {
self.sort_rows(&mut rows).await;
let row_orders = rows
self.sort_rows(&mut row_details).await;
let row_orders = row_details
.iter()
.map(|row| row.id.to_string())
.map(|row_detail| row_detail.row.id.to_string())
.collect::<Vec<String>>();
let notification = ReorderAllRowsResult {
@ -112,7 +113,7 @@ impl SortController {
},
SortEvent::RowDidChanged(row_id) => {
let old_row_index = self.row_index_cache.get(&row_id).cloned();
self.sort_rows(&mut rows).await;
self.sort_rows(&mut row_details).await;
let new_row_index = self.row_index_cache.get(&row_id).cloned();
match (old_row_index, new_row_index) {
(Some(old_row_index), Some(new_row_index)) => {
@ -150,17 +151,20 @@ impl SortController {
self.task_scheduler.write().await.add_task(task);
}
pub async fn sort_rows(&mut self, rows: &mut Vec<Arc<Row>>) {
pub async fn sort_rows(&mut self, rows: &mut Vec<Arc<RowDetail>>) {
if self.sorts.is_empty() {
return;
}
let field_revs = self.delegate.get_fields(&self.view_id, None).await;
let fields = self.delegate.get_fields(&self.view_id, None).await;
for sort in self.sorts.iter() {
rows.par_sort_by(|left, right| cmp_row(left, right, sort, &field_revs, &self.cell_cache));
rows
.par_sort_by(|left, right| cmp_row(&left.row, &right.row, sort, &fields, &self.cell_cache));
}
rows.iter().enumerate().for_each(|(index, row)| {
self.row_index_cache.insert(row.id.clone(), index);
rows.iter().enumerate().for_each(|(index, row_detail)| {
self
.row_index_cache
.insert(row_detail.row.id.clone(), index);
});
}
@ -231,10 +235,10 @@ impl SortController {
}
fn cmp_row(
left: &Arc<Row>,
right: &Arc<Row>,
left: &Row,
right: &Row,
sort: &Arc<Sort>,
field_revs: &[Arc<Field>],
fields: &[Arc<Field>],
cell_data_cache: &CellCache,
) -> Ordering {
let order = match (
@ -243,7 +247,7 @@ fn cmp_row(
) {
(Some(left_cell), Some(right_cell)) => {
let field_type = sort.field_type.clone();
match field_revs
match fields
.iter()
.find(|field_rev| field_rev.id == sort.field_id)
{