Phase 3.1: Complete mitra_app Riverpod migration (all blocs, fix auth bug)
- Migrate AuthBloc → MitraAuthNotifier (fixes stuck-loading bug: now returns MitraAuthInitialData when currentUser is null) - Migrate StatusBloc → OnlineStatusNotifier (heartbeat timer + lifecycle) - Migrate ExtensionBloc → MitraExtensionNotifier (accept/reject + goodbye) - Migrate ChatRequestBloc → ChatRequestNotifier (WebSocket incoming requests) - Migrate MitraChatBloc → MitraChatNotifier (WebSocket chat + messages) - Update router to use Riverpod auth state for redirects - Remove all flutter_bloc usage from mitra_app screens and main.dart - MultiBlocProvider fully removed from mitra_app Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
114
mitra_app/lib/core/auth/auth_notifier.dart
Normal file
114
mitra_app/lib/core/auth/auth_notifier.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'dart:async';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import '../api/api_client.dart';
|
||||
import '../api/api_client_provider.dart';
|
||||
|
||||
part 'auth_notifier.g.dart';
|
||||
|
||||
// States
|
||||
sealed class MitraAuthData {
|
||||
const MitraAuthData();
|
||||
}
|
||||
|
||||
class MitraAuthInitialData extends MitraAuthData {
|
||||
const MitraAuthInitialData();
|
||||
}
|
||||
|
||||
class MitraAuthAuthenticatedData extends MitraAuthData {
|
||||
final Map<String, dynamic> profile;
|
||||
const MitraAuthAuthenticatedData(this.profile);
|
||||
}
|
||||
|
||||
class MitraAuthOtpSentData extends MitraAuthData {
|
||||
final String verificationId;
|
||||
const MitraAuthOtpSentData(this.verificationId);
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class MitraAuth extends _$MitraAuth {
|
||||
FirebaseAuth get _auth => FirebaseAuth.instance;
|
||||
ApiClient get _apiClient => ref.read(apiClientProvider);
|
||||
ConfirmationResult? _webConfirmationResult;
|
||||
|
||||
@override
|
||||
FutureOr<MitraAuthData> build() async {
|
||||
if (_auth.currentUser != null) {
|
||||
return await _verifyAndReturn();
|
||||
}
|
||||
return const MitraAuthInitialData(); // FIX: was missing in BLoC version
|
||||
}
|
||||
|
||||
Future<void> requestOtp(String phone) async {
|
||||
state = const AsyncLoading();
|
||||
if (kIsWeb) {
|
||||
try {
|
||||
final confirmationResult = await _auth.signInWithPhoneNumber(phone);
|
||||
_webConfirmationResult = confirmationResult;
|
||||
state = const AsyncData(MitraAuthOtpSentData('web'));
|
||||
} catch (e) {
|
||||
state = AsyncError('Gagal mengirim OTP. Coba lagi.', StackTrace.current);
|
||||
}
|
||||
} else {
|
||||
final completer = Completer<void>();
|
||||
await _auth.verifyPhoneNumber(
|
||||
phoneNumber: phone,
|
||||
verificationCompleted: (_) {
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
verificationFailed: (e) {
|
||||
state = AsyncError('Gagal mengirim OTP. Coba lagi.', StackTrace.current);
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
codeSent: (verificationId, _) {
|
||||
state = AsyncData(MitraAuthOtpSentData(verificationId));
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
codeAutoRetrievalTimeout: (_) {
|
||||
if (!completer.isCompleted) completer.complete();
|
||||
},
|
||||
);
|
||||
await completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> verifyOtp(String verificationId, String smsCode) async {
|
||||
state = const AsyncLoading();
|
||||
try {
|
||||
if (kIsWeb && _webConfirmationResult != null) {
|
||||
await _webConfirmationResult!.confirm(smsCode);
|
||||
} else {
|
||||
final credential = PhoneAuthProvider.credential(
|
||||
verificationId: verificationId,
|
||||
smsCode: smsCode,
|
||||
);
|
||||
await _auth.signInWithCredential(credential);
|
||||
}
|
||||
state = AsyncData(await _verifyAndReturn());
|
||||
} catch (e) {
|
||||
state = AsyncError('OTP tidak valid. Coba lagi.', StackTrace.current);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
await _auth.signOut();
|
||||
state = const AsyncData(MitraAuthInitialData());
|
||||
}
|
||||
|
||||
Future<MitraAuthData> _verifyAndReturn() async {
|
||||
try {
|
||||
final response = await _apiClient.post('/api/mitra/auth/verify');
|
||||
return MitraAuthAuthenticatedData(response['data'] as Map<String, dynamic>);
|
||||
} on Exception catch (e) {
|
||||
await _auth.signOut();
|
||||
final msg = e.toString();
|
||||
if (msg.contains('ACCOUNT_NOT_FOUND')) {
|
||||
throw Exception('Akun tidak ditemukan. Hubungi administrator.');
|
||||
} else if (msg.contains('ACCOUNT_INACTIVE')) {
|
||||
throw Exception('Akun tidak aktif. Hubungi administrator.');
|
||||
}
|
||||
throw Exception('Gagal masuk. Coba lagi.');
|
||||
}
|
||||
}
|
||||
}
|
||||
25
mitra_app/lib/core/auth/auth_notifier.g.dart
Normal file
25
mitra_app/lib/core/auth/auth_notifier.g.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'auth_notifier.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$mitraAuthHash() => r'65235a41cde3a37feef0b3004a0a48b508bf9ac9';
|
||||
|
||||
/// See also [MitraAuth].
|
||||
@ProviderFor(MitraAuth)
|
||||
final mitraAuthProvider =
|
||||
AsyncNotifierProvider<MitraAuth, MitraAuthData>.internal(
|
||||
MitraAuth.new,
|
||||
name: r'mitraAuthProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$mitraAuthHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$MitraAuth = AsyncNotifier<MitraAuthData>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
Reference in New Issue
Block a user