Files
prosappco/lib/blocs/auth_bloc/auth_bloc.dart
T
Lizandro GuarnizoandClaude Sonnet 4.6 38beca4b4f feat: implement SMS OTP auth via backend API
- signInWithPhoneNumber: calls POST /auth/send-otp, stores pending phone
- verifyOTP: calls POST /auth/phone with phone+code, saves token
- linkWithOTP: calls POST /auth/verify-phone (authenticated) with phone+code
- AuthBloc._linkWithPhoneNumber: replaced Firebase PhoneVerificationService with repository call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 15:23:39 -05:00

127 lines
3.8 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;
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 _userRepository.signInWithPhoneNumber(event.phoneNumber);
emit(const AuthStateVerifyOAuth(false));
} catch (e) {
emit(AuthStateFailure(message: "Error al enviar código. $e"));
}
}
void _linkWithPhoneNumberOtp(
LinkWithPhoneNumberOtp event, Emitter<AuthState> emit) async {
emit(AuthStateProcess());
try {
final bool isVerified = await _userRepository.linkWithOTP(
event.phoneNumber, '', event.code);
if (isVerified) {
emit(AuthStateSuccess());
} else {
emit(const AuthStateVerifyOAuth(true));
}
} catch (e) {
emit(AuthStateFailure(message: "Error inesperado. $e"));
}
}
}