Refactor user and theme management to use BLoC pattern; remove provider classes and integrate new services for user and group functionalities
This commit is contained in:
120
lib/blocs/group/group_bloc.dart
Normal file
120
lib/blocs/group/group_bloc.dart
Normal file
@@ -0,0 +1,120 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../services/group_service.dart';
|
||||
import 'group_event.dart';
|
||||
import 'group_state.dart';
|
||||
import '../../data/models/group.dart';
|
||||
|
||||
class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
final GroupService _groupService;
|
||||
StreamSubscription? _groupsSubscription;
|
||||
|
||||
GroupBloc({GroupService? groupService})
|
||||
: _groupService = groupService ?? GroupService(),
|
||||
super(GroupInitial()) {
|
||||
on<GroupLoadRequested>(_onLoadRequested);
|
||||
on<_GroupUpdated>(_onGroupUpdated);
|
||||
on<GroupCreateRequested>(_onCreateRequested);
|
||||
on<GroupUpdateRequested>(_onUpdateRequested);
|
||||
on<GroupDeleteRequested>(_onDeleteRequested);
|
||||
on<GroupMemberAddRequested>(_onMemberAddRequested);
|
||||
on<GroupMemberRemoveRequested>(_onMemberRemoveRequested);
|
||||
}
|
||||
|
||||
Future<void> _onLoadRequested(
|
||||
GroupLoadRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
emit(GroupLoading());
|
||||
|
||||
await _groupsSubscription?.cancel();
|
||||
|
||||
_groupsSubscription = _groupService.getGroupsStreamByUser(event.userId).listen(
|
||||
(groups) => add(_GroupUpdated(groups: groups)),
|
||||
onError: (error) => emit(GroupError(message: error.toString())),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onGroupUpdated(
|
||||
_GroupUpdated event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
emit(GroupLoaded(groups: event.groups));
|
||||
}
|
||||
|
||||
Future<void> _onCreateRequested(
|
||||
GroupCreateRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _groupService.createGroup(event.group);
|
||||
emit(const GroupOperationSuccess(message: 'Groupe créé avec succès'));
|
||||
} catch (e) {
|
||||
emit(GroupError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUpdateRequested(
|
||||
GroupUpdateRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _groupService.updateGroup(event.group);
|
||||
emit(const GroupOperationSuccess(message: 'Groupe mis à jour'));
|
||||
} catch (e) {
|
||||
emit(GroupError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDeleteRequested(
|
||||
GroupDeleteRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _groupService.deleteGroup(event.groupId);
|
||||
emit(const GroupOperationSuccess(message: 'Groupe supprimé'));
|
||||
} catch (e) {
|
||||
emit(GroupError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onMemberAddRequested(
|
||||
GroupMemberAddRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _groupService.addMemberToGroup(event.groupId, event.memberId);
|
||||
emit(const GroupOperationSuccess(message: 'Membre ajouté'));
|
||||
} catch (e) {
|
||||
emit(GroupError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onMemberRemoveRequested(
|
||||
GroupMemberRemoveRequested event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
try {
|
||||
await _groupService.removeMemberFromGroup(event.groupId, event.memberId);
|
||||
emit(const GroupOperationSuccess(message: 'Membre retiré'));
|
||||
} catch (e) {
|
||||
emit(GroupError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_groupsSubscription?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Événement interne pour les mises à jour du stream
|
||||
class _GroupUpdated extends GroupEvent {
|
||||
final List<Group> groups;
|
||||
|
||||
const _GroupUpdated({required this.groups});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groups];
|
||||
}
|
||||
71
lib/blocs/group/group_event.dart
Normal file
71
lib/blocs/group/group_event.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../data/models/group.dart';
|
||||
|
||||
abstract class GroupEvent extends Equatable {
|
||||
const GroupEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GroupLoadRequested extends GroupEvent {
|
||||
final String userId;
|
||||
|
||||
const GroupLoadRequested({required this.userId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [userId];
|
||||
}
|
||||
|
||||
class GroupCreateRequested extends GroupEvent {
|
||||
final Group group;
|
||||
|
||||
const GroupCreateRequested({required this.group});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [group];
|
||||
}
|
||||
|
||||
class GroupUpdateRequested extends GroupEvent {
|
||||
final Group group;
|
||||
|
||||
const GroupUpdateRequested({required this.group});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [group];
|
||||
}
|
||||
|
||||
class GroupDeleteRequested extends GroupEvent {
|
||||
final String groupId;
|
||||
|
||||
const GroupDeleteRequested({required this.groupId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId];
|
||||
}
|
||||
|
||||
class GroupMemberAddRequested extends GroupEvent {
|
||||
final String groupId;
|
||||
final String memberId;
|
||||
|
||||
const GroupMemberAddRequested({
|
||||
required this.groupId,
|
||||
required this.memberId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, memberId];
|
||||
}
|
||||
|
||||
class GroupMemberRemoveRequested extends GroupEvent {
|
||||
final String groupId;
|
||||
final String memberId;
|
||||
|
||||
const GroupMemberRemoveRequested({
|
||||
required this.groupId,
|
||||
required this.memberId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groupId, memberId];
|
||||
}
|
||||
40
lib/blocs/group/group_state.dart
Normal file
40
lib/blocs/group/group_state.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../data/models/group.dart';
|
||||
|
||||
abstract class GroupState extends Equatable {
|
||||
const GroupState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GroupInitial extends GroupState {}
|
||||
|
||||
class GroupLoading extends GroupState {}
|
||||
|
||||
class GroupLoaded extends GroupState {
|
||||
final List<Group> groups;
|
||||
|
||||
const GroupLoaded({required this.groups});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groups];
|
||||
}
|
||||
|
||||
class GroupOperationSuccess extends GroupState {
|
||||
final String message;
|
||||
|
||||
const GroupOperationSuccess({required this.message});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class GroupError extends GroupState {
|
||||
final String message;
|
||||
|
||||
const GroupError({required this.message});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
@@ -13,6 +13,7 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
: _tripRepository = tripRepository,
|
||||
super(TripInitial()) {
|
||||
on<TripLoadRequested>(_onLoadRequested);
|
||||
on<_TripUpdated>(_onTripUpdated);
|
||||
on<TripCreateRequested>(_onCreateRequested);
|
||||
on<TripUpdateRequested>(_onUpdateRequested);
|
||||
on<TripDeleteRequested>(_onDeleteRequested);
|
||||
@@ -29,11 +30,18 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
await _tripsSubscription?.cancel();
|
||||
|
||||
_tripsSubscription = _tripRepository.getUserTrips(event.userId).listen(
|
||||
(trips) => add(const _TripUpdated(trips: [])), // Sera géré par un événement interne
|
||||
(trips) => add(_TripUpdated(trips: trips)),
|
||||
onError: (error) => emit(TripError(message: error.toString())),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onTripUpdated(
|
||||
_TripUpdated event,
|
||||
Emitter<TripState> emit,
|
||||
) async {
|
||||
emit(TripLoaded(trips: event.trips));
|
||||
}
|
||||
|
||||
Future<void> _onCreateRequested(
|
||||
TripCreateRequested event,
|
||||
Emitter<TripState> emit,
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'user_event.dart' as event;
|
||||
import 'user_state.dart' as state;
|
||||
|
||||
class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
final FirebaseAuth _auth = FirebaseAuth.instance;
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
|
||||
UserBloc() : super(state.UserInitial()) {
|
||||
on<event.UserInitialized>(_onUserInitialized);
|
||||
on<event.LoadUser>(_onLoadUser);
|
||||
on<event.UserUpdated>(_onUserUpdated);
|
||||
on<event.UserLoggedOut>(_onUserLoggedOut);
|
||||
}
|
||||
|
||||
Future<void> _onUserInitialized(
|
||||
event.UserInitialized event,
|
||||
Emitter<state.UserState> emit,
|
||||
) async {
|
||||
emit(state.UserLoading());
|
||||
|
||||
try {
|
||||
final currentUser = _auth.currentUser;
|
||||
|
||||
if (currentUser == null) {
|
||||
emit(state.UserError('Aucun utilisateur connecté'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupérer les données utilisateur depuis Firestore
|
||||
final userDoc = await _firestore
|
||||
.collection('users')
|
||||
.doc(currentUser.uid)
|
||||
.get();
|
||||
|
||||
if (!userDoc.exists) {
|
||||
// Créer un utilisateur par défaut si non existant
|
||||
final defaultUser = state.UserModel(
|
||||
id: currentUser.uid,
|
||||
email: currentUser.email ?? '',
|
||||
prenom: currentUser.displayName ?? 'Voyageur',
|
||||
);
|
||||
|
||||
await _firestore
|
||||
.collection('users')
|
||||
.doc(currentUser.uid)
|
||||
.set(defaultUser.toJson());
|
||||
|
||||
emit(state.UserLoaded(defaultUser));
|
||||
} else {
|
||||
final user = state.UserModel.fromJson({
|
||||
'id': currentUser.uid,
|
||||
...userDoc.data()!,
|
||||
});
|
||||
emit(state.UserLoaded(user));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.UserError('Erreur lors du chargement de l\'utilisateur: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadUser(
|
||||
event.LoadUser event,
|
||||
Emitter<state.UserState> emit,
|
||||
) async {
|
||||
emit(state.UserLoading());
|
||||
|
||||
try {
|
||||
final userDoc = await _firestore
|
||||
.collection('users')
|
||||
.doc(event.userId)
|
||||
.get();
|
||||
|
||||
if (userDoc.exists) {
|
||||
final user = state.UserModel.fromJson({
|
||||
'id': event.userId,
|
||||
...userDoc.data()!,
|
||||
});
|
||||
emit(state.UserLoaded(user));
|
||||
} else {
|
||||
emit(state.UserError('Utilisateur non trouvé'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.UserError('Erreur lors du chargement: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUserUpdated(
|
||||
event.UserUpdated event,
|
||||
Emitter<state.UserState> emit,
|
||||
) async {
|
||||
if (this.state is state.UserLoaded) {
|
||||
final currentUser = (this.state as state.UserLoaded).user;
|
||||
|
||||
try {
|
||||
await _firestore
|
||||
.collection('users')
|
||||
.doc(currentUser.id)
|
||||
.update(event.userData);
|
||||
|
||||
final updatedDoc = await _firestore
|
||||
.collection('users')
|
||||
.doc(currentUser.id)
|
||||
.get();
|
||||
|
||||
final updatedUser = state.UserModel.fromJson({
|
||||
'id': currentUser.id,
|
||||
...updatedDoc.data()!,
|
||||
});
|
||||
|
||||
emit(state.UserLoaded(updatedUser));
|
||||
} catch (e) {
|
||||
emit(state.UserError('Erreur lors de la mise à jour: $e'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUserLoggedOut(
|
||||
event.UserLoggedOut event,
|
||||
Emitter<state.UserState> emit,
|
||||
) async {
|
||||
emit(state.UserInitial());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
abstract class UserEvent {}
|
||||
|
||||
class UserInitialized extends UserEvent {}
|
||||
|
||||
class UserLoaded extends UserEvent {
|
||||
final String userId;
|
||||
|
||||
UserLoaded(this.userId);
|
||||
}
|
||||
|
||||
class UserUpdated extends UserEvent {
|
||||
final Map<String, dynamic> userData;
|
||||
|
||||
UserUpdated(this.userData);
|
||||
}
|
||||
|
||||
class UserLoggedOut extends UserEvent {}
|
||||
class LoadUser extends UserEvent {
|
||||
final String userId;
|
||||
|
||||
LoadUser(this.userId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class UserState extends Equatable {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class UserInitial extends UserState {}
|
||||
|
||||
class UserLoading extends UserState {}
|
||||
|
||||
class UserLoaded extends UserState {
|
||||
final UserModel user;
|
||||
|
||||
UserLoaded(this.user);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [user];
|
||||
}
|
||||
|
||||
class UserError extends UserState {
|
||||
final String message;
|
||||
|
||||
UserError(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
// Modèle utilisateur simple
|
||||
class UserModel {
|
||||
final String id;
|
||||
final String email;
|
||||
final String prenom;
|
||||
final String? nom;
|
||||
|
||||
UserModel({
|
||||
required this.id,
|
||||
required this.email,
|
||||
required this.prenom,
|
||||
this.nom,
|
||||
});
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
return UserModel(
|
||||
id: json['id'] ?? '',
|
||||
email: json['email'] ?? '',
|
||||
prenom: json['prenom'] ?? 'Voyageur',
|
||||
nom: json['nom'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'email': email,
|
||||
'prenom': prenom,
|
||||
'nom': nom,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user