diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 4c9a8a0f7a..7e91aa05a4 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -1201,7 +1201,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa 1.0.6", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] diff --git a/frontend/appflowy_web/wasm-libs/Cargo.lock b/frontend/appflowy_web/wasm-libs/Cargo.lock index 16cec98380..2274c4fb42 100644 --- a/frontend/appflowy_web/wasm-libs/Cargo.lock +++ b/frontend/appflowy_web/wasm-libs/Cargo.lock @@ -900,7 +900,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -2779,7 +2779,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -2799,6 +2799,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -2866,6 +2867,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "phf_shared" version = "0.8.0" diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 42d997baba..595a605df0 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1103,7 +1103,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -3641,7 +3641,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -3661,6 +3661,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -3728,6 +3729,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "phf_shared" version = "0.8.0" diff --git a/frontend/rust-lib/event-integration/src/user_event.rs b/frontend/rust-lib/event-integration/src/user_event.rs index 768a6e1a5c..b348b89d64 100644 --- a/frontend/rust-lib/event-integration/src/user_event.rs +++ b/frontend/rust-lib/event-integration/src/user_event.rs @@ -4,12 +4,14 @@ use std::sync::Arc; use bytes::Bytes; +use flowy_folder::entities::{RepeatedViewPB, WorkspacePB}; use nanoid::nanoid; use protobuf::ProtobufError; use tokio::sync::broadcast::{channel, Sender}; use tracing::error; use uuid::Uuid; +use flowy_folder::event_map::FolderEvent; use flowy_notification::entities::SubscribeObject; use flowy_notification::NotificationSender; use flowy_server::af_cloud::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID}; @@ -17,12 +19,12 @@ use flowy_server_pub::af_cloud_config::AFCloudConfiguration; use flowy_server_pub::AuthenticatorType; use flowy_user::entities::{ AuthenticatorPB, CloudSettingPB, CreateWorkspacePB, ImportAppFlowyDataPB, OauthSignInPB, - RepeatedUserWorkspacePB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, UpdateCloudConfigPB, - UpdateUserProfilePayloadPB, UserProfilePB, UserWorkspaceIdPB, UserWorkspacePB, + RenameWorkspacePB, RepeatedUserWorkspacePB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, + UpdateCloudConfigPB, UpdateUserProfilePayloadPB, UserProfilePB, UserWorkspaceIdPB, + UserWorkspacePB, }; use flowy_user::errors::{FlowyError, FlowyResult}; use flowy_user::event_map::UserEvent; -use flowy_user::event_map::UserEvent::*; use lib_dispatch::prelude::{af_spawn, AFPluginDispatcher, AFPluginRequest, ToBytes}; use crate::event_builder::EventBuilder; @@ -31,7 +33,7 @@ use crate::EventIntegrationTest; impl EventIntegrationTest { pub async fn enable_encryption(&self) -> String { let config = EventBuilder::new(self.clone()) - .event(GetCloudConfig) + .event(UserEvent::GetCloudConfig) .async_send() .await .parse::(); @@ -40,7 +42,7 @@ impl EventIntegrationTest { enable_encrypt: Some(true), }; let error = EventBuilder::new(self.clone()) - .event(SetCloudConfig) + .event(UserEvent::SetCloudConfig) .payload(update) .async_send() .await @@ -68,7 +70,7 @@ impl EventIntegrationTest { .into_bytes() .unwrap(); - let request = AFPluginRequest::new(SignUp).payload(payload); + let request = AFPluginRequest::new(UserEvent::SignUp).payload(payload); let user_profile = AFPluginDispatcher::async_send(&self.appflowy_core.dispatcher(), request) .await .parse::() @@ -95,7 +97,7 @@ impl EventIntegrationTest { }; EventBuilder::new(self.clone()) - .event(OauthSignIn) + .event(UserEvent::OauthSignIn) .payload(payload) .async_send() .await @@ -104,7 +106,7 @@ impl EventIntegrationTest { pub async fn sign_out(&self) { EventBuilder::new(self.clone()) - .event(SignOut) + .event(UserEvent::SignOut) .async_send() .await; } @@ -119,7 +121,7 @@ impl EventIntegrationTest { pub async fn get_user_profile(&self) -> Result { EventBuilder::new(self.clone()) - .event(GetUserProfile) + .event(UserEvent::GetUserProfile) .async_send() .await .try_parse::() @@ -127,7 +129,7 @@ impl EventIntegrationTest { pub async fn update_user_profile(&self, params: UpdateUserProfilePayloadPB) { EventBuilder::new(self.clone()) - .event(UpdateUserProfile) + .event(UserEvent::UpdateUserProfile) .payload(params) .async_send() .await; @@ -139,7 +141,7 @@ impl EventIntegrationTest { authenticator: AuthenticatorPB::AppFlowyCloud, }; let sign_in_url = EventBuilder::new(self.clone()) - .event(GenerateSignInURL) + .event(UserEvent::GenerateSignInURL) .payload(payload) .async_send() .await @@ -155,7 +157,7 @@ impl EventIntegrationTest { }; let user_profile = EventBuilder::new(self.clone()) - .event(OauthSignIn) + .event(UserEvent::OauthSignIn) .payload(payload) .async_send() .await @@ -182,7 +184,7 @@ impl EventIntegrationTest { }; let user_profile = EventBuilder::new(self.clone()) - .event(OauthSignIn) + .event(UserEvent::OauthSignIn) .payload(payload) .async_send() .await @@ -217,16 +219,53 @@ impl EventIntegrationTest { name: name.to_string(), }; EventBuilder::new(self.clone()) - .event(CreateWorkspace) + .event(UserEvent::CreateWorkspace) .payload(payload) .async_send() .await .parse::() } + pub async fn rename_workspace( + &self, + workspace_id: &str, + new_name: &str, + ) -> Result<(), FlowyError> { + let payload = RenameWorkspacePB { + workspace_id: workspace_id.to_owned(), + new_name: new_name.to_owned(), + }; + match EventBuilder::new(self.clone()) + .event(UserEvent::RenameWorkspace) + .payload(payload) + .async_send() + .await + .error() + { + Some(err) => Err(err), + None => Ok(()), + } + } + + pub async fn folder_read_current_workspace(&self) -> WorkspacePB { + EventBuilder::new(self.clone()) + .event(FolderEvent::ReadCurrentWorkspace) + .async_send() + .await + .parse() + } + + pub async fn folder_read_workspace_views(&self) -> RepeatedViewPB { + EventBuilder::new(self.clone()) + .event(FolderEvent::ReadWorkspaceViews) + .async_send() + .await + .parse() + } + pub async fn get_all_workspaces(&self) -> RepeatedUserWorkspacePB { EventBuilder::new(self.clone()) - .event(GetAllWorkspace) + .event(UserEvent::GetAllWorkspace) .async_send() .await .parse::() @@ -237,7 +276,7 @@ impl EventIntegrationTest { workspace_id: workspace_id.to_string(), }; EventBuilder::new(self.clone()) - .event(DeleteWorkspace) + .event(UserEvent::DeleteWorkspace) .payload(payload) .async_send() .await; @@ -248,7 +287,7 @@ impl EventIntegrationTest { workspace_id: workspace_id.to_string(), }; EventBuilder::new(self.clone()) - .event(OpenWorkspace) + .event(UserEvent::OpenWorkspace) .payload(payload) .async_send() .await; diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs index 18b367e619..802349c512 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs @@ -2,11 +2,26 @@ use std::time::Duration; use event_integration::user_event::user_localhost_af_cloud; use event_integration::EventIntegrationTest; -use flowy_user::entities::RepeatedUserWorkspacePB; +use flowy_user::entities::{RepeatedUserWorkspacePB, UserWorkspacePB}; use flowy_user::protobuf::UserNotification; use crate::util::receive_with_timeout; +#[tokio::test] +async fn af_cloud_workspace_name_change() { + user_localhost_af_cloud().await; + let test = EventIntegrationTest::new().await; + let user_profile_pb = test.af_cloud_sign_up().await; + let workspaces = test.get_all_workspaces().await; + let workspace_id = workspaces.items[0].workspace_id.as_str(); + test + .rename_workspace(workspace_id, "new_workspace_name") + .await + .expect("failed to rename workspace"); + let workspaces = get_synced_workspaces(&test, user_profile_pb.id).await; + assert_eq!(workspaces[0].name, "new_workspace_name".to_string()); +} + #[tokio::test] async fn af_cloud_create_workspace_test() { user_localhost_af_cloud().await; @@ -14,22 +29,34 @@ async fn af_cloud_create_workspace_test() { let user_profile_pb = test.af_cloud_sign_up().await; let workspaces = test.get_all_workspaces().await.items; + let first_workspace_id = workspaces[0].workspace_id.as_str(); assert_eq!(workspaces.len(), 1); - test.create_workspace("my second workspace").await; - let _workspaces = test.get_all_workspaces().await.items; - - let a = user_profile_pb.id.to_string(); - let rx = test - .notification_sender - .subscribe::(&a, UserNotification::DidUpdateUserWorkspaces as i32); - let workspaces = receive_with_timeout(rx, Duration::from_secs(30)) - .await - .unwrap() - .items; + let created_workspace = test.create_workspace("my second workspace").await; + assert_eq!(created_workspace.name, "my second workspace"); + let workspaces = get_synced_workspaces(&test, user_profile_pb.id).await; assert_eq!(workspaces.len(), 2); assert_eq!(workspaces[1].name, "my second workspace".to_string()); + + { + // before opening new workspace + let folder_ws = test.folder_read_current_workspace().await; + assert_eq!(&folder_ws.id, first_workspace_id); + let views = test.folder_read_workspace_views().await; + assert_eq!(views.items[0].parent_view_id.as_str(), first_workspace_id); + } + { + // after opening new workspace + test.open_workspace(&created_workspace.workspace_id).await; + let folder_ws = test.folder_read_current_workspace().await; + assert_eq!(folder_ws.id, created_workspace.workspace_id); + let views = test.folder_read_workspace_views().await; + assert_eq!( + views.items[0].parent_view_id.as_str(), + created_workspace.workspace_id + ); + } } #[tokio::test] @@ -50,3 +77,18 @@ async fn af_cloud_open_workspace_test() { assert_eq!(views[1].name, "my first document".to_string()); assert_eq!(views[2].name, "my second document".to_string()); } + +async fn get_synced_workspaces(test: &EventIntegrationTest, user_id: i64) -> Vec { + let _workspaces = test.get_all_workspaces().await.items; + let sub_id = user_id.to_string(); + let rx = test + .notification_sender + .subscribe::( + &sub_id, + UserNotification::DidUpdateUserWorkspaces as i32, + ); + receive_with_timeout(rx, Duration::from_secs(30)) + .await + .unwrap() + .items +} diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs index 73717798f0..625790d578 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::{anyhow, Error}; use client_api::entity::workspace_dto::{ - CreateWorkspaceMember, CreateWorkspaceParam, WorkspaceMemberChangeset, + CreateWorkspaceMember, CreateWorkspaceParam, PatchWorkspaceParam, WorkspaceMemberChangeset, }; use client_api::entity::{AFRole, AFWorkspace, AuthProvider, CollabParams, CreateCollabParams}; use client_api::{Client, ClientConfiguration}; @@ -16,6 +16,7 @@ use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, User use flowy_user_pub::entities::*; use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; +use uuid::Uuid; use crate::af_cloud::define::USER_SIGN_IN_URL; use crate::af_cloud::impls::user::dto::{ @@ -320,6 +321,32 @@ where Ok(()) }) } + + fn patch_workspace( + &self, + workspace_id: &str, + new_workspace_name: Option<&str>, + new_workspace_icon: Option<&str>, + ) -> FutureResult<(), FlowyError> { + let try_get_client = self.server.try_get_client(); + let owned_workspace_id = workspace_id.to_owned(); + let owned_workspace_name = new_workspace_name.map(|s| s.to_owned()); + let owned_workspace_icon = new_workspace_icon.map(|s| s.to_owned()); + FutureResult::new(async move { + let workspace_id: Uuid = owned_workspace_id + .parse() + .map_err(|_| ErrorCode::InvalidParams)?; + let client = try_get_client?; + client + .patch_workspace(PatchWorkspaceParam { + workspace_id, + workspace_name: owned_workspace_name, + workspace_icon: owned_workspace_icon, + }) + .await?; + Ok(()) + }) + } } async fn get_admin_client(client: &Arc) -> FlowyResult { diff --git a/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs b/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs index feb63ddc38..367d1bd732 100644 --- a/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs +++ b/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs @@ -179,7 +179,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl { FutureResult::new(async { Err( FlowyError::local_version_not_support() - .with_context("local server doesn't support mulitple workspaces"), + .with_context("local server doesn't support multiple workspaces"), ) }) } @@ -188,7 +188,21 @@ impl UserCloudService for LocalServerUserAuthServiceImpl { FutureResult::new(async { Err( FlowyError::local_version_not_support() - .with_context("local server doesn't support mulitple workspaces"), + .with_context("local server doesn't support multiple workspaces"), + ) + }) + } + + fn patch_workspace( + &self, + _workspace_id: &str, + _new_workspace_name: Option<&str>, + _new_workspace_icon: Option<&str>, + ) -> FutureResult<(), FlowyError> { + FutureResult::new(async { + Err( + FlowyError::local_version_not_support() + .with_context("local server doesn't support multiple workspaces"), ) }) } diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs index 1307787c73..382388558b 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs @@ -372,6 +372,20 @@ where ) }) } + + fn patch_workspace( + &self, + _workspace_id: &str, + _new_workspace_name: Option<&str>, + _new_workspace_icon: Option<&str>, + ) -> FutureResult<(), FlowyError> { + FutureResult::new(async { + Err( + FlowyError::local_version_not_support() + .with_context("supabase server doesn't support mulitple workspaces"), + ) + }) + } } pub struct CreateCollabAction { diff --git a/frontend/rust-lib/flowy-user-pub/src/cloud.rs b/frontend/rust-lib/flowy-user-pub/src/cloud.rs index 27cc2233f0..fe27ec5898 100644 --- a/frontend/rust-lib/flowy-user-pub/src/cloud.rs +++ b/frontend/rust-lib/flowy-user-pub/src/cloud.rs @@ -171,6 +171,14 @@ pub trait UserCloudService: Send + Sync + 'static { /// Returns the new workspace if successful fn create_workspace(&self, workspace_name: &str) -> FutureResult; + // Updates the workspace name and icon + fn patch_workspace( + &self, + workspace_id: &str, + new_workspace_name: Option<&str>, + new_workspace_icon: Option<&str>, + ) -> FutureResult<(), FlowyError>; + /// Deletes a workspace owned by the user. fn delete_workspace(&self, workspace_id: &str) -> FutureResult<(), FlowyError>; diff --git a/frontend/rust-lib/flowy-user/src/entities/user_profile.rs b/frontend/rust-lib/flowy-user/src/entities/user_profile.rs index 030c5b1179..2ccbe6143b 100644 --- a/frontend/rust-lib/flowy-user/src/entities/user_profile.rs +++ b/frontend/rust-lib/flowy-user/src/entities/user_profile.rs @@ -225,6 +225,9 @@ pub struct UserWorkspacePB { #[pb(index = 2)] pub name: String, + + #[pb(index = 3)] + pub created_at_timestamp: i64, } impl From for UserWorkspacePB { @@ -232,6 +235,7 @@ impl From for UserWorkspacePB { Self { workspace_id: value.id, name: value.name, + created_at_timestamp: value.created_at.timestamp(), } } } diff --git a/frontend/rust-lib/flowy-user/src/entities/workspace.rs b/frontend/rust-lib/flowy-user/src/entities/workspace.rs index daef940819..c98e256547 100644 --- a/frontend/rust-lib/flowy-user/src/entities/workspace.rs +++ b/frontend/rust-lib/flowy-user/src/entities/workspace.rs @@ -116,3 +116,24 @@ pub struct CreateWorkspacePB { #[validate(custom = "required_not_empty_str")] pub name: String, } + +#[derive(ProtoBuf, Default, Clone, Validate)] +pub struct RenameWorkspacePB { + #[pb(index = 1)] + #[validate(custom = "required_not_empty_str")] + pub workspace_id: String, + + #[pb(index = 2)] + #[validate(custom = "required_not_empty_str")] + pub new_name: String, +} + +#[derive(ProtoBuf, Default, Clone, Validate)] +pub struct ChangeWorkspaceIconPB { + #[pb(index = 1)] + #[validate(custom = "required_not_empty_str")] + pub workspace_id: String, + + #[pb(index = 2)] + pub new_icon: String, +} diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index 40cba9c282..bff1ef891b 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -683,3 +683,29 @@ pub async fn delete_workspace_handler( manager.delete_workspace(&workspace_id).await?; Ok(()) } + +#[tracing::instrument(level = "debug", skip_all, err)] +pub async fn rename_workspace_handler( + rename_workspace_param: AFPluginData, + manager: AFPluginState>, +) -> Result<(), FlowyError> { + let params = rename_workspace_param.try_into_inner()?; + let manager = upgrade_manager(manager)?; + manager + .patch_workspace(¶ms.workspace_id, Some(¶ms.new_name), None) + .await?; + Ok(()) +} + +#[tracing::instrument(level = "debug", skip_all, err)] +pub async fn change_workspace_icon_handler( + change_workspace_icon_param: AFPluginData, + manager: AFPluginState>, +) -> Result<(), FlowyError> { + let params = change_workspace_icon_param.try_into_inner()?; + let manager = upgrade_manager(manager)?; + manager + .patch_workspace(¶ms.workspace_id, None, Some(¶ms.new_icon)) + .await?; + Ok(()) +} diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 3ac2c8a7b8..611fd9bad1 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -62,6 +62,8 @@ pub fn init(user_manager: Weak) -> AFPlugin { .event(UserEvent::GetAllWorkspace, get_all_workspace_handler) .event(UserEvent::CreateWorkspace, create_workspace_handler) .event(UserEvent::DeleteWorkspace, delete_workspace_handler) + .event(UserEvent::RenameWorkspace, rename_workspace_handler) + .event(UserEvent::ChangeWorkspaceIcon, change_workspace_icon_handler) } #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] @@ -200,6 +202,12 @@ pub enum UserEvent { #[event(input = "UserWorkspaceIdPB")] DeleteWorkspace = 43, + + #[event(input = "RenameWorkspacePB")] + RenameWorkspace = 44, + + #[event(input = "ChangeWorkspaceIconPB")] + ChangeWorkspaceIcon = 45, } pub trait UserStatusCallback: Send + Sync + 'static { diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index aa75a6f912..1b3ea81533 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -167,6 +167,21 @@ impl UserManager { Ok(new_workspace) } + pub async fn patch_workspace( + &self, + workspace_id: &str, + new_workspace_name: Option<&str>, + new_workspace_icon: Option<&str>, + ) -> FlowyResult<()> { + self + .cloud_services + .get_user_service()? + .patch_workspace(workspace_id, new_workspace_name, new_workspace_icon) + .await?; + + Ok(()) + } + pub async fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> { self .cloud_services