feat: Implement initial authentication, multi-environment support, and core app infrastructure.
This commit is contained in:
159
lib/features/auth/presentation/providers/auth_provider.dart
Normal file
159
lib/features/auth/presentation/providers/auth_provider.dart
Normal file
@@ -0,0 +1,159 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:real_estate_mobile/core/network/api_exceptions.dart';
|
||||
import 'package:real_estate_mobile/features/auth/data/auth_repository.dart';
|
||||
import 'package:real_estate_mobile/features/auth/data/models/register_request.dart';
|
||||
import 'package:real_estate_mobile/features/auth/data/models/user_model.dart';
|
||||
|
||||
// Auth state
|
||||
enum AuthStatus { initial, loading, success, error }
|
||||
|
||||
class AuthState {
|
||||
final AuthStatus status;
|
||||
final UserModel? user;
|
||||
final String? errorMessage;
|
||||
final List<FieldError> fieldErrors;
|
||||
final String registeredEmail;
|
||||
|
||||
const AuthState({
|
||||
this.status = AuthStatus.initial,
|
||||
this.user,
|
||||
this.errorMessage,
|
||||
this.fieldErrors = const [],
|
||||
this.registeredEmail = '',
|
||||
});
|
||||
|
||||
AuthState copyWith({
|
||||
AuthStatus? status,
|
||||
UserModel? user,
|
||||
String? errorMessage,
|
||||
List<FieldError>? fieldErrors,
|
||||
String? registeredEmail,
|
||||
}) {
|
||||
return AuthState(
|
||||
status: status ?? this.status,
|
||||
user: user ?? this.user,
|
||||
errorMessage: errorMessage,
|
||||
fieldErrors: fieldErrors ?? this.fieldErrors,
|
||||
registeredEmail: registeredEmail ?? this.registeredEmail,
|
||||
);
|
||||
}
|
||||
|
||||
String? getFieldError(String fieldName) {
|
||||
final match = fieldErrors.where((e) => e.field == fieldName);
|
||||
if (match.isEmpty) return null;
|
||||
return match.first.errors.isNotEmpty ? match.first.errors.first : null;
|
||||
}
|
||||
}
|
||||
|
||||
// Auth notifier
|
||||
class AuthNotifier extends StateNotifier<AuthState> {
|
||||
final AuthRepository _repository;
|
||||
|
||||
AuthNotifier(this._repository) : super(const AuthState());
|
||||
|
||||
Future<void> login({
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.loading,
|
||||
errorMessage: null,
|
||||
fieldErrors: [],
|
||||
);
|
||||
|
||||
try {
|
||||
final result = await _repository.login(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
|
||||
if (result['requiresTwoFactor'] == true) {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.error,
|
||||
errorMessage: '2FA verification required',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.success,
|
||||
user: result['user'] as UserModel,
|
||||
);
|
||||
} on ApiException catch (e) {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.error,
|
||||
errorMessage: e.fieldErrors.isNotEmpty
|
||||
? 'Please fix the errors below'
|
||||
: e.message,
|
||||
fieldErrors: e.fieldErrors,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.error,
|
||||
errorMessage: 'An unexpected error occurred',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> register({
|
||||
required String name,
|
||||
required String email,
|
||||
required String password,
|
||||
required String role,
|
||||
}) async {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.loading,
|
||||
errorMessage: null,
|
||||
fieldErrors: [],
|
||||
);
|
||||
|
||||
// Split name into firstName and lastName
|
||||
final nameParts = name.trim().split(RegExp(r'\s+'));
|
||||
final firstName = nameParts.first;
|
||||
final lastName = nameParts.length > 1 ? nameParts.sublist(1).join(' ') : null;
|
||||
|
||||
try {
|
||||
final user = await _repository.register(
|
||||
RegisterRequest(
|
||||
email: email,
|
||||
password: password,
|
||||
role: role,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
),
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.success,
|
||||
user: user,
|
||||
registeredEmail: email,
|
||||
);
|
||||
} on ApiException catch (e) {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.error,
|
||||
errorMessage: e.fieldErrors.isNotEmpty
|
||||
? 'Please fix the errors below'
|
||||
: e.message,
|
||||
fieldErrors: e.fieldErrors,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
status: AuthStatus.error,
|
||||
errorMessage: 'An unexpected error occurred',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
state = const AuthState();
|
||||
}
|
||||
}
|
||||
|
||||
// Providers
|
||||
final authRepositoryProvider = Provider<AuthRepository>((ref) {
|
||||
return AuthRepository();
|
||||
});
|
||||
|
||||
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
||||
return AuthNotifier(ref.watch(authRepositoryProvider));
|
||||
});
|
||||
Reference in New Issue
Block a user