From 4856a024a2a4f015ab57c02d53eb4d3f7e409d1f Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 18 Aug 2022 15:02:44 +0800 Subject: [PATCH] chore: Update row when moving row caused cell data changed --- .../presentation/card/board_text_cell.dart | 2 + .../board_column/board_column_data.dart | 31 +++++--- .../flowy-grid/src/entities/cell_entities.rs | 6 +- .../flowy-grid/src/services/block_editor.rs | 4 +- .../flowy-grid/src/services/block_manager.rs | 18 ++--- .../flowy-grid/src/services/grid_editor.rs | 28 ++++---- .../src/services/grid_view_editor.rs | 6 +- .../src/services/grid_view_manager.rs | 23 ++++-- .../group/group_generator/checkbox_group.rs | 19 ++++- .../group/group_generator/group_controller.rs | 59 ++++----------- .../group_generator/select_option_group.rs | 71 +++++++++++++++---- .../src/services/group/group_service.rs | 5 +- .../tests/grid/block_test/row_test.rs | 4 +- .../tests/grid/block_test/script.rs | 4 +- .../src/revision/grid_block.rs | 17 ++++- .../src/client_grid/block_revision_pad.rs | 4 +- 16 files changed, 181 insertions(+), 120 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart index 2da156ded8..5148b2c438 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart @@ -1,8 +1,10 @@ import 'package:app_flowy/plugins/board/application/card/board_text_cell_bloc.dart'; import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; class BoardTextCell extends StatefulWidget { final GridCellControllerBuilder cellControllerBuilder; diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart index 4a04f4b662..6e184761c5 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column_data.dart @@ -84,20 +84,28 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin { Log.debug( '[$AFBoardColumnDataController] $columnData insert $item at $index'); - if (columnData._items.length > index) { - columnData._items.insert(index, item); + if (_containsItem(item)) { + return false; } else { - columnData._items.add(item); - } + if (columnData._items.length > index) { + columnData._items.insert(index, item); + } else { + columnData._items.add(item); + } - if (notify) notifyListeners(); - return true; + if (notify) notifyListeners(); + return true; + } } bool add(AFColumnItem item, {bool notify = true}) { - columnData._items.add(item); - if (notify) notifyListeners(); - return true; + if (_containsItem(item)) { + return false; + } else { + columnData._items.add(item); + if (notify) notifyListeners(); + return true; + } } /// Replace the item at index with the [newItem]. @@ -114,6 +122,11 @@ class AFBoardColumnDataController extends ChangeNotifier with EquatableMixin { notifyListeners(); } + + bool _containsItem(AFColumnItem item) { + return columnData._items.indexWhere((element) => element.id == item.id) != + -1; + } } /// [AFBoardColumnData] represents the data of each Column of the Board. diff --git a/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs index 1b86eb1e65..f7aa30649c 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs @@ -1,7 +1,7 @@ use flowy_derive::ProtoBuf; use flowy_error::ErrorCode; use flowy_grid_data_model::parser::NotEmptyStr; -use flowy_grid_data_model::revision::{CellRevision, RowMetaChangeset}; +use flowy_grid_data_model::revision::{CellRevision, RowChangeset}; use std::collections::HashMap; #[derive(ProtoBuf, Default)] @@ -135,7 +135,7 @@ pub struct CellChangesetPB { pub content: Option, } -impl std::convert::From for RowMetaChangeset { +impl std::convert::From for RowChangeset { fn from(changeset: CellChangesetPB) -> Self { let mut cell_by_field_id = HashMap::with_capacity(1); let field_id = changeset.field_id; @@ -144,7 +144,7 @@ impl std::convert::From for RowMetaChangeset { }; cell_by_field_id.insert(field_id, cell_rev); - RowMetaChangeset { + RowChangeset { row_id: changeset.row_id, height: None, visibility: None, diff --git a/frontend/rust-lib/flowy-grid/src/services/block_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs index 275ddef9fa..f576742c90 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_editor.rs @@ -1,7 +1,7 @@ use crate::entities::RowPB; use bytes::Bytes; use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision}; +use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowChangeset, RowRevision}; use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad}; use flowy_sync::entities::revision::Revision; @@ -88,7 +88,7 @@ impl GridBlockRevisionEditor { Ok(row_count) } - pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { + pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> { let _ = self.modify(|block_pad| Ok(block_pad.update_row(changeset)?)).await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index 91348c76b5..3d31f99676 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs @@ -7,7 +7,7 @@ use crate::services::row::{block_from_row_orders, make_row_from_row_rev, GridBlo use dashmap::DashMap; use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{ - GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, + GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision, }; use flowy_revision::disk::SQLiteGridBlockRevisionPersistence; use flowy_revision::{RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence}; @@ -103,17 +103,15 @@ impl GridBlockManager { Ok(changesets) } - pub async fn update_row(&self, changeset: RowMetaChangeset, row_builder: F) -> FlowyResult<()> - where - F: FnOnce(Arc) -> RowPB, - { + pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> { let editor = self.get_editor_from_row_id(&changeset.row_id).await?; let _ = editor.update_row(changeset.clone()).await?; match editor.get_row_rev(&changeset.row_id).await? { None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id), Some(row_rev) => { + let row_pb = make_row_from_row_rev(row_rev.clone()); let block_order_changeset = - GridBlockChangesetPB::update(&editor.block_id, vec![row_builder(row_rev.clone())]); + GridBlockChangesetPB::update(&editor.block_id, vec![row_pb]); let _ = self .notify_did_update_block(&editor.block_id, block_order_changeset) .await?; @@ -191,12 +189,10 @@ impl GridBlockManager { } } - pub async fn update_cell(&self, changeset: CellChangesetPB, row_builder: F) -> FlowyResult<()> - where - F: FnOnce(Arc) -> RowPB, + pub async fn update_cell(&self, changeset: CellChangesetPB ) -> FlowyResult<()> { - let row_changeset: RowMetaChangeset = changeset.clone().into(); - let _ = self.update_row(row_changeset, row_builder).await?; + let row_changeset: RowChangeset = changeset.clone().into(); + let _ = self.update_row(row_changeset, ).await?; self.notify_did_update_cell(changeset).await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index d099b5b269..e796929ce6 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -314,9 +314,9 @@ impl GridRevisionEditor { Ok(row_orders) } - pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { + pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> { let row_id = changeset.row_id.clone(); - let _ = self.block_manager.update_row(changeset, make_row_from_row_rev).await?; + let _ = self.block_manager.update_row(changeset).await?; self.view_manager.did_update_row(&row_id).await; Ok(()) } @@ -395,12 +395,11 @@ impl GridRevisionEditor { match self.grid_pad.read().await.get_field_rev(&field_id) { None => { - let msg = format!("Field not found with id: {}", &field_id); + let msg = format!("Field:{} not found", &field_id); Err(FlowyError::internal().context(msg)) } Some((_, field_rev)) => { tracing::trace!("field changeset: id:{} / value:{:?}", &field_id, content); - let cell_rev = self.get_cell_rev(&row_id, &field_id).await?; // Update the changeset.data property with the return value. content = Some(apply_cell_data_changeset(content.unwrap(), cell_rev, field_rev)?); @@ -410,11 +409,7 @@ impl GridRevisionEditor { field_id, content, }; - let _ = self - .block_manager - .update_cell(cell_changeset, make_row_from_row_rev) - .await?; - + let _ = self.block_manager.update_cell(cell_changeset).await?; self.view_manager.did_update_row(&row_id).await; Ok(()) } @@ -512,10 +507,19 @@ impl GridRevisionEditor { .block_manager .move_row(row_rev.clone(), from_index, to_index) .await?; - self.view_manager.move_row(row_rev, to_row_id.clone()).await; + + if let Some(row_changeset) = self.view_manager.move_row(row_rev, to_row_id.clone()).await { + tracing::trace!("Receive row changeset after moving the row"); + match self.block_manager.update_row(row_changeset).await { + Ok(_) => {} + Err(e) => { + tracing::error!("Apply row changeset error:{:?}", e); + } + } + } } - (_, None) => tracing::error!("Can not find the from row id: {}", from_row_id), - (None, _) => tracing::error!("Can not find the to row id: {}", to_row_id), + (_, None) => tracing::warn!("Can not find the from row id: {}", from_row_id), + (None, _) => tracing::warn!("Can not find the to row id: {}", to_row_id), } } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index 5b4af736a3..e972745dfe 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -7,7 +7,7 @@ use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDeleg use crate::services::group::{default_group_configuration, GroupConfigurationDelegate, GroupService}; use crate::services::setting::make_grid_setting; use flowy_error::{FlowyError, FlowyResult}; -use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowRevision}; +use flowy_grid_data_model::revision::{FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision}; use flowy_revision::{RevisionCloudService, RevisionManager, RevisionObjectBuilder}; use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad}; use flowy_sync::entities::grid::GridSettingChangesetParams; @@ -117,12 +117,12 @@ impl GridViewRevisionEditor { } } - pub(crate) async fn did_move_row(&self, row_rev: &RowRevision, upper_row_id: &str) { + pub(crate) async fn did_move_row(&self, row_rev: &RowRevision, row_changeset: &mut RowChangeset, upper_row_id: &str) { if let Some(changesets) = self .group_service .write() .await - .did_move_row(row_rev, upper_row_id, |field_id| { + .did_move_row(row_rev, row_changeset, upper_row_id, |field_id| { self.field_delegate.get_field_rev(&field_id) }) .await diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs index a71ac5722c..17c5e93569 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs @@ -1,13 +1,11 @@ -use crate::entities::{ - CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB, -}; +use crate::entities::{CreateRowParams, GridFilterConfiguration, GridSettingPB, RepeatedGridGroupPB, RowPB}; use crate::manager::GridUser; use crate::services::grid_editor_task::GridServiceTaskScheduler; use crate::services::grid_view_editor::GridViewRevisionEditor; use bytes::Bytes; use dashmap::DashMap; use flowy_error::FlowyResult; -use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; use flowy_revision::disk::SQLiteGridViewRevisionPersistence; use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence}; use flowy_sync::entities::grid::GridSettingChangesetParams; @@ -56,18 +54,21 @@ impl GridViewManager { }) } + /// When the row was created, we may need to modify the [RowRevision] according to the [CreateRowParams]. pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { for view_editor in self.view_editors.iter() { view_editor.will_create_row(row_rev, params).await; } } + /// Notify the view that the row was created. For the moment, the view is just sending notifications. pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { for view_editor in self.view_editors.iter() { view_editor.did_create_row(row_pb, params).await; } } + /// Insert/Delete the group's row if the corresponding data was changed. pub(crate) async fn did_update_row(&self, row_id: &str) { match self.row_delegate.gv_get_row_rev(row_id).await { None => { @@ -109,9 +110,19 @@ impl GridViewManager { Ok(RepeatedGridGroupPB { items: groups }) } - pub(crate) async fn move_row(&self, row_rev: Arc, to_row_id: String) { + /// It may generate a RowChangeset when the Row was moved from one group to another. + /// The return value, [RowChangeset], contains the changes made by the groups. + /// + pub(crate) async fn move_row(&self, row_rev: Arc, to_row_id: String) -> Option { + let mut row_changeset = RowChangeset::new(row_rev.id.clone()); for view_editor in self.view_editors.iter() { - view_editor.did_move_row(&row_rev, &to_row_id).await; + view_editor.did_move_row(&row_rev, &mut row_changeset, &to_row_id).await; + } + + if row_changeset.has_changed() { + Some(row_changeset) + } else { + None } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs index bfe5552867..c1f4261bb5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/checkbox_group.rs @@ -1,6 +1,6 @@ use crate::entities::{CheckboxGroupConfigurationPB, GroupRowsChangesetPB}; -use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK}; use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable}; @@ -37,7 +37,9 @@ impl Groupable for CheckboxGroupController { fn move_row_if_match( &mut self, + field_rev: &FieldRevision, _row_rev: &RowRevision, + row_changeset: &mut RowChangeset, _cell_data: &Self::CellDataType, _to_row_id: &str, ) -> Vec { @@ -57,11 +59,22 @@ impl GroupGenerator for CheckboxGroupGenerator { type TypeOptionType = CheckboxTypeOptionPB; fn generate_groups( + field_id: &str, _configuration: &Option, _type_option: &Option, ) -> Vec { - let check_group = Group::new("true".to_string(), "".to_string(), CHECK.to_string()); - let uncheck_group = Group::new("false".to_string(), "".to_string(), UNCHECK.to_string()); + let check_group = Group::new( + "true".to_string(), + field_id.to_owned(), + "".to_string(), + CHECK.to_string(), + ); + let uncheck_group = Group::new( + "false".to_string(), + field_id.to_owned(), + "".to_string(), + UNCHECK.to_string(), + ); vec![check_group, uncheck_group] } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs index 6135aabb7e..f088bb2176 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/group_controller.rs @@ -3,7 +3,7 @@ use crate::services::cell::{decode_any_cell_data, CellBytesParser}; use bytes::Bytes; use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{ - FieldRevision, GroupConfigurationRevision, RowRevision, TypeOptionDataDeserializer, + FieldRevision, GroupConfigurationRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer, }; use indexmap::IndexMap; use std::marker::PhantomData; @@ -14,6 +14,7 @@ pub trait GroupGenerator { type TypeOptionType; fn generate_groups( + field_id: &str, configuration: &Option, type_option: &Option, ) -> Vec; @@ -31,7 +32,9 @@ pub trait Groupable: Send + Sync { fn move_row_if_match( &mut self, + field_rev: &FieldRevision, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, cell_data: &Self::CellDataType, to_row_id: &str, ) -> Vec; @@ -61,6 +64,7 @@ pub trait GroupControllerSharedAction: Send + Sync { fn did_move_row( &mut self, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, field_rev: &FieldRevision, to_row_id: &str, ) -> FlowyResult>; @@ -85,6 +89,7 @@ pub struct GenericGroupController { #[derive(Clone)] pub struct Group { pub id: String, + pub field_id: String, pub desc: String, rows: Vec, pub content: String, @@ -101,9 +106,10 @@ impl std::convert::From for GroupPB { } impl Group { - pub fn new(id: String, desc: String, content: String) -> Self { + pub fn new(id: String, field_id: String, desc: String, content: String) -> Self { Self { id, + field_id, desc, rows: vec![], content, @@ -158,10 +164,11 @@ where }; let field_type_rev = field_rev.field_type_rev; let type_option = field_rev.get_type_option_entry::(field_type_rev); - let groups = G::generate_groups(&configuration, &type_option); + let groups = G::generate_groups(&field_rev.id, &configuration, &type_option); let default_group = Group::new( DEFAULT_GROUP_ID.to_owned(), + field_rev.id.clone(), format!("No {}", field_rev.name), "".to_string(), ); @@ -263,62 +270,20 @@ where fn did_move_row( &mut self, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, field_rev: &FieldRevision, to_row_id: &str, ) -> FlowyResult> { if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); let cell_data = cell_bytes.parser::

()?; - Ok(self.move_row_if_match(row_rev, &cell_data, to_row_id)) + Ok(self.move_row_if_match(field_rev, row_rev, row_changeset, &cell_data, to_row_id)) } else { Ok(vec![]) } } } -// impl GroupController -// where -// P: CellBytesParser, -// Self: Groupable, -// { -// pub fn handle_rows(&mut self, rows: &[Arc], field_rev: &FieldRevision) -> FlowyResult<()> { -// // The field_rev might be None if corresponding field_rev is deleted. -// if self.configuration.is_none() { -// return Ok(()); -// } -// -// for row in rows { -// if let Some(cell_rev) = row.cells.get(&self.field_id) { -// let mut records: Vec = vec![]; -// let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); -// let cell_data = cell_bytes.parser::

()?; -// for group in self.groups_map.values() { -// if self.can_group(&group.content, &cell_data) { -// records.push(GroupRecord { -// row: row.into(), -// group_id: group.id.clone(), -// }); -// } -// } -// -// if records.is_empty() { -// self.default_group.rows.push(row.into()); -// } else { -// for record in records { -// if let Some(group) = self.groups_map.get_mut(&record.group_id) { -// group.rows.push(record.row); -// } -// } -// } -// } else { -// self.default_group.rows.push(row.into()); -// } -// } -// -// Ok(()) -// } -// } - struct GroupRecord { row: RowPB, group_id: String, diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs index 086d87a0d2..71c1c9ef97 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_generator/select_option_group.rs @@ -5,7 +5,7 @@ use crate::services::field::{ }; use crate::services::group::{GenericGroupController, Group, GroupController, GroupGenerator, Groupable}; -use flowy_grid_data_model::revision::{FieldRevision, RowRevision}; +use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision}; // SingleSelect pub type SingleSelectGroupController = GenericGroupController< @@ -43,15 +43,25 @@ impl Groupable for SingleSelectGroupController { fn move_row_if_match( &mut self, + field_rev: &FieldRevision, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, cell_data: &Self::CellDataType, to_row_id: &str, ) -> Vec { - let mut changesets = vec![]; + let mut group_changeset = vec![]; self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - move_row(group, &mut changesets, cell_data, row_rev, to_row_id); + move_row( + group, + &mut group_changeset, + field_rev, + row_rev, + row_changeset, + cell_data, + to_row_id, + ); }); - changesets + group_changeset } } @@ -74,6 +84,7 @@ impl GroupGenerator for SingleSelectGroupGenerator { type ConfigurationType = SelectOptionGroupConfigurationPB; type TypeOptionType = SingleSelectTypeOptionPB; fn generate_groups( + field_id: &str, _configuration: &Option, type_option: &Option, ) -> Vec { @@ -82,7 +93,14 @@ impl GroupGenerator for SingleSelectGroupGenerator { Some(type_option) => type_option .options .iter() - .map(|option| Group::new(option.id.clone(), option.name.clone(), option.id.clone())) + .map(|option| { + Group::new( + option.id.clone(), + field_id.to_owned(), + option.name.clone(), + option.id.clone(), + ) + }) .collect(), } } @@ -125,15 +143,25 @@ impl Groupable for MultiSelectGroupController { fn move_row_if_match( &mut self, + field_rev: &FieldRevision, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, cell_data: &Self::CellDataType, to_row_id: &str, ) -> Vec { - let mut changesets = vec![]; + let mut group_changeset = vec![]; self.groups_map.iter_mut().for_each(|(_, group): (_, &mut Group)| { - move_row(group, &mut changesets, cell_data, row_rev, to_row_id); + move_row( + group, + &mut group_changeset, + field_rev, + row_rev, + row_changeset, + cell_data, + to_row_id, + ); }); - changesets + group_changeset } } @@ -156,6 +184,7 @@ impl GroupGenerator for MultiSelectGroupGenerator { type TypeOptionType = MultiSelectTypeOptionPB; fn generate_groups( + field_id: &str, _configuration: &Option, type_option: &Option, ) -> Vec { @@ -164,7 +193,14 @@ impl GroupGenerator for MultiSelectGroupGenerator { Some(type_option) => type_option .options .iter() - .map(|option| Group::new(option.id.clone(), option.name.clone(), option.id.clone())) + .map(|option| { + Group::new( + option.id.clone(), + field_id.to_owned(), + option.name.clone(), + option.id.clone(), + ) + }) .collect(), } } @@ -204,25 +240,30 @@ fn remove_row( fn move_row( group: &mut Group, - changesets: &mut Vec, - cell_data: &SelectOptionCellDataPB, + group_changeset: &mut Vec, + field_rev: &FieldRevision, row_rev: &RowRevision, - upper_row_id: &str, + row_changeset: &mut RowChangeset, + cell_data: &SelectOptionCellDataPB, + to_row_id: &str, ) { cell_data.select_options.iter().for_each(|option| { if option.id == group.id && group.contains_row(&row_rev.id) { - changesets.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); + group_changeset.push(GroupRowsChangesetPB::delete(group.id.clone(), vec![row_rev.id.clone()])); group.remove_row(&row_rev.id); } - if let Some(index) = group.index_of_row(upper_row_id) { + if let Some(index) = group.index_of_row(to_row_id) { let row_pb = RowPB::from(row_rev); let inserted_row = InsertedRowPB { row: row_pb.clone(), index: Some(index as i32), }; - changesets.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row])); + group_changeset.push(GroupRowsChangesetPB::insert(group.id.clone(), vec![inserted_row])); group.insert_row(index, row_pb); + + let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); + row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev); } }); } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs index e4bf92e22a..af0ec90ba4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_service.rs @@ -7,7 +7,7 @@ use crate::services::group::{ }; use bytes::Bytes; use flowy_error::FlowyResult; -use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision}; +use flowy_grid_data_model::revision::{gen_grid_group_id, FieldRevision, GroupConfigurationRevision, RowRevision, RowChangeset}; use lib_infra::future::AFFuture; use std::future::Future; use std::sync::Arc; @@ -96,6 +96,7 @@ impl GroupService { pub(crate) async fn did_move_row( &self, row_rev: &RowRevision, + row_changeset: &mut RowChangeset, upper_row_id: &str, get_field_fn: F, ) -> Option> @@ -110,7 +111,7 @@ impl GroupService { match group_controller .write() .await - .did_move_row(row_rev, &field_rev, upper_row_id) + .did_move_row(row_rev, row_changeset, &field_rev, upper_row_id) { Ok(changesets) => Some(changesets), Err(e) => { diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs index 85bfed576d..72b3a288a1 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs @@ -3,7 +3,7 @@ use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest}; use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER}; use flowy_grid::entities::FieldType; use flowy_grid::services::field::{SELECTION_IDS_SEPARATOR, UNCHECK}; -use flowy_grid_data_model::revision::RowMetaChangeset; +use flowy_grid_data_model::revision::RowChangeset; #[tokio::test] async fn grid_create_row_count_test() { @@ -24,7 +24,7 @@ async fn grid_create_row_count_test() { async fn grid_update_row() { let mut test = GridRowTest::new().await; let row_rev = test.row_builder().build(); - let changeset = RowMetaChangeset { + let changeset = RowChangeset { row_id: row_rev.id.clone(), height: None, visibility: None, diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 54a022e0d6..3830d7f1fe 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -5,7 +5,7 @@ use crate::grid::grid_editor::GridEditorTest; use flowy_grid::entities::{CreateRowParams, FieldType, GridCellIdParams, GridLayout, RowPB}; use flowy_grid::services::field::*; use flowy_grid_data_model::revision::{ - GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision, + GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowChangeset, RowRevision, }; use std::collections::HashMap; use std::sync::Arc; @@ -17,7 +17,7 @@ pub enum RowScript { row_rev: RowRevision, }, UpdateRow { - changeset: RowMetaChangeset, + changeset: RowChangeset, }, AssertRow { expected_row: RowRevision, diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_block.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_block.rs index def044f439..99737dc4d6 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_block.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_block.rs @@ -42,13 +42,28 @@ impl RowRevision { } } #[derive(Debug, Clone, Default)] -pub struct RowMetaChangeset { +pub struct RowChangeset { pub row_id: String, pub height: Option, pub visibility: Option, pub cell_by_field_id: HashMap, } +impl RowChangeset { + pub fn new(row_id: String) -> Self { + Self { + row_id, + height: None, + visibility: None, + cell_by_field_id: Default::default() + } + } + + pub fn has_changed(&self) -> bool { + self.height.is_some() || self.visibility.is_some() || !self.cell_by_field_id.is_empty() + } +} + #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct CellRevision { pub data: String, diff --git a/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs index 8abc1eaace..fa587d6a8c 100644 --- a/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/block_revision_pad.rs @@ -2,7 +2,7 @@ use crate::entities::revision::{md5, RepeatedRevision, Revision}; use crate::errors::{CollaborateError, CollaborateResult}; use crate::util::{cal_diff, make_text_delta_from_revisions}; use flowy_grid_data_model::revision::{ - gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision, + gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowChangeset, RowRevision, }; use lib_ot::core::{OperationTransform, PhantomAttributes, TextDelta, TextDeltaBuilder}; use std::borrow::Cow; @@ -143,7 +143,7 @@ impl GridBlockRevisionPad { self.block.rows.iter().position(|row| row.id == row_id) } - pub fn update_row(&mut self, changeset: RowMetaChangeset) -> CollaborateResult> { + pub fn update_row(&mut self, changeset: RowChangeset) -> CollaborateResult> { let row_id = changeset.row_id.clone(); self.modify_row(&row_id, |row| { let mut is_changed = None;