2023-11-29 12:55:13 -08:00
|
|
|
import 'package:appflowy/env/cloud_env.dart';
|
|
|
|
import 'package:appflowy/startup/startup.dart';
|
|
|
|
import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart';
|
2023-05-21 18:53:59 +08:00
|
|
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
2023-01-08 12:10:53 +08:00
|
|
|
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
|
|
|
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
|
|
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
|
|
|
show UserProfilePB;
|
2024-01-09 16:09:44 +07:00
|
|
|
import 'package:dartz/dartz.dart';
|
2021-07-13 13:14:49 +08:00
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
2024-01-09 16:09:44 +07:00
|
|
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
2021-07-13 13:14:49 +08:00
|
|
|
|
|
|
|
part 'sign_in_bloc.freezed.dart';
|
|
|
|
|
|
|
|
class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
2024-01-25 16:37:36 +01:00
|
|
|
SignInBloc(this.authService) : super(SignInState.initial()) {
|
|
|
|
if (isAppFlowyCloudEnabled) {
|
|
|
|
deepLinkStateListener =
|
|
|
|
getIt<AppFlowyCloudDeepLink>().subscribeDeepLinkLoadingState((value) {
|
|
|
|
if (isClosed) return;
|
|
|
|
|
|
|
|
add(SignInEvent.deepLinkStateChange(value));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_dispatch();
|
|
|
|
}
|
|
|
|
|
2022-03-01 10:22:28 -05:00
|
|
|
final AuthService authService;
|
2023-11-29 12:55:13 -08:00
|
|
|
void Function()? deepLinkStateListener;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> close() {
|
|
|
|
deepLinkStateListener?.call();
|
|
|
|
if (isAppFlowyCloudEnabled && deepLinkStateListener != null) {
|
|
|
|
getIt<AppFlowyCloudDeepLink>().unsubscribeDeepLinkLoadingState(
|
|
|
|
deepLinkStateListener!,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return super.close();
|
|
|
|
}
|
|
|
|
|
2024-01-25 16:37:36 +01:00
|
|
|
void _dispatch() {
|
|
|
|
on<SignInEvent>(
|
|
|
|
(event, emit) async {
|
|
|
|
await event.map(
|
|
|
|
signedInWithUserEmailAndPassword: (e) async {
|
|
|
|
await _performActionOnSignIn(
|
|
|
|
state,
|
|
|
|
emit,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
signedInWithOAuth: (value) async =>
|
|
|
|
_performActionOnSignInWithOAuth(state, emit, value.platform),
|
|
|
|
signedInAsGuest: (value) async =>
|
|
|
|
_performActionOnSignInAsGuest(state, emit),
|
|
|
|
emailChanged: (EmailChanged value) async {
|
|
|
|
emit(
|
|
|
|
state.copyWith(
|
|
|
|
email: value.email,
|
|
|
|
emailError: none(),
|
|
|
|
successOrFail: none(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
passwordChanged: (PasswordChanged value) async {
|
|
|
|
emit(
|
|
|
|
state.copyWith(
|
|
|
|
password: value.password,
|
|
|
|
passwordError: none(),
|
|
|
|
successOrFail: none(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
signedWithMagicLink: (SignedWithMagicLink value) async {
|
|
|
|
await _performActionOnSignInWithMagicLink(state, emit, value.email);
|
|
|
|
},
|
|
|
|
deepLinkStateChange: (_DeepLinkStateChange value) {
|
|
|
|
final deepLinkState = value.result.state;
|
2023-11-29 12:55:13 -08:00
|
|
|
|
2024-01-25 16:37:36 +01:00
|
|
|
switch (deepLinkState) {
|
|
|
|
case DeepLinkState.none:
|
|
|
|
break;
|
|
|
|
case DeepLinkState.loading:
|
2023-11-29 12:55:13 -08:00
|
|
|
emit(
|
2024-01-25 16:37:36 +01:00
|
|
|
state.copyWith(
|
|
|
|
isSubmitting: true,
|
|
|
|
emailError: none(),
|
|
|
|
passwordError: none(),
|
|
|
|
successOrFail: none(),
|
2023-11-29 12:55:13 -08:00
|
|
|
),
|
|
|
|
);
|
2024-01-25 16:37:36 +01:00
|
|
|
case DeepLinkState.finish:
|
|
|
|
if (value.result.result != null) {
|
|
|
|
emit(
|
|
|
|
value.result.result!.fold(
|
|
|
|
(error) => stateFromCode(error),
|
|
|
|
(userProfile) => state.copyWith(
|
|
|
|
isSubmitting: false,
|
|
|
|
successOrFail: some(left(userProfile)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
cancel: (value) {
|
|
|
|
emit(
|
|
|
|
state.copyWith(
|
|
|
|
isSubmitting: false,
|
|
|
|
emailError: none(),
|
|
|
|
passwordError: none(),
|
|
|
|
successOrFail: none(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
2021-07-13 13:14:49 +08:00
|
|
|
}
|
|
|
|
|
2022-12-30 11:16:47 +08:00
|
|
|
Future<void> _performActionOnSignIn(
|
2023-04-10 15:10:42 +08:00
|
|
|
SignInState state,
|
|
|
|
Emitter<SignInState> emit,
|
2023-05-21 18:53:59 +08:00
|
|
|
) async {
|
2023-11-27 18:54:31 -08:00
|
|
|
final result = await authService.signInWithEmailPassword(
|
2023-05-21 18:53:59 +08:00
|
|
|
email: state.email ?? '',
|
|
|
|
password: state.password ?? '',
|
|
|
|
);
|
|
|
|
emit(
|
|
|
|
result.fold(
|
|
|
|
(error) => stateFromCode(error),
|
|
|
|
(userProfile) => state.copyWith(
|
|
|
|
isSubmitting: false,
|
|
|
|
successOrFail: some(left(userProfile)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _performActionOnSignInWithOAuth(
|
|
|
|
SignInState state,
|
|
|
|
Emitter<SignInState> emit,
|
|
|
|
String platform,
|
2023-04-10 15:10:42 +08:00
|
|
|
) async {
|
|
|
|
emit(
|
|
|
|
state.copyWith(
|
2022-12-30 11:16:47 +08:00
|
|
|
isSubmitting: true,
|
|
|
|
emailError: none(),
|
|
|
|
passwordError: none(),
|
2023-04-10 15:10:42 +08:00
|
|
|
successOrFail: none(),
|
|
|
|
),
|
|
|
|
);
|
2021-07-13 13:14:49 +08:00
|
|
|
|
2023-05-21 18:53:59 +08:00
|
|
|
final result = await authService.signUpWithOAuth(
|
|
|
|
platform: platform,
|
2022-01-31 08:15:49 +08:00
|
|
|
);
|
2023-04-10 15:10:42 +08:00
|
|
|
emit(
|
|
|
|
result.fold(
|
2023-05-21 18:53:59 +08:00
|
|
|
(error) => stateFromCode(error),
|
2023-04-10 15:10:42 +08:00
|
|
|
(userProfile) => state.copyWith(
|
|
|
|
isSubmitting: false,
|
|
|
|
successOrFail: some(left(userProfile)),
|
|
|
|
),
|
2023-05-21 18:53:59 +08:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-03 08:48:04 +08:00
|
|
|
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)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-05-21 18:53:59 +08:00
|
|
|
Future<void> _performActionOnSignInAsGuest(
|
|
|
|
SignInState state,
|
|
|
|
Emitter<SignInState> emit,
|
|
|
|
) async {
|
|
|
|
emit(
|
|
|
|
state.copyWith(
|
|
|
|
isSubmitting: true,
|
|
|
|
emailError: none(),
|
|
|
|
passwordError: none(),
|
|
|
|
successOrFail: none(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
final result = await authService.signUpAsGuest();
|
|
|
|
emit(
|
|
|
|
result.fold(
|
2023-04-10 15:10:42 +08:00
|
|
|
(error) => stateFromCode(error),
|
2023-05-21 18:53:59 +08:00
|
|
|
(userProfile) => state.copyWith(
|
|
|
|
isSubmitting: false,
|
|
|
|
successOrFail: some(left(userProfile)),
|
|
|
|
),
|
2023-04-10 15:10:42 +08:00
|
|
|
),
|
|
|
|
);
|
2021-07-13 13:14:49 +08:00
|
|
|
}
|
2021-07-25 18:04:16 +08:00
|
|
|
|
2021-12-14 18:04:51 +08:00
|
|
|
SignInState stateFromCode(FlowyError error) {
|
2023-08-22 00:19:15 +08:00
|
|
|
switch (error.code) {
|
2021-08-31 11:32:51 +08:00
|
|
|
case ErrorCode.EmailFormatInvalid:
|
2022-12-30 11:16:47 +08:00
|
|
|
return state.copyWith(
|
2023-04-10 15:10:42 +08:00
|
|
|
isSubmitting: false,
|
|
|
|
emailError: some(error.msg),
|
|
|
|
passwordError: none(),
|
|
|
|
);
|
2021-08-31 11:32:51 +08:00
|
|
|
case ErrorCode.PasswordFormatInvalid:
|
2022-12-30 11:16:47 +08:00
|
|
|
return state.copyWith(
|
2023-04-10 15:10:42 +08:00
|
|
|
isSubmitting: false,
|
|
|
|
passwordError: some(error.msg),
|
|
|
|
emailError: none(),
|
|
|
|
);
|
2021-07-25 18:04:16 +08:00
|
|
|
default:
|
2022-12-30 11:16:47 +08:00
|
|
|
return state.copyWith(
|
2023-04-10 15:10:42 +08:00
|
|
|
isSubmitting: false,
|
|
|
|
successOrFail: some(right(error)),
|
|
|
|
);
|
2021-07-25 18:04:16 +08:00
|
|
|
}
|
|
|
|
}
|
2021-07-13 13:14:49 +08:00
|
|
|
}
|
2021-07-24 15:05:47 +08:00
|
|
|
|
|
|
|
@freezed
|
2022-03-25 15:02:43 +08:00
|
|
|
class SignInEvent with _$SignInEvent {
|
2022-12-30 11:16:47 +08:00
|
|
|
const factory SignInEvent.signedInWithUserEmailAndPassword() =
|
|
|
|
SignedInWithUserEmailAndPassword;
|
2023-05-21 18:53:59 +08:00
|
|
|
const factory SignInEvent.signedInWithOAuth(String platform) =
|
|
|
|
SignedInWithOAuth;
|
|
|
|
const factory SignInEvent.signedInAsGuest() = SignedInAsGuest;
|
2023-08-03 08:48:04 +08:00
|
|
|
const factory SignInEvent.signedWithMagicLink(String email) =
|
|
|
|
SignedWithMagicLink;
|
2021-07-24 15:05:47 +08:00
|
|
|
const factory SignInEvent.emailChanged(String email) = EmailChanged;
|
|
|
|
const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
|
2023-11-29 12:55:13 -08:00
|
|
|
const factory SignInEvent.deepLinkStateChange(DeepLinkResult result) =
|
|
|
|
_DeepLinkStateChange;
|
2024-01-09 16:09:44 +07:00
|
|
|
const factory SignInEvent.cancel() = _Cancel;
|
2021-07-24 15:05:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@freezed
|
2022-03-25 15:02:43 +08:00
|
|
|
class SignInState with _$SignInState {
|
2021-07-24 15:05:47 +08:00
|
|
|
const factory SignInState({
|
|
|
|
String? email,
|
|
|
|
String? password,
|
|
|
|
required bool isSubmitting,
|
2021-07-25 18:04:16 +08:00
|
|
|
required Option<String> passwordError,
|
|
|
|
required Option<String> emailError,
|
2022-07-19 14:40:56 +08:00
|
|
|
required Option<Either<UserProfilePB, FlowyError>> successOrFail,
|
2021-07-24 15:05:47 +08:00
|
|
|
}) = _SignInState;
|
|
|
|
|
|
|
|
factory SignInState.initial() => SignInState(
|
|
|
|
isSubmitting: false,
|
2021-07-25 18:04:16 +08:00
|
|
|
passwordError: none(),
|
|
|
|
emailError: none(),
|
|
|
|
successOrFail: none(),
|
2021-07-24 15:05:47 +08:00
|
|
|
);
|
|
|
|
}
|