Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d5946e75c | ||
|
|
f28a299786 | ||
|
|
4b0d61b534 | ||
|
|
cf8fb19976 | ||
|
|
1525a363f2 | ||
|
|
5a99da9a45 | ||
|
|
495226fa2c | ||
|
|
7ffd459875 |
No files matched your search
Vendored
+124
-124
@@ -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]",
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -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>();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-21
@@ -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({
|
||||
|
||||
-33
@@ -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<void> _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<AuthService>().signOut();
|
||||
await runAppFlowy();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeleteAccountBottomSheet extends StatefulWidget {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<void> 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(),
|
||||
|
||||
@@ -120,18 +120,6 @@ GoRouter generateRouter(Widget child) {
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: SignUpScreen.routeName,
|
||||
pageBuilder: (context, state) {
|
||||
return CustomTransitionPage(
|
||||
child: SignUpScreen(
|
||||
router: getIt<AuthRouter>(),
|
||||
),
|
||||
transitionsBuilder: _buildFadeTransition,
|
||||
transitionDuration: _slowDuration,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
// 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<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,10 @@ Future<Directory> appFlowyApplicationDataDirectory() async {
|
||||
return Directory(path.join(Directory.current.path, '.sandbox'));
|
||||
}
|
||||
}
|
||||
|
||||
/// The anon directory.
|
||||
Future<Directory> appFlowyAnonDirectory() async {
|
||||
final Directory documentsDir =
|
||||
await getApplicationSupportDirectory().then((directory) => directory);
|
||||
return Directory(path.join(documentsDir.path, 'anon'));
|
||||
}
|
||||
@@ -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<AnonUserEvent, AnonUserState> {
|
||||
AnonUserBloc() : super(AnonUserState.initial()) {
|
||||
on<AnonUserEvent>((event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
await _loadHistoricalUsers();
|
||||
},
|
||||
initial: () async {},
|
||||
didLoadAnonUsers: (List<UserProfilePB> anonUsers) {
|
||||
emit(state.copyWith(anonUsers: anonUsers));
|
||||
},
|
||||
@@ -24,20 +20,6 @@ class AnonUserBloc extends Bloc<AnonUserEvent, AnonUserState> {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadHistoricalUsers() async {
|
||||
final result = await UserBackendService.getAnonUser();
|
||||
result.fold(
|
||||
(anonUser) {
|
||||
add(AnonUserEvent.didLoadAnonUsers([anonUser]));
|
||||
},
|
||||
(error) {
|
||||
if (error.code != ErrorCode.RecordNotFound) {
|
||||
Log.error(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
||||
@@ -94,10 +94,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
|
||||
|
||||
+12
-3
@@ -29,7 +29,16 @@ class AppFlowyCloudMockAuthService implements AuthService {
|
||||
required String password,
|
||||
Map<String, String> 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<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
|
||||
Future<void> signUpAsGuest({
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
return _appFlowyAuthService.signUpAsGuest();
|
||||
await _appFlowyAuthService.signUpAsGuest();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -61,7 +61,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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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<String, String> 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<FlowyResult<UserProfilePB, FlowyError>> signUpAsGuest({
|
||||
Future<void> signUpAsGuest({
|
||||
Map<String, String> 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
|
||||
|
||||
@@ -251,16 +251,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) {
|
||||
|
||||
@@ -21,10 +21,6 @@ class AuthRouter {
|
||||
getIt<SplashRouter>().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
|
||||
|
||||
@@ -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';
|
||||
+4
-6
@@ -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(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SignInAnonymousButtonV3 extends StatelessWidget {
|
||||
},
|
||||
child: BlocBuilder<AnonUserBloc, AnonUserState>(
|
||||
builder: (context, state) {
|
||||
final text = LocaleKeys.signUp_getStartedText.tr();
|
||||
final text = LocaleKeys.signIn_anonymousMode.tr();
|
||||
final onTap = state.anonUsers.isEmpty
|
||||
? () {
|
||||
context
|
||||
|
||||
+2
-4
@@ -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(
|
||||
|
||||
@@ -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<SignUpBloc>(),
|
||||
child: BlocListener<SignUpBloc, SignUpState>(
|
||||
listener: (context, state) {
|
||||
final successOrFail = state.successOrFail;
|
||||
if (successOrFail != null) {
|
||||
_handleSuccessOrFail(context, successOrFail);
|
||||
}
|
||||
},
|
||||
child: const Scaffold(body: SignUpForm()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleSuccessOrFail(
|
||||
BuildContext context,
|
||||
FlowyResult<UserProfilePB, FlowyError> 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<SignUpBloc>().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<SignUpBloc>()
|
||||
.add(const SignUpEvent.signUpWithUserEmailAndPassword());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordTextField extends StatelessWidget {
|
||||
const PasswordTextField({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpBloc, SignUpState>(
|
||||
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<SignUpBloc>().state.passwordError ?? '',
|
||||
onChanged: (value) => context
|
||||
.read<SignUpBloc>()
|
||||
.add(SignUpEvent.passwordChanged(value)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RepeatPasswordTextField extends StatelessWidget {
|
||||
const RepeatPasswordTextField({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpBloc, SignUpState>(
|
||||
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<SignUpBloc>().state.repeatPasswordError ?? '',
|
||||
onChanged: (value) => context
|
||||
.read<SignUpBloc>()
|
||||
.add(SignUpEvent.repeatPasswordChanged(value)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EmailTextField extends StatelessWidget {
|
||||
const EmailTextField({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignUpBloc, SignUpState>(
|
||||
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<SignUpBloc>().state.emailError ?? '',
|
||||
onChanged: (value) =>
|
||||
context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ class _MobileWorkspaceStartScreenState
|
||||
selectedWorkspace!,
|
||||
);
|
||||
},
|
||||
child: Text(LocaleKeys.signUp_getStartedText.tr()),
|
||||
child: Text(LocaleKeys.signIn_anonymousMode.tr()),
|
||||
),
|
||||
const VSpace(spacing),
|
||||
],
|
||||
|
||||
+16
-14
@@ -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<AuthService>().signOut();
|
||||
onAction();
|
||||
onSignOut();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+39
-32
@@ -67,41 +67,27 @@ 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) ...[
|
||||
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<SettingsAccountView> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
-5
@@ -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:
|
||||
|
||||
-2
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ class AppFlowyUnitTest {
|
||||
final test = AppFlowyUnitTest();
|
||||
await test._signIn();
|
||||
await test._loadWorkspace();
|
||||
|
||||
await test._initialServices();
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -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?",
|
||||
|
||||
Generated
+3
-3
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
@@ -203,7 +203,7 @@ impl AppFlowyCollabBuilder {
|
||||
folder_data: Option<FolderData>,
|
||||
) -> Result<Arc<RwLock<Folder>>, 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?;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -85,6 +85,7 @@ impl EventIntegrationTest {
|
||||
device_id,
|
||||
"test".to_string(),
|
||||
name,
|
||||
false,
|
||||
)
|
||||
.log_filter(
|
||||
"trace",
|
||||
|
||||
@@ -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;
|
||||
@@ -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<WatchDiskEvent>)> {
|
||||
use notify::{Event, Watcher};
|
||||
|
||||
@@ -50,11 +45,5 @@ pub fn watch_offline_app() -> FlowyResult<(WatchContext, UnboundedReceiver<Watch
|
||||
.watch(&install_path, notify::RecursiveMode::NonRecursive)
|
||||
.map_err(|err| FlowyError::internal().with_context(err))?;
|
||||
|
||||
Ok((
|
||||
WatchContext {
|
||||
watcher,
|
||||
path: install_path,
|
||||
},
|
||||
rx,
|
||||
))
|
||||
Ok((WatchContext { watcher }, rx))
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use base64::Engine;
|
||||
use semver::Version;
|
||||
@@ -9,7 +9,6 @@ use url::Url;
|
||||
use crate::log_filter::create_log_filter;
|
||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_user::services::entities::URL_SAFE_ENGINE;
|
||||
use lib_infra::file_util::copy_dir_recursive;
|
||||
use lib_infra::util::OperatingSystem;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -26,6 +25,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>,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AFPluginRuntime>) -> 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));
|
||||
|
||||
@@ -175,7 +175,7 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, err)]
|
||||
#[instrument(level = "info", skip_all, err)]
|
||||
pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>(
|
||||
&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
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
|
||||
@@ -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();
|
||||
@@ -567,7 +567,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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -62,6 +62,12 @@ pub struct UserManager {
|
||||
pub(crate) is_loading_awareness: Arc<DashMap<Uuid, bool>>,
|
||||
}
|
||||
|
||||
impl Drop for UserManager {
|
||||
fn drop(&mut self) {
|
||||
info!("UserManager is dropped");
|
||||
}
|
||||
}
|
||||
|
||||
impl UserManager {
|
||||
pub fn new(
|
||||
cloud_services: Arc<dyn UserCloudServiceProvider>,
|
||||
@@ -403,7 +409,7 @@ impl UserManager {
|
||||
params: BoxAny,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
// 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<UserProfile, FlowyError> {
|
||||
select_user_profile(uid, self.db_connection(uid)?)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user