From d0ad0280e01849db68302e89becd57ad3636dfbe Mon Sep 17 00:00:00 2001 From: Conner Date: Sat, 23 Nov 2024 21:09:27 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[#17]=20PhoneAuthBloc,=20EmailDuplicate?= =?UTF-8?q?CheckBloc=EC=9D=84=20Listener=EB=A1=9C=20SignUpBloc=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=92=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/types/gender_type.dart | 4 ++ lib/core/types/visible_type.dart | 2 + lib/core/utils/regex/regex_util.dart | 2 +- .../data/data_sources/mock/mock_api.dart | 4 +- lib/feature/account/init_injections.dart | 8 ++-- .../email_duplicate_check_bloc.dart | 3 ++ .../email_duplicate_check_state.dart | 3 ++ .../bloc/phone_auth/phone_auth_bloc.dart | 5 +- .../bloc/phone_auth/phone_auth_state.dart | 6 ++- .../bloc/sign_up/sign_up_bloc.dart | 6 +++ .../bloc/sign_up/sign_up_bloc.handler.dart | 28 +++++++++++ .../bloc/sign_up/sign_up_event.dart | 28 +++++++++++ .../bloc/sign_up/sign_up_state.dart | 17 ++++++- .../page/sign_up/sign_up_page.dart | 48 ++++++++++++++++--- 14 files changed, 145 insertions(+), 19 deletions(-) diff --git a/lib/core/types/gender_type.dart b/lib/core/types/gender_type.dart index d340208..61499d7 100644 --- a/lib/core/types/gender_type.dart +++ b/lib/core/types/gender_type.dart @@ -24,3 +24,7 @@ enum GenderType with L10nKeyProvider { ) .toList(); } + +extension GenderTypeExt on GenderType { + bool get isNone => this == GenderType.none; +} diff --git a/lib/core/types/visible_type.dart b/lib/core/types/visible_type.dart index 4ddbe8f..03171b8 100644 --- a/lib/core/types/visible_type.dart +++ b/lib/core/types/visible_type.dart @@ -7,6 +7,8 @@ enum VisibleType { extension VisibleTypeExt on VisibleType { bool get isVisible => this == VisibleType.visible; + bool get isInvisible => this == VisibleType.invisible; + /// Boolean -> 노출 여부 static VisibleType fromBool(bool isVisible) { return isVisible ? VisibleType.visible : VisibleType.invisible; diff --git a/lib/core/utils/regex/regex_util.dart b/lib/core/utils/regex/regex_util.dart index 75e934a..5a8ab5e 100644 --- a/lib/core/utils/regex/regex_util.dart +++ b/lib/core/utils/regex/regex_util.dart @@ -16,7 +16,7 @@ class RegExUtil { /// 이름 static RegExp namePattern = RegExp( - r'^[가-힣a-zA-Z]{2,10}\$', + r'^[ㄱ-ㅎ가-힣A-Za-z]{2,10}$', ); /// 생년월일 패턴 diff --git a/lib/feature/account/data/data_sources/mock/mock_api.dart b/lib/feature/account/data/data_sources/mock/mock_api.dart index a337f79..87b4033 100644 --- a/lib/feature/account/data/data_sources/mock/mock_api.dart +++ b/lib/feature/account/data/data_sources/mock/mock_api.dart @@ -53,12 +53,14 @@ class AccountMockApi extends AccountApiImpl { FutureOr>> verifyAuthCode({ required AuthCodeVerificationRequestDto dto, }) async { + final isAuth = '111111' == dto.authCode; + /// Mock 응답 등록 dioAdapter.onPost( verifyAuthCodePath, (server) => server.reply( 200, - BaseResponseDtoMock.mock(true).toJson((value) => value), + BaseResponseDtoMock.mock(isAuth).toJson((value) => value), delay: const Duration(seconds: 1), ), data: dto.toJson(), diff --git a/lib/feature/account/init_injections.dart b/lib/feature/account/init_injections.dart index 4b82202..46dbfed 100644 --- a/lib/feature/account/init_injections.dart +++ b/lib/feature/account/init_injections.dart @@ -40,15 +40,15 @@ void initAccountDomainInjections() { } void initAccountPresentationInjections() { - getIt.registerFactory( + getIt.registerLazySingleton( () => PhoneAuthBloc(phoneAuthUseCase: getIt()), ); + getIt.registerLazySingleton( + () => EmailDuplicateCheckBloc(useCase: getIt()), + ); getIt.registerFactory( () => LoginBloc(loginUseCase: getIt()), ); - getIt.registerFactory( - () => EmailDuplicateCheckBloc(useCase: getIt()), - ); getIt.registerFactory( () => SignUpBloc(signUpUseCase: getIt()), ); diff --git a/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_bloc.dart b/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_bloc.dart index 14aa2c4..e5927c8 100644 --- a/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_bloc.dart +++ b/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_bloc.dart @@ -12,6 +12,9 @@ part 'email_duplicate_check_bloc.freezed.dart'; part 'email_duplicate_check_bloc.handler.dart'; +typedef EmailDuplicateCheckBlocListener + = BlocListener; + class EmailDuplicateCheckBloc extends BaseBloc { final EmailDuplicateCheckUseCase useCase; diff --git a/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_state.dart b/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_state.dart index f05f0ae..7bd70ff 100644 --- a/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_state.dart +++ b/lib/feature/account/presentation/bloc/email_duplicate_check/email_duplicate_check_state.dart @@ -20,4 +20,7 @@ class EmailDuplicateCheckState extends BaseBlocState extension EmailDuplicateCheckStateExt on EmailDuplicateCheckState { /// 에러 문구 노출 여부 bool get isVisibleError => errorVisible.isVisible; + + /// 유니크 여부 + bool get isUnique => errorVisible.isInvisible; } diff --git a/lib/feature/account/presentation/bloc/phone_auth/phone_auth_bloc.dart b/lib/feature/account/presentation/bloc/phone_auth/phone_auth_bloc.dart index e8c92a8..8fc8b5e 100644 --- a/lib/feature/account/presentation/bloc/phone_auth/phone_auth_bloc.dart +++ b/lib/feature/account/presentation/bloc/phone_auth/phone_auth_bloc.dart @@ -14,8 +14,9 @@ part 'phone_auth_bloc.handler.dart'; part 'phone_auth_bloc.parser.dart'; -class PhoneAuthBloc - extends Bloc { +typedef PhoneAuthBlocListener = BlocListener; + +class PhoneAuthBloc extends Bloc { final PhoneAuthUseCase phoneAuthUseCase; PhoneAuthBloc({ diff --git a/lib/feature/account/presentation/bloc/phone_auth/phone_auth_state.dart b/lib/feature/account/presentation/bloc/phone_auth/phone_auth_state.dart index 4b04559..f5cf98b 100644 --- a/lib/feature/account/presentation/bloc/phone_auth/phone_auth_state.dart +++ b/lib/feature/account/presentation/bloc/phone_auth/phone_auth_state.dart @@ -1,8 +1,7 @@ part of 'phone_auth_bloc.dart'; @freezed -class PhoneAuthState extends BaseBlocState - with _$PhoneAuthState { +class PhoneAuthState extends BaseBlocState with _$PhoneAuthState { factory PhoneAuthState({ required BaseBlocStatus status, @@ -26,4 +25,7 @@ extension PhoneAuthStateExt on PhoneAuthState { /// 에러 문구 노출 여부 bool get isVisibleAuthCodeError => authCodeErrorVisible.isVisible; + + /// 인증 완료 여부 + bool get isAuth => authCodeErrorVisible.isInvisible; } diff --git a/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.dart b/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.dart index 60dcd4e..085f504 100644 --- a/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.dart +++ b/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.dart @@ -11,6 +11,8 @@ part 'sign_up_bloc.freezed.dart'; part 'sign_up_bloc.handler.dart'; +typedef SignUpBlocBuilder = BlocBuilder; + class SignUpBloc extends BaseBloc { final SignUpUseCase signUpUseCase; @@ -22,6 +24,10 @@ class SignUpBloc extends BaseBloc { on(_onNameInputted); on(_onBirthDateInputted); on(_onGenderSelected); + on(_onPhoneInputted); + on(_onPhoneAuthChanged); + on(_onLoginIdInputted); + on(_onIsUniqueIdChanged); on(_onPasswordObscureToggled); on(_onPasswordInputted); on(_onPasswordVerifyInputted); diff --git a/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.handler.dart b/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.handler.dart index df29596..1731307 100644 --- a/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.handler.dart +++ b/lib/feature/account/presentation/bloc/sign_up/sign_up_bloc.handler.dart @@ -22,6 +22,34 @@ extension SignUpBlocHandler on SignUpBloc { emit(state.copyWith(gender: event.gender)); } + void _onPhoneInputted( + SignUpPhoneInputted event, + Emitter emit, + ) { + emit(state.copyWith(phone: event.phone)); + } + + void _onPhoneAuthChanged( + SignUpPhoneAuthChanged event, + Emitter emit, + ) { + emit(state.copyWith(isAuthPhone: event.isAuth)); + } + + void _onLoginIdInputted( + SignUpLoginIdInputted event, + Emitter emit, + ) { + emit(state.copyWith(loginId: event.loginId)); + } + + void _onIsUniqueIdChanged( + SignUpIsUniqueIdChanged event, + Emitter emit, + ) { + emit(state.copyWith(isUniqueId: event.isUnique)); + } + void _onPasswordObscureToggled( SignUpPasswordObscureToggled event, Emitter emit, diff --git a/lib/feature/account/presentation/bloc/sign_up/sign_up_event.dart b/lib/feature/account/presentation/bloc/sign_up/sign_up_event.dart index e7c85b4..1c0b397 100644 --- a/lib/feature/account/presentation/bloc/sign_up/sign_up_event.dart +++ b/lib/feature/account/presentation/bloc/sign_up/sign_up_event.dart @@ -30,6 +30,20 @@ class SignUpGenderSelected extends SignUpEvent { SignUpGenderSelected({required this.gender}); } +/// 휴대폰 입력 이벤트 +class SignUpPhoneInputted extends SignUpEvent { + final Phone phone; + + SignUpPhoneInputted({required this.phone}); +} + +/// 휴대폰 인증 입력 이벤트 +class SignUpPhoneAuthChanged extends SignUpEvent { + final bool isAuth; + + SignUpPhoneAuthChanged({required this.isAuth}); +} + /// 비밀번호 입력 이벤트 class SignUpPasswordInputted extends SignUpEvent { final String value; @@ -39,6 +53,20 @@ class SignUpPasswordInputted extends SignUpEvent { Password get password => Password(value); } +/// LoginId 입력 이벤트 +class SignUpLoginIdInputted extends SignUpEvent { + final Email loginId; + + SignUpLoginIdInputted({required this.loginId}); +} + +/// LoginId 인증 여부 +class SignUpIsUniqueIdChanged extends SignUpEvent { + final bool isUnique; + + SignUpIsUniqueIdChanged({required this.isUnique}); +} + /// 비밀번호 확인 입력 이벤트 class SignUpPasswordVerifyInputted extends SignUpEvent { final String value; diff --git a/lib/feature/account/presentation/bloc/sign_up/sign_up_state.dart b/lib/feature/account/presentation/bloc/sign_up/sign_up_state.dart index 221360a..ecc4c1a 100644 --- a/lib/feature/account/presentation/bloc/sign_up/sign_up_state.dart +++ b/lib/feature/account/presentation/bloc/sign_up/sign_up_state.dart @@ -9,7 +9,9 @@ class SignUpState extends BaseBlocState with _$SignUpState { @Default(BirthDate.empty) BirthDate birthDate, @Default(GenderType.none) GenderType gender, @Default(Phone.empty) Phone phone, + @Default(false) bool isAuthPhone, @Default(Email.empty) LoginId loginId, + @Default(false) bool isUniqueId, @Default(Password.empty) Password password, @Default(Password.empty) Password passwordVerify, @Default(true) bool isPasswordObscure, @@ -19,10 +21,21 @@ class SignUpState extends BaseBlocState with _$SignUpState { extension SignUpStateExt on SignUpState { VisibleType getPasswordErrorVisible() { - return VisibleTypeExt.fromBool(!checkPasswordValid()); + return VisibleTypeExt.fromBool(!_checkPasswordValid()); } - bool checkPasswordValid() { + bool _checkPasswordValid() { return password.isValid && password.isEqual(passwordVerify); } + + bool get isEnabledSubmit => _checkSubmitEnabled(); + + bool _checkSubmitEnabled() { + return name.isValid && + birthDate.isValid && + !gender.isNone && + isAuthPhone && + isUniqueId && + _checkPasswordValid(); + } } diff --git a/lib/feature/account/presentation/page/sign_up/sign_up_page.dart b/lib/feature/account/presentation/page/sign_up/sign_up_page.dart index 95fd813..658707a 100644 --- a/lib/feature/account/presentation/page/sign_up/sign_up_page.dart +++ b/lib/feature/account/presentation/page/sign_up/sign_up_page.dart @@ -9,17 +9,49 @@ import 'package:withu_app/shared/shared.dart'; import 'sign_up_page_key.dart'; -typedef SignUpBlocBuilder = BlocBuilder; - @RoutePage() class SignUpPage extends StatelessWidget { const SignUpPage({super.key}); @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => getIt(), - child: const SignUpPageContent(), + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => getIt(), + ), + BlocProvider( + create: (context) => getIt(), + ), + BlocProvider( + create: (context) => getIt(), + ), + ], + child: MultiBlocListener( + listeners: [ + PhoneAuthBlocListener( + listener: (context, state) { + context + .read() + .add(SignUpPhoneInputted(phone: state.phone)); + context + .read() + .add(SignUpPhoneAuthChanged(isAuth: state.isAuth)); + }, + ), + EmailDuplicateCheckBlocListener( + listener: (context, state) { + context + .read() + .add(SignUpLoginIdInputted(loginId: state.email)); + context + .read() + .add(SignUpIsUniqueIdChanged(isUnique: state.isUnique)); + }, + ), + ], + child: const SignUpPageContent(), + ), ); } } @@ -29,7 +61,7 @@ class SignUpPageContent extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return SignUpBlocBuilder( builder: (context, state) { return PageRoot( isLoading: state.status.isLoading, @@ -94,6 +126,7 @@ class NameInput extends StatelessWidget { return BaseInput( key: SignUpPageKey.name.toKey(), hintText: StringRes.enterTwoOrMoreChars.tr, + maxLength: 10, onChanged: (String text) { context.read().add(SignUpNameInputted(value: text)); }, @@ -110,6 +143,7 @@ class BirthDateInput extends StatelessWidget { key: SignUpPageKey.birthDate.toKey(), keyboardType: TextInputType.number, hintText: StringRes.enterEightChars.tr, + maxLength: 8, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, ], @@ -173,7 +207,7 @@ class SubmitButton extends StatelessWidget { return EnabledButton( key: SignUpPageKey.submitBtn.toKey(), text: StringRes.signUp.tr, - isEnabled: true, + isEnabled: state.isEnabledSubmit, onTap: () { context.read().add(SignUpSubmitPressed()); },