Skip to content

Commit

Permalink
feat: sync the created view after duplicating (#5674)
Browse files Browse the repository at this point in the history
* feat: sync the created view after duplicating

* chore: revert launch.json

* chore: refacotor code
  • Loading branch information
LucasXu0 authored Jul 2, 2024
1 parent a7b850e commit 8c1520b
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
await ViewBackendService.duplicate(
view: view,
openAfterDuplicate: true,
syncAfterDuplicate: true,
includeChildren: true,
parentViewId: newSpace.id,
suffix: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
final result = await ViewBackendService.duplicate(
view: view,
openAfterDuplicate: true,
syncAfterDuplicate: true,
includeChildren: true,
);
emit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,13 @@ class ViewBackendService {
required bool includeChildren,
String? parentViewId,
String? suffix,
required bool syncAfterDuplicate,
}) {
final payload = DuplicateViewPayloadPB.create()
..viewId = view.id
..openAfterDuplicate = openAfterDuplicate
..includeChildren = includeChildren;
..includeChildren = includeChildren
..syncAfterCreate = syncAfterDuplicate;

if (parentViewId != null) {
payload.parentViewId = parentViewId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,11 @@ class ViewMoreActionTypeWrapper extends CustomActionCell {
leftIcon: inner.leftIcon,
rightIcon: inner.rightIcon,
iconPadding: 10.0,
text: SizedBox(
height: 18.0,
child: FlowyText.regular(
inner.name,
color: inner == ViewMoreActionType.delete
? Theme.of(context).colorScheme.error
: null,
),
text: FlowyText.regular(
inner.name,
color: inner == ViewMoreActionType.delete
? Theme.of(context).colorScheme.error
: null,
),
onTap: onTap,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl EventIntegrationTest {
self
.appflowy_core
.folder_manager
.create_view_with_params(params)
.create_view_with_params(params, true)
.await
.unwrap();
}
Expand Down
16 changes: 8 additions & 8 deletions frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,16 @@ impl FolderOperationHandler for DocumentFolderOperation {
&self,
user_id: i64,
params: CreateViewParams,
) -> FutureResult<(), FlowyError> {
) -> FutureResult<Option<EncodedCollab>, FlowyError> {
debug_assert_eq!(params.layout, ViewLayoutPB::Document);
let view_id = params.view_id.to_string();
let manager = self.0.clone();
FutureResult::new(async move {
let data = DocumentDataPB::try_from(Bytes::from(params.initial_data))?;
manager
let encoded_collab = manager
.create_document(user_id, &view_id, Some(data.into()))
.await?;
Ok(())
Ok(Some(encoded_collab))
})
}

Expand Down Expand Up @@ -301,16 +301,16 @@ impl FolderOperationHandler for DatabaseFolderOperation {
&self,
_user_id: i64,
params: CreateViewParams,
) -> FutureResult<(), FlowyError> {
) -> FutureResult<Option<EncodedCollab>, FlowyError> {
match CreateDatabaseExtParams::from_map(params.meta.clone()) {
None => {
let database_manager = self.0.clone();
let view_id = params.view_id.to_string();
FutureResult::new(async move {
database_manager
let encoded_collab = database_manager
.create_database_with_database_data(&view_id, params.initial_data)
.await?;
Ok(())
Ok(Some(encoded_collab))
})
},
Some(database_params) => {
Expand Down Expand Up @@ -338,7 +338,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
database_parent_view_id,
)
.await?;
Ok(())
Ok(None)
})
},
}
Expand Down Expand Up @@ -505,7 +505,7 @@ impl FolderOperationHandler for ChatFolderOperation {
&self,
_user_id: i64,
_params: CreateViewParams,
) -> FutureResult<(), FlowyError> {
) -> FutureResult<Option<EncodedCollab>, FlowyError> {
FutureResult::new(async move { Err(FlowyError::not_support()) })
}

Expand Down
13 changes: 9 additions & 4 deletions frontend/rust-lib/flowy-database2/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLay
use collab_database::workspace_database::{
CollabDocStateByOid, CollabFuture, DatabaseCollabService, DatabaseMeta, WorkspaceDatabase,
};
use collab_entity::CollabType;
use collab_entity::{CollabType, EncodedCollab};
use collab_plugins::local_storage::kv::KVTransactionDB;
use tokio::sync::{Mutex, RwLock};
use tracing::{event, instrument, trace};
Expand Down Expand Up @@ -289,7 +289,7 @@ impl DatabaseManager {
&self,
view_id: &str,
data: Vec<u8>,
) -> FlowyResult<()> {
) -> FlowyResult<EncodedCollab> {
let database_data = DatabaseData::from_json_bytes(data)?;

let mut create_database_params = CreateDatabaseParams::from_database_data(database_data);
Expand All @@ -305,8 +305,13 @@ impl DatabaseManager {
}

let wdb = self.get_database_indexer().await?;
let _ = wdb.create_database(create_database_params)?;
Ok(())
let database = wdb.create_database(create_database_params)?;
let encoded_collab = database
.lock()
.get_collab()
.lock()
.encode_collab_v1(|collab| CollabType::Database.validate_require_data(collab))?;
Ok(encoded_collab)
}

pub async fn create_database_with_params(
Expand Down
6 changes: 6 additions & 0 deletions frontend/rust-lib/flowy-folder/src/entities/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,9 @@ pub struct DuplicateViewPayloadPB {
// If the suffix is None, the duplicated view will have the same name with (copy) suffix.
#[pb(index = 5, one_of)]
pub suffix: Option<String>,

#[pb(index = 6)]
pub sync_after_create: bool,
}

#[derive(Debug)]
Expand All @@ -612,6 +615,8 @@ pub struct DuplicateViewParams {
pub parent_view_id: Option<String>,

pub suffix: Option<String>,

pub sync_after_create: bool,
}

impl TryInto<DuplicateViewParams> for DuplicateViewPayloadPB {
Expand All @@ -625,6 +630,7 @@ impl TryInto<DuplicateViewParams> for DuplicateViewPayloadPB {
include_children: self.include_children,
parent_view_id: self.parent_view_id,
suffix: self.suffix,
sync_after_create: self.sync_after_create,
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/rust-lib/flowy-folder/src/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub(crate) async fn create_view_handler(
let folder = upgrade_folder(folder)?;
let params: CreateViewParams = data.into_inner().try_into()?;
let set_as_current = params.set_as_current;
let view = folder.create_view_with_params(params).await?;
let (view, _) = folder.create_view_with_params(params, true).await?;
if set_as_current {
let _ = folder.set_current_view(&view.id).await;
}
Expand Down
102 changes: 84 additions & 18 deletions frontend/rust-lib/flowy-folder/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,21 @@ impl FolderManager {
}
}

pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
/// Asynchronously creates a view with provided parameters and notifies the workspace if update is needed.
///
/// Commonly, the notify_workspace_update parameter is set to true when the view is created in the workspace.
/// If you're handling multiple views in the same hierarchy and want to notify the workspace only after the last view is created,
/// you can set notify_workspace_update to false to avoid multiple notifications.
pub async fn create_view_with_params(
&self,
params: CreateViewParams,
notify_workspace_update: bool,
) -> FlowyResult<(View, Option<EncodedCollab>)> {
let workspace_id = self.user.workspace_id()?;
let view_layout: ViewLayout = params.layout.clone().into();
let handler = self.get_handler(&view_layout)?;
let user_id = self.user.user_id()?;
let mut encoded_collab: Option<EncodedCollab> = None;

if params.meta.is_empty() && params.initial_data.is_empty() {
tracing::trace!("Create view with build-in data");
Expand All @@ -384,7 +394,7 @@ impl FolderManager {
.await?;
} else {
tracing::trace!("Create view with view data");
handler
encoded_collab = handler
.create_view_with_view_data(user_id, params.clone())
.await?;
}
Expand All @@ -403,12 +413,14 @@ impl FolderManager {
},
);

let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
if notify_workspace_update {
let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
}
}

Ok(view)
Ok((view, encoded_collab))
}

/// The orphan view is meant to be a view that is not attached to any parent view. By default, this
Expand Down Expand Up @@ -752,6 +764,7 @@ impl FolderManager {
params.open_after_duplicate,
params.include_children,
params.suffix,
params.sync_after_create,
)
.await
}
Expand All @@ -767,6 +780,7 @@ impl FolderManager {
open_after_duplicated: bool,
include_children: bool,
suffix: Option<String>,
sync_after_create: bool,
) -> Result<(), FlowyError> {
if view_id == parent_view_id {
return Err(FlowyError::new(
Expand All @@ -775,6 +789,7 @@ impl FolderManager {
));
}

// filter the view ids that in the trash or private section
let filtered_view_ids = self.with_folder(Vec::new, |folder| {
self.get_view_ids_should_be_filtered(folder)
});
Expand All @@ -783,6 +798,7 @@ impl FolderManager {
let mut is_source_view = true;
// use a stack to duplicate the view and its children
let mut stack = vec![(view_id.to_string(), parent_view_id.to_string())];
let mut objects = vec![];
let suffix = suffix.unwrap_or(" (copy)".to_string());

while let Some((current_view_id, current_parent_id)) = stack.pop() {
Expand Down Expand Up @@ -823,6 +839,7 @@ impl FolderManager {
} else {
view.name.clone()
};

let duplicate_params = CreateViewParams {
parent_view_id: current_parent_id.clone(),
name,
Expand All @@ -838,7 +855,30 @@ impl FolderManager {
icon: view.icon.clone(),
};

let duplicated_view = self.create_view_with_params(duplicate_params).await?;
// set the notify_workspace_update to false to avoid multiple notifications
let (duplicated_view, encoded_collab) = self
.create_view_with_params(duplicate_params, false)
.await?;

if sync_after_create {
if let Some(encoded_collab) = encoded_collab {
let object_id = duplicated_view.id.clone();
let collab_type = match duplicated_view.layout {
ViewLayout::Document => CollabType::Document,
ViewLayout::Board | ViewLayout::Grid | ViewLayout::Calendar => CollabType::Database,
ViewLayout::Chat => CollabType::Unknown,
};
// don't block the whole import process if the view can't be encoded
if collab_type != CollabType::Unknown {
match self.get_folder_collab_params(object_id, collab_type, encoded_collab) {
Ok(params) => objects.push(params),
Err(e) => {
error!("duplicate error {}", e);
},
}
}
}
}

if include_children {
let child_views = self.get_views_belong_to(&current_view_id).await?;
Expand All @@ -854,6 +894,23 @@ impl FolderManager {
is_source_view = false
}

let workspace_id = &self.user.workspace_id()?;

// Sync the view to the cloud
if sync_after_create {
self
.cloud_service
.batch_create_folder_collab_objects(workspace_id, objects)
.await?;
}

// notify the update here
notify_parent_view_did_change(
workspace_id,
self.mutex_folder.clone(),
vec![parent_view_id.to_string()],
);

Ok(())
}

Expand Down Expand Up @@ -1128,18 +1185,11 @@ impl FolderManager {

if sync_after_create {
if let Some(encoded_collab) = encoded_collab {
// Try to encode the collaboration data to bytes
let encode_collab_v1 = encoded_collab.encode_to_bytes().map_err(internal_error);

// If the view can't be encoded, skip it and don't block the whole import process
match encode_collab_v1 {
Ok(encode_collab_v1) => objects.push(FolderCollabParams {
object_id,
encoded_collab_v1: encode_collab_v1,
collab_type,
}),
// don't block the whole import process if the view can't be encoded
match self.get_folder_collab_params(object_id, collab_type, encoded_collab) {
Ok(params) => objects.push(params),
Err(e) => {
error!("import error {}", e)
error!("import error {}", e);
},
}
}
Expand Down Expand Up @@ -1214,6 +1264,22 @@ impl FolderManager {
}
}

fn get_folder_collab_params(
&self,
object_id: String,
collab_type: CollabType,
encoded_collab: EncodedCollab,
) -> FlowyResult<FolderCollabParams> {
// Try to encode the collaboration data to bytes
let encoded_collab_v1: Result<Vec<u8>, FlowyError> =
encoded_collab.encode_to_bytes().map_err(internal_error);
encoded_collab_v1.map(|encoded_collab_v1| FolderCollabParams {
object_id,
encoded_collab_v1,
collab_type,
})
}

/// Returns the relation of the view. The relation is a tuple of (is_workspace, parent_view_id,
/// child_view_ids). If the view is a workspace, then the parent_view_id is the workspace id.
/// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the
Expand Down
2 changes: 1 addition & 1 deletion frontend/rust-lib/flowy-folder/src/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl FolderManager {
icon: None,
extra: None,
};
self.create_view_with_params(params).await.unwrap();
self.create_view_with_params(params, true).await.unwrap();
view_id
}
}
Loading

0 comments on commit 8c1520b

Please sign in to comment.