mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
test: add supabase auth tests (#3250)
* test: add supabase auth tests * chore: format before test * chore: fix warnings * ci: rust test * chore: disable clicking get started button when logining through the google OAuth
This commit is contained in:
parent
12d6cbd46a
commit
77637ff461
@ -0,0 +1,85 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('auth', () {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
tester.expectToSeeHomePage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_OK.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_OK.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapSignInAsGuest();
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
await tester.openSettings();
|
||||
await tester.expectNoSettingsPage(SettingsPage.syncSetting);
|
||||
});
|
||||
|
||||
testWidgets('enable encryption', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
|
||||
// the switch should be off by default
|
||||
tester.assertEnableEncryptSwitchValue(false);
|
||||
await tester.toggleEnableEncrypt();
|
||||
|
||||
// the switch should be on after toggling
|
||||
tester.assertEnableEncryptSwitchValue(true);
|
||||
|
||||
// the switch can not be toggled back to off
|
||||
await tester.toggleEnableEncrypt();
|
||||
tester.assertEnableEncryptSwitchValue(true);
|
||||
});
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync();
|
||||
|
||||
// the switch should be off
|
||||
tester.assertEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'database_calendar_test.dart' as database_calendar_test;
|
||||
@ -19,6 +20,7 @@ import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'tabs_test.dart' as tabs_test;
|
||||
import 'hotkeys_test.dart' as hotkeys_test;
|
||||
import 'appearance_settings_test.dart' as appearance_test_runner;
|
||||
import 'auth/auth_test.dart' as auth_test_runner;
|
||||
|
||||
/// The main task runner for all integration tests in AppFlowy.
|
||||
///
|
||||
@ -63,6 +65,10 @@ void main() {
|
||||
// Appearance integration test
|
||||
appearance_test_runner.main();
|
||||
|
||||
if (isSupabaseEnabled) {
|
||||
auth_test_runner.main();
|
||||
}
|
||||
|
||||
// board_test.main();
|
||||
// empty_document_test.main();
|
||||
// smart_menu_test.main();
|
||||
|
@ -0,0 +1,64 @@
|
||||
import 'package:appflowy/user/presentation/sign_in_screen.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/sync_setting_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
await tapButton(find.byType(GoogleSignUpButton));
|
||||
}
|
||||
|
||||
Future<void> tapSignInAsGuest() async {
|
||||
await tapButton(find.byType(SignInAsGuestButton));
|
||||
}
|
||||
|
||||
void expectToSeeGoogleLoginButton() {
|
||||
expect(find.byType(GoogleSignUpButton), findsOneWidget);
|
||||
}
|
||||
|
||||
void assertSwitchValue(Finder finder, bool value) {
|
||||
final Switch switchWidget = widget(finder);
|
||||
final isSwitched = switchWidget.value;
|
||||
assert(isSwitched == value);
|
||||
}
|
||||
|
||||
void assertEnableEncryptSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(EnableEncrypt),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
void assertEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(EnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> toggleEnableEncrypt() async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(EnableEncrypt),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
);
|
||||
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> toggleEnableSync() async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(EnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
);
|
||||
|
||||
await tapButton(finder);
|
||||
}
|
||||
}
|
@ -29,6 +29,14 @@ extension AppFlowySettings on WidgetTester {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> expectNoSettingsPage(SettingsPage page) async {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is SettingsMenuElement && widget.page == page,
|
||||
);
|
||||
expect(button, findsNothing);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Restore the AppFlowy data storage location
|
||||
Future<void> restoreLocation() async {
|
||||
final button =
|
||||
|
@ -6,3 +6,4 @@ export 'expectation.dart';
|
||||
export 'editor_test_operations.dart';
|
||||
export 'mock/mock_url_launcher.dart';
|
||||
export 'ime.dart';
|
||||
export 'auth_operation.dart';
|
||||
|
2
frontend/appflowy_flutter/lib/env/env.dart
vendored
2
frontend/appflowy_flutter/lib/env/env.dart
vendored
@ -40,7 +40,7 @@ abstract class Env {
|
||||
|
||||
bool get isSupabaseEnabled {
|
||||
// Only enable supabase in release and develop mode.
|
||||
if (integrationEnv().isRelease || integrationEnv().isDevelop) {
|
||||
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||
return Env.supabaseUrl.isNotEmpty &&
|
||||
Env.supabaseAnonKey.isNotEmpty &&
|
||||
Env.supabaseJwtSecret.isNotEmpty;
|
||||
|
@ -12,6 +12,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/ser
|
||||
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/supabase_auth_service.dart';
|
||||
import 'package:appflowy/user/application/prelude.dart';
|
||||
import 'package:appflowy/user/application/user_listener.dart';
|
||||
@ -38,7 +39,7 @@ class DependencyResolver {
|
||||
GetIt getIt,
|
||||
IntegrationMode mode,
|
||||
) async {
|
||||
_resolveUserDeps(getIt);
|
||||
_resolveUserDeps(getIt, mode);
|
||||
_resolveHomeDeps(getIt);
|
||||
_resolveFolderDeps(getIt);
|
||||
_resolveDocDeps(getIt);
|
||||
@ -86,9 +87,13 @@ void _resolveCommonService(
|
||||
);
|
||||
}
|
||||
|
||||
void _resolveUserDeps(GetIt getIt) {
|
||||
void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
|
||||
if (isSupabaseEnabled) {
|
||||
getIt.registerFactory<AuthService>(() => SupabaseAuthService());
|
||||
if (mode.isIntegrationTest) {
|
||||
getIt.registerFactory<AuthService>(() => MockAuthService());
|
||||
} else {
|
||||
getIt.registerFactory<AuthService>(() => SupabaseAuthService());
|
||||
}
|
||||
} else {
|
||||
getIt.registerFactory<AuthService>(() => AppFlowyAuthService());
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class FlowyRunnerContext {
|
||||
Future<void> runAppFlowy() async {
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationEnv(),
|
||||
integrationMode(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ class FlowyRunner {
|
||||
|
||||
Future<void> initGetIt(
|
||||
GetIt getIt,
|
||||
IntegrationMode env,
|
||||
IntegrationMode mode,
|
||||
EntryPoint f,
|
||||
LaunchConfiguration config,
|
||||
) async {
|
||||
@ -98,14 +98,14 @@ Future<void> initGetIt(
|
||||
() => AppLauncher(
|
||||
context: LaunchContext(
|
||||
getIt,
|
||||
env,
|
||||
mode,
|
||||
config,
|
||||
),
|
||||
),
|
||||
);
|
||||
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
|
||||
|
||||
await DependencyResolver.resolve(getIt, env);
|
||||
await DependencyResolver.resolve(getIt, mode);
|
||||
}
|
||||
|
||||
class LaunchContext {
|
||||
@ -171,7 +171,7 @@ enum IntegrationMode {
|
||||
bool get isDevelop => this == IntegrationMode.develop;
|
||||
}
|
||||
|
||||
IntegrationMode integrationEnv() {
|
||||
IntegrationMode integrationMode() {
|
||||
if (Platform.environment.containsKey('FLUTTER_TEST')) {
|
||||
return IntegrationMode.unitTest;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ AppFlowyEnv getAppFlowyEnv() {
|
||||
/// The default directory to store the user data. The directory can be
|
||||
/// customized by the user via the [ApplicationDataStorage]
|
||||
Future<Directory> appFlowyApplicationDataDirectory() async {
|
||||
switch (integrationEnv()) {
|
||||
switch (integrationMode()) {
|
||||
case IntegrationMode.develop:
|
||||
final Directory documentsDir = await getApplicationSupportDirectory()
|
||||
..create();
|
||||
|
@ -19,7 +19,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Local,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
final request = SignInPayloadPB.create()
|
||||
..email = email
|
||||
@ -36,7 +36,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Local,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
final request = SignUpPayloadPB.create()
|
||||
..name = name
|
||||
@ -53,7 +53,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
@override
|
||||
Future<void> signOut({
|
||||
AuthTypePB authType = AuthTypePB.Local,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
await UserEventSignOut().send();
|
||||
return;
|
||||
@ -62,7 +62,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
|
||||
AuthTypePB authType = AuthTypePB.Local,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) {
|
||||
const password = "Guest!@123456";
|
||||
final uid = uuid();
|
||||
@ -78,7 +78,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
|
||||
required String platform,
|
||||
AuthTypePB authType = AuthTypePB.Local,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
return left(
|
||||
FlowyError.create()
|
||||
@ -95,7 +95,7 @@ class AppFlowyAuthService implements AuthService {
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||
required String email,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
return left(
|
||||
FlowyError.create()
|
||||
|
@ -18,7 +18,7 @@ abstract class AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType,
|
||||
Map<String, String> map,
|
||||
Map<String, String> params,
|
||||
});
|
||||
|
||||
/// Returns [UserProfilePB] if the user is authenticated, otherwise returns [FlowyError].
|
||||
@ -27,25 +27,25 @@ abstract class AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType,
|
||||
Map<String, String> map,
|
||||
Map<String, String> params,
|
||||
});
|
||||
|
||||
///
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
|
||||
required String platform,
|
||||
AuthTypePB authType,
|
||||
Map<String, String> map,
|
||||
Map<String, String> params,
|
||||
});
|
||||
|
||||
/// Returns a default [UserProfilePB]
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
|
||||
AuthTypePB authType,
|
||||
Map<String, String> map,
|
||||
Map<String, String> params,
|
||||
});
|
||||
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||
required String email,
|
||||
Map<String, String> map,
|
||||
Map<String, String> params,
|
||||
});
|
||||
|
||||
///
|
||||
|
@ -8,7 +8,7 @@ import 'package:flutter/services.dart';
|
||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
|
||||
Future<String> getDeviceId() async {
|
||||
if (integrationEnv().isTest) {
|
||||
if (integrationMode().isTest) {
|
||||
return "test_device_id";
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,110 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/user/application/auth/appflowy_auth_service.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';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:nanoid/nanoid.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
import 'auth_error.dart';
|
||||
|
||||
/// Only used for testing.
|
||||
class MockAuthService implements AuthService {
|
||||
MockAuthService();
|
||||
|
||||
SupabaseClient get _client => Supabase.instance.client;
|
||||
GoTrueClient get _auth => _client.auth;
|
||||
|
||||
final AppFlowyAuthService _appFlowyAuthService = AppFlowyAuthService();
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUp({
|
||||
required String name,
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
|
||||
required String platform,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
try {
|
||||
final response = await _auth.signUp(
|
||||
email: "${nanoid(10)}@appflowy.io",
|
||||
password: "AppFlowyTest123!",
|
||||
);
|
||||
|
||||
final uuid = response.user!.id;
|
||||
final email = response.user!.email!;
|
||||
|
||||
final payload = ThirdPartyAuthPB(
|
||||
authType: AuthTypePB.Supabase,
|
||||
map: {
|
||||
AuthServiceMapKeys.uuid: uuid,
|
||||
AuthServiceMapKeys.email: email,
|
||||
AuthServiceMapKeys.deviceId: 'MockDeviceId'
|
||||
},
|
||||
);
|
||||
return UserEventThirdPartyAuth(payload)
|
||||
.send()
|
||||
.then((value) => value.swap());
|
||||
} on AuthException catch (e) {
|
||||
Log.error(e);
|
||||
return Left(AuthError.supabaseSignInError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signOut({
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
}) async {
|
||||
await _auth.signOut();
|
||||
await _appFlowyAuthService.signOut(
|
||||
authType: authType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
// supabase don't support guest login.
|
||||
// so, just forward to our backend.
|
||||
return _appFlowyAuthService.signUpAsGuest();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||
required String email,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
||||
return UserBackendService.getCurrentUserProfile();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/startup/tasks/prelude.dart';
|
||||
import 'package:appflowy/user/application/auth/appflowy_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
@ -29,16 +28,8 @@ class SupabaseAuthService implements AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
if (!isSupabaseEnabled) {
|
||||
return _appFlowyAuthService.signUp(
|
||||
name: name,
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
}
|
||||
|
||||
// fetch the uuid from supabase.
|
||||
final response = await _auth.signUp(
|
||||
email: email,
|
||||
@ -55,7 +46,7 @@ class SupabaseAuthService implements AuthService {
|
||||
email: email,
|
||||
password: password,
|
||||
authType: authType,
|
||||
map: {
|
||||
params: {
|
||||
AuthServiceMapKeys.uuid: uuid,
|
||||
},
|
||||
);
|
||||
@ -66,15 +57,8 @@ class SupabaseAuthService implements AuthService {
|
||||
required String email,
|
||||
required String password,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
if (!isSupabaseEnabled) {
|
||||
return _appFlowyAuthService.signIn(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _auth.signInWithPassword(
|
||||
email: email,
|
||||
@ -88,7 +72,7 @@ class SupabaseAuthService implements AuthService {
|
||||
email: email,
|
||||
password: password,
|
||||
authType: authType,
|
||||
map: {
|
||||
params: {
|
||||
AuthServiceMapKeys.uuid: uuid,
|
||||
},
|
||||
);
|
||||
@ -102,11 +86,8 @@ class SupabaseAuthService implements AuthService {
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
|
||||
required String platform,
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
if (!isSupabaseEnabled) {
|
||||
return _appFlowyAuthService.signUpWithOAuth(platform: platform);
|
||||
}
|
||||
// Before signing in, sign out any existing users. Otherwise, the callback will be triggered even if the user doesn't click the 'Sign In' button on the website
|
||||
if (_auth.currentUser != null) {
|
||||
await _auth.signOut();
|
||||
@ -115,7 +96,7 @@ class SupabaseAuthService implements AuthService {
|
||||
final provider = platform.toProvider();
|
||||
final completer = supabaseLoginCompleter(
|
||||
onSuccess: (userId, userEmail) async {
|
||||
return await setupAuth(
|
||||
return await _setupAuth(
|
||||
map: {
|
||||
AuthServiceMapKeys.uuid: userId,
|
||||
AuthServiceMapKeys.email: userEmail,
|
||||
@ -140,9 +121,7 @@ class SupabaseAuthService implements AuthService {
|
||||
Future<void> signOut({
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
}) async {
|
||||
if (isSupabaseEnabled) {
|
||||
await _auth.signOut();
|
||||
}
|
||||
await _auth.signOut();
|
||||
await _appFlowyAuthService.signOut(
|
||||
authType: authType,
|
||||
);
|
||||
@ -151,7 +130,7 @@ class SupabaseAuthService implements AuthService {
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
|
||||
AuthTypePB authType = AuthTypePB.Supabase,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
// supabase don't support guest login.
|
||||
// so, just forward to our backend.
|
||||
@ -161,11 +140,11 @@ class SupabaseAuthService implements AuthService {
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||
required String email,
|
||||
Map<String, String> map = const {},
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
final completer = supabaseLoginCompleter(
|
||||
onSuccess: (userId, userEmail) async {
|
||||
return await setupAuth(
|
||||
return await _setupAuth(
|
||||
map: {
|
||||
AuthServiceMapKeys.uuid: userId,
|
||||
AuthServiceMapKeys.email: userEmail,
|
||||
@ -195,7 +174,7 @@ class SupabaseAuthService implements AuthService {
|
||||
return Right(user);
|
||||
}
|
||||
|
||||
Future<Either<FlowyError, UserProfilePB>> setupAuth({
|
||||
Future<Either<FlowyError, UserProfilePB>> _setupAuth({
|
||||
required Map<String, String> map,
|
||||
}) async {
|
||||
final payload = ThirdPartyAuthPB(
|
||||
|
@ -222,46 +222,59 @@ class SignInAsGuestButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => HistoricalUserBloc()
|
||||
..add(
|
||||
const HistoricalUserEvent.initial(),
|
||||
),
|
||||
child: BlocListener<HistoricalUserBloc, HistoricalUserState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.openedHistoricalUser != current.openedHistoricalUser,
|
||||
listener: (context, state) async {
|
||||
await runAppFlowy();
|
||||
},
|
||||
child: BlocBuilder<HistoricalUserBloc, HistoricalUserState>(
|
||||
builder: (context, state) {
|
||||
if (state.historicalUsers.isEmpty) {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.signIn_loginAsGuestButtonText.tr(),
|
||||
height: 48,
|
||||
borderRadius: Corners.s6Border,
|
||||
onPressed: () {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInAsGuest());
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.signIn_continueAnonymousUser.tr(),
|
||||
height: 48,
|
||||
borderRadius: Corners.s6Border,
|
||||
onPressed: () {
|
||||
final bloc = context.read<HistoricalUserBloc>();
|
||||
final user = bloc.state.historicalUsers.first;
|
||||
bloc.add(HistoricalUserEvent.openHistoricalUser(user));
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
builder: (context, signInState) {
|
||||
return BlocProvider(
|
||||
create: (context) => HistoricalUserBloc()
|
||||
..add(
|
||||
const HistoricalUserEvent.initial(),
|
||||
),
|
||||
child: BlocListener<HistoricalUserBloc, HistoricalUserState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.openedHistoricalUser != current.openedHistoricalUser,
|
||||
listener: (context, state) async {
|
||||
await runAppFlowy();
|
||||
},
|
||||
child: BlocBuilder<HistoricalUserBloc, HistoricalUserState>(
|
||||
builder: (context, state) {
|
||||
final text = state.historicalUsers.isEmpty
|
||||
? FlowyText.medium(
|
||||
LocaleKeys.signIn_loginAsGuestButtonText.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: FlowyText.medium(
|
||||
LocaleKeys.signIn_continueAnonymousUser.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
final onTap = state.historicalUsers.isEmpty
|
||||
? () {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInAsGuest());
|
||||
}
|
||||
: () {
|
||||
final bloc = context.read<HistoricalUserBloc>();
|
||||
final user = bloc.state.historicalUsers.first;
|
||||
bloc.add(HistoricalUserEvent.openHistoricalUser(user));
|
||||
};
|
||||
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
child: FlowyButton(
|
||||
isSelected: true,
|
||||
disable: signInState.isSubmitting,
|
||||
text: text,
|
||||
radius: Corners.s6Border,
|
||||
onTap: onTap,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -410,16 +423,8 @@ class ThirdPartySignInButtons extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: mainAxisAlignment,
|
||||
children: [
|
||||
ThirdPartySignInButton(
|
||||
icon: FlowySvgs.google_mark_xl,
|
||||
onPressed: () {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('google'),
|
||||
);
|
||||
},
|
||||
),
|
||||
children: const [
|
||||
GoogleSignUpButton(),
|
||||
// const SizedBox(width: 20),
|
||||
// ThirdPartySignInButton(
|
||||
// icon: 'login/github-mark',
|
||||
@ -444,3 +449,20 @@ class ThirdPartySignInButtons extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GoogleSignUpButton extends StatelessWidget {
|
||||
const GoogleSignUpButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ThirdPartySignInButton(
|
||||
icon: FlowySvgs.google_mark_xl,
|
||||
onPressed: () {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('google'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||
Future<void> _relaunchAppAndAutoRegister() async {
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationEnv(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
|
@ -177,7 +177,7 @@ class _ChangeStoragePathButtonState extends State<_ChangeStoragePathButton> {
|
||||
await context.read<SettingsLocationCubit>().setCustomPath(path);
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationEnv(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
@ -252,7 +252,7 @@ class _RecoverDefaultStorageButtonState
|
||||
.resetDataStoragePathToApplicationDefault();
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationEnv(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
|
@ -82,8 +82,7 @@ class SettingsUserView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return _renderLogoutButton(context);
|
||||
return SettingLogoutButton(user: user, didLogout: didLogout);
|
||||
}
|
||||
|
||||
Widget _renderUserNameInput(BuildContext context) {
|
||||
@ -106,40 +105,6 @@ class SettingsUserView extends StatelessWidget {
|
||||
context.read<SettingsUserViewBloc>().state.userProfile.openaiKey;
|
||||
return _OpenaiKeyInput(openAIKey);
|
||||
}
|
||||
|
||||
Widget _renderLogoutButton(BuildContext context) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 160,
|
||||
child: FlowyButton(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 2.0),
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.settings_menu_logout.tr(),
|
||||
fontSize: 13,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
onTap: () async {
|
||||
NavigatorAlertDialog(
|
||||
title: logoutPromptMessage(),
|
||||
confirm: () async {
|
||||
await getIt<AuthService>().signOut();
|
||||
didLogout();
|
||||
},
|
||||
).show(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String logoutPromptMessage() {
|
||||
switch (user.encryptionType) {
|
||||
case EncryptionTypePB.Symmetric:
|
||||
return LocaleKeys.settings_menu_selfEncryptionLogoutPrompt.tr();
|
||||
default:
|
||||
return LocaleKeys.settings_menu_logoutPrompt.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
@ -409,3 +374,48 @@ class IconOption extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingLogoutButton extends StatelessWidget {
|
||||
final UserProfilePB user;
|
||||
final VoidCallback didLogout;
|
||||
const SettingLogoutButton({
|
||||
required this.user,
|
||||
required this.didLogout,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 160,
|
||||
child: FlowyButton(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 2.0),
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.settings_menu_logout.tr(),
|
||||
fontSize: 13,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
onTap: () async {
|
||||
NavigatorAlertDialog(
|
||||
title: logoutPromptMessage(),
|
||||
confirm: () async {
|
||||
await getIt<AuthService>().signOut();
|
||||
didLogout();
|
||||
},
|
||||
).show(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String logoutPromptMessage() {
|
||||
switch (user.encryptionType) {
|
||||
case EncryptionTypePB.Symmetric:
|
||||
return LocaleKeys.settings_menu_selfEncryptionLogoutPrompt.tr();
|
||||
default:
|
||||
return LocaleKeys.settings_menu_logoutPrompt.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,30 +43,26 @@ class FlowyButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!disable) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
child: FlowyHover(
|
||||
style: HoverStyle(
|
||||
borderRadius: radius ?? Corners.s6Border,
|
||||
hoverColor: hoverColor ?? Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
onHover: onHover,
|
||||
isSelected: () => isSelected,
|
||||
builder: (context, onHover) => _render(),
|
||||
final color = hoverColor ?? Theme.of(context).colorScheme.secondary;
|
||||
final alpha = (255 * disableOpacity).toInt();
|
||||
color.withAlpha(alpha);
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: disable ? null : onTap,
|
||||
onSecondaryTap: disable ? null : onSecondaryTap,
|
||||
child: FlowyHover(
|
||||
cursor:
|
||||
disable ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
|
||||
style: HoverStyle(
|
||||
borderRadius: radius ?? Corners.s6Border,
|
||||
hoverColor: color,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Opacity(
|
||||
opacity: disableOpacity,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.forbidden,
|
||||
child: _render(),
|
||||
),
|
||||
);
|
||||
}
|
||||
onHover: disable ? null : onHover,
|
||||
isSelected: () => isSelected,
|
||||
builder: (context, onHover) => _render(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _render() {
|
||||
|
@ -1,5 +1,8 @@
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
||||
|
||||
#[target.aarch64-apple-darwin]
|
||||
#BINDGEN_EXTRA_CLANG_ARGS="--target=aarch64-apple-darwin"
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-mmacosx-version-min=11.0"]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-mmacosx-version-min=11.0"]
|
||||
|
Loading…
Reference in New Issue
Block a user