mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 06:37:14 -04:00
Merge pull request #7794 from AppFlowy-IO/optimize_write_user_workspaces
refactor: only notify when user workspaces were changed
This commit is contained in:
commit
65b7916a6a
55 changed files with 550 additions and 557 deletions
|
@ -202,7 +202,8 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
|||
List<Widget> _buildPublishActions(BuildContext context) {
|
||||
final userProfile = context.read<MobileViewPageBloc>().state.userProfilePB;
|
||||
// the publish feature is only available for AppFlowy Cloud
|
||||
if (userProfile == null || userProfile.authType != AuthTypePB.Server) {
|
||||
if (userProfile == null ||
|
||||
userProfile.workspaceAuthType != AuthTypePB.Server) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class HomePageSettingsPopupMenu extends StatelessWidget {
|
|||
text: LocaleKeys.settings_popupMenuItem_settings.tr(),
|
||||
),
|
||||
// only show the member items in cloud mode
|
||||
if (userProfile.authType == AuthTypePB.Server) ...[
|
||||
if (userProfile.workspaceAuthType == AuthTypePB.Server) ...[
|
||||
const PopupMenuDivider(height: 0.5),
|
||||
_buildItem(
|
||||
value: _MobileSettingsPopupMenuItem.members,
|
||||
|
|
|
@ -167,7 +167,7 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
|
|||
children: [
|
||||
MobileHomeSpace(userProfile: widget.userProfile),
|
||||
// only show ai chat button for cloud user
|
||||
if (widget.userProfile.authType == AuthTypePB.Server)
|
||||
if (widget.userProfile.workspaceAuthType == AuthTypePB.Server)
|
||||
Positioned(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 16,
|
||||
left: 20,
|
||||
|
|
|
@ -40,7 +40,7 @@ class UserSessionSettingGroup extends StatelessWidget {
|
|||
|
||||
// delete account button
|
||||
// only show the delete account button in cloud mode
|
||||
if (userProfile.authType == AuthTypePB.Server) ...[
|
||||
if (userProfile.workspaceAuthType == AuthTypePB.Server) ...[
|
||||
const VSpace(16.0),
|
||||
MobileLogoutButton(
|
||||
text: LocaleKeys.button_deleteAccount.tr(),
|
||||
|
|
|
@ -31,7 +31,7 @@ class DatabaseSyncBloc extends Bloc<DatabaseSyncEvent, DatabaseSyncBlocState> {
|
|||
emit(
|
||||
state.copyWith(
|
||||
shouldShowIndicator:
|
||||
userProfile?.authType == AuthTypePB.Server &&
|
||||
userProfile?.workspaceAuthType == AuthTypePB.Server &&
|
||||
databaseId != null,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -69,7 +69,8 @@ class RowBanner extends StatefulWidget {
|
|||
class _RowBannerState extends State<RowBanner> {
|
||||
final _isHovering = ValueNotifier(false);
|
||||
late final isLocalMode =
|
||||
(widget.userProfile?.authType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
(widget.userProfile?.workspaceAuthType ?? AuthTypePB.Local) ==
|
||||
AuthTypePB.Local;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
|
@ -101,7 +101,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||
|
||||
bool get isLocalMode {
|
||||
final userProfilePB = state.userProfilePB;
|
||||
final type = userProfilePB?.authType ?? AuthTypePB.Local;
|
||||
final type = userProfilePB?.workspaceAuthType ?? AuthTypePB.Local;
|
||||
return type == AuthTypePB.Local;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ class DocumentCollaboratorsBloc
|
|||
final userProfile = result.fold((s) => s, (f) => null);
|
||||
emit(
|
||||
state.copyWith(
|
||||
shouldShowIndicator: userProfile?.authType == AuthTypePB.Server,
|
||||
shouldShowIndicator:
|
||||
userProfile?.workspaceAuthType == AuthTypePB.Server,
|
||||
),
|
||||
);
|
||||
final deviceId = ApplicationInfo.deviceId;
|
||||
|
|
|
@ -30,7 +30,8 @@ class DocumentSyncBloc extends Bloc<DocumentSyncEvent, DocumentSyncBlocState> {
|
|||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
shouldShowIndicator: userProfile?.authType == AuthTypePB.Server,
|
||||
shouldShowIndicator:
|
||||
userProfile?.workspaceAuthType == AuthTypePB.Server,
|
||||
),
|
||||
);
|
||||
_syncStateListener.start(
|
||||
|
|
|
@ -185,7 +185,7 @@ Future<void> insertLocalFile(
|
|||
|
||||
// Check upload type
|
||||
final isLocalMode =
|
||||
(userProfile?.authType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
(userProfile?.workspaceAuthType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
|
||||
String? path;
|
||||
String? errorMsg;
|
||||
|
@ -230,7 +230,7 @@ Future<void> insertLocalFiles(
|
|||
|
||||
// Check upload type
|
||||
final isLocalMode =
|
||||
(userProfile?.authType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
(userProfile?.workspaceAuthType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
|
||||
for (final file in files) {
|
||||
final fileType = file.fileType.toMediaFileTypePB();
|
||||
|
|
|
@ -225,7 +225,8 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||
(s) => s,
|
||||
(f) => null,
|
||||
);
|
||||
final isAppFlowyCloud = userProfile?.authType == AuthTypePB.Server;
|
||||
final isAppFlowyCloud =
|
||||
userProfile?.workspaceAuthType == AuthTypePB.Server;
|
||||
final PageStyleCoverImageType type;
|
||||
if (!isAppFlowyCloud) {
|
||||
result = await saveImageToLocalStorage(path);
|
||||
|
|
|
@ -193,7 +193,7 @@ class ShareBloc extends Bloc<ShareEvent, ShareState> {
|
|||
Future<void> _updatePublishStatus(Emitter<ShareState> emit) async {
|
||||
final publishInfo = await ViewBackendService.getPublishInfo(view);
|
||||
final enablePublish = await UserBackendService.getCurrentUserProfile().fold(
|
||||
(v) => v.authType == AuthTypePB.Server,
|
||||
(v) => v.workspaceAuthType == AuthTypePB.Server,
|
||||
(p) => false,
|
||||
);
|
||||
|
||||
|
|
|
@ -294,8 +294,8 @@ class _IconUploaderState extends State<IconUploader> {
|
|||
(userProfile) => userProfile,
|
||||
(l) => null,
|
||||
);
|
||||
final isLocalMode =
|
||||
(userProfile?.authType ?? AuthTypePB.Local) == AuthTypePB.Local;
|
||||
final isLocalMode = (userProfile?.workspaceAuthType ?? AuthTypePB.Local) ==
|
||||
AuthTypePB.Local;
|
||||
if (isLocalMode) {
|
||||
result = await pickedImages.first.saveToLocal();
|
||||
} else {
|
||||
|
|
|
@ -46,7 +46,7 @@ class PasswordBloc extends Bloc<PasswordEvent, PasswordState> {
|
|||
bool _isInitialized = false;
|
||||
|
||||
Future<void> _init() async {
|
||||
if (userProfile.authType == AuthTypePB.Local) {
|
||||
if (userProfile.workspaceAuthType == AuthTypePB.Local) {
|
||||
Log.debug('PasswordBloc: skip init because user is local authenticator');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -74,8 +74,8 @@ class AnonUserItem extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final icon = isSelected ? const FlowySvg(FlowySvgs.check_s) : null;
|
||||
final isDisabled = isSelected || user.authType != AuthTypePB.Local;
|
||||
final desc = "${user.name}\t ${user.authType}\t";
|
||||
final isDisabled = isSelected || user.workspaceAuthType != AuthTypePB.Local;
|
||||
final desc = "${user.name}\t ${user.workspaceAuthType}\t";
|
||||
final child = SizedBox(
|
||||
height: 30,
|
||||
child: FlowyButton(
|
||||
|
|
|
@ -91,7 +91,7 @@ class SettingsDialogBloc
|
|||
]) async {
|
||||
if ([
|
||||
AuthTypePB.Local,
|
||||
].contains(userProfile.authType)) {
|
||||
].contains(userProfile.workspaceAuthType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
|||
final currentWorkspace = result.$1;
|
||||
final workspaces = result.$2;
|
||||
final isCollabWorkspaceOn =
|
||||
userProfile.authType == AuthTypePB.Server &&
|
||||
userProfile.userAuthType == AuthTypePB.Server &&
|
||||
FeatureFlag.collaborativeWorkspace.isOn;
|
||||
Log.info(
|
||||
'init workspace, current workspace: ${currentWorkspace?.workspaceId}, '
|
||||
|
|
|
@ -211,7 +211,7 @@ class ViewMoreActionTypeWrapper extends CustomActionCell {
|
|||
) {
|
||||
final userProfile = context.read<SpaceBloc>().userProfile;
|
||||
// move to feature doesn't support in local mode
|
||||
if (userProfile.authType != AuthTypePB.Server) {
|
||||
if (userProfile.workspaceAuthType != AuthTypePB.Server) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return BlocProvider.value(
|
||||
|
|
|
@ -70,7 +70,7 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
|
|||
// user email
|
||||
// Only show email if the user is authenticated and not using local auth
|
||||
if (isAuthEnabled &&
|
||||
state.userProfile.authType != AuthTypePB.Local) ...[
|
||||
state.userProfile.workspaceAuthType != AuthTypePB.Local) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.newSettings_myAccount_myAccount.tr(),
|
||||
children: [
|
||||
|
@ -82,26 +82,30 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
|
|||
),
|
||||
AccountSignInOutSection(
|
||||
userProfile: state.userProfile,
|
||||
onAction: state.userProfile.authType == AuthTypePB.Local
|
||||
onAction: state.userProfile.workspaceAuthType ==
|
||||
AuthTypePB.Local
|
||||
? widget.didLogin
|
||||
: widget.didLogout,
|
||||
signIn: state.userProfile.authType == AuthTypePB.Local,
|
||||
signIn: state.userProfile.workspaceAuthType ==
|
||||
AuthTypePB.Local,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
if (isAuthEnabled &&
|
||||
state.userProfile.authType == AuthTypePB.Local) ...[
|
||||
state.userProfile.workspaceAuthType == AuthTypePB.Local) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_accountPage_login_title.tr(),
|
||||
children: [
|
||||
AccountSignInOutSection(
|
||||
userProfile: state.userProfile,
|
||||
onAction: state.userProfile.authType == AuthTypePB.Local
|
||||
onAction: state.userProfile.workspaceAuthType ==
|
||||
AuthTypePB.Local
|
||||
? widget.didLogin
|
||||
: widget.didLogout,
|
||||
signIn: state.userProfile.authType == AuthTypePB.Local,
|
||||
signIn: state.userProfile.workspaceAuthType ==
|
||||
AuthTypePB.Local,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -116,7 +120,7 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
|
|||
),
|
||||
|
||||
// user deletion
|
||||
if (widget.userProfile.authType == AuthTypePB.Server)
|
||||
if (widget.userProfile.workspaceAuthType == AuthTypePB.Server)
|
||||
const AccountDeletionButton(),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -88,7 +88,7 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||
autoSeparate: false,
|
||||
children: [
|
||||
// We don't allow changing workspace name/icon for local/offline
|
||||
if (userProfile.authType != AuthTypePB.Local) ...[
|
||||
if (userProfile.workspaceAuthType != AuthTypePB.Local) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_workspacePage_workspaceName_title
|
||||
.tr(),
|
||||
|
@ -180,7 +180,7 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||
),
|
||||
const SettingsCategorySpacer(),
|
||||
|
||||
if (userProfile.authType != AuthTypePB.Local) ...[
|
||||
if (userProfile.workspaceAuthType != AuthTypePB.Local) ...[
|
||||
SingleSettingAction(
|
||||
label: LocaleKeys.settings_workspacePage_manageWorkspace_title
|
||||
.tr(),
|
||||
|
|
|
@ -140,7 +140,7 @@ class SettingsDialog extends StatelessWidget {
|
|||
case SettingsPage.shortcuts:
|
||||
return const SettingsShortcutsView();
|
||||
case SettingsPage.ai:
|
||||
if (user.authType == AuthTypePB.Server) {
|
||||
if (user.workspaceAuthType == AuthTypePB.Server) {
|
||||
return SettingsAIView(
|
||||
key: ValueKey(workspaceId),
|
||||
userProfile: user,
|
||||
|
|
|
@ -63,7 +63,7 @@ class SettingsMenu extends StatelessWidget {
|
|||
changeSelectedPage: changeSelectedPage,
|
||||
),
|
||||
if (FeatureFlag.membersSettings.isOn &&
|
||||
userProfile.authType == AuthTypePB.Server)
|
||||
userProfile.workspaceAuthType == AuthTypePB.Server)
|
||||
SettingsMenuElement(
|
||||
page: SettingsPage.member,
|
||||
selectedPage: currentPage,
|
||||
|
@ -109,7 +109,7 @@ class SettingsMenu extends StatelessWidget {
|
|||
),
|
||||
changeSelectedPage: changeSelectedPage,
|
||||
),
|
||||
if (userProfile.authType == AuthTypePB.Server)
|
||||
if (userProfile.workspaceAuthType == AuthTypePB.Server)
|
||||
SettingsMenuElement(
|
||||
page: SettingsPage.sites,
|
||||
selectedPage: currentPage,
|
||||
|
|
|
@ -96,7 +96,7 @@ class _MoreViewActionsState extends State<MoreViewActions> {
|
|||
return BlocBuilder<SpaceBloc, SpaceState>(
|
||||
builder: (context, state) {
|
||||
if (state.spaces.isEmpty &&
|
||||
userProfile.authType == AuthTypePB.Server) {
|
||||
userProfile.workspaceAuthType == AuthTypePB.Server) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
|
|
|
@ -359,7 +359,12 @@ impl AppFlowyCollabBuilder {
|
|||
{
|
||||
if let Some(collab_db) = collab_db.upgrade() {
|
||||
let write_txn = collab_db.write_txn();
|
||||
trace!("flush collab:{}-{}-{} to disk", uid, collab_type, object_id);
|
||||
trace!(
|
||||
"flush workspace: {} {}:collab:{} to disk",
|
||||
workspace_id,
|
||||
collab_type,
|
||||
object_id
|
||||
);
|
||||
let collab: &Collab = collab.borrow();
|
||||
let encode_collab =
|
||||
collab.encode_collab_v1(|collab| collab_type.validate_require_data(collab))?;
|
||||
|
|
|
@ -397,18 +397,3 @@ impl ViewTest {
|
|||
Self::new(sdk, ViewLayout::Calendar, data).await
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn create_workspace(sdk: &EventIntegrationTest, name: &str, desc: &str) -> WorkspacePB {
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name: name.to_owned(),
|
||||
desc: desc.to_owned(),
|
||||
};
|
||||
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(CreateFolderWorkspace)
|
||||
.payload(request)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<WorkspacePB>()
|
||||
}
|
||||
|
|
|
@ -4,23 +4,6 @@ use flowy_folder::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB, ViewIcon
|
|||
use flowy_folder::entities::*;
|
||||
use flowy_user::errors::ErrorCode;
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_workspace_event_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name: "my second workspace".to_owned(),
|
||||
desc: "".to_owned(),
|
||||
};
|
||||
let view_pb = EventBuilder::new(test)
|
||||
.event(flowy_folder::event_map::FolderEvent::CreateFolderWorkspace)
|
||||
.payload(request)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<flowy_folder::entities::ViewPB>();
|
||||
|
||||
assert_eq!(view_pb.parent_view_id, "my second workspace".to_owned());
|
||||
}
|
||||
|
||||
// #[tokio::test]
|
||||
// async fn open_workspace_event_test() {
|
||||
// let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
|
@ -464,35 +447,6 @@ async fn move_view_event_after_delete_view_test2() {
|
|||
assert_eq!(views[3].name, "My 1-5 view");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_parent_view_with_invalid_name() {
|
||||
for (name, code) in invalid_workspace_name_test_case() {
|
||||
let sdk = EventIntegrationTest::new().await;
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name,
|
||||
desc: "".to_owned(),
|
||||
};
|
||||
assert_eq!(
|
||||
EventBuilder::new(sdk)
|
||||
.event(flowy_folder::event_map::FolderEvent::CreateFolderWorkspace)
|
||||
.payload(request)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
.unwrap()
|
||||
.code,
|
||||
code
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
|
||||
vec![
|
||||
("".to_owned(), ErrorCode::WorkspaceNameInvalid),
|
||||
("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),
|
||||
]
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_view_across_parent_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
mod local_test;
|
||||
|
||||
// #[cfg(feature = "supabase_cloud_test")]
|
||||
// mod supabase_test;
|
||||
|
|
|
@ -72,7 +72,7 @@ async fn migrate_anon_user_data_to_af_cloud_test() {
|
|||
let user = test.af_cloud_sign_up().await;
|
||||
let workspace = test.get_current_workspace().await;
|
||||
println!("user workspace: {:?}", workspace.id);
|
||||
assert_eq!(user.auth_type, AuthTypePB::Server);
|
||||
assert_eq!(user.user_auth_type, AuthTypePB::Server);
|
||||
|
||||
let user_first_level_views = test.get_all_workspace_views().await;
|
||||
assert_eq!(user_first_level_views.len(), 3);
|
||||
|
|
|
@ -12,7 +12,7 @@ pub async fn get_synced_workspaces(
|
|||
test: &EventIntegrationTest,
|
||||
user_id: i64,
|
||||
) -> Vec<UserWorkspacePB> {
|
||||
let _workspaces = test.get_all_workspaces().await.items;
|
||||
let workspaces = test.get_all_workspaces().await.items;
|
||||
let sub_id = user_id.to_string();
|
||||
let rx = test
|
||||
.notification_sender
|
||||
|
@ -20,8 +20,9 @@ pub async fn get_synced_workspaces(
|
|||
&sub_id,
|
||||
UserNotification::DidUpdateUserWorkspaces as i32,
|
||||
);
|
||||
receive_with_timeout(rx, Duration::from_secs(60))
|
||||
.await
|
||||
.unwrap()
|
||||
.items
|
||||
if let Some(result) = receive_with_timeout(rx, Duration::from_secs(10)).await {
|
||||
result.items
|
||||
} else {
|
||||
workspaces
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,3 +292,39 @@ async fn af_cloud_different_open_same_workspace_test() {
|
|||
assert_eq!(views.len(), 2, "only get: {:?}", views); // Expecting two views.
|
||||
assert_eq!(views[0].name, "General");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_create_local_workspace_test() {
|
||||
use_localhost_af_cloud().await;
|
||||
let test = EventIntegrationTest::new().await;
|
||||
let _ = test.af_cloud_sign_up().await;
|
||||
|
||||
let workspaces = test.get_all_workspaces().await.items;
|
||||
assert_eq!(workspaces.len(), 1);
|
||||
|
||||
let created_workspace = test
|
||||
.create_workspace("my local workspace", AuthType::Local)
|
||||
.await;
|
||||
assert_eq!(created_workspace.name, "my local workspace");
|
||||
|
||||
let workspaces = test.get_all_workspaces().await.items;
|
||||
assert_eq!(workspaces.len(), 2);
|
||||
assert_eq!(workspaces[1].name, "my local workspace");
|
||||
|
||||
test
|
||||
.open_workspace(
|
||||
&created_workspace.workspace_id,
|
||||
created_workspace.workspace_auth_type,
|
||||
)
|
||||
.await;
|
||||
|
||||
let views = test.get_all_views().await;
|
||||
assert_eq!(views.len(), 2);
|
||||
assert!(views
|
||||
.iter()
|
||||
.any(|view| view.parent_view_id == workspaces[1].workspace_id));
|
||||
|
||||
for view in views {
|
||||
test.get_view(&view.id).await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ async fn anon_user_profile_get() {
|
|||
.await
|
||||
.parse::<UserProfilePB>();
|
||||
assert_eq!(user_profile.id, user.id);
|
||||
assert_eq!(user_profile.auth_type, AuthTypePB::Local);
|
||||
assert_eq!(user_profile.user_auth_type, AuthTypePB::Local);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -26,8 +26,7 @@ use flowy_document::deps::DocumentData;
|
|||
use flowy_document_pub::cloud::{DocumentCloudService, DocumentSnapshot};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_folder_pub::cloud::{
|
||||
FolderCloudService, FolderCollabParams, FolderData, FolderSnapshot, FullSyncCollabParams,
|
||||
Workspace, WorkspaceRecord,
|
||||
FolderCloudService, FolderCollabParams, FolderSnapshot, FullSyncCollabParams,
|
||||
};
|
||||
use flowy_folder_pub::entities::PublishPayload;
|
||||
use flowy_search_pub::cloud::SearchCloudService;
|
||||
|
@ -230,43 +229,13 @@ impl UserCloudServiceProvider for ServerProvider {
|
|||
|
||||
#[async_trait]
|
||||
impl FolderCloudService for ServerProvider {
|
||||
async fn create_workspace(&self, uid: i64, name: &str) -> Result<Workspace, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
let name = name.to_string();
|
||||
server.folder_service().create_workspace(uid, &name).await
|
||||
}
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server.folder_service().open_workspace(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server.folder_service().get_all_workspace().await
|
||||
}
|
||||
|
||||
async fn get_folder_data(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
uid: &i64,
|
||||
) -> Result<Option<FolderData>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
||||
server
|
||||
.folder_service()
|
||||
.get_folder_data(workspace_id, uid)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_folder_snapshots(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<FolderSnapshot>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.get_folder_snapshots(workspace_id, limit)
|
||||
.await
|
||||
|
@ -279,14 +248,25 @@ impl FolderCloudService for ServerProvider {
|
|||
collab_type: CollabType,
|
||||
object_id: &Uuid,
|
||||
) -> Result<Vec<u8>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.get_folder_doc_state(workspace_id, uid, collab_type, object_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn full_sync_collab_object(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
params: FullSyncCollabParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.full_sync_collab_object(workspace_id, params)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn batch_create_folder_collab_objects(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
|
@ -312,9 +292,8 @@ impl FolderCloudService for ServerProvider {
|
|||
workspace_id: &Uuid,
|
||||
payload: Vec<PublishPayload>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.publish_view(workspace_id, payload)
|
||||
.await
|
||||
|
@ -325,8 +304,8 @@ impl FolderCloudService for ServerProvider {
|
|||
workspace_id: &Uuid,
|
||||
view_ids: Vec<Uuid>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.unpublish_views(workspace_id, view_ids)
|
||||
.await
|
||||
|
@ -343,8 +322,8 @@ impl FolderCloudService for ServerProvider {
|
|||
view_id: Uuid,
|
||||
new_name: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.set_publish_name(workspace_id, view_id, new_name)
|
||||
.await
|
||||
|
@ -355,21 +334,13 @@ impl FolderCloudService for ServerProvider {
|
|||
workspace_id: &Uuid,
|
||||
new_namespace: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.set_publish_namespace(workspace_id, new_namespace)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.folder_service()
|
||||
.get_publish_namespace(workspace_id)
|
||||
.await
|
||||
}
|
||||
|
||||
/// List all published views of the current workspace.
|
||||
async fn list_published_views(
|
||||
&self,
|
||||
|
@ -413,6 +384,14 @@ impl FolderCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.folder_service()
|
||||
.get_publish_namespace(workspace_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn import_zip(&self, file_path: &str) -> Result<(), FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
|
@ -420,18 +399,6 @@ impl FolderCloudService for ServerProvider {
|
|||
.import_zip(file_path)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn full_sync_collab_object(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
params: FullSyncCollabParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.folder_service()
|
||||
.full_sync_collab_object(workspace_id, params)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
|
|
@ -11,22 +11,6 @@ use uuid::Uuid;
|
|||
/// [FolderCloudService] represents the cloud service for folder.
|
||||
#[async_trait]
|
||||
pub trait FolderCloudService: Send + Sync + 'static {
|
||||
/// Creates a new workspace for the user.
|
||||
/// Returns error if the cloud service doesn't support multiple workspaces
|
||||
async fn create_workspace(&self, uid: i64, name: &str) -> Result<Workspace, FlowyError>;
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError>;
|
||||
|
||||
/// Returns all workspaces of the user.
|
||||
/// Returns vec![] if the cloud service doesn't support multiple workspaces
|
||||
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError>;
|
||||
|
||||
async fn get_folder_data(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
uid: &i64,
|
||||
) -> Result<Option<FolderData>, FlowyError>;
|
||||
|
||||
async fn get_folder_snapshots(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
|
|
|
@ -18,28 +18,6 @@ fn upgrade_folder(
|
|||
Ok(folder)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, folder), err)]
|
||||
pub(crate) async fn create_workspace_handler(
|
||||
data: AFPluginData<CreateWorkspacePayloadPB>,
|
||||
folder: AFPluginState<Weak<FolderManager>>,
|
||||
) -> DataResult<WorkspacePB, FlowyError> {
|
||||
let folder = upgrade_folder(folder)?;
|
||||
let params: CreateWorkspaceParams = data.into_inner().try_into()?;
|
||||
let workspace = folder.create_workspace(params).await?;
|
||||
let views = folder
|
||||
.get_views_belong_to(&workspace.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|view| view_pb_without_child_views(view.as_ref().clone()))
|
||||
.collect::<Vec<ViewPB>>();
|
||||
data_result_ok(WorkspacePB {
|
||||
id: workspace.id,
|
||||
name: workspace.name,
|
||||
views,
|
||||
create_time: workspace.created_at,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub(crate) async fn get_all_workspace_handler(
|
||||
_data: AFPluginData<CreateWorkspacePayloadPB>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use crate::manager::FolderManager;
|
|||
pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
||||
AFPlugin::new().name("Flowy-Folder").state(folder)
|
||||
// Workspace
|
||||
.event(FolderEvent::CreateFolderWorkspace, create_workspace_handler)
|
||||
.event(FolderEvent::GetCurrentWorkspaceSetting, read_current_workspace_setting_handler)
|
||||
.event(FolderEvent::ReadCurrentWorkspace, read_current_workspace_handler)
|
||||
.event(FolderEvent::ReadWorkspaceViews, get_workspace_views_handler)
|
||||
|
@ -60,8 +59,7 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||
#[event_err = "FlowyError"]
|
||||
pub enum FolderEvent {
|
||||
/// Create a new workspace
|
||||
#[event(input = "CreateWorkspacePayloadPB", output = "WorkspacePB")]
|
||||
/// Deprecated: Create a new workspace
|
||||
CreateFolderWorkspace = 0,
|
||||
|
||||
/// Read the current opening workspace. Currently, we only support one workspace
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::entities::icon::UpdateViewIconParams;
|
||||
use crate::entities::{
|
||||
view_pb_with_child_views, view_pb_without_child_views, view_pb_without_child_views_from_arc,
|
||||
CreateViewParams, CreateWorkspaceParams, DeletedViewPB, DuplicateViewParams, FolderSnapshotPB,
|
||||
MoveNestedViewParams, RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, UpdateViewParams,
|
||||
ViewLayoutPB, ViewPB, ViewSectionPB, WorkspaceLatestPB, WorkspacePB,
|
||||
CreateViewParams, DeletedViewPB, DuplicateViewParams, FolderSnapshotPB, MoveNestedViewParams,
|
||||
RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, UpdateViewParams, ViewLayoutPB, ViewPB,
|
||||
ViewSectionPB, WorkspaceLatestPB, WorkspacePB,
|
||||
};
|
||||
use crate::manager_observer::{
|
||||
notify_child_views_changed, notify_did_update_workspace, notify_parent_view_did_change,
|
||||
|
@ -353,16 +353,6 @@ impl FolderManager {
|
|||
///
|
||||
pub async fn clear(&self, _user_id: i64) {}
|
||||
|
||||
#[tracing::instrument(level = "info", skip_all, err)]
|
||||
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> FlowyResult<Workspace> {
|
||||
let uid = self.user.user_id()?;
|
||||
let new_workspace = self
|
||||
.cloud_service
|
||||
.create_workspace(uid, ¶ms.name)
|
||||
.await?;
|
||||
Ok(new_workspace)
|
||||
}
|
||||
|
||||
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceLatestPB> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let latest_view = self.get_current_view().await;
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
use client_api::entity::workspace_dto::PublishInfoView;
|
||||
use client_api::entity::{
|
||||
workspace_dto::CreateWorkspaceParam, CollabParams, PublishCollabItem, PublishCollabMetadata,
|
||||
QueryCollab, QueryCollabParams,
|
||||
CollabParams, PublishCollabItem, PublishCollabMetadata, QueryCollab, QueryCollabParams,
|
||||
};
|
||||
use client_api::entity::{PatchPublishedCollab, PublishInfo};
|
||||
use collab::core::collab::DataSource;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab_entity::CollabType;
|
||||
use collab_folder::RepeatedViewIdentifier;
|
||||
use serde_json::to_vec;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Weak;
|
||||
|
@ -16,8 +12,7 @@ use uuid::Uuid;
|
|||
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_folder_pub::cloud::{
|
||||
Folder, FolderCloudService, FolderCollabParams, FolderData, FolderSnapshot, FullSyncCollabParams,
|
||||
Workspace, WorkspaceRecord,
|
||||
FolderCloudService, FolderCollabParams, FolderSnapshot, FullSyncCollabParams,
|
||||
};
|
||||
use flowy_folder_pub::entities::PublishPayload;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
|
@ -36,83 +31,6 @@ impl<T> FolderCloudService for AFCloudFolderCloudServiceImpl<T>
|
|||
where
|
||||
T: AFServer,
|
||||
{
|
||||
async fn create_workspace(&self, _uid: i64, name: &str) -> Result<Workspace, FlowyError> {
|
||||
let try_get_client = self.inner.try_get_client();
|
||||
let cloned_name = name.to_string();
|
||||
|
||||
let client = try_get_client?;
|
||||
let new_workspace = client
|
||||
.create_workspace(CreateWorkspaceParam {
|
||||
workspace_name: Some(cloned_name),
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(Workspace {
|
||||
id: new_workspace.workspace_id.to_string(),
|
||||
name: new_workspace.workspace_name,
|
||||
created_at: new_workspace.created_at.timestamp(),
|
||||
child_views: RepeatedViewIdentifier::new(vec![]),
|
||||
created_by: Some(new_workspace.owner_uid),
|
||||
last_edited_time: new_workspace.created_at.timestamp(),
|
||||
last_edited_by: Some(new_workspace.owner_uid),
|
||||
})
|
||||
}
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
|
||||
let try_get_client = self.inner.try_get_client();
|
||||
let client = try_get_client?;
|
||||
let _ = client.open_workspace(workspace_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError> {
|
||||
let try_get_client = self.inner.try_get_client();
|
||||
|
||||
let client = try_get_client?;
|
||||
let records = client
|
||||
.get_user_workspace_info()
|
||||
.await?
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|af_workspace| WorkspaceRecord {
|
||||
id: af_workspace.workspace_id.to_string(),
|
||||
name: af_workspace.workspace_name,
|
||||
created_at: af_workspace.created_at.timestamp(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(records)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn get_folder_data(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
uid: &i64,
|
||||
) -> Result<Option<FolderData>, FlowyError> {
|
||||
let uid = *uid;
|
||||
let try_get_client = self.inner.try_get_client();
|
||||
let params = QueryCollabParams {
|
||||
workspace_id: *workspace_id,
|
||||
inner: QueryCollab::new(*workspace_id, CollabType::Folder),
|
||||
};
|
||||
let doc_state = try_get_client?
|
||||
.get_collab(params)
|
||||
.await
|
||||
.map_err(FlowyError::from)?
|
||||
.encode_collab
|
||||
.doc_state
|
||||
.to_vec();
|
||||
check_request_workspace_id_is_match(workspace_id, &self.logged_user, "get folder data")?;
|
||||
let folder = Folder::from_collab_doc_state(
|
||||
uid,
|
||||
CollabOrigin::Empty,
|
||||
DataSource::DocStateV1(doc_state),
|
||||
&workspace_id.to_string(),
|
||||
vec![],
|
||||
)?;
|
||||
Ok(folder.get_folder_data(&workspace_id.to_string()))
|
||||
}
|
||||
|
||||
async fn get_folder_snapshots(
|
||||
&self,
|
||||
_workspace_id: &str,
|
||||
|
@ -278,15 +196,6 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
let namespace = self
|
||||
.inner
|
||||
.try_get_client()?
|
||||
.get_workspace_publish_namespace(workspace_id)
|
||||
.await?;
|
||||
Ok(namespace)
|
||||
}
|
||||
|
||||
async fn list_published_views(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
|
@ -337,6 +246,15 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
let namespace = self
|
||||
.inner
|
||||
.try_get_client()?
|
||||
.get_workspace_publish_namespace(workspace_id)
|
||||
.await?;
|
||||
Ok(namespace)
|
||||
}
|
||||
|
||||
async fn import_zip(&self, file_path: &str) -> Result<(), FlowyError> {
|
||||
let file_path = PathBuf::from(file_path);
|
||||
let client = self.inner.try_get_client()?;
|
||||
|
|
|
@ -21,16 +21,6 @@ use client_api::{Client, ClientConfiguration};
|
|||
use collab_entity::{CollabObject, CollabType};
|
||||
use tracing::{instrument, trace};
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver};
|
||||
use flowy_user_pub::entities::{
|
||||
AFCloudOAuthParams, AuthResponse, Role, UpdateUserProfileParams, UserProfile, UserWorkspace,
|
||||
WorkspaceInvitation, WorkspaceInvitationStatus, WorkspaceMember,
|
||||
};
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::af_cloud::define::{LoggedUser, USER_SIGN_IN_URL};
|
||||
use crate::af_cloud::impls::user::dto::{
|
||||
af_update_from_update_params, from_af_workspace_member, to_af_role, user_profile_from_af_profile,
|
||||
|
@ -38,6 +28,16 @@ use crate::af_cloud::impls::user::dto::{
|
|||
use crate::af_cloud::impls::user::util::encryption_type_from_profile;
|
||||
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
|
||||
use crate::af_cloud::{AFCloudClient, AFServer};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver};
|
||||
use flowy_user_pub::entities::{
|
||||
AFCloudOAuthParams, AuthResponse, AuthType, Role, UpdateUserProfileParams, UserProfile,
|
||||
UserWorkspace, WorkspaceInvitation, WorkspaceInvitationStatus, WorkspaceMember,
|
||||
};
|
||||
use flowy_user_pub::sql::select_user_workspace;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_status};
|
||||
|
||||
|
@ -178,25 +178,30 @@ where
|
|||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
async fn get_user_profile(&self, _uid: i64) -> Result<UserProfile, FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
let expected_workspace_id = self
|
||||
async fn get_user_profile(
|
||||
&self,
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let client = self.server.try_get_client()?;
|
||||
let logged_user = self
|
||||
.logged_user
|
||||
.upgrade()
|
||||
.ok_or_else(FlowyError::user_not_login)?
|
||||
.workspace_id()?;
|
||||
let client = try_get_client?;
|
||||
.ok_or_else(FlowyError::user_not_login)?;
|
||||
|
||||
let profile = client.get_profile().await?;
|
||||
let token = client.get_token()?;
|
||||
let profile = user_profile_from_af_profile(token, profile)?;
|
||||
|
||||
let mut conn = logged_user.get_sqlite_db(uid)?;
|
||||
let workspace_auth_type = select_user_workspace(workspace_id, &mut conn)
|
||||
.map(|row| AuthType::from(row.workspace_type))
|
||||
.unwrap_or(AuthType::AppFlowyCloud);
|
||||
let profile = user_profile_from_af_profile(token, profile, workspace_auth_type)?;
|
||||
|
||||
// Discard the response if the user has switched to a new workspace. This avoids updating the
|
||||
// user profile with potentially outdated information when the workspace ID no longer matches.
|
||||
check_request_workspace_id_is_match(
|
||||
&expected_workspace_id,
|
||||
&self.logged_user,
|
||||
"get user profile",
|
||||
)?;
|
||||
let workspace_id = Uuid::from_str(workspace_id)?;
|
||||
check_request_workspace_id_is_match(&workspace_id, &self.logged_user, "get user profile")?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
|
@ -219,10 +224,10 @@ where
|
|||
}
|
||||
|
||||
async fn create_workspace(&self, workspace_name: &str) -> Result<UserWorkspace, FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
let workspace_name_owned = workspace_name.to_owned();
|
||||
let client = try_get_client?;
|
||||
let new_workspace = client
|
||||
let new_workspace = self
|
||||
.server
|
||||
.try_get_client()?
|
||||
.create_workspace(CreateWorkspaceParam {
|
||||
workspace_name: Some(workspace_name_owned),
|
||||
})
|
||||
|
@ -236,10 +241,10 @@ where
|
|||
new_workspace_name: Option<String>,
|
||||
new_workspace_icon: Option<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
let workspace_id = workspace_id.to_owned();
|
||||
let client = try_get_client?;
|
||||
client
|
||||
self
|
||||
.server
|
||||
.try_get_client()?
|
||||
.patch_workspace(PatchWorkspaceParam {
|
||||
workspace_id,
|
||||
workspace_name: new_workspace_name,
|
||||
|
|
|
@ -25,6 +25,7 @@ pub fn af_update_from_update_params(update: UpdateUserProfileParams) -> UpdateUs
|
|||
pub fn user_profile_from_af_profile(
|
||||
token: String,
|
||||
profile: AFUserProfile,
|
||||
workspace_auth_type: AuthType,
|
||||
) -> Result<UserProfile, Error> {
|
||||
let icon_url = {
|
||||
profile
|
||||
|
@ -44,6 +45,7 @@ pub fn user_profile_from_af_profile(
|
|||
auth_type: AuthType::AppFlowyCloud,
|
||||
uid: profile.uid,
|
||||
updated_at: profile.updated_at,
|
||||
workspace_auth_type,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@ use collab_plugins::local_storage::kv::doc::CollabKVAction;
|
|||
use collab_plugins::local_storage::kv::KVTransactionDB;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_folder_pub::cloud::{
|
||||
gen_workspace_id, FolderCloudService, FolderCollabParams, FolderData, FolderSnapshot,
|
||||
FullSyncCollabParams, Workspace, WorkspaceRecord,
|
||||
FolderCloudService, FolderCollabParams, FolderSnapshot, FullSyncCollabParams,
|
||||
};
|
||||
use flowy_folder_pub::entities::PublishPayload;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
|
@ -26,31 +25,6 @@ pub(crate) struct LocalServerFolderCloudServiceImpl {
|
|||
|
||||
#[async_trait]
|
||||
impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
||||
async fn create_workspace(&self, uid: i64, name: &str) -> Result<Workspace, FlowyError> {
|
||||
let name = name.to_string();
|
||||
Ok(Workspace::new(
|
||||
gen_workspace_id().to_string(),
|
||||
name.to_string(),
|
||||
uid,
|
||||
))
|
||||
}
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
async fn get_folder_data(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
uid: &i64,
|
||||
) -> Result<Option<FolderData>, FlowyError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn get_folder_snapshots(
|
||||
&self,
|
||||
_workspace_id: &str,
|
||||
|
@ -89,6 +63,14 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||
}
|
||||
}
|
||||
|
||||
async fn full_sync_collab_object(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
params: FullSyncCollabParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn batch_create_folder_collab_objects(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
|
@ -121,18 +103,6 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn set_publish_namespace(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
new_namespace: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn set_publish_name(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
|
@ -142,6 +112,14 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn set_publish_namespace(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
new_namespace: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn list_published_views(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
|
@ -168,15 +146,11 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn import_zip(&self, _file_path: &str) -> Result<(), FlowyError> {
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
|
||||
async fn full_sync_collab_object(
|
||||
&self,
|
||||
workspace_id: &Uuid,
|
||||
params: FullSyncCollabParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
Ok(())
|
||||
async fn import_zip(&self, _file_path: &str) -> Result<(), FlowyError> {
|
||||
Err(FlowyError::local_version_not_support())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ impl UserCloudService for LocalServerUserServiceImpl {
|
|||
let params = params.unbox_or_error::<SignUpParams>()?;
|
||||
let uid = ID_GEN.lock().await.next_id();
|
||||
let workspace_id = Uuid::new_v4().to_string();
|
||||
let user_workspace = UserWorkspace::new_local(workspace_id, "");
|
||||
let user_workspace = UserWorkspace::new_local(workspace_id, "My Workspace");
|
||||
let user_name = if params.name.is_empty() {
|
||||
DEFAULT_USER_NAME()
|
||||
} else {
|
||||
|
@ -135,9 +135,13 @@ impl UserCloudService for LocalServerUserServiceImpl {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
|
||||
async fn get_user_profile(
|
||||
&self,
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let mut conn = self.logged_user.get_sqlite_db(uid)?;
|
||||
let profile = select_user_profile(uid, &mut conn)?;
|
||||
let profile = select_user_profile(uid, workspace_id, &mut conn)?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
|
@ -150,8 +154,8 @@ impl UserCloudService for LocalServerUserServiceImpl {
|
|||
}
|
||||
|
||||
async fn get_all_workspace(&self, uid: i64) -> Result<Vec<UserWorkspace>, FlowyError> {
|
||||
let conn = self.logged_user.get_sqlite_db(uid)?;
|
||||
let workspaces = select_all_user_workspace(uid, conn)?;
|
||||
let mut conn = self.logged_user.get_sqlite_db(uid)?;
|
||||
let workspaces = select_all_user_workspace(uid, &mut conn)?;
|
||||
Ok(workspaces)
|
||||
}
|
||||
|
||||
|
@ -223,7 +227,7 @@ impl UserCloudService for LocalServerUserServiceImpl {
|
|||
Err(err) => {
|
||||
if err.is_record_not_found() {
|
||||
let mut conn = self.logged_user.get_sqlite_db(uid)?;
|
||||
let profile = select_user_profile(uid, &mut conn)?;
|
||||
let profile = select_user_profile(uid, &workspace_id.to_string(), &mut conn)?;
|
||||
let row = WorkspaceMemberTable {
|
||||
email: profile.email.to_string(),
|
||||
role: 0,
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use flowy_search_pub::cloud::SearchCloudService;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::af_cloud::define::LoggedUser;
|
||||
use crate::local_server::impls::{
|
||||
LocalChatServiceImpl, LocalServerDatabaseCloudServiceImpl, LocalServerDocumentCloudServiceImpl,
|
||||
LocalServerFolderCloudServiceImpl, LocalServerUserServiceImpl,
|
||||
};
|
||||
use crate::AppFlowyServer;
|
||||
use anyhow::Error;
|
||||
use flowy_ai::local_ai::controller::LocalAIController;
|
||||
use flowy_ai_pub::cloud::ChatCloudService;
|
||||
use flowy_database_pub::cloud::{DatabaseAIService, DatabaseCloudService};
|
||||
use flowy_document_pub::cloud::DocumentCloudService;
|
||||
use flowy_folder_pub::cloud::FolderCloudService;
|
||||
use flowy_search_pub::cloud::SearchCloudService;
|
||||
use flowy_storage_pub::cloud::StorageCloudService;
|
||||
use flowy_user_pub::cloud::UserCloudService;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub struct LocalServer {
|
||||
|
@ -40,6 +40,10 @@ impl LocalServer {
|
|||
}
|
||||
|
||||
impl AppFlowyServer for LocalServer {
|
||||
fn set_token(&self, _token: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn user_service(&self) -> Arc<dyn UserCloudService> {
|
||||
Arc::new(LocalServerUserServiceImpl {
|
||||
logged_user: self.logged_user.clone(),
|
||||
|
|
|
@ -41,9 +41,7 @@ where
|
|||
/// and functionalities in AppFlowy. The methods provided ensure efficient, asynchronous operations
|
||||
/// for managing and accessing user data, folders, collaborative objects, and documents in a cloud environment.
|
||||
pub trait AppFlowyServer: Send + Sync + 'static {
|
||||
fn set_token(&self, _token: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn set_token(&self, _token: &str) -> Result<(), Error>;
|
||||
|
||||
fn set_ai_model(&self, _ai_model: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
|
|
|
@ -168,7 +168,8 @@ pub trait UserCloudService: Send + Sync + 'static {
|
|||
|
||||
/// Get the user information using the user's token or uid
|
||||
/// return None if the user is not found
|
||||
async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError>;
|
||||
async fn get_user_profile(&self, uid: i64, workspace_id: &str)
|
||||
-> Result<UserProfile, FlowyError>;
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<UserWorkspace, FlowyError>;
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ pub struct UserProfile {
|
|||
pub token: String,
|
||||
pub icon_url: String,
|
||||
pub auth_type: AuthType,
|
||||
pub workspace_auth_type: AuthType,
|
||||
pub updated_at: i64,
|
||||
}
|
||||
|
||||
|
@ -207,6 +208,7 @@ where
|
|||
token: value.user_token().unwrap_or_default(),
|
||||
icon_url,
|
||||
auth_type: *auth_type,
|
||||
workspace_auth_type: *auth_type,
|
||||
updated_at: value.updated_at(),
|
||||
}
|
||||
}
|
||||
|
@ -317,7 +319,7 @@ pub enum UserTokenState {
|
|||
}
|
||||
|
||||
// Workspace Role
|
||||
#[derive(Clone, Debug, Serialize_repr, Deserialize_repr)]
|
||||
#[derive(Clone, Copy, Debug, Serialize_repr, Deserialize_repr, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Role {
|
||||
Owner = 0,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::cloud::UserUpdate;
|
||||
use crate::entities::{AuthType, UpdateUserProfileParams, UserProfile};
|
||||
use crate::sql::select_user_workspace;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_sqlite::schema::user_table;
|
||||
use flowy_sqlite::{prelude::*, DBConnection, ExpressionMethods, RunQueryDsl};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
/// The order of the fields in the struct must be the same as the order of the fields in the table.
|
||||
/// Check out the [schema.rs] for table schema.
|
||||
|
@ -35,20 +37,6 @@ impl From<(UserProfile, AuthType)> for UserTable {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<UserTable> for UserProfile {
|
||||
fn from(table: UserTable) -> Self {
|
||||
UserProfile {
|
||||
uid: table.id.parse::<i64>().unwrap_or(0),
|
||||
email: table.email,
|
||||
name: table.name,
|
||||
token: table.token,
|
||||
icon_url: table.icon_url,
|
||||
auth_type: AuthType::from(table.auth_type),
|
||||
updated_at: table.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Identifiable, Default, Debug)]
|
||||
#[diesel(table_name = user_table)]
|
||||
pub struct UserTableChangeset {
|
||||
|
@ -96,6 +84,7 @@ pub fn update_user_profile(
|
|||
conn: &mut SqliteConnection,
|
||||
changeset: UserTableChangeset,
|
||||
) -> Result<(), FlowyError> {
|
||||
trace!("update user profile: {:?}", changeset);
|
||||
let user_id = changeset.id.clone();
|
||||
update(user_table::dsl::user_table.filter(user_table::id.eq(&user_id)))
|
||||
.set(changeset)
|
||||
|
@ -103,11 +92,8 @@ pub fn update_user_profile(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_user_profile(
|
||||
uid: i64,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let user: UserProfile = user_table::dsl::user_table
|
||||
fn select_user_table_row(uid: i64, conn: &mut SqliteConnection) -> Result<UserTable, FlowyError> {
|
||||
let row = user_table::dsl::user_table
|
||||
.filter(user_table::id.eq(&uid.to_string()))
|
||||
.first::<UserTable>(conn)
|
||||
.map_err(|err| {
|
||||
|
@ -115,12 +101,55 @@ pub fn select_user_profile(
|
|||
"Can't find the user profile for user id: {}, error: {:?}",
|
||||
uid, err
|
||||
))
|
||||
})?
|
||||
.into();
|
||||
})?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub fn select_user_profile(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let workspace = select_user_workspace(workspace_id, conn)?;
|
||||
let workspace_auth_type = AuthType::from(workspace.workspace_type);
|
||||
let row = select_user_table_row(uid, conn)?;
|
||||
|
||||
let user = UserProfile {
|
||||
uid: row.id.parse::<i64>().unwrap_or(0),
|
||||
email: row.email,
|
||||
name: row.name,
|
||||
token: row.token,
|
||||
icon_url: row.icon_url,
|
||||
auth_type: AuthType::from(row.auth_type),
|
||||
workspace_auth_type,
|
||||
updated_at: row.updated_at,
|
||||
};
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub fn select_workspace_auth_type(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<AuthType, FlowyError> {
|
||||
match select_user_workspace(workspace_id, conn) {
|
||||
Ok(workspace) => Ok(AuthType::from(workspace.workspace_type)),
|
||||
Err(err) => {
|
||||
if err.is_record_not_found() {
|
||||
let row = select_user_table_row(uid, conn)?;
|
||||
warn!(
|
||||
"user user auth type:{} as workspace auth type",
|
||||
row.auth_type
|
||||
);
|
||||
Ok(AuthType::from(row.auth_type))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upsert_user(user: UserTable, mut conn: DBConnection) -> FlowyResult<()> {
|
||||
conn.immediate_transaction(|conn| {
|
||||
// delete old user if exists
|
||||
|
|
|
@ -2,8 +2,10 @@ use crate::entities::{AuthType, UserWorkspace};
|
|||
use chrono::{TimeZone, Utc};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_sqlite::schema::user_workspace_table;
|
||||
use flowy_sqlite::schema::user_workspace_table::dsl;
|
||||
use flowy_sqlite::DBConnection;
|
||||
use flowy_sqlite::{prelude::*, ExpressionMethods, RunQueryDsl, SqliteConnection};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use tracing::{info, warn};
|
||||
|
||||
#[derive(Clone, Default, Queryable, Identifiable, Insertable)]
|
||||
|
@ -26,11 +28,43 @@ pub struct UserWorkspaceChangeset {
|
|||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
pub icon: Option<String>,
|
||||
pub role: Option<i32>,
|
||||
pub member_count: Option<i64>,
|
||||
}
|
||||
|
||||
impl UserWorkspaceChangeset {
|
||||
pub fn has_changes(&self) -> bool {
|
||||
self.name.is_some() || self.icon.is_some() || self.role.is_some() || self.member_count.is_some()
|
||||
}
|
||||
pub fn from_version(old: &UserWorkspace, new: &UserWorkspace) -> Self {
|
||||
let mut changeset = Self {
|
||||
id: new.id.clone(),
|
||||
name: None,
|
||||
icon: None,
|
||||
role: None,
|
||||
member_count: None,
|
||||
};
|
||||
|
||||
if old.name != new.name {
|
||||
changeset.name = Some(new.name.clone());
|
||||
}
|
||||
if old.icon != new.icon {
|
||||
changeset.icon = Some(new.icon.clone());
|
||||
}
|
||||
if old.role != new.role {
|
||||
changeset.role = new.role.map(|v| v as i32);
|
||||
}
|
||||
if old.member_count != new.member_count {
|
||||
changeset.member_count = Some(new.member_count);
|
||||
}
|
||||
|
||||
changeset
|
||||
}
|
||||
}
|
||||
|
||||
impl UserWorkspaceTable {
|
||||
pub fn from_workspace(
|
||||
uid: i64,
|
||||
uid_val: i64,
|
||||
workspace: &UserWorkspace,
|
||||
auth_type: AuthType,
|
||||
) -> Result<Self, FlowyError> {
|
||||
|
@ -44,12 +78,12 @@ impl UserWorkspaceTable {
|
|||
Ok(Self {
|
||||
id: workspace.id.clone(),
|
||||
name: workspace.name.clone(),
|
||||
uid,
|
||||
uid: uid_val,
|
||||
created_at: workspace.created_at.timestamp(),
|
||||
database_storage_id: workspace.workspace_database_id.clone(),
|
||||
icon: workspace.icon.clone(),
|
||||
member_count: workspace.member_count,
|
||||
role: workspace.role.clone().map(|v| v as i32),
|
||||
role: workspace.role.map(|v| v as i32),
|
||||
workspace_type: auth_type as i32,
|
||||
})
|
||||
}
|
||||
|
@ -59,19 +93,20 @@ pub fn select_user_workspace(
|
|||
workspace_id: &str,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> FlowyResult<UserWorkspaceTable> {
|
||||
let row = user_workspace_table::dsl::user_workspace_table
|
||||
let row = dsl::user_workspace_table
|
||||
.filter(user_workspace_table::id.eq(workspace_id))
|
||||
.first::<UserWorkspaceTable>(conn)?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub fn select_all_user_workspace(
|
||||
user_id: i64,
|
||||
mut conn: DBConnection,
|
||||
uid: i64,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<Vec<UserWorkspace>, FlowyError> {
|
||||
let rows = user_workspace_table::dsl::user_workspace_table
|
||||
.filter(user_workspace_table::uid.eq(user_id))
|
||||
.load::<UserWorkspaceTable>(&mut *conn)?;
|
||||
.filter(user_workspace_table::uid.eq(uid))
|
||||
.order(user_workspace_table::created_at.desc())
|
||||
.load::<UserWorkspaceTable>(conn)?;
|
||||
Ok(rows.into_iter().map(UserWorkspace::from).collect())
|
||||
}
|
||||
|
||||
|
@ -87,32 +122,6 @@ pub fn update_user_workspace(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn upsert_user_workspace(
|
||||
uid: i64,
|
||||
auth_type: AuthType,
|
||||
user_workspace: UserWorkspace,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<(), FlowyError> {
|
||||
let row = UserWorkspaceTable::from_workspace(uid, &user_workspace, auth_type)?;
|
||||
diesel::insert_into(user_workspace_table::table)
|
||||
.values(row.clone())
|
||||
.on_conflict(user_workspace_table::id)
|
||||
.do_update()
|
||||
.set((
|
||||
user_workspace_table::name.eq(row.name),
|
||||
user_workspace_table::uid.eq(row.uid),
|
||||
user_workspace_table::created_at.eq(row.created_at),
|
||||
user_workspace_table::database_storage_id.eq(row.database_storage_id),
|
||||
user_workspace_table::icon.eq(row.icon),
|
||||
user_workspace_table::member_count.eq(row.member_count),
|
||||
user_workspace_table::role.eq(row.role),
|
||||
user_workspace_table::workspace_type.eq(row.workspace_type),
|
||||
))
|
||||
.execute(conn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_user_workspace(mut conn: DBConnection, workspace_id: &str) -> FlowyResult<()> {
|
||||
let n = conn.immediate_transaction(|conn| {
|
||||
let rows_affected: usize =
|
||||
|
@ -151,7 +160,7 @@ pub fn delete_user_all_workspace(
|
|||
conn: &mut SqliteConnection,
|
||||
) -> FlowyResult<()> {
|
||||
let n = diesel::delete(
|
||||
user_workspace_table::dsl::user_workspace_table
|
||||
dsl::user_workspace_table
|
||||
.filter(user_workspace_table::uid.eq(uid))
|
||||
.filter(user_workspace_table::workspace_type.eq(auth_type as i32)),
|
||||
)
|
||||
|
@ -163,24 +172,93 @@ pub fn delete_user_all_workspace(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all user workspaces for the given user and auth type, then insert the provided user workspaces.
|
||||
pub fn delete_all_then_insert_user_workspaces(
|
||||
uid: i64,
|
||||
mut conn: DBConnection,
|
||||
#[derive(Debug)]
|
||||
pub enum WorkspaceChange {
|
||||
Inserted(String),
|
||||
Updated(String),
|
||||
}
|
||||
|
||||
pub fn upsert_user_workspace(
|
||||
uid_val: i64,
|
||||
auth_type: AuthType,
|
||||
user_workspace: UserWorkspace,
|
||||
conn: &mut SqliteConnection,
|
||||
) -> Result<usize, FlowyError> {
|
||||
let row = UserWorkspaceTable::from_workspace(uid_val, &user_workspace, auth_type)?;
|
||||
let n = insert_into(user_workspace_table::table)
|
||||
.values(row.clone())
|
||||
.on_conflict(user_workspace_table::id)
|
||||
.do_update()
|
||||
.set((
|
||||
user_workspace_table::name.eq(row.name),
|
||||
user_workspace_table::uid.eq(row.uid),
|
||||
user_workspace_table::created_at.eq(row.created_at),
|
||||
user_workspace_table::database_storage_id.eq(row.database_storage_id),
|
||||
user_workspace_table::icon.eq(row.icon),
|
||||
user_workspace_table::member_count.eq(row.member_count),
|
||||
user_workspace_table::role.eq(row.role),
|
||||
))
|
||||
.execute(conn)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn sync_user_workspaces_with_diff(
|
||||
uid_val: i64,
|
||||
auth_type: AuthType,
|
||||
user_workspaces: &[UserWorkspace],
|
||||
) -> FlowyResult<()> {
|
||||
conn.immediate_transaction(|conn| {
|
||||
delete_user_all_workspace(uid, auth_type, conn)?;
|
||||
info!(
|
||||
"Insert {} workspaces for user {} and auth type {:?}",
|
||||
user_workspaces.len(),
|
||||
uid,
|
||||
auth_type
|
||||
);
|
||||
for user_workspace in user_workspaces {
|
||||
upsert_user_workspace(uid, auth_type, user_workspace.clone(), conn)?;
|
||||
conn: &mut SqliteConnection,
|
||||
) -> FlowyResult<Vec<WorkspaceChange>> {
|
||||
let diff = conn.immediate_transaction(|conn| {
|
||||
// 1) Load all existing workspaces into a map
|
||||
let existing_rows: Vec<UserWorkspaceTable> = dsl::user_workspace_table
|
||||
.filter(user_workspace_table::uid.eq(uid_val))
|
||||
.filter(user_workspace_table::workspace_type.eq(auth_type as i32))
|
||||
.load(conn)?;
|
||||
let mut existing_map: HashMap<String, UserWorkspaceTable> = existing_rows
|
||||
.into_iter()
|
||||
.map(|r| (r.id.clone(), r))
|
||||
.collect();
|
||||
|
||||
// 2) Build incoming ID set and delete any stale ones
|
||||
let incoming_ids: HashSet<String> = user_workspaces.iter().map(|uw| uw.id.clone()).collect();
|
||||
let to_delete: Vec<String> = existing_map
|
||||
.keys()
|
||||
.filter(|id| !incoming_ids.contains(*id))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if !to_delete.is_empty() {
|
||||
diesel::delete(dsl::user_workspace_table.filter(user_workspace_table::id.eq_any(&to_delete)))
|
||||
.execute(conn)?;
|
||||
}
|
||||
Ok::<(), FlowyError>(())
|
||||
})
|
||||
|
||||
// 3) For each incoming workspace, either INSERT or UPDATE if changed
|
||||
let mut diffs = Vec::new();
|
||||
for uw in user_workspaces {
|
||||
match existing_map.remove(&uw.id) {
|
||||
None => {
|
||||
// new workspace → insert
|
||||
let new_row = UserWorkspaceTable::from_workspace(uid_val, uw, auth_type)?;
|
||||
diesel::insert_into(user_workspace_table::table)
|
||||
.values(new_row)
|
||||
.execute(conn)?;
|
||||
diffs.push(WorkspaceChange::Inserted(uw.id.clone()));
|
||||
},
|
||||
|
||||
Some(old) => {
|
||||
let changes = UserWorkspaceChangeset::from_version(&UserWorkspace::from(old), uw);
|
||||
if changes.has_changes() {
|
||||
diesel::update(dsl::user_workspace_table.find(&uw.id))
|
||||
.set(&changes)
|
||||
.execute(conn)?;
|
||||
diffs.push(WorkspaceChange::Updated(uw.id.clone()));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, FlowyError>(diffs)
|
||||
})?;
|
||||
Ok(diff)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,10 @@ pub struct UserProfilePB {
|
|||
pub icon_url: String,
|
||||
|
||||
#[pb(index = 6)]
|
||||
pub auth_type: AuthTypePB,
|
||||
pub user_auth_type: AuthTypePB,
|
||||
|
||||
#[pb(index = 7)]
|
||||
pub workspace_auth_type: AuthTypePB,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
||||
|
@ -62,7 +65,8 @@ impl From<UserProfile> for UserProfilePB {
|
|||
name: user_profile.name,
|
||||
token: user_profile.token,
|
||||
icon_url: user_profile.icon_url,
|
||||
auth_type: user_profile.auth_type.into(),
|
||||
user_auth_type: user_profile.auth_type.into(),
|
||||
workspace_auth_type: user_profile.workspace_auth_type.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,16 +91,22 @@ pub async fn get_user_profile_handler(
|
|||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<UserProfilePB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let uid = manager.get_session()?.user_id;
|
||||
let mut user_profile = manager.get_user_profile_from_disk(uid).await?;
|
||||
let session = manager.get_session()?;
|
||||
|
||||
let mut user_profile = manager
|
||||
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
|
||||
.await?;
|
||||
|
||||
let weak_manager = Arc::downgrade(&manager);
|
||||
let cloned_user_profile = user_profile.clone();
|
||||
let workspace_id = session.user_workspace.id.clone();
|
||||
|
||||
// Refresh the user profile in the background
|
||||
tokio::spawn(async move {
|
||||
if let Some(manager) = weak_manager.upgrade() {
|
||||
let _ = manager.refresh_user_profile(&cloned_user_profile).await;
|
||||
let _ = manager
|
||||
.refresh_user_profile(&cloned_user_profile, &workspace_id)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -425,7 +431,10 @@ pub async fn get_all_workspace_handler(
|
|||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<RepeatedUserWorkspacePB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let profile = manager.get_user_profile().await?;
|
||||
let session = manager.get_session()?;
|
||||
let profile = manager
|
||||
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
|
||||
.await?;
|
||||
let user_workspaces = manager
|
||||
.get_all_user_workspaces(profile.uid, profile.auth_type)
|
||||
.await?;
|
||||
|
@ -645,6 +654,8 @@ pub async fn rename_workspace_handler(
|
|||
id: params.workspace_id,
|
||||
name: Some(params.new_name),
|
||||
icon: None,
|
||||
role: None,
|
||||
member_count: None,
|
||||
};
|
||||
manager.patch_workspace(&workspace_id, changeset).await?;
|
||||
Ok(())
|
||||
|
@ -662,6 +673,8 @@ pub async fn change_workspace_icon_handler(
|
|||
id: workspace_id.to_string(),
|
||||
name: None,
|
||||
icon: Some(params.new_icon),
|
||||
role: None,
|
||||
member_count: None,
|
||||
};
|
||||
manager.patch_workspace(&workspace_id, changeset).await?;
|
||||
Ok(())
|
||||
|
|
|
@ -36,7 +36,7 @@ use std::collections::{HashMap, HashSet};
|
|||
|
||||
use collab_document::blocks::TextDelta;
|
||||
use collab_document::document::Document;
|
||||
use flowy_user_pub::sql::select_user_profile;
|
||||
use flowy_user_pub::sql::{select_user_profile, select_workspace_auth_type};
|
||||
use semver::Version;
|
||||
use serde_json::json;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -101,14 +101,25 @@ pub(crate) fn prepare_import(
|
|||
CollabKVDB::open(collab_db_path)
|
||||
.map_err(|err| anyhow!("[AppflowyData]: open import collab db failed: {:?}", err))?,
|
||||
);
|
||||
let imported_user = select_user_profile(
|
||||
|
||||
let mut conn = imported_sqlite_db.get_connection()?;
|
||||
let imported_workspace_auth_type = select_user_profile(
|
||||
imported_session.user_id,
|
||||
&mut *imported_sqlite_db.get_connection()?,
|
||||
)?;
|
||||
&imported_session.user_workspace.id,
|
||||
&mut conn,
|
||||
)
|
||||
.map(|v| v.workspace_auth_type)
|
||||
.or_else(|_| {
|
||||
select_workspace_auth_type(
|
||||
imported_session.user_id,
|
||||
&imported_session.user_workspace.id,
|
||||
&mut conn,
|
||||
)
|
||||
})?;
|
||||
|
||||
run_collab_data_migration(
|
||||
&imported_session,
|
||||
&imported_user,
|
||||
&imported_workspace_auth_type,
|
||||
imported_collab_db.clone(),
|
||||
imported_sqlite_db.get_pool(),
|
||||
other_store_preferences.clone(),
|
||||
|
|
|
@ -125,9 +125,10 @@ impl UserDB {
|
|||
&self,
|
||||
pool: &Arc<ConnectionPool>,
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let mut conn = pool.get()?;
|
||||
let profile = select_user_profile(uid, &mut conn)?;
|
||||
let profile = select_user_profile(uid, workspace_id, &mut conn)?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,9 @@ impl UserManager {
|
|||
*self.collab_interact.write().await = Arc::new(collab_interact);
|
||||
|
||||
if let Ok(session) = self.get_session() {
|
||||
let user = self.get_user_profile_from_disk(session.user_id).await?;
|
||||
let user = self
|
||||
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
|
||||
.await?;
|
||||
self.cloud_service.set_server_auth_type(&user.auth_type);
|
||||
|
||||
// Get the current authenticator from the environment variable
|
||||
|
@ -175,6 +177,7 @@ impl UserManager {
|
|||
event!(tracing::Level::DEBUG, "Listen token state change");
|
||||
let user_uid = user.uid;
|
||||
let local_token = user.token.clone();
|
||||
let workspace_id = session.user_workspace.id.clone();
|
||||
tokio::spawn(async move {
|
||||
while let Some(token_state) = token_state_rx.next().await {
|
||||
debug!("Token state changed: {:?}", token_state);
|
||||
|
@ -184,7 +187,7 @@ impl UserManager {
|
|||
if new_token != local_token {
|
||||
if let Some(conn) = weak_pool.upgrade().and_then(|pool| pool.get().ok()) {
|
||||
// Save the new token
|
||||
if let Err(err) = save_user_token(user_uid, conn, new_token) {
|
||||
if let Err(err) = save_user_token(user_uid, &workspace_id, conn, new_token) {
|
||||
error!("Save user token failed: {}", err);
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +253,7 @@ impl UserManager {
|
|||
(Ok(collab_db), Ok(sqlite_pool)) => {
|
||||
run_collab_data_migration(
|
||||
&session,
|
||||
&user,
|
||||
&user.auth_type,
|
||||
collab_db,
|
||||
sqlite_pool,
|
||||
self.store_preferences.clone(),
|
||||
|
@ -503,6 +506,7 @@ impl UserManager {
|
|||
let session = self.get_session()?;
|
||||
upsert_user_profile_change(
|
||||
session.user_id,
|
||||
&session.user_workspace.id,
|
||||
self.db_connection(session.user_id)?,
|
||||
changeset,
|
||||
)?;
|
||||
|
@ -535,20 +539,22 @@ impl UserManager {
|
|||
.backup(session.user_id, &session.user_workspace.id);
|
||||
}
|
||||
|
||||
pub async fn get_user_profile(&self) -> FlowyResult<UserProfile> {
|
||||
let uid = self.get_session()?.user_id;
|
||||
let profile = self.get_user_profile_from_disk(uid).await?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
/// Fetches the user profile for the given user ID.
|
||||
pub async fn get_user_profile_from_disk(&self, uid: i64) -> Result<UserProfile, FlowyError> {
|
||||
pub async fn get_user_profile_from_disk(
|
||||
&self,
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
let mut conn = self.db_connection(uid)?;
|
||||
select_user_profile(uid, &mut conn)
|
||||
select_user_profile(uid, workspace_id, &mut conn)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info", skip_all, err)]
|
||||
pub async fn refresh_user_profile(&self, old_user_profile: &UserProfile) -> FlowyResult<()> {
|
||||
pub async fn refresh_user_profile(
|
||||
&self,
|
||||
old_user_profile: &UserProfile,
|
||||
workspace_id: &str,
|
||||
) -> FlowyResult<()> {
|
||||
// If the user is a local user, no need to refresh the user profile
|
||||
if old_user_profile.auth_type.is_local() {
|
||||
return Ok(());
|
||||
|
@ -565,7 +571,7 @@ impl UserManager {
|
|||
let result: Result<UserProfile, FlowyError> = self
|
||||
.cloud_service
|
||||
.get_user_service()?
|
||||
.get_user_profile(uid)
|
||||
.get_user_profile(uid, workspace_id)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
|
@ -576,6 +582,7 @@ impl UserManager {
|
|||
let changeset = UserTableChangeset::from_user_profile(new_user_profile);
|
||||
let _ = upsert_user_profile_change(
|
||||
uid,
|
||||
workspace_id,
|
||||
self.authenticate_user.database.get_connection(uid)?,
|
||||
changeset,
|
||||
);
|
||||
|
@ -729,12 +736,8 @@ impl UserManager {
|
|||
self.set_anon_user(session);
|
||||
}
|
||||
|
||||
delete_all_then_insert_user_workspaces(
|
||||
uid,
|
||||
self.db_connection(uid)?,
|
||||
auth_type,
|
||||
response.user_workspaces(),
|
||||
)?;
|
||||
let mut conn = self.db_connection(uid)?;
|
||||
sync_user_workspaces_with_diff(uid, auth_type, response.user_workspaces(), &mut conn)?;
|
||||
info!(
|
||||
"Save new user profile to disk, authenticator: {:?}",
|
||||
auth_type
|
||||
|
@ -756,6 +759,7 @@ impl UserManager {
|
|||
// Save the user profile change
|
||||
upsert_user_profile_change(
|
||||
user_update.uid,
|
||||
&session.user_workspace.id,
|
||||
self.db_connection(user_update.uid)?,
|
||||
UserTableChangeset::from(user_update),
|
||||
)?;
|
||||
|
@ -805,6 +809,7 @@ fn current_authenticator() -> AuthType {
|
|||
|
||||
pub fn upsert_user_profile_change(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
mut conn: DBConnection,
|
||||
changeset: UserTableChangeset,
|
||||
) -> FlowyResult<()> {
|
||||
|
@ -814,10 +819,7 @@ pub fn upsert_user_profile_change(
|
|||
changeset
|
||||
);
|
||||
update_user_profile(&mut conn, changeset)?;
|
||||
let user: UserProfile = user_table::dsl::user_table
|
||||
.filter(user_table::id.eq(&uid.to_string()))
|
||||
.first::<UserTable>(&mut *conn)?
|
||||
.into();
|
||||
let user = select_user_profile(uid, workspace_id, &mut conn)?;
|
||||
send_notification(&uid.to_string(), UserNotification::DidUpdateUserProfile)
|
||||
.payload(UserProfilePB::from(user))
|
||||
.send();
|
||||
|
@ -825,10 +827,15 @@ pub fn upsert_user_profile_change(
|
|||
}
|
||||
|
||||
#[instrument(level = "info", skip_all, err)]
|
||||
fn save_user_token(uid: i64, conn: DBConnection, token: String) -> FlowyResult<()> {
|
||||
fn save_user_token(
|
||||
uid: i64,
|
||||
workspace_id: &str,
|
||||
conn: DBConnection,
|
||||
token: String,
|
||||
) -> FlowyResult<()> {
|
||||
let params = UpdateUserProfileParams::new(uid).with_token(token);
|
||||
let changeset = UserTableChangeset::new(params);
|
||||
upsert_user_profile_change(uid, conn, changeset)
|
||||
upsert_user_profile_change(uid, workspace_id, conn, changeset)
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all, err)]
|
||||
|
@ -862,7 +869,7 @@ fn mark_all_migrations_as_applied(sqlite_pool: &Arc<ConnectionPool>) {
|
|||
|
||||
pub(crate) fn run_collab_data_migration(
|
||||
session: &Session,
|
||||
user: &UserProfile,
|
||||
auth_type: &AuthType,
|
||||
collab_db: Arc<CollabKVDB>,
|
||||
sqlite_pool: Arc<ConnectionPool>,
|
||||
kv: Arc<KVStorePreferences>,
|
||||
|
@ -871,7 +878,7 @@ pub(crate) fn run_collab_data_migration(
|
|||
let migrations = collab_migration_list();
|
||||
match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool, kv).run(
|
||||
migrations,
|
||||
&user.auth_type,
|
||||
auth_type,
|
||||
app_version,
|
||||
) {
|
||||
Ok(applied_migrations) => {
|
||||
|
@ -886,6 +893,7 @@ pub(crate) fn run_collab_data_migration(
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all, err)]
|
||||
pub async fn sign_out(
|
||||
cloud_services: &Arc<dyn UserCloudServiceProvider>,
|
||||
session: &Session,
|
||||
|
|
|
@ -20,7 +20,7 @@ impl UserManager {
|
|||
|
||||
let session = self.get_session().ok()?;
|
||||
let user_profile = self
|
||||
.get_user_profile_from_disk(session.user_id)
|
||||
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
|
@ -48,7 +48,7 @@ impl UserManager {
|
|||
"Anon user not found",
|
||||
))?;
|
||||
let profile = self
|
||||
.get_user_profile_from_disk(anon_session.user_id)
|
||||
.get_user_profile_from_disk(anon_session.user_id, &anon_session.user_workspace.id)
|
||||
.await?;
|
||||
Ok(UserProfilePB::from(profile))
|
||||
}
|
||||
|
|
|
@ -374,9 +374,11 @@ impl UserManager {
|
|||
.unwrap_or(false);
|
||||
|
||||
if !is_loading {
|
||||
let user_profile = self.get_user_profile_from_disk(session.user_id).await?;
|
||||
let user_profile = self
|
||||
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
|
||||
.await?;
|
||||
self
|
||||
.initial_user_awareness(&session, &user_profile.auth_type)
|
||||
.initial_user_awareness(&session, &user_profile.workspace_auth_type)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ impl UserManager {
|
|||
collab_data: ImportedCollabData,
|
||||
) -> Result<(), FlowyError> {
|
||||
let user = self
|
||||
.get_user_profile_from_disk(current_session.user_id)
|
||||
.get_user_profile_from_disk(current_session.user_id, ¤t_session.user_workspace.id)
|
||||
.await?;
|
||||
let user_collab_db = self
|
||||
.get_collab_db(current_session.user_id)?
|
||||
|
@ -115,7 +115,7 @@ impl UserManager {
|
|||
user_id,
|
||||
weak_user_collab_db,
|
||||
¤t_session.user_workspace.workspace_id()?,
|
||||
&user.auth_type,
|
||||
&user.workspace_auth_type,
|
||||
collab_data,
|
||||
weak_user_cloud_service,
|
||||
)
|
||||
|
@ -154,11 +154,19 @@ impl UserManager {
|
|||
#[instrument(skip(self), err)]
|
||||
pub async fn open_workspace(&self, workspace_id: &Uuid, auth_type: AuthType) -> FlowyResult<()> {
|
||||
info!("open workspace: {}, auth_type:{}", workspace_id, auth_type);
|
||||
let workspace_id_str = workspace_id.to_string();
|
||||
self.cloud_service.set_server_auth_type(&auth_type);
|
||||
|
||||
let uid = self.user_id()?;
|
||||
let profile = self
|
||||
.get_user_profile_from_disk(uid, &workspace_id_str)
|
||||
.await?;
|
||||
if let Err(err) = self.cloud_service.set_token(&profile.token) {
|
||||
error!("Set token failed: {}", err);
|
||||
}
|
||||
|
||||
let mut conn = self.db_connection(self.user_id()?)?;
|
||||
let user_workspace = match select_user_workspace(&workspace_id.to_string(), &mut conn) {
|
||||
let user_workspace = match select_user_workspace(&workspace_id_str, &mut conn) {
|
||||
Err(err) => {
|
||||
if err.is_record_not_found() {
|
||||
sync_workspace(
|
||||
|
@ -190,19 +198,18 @@ impl UserManager {
|
|||
.set_user_workspace(user_workspace.clone())?;
|
||||
|
||||
let uid = self.user_id()?;
|
||||
let user_profile = self.get_user_profile_from_disk(uid).await?;
|
||||
if let Err(err) = self
|
||||
.user_status_callback
|
||||
.read()
|
||||
.await
|
||||
.on_workspace_opened(uid, workspace_id, &user_workspace, &user_profile.auth_type)
|
||||
.on_workspace_opened(uid, workspace_id, &user_workspace, &auth_type)
|
||||
.await
|
||||
{
|
||||
error!("Open workspace failed: {:?}", err);
|
||||
}
|
||||
|
||||
if let Err(err) = self
|
||||
.initial_user_awareness(self.get_session()?.as_ref(), &user_profile.auth_type)
|
||||
.initial_user_awareness(self.get_session()?.as_ref(), &auth_type)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
|
@ -220,19 +227,13 @@ impl UserManager {
|
|||
workspace_name: &str,
|
||||
auth_type: AuthType,
|
||||
) -> FlowyResult<UserWorkspace> {
|
||||
let new_workspace = match auth_type {
|
||||
AuthType::Local => {
|
||||
let workspace_id = Uuid::new_v4();
|
||||
UserWorkspace::new_local(workspace_id.to_string(), workspace_name)
|
||||
},
|
||||
AuthType::AppFlowyCloud => {
|
||||
self
|
||||
.cloud_service
|
||||
.get_user_service()?
|
||||
.create_workspace(workspace_name)
|
||||
.await?
|
||||
},
|
||||
};
|
||||
self.cloud_service.set_server_auth_type(&auth_type);
|
||||
|
||||
let new_workspace = self
|
||||
.cloud_service
|
||||
.get_user_service()?
|
||||
.create_workspace(workspace_name)
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
"create workspace: {}, name:{}, auth_type: {}",
|
||||
|
@ -410,27 +411,59 @@ impl UserManager {
|
|||
uid: i64,
|
||||
auth_type: AuthType,
|
||||
) -> FlowyResult<Vec<UserWorkspace>> {
|
||||
let conn = self.db_connection(uid)?;
|
||||
let workspaces = select_all_user_workspace(uid, conn)?;
|
||||
// 1) Load & return the local copy immediately
|
||||
let mut conn = self.db_connection(uid)?;
|
||||
let local_workspaces = select_all_user_workspace(uid, &mut conn)?;
|
||||
|
||||
if let Ok(service) = self.cloud_service.get_user_service() {
|
||||
if let Ok(pool) = self.db_pool(uid) {
|
||||
tokio::spawn(async move {
|
||||
if let Ok(new_user_workspaces) = service.get_all_workspace(uid).await {
|
||||
if let Ok(conn) = pool.get() {
|
||||
let _ =
|
||||
delete_all_then_insert_user_workspaces(uid, conn, auth_type, &new_user_workspaces);
|
||||
let repeated_workspace_pbs =
|
||||
RepeatedUserWorkspacePB::from((auth_type, new_user_workspaces));
|
||||
// 2) If both cloud service and pool are available, fire off a background sync
|
||||
if let (Ok(service), Ok(pool)) = (self.cloud_service.get_user_service(), self.db_pool(uid)) {
|
||||
// capture only what we need
|
||||
let auth_copy = auth_type;
|
||||
|
||||
tokio::spawn(async move {
|
||||
// fetch remote list
|
||||
let new_ws = match service.get_all_workspace(uid).await {
|
||||
Ok(ws) => ws,
|
||||
Err(e) => {
|
||||
trace!("failed to fetch remote workspaces for {}: {:?}", uid, e);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// get a pooled DB connection
|
||||
let mut conn = match pool.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
trace!("failed to get DB connection for {}: {:?}", uid, e);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// sync + diff
|
||||
match sync_user_workspaces_with_diff(uid, auth_copy, &new_ws, &mut conn) {
|
||||
Ok(changes) if !changes.is_empty() => {
|
||||
info!(
|
||||
"synced {} workspaces for user {} and auth type {:?}. changes: {:?}",
|
||||
changes.len(),
|
||||
uid,
|
||||
auth_copy,
|
||||
changes
|
||||
);
|
||||
// only send notification if there were real changes
|
||||
if let Ok(updated_list) = select_all_user_workspace(uid, &mut conn) {
|
||||
let repeated_pb = RepeatedUserWorkspacePB::from((auth_copy, updated_list));
|
||||
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)
|
||||
.payload(repeated_workspace_pbs)
|
||||
.payload(repeated_pb)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
Ok(_) => trace!("no workspaces updated for {}", uid),
|
||||
Err(e) => trace!("sync error for {}: {:?}", uid, e),
|
||||
}
|
||||
});
|
||||
}
|
||||
Ok(workspaces)
|
||||
|
||||
Ok(local_workspaces)
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip(self), err)]
|
||||
|
@ -660,7 +693,7 @@ impl UserManager {
|
|||
|
||||
let record = WorkspaceMemberTable {
|
||||
email: member.email.clone(),
|
||||
role: member.role.clone().into(),
|
||||
role: member.role.into(),
|
||||
name: member.name.clone(),
|
||||
avatar_url: member.avatar_url.clone(),
|
||||
uid,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue