import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:user_repository/user_repository.dart'; part 'auth_event.dart'; part 'auth_state.dart'; class AuthBloc extends Bloc { final UserRepository _userRepository; final PhoneVerificationService phoneVerificationService = PhoneVerificationService(); String? _verificationId; AuthBloc({required UserRepository userRepository}) : _userRepository = userRepository, super(AuthStateInitial()) { on(_onAuthEventLoginOAuth); on(_onAuthEventVerifyOAuth); on(_onAuthEventAddEmailAndPassword); on(_updatePassword); on(_linkWithPhoneNumber); on(_linkWithPhoneNumberOtp); } void _onAuthEventLoginOAuth( AuthEventLoginOAuth event, Emitter emit) async { emit(AuthStateProcess()); try { await _userRepository.signInWithPhoneNumber(event.phone); emit(const AuthStateVerifyOAuth(false)); } catch (e) { emit(const AuthStateFailure()); } } void _onAuthEventVerifyOAuth( AuthEventVerifyOAuth event, Emitter emit) async { emit(AuthStateProcess()); try { final bool isVerified = await _userRepository.verifyOTP(event.code); if (isVerified) { emit(AuthStateSuccess()); } else { emit(const AuthStateVerifyOAuth(true)); } } catch (e) { emit(const AuthStateFailure()); } } void _onAuthEventAddEmailAndPassword( AuthEventAddEmailAndPassword event, Emitter emit) async { emit(AuthStateProcess()); try { final String? error = await _userRepository.addEmailAndPassword( event.email, event.password); if (error == null) { emit(AuthStateSuccess()); } else { if (error == 'requires-recent-login') { emit(AuthStateRequiresRecentLogin()); } else if (error == 'email-already-in-use') { emit(AuthStateEmailAlreadyInUse()); } else { emit(const AuthStateFailure()); } } } catch (e) { emit(const AuthStateFailure()); } } void _updatePassword( AuthEventUpdatePassword event, Emitter emit) async { emit(AuthStateProcess()); try { final error = await _userRepository.updatePassword( event.actualPassword, event.password); if (error == null) { emit(AuthStateSuccess()); } else { switch (error) { case UpdatePassworErros.credentialsWrong: emit(const AuthStateFailure(message: "Contrasen虄a incorrecta")); break; case UpdatePassworErros.userNotFound: emit(const AuthStateFailure(message: "Usuario no encontrado")); break; case UpdatePassworErros.unknown: emit(const AuthStateFailure(message: "Error inesperado. 馃ジ")); break; } } } catch (e) { emit(const AuthStateFailure(message: "Error inesperado. 馃ジ")); } } void _linkWithPhoneNumber( LinkWithPhoneNumber event, Emitter emit) async { emit(AuthStateProcess()); try { await for (PhoneAuthEvent event in phoneVerificationService.verifyPhoneNumber(event.phoneNumber)) { switch (event.type) { case PhoneAuthEventType.verificationCompleted: AuthCredential credential = event.data; print('Verificaci贸n completada. Credencial: $credential'); break; case PhoneAuthEventType.verificationFailed: FirebaseAuthException exception = event.data; print('Verificaci贸n fallida. Excepci贸n: $exception'); emit(AuthStateFailure(message: "Error inesperado. 馃ジ $exception")); return; // Detener la ejecuci贸n aqu铆 case PhoneAuthEventType.codeAutoRetrievalTimeout: String verificationId = event.data; print( 'Tiempo de espera agotado para recuperar el c贸digo. ID: $verificationId'); emit(const AuthStateFailure( message: "Tiempo de espera agotado. 馃ジ", )); break; case PhoneAuthEventType.codeSent: Map eventData = event.data; String verificationId = eventData['verificationId']; int? resendToken = eventData['resendToken']; print( 'C贸digo enviado. ID: $verificationId, resendToken: $resendToken'); _verificationId = verificationId; emit(const AuthStateVerifyOAuth(false)); break; } } } catch (e) { emit(const AuthStateFailure(message: "Error inesperado. 馃ジ")); } } void _linkWithPhoneNumber3( LinkWithPhoneNumber event, Emitter emit) async { emit(AuthStateProcess()); try { StreamSubscription subscription = phoneVerificationService .verifyPhoneNumber(event.phoneNumber) .listen((event) async { switch (event.type) { case PhoneAuthEventType.verificationCompleted: AuthCredential credential = event.data; print('Verificaci贸n completada. Credencial: $credential'); break; case PhoneAuthEventType.verificationFailed: FirebaseAuthException exception = event.data; print('Verificaci贸n fallida. Excepci贸n: $exception'); break; case PhoneAuthEventType.codeAutoRetrievalTimeout: String verificationId = event.data; print( 'Tiempo de espera agotado para recuperar el c贸digo. ID: $verificationId'); break; case PhoneAuthEventType.codeSent: Map eventData = event.data; String verificationId = eventData['verificationId']; int? resendToken = eventData['resendToken']; print( 'C贸digo enviado. ID: $verificationId, resendToken: $resendToken'); _verificationId = verificationId; emit(const AuthStateVerifyOAuth(false)); break; } if (event.type == PhoneAuthEventType.verificationFailed) { // Detener la suscripci贸n si la verificaci贸n falla // subscription.cancel(); emit(const AuthStateFailure(message: "Error inesperado. 馃ジ")); } }); // await _userRepository } catch (e) { emit(const AuthStateFailure(message: "Error inesperado. 馃ジ")); } } void _linkWithPhoneNumberOtp( LinkWithPhoneNumberOtp event, Emitter emit) async { emit(AuthStateProcess()); try { final bool isVerified = await _userRepository.linkWithOTP( event.phoneNumber, _verificationId!, event.code); if (isVerified) { emit(AuthStateSuccess()); } else { emit(const AuthStateVerifyOAuth(true)); } } catch (e) { if (e is FirebaseAuthException) { switch (e.code) { case "invalid-verification-code": emit(const AuthStateFailure( message: "Co虂digo de verificaci贸n incorrecto. 馃ジ", )); break; case "provider-already-linked": emit(const AuthStateFailure( message: "Cuenta ya vinculada. 馃ジ", )); break; case "credential-already-in-use": emit(const AuthStateFailure( message: "Este numero ya se encuentra registrado con otra cuenta. 馃ジ", )); break; default: emit(AuthStateFailure(message: "Error inesperado. 馃ジ $e")); break; } } else { emit(AuthStateFailure(message: "Error inesperado. 馃ジ $e")); } } } }