chore: anon mode

This commit is contained in:
Nathan 2025-04-08 21:22:16 +08:00
parent eae4e42dcc
commit 7ffd459875
29 changed files with 198 additions and 214 deletions

View file

@ -54,8 +54,6 @@ extension AppFlowyTestBase on WidgetTester {
final rustEnvs = <String, String>{};
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<AuthService>();

View file

@ -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

View file

@ -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<String, String> envs;

View file

@ -23,9 +23,6 @@ import 'package:appflowy_backend/log.dart';
///
Future<void> _setAuthenticatorType(AuthenticatorType ty) async {
switch (ty) {
case AuthenticatorType.local:
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 0.toString());
break;
case AuthenticatorType.appflowyCloud:
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 2.toString());
break;
@ -63,8 +60,6 @@ Future<AuthenticatorType> 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<void> useAppFlowyBetaCloudWithURL(
await _setAppFlowyCloudUrl(url);
}
Future<void> useLocalServer() async {
await _setAuthenticatorType(AuthenticatorType.local);
}
// Use getIt<AppFlowyCloudSharedEnv>() to get the shared environment.
class AppFlowyCloudSharedEnv {
AppFlowyCloudSharedEnv({

View file

@ -99,13 +99,6 @@ void _resolveCommonService(
void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
switch (currentCloudType()) {
case AuthenticatorType.local:
getIt.registerFactory<AuthService>(
() => BackendAuthService(
AuthenticatorPB.Local,
),
);
break;
case AuthenticatorType.appflowyCloud:
case AuthenticatorType.appflowyCloudSelfHost:
case AuthenticatorType.appflowyCloudDevelop:

View file

@ -129,7 +129,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(),

View file

@ -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<void> initialize(LaunchContext context) async {
final root = await getApplicationSupportDirectory();
final applicationPath = await appFlowyApplicationDataDirectory();
final dir = customApplicationPath ?? applicationPath;
// Determine application paths in parallel rather than sequentially
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<FlowySDK>().init(jsonEncode(env.toJson()));
@ -49,7 +60,8 @@ AppFlowyConfiguration _makeAppFlowyConfiguration(
String appVersion,
String customAppPath,
String originAppPath,
String deviceId, {
String deviceId,
bool isAnon, {
required Map<String, String> rustEnvs,
}) {
final env = getIt<AppFlowyCloudSharedEnv>();
@ -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,9 @@ Future<Directory> appFlowyApplicationDataDirectory() async {
return Directory(path.join(Directory.current.path, '.sandbox'));
}
}
Future<Directory> appFlowyAnonDirectory() async {
final Directory documentsDir =
await getApplicationSupportDirectory().then((directory) => directory);
return Directory(path.join(documentsDir.path, 'anon'));
}

View file

@ -89,10 +89,10 @@ class AppFlowyCloudAuthService implements AuthService {
}
@override
Future<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
Future<void> signUpAsGuest({
Map<String, String> params = const {},
}) async {
return _backendAuthService.signUpAsGuest();
await _backendAuthService.signUpAsGuest();
}
@override

View file

@ -88,10 +88,10 @@ class AppFlowyCloudMockAuthService implements AuthService {
}
@override
Future<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
Future<void> signUpAsGuest({
Map<String, String> params = const {},
}) async {
return _appFlowyAuthService.signUpAsGuest();
await _appFlowyAuthService.signUpAsGuest();
}
@override

View file

@ -60,7 +60,7 @@ abstract class AuthService {
/// - `params`: Additional parameters for guest registration (optional).
///
/// Returns a default [UserProfilePB].
Future<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
Future<void> signUpAsGuest({
Map<String, String> params,
});

View file

@ -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';
@ -7,9 +8,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show SignInPayloadPB, SignUpPayloadPB, 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 {
@ -60,23 +58,24 @@ class BackendAuthService implements AuthService {
}
@override
Future<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
Future<void> signUpAsGuest({
Map<String, String> params = const {},
}) async {
const password = "Guest!@123456";
final userEmail = "anon@appflowy.io";
await runAppFlowy(isAnon: true);
// 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;
// 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;
}
@override

View file

@ -195,16 +195,7 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
),
);
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) {

View file

@ -35,9 +35,8 @@ class MobileSignInScreen extends StatelessWidget {
const VSpace(spacing),
_buildAppNameText(colorScheme),
const VSpace(spacing * 2),
isLocalAuthEnabled
? const SignInAnonymousButtonV3()
: const SignInWithMagicLinkButtons(),
const SignInAnonymousButtonV3(),
const SignInWithMagicLinkButtons(),
const VSpace(spacing),
if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme),
const VSpace(spacing * 1.5),
@ -121,9 +120,8 @@ class MobileSignInScreen extends StatelessWidget {
},
),
const HSpace(24),
isLocalAuthEnabled
? const ChangeCloudModeButton()
: const SignInAnonymousButtonV2(),
const ChangeCloudModeButton(),
const SignInAnonymousButtonV2(),
],
);
}

View file

@ -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:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
@ -17,9 +16,7 @@ class SignInAgreement extends StatelessWidget {
text: TextSpan(
children: [
TextSpan(
text: isLocalAuthEnabled
? '${LocaleKeys.web_signInLocalAgreement.tr()} '
: '${LocaleKeys.web_signInAgreement.tr()} ',
text: '${LocaleKeys.web_signInAgreement.tr()} ',
style: const TextStyle(color: Colors.grey, fontSize: 12),
),
TextSpan(

View file

@ -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<SkipLogInScreen> {
}
Future<void> _autoRegister(BuildContext context) async {
final result = await getIt<AuthService>().signUpAsGuest();
result.fold(
(user) => getIt<AuthRouter>().goHomeScreen(context, user),
(error) => Log.error(error),
);
await getIt<AuthService>().signUpAsGuest();
}
Future<void> _relaunchAppAndAutoRegister() async => runAppFlowy(isAnon: true);

View file

@ -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<void>(
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<SplashBloc> _buildChild(BuildContext context) {
@ -99,13 +86,6 @@ class SplashScreen extends StatelessWidget {
context.go(SkipLogInScreen.routeName);
}
}
Future<void> _registerIfNeeded() async {
final result = await UserEventGetUserProfile().send();
if (result.isFailure) {
await getIt<AuthService>().signUpAsGuest();
}
}
}
class Body extends StatelessWidget {

View file

@ -8,6 +8,7 @@ import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/magic_
import 'package:appflowy/util/navigator_context_extension.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_third_party_login.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-user/auth.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -63,8 +64,18 @@ class AccountSignInOutButton extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
fontWeight: FontWeight.w500,
radius: 8.0,
onTap: () =>
signIn ? _showSignInDialog(context) : _showLogoutDialog(context),
onTap: () {
// If current mode is anonymous, we need to pop the dialog and restart app again.
// After restarting, the app will switch to non-anonymous mode.
if (userProfile.authenticator == AuthenticatorPB.Local) {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
runAppFlowy();
} else {
signIn ? _showSignInDialog(context) : _showLogoutDialog(context);
}
},
);
}

View file

@ -67,32 +67,17 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
],
),
// user email
// Only show email if the user is authenticated and not using local auth
if (isAuthEnabled &&
state.userProfile.authenticator != AuthenticatorPB.Local) ...[
// Account section (email or login)
if (isAuthEnabled) ...[
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) ...[
SettingsCategory(
title: LocaleKeys.settings_accountPage_login_title.tr(),
title:
state.userProfile.authenticator != AuthenticatorPB.Local
? LocaleKeys.settings_accountPage_email_title.tr()
: LocaleKeys.settings_accountPage_login_title.tr(),
children: [
if (state.userProfile.authenticator !=
AuthenticatorPB.Local)
FlowyText.regular(state.userProfile.email),
AccountSignInOutSection(
userProfile: state.userProfile,
onAction: state.userProfile.authenticator ==

View file

@ -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:

View file

@ -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);

View file

@ -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

View file

@ -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<String, String>,

View file

@ -109,6 +109,7 @@ 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);
configuration.write_env();
@ -131,6 +132,7 @@ 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() {

View file

@ -26,6 +26,7 @@ pub struct AppFlowyCoreConfig {
/// choose a custom path for the user data, the storage_path will be different from
/// the origin_application_path.
pub application_path: String,
pub is_anon: bool,
pub(crate) log_filter: String,
pub cloud_config: Option<AFCloudConfiguration>,
}
@ -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.
@ -130,13 +134,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 +158,7 @@ impl AppFlowyCoreConfig {
device_id,
platform,
log_filter,
is_anon,
cloud_config,
}
}

View file

@ -9,6 +9,7 @@ use flowy_folder::manager::FolderManager;
use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_search::services::manager::SearchManager;
use flowy_server::af_cloud::define::ServerUser;
use std::path::Path;
use std::sync::{Arc, Weak};
use std::time::Duration;
use sysinfo::System;
@ -96,7 +97,26 @@ impl AppFlowyCore {
);
}
Self::init(config, runtime).await
let is_anon = config.is_anon;
let create_anon = if is_anon {
!Path::new(&config.storage_path).exists()
} else {
false
};
let this = Self::init(config, runtime).await;
if is_anon {
if create_anon {
if let Err(err) = this.user_manager.create_anon_user_once().await {
error!("Create anon user failed: {}", err);
}
} else {
if let Err(err) = this.user_manager.active_anon_user().await {
error!("Active anon user failed: {}", err);
}
}
}
this
}
pub fn close_db(&self) {

View file

@ -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();

View file

@ -552,7 +552,7 @@ pub async fn open_anon_user_handler(
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
manager.open_anon_user().await?;
manager.active_anon_user().await?;
Ok(())
}

View file

@ -580,6 +580,33 @@ 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<()> {
let anon_session = self.get_anon_session().await?;
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<UserProfile, FlowyError> {
select_user_profile(uid, self.db_connection(uid)?)

View file

@ -1,4 +1,3 @@
use std::sync::Arc;
use tracing::instrument;
use crate::entities::UserProfilePB;
@ -43,6 +42,14 @@ impl UserManager {
}
pub async fn get_anon_user(&self) -> FlowyResult<UserProfilePB> {
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))
}
pub async fn get_anon_session(&self) -> FlowyResult<Session> {
let anon_session = self
.store_preferences
.get_object::<Session>(ANON_USER)
@ -50,26 +57,6 @@ impl UserManager {
ErrorCode::RecordNotFound,
"Anon user not found",
))?;
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<()> {
let anon_session = self
.store_preferences
.get_object::<Arc<Session>>(ANON_USER)
.ok_or(FlowyError::new(
ErrorCode::RecordNotFound,
"Anon user not found",
))?;
self.authenticate_user.set_session(Some(anon_session))?;
Ok(())
Ok(anon_session)
}
}