diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index d4ff85a2ddc69..3a1f0310a2b6d 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -1,125 +1,125 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - // This task only builds the Dart code of AppFlowy. - // It supports both the desktop and mobile version. - "name": "AF: Build Dart Only", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "env": { - "RUST_LOG": "debug", - }, - // uncomment the following line to testing performance. - // "flutterMode": "profile", - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - // This task builds the Rust and Dart code of AppFlowy. - "name": "AF-desktop: Build All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Build Appflowy Core", - "env": { - "RUST_LOG": "trace", - "RUST_BACKTRACE": "1" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - // This task builds will: - // - call the clean task, - // - rebuild all the generated Files (including freeze and language files) - // - rebuild the the Rust and Dart code of AppFlowy. - "name": "AF-desktop: Clean + Rebuild All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Clean + Rebuild All", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-iOS: Build All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Build Appflowy Core For iOS", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-iOS: Clean + Rebuild All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Clean + Rebuild All (iOS)", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-iOS-Simulator: Build All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Build Appflowy Core For iOS Simulator", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-iOS-Simulator: Clean + Rebuild All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Clean + Rebuild All (iOS Simulator)", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-Android: Build All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Build Appflowy Core For Android", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-Android: Clean + Rebuild All", - "request": "launch", - "program": "./lib/main.dart", - "type": "dart", - "preLaunchTask": "AF: Clean + Rebuild All (Android)", - "env": { - "RUST_LOG": "trace" - }, - "cwd": "${workspaceRoot}/appflowy_flutter" - }, - { - "name": "AF-desktop: Debug Rust", - "type": "lldb", - "request": "attach", - "pid": "${command:pickMyProcess}" - // To launch the application directly, use the following configuration: - // "request": "launch", - // "program": "[YOUR_APPLICATION_PATH]", - }, - ] -} + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + // This task only builds the Dart code of AppFlowy. + // It supports both the desktop and mobile version. + "name": "AF: Build Dart Only", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "env": { + "RUST_LOG": "trace", + }, + // uncomment the following line to testing performance. + // "flutterMode": "profile", + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + // This task builds the Rust and Dart code of AppFlowy. + "name": "AF-desktop: Build All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Build Appflowy Core", + "env": { + "RUST_LOG": "trace", + "RUST_BACKTRACE": "1" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + // This task builds will: + // - call the clean task, + // - rebuild all the generated Files (including freeze and language files) + // - rebuild the the Rust and Dart code of AppFlowy. + "name": "AF-desktop: Clean + Rebuild All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Clean + Rebuild All", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-iOS: Build All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Build Appflowy Core For iOS", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-iOS: Clean + Rebuild All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Clean + Rebuild All (iOS)", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-iOS-Simulator: Build All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Build Appflowy Core For iOS Simulator", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-iOS-Simulator: Clean + Rebuild All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Clean + Rebuild All (iOS Simulator)", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-Android: Build All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Build Appflowy Core For Android", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-Android: Clean + Rebuild All", + "request": "launch", + "program": "./lib/main.dart", + "type": "dart", + "preLaunchTask": "AF: Clean + Rebuild All (Android)", + "env": { + "RUST_LOG": "trace" + }, + "cwd": "${workspaceRoot}/appflowy_flutter" + }, + { + "name": "AF-desktop: Debug Rust", + "type": "lldb", + "request": "attach", + "pid": "${command:pickMyProcess}" + // To launch the application directly, use the following configuration: + // "request": "launch", + // "program": "[YOUR_APPLICATION_PATH]", + }, + ] +} \ No newline at end of file diff --git a/frontend/appflowy_flutter/integration_test/shared/base.dart b/frontend/appflowy_flutter/integration_test/shared/base.dart index 493cb4c1f0f90..e7502e24498fb 100644 --- a/frontend/appflowy_flutter/integration_test/shared/base.dart +++ b/frontend/appflowy_flutter/integration_test/shared/base.dart @@ -54,8 +54,6 @@ extension AppFlowyTestBase on WidgetTester { final rustEnvs = {}; if (cloudType != null) { switch (cloudType) { - case AuthenticatorType.local: - break; case AuthenticatorType.appflowyCloudSelfHost: rustEnvs["GOTRUE_ADMIN_EMAIL"] = "admin@example.com"; rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password"; @@ -71,9 +69,6 @@ extension AppFlowyTestBase on WidgetTester { () async { if (cloudType != null) { switch (cloudType) { - case AuthenticatorType.local: - await useLocalServer(); - break; case AuthenticatorType.appflowyCloudSelfHost: await useTestSelfHostedAppFlowyCloud(); getIt.unregister(); diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index 4b7ed5d639a08..92e52a1a79988 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -181,37 +181,37 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: - app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874 - appflowy_backend: 78f6a053f756e6bc29bcc5a2106cbe77b756e97a - connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf - device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 + app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0 + appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88 + connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517 - flowy_infra_ui: 931b73a18b54a392ab6152eebe29a63a30751f53 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038 - image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a - integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e - irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486 - keyboard_height_plugin: ef70a8181b29f27670e9e2450814ca6b6dc05b05 - open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1 - package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d + fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9 + keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86 + open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4 + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - saver_gallery: af2d0c762dafda254e0ad025ef0dabd6506cd490 + saver_gallery: 76172dc4bf6b40e66d694948ada9ff402304dd87 SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 - share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 + sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 + share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 - url_launcher_ios: 694010445543906933d732453a59da0a173ae33d - webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4 PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca diff --git a/frontend/appflowy_flutter/lib/env/backend_env.dart b/frontend/appflowy_flutter/lib/env/backend_env.dart index eb8a61d037635..e9fa8bef637f2 100644 --- a/frontend/appflowy_flutter/lib/env/backend_env.dart +++ b/frontend/appflowy_flutter/lib/env/backend_env.dart @@ -16,6 +16,7 @@ class AppFlowyConfiguration { required this.platform, required this.authenticator_type, required this.appflowy_cloud_config, + required this.is_anon, required this.envs, }); @@ -28,6 +29,7 @@ class AppFlowyConfiguration { final String origin_app_path; final String device_id; final String platform; + final bool is_anon; final int authenticator_type; final AppFlowyCloudConfiguration appflowy_cloud_config; final Map envs; diff --git a/frontend/appflowy_flutter/lib/env/cloud_env.dart b/frontend/appflowy_flutter/lib/env/cloud_env.dart index 15f3ada42e3fc..82459cc96bfb5 100644 --- a/frontend/appflowy_flutter/lib/env/cloud_env.dart +++ b/frontend/appflowy_flutter/lib/env/cloud_env.dart @@ -23,9 +23,6 @@ import 'package:appflowy_backend/log.dart'; /// Future _setAuthenticatorType(AuthenticatorType ty) async { switch (ty) { - case AuthenticatorType.local: - await getIt().set(KVKeys.kCloudType, 0.toString()); - break; case AuthenticatorType.appflowyCloud: await getIt().set(KVKeys.kCloudType, 2.toString()); break; @@ -63,8 +60,6 @@ Future getAuthenticatorType() async { } switch (value ?? "0") { - case "0": - return AuthenticatorType.local; case "2": return AuthenticatorType.appflowyCloud; case "3": @@ -100,24 +95,17 @@ bool get isAuthEnabled { return false; } -bool get isLocalAuthEnabled { - return currentCloudType().isLocal; -} - /// Determines if AppFlowy Cloud is enabled. bool get isAppFlowyCloudEnabled { return currentCloudType().isAppFlowyCloudEnabled; } enum AuthenticatorType { - local, appflowyCloud, appflowyCloudSelfHost, // The 'appflowyCloudDevelop' type is used for develop purposes only. appflowyCloudDevelop; - bool get isLocal => this == AuthenticatorType.local; - bool get isAppFlowyCloudEnabled => this == AuthenticatorType.appflowyCloudSelfHost || this == AuthenticatorType.appflowyCloudDevelop || @@ -125,8 +113,6 @@ enum AuthenticatorType { int get value { switch (this) { - case AuthenticatorType.local: - return 0; case AuthenticatorType.appflowyCloud: return 2; case AuthenticatorType.appflowyCloudSelfHost: @@ -138,8 +124,6 @@ enum AuthenticatorType { static AuthenticatorType fromValue(int value) { switch (value) { - case 0: - return AuthenticatorType.local; case 2: return AuthenticatorType.appflowyCloud; case 3: @@ -147,7 +131,7 @@ enum AuthenticatorType { case 4: return AuthenticatorType.appflowyCloudDevelop; default: - return AuthenticatorType.local; + return AuthenticatorType.appflowyCloud; } } } @@ -180,10 +164,6 @@ Future useAppFlowyBetaCloudWithURL( await _setAppFlowyCloudUrl(url); } -Future useLocalServer() async { - await _setAuthenticatorType(AuthenticatorType.local); -} - // Use getIt() to get the shared environment. class AppFlowyCloudSharedEnv { AppFlowyCloudSharedEnv({ diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/user_session_setting_group.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/user_session_setting_group.dart index b3b7cb71c5f14..18be05abf02cb 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/user_session_setting_group.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/user_session_setting_group.dart @@ -1,9 +1,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; -import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart'; import 'package:appflowy/startup/startup.dart'; -import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/sign_in_bloc.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart'; import 'package:appflowy/workspace/presentation/settings/pages/account/account_deletion.dart'; @@ -32,12 +30,6 @@ class UserSessionSettingGroup extends StatelessWidget { if (showThirdPartyLogin) _buildThirdPartySignInButtons(context), const VSpace(8.0), - // logout button - MobileLogoutButton( - text: LocaleKeys.settings_menu_logout.tr(), - onPressed: () async => _showLogoutDialog(), - ), - // delete account button // only show the delete account button in cloud mode if (userProfile.authenticator == AuthenticatorPB.AppFlowyCloud) ...[ @@ -79,31 +71,6 @@ class UserSessionSettingGroup extends StatelessWidget { builder: (_) => const _DeleteAccountBottomSheet(), ); } - - Future _showLogoutDialog() async { - return showFlowyCupertinoConfirmDialog( - title: LocaleKeys.settings_menu_logoutPrompt.tr(), - leftButton: FlowyText( - LocaleKeys.button_cancel.tr(), - fontSize: 17.0, - figmaLineHeight: 24.0, - fontWeight: FontWeight.w500, - color: const Color(0xFF007AFF), - ), - rightButton: FlowyText( - LocaleKeys.button_logout.tr(), - fontSize: 17.0, - figmaLineHeight: 24.0, - fontWeight: FontWeight.w400, - color: const Color(0xFFFE0220), - ), - onRightButtonPressed: (context) async { - Navigator.of(context).pop(); - await getIt().signOut(); - await runAppFlowy(); - }, - ); - } } class _DeleteAccountBottomSheet extends StatefulWidget { diff --git a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart index 621ba988cf217..0f92bfbca837d 100644 --- a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart +++ b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart @@ -99,13 +99,6 @@ void _resolveCommonService( void _resolveUserDeps(GetIt getIt, IntegrationMode mode) { switch (currentCloudType()) { - case AuthenticatorType.local: - getIt.registerFactory( - () => BackendAuthService( - AuthenticatorPB.Local, - ), - ); - break; case AuthenticatorType.appflowyCloud: case AuthenticatorType.appflowyCloudSelfHost: case AuthenticatorType.appflowyCloudDevelop: diff --git a/frontend/appflowy_flutter/lib/startup/startup.dart b/frontend/appflowy_flutter/lib/startup/startup.dart index 7a282b3856d58..3e990823de7b3 100644 --- a/frontend/appflowy_flutter/lib/startup/startup.dart +++ b/frontend/appflowy_flutter/lib/startup/startup.dart @@ -32,6 +32,21 @@ class FlowyRunnerContext { final Directory applicationDataDirectory; } +/// Launches AppFlowy application with optional anonymous mode. +/// +/// [isAnon] When set to true, the application runs in anonymous mode where: +/// - No user authentication is required +/// - All data is stored locally in a separate anonymous directory +/// - The data is isolated from the main application storage +/// +/// Example: +/// ```dart +/// // Launch in normal mode +/// await runAppFlowy(); +/// +/// // Launch in anonymous mode +/// await runAppFlowy(isAnon: true); +/// ``` Future runAppFlowy({bool isAnon = false}) async { Log.info('restart AppFlowy: isAnon: $isAnon'); @@ -129,7 +144,10 @@ class FlowyRunner { // init the app window InitAppWindowTask(), // Init Rust SDK - InitRustSDKTask(customApplicationPath: applicationDataDirectory), + InitRustSDKTask( + customApplicationPath: applicationDataDirectory, + isAnon: isAnon, + ), // Load Plugins, like document, grid ... const PluginLoadTask(), const FileStorageTask(), diff --git a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart index b326276c56364..5d102715d13a3 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart @@ -120,18 +120,6 @@ GoRouter generateRouter(Widget child) { ); }, ), - GoRoute( - path: SignUpScreen.routeName, - pageBuilder: (context, state) { - return CustomTransitionPage( - child: SignUpScreen( - router: getIt(), - ), - transitionsBuilder: _buildFadeTransition, - transitionDuration: _slowDuration, - ); - }, - ), ], ); } diff --git a/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart b/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart index c406dd161aa92..2e2bdbd9d3798 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart @@ -12,11 +12,13 @@ import '../startup.dart'; class InitRustSDKTask extends LaunchTask { const InitRustSDKTask({ - this.customApplicationPath, + required this.isAnon, + required this.customApplicationPath, }); // Customize the RustSDK initialization path final Directory? customApplicationPath; + final bool isAnon; @override LaunchTaskType get type => LaunchTaskType.dataProcessing; @@ -24,8 +26,16 @@ class InitRustSDKTask extends LaunchTask { @override Future initialize(LaunchContext context) async { final root = await getApplicationSupportDirectory(); - final applicationPath = await appFlowyApplicationDataDirectory(); - final dir = customApplicationPath ?? applicationPath; + + // Only one anon user no matter what the cloud URL is + final applicationPath = isAnon + ? await appFlowyAnonDirectory() + : await appFlowyApplicationDataDirectory(); + + final dir = + isAnon ? applicationPath : (customApplicationPath ?? applicationPath); + + // Get device ID in parallel with path resolution final deviceId = await getDeviceId(); // Pass the environment variables to the Rust SDK @@ -35,6 +45,7 @@ class InitRustSDKTask extends LaunchTask { dir.path, applicationPath.path, deviceId, + isAnon, rustEnvs: context.config.rustEnvs, ); await context.getIt().init(jsonEncode(env.toJson())); @@ -49,7 +60,8 @@ AppFlowyConfiguration _makeAppFlowyConfiguration( String appVersion, String customAppPath, String originAppPath, - String deviceId, { + String deviceId, + bool isAnon, { required Map rustEnvs, }) { final env = getIt(); @@ -62,6 +74,7 @@ AppFlowyConfiguration _makeAppFlowyConfiguration( platform: Platform.operatingSystem, authenticator_type: env.authenticatorType.value, appflowy_cloud_config: env.appflowyCloudConfig, + is_anon: isAnon, envs: rustEnvs, ); } @@ -82,3 +95,10 @@ Future appFlowyApplicationDataDirectory() async { return Directory(path.join(Directory.current.path, '.sandbox')); } } + +/// The anon directory. +Future appFlowyAnonDirectory() async { + final Directory documentsDir = + await getApplicationSupportDirectory().then((directory) => directory); + return Directory(path.join(documentsDir.path, 'anon')); +} diff --git a/frontend/appflowy_flutter/lib/user/application/anon_user_bloc.dart b/frontend/appflowy_flutter/lib/user/application/anon_user_bloc.dart index 292760ca4ba00..e46390d87bbf3 100644 --- a/frontend/appflowy_flutter/lib/user/application/anon_user_bloc.dart +++ b/frontend/appflowy_flutter/lib/user/application/anon_user_bloc.dart @@ -1,6 +1,4 @@ import 'package:appflowy/user/application/user_service.dart'; -import 'package:appflowy_backend/log.dart'; -import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,9 +9,7 @@ class AnonUserBloc extends Bloc { AnonUserBloc() : super(AnonUserState.initial()) { on((event, emit) async { await event.when( - initial: () async { - await _loadHistoricalUsers(); - }, + initial: () async {}, didLoadAnonUsers: (List anonUsers) { emit(state.copyWith(anonUsers: anonUsers)); }, @@ -24,20 +20,6 @@ class AnonUserBloc extends Bloc { ); }); } - - Future _loadHistoricalUsers() async { - final result = await UserBackendService.getAnonUser(); - result.fold( - (anonUser) { - add(AnonUserEvent.didLoadAnonUsers([anonUser])); - }, - (error) { - if (error.code != ErrorCode.RecordNotFound) { - Log.error(error); - } - }, - ); - } } @freezed diff --git a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart index 6d02f188c81c0..f25828044c7ad 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart @@ -94,10 +94,10 @@ class AppFlowyCloudAuthService implements AuthService { } @override - Future> signUpAsGuest({ + Future signUpAsGuest({ Map params = const {}, }) async { - return _backendAuthService.signUpAsGuest(); + await _backendAuthService.signUpAsGuest(); } @override diff --git a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_mock_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_mock_auth_service.dart index d8cee89b5913a..09add47b43996 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_mock_auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_mock_auth_service.dart @@ -29,7 +29,16 @@ class AppFlowyCloudMockAuthService implements AuthService { required String password, Map params = const {}, }) async { - throw UnimplementedError(); + final request = SignUpPayloadPB.create() + ..name = name + ..email = email + ..password = password + ..authType = _appFlowyAuthService.authType + ..deviceId = await getDeviceId(); + final response = await UserEventSignUp(request).send().then( + (value) => value, + ); + return response; } @override @@ -89,10 +98,10 @@ class AppFlowyCloudMockAuthService implements AuthService { } @override - Future> signUpAsGuest({ + Future signUpAsGuest({ Map params = const {}, }) async { - return _appFlowyAuthService.signUpAsGuest(); + await _appFlowyAuthService.signUpAsGuest(); } @override diff --git a/frontend/appflowy_flutter/lib/user/application/auth/auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/auth_service.dart index 9879b9a18e395..e4d1281e09fad 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/auth_service.dart @@ -61,7 +61,7 @@ abstract class AuthService { /// - `params`: Additional parameters for guest registration (optional). /// /// Returns a default [UserProfilePB]. - Future> signUpAsGuest({ + Future signUpAsGuest({ Map params, }); diff --git a/frontend/appflowy_flutter/lib/user/application/auth/backend_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/backend_auth_service.dart index f47fd5a4a6f58..e1d4207312cfe 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/backend_auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/backend_auth_service.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; @@ -5,11 +6,8 @@ import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart' - show SignInPayloadPB, SignUpPayloadPB, UserProfilePB; + show SignInPayloadPB, UserProfilePB; import 'package:appflowy_result/appflowy_result.dart'; -import 'package:easy_localization/easy_localization.dart'; - -import '../../../generated/locale_keys.g.dart'; import 'device_id.dart'; class BackendAuthService implements AuthService { @@ -39,16 +37,17 @@ class BackendAuthService implements AuthService { required String password, Map params = const {}, }) async { - final request = SignUpPayloadPB.create() - ..name = name - ..email = email - ..password = password - ..authType = authType - ..deviceId = await getDeviceId(); - final response = await UserEventSignUp(request).send().then( - (value) => value, - ); - return response; + throw UnimplementedError(); + // final request = SignUpPayloadPB.create() + // ..name = name + // ..email = email + // ..password = password + // ..authType = authType + // ..deviceId = await getDeviceId(); + // final response = await UserEventSignUp(request).send().then( + // (value) => value, + // ); + // return response; } @override @@ -60,23 +59,10 @@ class BackendAuthService implements AuthService { } @override - Future> signUpAsGuest({ + Future signUpAsGuest({ Map params = const {}, }) async { - const password = "Guest!@123456"; - final userEmail = "anon@appflowy.io"; - - final request = SignUpPayloadPB.create() - ..name = LocaleKeys.defaultUsername.tr() - ..email = userEmail - ..password = password - // When sign up as guest, the auth type is always local. - ..authType = AuthenticatorPB.Local - ..deviceId = await getDeviceId(); - final response = await UserEventSignUp(request).send().then( - (value) => value, - ); - return response; + await runAppFlowy(isAnon: true); } @override diff --git a/frontend/appflowy_flutter/lib/user/application/sign_in_bloc.dart b/frontend/appflowy_flutter/lib/user/application/sign_in_bloc.dart index 54a60702c8e3d..bbc2393e9f2cc 100644 --- a/frontend/appflowy_flutter/lib/user/application/sign_in_bloc.dart +++ b/frontend/appflowy_flutter/lib/user/application/sign_in_bloc.dart @@ -251,16 +251,7 @@ class SignInBloc extends Bloc { ), ); - final result = await authService.signUpAsGuest(); - emit( - result.fold( - (userProfile) => state.copyWith( - isSubmitting: false, - successOrFail: FlowyResult.success(userProfile), - ), - (error) => _stateFromCode(error), - ), - ); + await authService.signUpAsGuest(); } SignInState _stateFromCode(FlowyError error) { diff --git a/frontend/appflowy_flutter/lib/user/presentation/router.dart b/frontend/appflowy_flutter/lib/user/presentation/router.dart index 370d9c2062d0e..439ba9ec33f66 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/router.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/router.dart @@ -21,10 +21,6 @@ class AuthRouter { getIt().pushWorkspaceStartScreen(context, userProfile); } - void pushSignUpScreen(BuildContext context) { - context.push(SignUpScreen.routeName); - } - /// Navigates to the home screen based on the current workspace and platform. /// /// This function takes in a [BuildContext] and a [UserProfilePB] object to diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/screens.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/screens.dart index 088da389789d6..fa319aa2b9680 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/screens.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/screens.dart @@ -1,7 +1,6 @@ export 'sign_in_screen/sign_in_screen.dart'; export 'skip_log_in_screen.dart'; export 'splash_screen.dart'; -export 'sign_up_screen.dart'; export 'encrypt_secret_screen.dart'; export 'workspace_error_screen.dart'; export 'workspace_start_screen/workspace_start_screen.dart'; diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart index 6326e1a8113bd..f394b7e3acee7 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart @@ -36,9 +36,8 @@ class MobileSignInScreen extends StatelessWidget { const VSpace(spacing), _buildAppNameText(colorScheme), const VSpace(spacing * 2), - isLocalAuthEnabled - ? const SignInAnonymousButtonV3() - : const ContinueWithEmailAndPassword(), + const SignInAnonymousButtonV3(), + const ContinueWithEmailAndPassword(), const VSpace(spacing), if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme), const VSpace(spacing * 1.5), @@ -129,9 +128,8 @@ class MobileSignInScreen extends StatelessWidget { }, ), const HSpace(24), - isLocalAuthEnabled - ? const ChangeCloudModeButton() - : const SignInAnonymousButtonV2(), + const ChangeCloudModeButton(), + const SignInAnonymousButtonV2(), ], ); } diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart index c6b2d5401c43f..f6a4dd2ab4e1e 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/anonymous_sign_in_button.dart @@ -29,7 +29,7 @@ class SignInAnonymousButtonV3 extends StatelessWidget { }, child: BlocBuilder( builder: (context, state) { - final text = LocaleKeys.signUp_getStartedText.tr(); + final text = LocaleKeys.signIn_anonymousMode.tr(); final onTap = state.anonUsers.isEmpty ? () { context diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/sign_in_agreement.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/sign_in_agreement.dart index 4ea819d99789d..6db0c563cf7cb 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/sign_in_agreement.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/sign_in_agreement.dart @@ -1,5 +1,4 @@ import 'package:appflowy/core/helpers/url_launcher.dart'; -import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy_ui/appflowy_ui.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -20,14 +19,13 @@ class SignInAgreement extends StatelessWidget { final underlinedTextStyle = theme.textStyle.caption.underline( color: theme.textColorScheme.secondary, ); + return RichText( textAlign: TextAlign.center, text: TextSpan( children: [ TextSpan( - text: isLocalAuthEnabled - ? '${LocaleKeys.web_signInLocalAgreement.tr()} \n' - : '${LocaleKeys.web_signInAgreement.tr()} \n', + text: '${LocaleKeys.web_signInAgreement.tr()} \n', style: textStyle, ), TextSpan( diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_up_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_up_screen.dart deleted file mode 100644 index 8aea8dde5579b..0000000000000 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_up_screen.dart +++ /dev/null @@ -1,220 +0,0 @@ -import 'package:appflowy/generated/flowy_svgs.g.dart'; -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/startup/startup.dart'; -import 'package:appflowy/user/application/sign_up_bloc.dart'; -import 'package:appflowy/user/presentation/router.dart'; -import 'package:appflowy/user/presentation/widgets/widgets.dart'; -import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; -import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart' - show UserProfilePB; -import 'package:appflowy_result/appflowy_result.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra_ui/style_widget/snap_bar.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flowy_infra_ui/widget/rounded_button.dart'; -import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; -import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class SignUpScreen extends StatelessWidget { - const SignUpScreen({ - super.key, - required this.router, - }); - - static const routeName = '/SignUpScreen'; - final AuthRouter router; - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => getIt(), - child: BlocListener( - listener: (context, state) { - final successOrFail = state.successOrFail; - if (successOrFail != null) { - _handleSuccessOrFail(context, successOrFail); - } - }, - child: const Scaffold(body: SignUpForm()), - ), - ); - } - - void _handleSuccessOrFail( - BuildContext context, - FlowyResult result, - ) { - result.fold( - (user) => router.pushWorkspaceStartScreen(context, user), - (error) => showSnapBar(context, error.msg), - ); - } -} - -class SignUpForm extends StatelessWidget { - const SignUpForm({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return Align( - child: AuthFormContainer( - children: [ - FlowyLogoTitle( - title: LocaleKeys.signUp_title.tr(), - logoSize: const Size(60, 60), - ), - const VSpace(30), - const EmailTextField(), - const VSpace(5), - const PasswordTextField(), - const VSpace(5), - const RepeatPasswordTextField(), - const VSpace(30), - const SignUpButton(), - const VSpace(10), - const SignUpPrompt(), - if (context.read().state.isSubmitting) ...[ - const SizedBox(height: 8), - const LinearProgressIndicator(), - ], - ], - ), - ); - } -} - -class SignUpPrompt extends StatelessWidget { - const SignUpPrompt({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FlowyText.medium( - LocaleKeys.signUp_alreadyHaveAnAccount.tr(), - color: Theme.of(context).hintColor, - ), - TextButton( - style: TextButton.styleFrom( - textStyle: Theme.of(context).textTheme.bodyMedium, - ), - onPressed: () => Navigator.pop(context), - child: FlowyText.medium( - LocaleKeys.signIn_buttonText.tr(), - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ); - } -} - -class SignUpButton extends StatelessWidget { - const SignUpButton({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return RoundedTextButton( - title: LocaleKeys.signUp_getStartedText.tr(), - height: 48, - onPressed: () { - context - .read() - .add(const SignUpEvent.signUpWithUserEmailAndPassword()); - }, - ); - } -} - -class PasswordTextField extends StatelessWidget { - const PasswordTextField({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.passwordError != current.passwordError, - builder: (context, state) { - return RoundedInputField( - obscureText: true, - obscureIcon: const FlowySvg(FlowySvgs.hide_m), - obscureHideIcon: const FlowySvg(FlowySvgs.show_m), - hintText: LocaleKeys.signUp_passwordHint.tr(), - normalBorderColor: Theme.of(context).colorScheme.outline, - errorBorderColor: Theme.of(context).colorScheme.error, - cursorColor: Theme.of(context).colorScheme.primary, - errorText: context.read().state.passwordError ?? '', - onChanged: (value) => context - .read() - .add(SignUpEvent.passwordChanged(value)), - ); - }, - ); - } -} - -class RepeatPasswordTextField extends StatelessWidget { - const RepeatPasswordTextField({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.repeatPasswordError != current.repeatPasswordError, - builder: (context, state) { - return RoundedInputField( - obscureText: true, - obscureIcon: const FlowySvg(FlowySvgs.hide_m), - obscureHideIcon: const FlowySvg(FlowySvgs.show_m), - hintText: LocaleKeys.signUp_repeatPasswordHint.tr(), - normalBorderColor: Theme.of(context).colorScheme.outline, - errorBorderColor: Theme.of(context).colorScheme.error, - cursorColor: Theme.of(context).colorScheme.primary, - errorText: context.read().state.repeatPasswordError ?? '', - onChanged: (value) => context - .read() - .add(SignUpEvent.repeatPasswordChanged(value)), - ); - }, - ); - } -} - -class EmailTextField extends StatelessWidget { - const EmailTextField({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.emailError != current.emailError, - builder: (context, state) { - return RoundedInputField( - hintText: LocaleKeys.signUp_emailHint.tr(), - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), - normalBorderColor: Theme.of(context).colorScheme.outline, - errorBorderColor: Theme.of(context).colorScheme.error, - cursorColor: Theme.of(context).colorScheme.primary, - errorText: context.read().state.emailError ?? '', - onChanged: (value) => - context.read().add(SignUpEvent.emailChanged(value)), - ); - }, - ); - } -} diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart index ee089dfce0b99..75cb3232f1a58 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart @@ -5,10 +5,8 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/anon_user_bloc.dart'; import 'package:appflowy/user/application/auth/auth_service.dart'; -import 'package:appflowy/user/presentation/router.dart'; import 'package:appflowy/user/presentation/widgets/widgets.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; -import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/language.dart'; import 'package:flowy_infra/size.dart'; @@ -79,11 +77,7 @@ class _SkipLogInScreenState extends State { } Future _autoRegister(BuildContext context) async { - final result = await getIt().signUpAsGuest(); - result.fold( - (user) => getIt().goHomeScreen(context, user), - (error) => Log.error(error), - ); + await getIt().signUpAsGuest(); } Future _relaunchAppAndAutoRegister() async => runAppFlowy(isAnon: true); diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart index 146bf06df1e74..9ae5d6b9b210e 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart @@ -1,7 +1,6 @@ import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/startup/startup.dart'; -import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/splash_bloc.dart'; import 'package:appflowy/user/domain/auth_state.dart'; import 'package:appflowy/user/presentation/helpers/helpers.dart'; @@ -22,19 +21,7 @@ class SplashScreen extends StatelessWidget { @override Widget build(BuildContext context) { - if (isAnon) { - return FutureBuilder( - future: _registerIfNeeded(), - builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return const SizedBox.shrink(); - } - return _buildChild(context); - }, - ); - } else { - return _buildChild(context); - } + return _buildChild(context); } BlocProvider _buildChild(BuildContext context) { @@ -99,13 +86,6 @@ class SplashScreen extends StatelessWidget { context.go(SkipLogInScreen.routeName); } } - - Future _registerIfNeeded() async { - final result = await UserEventGetUserProfile().send(); - if (result.isFailure) { - await getIt().signUpAsGuest(); - } - } } class Body extends StatelessWidget { diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/workspace_start_screen/mobile_workspace_start_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/workspace_start_screen/mobile_workspace_start_screen.dart index 59b61aa54b9f9..7ad9cdd9f3da5 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/workspace_start_screen/mobile_workspace_start_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/workspace_start_screen/mobile_workspace_start_screen.dart @@ -121,7 +121,7 @@ class _MobileWorkspaceStartScreenState selectedWorkspace!, ); }, - child: Text(LocaleKeys.signUp_getStartedText.tr()), + child: Text(LocaleKeys.signIn_anonymousMode.tr()), ), const VSpace(spacing), ], diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/account/account_sign_in_out.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/account/account_sign_in_out.dart index 984598f29c398..0d869ffa36f9d 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/account/account_sign_in_out.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/account/account_sign_in_out.dart @@ -18,13 +18,13 @@ class AccountSignInOutSection extends StatelessWidget { const AccountSignInOutSection({ super.key, required this.userProfile, - required this.onAction, - this.signIn = true, + required this.onSignOut, + this.displaySignIn = true, }); final UserProfilePB userProfile; - final VoidCallback onAction; - final bool signIn; + final VoidCallback onSignOut; + final bool displaySignIn; @override Widget build(BuildContext context) { @@ -34,8 +34,8 @@ class AccountSignInOutSection extends StatelessWidget { const Spacer(), AccountSignInOutButton( userProfile: userProfile, - onAction: onAction, - signIn: signIn, + onSignOut: onSignOut, + displaySignIn: displaySignIn, ), ], ); @@ -46,25 +46,27 @@ class AccountSignInOutButton extends StatelessWidget { const AccountSignInOutButton({ super.key, required this.userProfile, - required this.onAction, - this.signIn = true, + required this.onSignOut, + this.displaySignIn = true, }); final UserProfilePB userProfile; - final VoidCallback onAction; - final bool signIn; + final VoidCallback onSignOut; + final bool displaySignIn; @override Widget build(BuildContext context) { return PrimaryRoundedButton( - text: signIn + text: displaySignIn ? LocaleKeys.settings_accountPage_login_loginLabel.tr() : LocaleKeys.settings_accountPage_login_logoutLabel.tr(), margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), fontWeight: FontWeight.w500, radius: 8.0, - onTap: () => - signIn ? _showSignInDialog(context) : _showLogoutDialog(context), + onTap: () { + displaySignIn ? _showSignInDialog(context) : _showLogoutDialog(context); + // } + }, ); } @@ -77,7 +79,7 @@ class AccountSignInOutButton extends StatelessWidget { : LocaleKeys.settings_menu_logoutPrompt.tr(), onConfirm: () async { await getIt().signOut(); - onAction(); + onSignOut(); }, ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_account_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_account_view.dart index 701d1cb565498..b626b20516225 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_account_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_account_view.dart @@ -67,41 +67,27 @@ class _SettingsAccountViewState extends State { ], ), - // user email - // Only show email if the user is authenticated and not using local auth - if (isAuthEnabled && - state.userProfile.authenticator != AuthenticatorPB.Local) ...[ - SettingsCategory( - title: LocaleKeys.settings_accountPage_email_title.tr(), - children: [ - FlowyText.regular(state.userProfile.email), - AccountSignInOutSection( - userProfile: state.userProfile, - onAction: state.userProfile.authenticator == - AuthenticatorPB.Local - ? widget.didLogin - : widget.didLogout, - signIn: state.userProfile.authenticator == - AuthenticatorPB.Local, - ), - ], - ), - ], - - if (isAuthEnabled && - state.userProfile.authenticator == AuthenticatorPB.Local) ...[ + // Account section (email or login) + if (isAuthEnabled) ...[ SettingsCategory( title: LocaleKeys.settings_accountPage_login_title.tr(), children: [ - AccountSignInOutSection( - userProfile: state.userProfile, - onAction: state.userProfile.authenticator == - AuthenticatorPB.Local - ? widget.didLogin - : widget.didLogout, - signIn: state.userProfile.authenticator == - AuthenticatorPB.Local, - ), + // show user email + if (state.userProfile.authenticator == + AuthenticatorPB.Local) ...[ + // run appflowy without anonymous mode + const _ExitAnonMode(), + ], + + if (state.userProfile.authenticator != + AuthenticatorPB.Local) ...[ + FlowyText.regular(state.userProfile.email), + AccountSignInOutSection( + userProfile: state.userProfile, + onSignOut: widget.didLogout, + displaySignIn: false, + ), + ], ], ), ], @@ -125,3 +111,24 @@ class _SettingsAccountViewState extends State { ); } } + +class _ExitAnonMode extends StatelessWidget { + const _ExitAnonMode(); + + @override + Widget build(BuildContext context) { + return PrimaryRoundedButton( + text: LocaleKeys.signIn_exitAnonymousMode.tr(), + margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + fontWeight: FontWeight.w500, + radius: 8.0, + onTap: () { + if (Navigator.canPop(context)) { + Navigator.pop(context); + } + + runAppFlowy(); + }, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart index 692be99baa6fc..25ce9c4b52f15 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart @@ -9,7 +9,6 @@ import 'package:appflowy/workspace/application/settings/cloud_setting_bloc.dart' import 'package:appflowy/workspace/presentation/settings/shared/af_dropdown_menu_entry.dart'; import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart'; import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart'; -import 'package:appflowy/workspace/presentation/settings/widgets/setting_local_cloud.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -62,8 +61,6 @@ class SettingCloud extends StatelessWidget { Widget _viewFromCloudType(AuthenticatorType cloudType) { switch (cloudType) { - case AuthenticatorType.local: - return SettingLocalCloud(restartAppFlowy: restartAppFlowy); case AuthenticatorType.appflowyCloud: return AppFlowyCloudViewSetting(restartAppFlowy: restartAppFlowy); case AuthenticatorType.appflowyCloudSelfHost: @@ -239,8 +236,6 @@ class _CloudServerSwitcher extends StatelessWidget { String titleFromCloudType(AuthenticatorType cloudType) { switch (cloudType) { - case AuthenticatorType.local: - return LocaleKeys.settings_menu_cloudLocal.tr(); case AuthenticatorType.appflowyCloud: return LocaleKeys.settings_menu_cloudAppFlowy.tr(); case AuthenticatorType.appflowyCloudSelfHost: diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart index 68680c0dd0fe1..5f664c893173a 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart @@ -1,4 +1,3 @@ -import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/_restart_app_button.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; @@ -22,7 +21,6 @@ class SettingLocalCloud extends StatelessWidget { NavigatorAlertDialog( title: LocaleKeys.settings_menu_restartAppTip.tr(), confirm: () async { - await useLocalServer(); restartAppFlowy(); }, ).show(context); diff --git a/frontend/appflowy_flutter/macos/Podfile.lock b/frontend/appflowy_flutter/macos/Podfile.lock index 30ee626f09b91..b4a1a3d20d5ac 100644 --- a/frontend/appflowy_flutter/macos/Podfile.lock +++ b/frontend/appflowy_flutter/macos/Podfile.lock @@ -144,34 +144,34 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468 - appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7 - auto_updater_macos: 3a42f1a06be6981f1a18be37e6e7bf86aa732118 - bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9 - connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5 - desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43 - device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041 - file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 - flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e + app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a + appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9 + auto_updater_macos: 3e3462c418fe4e731917eacd8d28eef7af84086d + bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 + device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 + file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d + flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277 - hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2 - irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba - local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e - package_info_plus: f0052d280d17aa382b932f399edf32507174e870 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c + irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 + local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff + package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda - screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f + screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 - share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 + share_plus: 1fa619de8392a4398bfaf176d441853922614e89 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 Sparkle: 5f8960a7a119aa7d45dacc0d5837017170bc5675 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189 - url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 - webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c - window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 + url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 + webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4 + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823 diff --git a/frontend/appflowy_flutter/test/util.dart b/frontend/appflowy_flutter/test/util.dart index c4f2a21c647b8..d6938f5a66886 100644 --- a/frontend/appflowy_flutter/test/util.dart +++ b/frontend/appflowy_flutter/test/util.dart @@ -31,7 +31,6 @@ class AppFlowyUnitTest { final test = AppFlowyUnitTest(); await test._signIn(); await test._loadWorkspace(); - await test._initialServices(); return test; } diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 29473d2e00b70..373afaf231625 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -38,6 +38,7 @@ "continueAnonymousUser": "Continue with an anonymous session", "anonymous": "Anonymous", "anonymousMode": "Anonymous mode", + "exitAnonymousMode": "Exit anonymous mode", "buttonText": "Sign In", "signingInText": "Signing in...", "forgotPassword": "Forgot Password?", diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index fdf8c8348e20f..006c43d696e0a 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -345,7 +345,7 @@ dependencies = [ [[package]] name = "af-local-ai" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=9d731d89ea2e0fd764da2effa8a456210c5a39c3#9d731d89ea2e0fd764da2effa8a456210c5a39c3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa#093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" dependencies = [ "af-plugin", "anyhow", @@ -362,7 +362,7 @@ dependencies = [ [[package]] name = "af-mcp" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=9d731d89ea2e0fd764da2effa8a456210c5a39c3#9d731d89ea2e0fd764da2effa8a456210c5a39c3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa#093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" dependencies = [ "anyhow", "futures-util", @@ -376,7 +376,7 @@ dependencies = [ [[package]] name = "af-plugin" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=9d731d89ea2e0fd764da2effa8a456210c5a39c3#9d731d89ea2e0fd764da2effa8a456210c5a39c3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa#093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" dependencies = [ "anyhow", "cfg-if", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index d3427ef99c4ea..475cefb3d43d9 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -152,6 +152,6 @@ collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFl # To update the commit ID, run: # scripts/tool/update_local_ai_rev.sh new_rev_id # ⚠️⚠️⚠️️ -af-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "9d731d89ea2e0fd764da2effa8a456210c5a39c3" } -af-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "9d731d89ea2e0fd764da2effa8a456210c5a39c3" } -af-mcp = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "9d731d89ea2e0fd764da2effa8a456210c5a39c3" } +af-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" } +af-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" } +af-mcp = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "093d3f4916a7b924ac66b4f0a9d81cc5fcc72eaa" } diff --git a/frontend/rust-lib/collab-integrate/src/collab_builder.rs b/frontend/rust-lib/collab-integrate/src/collab_builder.rs index e31c38d043237..208110334268d 100644 --- a/frontend/rust-lib/collab-integrate/src/collab_builder.rs +++ b/frontend/rust-lib/collab-integrate/src/collab_builder.rs @@ -203,7 +203,7 @@ impl AppFlowyCollabBuilder { folder_data: Option, ) -> Result>, Error> { let expected_collab_type = CollabType::Folder; - assert_eq!(object.collab_type, expected_collab_type); + debug_assert_eq!(object.collab_type, expected_collab_type); let folder = match folder_data { None => { let collab = self.build_collab(&object, &collab_db, doc_state).await?; diff --git a/frontend/rust-lib/dart-ffi/src/env_serde.rs b/frontend/rust-lib/dart-ffi/src/env_serde.rs index a59b28361f6f8..066caa6c5b6da 100644 --- a/frontend/rust-lib/dart-ffi/src/env_serde.rs +++ b/frontend/rust-lib/dart-ffi/src/env_serde.rs @@ -16,6 +16,7 @@ pub struct AppFlowyDartConfiguration { pub device_id: String, pub platform: String, pub authenticator_type: AuthenticatorType, + pub is_anon: bool, pub(crate) appflowy_cloud_config: AFCloudConfiguration, #[serde(default)] pub(crate) envs: HashMap, diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs index 6c3d08ae01972..63726efb088a4 100644 --- a/frontend/rust-lib/dart-ffi/src/lib.rs +++ b/frontend/rust-lib/dart-ffi/src/lib.rs @@ -109,7 +109,19 @@ pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 { let serde_str = c_str .to_str() .expect("Failed to convert C string to Rust string"); - let configuration = AppFlowyDartConfiguration::from_str(serde_str); + + // if let Some(core) = DART_APPFLOWY_CORE.core.write().unwrap().take() { + // // Use println! instead of tracing, because tracing is not initialized yet. + // println!("release previous appflowy core: {:?}", core.config); + // core.close_db(); + // } + + let mut configuration = AppFlowyDartConfiguration::from_str(serde_str); + if configuration.is_anon { + configuration.authenticator_type = AuthenticatorType::Local; + } + + println!("active appflowy configuration: {:?}", configuration); configuration.write_env(); if configuration.authenticator_type == AuthenticatorType::AppFlowyCloud { @@ -131,12 +143,9 @@ pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 { configuration.device_id, configuration.platform, DEFAULT_NAME.to_string(), + configuration.is_anon, ); - if let Some(core) = &*DART_APPFLOWY_CORE.core.write().unwrap() { - core.close_db(); - } - let log_stream = LOG_STREAM_ISOLATE .write() .unwrap() @@ -153,6 +162,8 @@ pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 { *DART_APPFLOWY_CORE.sender.write().unwrap() = Some(sender); *DART_APPFLOWY_CORE.handle.write().unwrap() = Some(handle); let cloned_runtime = runtime.clone(); + + println!("init appflowy core: {:?}", config); *DART_APPFLOWY_CORE.core.write().unwrap() = runtime .block_on(async move { Some(AppFlowyCore::new(config, cloned_runtime, log_stream).await) }); 0 diff --git a/frontend/rust-lib/event-integration-test/src/lib.rs b/frontend/rust-lib/event-integration-test/src/lib.rs index 02efc0f75aa00..0b832c05b060e 100644 --- a/frontend/rust-lib/event-integration-test/src/lib.rs +++ b/frontend/rust-lib/event-integration-test/src/lib.rs @@ -85,6 +85,7 @@ impl EventIntegrationTest { device_id, "test".to_string(), name, + false, ) .log_filter( "trace", diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/mod.rs b/frontend/rust-lib/flowy-ai/src/local_ai/mod.rs index c0fd967d43ae7..7a09100838862 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/mod.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/mod.rs @@ -3,4 +3,6 @@ mod request; pub mod resource; pub mod stream_util; + +#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] pub mod watch; diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/watch.rs b/frontend/rust-lib/flowy-ai/src/local_ai/watch.rs index 2baed3f0a5dd7..0f982425f782f 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/watch.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/watch.rs @@ -1,19 +1,14 @@ use crate::local_ai::resource::WatchDiskEvent; use af_plugin::core::path::{install_path, ollama_plugin_path}; use flowy_error::{FlowyError, FlowyResult}; -use std::path::PathBuf; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tracing::{error, trace}; -#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] -#[allow(dead_code)] pub struct WatchContext { + #[allow(dead_code)] watcher: notify::RecommendedWatcher, - pub path: PathBuf, } -#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] -#[allow(dead_code)] pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver)> { use notify::{Event, Watcher}; @@ -50,11 +45,5 @@ pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver, } @@ -35,7 +35,7 @@ impl AppFlowyCoreConfig { let dir = std::path::Path::new(path_str); if !dir.exists() { match std::fs::create_dir_all(dir) { - Ok(_) => info!("Created {} path: {}", label, path_str), + Ok(_) => info!("ensure path, create {} path: {}", label, path_str), Err(err) => error!( "Failed to create {} path: {}. Error: {}", label, path_str, err @@ -52,6 +52,7 @@ impl fmt::Debug for AppFlowyCoreConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut debug = f.debug_struct("AppFlowy Configuration"); debug.field("app_version", &self.app_version); + debug.field("instance_name", &self.name); debug.field("storage_path", &self.storage_path); debug.field("application_path", &self.application_path); if let Some(config) = &self.cloud_config { @@ -64,7 +65,10 @@ impl fmt::Debug for AppFlowyCoreConfig { } } -fn make_user_data_folder(root: &str, url: &str) -> String { +fn make_user_data_folder(root: &str, url: &str, is_anon: bool) -> String { + if is_anon { + return root.to_string(); + } // If a URL is provided, try to parse it and extract the domain name. // This isolates the user data folder by the domain, which prevents data sharing // between different AppFlowy cloud instances. @@ -99,27 +103,10 @@ fn make_user_data_folder(root: &str, url: &str) -> String { storage_path = PathBuf::from(new_storage_path); } - // Copy the user data folder from the root path to the isolated path - // The root path without any suffix is the created by the local version AppFlowy - if !storage_path.exists() && Path::new(root).exists() { - info!("Copy dir from {} to {:?}", root, storage_path); - let src = Path::new(root); - match copy_dir_recursive(src, &storage_path) { - Ok(_) => storage_path - .into_os_string() - .into_string() - .unwrap_or_else(|_| root.to_string()), - Err(err) => { - error!("Copy dir failed: {}", err); - root.to_string() - }, - } - } else { - storage_path - .into_os_string() - .into_string() - .unwrap_or_else(|_| root.to_string()) - } + storage_path + .into_os_string() + .into_string() + .unwrap_or_else(|_| root.to_string()) } impl AppFlowyCoreConfig { @@ -130,13 +117,14 @@ impl AppFlowyCoreConfig { device_id: String, platform: String, name: String, + is_anon: bool, ) -> Self { let cloud_config = AFCloudConfiguration::from_env().ok(); // By default enable sync trace log let log_crates = vec!["sync_trace_log".to_string()]; let storage_path = match &cloud_config { None => custom_application_path, - Some(config) => make_user_data_folder(&custom_application_path, &config.base_url), + Some(config) => make_user_data_folder(&custom_application_path, &config.base_url, is_anon), }; let log_filter = create_log_filter( @@ -153,6 +141,7 @@ impl AppFlowyCoreConfig { device_id, platform, log_filter, + is_anon, cloud_config, } } diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index 79a889f86f3d4..d4eef05b19418 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Weak}; use std::time::Duration; use sysinfo::System; use tokio::sync::RwLock; -use tracing::{debug, error, event, info, instrument}; +use tracing::{debug, error, event, info, instrument, trace, warn}; use uuid::Uuid; use flowy_sqlite::kv::KVStorePreferences; @@ -96,20 +96,38 @@ impl AppFlowyCore { ); } - Self::init(config, runtime).await + let is_anon = config.is_anon; + let this = Self::init(config, runtime).await; + trace!("is_anon: {}", is_anon); + if is_anon { + if let Err(err) = this.user_manager.active_anon_user().await { + if err.is_record_not_found() { + warn!( + "Anon user doesn't exist: {}. Try to create a new anon user.", + err + ); + if let Err(err) = this.user_manager.create_anon_user_once().await { + error!("Create anon user failed: {}", err); + } + } + error!("Active anon user failed: {}", err); + } + } + this } pub fn close_db(&self) { + info!("Close DB. is anon: {}", self.config.is_anon); self.user_manager.close_db(); } #[instrument(skip(config, runtime))] async fn init(config: AppFlowyCoreConfig, runtime: Arc) -> Self { config.ensure_path(); + info!("🔥core config:{:?}", &config); // Init the key value database let store_preference = Arc::new(KVStorePreferences::new(&config.storage_path).unwrap()); - info!("🔥{:?}", &config); let task_scheduler = TaskDispatcher::new(Duration::from_secs(10)); let task_dispatcher = Arc::new(RwLock::new(task_scheduler)); diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 180a5dc1f3630..cead6ce526228 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -175,7 +175,7 @@ impl FolderManager { } } - #[instrument(level = "trace", skip_all, err)] + #[instrument(level = "info", skip_all, err)] pub(crate) async fn make_folder>>( &self, uid: i64, @@ -265,6 +265,10 @@ impl FolderManager { #[tracing::instrument(skip(self, user_id), err)] pub async fn initialize_with_workspace_id(&self, user_id: i64) -> FlowyResult<()> { let workspace_id = self.user.workspace_id()?; + info!( + "initialize user workspace: uid:{}, workspace_id:{}", + user_id, workspace_id + ); let object_id = &workspace_id; let is_exist = self diff --git a/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs b/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs index 799f5b0666b71..322962c77489a 100644 --- a/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs +++ b/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs @@ -1,13 +1,13 @@ use std::path::Path; +use crate::kv::schema::{kv_table, kv_table::dsl, KV_SQL}; +use crate::sqlite_impl::{Database, PoolConfig}; use ::diesel::{query_dsl::*, ExpressionMethods}; use anyhow::anyhow; use diesel::sql_query; use serde::de::DeserializeOwned; use serde::Serialize; - -use crate::kv::schema::{kv_table, kv_table::dsl, KV_SQL}; -use crate::sqlite_impl::{Database, PoolConfig}; +use tracing::info; const DB_NAME: &str = "cache.db"; @@ -29,7 +29,7 @@ impl KVStorePreferences { let mut conn = database.get_connection().unwrap(); sql_query(KV_SQL).execute(&mut conn).unwrap(); - tracing::trace!("Init StorePreferences with path: {}", root); + info!("Init StorePreferences with path: {}", root); Ok(Self { database: Some(database), }) diff --git a/frontend/rust-lib/flowy-user-pub/src/lib.rs b/frontend/rust-lib/flowy-user-pub/src/lib.rs index 2e51ecc626ab9..72197f0efc6a2 100644 --- a/frontend/rust-lib/flowy-user-pub/src/lib.rs +++ b/frontend/rust-lib/flowy-user-pub/src/lib.rs @@ -3,4 +3,5 @@ pub mod entities; pub mod session; pub mod workspace_service; -pub const DEFAULT_USER_NAME: fn() -> String = || "Me".to_string(); +// anonymous user name +pub const DEFAULT_USER_NAME: fn() -> String = || "Anonymous".to_string(); diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index 0e42525d04b54..a4307df2a541e 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -567,7 +567,7 @@ pub async fn open_anon_user_handler( manager: AFPluginState>, ) -> Result<(), FlowyError> { let manager = upgrade_manager(manager)?; - manager.open_anon_user().await?; + manager.active_anon_user().await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs b/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs index 84d43fe4f2e89..741463ebe6cc0 100644 --- a/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs +++ b/frontend/rust-lib/flowy-user/src/services/authenticate_user.rs @@ -109,7 +109,7 @@ impl AuthenticateUser { pub fn close_db(&self) -> FlowyResult<()> { let session = self.get_session()?; - info!("Close db for user: {}", session.user_id); + info!("Close DB for user: {}", session.user_id); self.database.close(session.user_id)?; Ok(()) } @@ -125,14 +125,20 @@ impl AuthenticateUser { match session { None => { let previous = self.session.swap(session); - info!("remove session: {:?}", previous); + info!( + "remove session: {:?} in {}", + previous, self.user_config.storage_path + ); self .store_preferences .remove(self.user_config.session_cache_key.as_ref()); }, Some(session) => { self.session.swap(Some(session.clone())); - info!("Set current session: {:?}", session); + info!( + "Set current session: {:?} in {}", + session, self.user_config.storage_path + ); self .store_preferences .set_object(&self.user_config.session_cache_key, &session) diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index b4bc8911e1f76..1f3715a6ac7a8 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -62,6 +62,12 @@ pub struct UserManager { pub(crate) is_loading_awareness: Arc>, } +impl Drop for UserManager { + fn drop(&mut self) { + info!("UserManager is dropped"); + } +} + impl UserManager { pub fn new( cloud_services: Arc, @@ -403,7 +409,7 @@ impl UserManager { params: BoxAny, ) -> Result { // sign out the current user if there is one - let migration_user = self.get_migration_user(&authenticator).await; + // let migration_user = self.get_migration_user(&authenticator).await; self.cloud_services.set_user_authenticator(&authenticator); let auth_service = self.cloud_services.get_user_service()?; let response: AuthResponse = auth_service.sign_up(params).await?; @@ -411,13 +417,13 @@ impl UserManager { if new_user_profile.encryption_type.require_encrypt_secret() { self.auth_process.lock().await.replace(UserAuthProcess { user_profile: new_user_profile.clone(), - migration_user, + migration_user: None, response, authenticator, }); } else { self - .continue_sign_up(&new_user_profile, migration_user, response, &authenticator) + .continue_sign_up(&new_user_profile, None, response, &authenticator) .await?; } Ok(new_user_profile) @@ -581,6 +587,35 @@ impl UserManager { .backup(session.user_id, &session.user_workspace.id); } + pub async fn create_anon_user_once(&self) -> Result<(), FlowyError> { + info!("Create anon user once"); + let params = SignUpParams { + email: "anon@appflowy.io".to_string(), + name: "Me".to_string(), + password: "password".to_string(), + auth_type: Authenticator::Local, + device_id: "anon device".to_string(), + }; + // check anon user is exist or not, if not, create anon user + if let Err(err) = self + .sign_up(Authenticator::Local, BoxAny::new(params)) + .await + { + error!("Create anon user failed: {}", err); + } + Ok(()) + } + + pub async fn active_anon_user(&self) -> FlowyResult<()> { + info!("Active anon user"); + let anon_session = self.get_anon_session().await?; + info!("anon session: {:?}", anon_session); + self + .authenticate_user + .set_session(Some(Arc::new(anon_session)))?; + Ok(()) + } + /// Fetches the user profile for the given user ID. pub async fn get_user_profile_from_disk(&self, uid: i64) -> Result { select_user_profile(uid, self.db_connection(uid)?) diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs index 8d20bae42779c..91dcdd6d5da21 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_history_user.rs @@ -1,4 +1,3 @@ -use std::sync::Arc; use tracing::instrument; use crate::entities::UserProfilePB; @@ -43,33 +42,21 @@ impl UserManager { } pub async fn get_anon_user(&self) -> FlowyResult { - let anon_session = self - .store_preferences - .get_object::(ANON_USER) - .ok_or(FlowyError::new( - ErrorCode::RecordNotFound, - "Anon user not found", - ))?; + let anon_session = self.get_anon_session().await?; let profile = self .get_user_profile_from_disk(anon_session.user_id) .await?; Ok(UserProfilePB::from(profile)) } - /// Opens a historical user's session based on their user ID, device ID, and authentication type. - /// - /// This function facilitates the re-opening of a user's session from historical tracking. - /// It retrieves the user's workspace and establishes a new session for the user. - /// - pub async fn open_anon_user(&self) -> FlowyResult<()> { + pub async fn get_anon_session(&self) -> FlowyResult { let anon_session = self .store_preferences - .get_object::>(ANON_USER) + .get_object::(ANON_USER) .ok_or(FlowyError::new( ErrorCode::RecordNotFound, "Anon user not found", ))?; - self.authenticate_user.set_session(Some(anon_session))?; - Ok(()) + Ok(anon_session) } } diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 3f3d90127d2a3..0a1a487c26d82 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -503,7 +503,7 @@ impl UserManager { Ok(plan_details) } - #[instrument(level = "info", skip(self), err)] + #[instrument(level = "info", skip(self))] pub async fn get_workspace_usage( &self, workspace_id: String,