mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #541 from AppFlowy-IO/fix/grid_duplicate
fix: duplicate grid
This commit is contained in:
commit
4850a97315
@ -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
frontend/rust-lib/Cargo.lock
generated
1
frontend/rust-lib/Cargo.lock
generated
@ -937,6 +937,7 @@ dependencies = [
|
||||
"flowy-revision",
|
||||
"flowy-sync",
|
||||
"flowy-test",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"lib-dispatch",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl ViewController {
|
||||
params.data = view_data.to_vec();
|
||||
} else {
|
||||
let delta_data = processor
|
||||
.process_create_view_data(&user_id, ¶ms.view_id, params.data.clone())
|
||||
.process_view_delta_data(&user_id, ¶ms.view_id, params.data.clone())
|
||||
.await?;
|
||||
let _ = self
|
||||
.create_view(¶ms.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?;
|
||||
|
@ -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" }
|
||||
|
@ -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);
|
||||
});
|
||||
for block_meta_data in build_context.blocks_meta_data {
|
||||
let block_id = block_meta_data.block_id.clone();
|
||||
|
||||
// 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?;
|
||||
// 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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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()?;
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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| {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user