Files
prosappco/lib/blocs/auth_bloc/auth_bloc.dart
T
Lizandro GuarnizoandClaude Sonnet 4.6 733384091c feat: migrate prosappco from Firebase to NestJS REST API (Fase 2)
- Replace all Firebase* repositories with Api* repositories using HTTP + SharedPreferences JWT
- Remove Firebase.initializeApp() and firebase_messaging background handler from main.dart
- Update DI (app_di.dart) to inject Api* repositories instead of Firebase* ones
- Replace all Timestamp/cloud_firestore usage with ISO 8601 String dates
- Stub PhoneVerificationService (Firebase phone OTP → backend OTP when implemented)
- Add ApiService singleton with JWT management in lib/services/
- Legacy firebase_*_repository.dart files preserved for Fase 4 cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 16:53:11 -05:00

159 lines
5.2 KiB
Dart

import 'dart:async';
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<AuthEvent, AuthState> {
final UserRepository _userRepository;
final PhoneVerificationService phoneVerificationService =
PhoneVerificationService();
String? _verificationId;
AuthBloc({required UserRepository userRepository})
: _userRepository = userRepository,
super(AuthStateInitial()) {
on<AuthEventLoginOAuth>(_onAuthEventLoginOAuth);
on<AuthEventVerifyOAuth>(_onAuthEventVerifyOAuth);
on<AuthEventAddEmailAndPassword>(_onAuthEventAddEmailAndPassword);
on<AuthEventUpdatePassword>(_updatePassword);
on<LinkWithPhoneNumber>(_linkWithPhoneNumber);
on<LinkWithPhoneNumberOtp>(_linkWithPhoneNumberOtp);
}
void _onAuthEventLoginOAuth(
AuthEventLoginOAuth event, Emitter<AuthState> emit) async {
emit(AuthStateProcess());
try {
await _userRepository.signInWithPhoneNumber(event.phone);
emit(const AuthStateVerifyOAuth(false));
} catch (e) {
emit(const AuthStateFailure());
}
}
void _onAuthEventVerifyOAuth(
AuthEventVerifyOAuth event, Emitter<AuthState> 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<AuthState> 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<AuthState> 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: "Contrasena 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<AuthState> emit) async {
emit(AuthStateProcess());
try {
await for (PhoneAuthEvent event
in phoneVerificationService.verifyPhoneNumber(event.phoneNumber)) {
switch (event.type) {
case PhoneAuthEventType.verificationCompleted:
final credential = event.data;
print('Verificacion completada. Credencial: $credential');
break;
case PhoneAuthEventType.verificationFailed:
final exception = event.data;
print('Verificacion fallida. Excepcion: $exception');
emit(AuthStateFailure(message: "Error inesperado. $exception"));
return;
case PhoneAuthEventType.codeAutoRetrievalTimeout:
final verificationId = event.data as String;
print('Tiempo de espera agotado. ID: $verificationId');
emit(const AuthStateFailure(
message: "Tiempo de espera agotado.",
));
break;
case PhoneAuthEventType.codeSent:
final eventData = event.data as Map<String, dynamic>;
final verificationId = eventData['verificationId'] as String;
final resendToken = eventData['resendToken'] as int?;
print('Codigo enviado. ID: $verificationId, resendToken: $resendToken');
_verificationId = verificationId;
emit(const AuthStateVerifyOAuth(false));
break;
}
}
} catch (e) {
emit(const AuthStateFailure(message: "Error inesperado."));
}
}
void _linkWithPhoneNumberOtp(
LinkWithPhoneNumberOtp event, Emitter<AuthState> 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) {
emit(AuthStateFailure(message: "Error inesperado. $e"));
}
}
}