diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index a8ed3d56c1..3a62924e83 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -260,7 +260,7 @@ pub trait ViewDataProcessor { fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>; - fn get_delta_data(&self, view_id: &str) -> FutureResult; + fn get_view_data(&self, view_id: &str) -> FutureResult; fn create_default_view( &self, diff --git a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs index 1f0b61ebb1..402a68813b 100644 --- a/frontend/rust-lib/flowy-folder/src/services/view/controller.rs +++ b/frontend/rust-lib/flowy-folder/src/services/view/controller.rs @@ -61,11 +61,13 @@ impl ViewController { let processor = self.get_data_processor(params.data_type.clone())?; let user_id = self.user.user_id()?; if params.view_content_data.is_empty() { + tracing::trace!("Create view with build-in data"); let view_data = processor .create_default_view(&user_id, ¶ms.view_id, params.layout.clone()) .await?; params.view_content_data = view_data.to_vec(); } else { + tracing::trace!("Create view with view data"); let delta_data = processor .create_view_from_delta_data( &user_id, @@ -231,7 +233,7 @@ impl ViewController { .await?; let processor = self.get_data_processor(view_rev.data_type.clone())?; - let delta_bytes = processor.get_delta_data(view_id).await?; + let view_data = processor.get_view_data(view_id).await?; let duplicate_params = CreateViewParams { belong_to_id: view_rev.app_id.clone(), name: format!("{} (copy)", &view_rev.name), @@ -239,7 +241,7 @@ impl ViewController { thumbnail: view_rev.thumbnail, data_type: view_rev.data_type.into(), layout: view_rev.layout.into(), - view_content_data: delta_bytes.to_vec(), + view_content_data: view_data.to_vec(), view_id: gen_view_id(), }; diff --git a/frontend/rust-lib/flowy-grid/src/dart_notification.rs b/frontend/rust-lib/flowy-grid/src/dart_notification.rs index 8d7ec2ba36..6e47dfceb9 100644 --- a/frontend/rust-lib/flowy-grid/src/dart_notification.rs +++ b/frontend/rust-lib/flowy-grid/src/dart_notification.rs @@ -33,8 +33,3 @@ impl std::convert::From for i32 { pub fn send_dart_notification(id: &str, ty: GridNotification) -> DartNotifyBuilder { DartNotifyBuilder::new(id, ty, OBSERVABLE_CATEGORY) } - -#[tracing::instrument(level = "trace")] -pub fn send_anonymous_dart_notification(ty: GridNotification) -> DartNotifyBuilder { - DartNotifyBuilder::new("", ty, OBSERVABLE_CATEGORY) -} diff --git a/frontend/rust-lib/flowy-grid/src/manager.rs b/frontend/rust-lib/flowy-grid/src/manager.rs index 39f8641c7e..3e7b97f50b 100644 --- a/frontend/rust-lib/flowy-grid/src/manager.rs +++ b/frontend/rust-lib/flowy-grid/src/manager.rs @@ -183,7 +183,14 @@ pub async fn make_grid_view_data( grid_manager: Arc, build_context: BuildGridContext, ) -> FlowyResult { - for block_meta_data in &build_context.blocks { + let BuildGridContext { + field_revs, + block_metas, + blocks, + grid_view_revision_data, + } = build_context; + + for block_meta_data in &blocks { let block_id = &block_meta_data.block_id; // Indexing the block's rows block_meta_data.rows.iter().for_each(|row| { @@ -200,7 +207,7 @@ pub async fn make_grid_view_data( // Will replace the grid_id with the value returned by the gen_grid_id() let grid_id = view_id.to_owned(); - let grid_rev = GridRevision::from_build_context(&grid_id, build_context); + let grid_rev = GridRevision::from_build_context(&grid_id, field_revs, block_metas); // Create grid let grid_rev_delta = make_grid_delta(&grid_rev); @@ -210,7 +217,11 @@ pub async fn make_grid_view_data( let _ = grid_manager.create_grid(&grid_id, repeated_revision).await?; // Create grid view - let grid_view = GridViewRevision::new(grid_id, view_id.to_owned(), layout.into()); + let grid_view = if grid_view_revision_data.is_empty() { + GridViewRevision::new(grid_id, view_id.to_owned(), layout.into()) + } else { + GridViewRevision::from_json(grid_view_revision_data)? + }; let grid_view_delta = make_grid_view_delta(&grid_view); let grid_view_delta_bytes = grid_view_delta.json_bytes(); let repeated_revision: RepeatedRevision = diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs index 8f76ea62c5..f7ef205435 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs @@ -19,7 +19,10 @@ impl std::str::FromStr for AnyCellData { type Err = FlowyError; fn from_str(s: &str) -> Result { - let type_option_cell_data: AnyCellData = serde_json::from_str(s)?; + let type_option_cell_data: AnyCellData = serde_json::from_str(s).map_err(|err| { + let msg = format!("Deserialize {} to any cell data failed. Serde error: {}", s, err); + FlowyError::internal().context(msg) + })?; Ok(type_option_cell_data) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 20dd09a5ef..569a5f2f7d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -1,6 +1,7 @@ use crate::entities::FieldType; use crate::services::cell::{AnyCellData, CellBytes}; use crate::services::field::*; +use std::fmt::Debug; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, FieldTypeRevision}; @@ -73,20 +74,30 @@ pub fn apply_cell_data_changeset>( Ok(AnyCellData::new(s, field_type).json()) } -pub fn decode_any_cell_data>(data: T, field_rev: &FieldRevision) -> CellBytes { - if let Ok(any_cell_data) = data.try_into() { - let AnyCellData { data, field_type } = any_cell_data; - let to_field_type = field_rev.ty.into(); - match try_decode_cell_data(data.into(), field_rev, &field_type, &to_field_type) { - Ok(cell_bytes) => cell_bytes, - Err(e) => { - tracing::error!("Decode cell data failed, {:?}", e); - CellBytes::default() +pub fn decode_any_cell_data + Debug>( + data: T, + field_rev: &FieldRevision, +) -> CellBytes { + match data.try_into() { + Ok(any_cell_data) => { + let AnyCellData { data, field_type } = any_cell_data; + let to_field_type = field_rev.ty.into(); + match try_decode_cell_data(data.into(), field_rev, &field_type, &to_field_type) { + Ok(cell_bytes) => cell_bytes, + Err(e) => { + tracing::error!("Decode cell data failed, {:?}", e); + CellBytes::default() + } } } - } else { - tracing::error!("Decode type option data failed"); - CellBytes::default() + Err(err) => { + tracing::error!( + "Decode type option data to type: {} failed: {:?}", + std::any::type_name::(), + err, + ); + CellBytes::default() + } } } 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 aa913c2c4e..b2fe28665e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -673,6 +673,7 @@ impl GridRevisionEditor { pub async fn duplicate_grid(&self) -> FlowyResult { let grid_pad = self.grid_pad.read().await; + let grid_view_revision_data = self.view_manager.duplicate_grid_view().await?; let original_blocks = grid_pad.get_block_meta_revs(); let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_block_meta().await; @@ -698,6 +699,7 @@ impl GridRevisionEditor { field_revs: duplicated_fields.into_iter().map(Arc::new).collect(), block_metas: duplicated_blocks, blocks: blocks_meta_data, + grid_view_revision_data, }) } 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 c926743f9f..0889aec43e 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 @@ -74,6 +74,11 @@ impl GridViewRevisionEditor { }) } + pub(crate) async fn duplicate_view_data(&self) -> FlowyResult { + let json_str = self.pad.read().await.json_str()?; + Ok(json_str) + } + pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { if params.group_id.is_none() { return; 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 b1041a818c..db7aa53ee2 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 @@ -56,6 +56,12 @@ impl GridViewManager { }) } + pub(crate) async fn duplicate_grid_view(&self) -> FlowyResult { + let editor = self.get_default_view_editor().await?; + let view_data = editor.duplicate_view_data().await?; + Ok(view_data) + } + /// 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() { diff --git a/frontend/rust-lib/flowy-grid/src/services/group/action.rs b/frontend/rust-lib/flowy-grid/src/services/group/action.rs index fc92b68c46..fd9adcb8b5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/action.rs @@ -14,5 +14,6 @@ pub trait GroupAction: Send + Sync { fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; + // Move row from one group to another fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec; } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs index 5ea8639c92..f65b87e829 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -86,8 +86,7 @@ where G: GroupGenerator, TypeOptionType = T>, { pub async fn new(field_rev: &Arc, mut configuration: GroupContext) -> FlowyResult { - let field_type_rev = field_rev.ty; - let type_option = field_rev.get_type_option::(field_type_rev); + let type_option = field_rev.get_type_option::(field_rev.ty); let groups = G::generate_groups(&field_rev.id, &configuration, &type_option); let _ = configuration.init_groups(groups, true)?; @@ -278,12 +277,19 @@ where } } + #[tracing::instrument(level = "trace", skip_all, err)] fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult> { - if let Some(cell_rev) = context.row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), context.field_rev); + let cell_rev = match context.row_rev.cells.get(&self.field_id) { + Some(cell_rev) => Some(cell_rev.clone()), + None => self.default_cell_rev(), + }; + + if let Some(cell_rev) = cell_rev { + let cell_bytes = decode_any_cell_data(cell_rev.data, context.field_rev); let cell_data = cell_bytes.parser::

()?; Ok(self.move_row(&cell_data, context)) } else { + tracing::warn!("Unexpected moving group row, changes should not be empty"); Ok(vec![]) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs index a1d81b0d5c..9c3e71bc3e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs @@ -1,11 +1,12 @@ -use crate::entities::{GroupChangesetPB, InsertedRowPB, RowPB}; -use crate::services::cell::insert_select_option_cell; -use crate::services::field::{SelectOptionCellDataPB, SelectOptionPB}; +use crate::entities::{FieldType, GroupChangesetPB, InsertedRowPB, RowPB}; +use crate::services::cell::{insert_checkbox_cell, insert_select_option_cell}; +use crate::services::field::{SelectOptionCellDataPB, SelectOptionPB, CHECK}; use crate::services::group::configuration::GroupContext; -use crate::services::group::{GeneratedGroup, Group}; - use crate::services::group::controller::MoveGroupRowContext; -use flowy_grid_data_model::revision::{GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision}; +use crate::services::group::{GeneratedGroup, Group}; +use flowy_grid_data_model::revision::{ + CellRevision, FieldRevision, GroupRevision, RowRevision, SelectOptionGroupConfigurationRevision, +}; pub type SelectOptionGroupContext = GroupContext; @@ -109,10 +110,16 @@ pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> O // Update the corresponding row's cell content. if from_index.is_none() { - tracing::debug!("Mark row:{} belong to group:{}", row_rev.id, group.id); - 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); - changeset.updated_rows.push(RowPB::from(*row_rev)); + let cell_rev = make_inserted_cell_rev(&group.id, field_rev); + if let Some(cell_rev) = cell_rev { + tracing::debug!( + "Update content of the cell in the row:{} to group:{}", + row_rev.id, + group.id + ); + row_changeset.cell_by_field_id.insert(field_rev.id.clone(), cell_rev); + changeset.updated_rows.push(RowPB::from(*row_rev)); + } } } if changeset.is_empty() { @@ -122,6 +129,27 @@ pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> O } } +pub fn make_inserted_cell_rev(group_id: &str, field_rev: &FieldRevision) -> Option { + let field_type: FieldType = field_rev.ty.into(); + match field_type { + FieldType::SingleSelect => { + let cell_rev = insert_select_option_cell(group_id.to_owned(), field_rev); + Some(cell_rev) + } + FieldType::MultiSelect => { + let cell_rev = insert_select_option_cell(group_id.to_owned(), field_rev); + Some(cell_rev) + } + FieldType::Checkbox => { + let cell_rev = insert_checkbox_cell(group_id == CHECK, field_rev); + Some(cell_rev) + } + _ => { + tracing::warn!("Unknown field type: {:?}", field_type); + None + } + } +} pub fn generate_select_option_groups( _field_id: &str, _group_ctx: &SelectOptionGroupContext, diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs index 91cbfc2355..f9eac21ba0 100644 --- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs @@ -170,7 +170,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor { }) } - fn get_delta_data(&self, view_id: &str) -> FutureResult { + fn get_view_data(&self, view_id: &str) -> FutureResult { let view_id = view_id.to_string(); let manager = self.0.clone(); FutureResult::new(async move { @@ -247,7 +247,7 @@ impl ViewDataProcessor for GridViewDataProcessor { }) } - fn get_delta_data(&self, view_id: &str) -> FutureResult { + fn get_view_data(&self, view_id: &str) -> FutureResult { let view_id = view_id.to_string(); let grid_manager = self.0.clone(); FutureResult::new(async move { diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs index 90187d9b7b..7f1b41782b 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs @@ -34,11 +34,15 @@ impl GridRevision { } } - pub fn from_build_context(grid_id: &str, context: BuildGridContext) -> Self { + pub fn from_build_context( + grid_id: &str, + field_revs: Vec>, + block_metas: Vec, + ) -> Self { Self { grid_id: grid_id.to_owned(), - fields: context.field_revs, - blocks: context.block_metas.into_iter().map(Arc::new).collect(), + fields: field_revs, + blocks: block_metas.into_iter().map(Arc::new).collect(), } } } @@ -188,6 +192,9 @@ pub struct BuildGridContext { pub field_revs: Vec>, pub block_metas: Vec, pub blocks: Vec, + + // String in JSON format. It can be deserialized into [GridViewRevision] + pub grid_view_revision_data: String, } impl BuildGridContext { diff --git a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs index 24b991be6a..06178d23c1 100644 --- a/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs +++ b/shared-lib/flowy-grid-data-model/src/revision/grid_view.rs @@ -58,6 +58,10 @@ impl GridViewRevision { // row_orders: vec![], } } + + pub fn from_json(json: String) -> Result { + serde_json::from_str(&json) + } } #[derive(Debug, Clone, Default, Serialize, Deserialize)]