mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: implement magic link login (#3086)
* feat: implement magic link login * ci: create env file * ci: generate flutter env files * ci: disable inject env * chore: update table name * Update frontend/appflowy_flutter/lib/env/env.dart Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> * chore: fix compile --------- Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
parent
a1143e24f3
commit
ea0c4e96d2
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -20,6 +20,14 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# - name: Create .env file
|
||||||
|
# working-directory: frontend/appflowy_flutter
|
||||||
|
# run: |
|
||||||
|
# touch .env
|
||||||
|
# echo SUPABASE_URL=${{ secrets.HOST_URL }} >> .env
|
||||||
|
# echo SUPABASE_ANON_KEY=${{ secrets.HOST_ANON_KEY }} >> .env
|
||||||
|
# echo SUPABASE_JWT_SECRET=${{ secrets.HOST_JWT_SECRET }} >> .env
|
||||||
|
|
||||||
- name: Build release notes
|
- name: Build release notes
|
||||||
run: |
|
run: |
|
||||||
touch ${{ env.RELEASE_NOTES_PATH }}
|
touch ${{ env.RELEASE_NOTES_PATH }}
|
||||||
|
20
frontend/appflowy_flutter/lib/env/env.dart
vendored
20
frontend/appflowy_flutter/lib/env/env.dart
vendored
@ -1,4 +1,5 @@
|
|||||||
// lib/env/env.dart
|
// lib/env/env.dart
|
||||||
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:envied/envied.dart';
|
import 'package:envied/envied.dart';
|
||||||
|
|
||||||
part 'env.g.dart';
|
part 'env.g.dart';
|
||||||
@ -37,12 +38,13 @@ abstract class Env {
|
|||||||
static final String supabaseJwtSecret = _Env.supabaseJwtSecret;
|
static final String supabaseJwtSecret = _Env.supabaseJwtSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isSupabaseEnable => false;
|
bool get isSupabaseEnabled {
|
||||||
// Env.supabaseUrl.isNotEmpty &&
|
// Only enable supabase in release and develop mode.
|
||||||
// Env.supabaseAnonKey.isNotEmpty &&
|
if (integrationEnv().isRelease || integrationEnv().isDevelop) {
|
||||||
// Env.supabaseKey.isNotEmpty &&
|
return Env.supabaseUrl.isNotEmpty &&
|
||||||
// Env.supabaseJwtSecret.isNotEmpty &&
|
Env.supabaseAnonKey.isNotEmpty &&
|
||||||
// Env.supabaseDb.isNotEmpty &&
|
Env.supabaseJwtSecret.isNotEmpty;
|
||||||
// Env.supabaseDbUser.isNotEmpty &&
|
} else {
|
||||||
// Env.supabaseDbPassword.isNotEmpty &&
|
return false;
|
||||||
// Env.supabaseDbPort.isNotEmpty;
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/network_monitor.dart';
|
import 'package:appflowy/core/network_monitor.dart';
|
||||||
|
import 'package:appflowy/env/env.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_action_sheet_bloc.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_action_sheet_bloc.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||||
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
|
||||||
@ -82,8 +83,11 @@ void _resolveCommonService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _resolveUserDeps(GetIt getIt) {
|
void _resolveUserDeps(GetIt getIt) {
|
||||||
// getIt.registerFactory<AuthService>(() => AppFlowyAuthService());
|
if (isSupabaseEnabled) {
|
||||||
getIt.registerFactory<AuthService>(() => SupabaseAuthService());
|
getIt.registerFactory<AuthService>(() => SupabaseAuthService());
|
||||||
|
} else {
|
||||||
|
getIt.registerFactory<AuthService>(() => AppFlowyAuthService());
|
||||||
|
}
|
||||||
|
|
||||||
getIt.registerFactory<AuthRouter>(() => AuthRouter());
|
getIt.registerFactory<AuthRouter>(() => AuthRouter());
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ bool isSupabaseInitialized = false;
|
|||||||
class InitSupabaseTask extends LaunchTask {
|
class InitSupabaseTask extends LaunchTask {
|
||||||
@override
|
@override
|
||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
if (!isSupabaseEnable) {
|
if (!isSupabaseEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,8 @@ class InitSupabaseTask extends LaunchTask {
|
|||||||
await Supabase.initialize(
|
await Supabase.initialize(
|
||||||
url: Env.supabaseUrl,
|
url: Env.supabaseUrl,
|
||||||
anonKey: Env.supabaseAnonKey,
|
anonKey: Env.supabaseAnonKey,
|
||||||
debug: false,
|
debug: true,
|
||||||
|
// authFlowType: AuthFlowType.pkce,
|
||||||
);
|
);
|
||||||
|
|
||||||
isSupabaseInitialized = true;
|
isSupabaseInitialized = true;
|
||||||
|
@ -75,12 +75,28 @@ class AppFlowyAuthService implements AuthService {
|
|||||||
required String platform,
|
required String platform,
|
||||||
AuthTypePB authType = AuthTypePB.Local,
|
AuthTypePB authType = AuthTypePB.Local,
|
||||||
Map<String, String> map = const {},
|
Map<String, String> map = const {},
|
||||||
}) {
|
}) async {
|
||||||
throw UnimplementedError();
|
return left(
|
||||||
|
FlowyError.create()
|
||||||
|
..code = 0
|
||||||
|
..msg = "Unsupported sign up action",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
||||||
return UserBackendService.getCurrentUserProfile();
|
return UserBackendService.getCurrentUserProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||||
|
required String email,
|
||||||
|
Map<String, String> map = const {},
|
||||||
|
}) async {
|
||||||
|
return left(
|
||||||
|
FlowyError.create()
|
||||||
|
..code = 0
|
||||||
|
..msg = "Unsupported sign up action",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,11 @@ abstract class AuthService {
|
|||||||
Map<String, String> map,
|
Map<String, String> map,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||||
|
required String email,
|
||||||
|
Map<String, String> map,
|
||||||
|
});
|
||||||
|
|
||||||
///
|
///
|
||||||
Future<void> signOut();
|
Future<void> signOut();
|
||||||
|
|
||||||
|
@ -9,9 +9,13 @@ import 'package:appflowy_backend/log.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
import 'auth_error.dart';
|
import 'auth_error.dart';
|
||||||
|
|
||||||
|
// can't use underscore here.
|
||||||
|
const loginCallback = 'io.appflowy.appflowy-flutter://login-callback';
|
||||||
|
|
||||||
class SupabaseAuthService implements AuthService {
|
class SupabaseAuthService implements AuthService {
|
||||||
SupabaseAuthService();
|
SupabaseAuthService();
|
||||||
|
|
||||||
@ -28,7 +32,7 @@ class SupabaseAuthService implements AuthService {
|
|||||||
AuthTypePB authType = AuthTypePB.Supabase,
|
AuthTypePB authType = AuthTypePB.Supabase,
|
||||||
Map<String, String> map = const {},
|
Map<String, String> map = const {},
|
||||||
}) async {
|
}) async {
|
||||||
if (!isSupabaseEnable) {
|
if (!isSupabaseEnabled) {
|
||||||
return _appFlowyAuthService.signUp(
|
return _appFlowyAuthService.signUp(
|
||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email,
|
||||||
@ -65,7 +69,7 @@ class SupabaseAuthService implements AuthService {
|
|||||||
AuthTypePB authType = AuthTypePB.Supabase,
|
AuthTypePB authType = AuthTypePB.Supabase,
|
||||||
Map<String, String> map = const {},
|
Map<String, String> map = const {},
|
||||||
}) async {
|
}) async {
|
||||||
if (!isSupabaseEnable) {
|
if (!isSupabaseEnabled) {
|
||||||
return _appFlowyAuthService.signIn(
|
return _appFlowyAuthService.signIn(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
@ -101,39 +105,25 @@ class SupabaseAuthService implements AuthService {
|
|||||||
AuthTypePB authType = AuthTypePB.Supabase,
|
AuthTypePB authType = AuthTypePB.Supabase,
|
||||||
Map<String, String> map = const {},
|
Map<String, String> map = const {},
|
||||||
}) async {
|
}) async {
|
||||||
if (!isSupabaseEnable) {
|
if (!isSupabaseEnabled) {
|
||||||
return _appFlowyAuthService.signUpWithOAuth(
|
return _appFlowyAuthService.signUpWithOAuth(platform: platform);
|
||||||
platform: platform,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
final provider = platform.toProvider();
|
final provider = platform.toProvider();
|
||||||
final completer = Completer<Either<FlowyError, UserProfilePB>>();
|
final completer = supabaseLoginCompleter(
|
||||||
late final StreamSubscription<AuthState> subscription;
|
onSuccess: (userId, userEmail) async {
|
||||||
subscription = _auth.onAuthStateChange.listen((event) async {
|
return await setupAuth(
|
||||||
final user = event.session?.user;
|
|
||||||
if (event.event != AuthChangeEvent.signedIn || user == null) {
|
|
||||||
completer.complete(left(AuthError.supabaseSignInWithOauthError));
|
|
||||||
} else {
|
|
||||||
final Either<FlowyError, UserProfilePB> response = await setupAuth(
|
|
||||||
map: {
|
map: {
|
||||||
AuthServiceMapKeys.uuid: user.id,
|
AuthServiceMapKeys.uuid: userId,
|
||||||
AuthServiceMapKeys.email: user.email ?? user.newEmail ?? ''
|
AuthServiceMapKeys.email: userEmail
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
completer.complete(response);
|
},
|
||||||
}
|
);
|
||||||
subscription.cancel();
|
|
||||||
});
|
|
||||||
final Map<String, String> query = {};
|
|
||||||
if (provider == Provider.google) {
|
|
||||||
query['access_type'] = 'offline';
|
|
||||||
query['prompt'] = 'consent';
|
|
||||||
}
|
|
||||||
final response = await _auth.signInWithOAuth(
|
final response = await _auth.signInWithOAuth(
|
||||||
provider,
|
provider,
|
||||||
queryParams: query,
|
queryParams: queryParamsForProvider(provider),
|
||||||
redirectTo:
|
redirectTo: loginCallback,
|
||||||
'io.appflowy.appflowy-flutter://login-callback', // can't use underscore here.
|
|
||||||
);
|
);
|
||||||
if (!response) {
|
if (!response) {
|
||||||
completer.complete(left(AuthError.supabaseSignInWithOauthError));
|
completer.complete(left(AuthError.supabaseSignInWithOauthError));
|
||||||
@ -145,7 +135,7 @@ class SupabaseAuthService implements AuthService {
|
|||||||
Future<void> signOut({
|
Future<void> signOut({
|
||||||
AuthTypePB authType = AuthTypePB.Supabase,
|
AuthTypePB authType = AuthTypePB.Supabase,
|
||||||
}) async {
|
}) async {
|
||||||
if (isSupabaseEnable) {
|
if (isSupabaseEnabled) {
|
||||||
await _auth.signOut();
|
await _auth.signOut();
|
||||||
}
|
}
|
||||||
await _appFlowyAuthService.signOut(
|
await _appFlowyAuthService.signOut(
|
||||||
@ -163,17 +153,28 @@ class SupabaseAuthService implements AuthService {
|
|||||||
return _appFlowyAuthService.signUpAsGuest();
|
return _appFlowyAuthService.signUpAsGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
@override
|
||||||
// Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||||
// final loginType = await getIt<KeyValueStorage>()
|
required String email,
|
||||||
// .get(KVKeys.loginType)
|
Map<String, String> map = const {},
|
||||||
// .then((value) => value.toOption().toNullable());
|
}) async {
|
||||||
// if (!isSupabaseEnable || (loginType != null && loginType != 'supabase')) {
|
final completer = supabaseLoginCompleter(
|
||||||
// return _appFlowyAuthService.getUser();
|
onSuccess: (userId, userEmail) async {
|
||||||
// }
|
return await setupAuth(
|
||||||
// final user = await getSupabaseUser();
|
map: {
|
||||||
// return user.map((r) => r.toUserProfile());
|
AuthServiceMapKeys.uuid: userId,
|
||||||
// }
|
AuthServiceMapKeys.email: userEmail
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await _auth.signInWithOtp(
|
||||||
|
email: email,
|
||||||
|
emailRedirectTo: kIsWeb ? null : loginCallback,
|
||||||
|
);
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
||||||
@ -215,3 +216,45 @@ extension on String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Completer<Either<FlowyError, UserProfilePB>> supabaseLoginCompleter({
|
||||||
|
required Future<Either<FlowyError, UserProfilePB>> Function(
|
||||||
|
String userId,
|
||||||
|
String userEmail,
|
||||||
|
) onSuccess,
|
||||||
|
}) {
|
||||||
|
final completer = Completer<Either<FlowyError, UserProfilePB>>();
|
||||||
|
late final StreamSubscription<AuthState> subscription;
|
||||||
|
final auth = Supabase.instance.client.auth;
|
||||||
|
|
||||||
|
subscription = auth.onAuthStateChange.listen((event) async {
|
||||||
|
final user = event.session?.user;
|
||||||
|
if (event.event != AuthChangeEvent.signedIn || user == null) {
|
||||||
|
completer.complete(left(AuthError.supabaseSignInWithOauthError));
|
||||||
|
} else {
|
||||||
|
final response = await onSuccess(
|
||||||
|
user.id,
|
||||||
|
user.email ?? user.newEmail ?? '',
|
||||||
|
);
|
||||||
|
completer.complete(response);
|
||||||
|
}
|
||||||
|
subscription.cancel();
|
||||||
|
});
|
||||||
|
return completer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> queryParamsForProvider(Provider provider) {
|
||||||
|
switch (provider) {
|
||||||
|
case Provider.github:
|
||||||
|
return {};
|
||||||
|
case Provider.google:
|
||||||
|
return {
|
||||||
|
'access_type': 'offline',
|
||||||
|
'prompt': 'consent',
|
||||||
|
};
|
||||||
|
case Provider.discord:
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -48,6 +48,9 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
signedWithMagicLink: (SignedWithMagicLink value) async {
|
||||||
|
await _performActionOnSignInWithMagicLink(state, emit, value.email);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -99,6 +102,34 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _performActionOnSignInWithMagicLink(
|
||||||
|
SignInState state,
|
||||||
|
Emitter<SignInState> emit,
|
||||||
|
String email,
|
||||||
|
) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isSubmitting: true,
|
||||||
|
emailError: none(),
|
||||||
|
passwordError: none(),
|
||||||
|
successOrFail: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await authService.signInWithMagicLink(
|
||||||
|
email: email,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
result.fold(
|
||||||
|
(error) => stateFromCode(error),
|
||||||
|
(userProfile) => state.copyWith(
|
||||||
|
isSubmitting: false,
|
||||||
|
successOrFail: some(left(userProfile)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _performActionOnSignInAsGuest(
|
Future<void> _performActionOnSignInAsGuest(
|
||||||
SignInState state,
|
SignInState state,
|
||||||
Emitter<SignInState> emit,
|
Emitter<SignInState> emit,
|
||||||
@ -154,6 +185,8 @@ class SignInEvent with _$SignInEvent {
|
|||||||
const factory SignInEvent.signedInWithOAuth(String platform) =
|
const factory SignInEvent.signedInWithOAuth(String platform) =
|
||||||
SignedInWithOAuth;
|
SignedInWithOAuth;
|
||||||
const factory SignInEvent.signedInAsGuest() = SignedInAsGuest;
|
const factory SignInEvent.signedInAsGuest() = SignedInAsGuest;
|
||||||
|
const factory SignInEvent.signedWithMagicLink(String email) =
|
||||||
|
SignedWithMagicLink;
|
||||||
const factory SignInEvent.emailChanged(String email) = EmailChanged;
|
const factory SignInEvent.emailChanged(String email) = EmailChanged;
|
||||||
const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
|
const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
|
||||||
}
|
}
|
||||||
|
@ -333,31 +333,31 @@ class ThirdPartySignInButtons extends StatelessWidget {
|
|||||||
icon: 'login/google-mark',
|
icon: 'login/google-mark',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||||
context
|
context.read<SignInBloc>().add(
|
||||||
.read<SignInBloc>()
|
const SignInEvent.signedInWithOAuth('google'),
|
||||||
.add(const SignInEvent.signedInWithOAuth('google'));
|
);
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
ThirdPartySignInButton(
|
|
||||||
icon: 'login/github-mark',
|
|
||||||
onPressed: () {
|
|
||||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
|
||||||
context
|
|
||||||
.read<SignInBloc>()
|
|
||||||
.add(const SignInEvent.signedInWithOAuth('github'));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
ThirdPartySignInButton(
|
|
||||||
icon: 'login/discord-mark',
|
|
||||||
onPressed: () {
|
|
||||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
|
||||||
context
|
|
||||||
.read<SignInBloc>()
|
|
||||||
.add(const SignInEvent.signedInWithOAuth('discord'));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// const SizedBox(width: 20),
|
||||||
|
// ThirdPartySignInButton(
|
||||||
|
// icon: 'login/github-mark',
|
||||||
|
// onPressed: () {
|
||||||
|
// getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||||
|
// context
|
||||||
|
// .read<SignInBloc>()
|
||||||
|
// .add(const SignInEvent.signedInWithOAuth('github'));
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// const SizedBox(width: 20),
|
||||||
|
// ThirdPartySignInButton(
|
||||||
|
// icon: 'login/discord-mark',
|
||||||
|
// onPressed: () {
|
||||||
|
// getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||||
|
// context
|
||||||
|
// .read<SignInBloc>()
|
||||||
|
// .add(const SignInEvent.signedInWithOAuth('discord'));
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ class SplashScreen extends StatelessWidget {
|
|||||||
|
|
||||||
void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
|
void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
|
||||||
// if the env is not configured, we will skip to the 'skip login screen'.
|
// if the env is not configured, we will skip to the 'skip login screen'.
|
||||||
if (isSupabaseEnable) {
|
if (isSupabaseEnabled) {
|
||||||
getIt<SplashRoute>().pushSignInScreen(context);
|
getIt<SplashRoute>().pushSignInScreen(context);
|
||||||
} else {
|
} else {
|
||||||
getIt<SplashRoute>().pushSkipLoginScreen(context);
|
getIt<SplashRoute>().pushSkipLoginScreen(context);
|
||||||
|
@ -60,7 +60,7 @@ class SettingsMenu extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Only show supabase setting if supabase is enabled and the current auth type is not local
|
// Only show supabase setting if supabase is enabled and the current auth type is not local
|
||||||
if (isSupabaseEnable &&
|
if (isSupabaseEnabled &&
|
||||||
context.read<SettingsDialogBloc>().state.userProfile.authType !=
|
context.read<SettingsDialogBloc>().state.userProfile.authType !=
|
||||||
AuthTypePB.Local)
|
AuthTypePB.Local)
|
||||||
SettingsMenuElement(
|
SettingsMenuElement(
|
||||||
|
@ -60,7 +60,7 @@ class SettingsUserView extends StatelessWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
SettingsUserState state,
|
SettingsUserState state,
|
||||||
) {
|
) {
|
||||||
if (!isSupabaseEnable) {
|
if (!isSupabaseEnabled) {
|
||||||
return _renderLogoutButton(context);
|
return _renderLogoutButton(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ async fn send_update(
|
|||||||
|
|
||||||
let params = builder.build();
|
let params = builder.build();
|
||||||
postgrest
|
postgrest
|
||||||
.from(&table_name(&object.ty))
|
.from(AF_COLLAB_UPDATE_TABLE)
|
||||||
.insert(params)
|
.insert(params)
|
||||||
.execute()
|
.execute()
|
||||||
.await?
|
.await?
|
||||||
|
17
frontend/scripts/code_generation/env/generate_env.cmd
vendored
Normal file
17
frontend/scripts/code_generation/env/generate_env.cmd
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
REM Store the current working directory
|
||||||
|
set "original_dir=%CD%"
|
||||||
|
|
||||||
|
REM Change the current working directory to the script's location
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
REM Navigate to the project root
|
||||||
|
cd ..\..\..\appflowy_flutter
|
||||||
|
|
||||||
|
REM Navigate to the appflowy_flutter directory and generate files
|
||||||
|
echo Generating env files
|
||||||
|
call flutter packages pub get >nul 2>&1 && call dart run build_runner clean && call dart run build_runner build --delete-conflicting-outputs
|
||||||
|
echo Done generating env files
|
||||||
|
|
||||||
|
cd /d "%original_dir%"
|
19
frontend/scripts/code_generation/env/generate_env.sh
vendored
Normal file
19
frontend/scripts/code_generation/env/generate_env.sh
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Store the current working directory
|
||||||
|
original_dir=$(pwd)
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
# Navigate to the project root
|
||||||
|
cd ../../../appflowy_flutter
|
||||||
|
|
||||||
|
# Navigate to the appflowy_flutter directory and generate files
|
||||||
|
echo "Generating env files"
|
||||||
|
# flutter clean >/dev/null 2>&1 && flutter packages pub get >/dev/null 2>&1 && dart run build_runner clean &&
|
||||||
|
flutter packages pub get >/dev/null 2>&1
|
||||||
|
dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
|
||||||
|
echo "Done generating env files"
|
||||||
|
|
||||||
|
# Return to the original directory
|
||||||
|
cd "$original_dir"
|
@ -22,6 +22,13 @@ cd freezed
|
|||||||
REM Allow execution permissions on CI
|
REM Allow execution permissions on CI
|
||||||
chmod +x generate_freezed.cmd
|
chmod +x generate_freezed.cmd
|
||||||
call generate_freezed.cmd %*
|
call generate_freezed.cmd %*
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo Generating env files using build_runner
|
||||||
|
cd env
|
||||||
|
REM Allow execution permissions on CI
|
||||||
|
chmod +x generate_env.cmd
|
||||||
|
call generate_env.cmd %*
|
||||||
|
|
||||||
REM Return to the original directory
|
REM Return to the original directory
|
||||||
cd /d "%original_dir%"
|
cd /d "%original_dir%"
|
||||||
|
@ -22,6 +22,13 @@ cd freezed
|
|||||||
# Allow execution permissions on CI
|
# Allow execution permissions on CI
|
||||||
chmod +x ./generate_freezed.sh
|
chmod +x ./generate_freezed.sh
|
||||||
./generate_freezed.sh "$@"
|
./generate_freezed.sh "$@"
|
||||||
|
cd..
|
||||||
|
|
||||||
|
echo "Generating env files using build_runner"
|
||||||
|
cd env
|
||||||
|
# Allow execution permissions on CI
|
||||||
|
chmod +x ./generate_env.sh
|
||||||
|
./generate_env.sh "$@"
|
||||||
|
|
||||||
# Return to the original directory
|
# Return to the original directory
|
||||||
cd "$original_dir"
|
cd "$original_dir"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user