Quellcode durchsuchen

Merge pull request #541 from AppFlowy-IO/fix/grid_duplicate

fix: duplicate grid
Nathan.fooo vor 2 Jahren
Ursprung
Commit
4850a97315

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/add_button.dart

@@ -46,8 +46,8 @@ class ActionList {
         return CreateItem(
           pluginBuilder: pluginBuilder,
           onSelected: (builder) {
-            FlowyOverlay.of(buildContext).remove(_identifier);
             onSelected(builder);
+            FlowyOverlay.of(buildContext).remove(_identifier);
           },
         );
       },

+ 1 - 0
frontend/rust-lib/Cargo.lock

@@ -937,6 +937,7 @@ dependencies = [
  "flowy-revision",
  "flowy-sync",
  "flowy-test",
+ "futures",
  "indexmap",
  "lazy_static",
  "lib-dispatch",

+ 2 - 2
frontend/rust-lib/flowy-folder/src/manager.rs

@@ -241,11 +241,11 @@ pub trait ViewDataProcessor {
 
     fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
 
-    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
+    fn view_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
 
     fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError>;
 
-    fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError>;
+    fn process_view_delta_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError>;
 
     fn data_type(&self) -> ViewDataType;
 }

+ 3 - 3
frontend/rust-lib/flowy-folder/src/services/view/controller.rs

@@ -60,7 +60,7 @@ impl ViewController {
             params.data = view_data.to_vec();
         } else {
             let delta_data = processor
-                .process_create_view_data(&user_id, &params.view_id, params.data.clone())
+                .process_view_delta_data(&user_id, &params.view_id, params.data.clone())
                 .await?;
             let _ = self
                 .create_view(&params.view_id, params.data_type.clone(), delta_data)
@@ -176,7 +176,7 @@ impl ViewController {
             .await?;
 
         let processor = self.get_data_processor(&view.data_type)?;
-        let delta_bytes = processor.delta_bytes(view_id).await?;
+        let delta_bytes = processor.view_delta_data(view_id).await?;
         let duplicate_params = CreateViewParams {
             belong_to_id: view.belong_to_id.clone(),
             name: format!("{} (copy)", &view.name),
@@ -238,7 +238,7 @@ impl ViewController {
 }
 
 impl ViewController {
-    #[tracing::instrument(level = "debug", skip(self), err)]
+    #[tracing::instrument(level = "debug", skip(self, params), err)]
     async fn create_view_on_server(&self, params: CreateViewParams) -> Result<View, FlowyError> {
         let token = self.user.token()?;
         let view = self.cloud_service.create_view(&token, params).await?;

+ 1 - 0
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -37,6 +37,7 @@ serde_repr = "0.1"
 indexmap = {version = "1.8.1", features = ["serde"]}
 fancy-regex = "0.10.0"
 url = { version = "2"}
+futures = "0.3.15"
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }

+ 18 - 15
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -154,11 +154,10 @@ pub async fn make_grid_view_data(
     grid_manager: Arc<GridManager>,
     build_context: BuildGridContext,
 ) -> FlowyResult<Bytes> {
-    let block_id = build_context.block_meta.block_id.clone();
     let grid_meta = GridMeta {
         grid_id: view_id.to_string(),
         fields: build_context.field_metas,
-        blocks: vec![build_context.block_meta],
+        blocks: build_context.blocks,
     };
 
     // Create grid
@@ -168,19 +167,23 @@ pub async fn make_grid_view_data(
         Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
     let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
 
-    // Indexing the block's rows
-    build_context.block_meta_data.rows.iter().for_each(|row| {
-        let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
-    });
-
-    // Create grid's block
-    let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
-    let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
-    let repeated_revision: RepeatedRevision =
-        Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into();
-    let _ = grid_manager
-        .create_grid_block_meta(&block_id, repeated_revision)
-        .await?;
+    for block_meta_data in build_context.blocks_meta_data {
+        let block_id = block_meta_data.block_id.clone();
+
+        // Indexing the block's rows
+        block_meta_data.rows.iter().for_each(|row| {
+            let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
+        });
+
+        // Create grid's block
+        let grid_block_meta_delta = make_block_meta_delta(&block_meta_data);
+        let block_meta_delta_data = grid_block_meta_delta.to_delta_bytes();
+        let repeated_revision: RepeatedRevision =
+            Revision::initial_revision(user_id, &block_id, block_meta_delta_data).into();
+        let _ = grid_manager
+            .create_grid_block_meta(&block_id, repeated_revision)
+            .await?;
+    }
 
     Ok(grid_delta_data)
 }

+ 5 - 1
frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs

@@ -1,6 +1,6 @@
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_grid_data_model::entities::{CellMeta, RowMeta, RowMetaChangeset, RowOrder};
+use flowy_grid_data_model::entities::{CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset, RowOrder};
 use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
 use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockMetaPad};
 use flowy_sync::entities::revision::Revision;
@@ -41,6 +41,10 @@ impl GridBlockMetaEditor {
         })
     }
 
+    pub async fn duplicate_block_meta_data(&self, duplicated_block_id: &str) -> GridBlockMetaData {
+        self.pad.read().await.duplicate_data(duplicated_block_id).await
+    }
+
     /// return current number of rows and the inserted index. The inserted index will be None if the start_row_id is None
     pub(crate) async fn create_row(
         &self,

+ 2 - 1
frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs

@@ -47,7 +47,7 @@ impl GridBlockManager {
         debug_assert!(!block_id.is_empty());
         match self.block_editor_map.get(block_id) {
             None => {
-                tracing::error!("The is a fatal error, block is not exist");
+                tracing::error!("This is a fatal error, block with id:{} is not exist", block_id);
                 let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
                 self.block_editor_map.insert(block_id.to_owned(), editor.clone());
                 Ok(editor)
@@ -267,6 +267,7 @@ async fn make_block_meta_editor_map(
 }
 
 async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockMetaEditor> {
+    tracing::trace!("Open block:{} meta editor", block_id);
     let token = user.token()?;
     let user_id = user.user_id()?;
     let pool = user.db_pool()?;

+ 5 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs

@@ -77,7 +77,7 @@ impl NumberTypeOption {
     }
 
     fn cell_content_from_number_str(&self, s: &str) -> FlowyResult<String> {
-        return match self.format {
+        match self.format {
             NumberFormat::Number => {
                 if let Ok(v) = s.parse::<f64>() {
                     return Ok(v.to_string());
@@ -94,7 +94,7 @@ impl NumberTypeOption {
                 Ok(content)
             }
             _ => self.money_from_number_str(s),
-        };
+        }
     }
 
     pub fn set_format(&mut self, format: NumberFormat) {
@@ -173,7 +173,9 @@ impl CellDataOperation<String> for NumberTypeOption {
                 Ok(DecodedCellData::new(content))
             }
             _ => {
-                let content = self.money_from_number_str(&cell_data).unwrap_or("".to_string());
+                let content = self
+                    .money_from_number_str(&cell_data)
+                    .unwrap_or_else(|_| "".to_string());
                 Ok(DecodedCellData::new(content))
             }
         }

+ 29 - 0
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -487,6 +487,35 @@ impl GridMetaEditor {
         self.grid_pad.read().await.delta_bytes()
     }
 
+    pub async fn duplicate_grid(&self) -> FlowyResult<BuildGridContext> {
+        let grid_pad = self.grid_pad.read().await;
+        let original_blocks = grid_pad.get_block_metas();
+        let (duplicated_fields, duplicated_blocks) = grid_pad.duplicate_grid_meta().await;
+
+        let mut blocks_meta_data = vec![];
+        if original_blocks.len() == duplicated_blocks.len() {
+            for (index, original_block_meta) in original_blocks.iter().enumerate() {
+                let grid_block_meta_editor = self.block_manager.get_editor(&original_block_meta.block_id).await?;
+                let duplicated_block_id = &duplicated_blocks[index].block_id;
+
+                tracing::trace!("Duplicate block:{} meta data", duplicated_block_id);
+                let duplicated_block_meta_data = grid_block_meta_editor
+                    .duplicate_block_meta_data(duplicated_block_id)
+                    .await;
+                blocks_meta_data.push(duplicated_block_meta_data);
+            }
+        } else {
+            debug_assert_eq!(original_blocks.len(), duplicated_blocks.len());
+        }
+        drop(grid_pad);
+
+        Ok(BuildGridContext {
+            field_metas: duplicated_fields,
+            blocks: duplicated_blocks,
+            blocks_meta_data,
+        })
+    }
+
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
         F: for<'a> FnOnce(&'a mut GridMetaPad) -> FlowyResult<Option<GridChangeset>>,

+ 6 - 6
frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs

@@ -173,7 +173,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
         })
     }
 
-    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
+    fn view_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
         let view_id = view_id.to_string();
         let manager = self.0.clone();
         FutureResult::new(async move {
@@ -197,7 +197,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
         })
     }
 
-    fn process_create_view_data(
+    fn process_view_delta_data(
         &self,
         _user_id: &str,
         _view_id: &str,
@@ -245,13 +245,13 @@ impl ViewDataProcessor for GridViewDataProcessor {
         })
     }
 
-    fn delta_bytes(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
+    fn view_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();
         FutureResult::new(async move {
             let editor = grid_manager.open_grid(view_id).await?;
-            let delta_bytes = editor.delta_bytes().await;
-            Ok(delta_bytes)
+            let delta_bytes = editor.duplicate_grid().await?;
+            Ok(delta_bytes.into())
         })
     }
 
@@ -264,7 +264,7 @@ impl ViewDataProcessor for GridViewDataProcessor {
         FutureResult::new(async move { make_grid_view_data(&user_id, &view_id, grid_manager, build_context).await })
     }
 
-    fn process_create_view_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError> {
+    fn process_view_delta_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError> {
         let user_id = user_id.to_string();
         let view_id = view_id.to_string();
         let grid_manager = self.0.clone();

+ 9 - 19
shared-lib/flowy-grid-data-model/src/entities/meta.rs

@@ -203,11 +203,17 @@ impl CellMeta {
     }
 }
 
-#[derive(Clone, Deserialize, Serialize)]
+#[derive(Clone, Default, Deserialize, Serialize)]
 pub struct BuildGridContext {
     pub field_metas: Vec<FieldMeta>,
-    pub block_meta: GridBlockMeta,
-    pub block_meta_data: GridBlockMetaData,
+    pub blocks: Vec<GridBlockMeta>,
+    pub blocks_meta_data: Vec<GridBlockMetaData>,
+}
+
+impl BuildGridContext {
+    pub fn new() -> Self {
+        Self::default()
+    }
 }
 
 impl std::convert::From<BuildGridContext> for Bytes {
@@ -225,19 +231,3 @@ impl std::convert::TryFrom<Bytes> for BuildGridContext {
         Ok(ctx)
     }
 }
-
-impl std::default::Default for BuildGridContext {
-    fn default() -> Self {
-        let block_meta = GridBlockMeta::new();
-        let block_meta_data = GridBlockMetaData {
-            block_id: block_meta.block_id.clone(),
-            rows: vec![],
-        };
-
-        Self {
-            field_metas: vec![],
-            block_meta,
-            block_meta_data,
-        }
-    }
-}

+ 20 - 1
shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs

@@ -1,7 +1,9 @@
 use crate::entities::revision::{md5, RepeatedRevision, Revision};
 use crate::errors::{CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
-use flowy_grid_data_model::entities::{gen_block_id, CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset};
+use flowy_grid_data_model::entities::{
+    gen_block_id, gen_row_id, CellMeta, GridBlockMetaData, RowMeta, RowMetaChangeset,
+};
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
 use serde::{Deserialize, Serialize};
 use std::borrow::Cow;
@@ -22,6 +24,23 @@ pub struct GridBlockMetaPad {
 }
 
 impl GridBlockMetaPad {
+    pub async fn duplicate_data(&self, duplicated_block_id: &str) -> GridBlockMetaData {
+        let duplicated_rows = self
+            .rows
+            .iter()
+            .map(|row| {
+                let mut duplicated_row = row.as_ref().clone();
+                duplicated_row.id = gen_row_id();
+                duplicated_row.block_id = duplicated_block_id.to_string();
+                duplicated_row
+            })
+            .collect::<Vec<RowMeta>>();
+        GridBlockMetaData {
+            block_id: duplicated_block_id.to_string(),
+            rows: duplicated_rows,
+        }
+    }
+
     pub fn from_delta(delta: GridBlockMetaDelta) -> CollaborateResult<Self> {
         let s = delta.to_str()?;
         let meta_data: GridBlockMetaData = serde_json::from_str(&s).map_err(|e| {

+ 25 - 7
shared-lib/flowy-sync/src/client_grid/grid_builder.rs

@@ -1,11 +1,27 @@
 use crate::errors::{CollaborateError, CollaborateResult};
-use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, RowMeta};
+use flowy_grid_data_model::entities::{BuildGridContext, FieldMeta, GridBlockMeta, GridBlockMetaData, RowMeta};
 
-#[derive(Default)]
 pub struct GridBuilder {
     build_context: BuildGridContext,
 }
 
+impl std::default::Default for GridBuilder {
+    fn default() -> Self {
+        let mut build_context = BuildGridContext::new();
+
+        let block_meta = GridBlockMeta::new();
+        let block_meta_data = GridBlockMetaData {
+            block_id: block_meta.block_id.clone(),
+            rows: vec![],
+        };
+
+        build_context.blocks.push(block_meta);
+        build_context.blocks_meta_data.push(block_meta_data);
+
+        GridBuilder { build_context }
+    }
+}
+
 impl GridBuilder {
     pub fn add_field(mut self, field: FieldMeta) -> Self {
         self.build_context.field_metas.push(field);
@@ -13,9 +29,11 @@ impl GridBuilder {
     }
 
     pub fn add_empty_row(mut self) -> Self {
-        let row = RowMeta::new(&self.build_context.block_meta.block_id);
-        self.build_context.block_meta_data.rows.push(row);
-        self.build_context.block_meta.row_count += 1;
+        let row = RowMeta::new(&self.build_context.blocks.first().unwrap().block_id);
+        let block_meta = self.build_context.blocks.first_mut().unwrap();
+        let block_meta_data = self.build_context.blocks_meta_data.first_mut().unwrap();
+        block_meta_data.rows.push(row);
+        block_meta.row_count += 1;
         self
     }
 
@@ -57,13 +75,13 @@ mod tests {
         let grid_meta = GridMeta {
             grid_id,
             fields: build_context.field_metas,
-            blocks: vec![build_context.block_meta],
+            blocks: build_context.blocks,
         };
 
         let grid_meta_delta = make_grid_delta(&grid_meta);
         let _: GridMeta = serde_json::from_str(&grid_meta_delta.to_str().unwrap()).unwrap();
 
-        let grid_block_meta_delta = make_block_meta_delta(&build_context.block_meta_data);
+        let grid_block_meta_delta = make_block_meta_delta(build_context.blocks_meta_data.first().unwrap());
         let _: GridBlockMetaData = serde_json::from_str(&grid_block_meta_delta.to_str().unwrap()).unwrap();
     }
 }

+ 24 - 2
shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

@@ -3,8 +3,8 @@ use crate::errors::{internal_error, CollaborateError, CollaborateResult};
 use crate::util::{cal_diff, make_delta_from_revisions};
 use bytes::Bytes;
 use flowy_grid_data_model::entities::{
-    gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, GridBlockMetaChangeset,
-    GridMeta,
+    gen_block_id, gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta,
+    GridBlockMetaChangeset, GridMeta,
 };
 use lib_infra::util::move_vec_element;
 use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
@@ -24,6 +24,28 @@ pub trait JsonDeserializer {
 }
 
 impl GridMetaPad {
+    pub async fn duplicate_grid_meta(&self) -> (Vec<FieldMeta>, Vec<GridBlockMeta>) {
+        let fields = self
+            .grid_meta
+            .fields
+            .iter()
+            .map(|field| field.clone())
+            .collect::<Vec<FieldMeta>>();
+
+        let blocks = self
+            .grid_meta
+            .blocks
+            .iter()
+            .map(|block| {
+                let mut duplicated_block = block.clone();
+                duplicated_block.block_id = gen_block_id();
+                duplicated_block
+            })
+            .collect::<Vec<GridBlockMeta>>();
+
+        (fields, blocks)
+    }
+
     pub fn from_delta(delta: GridMetaDelta) -> CollaborateResult<Self> {
         let s = delta.to_str()?;
         let grid: GridMeta = serde_json::from_str(&s)