Phase 3.1: Remove flutter_bloc + equatable, delete old bloc files
- Remove flutter_bloc and equatable dependencies from both apps - Delete all 10 old bloc files (5 per app) - Fix 6 remaining screens that used context.read<ApiClient>() from flutter_bloc → converted to ConsumerStatefulWidget/ConsumerWidget with ref.read(apiClientProvider) - Both apps now use Riverpod exclusively for state management Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../api/api_client.dart';
|
||||
|
||||
// Events
|
||||
abstract class AuthEvent extends Equatable {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class AppStarted extends AuthEvent {}
|
||||
class PhoneOtpRequested extends AuthEvent {
|
||||
final String phone;
|
||||
PhoneOtpRequested(this.phone);
|
||||
@override List<Object?> get props => [phone];
|
||||
}
|
||||
class OtpVerified extends AuthEvent {
|
||||
final String verificationId;
|
||||
final String smsCode;
|
||||
OtpVerified(this.verificationId, this.smsCode);
|
||||
@override List<Object?> get props => [verificationId, smsCode];
|
||||
}
|
||||
class LogoutRequested extends AuthEvent {}
|
||||
|
||||
// States
|
||||
abstract class AuthState extends Equatable {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class AuthInitial extends AuthState {}
|
||||
class AuthLoading extends AuthState {}
|
||||
class AuthAuthenticated extends AuthState {
|
||||
final Map<String, dynamic> profile;
|
||||
AuthAuthenticated(this.profile);
|
||||
@override List<Object?> get props => [profile];
|
||||
}
|
||||
class AuthOtpSent extends AuthState {
|
||||
final String verificationId;
|
||||
AuthOtpSent(this.verificationId);
|
||||
@override List<Object?> get props => [verificationId];
|
||||
}
|
||||
class AuthError extends AuthState {
|
||||
final String message;
|
||||
AuthError(this.message);
|
||||
@override List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
// Bloc
|
||||
class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
final ApiClient apiClient;
|
||||
final _auth = FirebaseAuth.instance;
|
||||
ConfirmationResult? _webConfirmationResult;
|
||||
|
||||
AuthBloc({required this.apiClient}) : super(AuthLoading()) {
|
||||
on<AppStarted>(_onAppStarted);
|
||||
on<PhoneOtpRequested>(_onPhoneOtpRequested);
|
||||
on<OtpVerified>(_onOtpVerified);
|
||||
on<LogoutRequested>(_onLogout);
|
||||
}
|
||||
|
||||
Future<void> _onAppStarted(AppStarted event, Emitter<AuthState> emit) async {
|
||||
if (_auth.currentUser != null) {
|
||||
await _verifyAndEmit(emit);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onPhoneOtpRequested(PhoneOtpRequested event, Emitter<AuthState> emit) async {
|
||||
emit(AuthLoading());
|
||||
|
||||
if (kIsWeb) {
|
||||
try {
|
||||
final confirmationResult = await _auth.signInWithPhoneNumber(event.phone);
|
||||
_webConfirmationResult = confirmationResult;
|
||||
emit(AuthOtpSent('web'));
|
||||
} catch (e) {
|
||||
emit(AuthError('Gagal mengirim OTP. Coba lagi.'));
|
||||
}
|
||||
} else {
|
||||
final completer = Completer<void>();
|
||||
await _auth.verifyPhoneNumber(
|
||||
phoneNumber: event.phone,
|
||||
verificationCompleted: (_) {
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
verificationFailed: (e) {
|
||||
emit(AuthError('Gagal mengirim OTP. Coba lagi.'));
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
codeSent: (verificationId, _) {
|
||||
emit(AuthOtpSent(verificationId));
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
codeAutoRetrievalTimeout: (_) {
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
);
|
||||
await completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onOtpVerified(OtpVerified event, Emitter<AuthState> emit) async {
|
||||
emit(AuthLoading());
|
||||
try {
|
||||
if (kIsWeb && _webConfirmationResult != null) {
|
||||
await _webConfirmationResult!.confirm(event.smsCode);
|
||||
} else {
|
||||
final credential = PhoneAuthProvider.credential(
|
||||
verificationId: event.verificationId,
|
||||
smsCode: event.smsCode,
|
||||
);
|
||||
await _auth.signInWithCredential(credential);
|
||||
}
|
||||
await _verifyAndEmit(emit);
|
||||
} catch (e) {
|
||||
emit(AuthError('OTP tidak valid. Coba lagi.'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLogout(LogoutRequested event, Emitter<AuthState> emit) async {
|
||||
await _auth.signOut();
|
||||
emit(AuthInitial());
|
||||
}
|
||||
|
||||
Future<void> _verifyAndEmit(Emitter<AuthState> emit) async {
|
||||
try {
|
||||
final response = await apiClient.post('/api/mitra/auth/verify');
|
||||
emit(AuthAuthenticated(response['data'] as Map<String, dynamic>));
|
||||
} on Exception catch (e) {
|
||||
await _auth.signOut();
|
||||
// Surface specific errors from backend
|
||||
final msg = e.toString();
|
||||
if (msg.contains('ACCOUNT_NOT_FOUND')) {
|
||||
emit(AuthError('Akun tidak ditemukan. Hubungi administrator.'));
|
||||
} else if (msg.contains('ACCOUNT_INACTIVE')) {
|
||||
emit(AuthError('Akun tidak aktif. Hubungi administrator.'));
|
||||
} else {
|
||||
emit(AuthError('Gagal masuk. Coba lagi.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user