Files
flux/lib/core/blocs/session/session_cubit.dart
2026-05-13 12:41:07 +02:00

172 lines
5.7 KiB
Dart

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/data/core_repository.dart';
import 'package:flux/features/company/models/company_model.dart';
import 'package:flux/features/master_data/staff/models/staff_member_model.dart';
import 'package:flux/features/master_data/store/models/store_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:collection/collection.dart'; // Per firstWhereOrNull
// Importa lo state con l'Enum e il CoreRepository...
part 'session_state.dart';
class SessionCubit extends Cubit<SessionState> {
final CoreRepository _repository;
final SharedPreferences _prefs; // Iniettato via GetIt
final SupabaseClient _supabase = Supabase.instance.client;
static const String _lastStoreKey = 'last_selected_store_id';
SessionCubit(this._repository, this._prefs)
: super(const SessionState(status: SessionStatus.initial)) {
initializeSession();
// Possiamo metterci in ascolto dei cambiamenti di Auth (Login/Logout)
_supabase.auth.onAuthStateChange.listen((data) {
final AuthChangeEvent event = data.event;
if (event == AuthChangeEvent.signedIn) {
initializeSession();
} else if (event == AuthChangeEvent.signedOut) {
emit(const SessionState(status: SessionStatus.unauthenticated));
}
});
}
Future<void> initializeSession() async {
final user = _supabase.auth.currentUser;
if (user == null) {
return emit(state.copyWith(status: SessionStatus.unauthenticated));
}
try {
// 1. CHI È QUESTO UTENTE? (Vediamo se ha un profilo staff, che sia Invitato o Admin)
StaffMemberModel? staff = await _repository.getStaffMemberByUserId(
user.id,
);
CompanyModel? company;
if (staff != null) {
// --- LA MAGIA DEL SENSORE ---
if (staff.hasJoined == false) {
// È la primissima volta che entra! Aggiorniamo il DB.
await _repository.updateStaffMember(staff.id!, {'has_joined': true});
// Aggiorniamo anche il nostro modello in memoria per questa sessione
staff = staff.copyWith(hasJoined: true);
}
company = await _repository.getCompanyById(staff.companyId);
} else {
// È l'Admin in onboarding
company = await _repository.getCompanyByOwnerId(user.id);
}
// 1. Controllo Azienda
if (staff != null) {
// L'utente esiste già nel sistema! Carichiamo l'azienda per cui lavora
company = await _repository.getCompanyById(staff.companyId);
} else {
// L'utente non ha profilo. Probabilmente è l'Admin che ha appena
// fatto Sign Up e sta iniziando l'Onboarding
company = await _repository.getCompanyByOwnerId(user.id);
}
if (company == null) {
return emit(
state.copyWith(
status: SessionStatus.onboardingRequired,
user: user,
onboardingStep: OnboardingStep.company,
),
);
} else {
emit(state.copyWith(company: company));
}
// 2. Controllo Negozi
final stores = await _repository.getStoresByCompanyId(company.id!);
if (stores.isEmpty) {
return emit(
state.copyWith(
status: SessionStatus.onboardingRequired,
user: user,
company: company,
onboardingStep: OnboardingStep.store,
),
);
} else {
emit(state.copyWith(currentStore: stores.first));
}
// 3. Controllo Staff (Paziente Zero)
if (staff == null) {
return emit(
state.copyWith(
status: SessionStatus.onboardingRequired,
user: user,
company: company,
onboardingStep: OnboardingStep.staff,
),
);
}
// --- TUTTO COMPLETATO: LOGICA DEL NEGOZIO DI DEFAULT ---
// Leggiamo l'ultimo negozio dalle SharedPreferences
final lastStoreId = _prefs.getString(_lastStoreKey);
// Cerchiamo quel negozio nella lista. Se non c'è (magari è stato eliminato), prendiamo il primo.
final activeStore =
stores.firstWhereOrNull((s) => s.id == lastStoreId) ?? stores.first;
// Se non avevamo il lastStoreId salvato, salviamolo ora
if (lastStoreId != activeStore.id && activeStore.id != null) {
await _prefs.setString(_lastStoreKey, activeStore.id!);
}
// 4. BENVENUTO A BORDO
emit(
state.copyWith(
status: SessionStatus.authenticated,
user: user,
company: company,
currentStore: activeStore,
currentStaffMember: staff,
onboardingStep: OnboardingStep.none, // Svuotiamo l'onboarding
),
);
} catch (e) {
// Se esplode il database, non lasciamo l'app freezata in 'initial'
emit(
state.copyWith(
status: SessionStatus
.unauthenticated, // O un nuovo stato SessionStatus.error
),
);
}
}
void updateCurrentCompany(CompanyModel newCompany) {
emit(state.copyWith(company: newCompany));
}
// --- FUNZIONE EXTRA: CAMBIO NEGOZIO DALLA DASHBOARD ---
Future<void> changeStore(StoreModel newStore) async {
if (newStore.id != null) {
await _prefs.setString(_lastStoreKey, newStore.id!);
emit(state.copyWith(currentStore: newStore));
}
}
// --- LOGOUT ---
Future<void> signOut() async {
await _supabase.auth.signOut();
// Non serve emettere stato qui, ci pensa il listener nel costruttore!
}
void setIsMobileDevice(bool isMobile) {
emit(state.copyWith(isMobileDevice: isMobile));
}
void setIsSingleUserMode(bool isSingleUser) {
emit(state.copyWith(isSingleUserMode: isSingleUser));
}
}