diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index 0e508e8ca0..9c90ae776c 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -202,4 +202,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac -COCOAPODS: 1.12.1 +COCOAPODS: 1.11.3 diff --git a/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj index 1d08d2be93..17a40b25c3 100644 --- a/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj +++ b/frontend/appflowy_flutter/ios/Runner.xcodeproj/project.pbxproj @@ -346,9 +346,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = VHB67HRSZG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AppFlowy; @@ -359,6 +361,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -482,9 +485,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = VHB67HRSZG; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AppFlowy; @@ -495,6 +500,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -513,6 +519,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = VHB67HRSZG; @@ -526,6 +534,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_STYLE = "non-global"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 001ed8a3ed..d17aa75d52 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "reqwest", @@ -786,7 +786,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -1469,7 +1469,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -2828,7 +2828,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "futures-util", @@ -2844,7 +2844,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -3266,7 +3266,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "reqwest", @@ -5026,7 +5026,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "bincode", @@ -5779,7 +5779,7 @@ dependencies = [ [[package]] name = "shared_entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -7669,7 +7669,7 @@ dependencies = [ [[package]] name = "workspace-template" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "async-trait", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 4095983c23..ec14fb4545 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"] # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f13a03beada8b7c3991f98016ccc0b708b22090c" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee31a680b5789b8c7c5397e6eadd4dd7739526f9" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 7152761c7c..5754b25046 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "reqwest", @@ -667,7 +667,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -1147,7 +1147,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.8.0", "smallvec", ] @@ -1275,7 +1275,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -2469,7 +2469,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "futures-util", @@ -2485,7 +2485,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -2846,7 +2846,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "reqwest", @@ -3651,7 +3651,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", + "phf_macros", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -3671,7 +3671,6 @@ 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", ] @@ -3739,19 +3738,6 @@ 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.31", -] - [[package]] name = "phf_shared" version = "0.8.0" @@ -3955,7 +3941,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" dependencies = [ "bytes", "heck 0.4.1", - "itertools 0.11.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -3976,7 +3962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.31", @@ -4315,7 +4301,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "bincode", @@ -4981,7 +4967,7 @@ dependencies = [ [[package]] name = "shared_entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "app-error", @@ -6319,7 +6305,7 @@ dependencies = [ [[package]] name = "workspace-template" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f13a03beada8b7c3991f98016ccc0b708b22090c#f13a03beada8b7c3991f98016ccc0b708b22090c" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ee31a680b5789b8c7c5397e6eadd4dd7739526f9#ee31a680b5789b8c7c5397e6eadd4dd7739526f9" dependencies = [ "anyhow", "async-trait", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 84fb50a7b2..d0b7bea8f0 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -99,7 +99,7 @@ incremental = false # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f13a03beada8b7c3991f98016ccc0b708b22090c" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ee31a680b5789b8c7c5397e6eadd4dd7739526f9" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs index 7240a1b662..47a617ef52 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs @@ -1,2 +1,3 @@ mod auth_test; mod member_test; +mod sync_anon_data_test; diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/sync_anon_data_test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/sync_anon_data_test.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/sync_anon_data_test.rs @@ -0,0 +1 @@ + diff --git a/frontend/rust-lib/flowy-user/src/anon_user_upgrade/mod.rs b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/mod.rs index f48ee7a701..b4078b6d56 100644 --- a/frontend/rust-lib/flowy-user/src/anon_user_upgrade/mod.rs +++ b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/mod.rs @@ -1,5 +1,7 @@ pub use anon_user_data::*; -pub use sync_new_user::*; +pub use sync_af_cloud_new_user::*; +pub use sync_supabase_new_user::*; mod anon_user_data; -mod sync_new_user; +mod sync_af_cloud_new_user; +mod sync_supabase_new_user; diff --git a/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_af_cloud_new_user.rs b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_af_cloud_new_user.rs new file mode 100644 index 0000000000..4ff435f41a --- /dev/null +++ b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_af_cloud_new_user.rs @@ -0,0 +1,369 @@ +use std::future::Future; +use std::ops::Deref; +use std::pin::Pin; +use std::sync::Arc; + +use anyhow::{anyhow, Error}; +use collab::core::collab::MutexCollab; +use collab::preclude::Collab; +use collab_database::database::get_database_row_ids; +use collab_database::rows::database_row_document_id_from_row_id; +use collab_database::user::{get_database_with_views, DatabaseWithViews}; +use collab_entity::{CollabObject, CollabType}; +use collab_folder::{Folder, View, ViewLayout}; +use parking_lot::Mutex; + +use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction}; +use flowy_error::FlowyResult; +use flowy_user_deps::cloud::UserCloudService; + +use crate::migrations::MigrationUser; + +#[tracing::instrument(level = "info", skip_all, err)] +pub async fn sync_af_user_data_to_cloud( + user_service: Arc, + device_id: &str, + new_user: &MigrationUser, + collab_db: &Arc, +) -> FlowyResult<()> { + let workspace_id = new_user.session.user_workspace.id.clone(); + let uid = new_user.session.user_id; + let folder = Arc::new( + sync_folder( + uid, + &workspace_id, + device_id, + collab_db, + user_service.clone(), + ) + .await?, + ); + + let database_records = sync_database_views( + uid, + &workspace_id, + device_id, + &new_user.session.user_workspace.database_views_aggregate_id, + collab_db, + user_service.clone(), + ) + .await; + + let views = folder.lock().get_current_workspace_views(); + for view in views { + let view_id = view.id.clone(); + if let Err(err) = sync_view( + uid, + folder.clone(), + database_records.clone(), + workspace_id.to_string(), + device_id.to_string(), + view, + collab_db.clone(), + user_service.clone(), + ) + .await + { + tracing::error!("🔴sync {} failed: {:?}", view_id, err); + } + } + tokio::task::yield_now().await; + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn sync_view( + uid: i64, + folder: Arc, + database_records: Vec>, + workspace_id: String, + device_id: String, + view: Arc, + collab_db: Arc, + user_service: Arc, +) -> Pin> + Send + Sync>> { + Box::pin(async move { + let collab_type = collab_type_from_view_layout(&view.layout); + let object_id = object_id_from_view(&view, &database_records)?; + tracing::debug!( + "sync view: {:?}:{} with object_id: {}", + view.layout, + view.id, + object_id + ); + + let collab_object = CollabObject::new( + uid, + object_id, + collab_type, + workspace_id.to_string(), + device_id.clone(), + ); + + match view.layout { + ViewLayout::Document => { + let encode_v1 = get_collab_encode_v1(uid, &collab_object, &collab_db)?; + tracing::info!( + "sync object: {} with update: {}", + collab_object, + encode_v1.len() + ); + user_service + .create_collab_object(&collab_object, encode_v1) + .await?; + }, + ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => { + let (database_encode_v1, row_ids) = + get_database_encode_v1(uid, &collab_object, &collab_db)?; + tracing::info!( + "sync object: {} with update: {}", + collab_object, + database_encode_v1.len() + ); + user_service + .create_collab_object(&collab_object, database_encode_v1) + .await?; + + // sync database's row + for row_id in row_ids { + tracing::debug!("sync row: {}", row_id); + let document_id = database_row_document_id_from_row_id(&row_id); + + let database_row_collab_object = CollabObject::new( + uid, + row_id, + CollabType::DatabaseRow, + workspace_id.to_string(), + device_id.clone(), + ); + let database_row_encode_v1 = + get_collab_encode_v1(uid, &database_row_collab_object, &collab_db)?; + tracing::info!( + "sync object: {} with update: {}", + database_row_collab_object, + database_row_encode_v1.len() + ); + + let _ = user_service + .create_collab_object(&database_row_collab_object, database_row_encode_v1) + .await; + + let database_row_document = CollabObject::new( + uid, + document_id, + CollabType::Document, + workspace_id.to_string(), + device_id.to_string(), + ); + // sync document in the row if exist + if let Ok(document_encode_v1) = + get_collab_encode_v1(uid, &database_row_document, &collab_db) + { + tracing::info!( + "sync database row document: {} with update: {}", + database_row_document, + document_encode_v1.len() + ); + let _ = user_service + .create_collab_object(&database_row_document, document_encode_v1) + .await; + } + } + }, + } + + tokio::task::yield_now().await; + + let child_views = folder.lock().views.get_views_belong_to(&view.id); + for child_view in child_views { + let cloned_child_view = child_view.clone(); + if let Err(err) = Box::pin(sync_view( + uid, + folder.clone(), + database_records.clone(), + workspace_id.clone(), + device_id.to_string(), + child_view, + collab_db.clone(), + user_service.clone(), + )) + .await + { + tracing::error!( + "🔴sync {:?}:{} failed: {:?}", + cloned_child_view.layout, + cloned_child_view.id, + err + ) + } + tokio::task::yield_now().await; + } + Ok(()) + }) +} + +fn get_collab_encode_v1( + uid: i64, + collab_object: &CollabObject, + collab_db: &Arc, +) -> Result, PersistenceError> { + let collab = Collab::new(uid, &collab_object.object_id, "phantom", vec![]); + let _ = collab.with_origin_transact_mut(|txn| { + collab_db + .read_txn() + .load_doc_with_txn(uid, &collab_object.object_id, txn) + })?; + Ok(collab.encode_collab_v1().encode_to_bytes()?) +} + +fn get_database_encode_v1( + uid: i64, + collab_object: &CollabObject, + collab_db: &Arc, +) -> Result<(Vec, Vec), PersistenceError> { + let collab = Collab::new(uid, &collab_object.object_id, "phantom", vec![]); + let _ = collab.with_origin_transact_mut(|txn| { + collab_db + .read_txn() + .load_doc_with_txn(uid, &collab_object.object_id, txn) + })?; + + let row_ids = get_database_row_ids(&collab).unwrap_or_default(); + Ok((collab.encode_collab_v1().encode_to_bytes()?, row_ids)) +} + +async fn sync_folder( + uid: i64, + workspace_id: &str, + device_id: &str, + collab_db: &Arc, + user_service: Arc, +) -> Result { + let (folder, encode_v1) = { + let collab = Collab::new(uid, workspace_id, "phantom", vec![]); + // Use the temporary result to short the lifetime of the TransactionMut + collab.with_origin_transact_mut(|txn| { + collab_db + .read_txn() + .load_doc_with_txn(uid, workspace_id, txn) + })?; + let data = collab.encode_collab_v1().encode_to_bytes(); + ( + MutexFolder::new(Folder::open( + uid, + Arc::new(MutexCollab::from_collab(collab)), + None, + )?), + data, + ) + }; + let encode_v1 = encode_v1?; + let collab_object = CollabObject::new( + uid, + workspace_id.to_string(), + CollabType::Folder, + workspace_id.to_string(), + device_id.to_string(), + ); + tracing::info!( + "sync object: {} with update: {}", + collab_object, + encode_v1.len() + ); + if let Err(err) = user_service + .create_collab_object(&collab_object, encode_v1) + .await + { + tracing::error!("🔴sync folder failed: {:?}", err); + } + + Ok(folder) +} + +async fn sync_database_views( + uid: i64, + workspace_id: &str, + device_id: &str, + database_views_aggregate_id: &str, + collab_db: &Arc, + user_service: Arc, +) -> Vec> { + let collab_object = CollabObject::new( + uid, + database_views_aggregate_id.to_string(), + CollabType::WorkspaceDatabase, + workspace_id.to_string(), + device_id.to_string(), + ); + + // Use the temporary result to short the lifetime of the TransactionMut + let result = { + let collab = Collab::new(uid, database_views_aggregate_id, "phantom", vec![]); + collab + .with_origin_transact_mut(|txn| { + collab_db + .read_txn() + .load_doc_with_txn(uid, database_views_aggregate_id, txn) + }) + .map(|_| { + ( + get_database_with_views(&collab), + collab.encode_collab_v1().encode_to_bytes(), + ) + }) + }; + + if let Ok((records, encode_v1)) = result { + if let Ok(encode_v1) = encode_v1 { + let _ = user_service + .create_collab_object(&collab_object, encode_v1) + .await; + } + + records.into_iter().map(Arc::new).collect() + } else { + vec![] + } +} + +struct MutexFolder(Mutex); +impl MutexFolder { + pub fn new(folder: Folder) -> Self { + Self(Mutex::new(folder)) + } +} +impl Deref for MutexFolder { + type Target = Mutex; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +unsafe impl Sync for MutexFolder {} +unsafe impl Send for MutexFolder {} + +fn collab_type_from_view_layout(view_layout: &ViewLayout) -> CollabType { + match view_layout { + ViewLayout::Document => CollabType::Document, + ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => CollabType::Database, + } +} + +fn object_id_from_view( + view: &Arc, + database_records: &[Arc], +) -> Result { + if view.layout.is_database() { + match database_records + .iter() + .find(|record| record.linked_views.contains(&view.id)) + { + None => Err(anyhow!( + "🔴sync view: {} failed: no database for this view", + view.id + )), + Some(record) => Ok(record.database_id.clone()), + } + } else { + Ok(view.id.clone()) + } +} diff --git a/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_new_user.rs b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_supabase_new_user.rs similarity index 97% rename from frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_new_user.rs rename to frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_supabase_new_user.rs index 1880693f2a..9f00729aeb 100644 --- a/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_new_user.rs +++ b/frontend/rust-lib/flowy-user/src/anon_user_upgrade/sync_supabase_new_user.rs @@ -20,7 +20,7 @@ use flowy_user_deps::cloud::UserCloudService; use crate::migrations::MigrationUser; #[tracing::instrument(level = "info", skip_all, err)] -pub async fn sync_user_data_to_cloud( +pub async fn sync_supabase_user_data_to_cloud( user_service: Arc, device_id: &str, new_user: &MigrationUser, @@ -52,7 +52,7 @@ pub async fn sync_user_data_to_cloud( let views = folder.lock().get_current_workspace_views(); for view in views { let view_id = view.id.clone(); - if let Err(err) = sync_views( + if let Err(err) = sync_view( uid, folder.clone(), database_records.clone(), @@ -72,7 +72,7 @@ pub async fn sync_user_data_to_cloud( } #[allow(clippy::too_many_arguments)] -fn sync_views( +fn sync_view( uid: i64, folder: Arc, database_records: Vec>, @@ -172,10 +172,12 @@ fn sync_views( }, } + tokio::task::yield_now().await; + let child_views = folder.lock().views.get_views_belong_to(&view.id); for child_view in child_views { let cloned_child_view = child_view.clone(); - if let Err(err) = Box::pin(sync_views( + if let Err(err) = Box::pin(sync_view( uid, folder.clone(), database_records.clone(), @@ -194,6 +196,7 @@ fn sync_views( err ) } + tokio::task::yield_now().await; } Ok(()) }) diff --git a/frontend/rust-lib/flowy-user/src/manager.rs b/frontend/rust-lib/flowy-user/src/manager.rs index 873e4b3db2..e85286af00 100644 --- a/frontend/rust-lib/flowy-user/src/manager.rs +++ b/frontend/rust-lib/flowy-user/src/manager.rs @@ -26,7 +26,9 @@ use flowy_user_deps::entities::*; use lib_dispatch::prelude::af_spawn; use lib_infra::box_any::BoxAny; -use crate::anon_user_upgrade::{migration_anon_user_on_sign_up, sync_user_data_to_cloud}; +use crate::anon_user_upgrade::{ + migration_anon_user_on_sign_up, sync_af_user_data_to_cloud, sync_supabase_user_data_to_cloud, +}; use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB}; use crate::event_map::{DefaultUserStatusCallback, UserCloudServiceProvider, UserStatusCallback}; use crate::migrations::document_empty_content::HistoricalEmptyDocumentMigration; @@ -357,30 +359,30 @@ impl UserManager { params: BoxAny, ) -> Result { // sign out the current user if there is one + let migration_user = self.get_migration_user(&authenticator).await; let _ = self.sign_out().await; self.update_authenticator(&authenticator).await; - let migration_user = self.get_migration_user(&authenticator).await; let auth_service = self.cloud_services.get_user_service()?; let response: AuthResponse = auth_service.sign_up(params).await?; - let user_profile = UserProfile::from((&response, &authenticator)); - if user_profile.encryption_type.require_encrypt_secret() { + let new_user_profile = UserProfile::from((&response, &authenticator)); + if new_user_profile.encryption_type.require_encrypt_secret() { self .resumable_sign_up .lock() .await .replace(ResumableSignUp { - user_profile: user_profile.clone(), + user_profile: new_user_profile.clone(), migration_user, response, authenticator, }); } else { self - .continue_sign_up(&user_profile, migration_user, response, &authenticator) + .continue_sign_up(&new_user_profile, migration_user, response, &authenticator) .await?; } - Ok(user_profile) + Ok(new_user_profile) } #[tracing::instrument(level = "info", skip(self))] @@ -408,7 +410,7 @@ impl UserManager { #[tracing::instrument(level = "info", skip_all, err)] async fn continue_sign_up( &self, - user_profile: &UserProfile, + new_user_profile: &UserProfile, migration_user: Option, response: AuthResponse, authenticator: &Authenticator, @@ -425,7 +427,7 @@ impl UserManager { if response.is_new_user { if let Some(old_user) = migration_user { let new_user = MigrationUser { - user_profile: user_profile.clone(), + user_profile: new_user_profile.clone(), session: new_session.clone(), }; event!( @@ -435,7 +437,7 @@ impl UserManager { new_user.user_profile.uid ); self - .migrate_anon_user_data_to_cloud(&old_user, &new_user) + .migrate_anon_user_data_to_cloud(&old_user, &new_user, authenticator) .await?; let _ = self.database.close(old_user.session.user_id); } @@ -454,7 +456,7 @@ impl UserManager { .await .did_sign_up( response.is_new_user, - user_profile, + new_user_profile, &new_session.user_workspace, &self.user_config.device_id, ) @@ -791,20 +793,38 @@ impl UserManager { &self, old_user: &MigrationUser, new_user: &MigrationUser, + authenticator: &Authenticator, ) -> Result<(), FlowyError> { let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?; let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?; migration_anon_user_on_sign_up(old_user, &old_collab_db, new_user, &new_collab_db)?; - if let Err(err) = sync_user_data_to_cloud( - self.cloud_services.get_user_service()?, - &self.user_config.device_id, - new_user, - &new_collab_db, - ) - .await - { - error!("Sync user data to cloud failed: {:?}", err); + match authenticator { + Authenticator::Supabase => { + if let Err(err) = sync_supabase_user_data_to_cloud( + self.cloud_services.get_user_service()?, + &self.user_config.device_id, + new_user, + &new_collab_db, + ) + .await + { + error!("Sync user data to cloud failed: {:?}", err); + } + }, + Authenticator::AppFlowyCloud => { + if let Err(err) = sync_af_user_data_to_cloud( + self.cloud_services.get_user_service()?, + &self.user_config.device_id, + new_user, + &new_collab_db, + ) + .await + { + error!("Sync user data to cloud failed: {:?}", err); + } + }, + _ => {}, } // Save the old user workspace setting. diff --git a/frontend/rust-lib/flowy-user/src/migrations/define.rs b/frontend/rust-lib/flowy-user/src/migrations/define.rs index dad7f94f70..a3c9d7449b 100644 --- a/frontend/rust-lib/flowy-user/src/migrations/define.rs +++ b/frontend/rust-lib/flowy-user/src/migrations/define.rs @@ -2,7 +2,7 @@ use flowy_user_deps::entities::UserProfile; use crate::services::entities::Session; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MigrationUser { pub user_profile: UserProfile, pub session: Session, diff --git a/frontend/rust-lib/flowy-user/src/services/database.rs b/frontend/rust-lib/flowy-user/src/services/database.rs index c4e1fb0ffc..68accf96de 100644 --- a/frontend/rust-lib/flowy-user/src/services/database.rs +++ b/frontend/rust-lib/flowy-user/src/services/database.rs @@ -34,6 +34,15 @@ pub struct UserDB { impl UserDB { pub fn new(paths: impl UserDBPath) -> Self { + // if let Some(mut db) = DB_MAP.try_write_for(Duration::from_millis(300)) { + // info!("clear sqlite db map"); + // db.clear(); + // } + // + // if let Some(mut collab_db) = COLLAB_DB_MAP.try_write_for(Duration::from_millis(300)) { + // info!("clear collab db map"); + // collab_db.clear(); + // } Self { paths: Box::new(paths), } diff --git a/frontend/rust-lib/flowy-user/src/services/historical_user.rs b/frontend/rust-lib/flowy-user/src/services/historical_user.rs index d3f06aa8e0..78c6fe33c0 100644 --- a/frontend/rust-lib/flowy-user/src/services/historical_user.rs +++ b/frontend/rust-lib/flowy-user/src/services/historical_user.rs @@ -1,4 +1,5 @@ use diesel::RunQueryDsl; +use tracing::instrument; use flowy_error::FlowyResult; use flowy_sqlite::schema::user_workspace_table; @@ -13,6 +14,7 @@ use crate::services::user_workspace_sql::UserWorkspaceTable; const HISTORICAL_USER: &str = "af_historical_users"; impl UserManager { + #[instrument(skip_all)] pub async fn get_migration_user(&self, auth_type: &Authenticator) -> Option { // Only migrate the data if the user is login in as a guest and sign up as a new user if the current // auth type is not [AuthType::Local].