This commit is contained in:
2026-05-21 14:43:47 +02:00
parent b9c3eb7091
commit 5b2702daed
16 changed files with 1266 additions and 44 deletions

View File

@@ -0,0 +1,80 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/features/notes/data/notes_repository.dart';
import 'package:flux/features/notes/models/note_model.dart';
import 'package:get_it/get_it.dart';
part 'notes_event.dart';
part 'notes_state.dart';
class NotesBloc extends Bloc<NotesEvent, NotesState> {
final NotesRepository _repository = GetIt.I.get<NotesRepository>();
final String _companyId = GetIt.I.get<SessionCubit>().state.company!.id!;
final String _currentStaffId = GetIt.I
.get<SessionCubit>()
.state
.currentStaffMember!
.id!;
NotesBloc() : super(const NotesState()) {
on<SubscribeToNotesRequested>(_onSubscribeToNotesRequested);
on<NoteSavedRequested>(_onNoteSavedRequested);
on<NoteDeletedRequested>(_onNoteDeletedRequested);
// Facciamo partire l'ascolto in tempo reale al boot del BLoC
add(SubscribeToNotesRequested());
}
Future<void> _onSubscribeToNotesRequested(
SubscribeToNotesRequested event,
Emitter<NotesState> emit,
) async {
emit(state.copyWith(status: NotesStatus.loading));
// Usiamo l'emit.forEach sullo stream pulito del repository
await emit.forEach<List<NoteModel>>(
_repository.notesStream(
companyId: _companyId,
currentStaffId: _currentStaffId,
),
onData: (notesList) {
return state.copyWith(status: NotesStatus.success, notes: notesList);
},
onError: (error, stackTrace) {
return state.copyWith(
status: NotesStatus.failure,
errorMessage: 'Errore nello stream realtime: $error',
);
},
);
}
Future<void> _onNoteSavedRequested(
NoteSavedRequested event,
Emitter<NotesState> emit,
) async {
try {
await _repository.saveNote(event.note);
// Non serve fare l'emit! Ci pensa lo stream a far rimbalzare i dati aggiornati
} catch (e) {
emit(
state.copyWith(status: NotesStatus.failure, errorMessage: e.toString()),
);
}
}
Future<void> _onNoteDeletedRequested(
NoteDeletedRequested event,
Emitter<NotesState> emit,
) async {
try {
await _repository.deleteNote(event.noteId);
// Anche qui, lo stream rileva la cancellazione in automatico
} catch (e) {
emit(
state.copyWith(status: NotesStatus.failure, errorMessage: e.toString()),
);
}
}
}

View File

@@ -0,0 +1,17 @@
part of 'notes_bloc.dart';
sealed class NotesEvent {}
/// Fa partire lo stream e gestisce sia il caricamento iniziale che il realtime
class SubscribeToNotesRequested extends NotesEvent {}
class NoteDeletedRequested extends NotesEvent {
final String noteId;
NoteDeletedRequested(this.noteId);
}
/// Salva o aggiorna una nota
class NoteSavedRequested extends NotesEvent {
final NoteModel note;
NoteSavedRequested(this.note);
}

View File

@@ -0,0 +1,39 @@
part of 'notes_bloc.dart';
enum NotesStatus { initial, loading, success, failure }
class NotesState {
final NotesStatus status;
final List<NoteModel> notes;
final String? errorMessage;
const NotesState({
this.status = NotesStatus.initial,
this.notes = const [],
this.errorMessage,
});
NotesState copyWith({
NotesStatus? status,
List<NoteModel>? notes,
String? errorMessage,
}) {
return NotesState(
status: status ?? this.status,
notes: notes ?? this.notes,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is NotesState &&
other.status == status &&
listEquals(other.notes, notes) &&
other.errorMessage == errorMessage;
}
@override
int get hashCode => status.hashCode ^ notes.hashCode ^ errorMessage.hashCode;
}