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>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
726cf12fd2
commit
733384091c
@@ -1,6 +1,5 @@
|
||||
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';
|
||||
@@ -86,18 +85,18 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
} else {
|
||||
switch (error) {
|
||||
case UpdatePassworErros.credentialsWrong:
|
||||
emit(const AuthStateFailure(message: "Contraseña incorrecta"));
|
||||
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. 🥸"));
|
||||
emit(const AuthStateFailure(message: "Error inesperado."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
emit(const AuthStateFailure(message: "Error inesperado. 🥸"));
|
||||
emit(const AuthStateFailure(message: "Error inesperado."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,81 +109,33 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
in phoneVerificationService.verifyPhoneNumber(event.phoneNumber)) {
|
||||
switch (event.type) {
|
||||
case PhoneAuthEventType.verificationCompleted:
|
||||
AuthCredential credential = event.data;
|
||||
print('Verificación completada. Credencial: $credential');
|
||||
final credential = event.data;
|
||||
print('Verificacion 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í
|
||||
final exception = event.data;
|
||||
print('Verificacion fallida. Excepcion: $exception');
|
||||
emit(AuthStateFailure(message: "Error inesperado. $exception"));
|
||||
return;
|
||||
case PhoneAuthEventType.codeAutoRetrievalTimeout:
|
||||
String verificationId = event.data;
|
||||
print(
|
||||
'Tiempo de espera agotado para recuperar el código. ID: $verificationId');
|
||||
final verificationId = event.data as String;
|
||||
print('Tiempo de espera agotado. ID: $verificationId');
|
||||
emit(const AuthStateFailure(
|
||||
message: "Tiempo de espera agotado. 🥸",
|
||||
message: "Tiempo de espera agotado.",
|
||||
));
|
||||
break;
|
||||
case PhoneAuthEventType.codeSent:
|
||||
Map<String, dynamic> eventData = event.data;
|
||||
String verificationId = eventData['verificationId'];
|
||||
int? resendToken = eventData['resendToken'];
|
||||
print(
|
||||
'Código enviado. ID: $verificationId, resendToken: $resendToken');
|
||||
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 _linkWithPhoneNumber3(
|
||||
LinkWithPhoneNumber event, Emitter<AuthState> emit) async {
|
||||
emit(AuthStateProcess());
|
||||
|
||||
try {
|
||||
StreamSubscription<PhoneAuthEvent> 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<String, dynamic> 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. 🥸"));
|
||||
emit(const AuthStateFailure(message: "Error inesperado."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +144,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(AuthStateProcess());
|
||||
try {
|
||||
final bool isVerified = await _userRepository.linkWithOTP(
|
||||
event.phoneNumber, _verificationId!, event.code);
|
||||
event.phoneNumber, _verificationId ?? '', event.code);
|
||||
|
||||
if (isVerified) {
|
||||
emit(AuthStateSuccess());
|
||||
@@ -201,34 +152,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const AuthStateVerifyOAuth(true));
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is FirebaseAuthException) {
|
||||
switch (e.code) {
|
||||
case "invalid-verification-code":
|
||||
emit(const AuthStateFailure(
|
||||
message: "Có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"));
|
||||
}
|
||||
emit(AuthStateFailure(message: "Error inesperado. $e"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ part 'chat_event.dart';
|
||||
part 'chat_state.dart';
|
||||
|
||||
class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
final FirebaseChatRepository _chatRepository;
|
||||
final ApiChatRepository _chatRepository;
|
||||
|
||||
ChatBloc({
|
||||
required FirebaseChatRepository chatRepository,
|
||||
required ApiChatRepository chatRepository,
|
||||
}) : _chatRepository = chatRepository,
|
||||
super(ChatInitial()) {
|
||||
on<LoadChatEvent>(_onLoadChatEvent);
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:prosappco/firebase_options.dart';
|
||||
import 'package:prosappco/local_notifications/local_notifications.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
|
||||
part 'notification_event.dart';
|
||||
part 'notification_state.dart';
|
||||
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp();
|
||||
}
|
||||
// Stub: Firebase messaging removed. Background handler is no longer needed.
|
||||
Future<void> firebaseMessagingBackgroundHandler(dynamic message) async {}
|
||||
|
||||
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
|
||||
final Future<void> Function() requestLocalNotificationPermission;
|
||||
final void Function({
|
||||
required int id,
|
||||
@@ -33,83 +24,18 @@ class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
required this.showLocalNotification})
|
||||
: super(const NotificationState()) {
|
||||
on<NotificationStatusChanged>(_notificationStatusChanged);
|
||||
|
||||
_initialStatusCheck();
|
||||
|
||||
_onForegroundMessage();
|
||||
}
|
||||
|
||||
static Future<void> initializeFCM() async {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
}
|
||||
|
||||
void _notificationStatusChanged(
|
||||
NotificationStatusChanged event, Emitter<NotificationState> emit) {
|
||||
emit(state.copyWith(status: event.status));
|
||||
|
||||
_getFCMToken();
|
||||
}
|
||||
|
||||
void _initialStatusCheck() async {
|
||||
final settings = await messaging.getNotificationSettings();
|
||||
add(NotificationStatusChanged(settings.authorizationStatus));
|
||||
_getFCMToken();
|
||||
}
|
||||
|
||||
Future<void> _saveFCMTokenToFirestore(String token) async {
|
||||
try {
|
||||
CollectionReference users = FirebaseFirestore.instance.collection('users');
|
||||
|
||||
await users.doc(FirebaseAuth.instance.currentUser!.uid).update({
|
||||
'token': token,
|
||||
});
|
||||
} catch (e) {
|
||||
print('tokenFCM Error saving FCM token to Firestore: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _getFCMToken() async {
|
||||
if (state.status != AuthorizationStatus.authorized) return;
|
||||
|
||||
final fcmToken = await messaging.getToken();
|
||||
log('tokenFCM ${fcmToken.toString()}');
|
||||
|
||||
if (fcmToken == null) return;
|
||||
|
||||
_saveFCMTokenToFirestore(fcmToken);
|
||||
}
|
||||
|
||||
void _handleRemoteMessage(RemoteMessage message) {
|
||||
if (message.notification == null) return;
|
||||
|
||||
showLocalNotification(
|
||||
id: 1,
|
||||
title: message.notification!.title,
|
||||
body: message.notification!.body,
|
||||
);
|
||||
}
|
||||
|
||||
void _onForegroundMessage() {
|
||||
FirebaseMessaging.onMessage.listen(_handleRemoteMessage);
|
||||
}
|
||||
|
||||
void requestPermission() async {
|
||||
NotificationSettings settings = await messaging.requestPermission(
|
||||
alert: true,
|
||||
announcement: false,
|
||||
badge: true,
|
||||
carPlay: false,
|
||||
criticalAlert: false,
|
||||
provisional: false,
|
||||
sound: true,
|
||||
);
|
||||
|
||||
// Solicitar permiso a las local notificaciones
|
||||
|
||||
await requestLocalNotificationPermission();
|
||||
|
||||
add(NotificationStatusChanged(settings.authorizationStatus));
|
||||
try {
|
||||
await requestLocalNotificationPermission();
|
||||
} catch (e) {
|
||||
log('NotificationBloc.requestPermission error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
part of 'notification_bloc.dart';
|
||||
|
||||
// Stub for firebase_messaging AuthorizationStatus
|
||||
enum AuthorizationStatus {
|
||||
authorized,
|
||||
denied,
|
||||
notDetermined,
|
||||
provisional,
|
||||
}
|
||||
|
||||
class NotificationState extends Equatable {
|
||||
final AuthorizationStatus status;
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ part 'professional_event.dart';
|
||||
part 'professional_state.dart';
|
||||
|
||||
class ProfessionalBloc extends Bloc<ProfessionalEvent, ProfessionalState> {
|
||||
final FirebaseProfessionalRepository _professionalRepository;
|
||||
final ApiProfessionalRepository _professionalRepository;
|
||||
final UserRepository _userRepository;
|
||||
|
||||
ProfessionalBloc(
|
||||
{required FirebaseProfessionalRepository professionalRepository,
|
||||
{required ApiProfessionalRepository professionalRepository,
|
||||
required UserRepository userRepository})
|
||||
: _professionalRepository = professionalRepository,
|
||||
_userRepository = userRepository,
|
||||
|
||||
@@ -9,11 +9,11 @@ part 'professional_list_state.dart';
|
||||
class ProfessionalListBloc
|
||||
extends Bloc<ProfessionalListEvent, ProfessionalListState> {
|
||||
final UserRepository _userRepository;
|
||||
final FirebaseProfessionalRepository _firebaseProfessionalRepository;
|
||||
final ApiProfessionalRepository _firebaseProfessionalRepository;
|
||||
|
||||
ProfessionalListBloc({
|
||||
required UserRepository userRepository,
|
||||
required FirebaseProfessionalRepository firebaseProfessonalRepository,
|
||||
required ApiProfessionalRepository firebaseProfessonalRepository,
|
||||
}) : _userRepository = userRepository,
|
||||
_firebaseProfessionalRepository = firebaseProfessonalRepository,
|
||||
super(ProfessionalListInitial()) {
|
||||
|
||||
@@ -7,10 +7,10 @@ part 'professional_profile_state.dart';
|
||||
|
||||
class ProfessionalProfileBloc
|
||||
extends Bloc<ProfessionalProfileEvent, ProfessionalProfileState> {
|
||||
final FirebaseProfessionalRepository _professionalRepository;
|
||||
final ApiProfessionalRepository _professionalRepository;
|
||||
|
||||
ProfessionalProfileBloc(
|
||||
{required FirebaseProfessionalRepository professionalRepository})
|
||||
{required ApiProfessionalRepository professionalRepository})
|
||||
: _professionalRepository = professionalRepository,
|
||||
super((UpdateProfessionalInfoInitial())) {
|
||||
on<UpdateProfessionalBannerInfo>(_onUpdateProfessionalBannerInfo);
|
||||
|
||||
@@ -9,11 +9,11 @@ part 'score_event.dart';
|
||||
part 'score_state.dart';
|
||||
|
||||
class ScoreBloc extends Bloc<ScoreEvent, ScoreState> {
|
||||
final FirebaseScoreRepository _scoreRepository;
|
||||
final ApiScoreRepository _scoreRepository;
|
||||
final UserRepository _userRepository;
|
||||
|
||||
ScoreBloc({
|
||||
required FirebaseScoreRepository scoreRepository,
|
||||
required ApiScoreRepository scoreRepository,
|
||||
required UserRepository userRepository,
|
||||
}) : _scoreRepository = scoreRepository,
|
||||
_userRepository = userRepository,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
@@ -13,14 +12,14 @@ part 'service_event.dart';
|
||||
part 'service_state.dart';
|
||||
|
||||
class ServiceBloc extends Bloc<ServiceEvent, ServiceState> {
|
||||
final FirebaseServiceRepository _serviceRepository;
|
||||
final ApiServiceRepository _serviceRepository;
|
||||
final UserRepository _userRepository;
|
||||
final FirebaseProfessionalRepository _professionalRepository;
|
||||
final ApiProfessionalRepository _professionalRepository;
|
||||
|
||||
ServiceBloc({
|
||||
required FirebaseServiceRepository serviceRepository,
|
||||
required ApiServiceRepository serviceRepository,
|
||||
required UserRepository userRepository,
|
||||
required FirebaseProfessionalRepository professionRepository,
|
||||
required ApiProfessionalRepository professionRepository,
|
||||
}) : _serviceRepository = serviceRepository,
|
||||
_userRepository = userRepository,
|
||||
_professionalRepository = professionRepository,
|
||||
|
||||
@@ -117,7 +117,7 @@ class CreateService extends ServiceEvent {
|
||||
final double latitude;
|
||||
final double longitude;
|
||||
final String day;
|
||||
final Timestamp createdAt;
|
||||
final String createdAt;
|
||||
final String description;
|
||||
final TimeOfDay range1Hour1;
|
||||
final TimeOfDay range1Hour2;
|
||||
|
||||
Reference in New Issue
Block a user