From 605a53f2ae08319a5ea1cebe79b17510b5b40fd0 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 27 Jun 2024 14:12:52 +0800 Subject: [PATCH] feat: open the duplicated space by default (#5638) --- .../application/sidebar/space/space_bloc.dart | 45 ++++++++++++++++--- .../application/view/view_service.dart | 11 +++++ .../flowy-folder/src/entities/view.rs | 16 +++++++ frontend/rust-lib/flowy-folder/src/manager.rs | 11 ++++- 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index e754528325..f6a2226e6a 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -297,12 +297,11 @@ class SpaceBloc extends Bloc { if (currentSpace == null) { return; } - await ViewBackendService.duplicate( - view: currentSpace, - openAfterDuplicate: false, - includeChildren: true, - ); - add(const SpaceEvent.didReceiveSpaceUpdate()); + final newSpace = await _duplicateSpace(currentSpace); + // open the duplicated space + if (newSpace != null) { + add(SpaceEvent.open(newSpace)); + } }, ); }, @@ -602,6 +601,40 @@ class SpaceBloc extends Bloc { return false; } + + Future _duplicateSpace(ViewPB space) async { + // if the space is not duplicated, try to create a new space + final icon = space.icon.value.isNotEmpty + ? space.icon.value + : builtInSpaceIcons.first; + final iconColor = space.spaceIconColor ?? builtInSpaceColors.first; + final newSpace = await _createSpace( + name: '${space.name} (copy)', + icon: icon, + iconColor: iconColor, + permission: space.spacePermission, + ); + + if (newSpace == null) { + return null; + } + + for (final view in space.childViews) { + unawaited( + ViewBackendService.duplicate( + view: view, + openAfterDuplicate: true, + includeChildren: true, + parentViewId: newSpace.id, + suffix: '', + ), + ); + } + + Log.info('Space duplicated: $newSpace'); + add(const SpaceEvent.didReceiveSpaceUpdate()); + return newSpace; + } } @freezed diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart index 2e27ba364d..9a0874b711 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart @@ -141,11 +141,22 @@ class ViewBackendService { required bool openAfterDuplicate, // should include children views required bool includeChildren, + String? parentViewId, + String? suffix, }) { final payload = DuplicateViewPayloadPB.create() ..viewId = view.id ..openAfterDuplicate = openAfterDuplicate ..includeChildren = includeChildren; + + if (parentViewId != null) { + payload.parentViewId = parentViewId; + } + + if (suffix != null) { + payload.suffix = suffix; + } + return FolderEventDuplicateView(payload).send(); } diff --git a/frontend/rust-lib/flowy-folder/src/entities/view.rs b/frontend/rust-lib/flowy-folder/src/entities/view.rs index 25c5e1c9ab..79654602e8 100644 --- a/frontend/rust-lib/flowy-folder/src/entities/view.rs +++ b/frontend/rust-lib/flowy-folder/src/entities/view.rs @@ -584,6 +584,16 @@ pub struct DuplicateViewPayloadPB { #[pb(index = 3)] pub include_children: bool, + + // duplicate the view to the specified parent view. + // if the parent_view_id is None, the view will be duplicated to the same parent view. + #[pb(index = 4, one_of)] + pub parent_view_id: Option, + + // The suffix of the duplicated view name. + // If the suffix is None, the duplicated view will have the same name with (copy) suffix. + #[pb(index = 5, one_of)] + pub suffix: Option, } #[derive(Debug)] @@ -593,6 +603,10 @@ pub struct DuplicateViewParams { pub open_after_duplicate: bool, pub include_children: bool, + + pub parent_view_id: Option, + + pub suffix: Option, } impl TryInto for DuplicateViewPayloadPB { @@ -604,6 +618,8 @@ impl TryInto for DuplicateViewPayloadPB { view_id, open_after_duplicate: self.open_after_duplicate, include_children: self.include_children, + parent_view_id: self.parent_view_id, + suffix: self.suffix, }) } } diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index fa8c688310..e053caabb4 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -741,12 +741,17 @@ impl FolderManager { let view = self .with_folder(|| None, |folder| folder.views.get_view(¶ms.view_id)) .ok_or_else(|| FlowyError::record_not_found().with_context("Can't duplicate the view"))?; + let parent_view_id = params + .parent_view_id + .clone() + .unwrap_or(view.parent_view_id.clone()); self .duplicate_view_with_parent_id( &view.id, - &view.parent_view_id, + &parent_view_id, params.open_after_duplicate, params.include_children, + params.suffix, ) .await } @@ -761,6 +766,7 @@ impl FolderManager { parent_view_id: &str, open_after_duplicated: bool, include_children: bool, + suffix: Option, ) -> Result<(), FlowyError> { if view_id == parent_view_id { return Err(FlowyError::new( @@ -777,6 +783,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 suffix = suffix.unwrap_or(" (copy)".to_string()); while let Some((current_view_id, current_parent_id)) = stack.pop() { let view = self @@ -812,7 +819,7 @@ impl FolderManager { ); let name = if is_source_view { - format!("{} (copy)", &view.name) + format!("{}{}", &view.name, suffix) } else { view.name.clone() };