mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: sync the created view after duplicating (#5674)
* feat: sync the created view after duplicating * chore: revert launch.json * chore: refacotor code
This commit is contained in:
parent
a7b850e752
commit
8c1520b273
@ -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: '',
|
||||
|
@ -157,6 +157,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||
final result = await ViewBackendService.duplicate(
|
||||
view: view,
|
||||
openAfterDuplicate: true,
|
||||
syncAfterDuplicate: true,
|
||||
includeChildren: true,
|
||||
);
|
||||
emit(
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -140,7 +140,7 @@ impl EventIntegrationTest {
|
||||
self
|
||||
.appflowy_core
|
||||
.folder_manager
|
||||
.create_view_with_params(params)
|
||||
.create_view_with_params(params, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
@ -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) => {
|
||||
@ -338,7 +338,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
database_parent_view_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
Ok(None)
|
||||
})
|
||||
},
|
||||
}
|
||||
@ -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()) })
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
@ -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);
|
||||
@ -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(
|
||||
|
@ -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)]
|
||||
@ -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 {
|
||||
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
@ -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?;
|
||||
}
|
||||
@ -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
|
||||
@ -752,6 +764,7 @@ impl FolderManager {
|
||||
params.open_after_duplicate,
|
||||
params.include_children,
|
||||
params.suffix,
|
||||
params.sync_after_create,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -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(
|
||||
@ -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)
|
||||
});
|
||||
@ -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() {
|
||||
@ -823,6 +839,7 @@ impl FolderManager {
|
||||
} else {
|
||||
view.name.clone()
|
||||
};
|
||||
|
||||
let duplicate_params = CreateViewParams {
|
||||
parent_view_id: current_parent_id.clone(),
|
||||
name,
|
||||
@ -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(¤t_view_id).await?;
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -61,11 +61,14 @@ pub trait FolderOperationHandler {
|
||||
/// * `layout`: the layout of the view
|
||||
/// * `meta`: use to carry extra information. For example, the database view will use this
|
||||
/// to carry the reference database id.
|
||||
///
|
||||
/// The return value is the [Option<EncodedCollab>] that can be used to create the view.
|
||||
/// It can be used in syncing the view data to cloud.
|
||||
fn create_view_with_view_data(
|
||||
&self,
|
||||
user_id: i64,
|
||||
params: CreateViewParams,
|
||||
) -> FutureResult<(), FlowyError>;
|
||||
) -> FutureResult<Option<EncodedCollab>, FlowyError>;
|
||||
|
||||
/// Create a view with the pre-defined data.
|
||||
/// For example, the initial data of the grid/calendar/kanban board when
|
||||
|
Loading…
Reference in New Issue
Block a user