- 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>
159 lines
5.2 KiB
Dart
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"));
|
|
}
|
|
}
|
|
}
|