204 lines
5.7 KiB
Dart
204 lines
5.7 KiB
Dart
import 'package:flutter/widgets.dart' show Locale;
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:pocketbase/pocketbase.dart';
|
||
import '../auth/auth_repository.dart';
|
||
import '../services/notification_service.dart';
|
||
import '../../models/tenant.dart';
|
||
import '../../models/user_profile.dart';
|
||
import 'locale_provider.dart';
|
||
|
||
class AuthState {
|
||
const AuthState({
|
||
this.profile,
|
||
this.activeTenant,
|
||
this.memberships = const [],
|
||
this.isLoading = true,
|
||
this.error,
|
||
});
|
||
|
||
final UserProfile? profile;
|
||
final TenantMembership? activeTenant;
|
||
final List<TenantMembership> memberships;
|
||
final bool isLoading;
|
||
final String? error;
|
||
|
||
bool get isAuthenticated => profile != null;
|
||
|
||
AuthState copyWith({
|
||
UserProfile? profile,
|
||
TenantMembership? activeTenant,
|
||
List<TenantMembership>? memberships,
|
||
bool? isLoading,
|
||
String? error,
|
||
bool clearError = false,
|
||
}) =>
|
||
AuthState(
|
||
profile: profile ?? this.profile,
|
||
activeTenant: activeTenant ?? this.activeTenant,
|
||
memberships: memberships ?? this.memberships,
|
||
isLoading: isLoading ?? this.isLoading,
|
||
error: clearError ? null : (error ?? this.error),
|
||
);
|
||
}
|
||
|
||
class AuthNotifier extends StateNotifier<AuthState> {
|
||
AuthNotifier({this.onLocaleLoaded}) : super(const AuthState()) {
|
||
_init();
|
||
}
|
||
|
||
final void Function(String languageCode)? onLocaleLoaded;
|
||
final _repo = AuthRepository.instance;
|
||
|
||
Future<void> _init() async {
|
||
final loggedIn = await _repo.isLoggedIn();
|
||
if (!loggedIn) {
|
||
state = const AuthState(isLoading: false);
|
||
return;
|
||
}
|
||
try {
|
||
final result = await _repo.refreshSession();
|
||
state = AuthState(
|
||
profile: result.user,
|
||
memberships: result.tenants,
|
||
activeTenant:
|
||
result.tenants.isEmpty ? null : result.tenants.first,
|
||
isLoading: false,
|
||
);
|
||
final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab;
|
||
NotificationService.loginUser(result.user.id, isLab: isLab);
|
||
_applyLocale(result.user.preferredLanguage);
|
||
} catch (_) {
|
||
state = const AuthState(isLoading: false);
|
||
}
|
||
}
|
||
|
||
void _applyLocale(String? code) {
|
||
if (code != null && code.isNotEmpty) {
|
||
onLocaleLoaded?.call(code);
|
||
}
|
||
}
|
||
|
||
Future<void> signIn(
|
||
String email,
|
||
String password, {
|
||
required bool rememberSession,
|
||
}) async {
|
||
state = state.copyWith(isLoading: true, clearError: true);
|
||
try {
|
||
final result = await _repo.login(
|
||
email,
|
||
password,
|
||
rememberSession: rememberSession,
|
||
);
|
||
state = AuthState(
|
||
profile: result.user,
|
||
memberships: result.tenants,
|
||
activeTenant:
|
||
result.tenants.isEmpty ? null : result.tenants.first,
|
||
isLoading: false,
|
||
);
|
||
final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab;
|
||
NotificationService.loginUser(result.user.id, isLab: isLab);
|
||
_applyLocale(result.user.preferredLanguage);
|
||
} catch (e) {
|
||
state = state.copyWith(isLoading: false, error: _parseError(e));
|
||
}
|
||
}
|
||
|
||
Future<void> register({
|
||
required String email,
|
||
required String password,
|
||
String? firstName,
|
||
String? lastName,
|
||
}) async {
|
||
state = state.copyWith(isLoading: true, clearError: true);
|
||
try {
|
||
final result = await _repo.register(
|
||
email: email,
|
||
password: password,
|
||
firstName: firstName,
|
||
lastName: lastName,
|
||
);
|
||
state = AuthState(
|
||
profile: result.user,
|
||
memberships: result.tenants,
|
||
activeTenant:
|
||
result.tenants.isEmpty ? null : result.tenants.first,
|
||
isLoading: false,
|
||
);
|
||
} catch (e) {
|
||
state = state.copyWith(isLoading: false, error: _parseError(e));
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
Future<void> signOut() async {
|
||
await _repo.logout();
|
||
await NotificationService.logoutUser();
|
||
state = const AuthState(isLoading: false);
|
||
}
|
||
|
||
void setActiveTenant(TenantMembership membership) {
|
||
state = state.copyWith(activeTenant: membership);
|
||
}
|
||
|
||
Future<void> refresh() async {
|
||
try {
|
||
final result = await _repo.refreshSession();
|
||
final currentId = state.activeTenant?.tenant.id;
|
||
final newActive = currentId != null
|
||
? result.tenants.firstWhere(
|
||
(m) => m.tenant.id == currentId,
|
||
orElse: () => result.tenants.isNotEmpty
|
||
? result.tenants.first
|
||
: state.activeTenant!,
|
||
)
|
||
: (result.tenants.isNotEmpty ? result.tenants.first : null);
|
||
state = state.copyWith(
|
||
profile: result.user,
|
||
memberships: result.tenants,
|
||
activeTenant: newActive,
|
||
);
|
||
} catch (_) {}
|
||
}
|
||
|
||
Future<void> updateLanguage(String languageCode) async {
|
||
final userId = state.profile?.id;
|
||
if (userId == null) return;
|
||
await _repo.updateUserLanguage(userId, languageCode);
|
||
}
|
||
|
||
Future<void> updateTenantInfo({
|
||
required String tenantId,
|
||
required String companyName,
|
||
String? defaultCurrency,
|
||
}) async {
|
||
await _repo.updateTenant(
|
||
tenantId,
|
||
companyName: companyName,
|
||
defaultCurrency: defaultCurrency,
|
||
);
|
||
await refresh();
|
||
}
|
||
|
||
String _parseError(Object e) {
|
||
if (e is ClientException) {
|
||
final code = e.statusCode;
|
||
if (code == 400 || code == 401 || code == 403) {
|
||
return 'E-posta veya şifre hatalı.';
|
||
}
|
||
final msg = e.response['message'] as String? ?? '';
|
||
if (msg.isNotEmpty) return msg;
|
||
}
|
||
return 'Bağlantı hatası. Lütfen tekrar deneyin.';
|
||
}
|
||
}
|
||
|
||
final authProvider =
|
||
StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
||
return AuthNotifier(
|
||
onLocaleLoaded: (code) =>
|
||
ref.read(localeProvider.notifier).setLocale(Locale(code)),
|
||
);
|
||
});
|