feat: integrate ErrorService for consistent error display and standardize bloc error messages.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
/// accountBloc.close();
|
||||
/// ```
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:travel_mate/services/error_service.dart';
|
||||
@@ -30,7 +31,7 @@ import 'account_state.dart';
|
||||
import '../../repositories/account_repository.dart';
|
||||
import '../../models/account.dart';
|
||||
|
||||
class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
final AccountRepository _repository;
|
||||
StreamSubscription? _accountsSubscription;
|
||||
final _errorService = ErrorService();
|
||||
@@ -47,21 +48,27 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
Future<void> _onLoadAccountsByUserId(
|
||||
LoadAccountsByUserId event,
|
||||
Emitter<AccountState> emit,
|
||||
) async {
|
||||
) async {
|
||||
try {
|
||||
emit(AccountLoading());
|
||||
await _accountsSubscription?.cancel();
|
||||
_accountsSubscription = _repository.getAccountByUserId(event.userId).listen(
|
||||
(accounts) {
|
||||
add(_AccountsUpdated(accounts));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_AccountsUpdated([], error: error.toString()));
|
||||
},
|
||||
);
|
||||
_accountsSubscription = _repository
|
||||
.getAccountByUserId(event.userId)
|
||||
.listen(
|
||||
(accounts) {
|
||||
add(_AccountsUpdated(accounts));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_AccountsUpdated([], error: error.toString()));
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(AccountError(e.toString()));
|
||||
_errorService.logError(
|
||||
'AccountBloc',
|
||||
'Error loading accounts: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const AccountError('Impossible de charger les comptes'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +96,12 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
);
|
||||
emit(AccountOperationSuccess('Compte créé avec succès. ID: $accountId'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(AccountError('Erreur lors de la création du compte: ${e.toString()}'));
|
||||
_errorService.logError(
|
||||
'AccountBloc',
|
||||
'Error creating account: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const AccountError('Impossible de créer le compte'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +109,7 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
CreateAccountWithMembers event,
|
||||
Emitter<AccountState> emit,
|
||||
) async {
|
||||
try{
|
||||
try {
|
||||
emit(AccountLoading());
|
||||
final accountId = await _repository.createAccountWithMembers(
|
||||
account: event.account,
|
||||
@@ -106,8 +117,12 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
);
|
||||
emit(AccountOperationSuccess('Compte créé avec succès. ID: $accountId'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(AccountError('Erreur lors de la création du compte: ${e.toString()}'));
|
||||
_errorService.logError(
|
||||
'AccountBloc',
|
||||
'Error creating account with members: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const AccountError('Impossible de créer le compte'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,8 +135,12 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
await _repository.addMemberToAccount(event.accountId, event.member);
|
||||
emit(AccountOperationSuccess('Membre ajouté avec succès'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(AccountError('Erreur lors de l\'ajout du membre: ${e.toString()}'));
|
||||
_errorService.logError(
|
||||
'AccountBloc',
|
||||
'Error adding member: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const AccountError('Impossible d\'ajouter le membre'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,11 +150,18 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
|
||||
) async {
|
||||
try {
|
||||
emit(AccountLoading());
|
||||
await _repository.removeMemberFromAccount(event.accountId, event.memberId);
|
||||
await _repository.removeMemberFromAccount(
|
||||
event.accountId,
|
||||
event.memberId,
|
||||
);
|
||||
emit(AccountOperationSuccess('Membre supprimé avec succès'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(AccountError('Erreur lors de la suppression du membre: ${e.toString()}'));
|
||||
_errorService.logError(
|
||||
'AccountBloc',
|
||||
'Error removing member: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const AccountError('Impossible de supprimer le membre'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,4 +180,4 @@ class _AccountsUpdated extends AccountEvent {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [accounts, error];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,11 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
emit(
|
||||
ActivityLoaded(activities: activities, filteredActivities: activities),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur chargement activités: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de charger les activités'));
|
||||
}
|
||||
@@ -83,10 +84,11 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
emit(
|
||||
ActivityLoaded(activities: activities, filteredActivities: activities),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur chargement activités: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de charger les activités'));
|
||||
}
|
||||
@@ -112,8 +114,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
// Recharger les activités pour mettre à jour l'UI
|
||||
add(LoadActivities(event.tripId));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur mise à jour date: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur mise à jour date: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de mettre à jour la date'));
|
||||
}
|
||||
}
|
||||
@@ -162,8 +168,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur recherche activités: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur recherche activités: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de rechercher les activités'));
|
||||
}
|
||||
}
|
||||
@@ -211,10 +221,11 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur recherche activités avec coordonnées: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de rechercher les activités'));
|
||||
}
|
||||
@@ -240,8 +251,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
emit(
|
||||
ActivitySearchResults(searchResults: searchResults, query: event.query),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur recherche textuelle: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur recherche textuelle: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de rechercher les activités'));
|
||||
}
|
||||
}
|
||||
@@ -292,8 +307,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur ajout activité: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur ajout activité: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
||||
}
|
||||
}
|
||||
@@ -350,8 +369,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur ajout activité: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur ajout activité: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
||||
}
|
||||
}
|
||||
@@ -418,8 +441,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible d\'ajouter les activités'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur ajout en lot: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur ajout en lot: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible d\'ajouter les activités'));
|
||||
}
|
||||
}
|
||||
@@ -479,8 +506,8 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible d\'enregistrer le vote'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur vote: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('activity_bloc', 'Erreur vote: $e', stackTrace);
|
||||
emit(const ActivityError('Impossible d\'enregistrer le vote'));
|
||||
}
|
||||
}
|
||||
@@ -511,8 +538,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible de supprimer l\'activité'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur suppression: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur suppression: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de supprimer l\'activité'));
|
||||
}
|
||||
}
|
||||
@@ -593,8 +624,12 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
} else {
|
||||
emit(const ActivityError('Impossible de mettre à jour l\'activité'));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur mise à jour: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'activity_bloc',
|
||||
'Erreur mise à jour: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const ActivityError('Impossible de mettre à jour l\'activité'));
|
||||
}
|
||||
}
|
||||
@@ -614,8 +649,8 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
||||
vote: 1,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('activity_bloc', 'Erreur favori: $e');
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('activity_bloc', 'Erreur favori: $e', stackTrace);
|
||||
emit(const ActivityError('Impossible de modifier les favoris'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const AuthError(message: 'Invalid email or password'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const AuthError(message: 'Failed to create account'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const AuthError(message: 'Failed to create account with Google'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const AuthError(message: 'Failed to create account with Apple'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
await _authRepository.resetPassword(event.email);
|
||||
emit(AuthPasswordResetSent(email: event.email));
|
||||
} catch (e) {
|
||||
emit(AuthError(message: e.toString()));
|
||||
emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +105,13 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
emit(
|
||||
GroupBalancesLoaded(balances: userBalances, settlements: settlements),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceBloc', 'Error loading balance: $e');
|
||||
emit(BalanceError(e.toString()));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'BalanceBloc',
|
||||
'Error loading balance: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const BalanceError('Impossible de charger la balance'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,9 +147,13 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
emit(
|
||||
GroupBalancesLoaded(balances: userBalances, settlements: settlements),
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceBloc', 'Error refreshing balance: $e');
|
||||
emit(BalanceError(e.toString()));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'BalanceBloc',
|
||||
'Error refreshing balance: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const BalanceError('Impossible de rafraîchir la balance'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,9 +182,15 @@ class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
|
||||
|
||||
// Reload balance after settlement
|
||||
add(RefreshBalance(event.groupId));
|
||||
} catch (e) {
|
||||
_errorService.logError('BalanceBloc', 'Error marking settlement: $e');
|
||||
emit(BalanceError(e.toString()));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'BalanceBloc',
|
||||
'Error marking settlement: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(
|
||||
const BalanceError('Impossible de marquer le règlement comme terminé'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
);
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error loading expenses: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible de charger les dépenses'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
emit(const ExpenseOperationSuccess('Expense created successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error creating expense: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible de créer la dépense'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
emit(const ExpenseOperationSuccess('Expense updated successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error updating expense: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible de mettre à jour la dépense'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
emit(const ExpenseOperationSuccess('Expense deleted successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error deleting expense: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible de supprimer la dépense'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
emit(const ExpenseOperationSuccess('Payment marked as completed'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error marking split as paid: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible de marquer comme payé'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
|
||||
emit(const ExpenseOperationSuccess('Expense archived successfully'));
|
||||
} catch (e) {
|
||||
_errorService.logError('ExpenseBloc', 'Error archiving expense: $e');
|
||||
emit(ExpenseError(e.toString()));
|
||||
emit(const ExpenseError('Impossible d\'archiver la dépense'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// final groupBloc = GroupBloc(groupRepository);
|
||||
///
|
||||
///
|
||||
/// // Load groups for a user
|
||||
/// groupBloc.add(LoadGroupsByUserId('userId123'));
|
||||
///
|
||||
///
|
||||
/// // Create a new group with members
|
||||
/// groupBloc.add(CreateGroupWithMembers(
|
||||
/// group: newGroup,
|
||||
@@ -32,6 +32,7 @@
|
||||
/// ));
|
||||
/// ```
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:travel_mate/services/error_service.dart';
|
||||
@@ -44,18 +45,18 @@ import '../../models/group.dart';
|
||||
class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
/// Repository for group data operations
|
||||
final GroupRepository _repository;
|
||||
|
||||
|
||||
/// Subscription to group stream for real-time updates
|
||||
StreamSubscription? _groupsSubscription;
|
||||
|
||||
|
||||
/// Service for error handling and logging
|
||||
final _errorService = ErrorService();
|
||||
|
||||
/// Constructor for GroupBloc.
|
||||
///
|
||||
///
|
||||
/// Initializes the bloc with the group repository and sets up event handlers
|
||||
/// for all group-related operations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [_repository]: Repository for group data operations
|
||||
GroupBloc(this._repository) : super(GroupInitial()) {
|
||||
@@ -71,39 +72,45 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadGroupsByUserId] events.
|
||||
///
|
||||
///
|
||||
/// Loads all groups for a specific user with real-time updates via stream subscription.
|
||||
/// Cancels any existing subscription before creating a new one to prevent memory leaks.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The LoadGroupsByUserId event containing the user ID
|
||||
/// [emit]: State emitter function
|
||||
Future<void> _onLoadGroupsByUserId(
|
||||
LoadGroupsByUserId event,
|
||||
Emitter<GroupState> emit,
|
||||
) async {
|
||||
) async {
|
||||
try {
|
||||
emit(GroupLoading());
|
||||
await _groupsSubscription?.cancel();
|
||||
_groupsSubscription = _repository.getGroupsByUserId(event.userId).listen(
|
||||
(groups) {
|
||||
add(_GroupsUpdated(groups));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_GroupsUpdated([], error: error.toString()));
|
||||
},
|
||||
);
|
||||
_groupsSubscription = _repository
|
||||
.getGroupsByUserId(event.userId)
|
||||
.listen(
|
||||
(groups) {
|
||||
add(_GroupsUpdated(groups));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_GroupsUpdated([], error: error.toString()));
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(e.toString(), stackTrace);
|
||||
emit(GroupError(e.toString()));
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error loading groups: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de charger les groupes'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [_GroupsUpdated] events.
|
||||
///
|
||||
///
|
||||
/// Processes real-time updates from the group stream, either emitting
|
||||
/// the updated group list or an error state if the stream encountered an error.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The _GroupsUpdated event containing groups or error information
|
||||
/// [emit]: State emitter function
|
||||
@@ -120,10 +127,10 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadGroupsByTrip] events.
|
||||
///
|
||||
///
|
||||
/// Loads the group associated with a specific trip. Since each trip typically
|
||||
/// has one primary group, this returns a single group or an empty list.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The LoadGroupsByTrip event containing the trip ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -139,16 +146,21 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
} else {
|
||||
emit(const GroupsLoaded([]));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GroupError(e.toString()));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error loading group by trip: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de charger le groupe du voyage'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [CreateGroup] events.
|
||||
///
|
||||
///
|
||||
/// Creates a new group without any initial members. The group creator
|
||||
/// can add members later using AddMemberToGroup events.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The CreateGroup event containing the group data
|
||||
/// [emit]: State emitter function
|
||||
@@ -164,17 +176,22 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
);
|
||||
emit(GroupCreated(groupId: groupId));
|
||||
emit(const GroupOperationSuccess('Group created successfully'));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during creation: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error creating group: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de créer le groupe'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [CreateGroupWithMembers] events.
|
||||
///
|
||||
///
|
||||
/// Creates a new group with an initial set of members. This is useful
|
||||
/// for setting up complete groups in one operation, such as when
|
||||
/// planning a trip with known participants.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The CreateGroupWithMembers event containing group data and member list
|
||||
/// [emit]: State emitter function
|
||||
@@ -189,16 +206,21 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
members: event.members,
|
||||
);
|
||||
emit(GroupCreated(groupId: groupId));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during creation: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error creating group with members: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de créer le groupe'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [AddMemberToGroup] events.
|
||||
///
|
||||
///
|
||||
/// Adds a new member to an existing group. The member will be able to
|
||||
/// participate in group expenses and access group features.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The AddMemberToGroup event containing group ID and member data
|
||||
/// [emit]: State emitter function
|
||||
@@ -209,16 +231,21 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
try {
|
||||
await _repository.addMember(event.groupId, event.member);
|
||||
emit(const GroupOperationSuccess('Member added'));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during addition: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error adding member: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible d\'ajouter le membre'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [RemoveMemberFromGroup] events.
|
||||
///
|
||||
///
|
||||
/// Removes a member from a group. This will affect expense calculations
|
||||
/// and the member will no longer have access to group features.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The RemoveMemberFromGroup event containing group ID and user ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -229,16 +256,21 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
try {
|
||||
await _repository.removeMember(event.groupId, event.userId);
|
||||
emit(const GroupOperationSuccess('Member removed'));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during removal: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error removing member: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de supprimer le membre'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [UpdateGroup] events.
|
||||
///
|
||||
///
|
||||
/// Updates group information such as name, description, or settings.
|
||||
/// Member lists are managed through separate add/remove member events.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The UpdateGroup event containing group ID and updated group data
|
||||
/// [emit]: State emitter function
|
||||
@@ -249,16 +281,21 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
try {
|
||||
await _repository.updateGroup(event.groupId, event.group);
|
||||
emit(const GroupOperationSuccess('Group updated'));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during update: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error updating group: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de mettre à jour le groupe'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [DeleteGroup] events.
|
||||
///
|
||||
///
|
||||
/// Permanently deletes a group and all associated data. This action
|
||||
/// cannot be undone and will affect all group members and expenses.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The DeleteGroup event containing the trip ID to delete
|
||||
/// [emit]: State emitter function
|
||||
@@ -269,13 +306,18 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
try {
|
||||
await _repository.deleteGroup(event.tripId);
|
||||
emit(const GroupOperationSuccess('Group deleted'));
|
||||
} catch (e) {
|
||||
emit(GroupError('Error during deletion: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'GroupBloc',
|
||||
'Error deleting group: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const GroupError('Impossible de supprimer le groupe'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleans up resources when the bloc is closed.
|
||||
///
|
||||
///
|
||||
/// Cancels the group stream subscription to prevent memory leaks
|
||||
/// and ensure proper disposal of resources.
|
||||
@override
|
||||
@@ -286,18 +328,18 @@ class GroupBloc extends Bloc<GroupEvent, GroupState> {
|
||||
}
|
||||
|
||||
/// Private event for handling real-time group updates from streams.
|
||||
///
|
||||
///
|
||||
/// This internal event is used to process updates from the group stream
|
||||
/// subscription and emit appropriate states based on the received data.
|
||||
class _GroupsUpdated extends GroupEvent {
|
||||
/// List of groups received from the stream
|
||||
final List<Group> groups;
|
||||
|
||||
|
||||
/// Error message if the stream encountered an error
|
||||
final String? error;
|
||||
|
||||
/// Creates a _GroupsUpdated event.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [groups]: List of groups from the stream update
|
||||
/// [error]: Optional error message if stream failed
|
||||
@@ -305,4 +347,4 @@ class _GroupsUpdated extends GroupEvent {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [groups, error];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// final messageBloc = MessageBloc();
|
||||
///
|
||||
///
|
||||
/// // Load messages for a group
|
||||
/// messageBloc.add(LoadMessages('groupId123'));
|
||||
///
|
||||
///
|
||||
/// // Send a message
|
||||
/// messageBloc.add(SendMessage(
|
||||
/// groupId: 'groupId123',
|
||||
@@ -30,7 +30,7 @@
|
||||
/// senderId: 'userId123',
|
||||
/// senderName: 'John Doe',
|
||||
/// ));
|
||||
///
|
||||
///
|
||||
/// // React to a message
|
||||
/// messageBloc.add(ReactToMessage(
|
||||
/// groupId: 'groupId123',
|
||||
@@ -40,6 +40,7 @@
|
||||
/// ));
|
||||
/// ```
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../models/message.dart';
|
||||
@@ -52,23 +53,23 @@ import 'message_state.dart';
|
||||
class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
/// Service for message operations and business logic
|
||||
final MessageService _messageService;
|
||||
|
||||
|
||||
/// Subscription to message stream for real-time updates
|
||||
StreamSubscription<List<Message>>? _messagesSubscription;
|
||||
|
||||
/// Constructor for MessageBloc.
|
||||
///
|
||||
///
|
||||
/// Initializes the bloc with an optional message service. If no service is provided,
|
||||
/// creates a default MessageService with MessageRepository. Sets up event handlers
|
||||
/// for all message-related operations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [messageService]: Optional service for message operations (auto-created if null)
|
||||
MessageBloc({MessageService? messageService})
|
||||
: _messageService = messageService ?? MessageService(
|
||||
messageRepository: MessageRepository(),
|
||||
),
|
||||
super(MessageInitial()) {
|
||||
: _messageService =
|
||||
messageService ??
|
||||
MessageService(messageRepository: MessageRepository()),
|
||||
super(MessageInitial()) {
|
||||
on<LoadMessages>(_onLoadMessages);
|
||||
on<SendMessage>(_onSendMessage);
|
||||
on<DeleteMessage>(_onDeleteMessage);
|
||||
@@ -79,10 +80,10 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadMessages] events.
|
||||
///
|
||||
///
|
||||
/// Loads messages for a specific group with real-time updates via stream subscription.
|
||||
/// Cancels any existing subscription before creating a new one to prevent memory leaks.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The LoadMessages event containing the group ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -101,31 +102,28 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
add(_MessagesUpdated(messages: messages, groupId: event.groupId));
|
||||
},
|
||||
onError: (error) {
|
||||
add(_MessagesError('Error loading messages: $error'));
|
||||
add(const _MessagesError('Impossible de charger les messages'));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Handles [_MessagesUpdated] events.
|
||||
///
|
||||
///
|
||||
/// Processes real-time updates from the message stream, emitting the
|
||||
/// updated message list with the associated group ID.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The _MessagesUpdated event containing messages and group ID
|
||||
/// [emit]: State emitter function
|
||||
void _onMessagesUpdated(
|
||||
_MessagesUpdated event,
|
||||
Emitter<MessageState> emit,
|
||||
) {
|
||||
void _onMessagesUpdated(_MessagesUpdated event, Emitter<MessageState> emit) {
|
||||
emit(MessagesLoaded(messages: event.messages, groupId: event.groupId));
|
||||
}
|
||||
|
||||
/// Handles [SendMessage] events.
|
||||
///
|
||||
///
|
||||
/// Sends a new message to a group chat. The stream subscription will
|
||||
/// automatically update the UI with the new message, so no state is emitted here.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The SendMessage event containing message details
|
||||
/// [emit]: State emitter function
|
||||
@@ -142,15 +140,15 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
senderName: event.senderName,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(MessageError('Error sending message: $e'));
|
||||
emit(const MessageError('Impossible d\'envoyer le message'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [DeleteMessage] events.
|
||||
///
|
||||
///
|
||||
/// Deletes a message from the group chat. The Firestore stream will
|
||||
/// automatically update the UI, so no state is emitted here unless there's an error.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The DeleteMessage event containing group ID and message ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -166,15 +164,15 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
messageId: event.messageId,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(MessageError('Error deleting message: $e'));
|
||||
emit(const MessageError('Impossible de supprimer le message'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [UpdateMessage] events.
|
||||
///
|
||||
///
|
||||
/// Updates/edits an existing message in the group chat. The Firestore stream will
|
||||
/// automatically update the UI with the edited message, so no state is emitted here.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The UpdateMessage event containing message ID and new text
|
||||
/// [emit]: State emitter function
|
||||
@@ -191,15 +189,15 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
newText: event.newText,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(MessageError('Error updating message: $e'));
|
||||
emit(const MessageError('Impossible de modifier le message'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [ReactToMessage] events.
|
||||
///
|
||||
///
|
||||
/// Adds an emoji reaction to a message. The Firestore stream will
|
||||
/// automatically update the UI with the new reaction, so no state is emitted here.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The ReactToMessage event containing message ID, user ID, and reaction
|
||||
/// [emit]: State emitter function
|
||||
@@ -217,15 +215,15 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
reaction: event.reaction,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(MessageError('Error adding reaction: $e'));
|
||||
emit(const MessageError('Impossible d\'ajouter la réaction'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [RemoveReaction] events.
|
||||
///
|
||||
///
|
||||
/// Removes a user's reaction from a message. The Firestore stream will
|
||||
/// automatically update the UI with the removed reaction, so no state is emitted here.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The RemoveReaction event containing message ID and user ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -242,12 +240,12 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
userId: event.userId,
|
||||
);
|
||||
} catch (e) {
|
||||
emit(MessageError('Error removing reaction: $e'));
|
||||
emit(const MessageError('Impossible de supprimer la réaction'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleans up resources when the bloc is closed.
|
||||
///
|
||||
///
|
||||
/// Cancels the message stream subscription to prevent memory leaks
|
||||
/// and ensure proper disposal of resources.
|
||||
@override
|
||||
@@ -258,32 +256,29 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
|
||||
}
|
||||
|
||||
/// Private event for handling real-time message updates from streams.
|
||||
///
|
||||
///
|
||||
/// This internal event is used to process updates from the message stream
|
||||
/// subscription and emit appropriate states based on the received data.
|
||||
class _MessagesUpdated extends MessageEvent {
|
||||
/// List of messages received from the stream
|
||||
final List<Message> messages;
|
||||
|
||||
|
||||
/// Group ID associated with the messages
|
||||
final String groupId;
|
||||
|
||||
/// Creates a _MessagesUpdated event.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [messages]: List of messages from the stream update
|
||||
/// [groupId]: ID of the group these messages belong to
|
||||
const _MessagesUpdated({
|
||||
required this.messages,
|
||||
required this.groupId,
|
||||
});
|
||||
const _MessagesUpdated({required this.messages, required this.groupId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [messages, groupId];
|
||||
}
|
||||
|
||||
/// Private event for handling message stream errors.
|
||||
///
|
||||
///
|
||||
/// This internal event is used to process errors from the message stream
|
||||
/// subscription and emit appropriate error states.
|
||||
class _MessagesError extends MessageEvent {
|
||||
@@ -291,7 +286,7 @@ class _MessagesError extends MessageEvent {
|
||||
final String error;
|
||||
|
||||
/// Creates a _MessagesError event.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [error]: Error message from the stream failure
|
||||
const _MessagesError(this.error);
|
||||
|
||||
@@ -22,43 +22,46 @@
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// final tripBloc = TripBloc(tripRepository);
|
||||
///
|
||||
///
|
||||
/// // Load trips for a user
|
||||
/// tripBloc.add(LoadTripsByUserId(userId: 'userId123'));
|
||||
///
|
||||
///
|
||||
/// // Create a new trip
|
||||
/// tripBloc.add(TripCreateRequested(trip: newTrip));
|
||||
///
|
||||
///
|
||||
/// // Update a trip
|
||||
/// tripBloc.add(TripUpdateRequested(trip: updatedTrip));
|
||||
///
|
||||
///
|
||||
/// // Delete a trip
|
||||
/// tripBloc.add(TripDeleteRequested(tripId: 'tripId456'));
|
||||
/// ```
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:travel_mate/models/trip.dart';
|
||||
import 'trip_event.dart';
|
||||
import 'trip_state.dart';
|
||||
import '../../repositories/trip_repository.dart';
|
||||
import '../../services/error_service.dart';
|
||||
|
||||
/// BLoC that manages trip-related operations and state.
|
||||
class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
/// Repository for trip data operations
|
||||
final TripRepository _repository;
|
||||
|
||||
final _errorService = ErrorService();
|
||||
|
||||
/// Subscription to trip stream for real-time updates
|
||||
StreamSubscription? _tripsSubscription;
|
||||
|
||||
|
||||
/// Current user ID for automatic list refreshing after operations
|
||||
String? _currentUserId;
|
||||
|
||||
/// Constructor for TripBloc.
|
||||
///
|
||||
///
|
||||
/// Initializes the bloc with the trip repository and sets up event handlers
|
||||
/// for all trip-related operations.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [_repository]: Repository for trip data operations
|
||||
TripBloc(this._repository) : super(TripInitial()) {
|
||||
@@ -71,11 +74,11 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
}
|
||||
|
||||
/// Handles [LoadTripsByUserId] events.
|
||||
///
|
||||
///
|
||||
/// Loads all trips for a specific user with real-time updates via stream subscription.
|
||||
/// Stores the user ID for future automatic refreshing after operations and cancels
|
||||
/// any existing subscription to prevent memory leaks.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The LoadTripsByUserId event containing the user ID
|
||||
/// [emit]: State emitter function
|
||||
@@ -84,41 +87,45 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
Emitter<TripState> emit,
|
||||
) async {
|
||||
emit(TripLoading());
|
||||
|
||||
|
||||
_currentUserId = event.userId;
|
||||
await _tripsSubscription?.cancel();
|
||||
|
||||
_tripsSubscription = _repository.getTripsByUserId(event.userId).listen(
|
||||
(trips) {
|
||||
add(_TripsUpdated(trips));
|
||||
},
|
||||
onError: (error) {
|
||||
emit(TripError(error.toString()));
|
||||
},
|
||||
);
|
||||
_tripsSubscription = _repository
|
||||
.getTripsByUserId(event.userId)
|
||||
.listen(
|
||||
(trips) {
|
||||
add(_TripsUpdated(trips));
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
_errorService.logError(
|
||||
'TripBloc',
|
||||
'Error loading trips: $error',
|
||||
stackTrace,
|
||||
);
|
||||
emit(const TripError('Impossible de charger les voyages'));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Handles [_TripsUpdated] events.
|
||||
///
|
||||
///
|
||||
/// Processes real-time updates from the trip stream and emits the
|
||||
/// updated trip list to the UI.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The _TripsUpdated event containing the updated trip list
|
||||
/// [emit]: State emitter function
|
||||
void _onTripsUpdated(
|
||||
_TripsUpdated event,
|
||||
Emitter<TripState> emit,
|
||||
) {
|
||||
void _onTripsUpdated(_TripsUpdated event, Emitter<TripState> emit) {
|
||||
emit(TripLoaded(event.trips));
|
||||
}
|
||||
|
||||
/// Handles [TripCreateRequested] events.
|
||||
///
|
||||
///
|
||||
/// Creates a new trip and automatically refreshes the user's trip list
|
||||
/// to show the newly created trip. Includes a delay to allow the creation
|
||||
/// to complete before refreshing.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The TripCreateRequested event containing the trip data
|
||||
/// [emit]: State emitter function
|
||||
@@ -128,27 +135,27 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
) async {
|
||||
try {
|
||||
emit(TripLoading());
|
||||
|
||||
|
||||
final tripId = await _repository.createTrip(event.trip);
|
||||
|
||||
|
||||
emit(TripCreated(tripId: tripId));
|
||||
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
if (_currentUserId != null) {
|
||||
add(LoadTripsByUserId(userId: _currentUserId!));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
emit(TripError('Error during creation: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('TripBloc', 'Error creating trip: $e', stackTrace);
|
||||
emit(const TripError('Impossible de créer le voyage'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [TripUpdateRequested] events.
|
||||
///
|
||||
///
|
||||
/// Updates an existing trip and automatically refreshes the user's trip list
|
||||
/// to show the updated information. Includes a delay to allow the update
|
||||
/// to complete before refreshing.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The TripUpdateRequested event containing the updated trip data
|
||||
/// [emit]: State emitter function
|
||||
@@ -163,18 +170,18 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
if (_currentUserId != null) {
|
||||
add(LoadTripsByUserId(userId: _currentUserId!));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
emit(TripError('Error during update: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('TripBloc', 'Error updating trip: $e', stackTrace);
|
||||
emit(const TripError('Impossible de mettre à jour le voyage'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [TripDeleteRequested] events.
|
||||
///
|
||||
///
|
||||
/// Deletes a trip and automatically refreshes the user's trip list
|
||||
/// to remove the deleted trip from the UI. Includes a delay to allow
|
||||
/// the deletion to complete before refreshing.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The TripDeleteRequested event containing the trip ID to delete
|
||||
/// [emit]: State emitter function
|
||||
@@ -184,39 +191,36 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
) async {
|
||||
try {
|
||||
await _repository.deleteTrip(event.tripId);
|
||||
|
||||
|
||||
emit(const TripOperationSuccess('Trip deleted successfully'));
|
||||
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
if (_currentUserId != null) {
|
||||
add(LoadTripsByUserId(userId: _currentUserId!));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
emit(TripError('Error during deletion: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('TripBloc', 'Error deleting trip: $e', stackTrace);
|
||||
emit(const TripError('Impossible de supprimer le voyage'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles [ResetTrips] events.
|
||||
///
|
||||
///
|
||||
/// Resets the trip state to initial and cleans up resources.
|
||||
/// Cancels the trip stream subscription and clears the current user ID.
|
||||
/// This is useful for user logout or when switching contexts.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [event]: The ResetTrips event
|
||||
/// [emit]: State emitter function
|
||||
Future<void> _onResetTrips(
|
||||
ResetTrips event,
|
||||
Emitter<TripState> emit,
|
||||
) async {
|
||||
Future<void> _onResetTrips(ResetTrips event, Emitter<TripState> emit) async {
|
||||
await _tripsSubscription?.cancel();
|
||||
_currentUserId = null;
|
||||
emit(TripInitial());
|
||||
}
|
||||
|
||||
/// Cleans up resources when the bloc is closed.
|
||||
///
|
||||
///
|
||||
/// Cancels the trip stream subscription to prevent memory leaks
|
||||
/// and ensure proper disposal of resources.
|
||||
@override
|
||||
@@ -227,7 +231,7 @@ class TripBloc extends Bloc<TripEvent, TripState> {
|
||||
}
|
||||
|
||||
/// Private event for handling real-time trip updates from streams.
|
||||
///
|
||||
///
|
||||
/// This internal event is used to process updates from the trip stream
|
||||
/// subscription and emit appropriate states based on the received data.
|
||||
class _TripsUpdated extends TripEvent {
|
||||
@@ -235,11 +239,11 @@ class _TripsUpdated extends TripEvent {
|
||||
final List<Trip> trips;
|
||||
|
||||
/// Creates a _TripsUpdated event.
|
||||
///
|
||||
///
|
||||
/// Args:
|
||||
/// [trips]: List of trips from the stream update
|
||||
const _TripsUpdated(this.trips);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [trips];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:travel_mate/services/notification_service.dart';
|
||||
import 'package:travel_mate/services/logger_service.dart';
|
||||
import 'package:travel_mate/services/error_service.dart';
|
||||
import 'user_event.dart' as event;
|
||||
import 'user_state.dart' as state;
|
||||
|
||||
@@ -17,6 +19,8 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
/// Firestore instance for user data operations.
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
|
||||
final _errorService = ErrorService();
|
||||
|
||||
/// Creates a new [UserBloc] with initial state.
|
||||
///
|
||||
/// Registers event handlers for all user-related events.
|
||||
@@ -50,7 +54,7 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
final notificationService = NotificationService();
|
||||
await notificationService.initialize();
|
||||
final fcmToken = await notificationService.getFCMToken();
|
||||
print('DEBUG: UserBloc - FCM Token retrieved: $fcmToken');
|
||||
LoggerService.info('UserBloc - FCM Token retrieved: $fcmToken');
|
||||
|
||||
// Fetch user data from Firestore
|
||||
final userDoc = await _firestore
|
||||
@@ -81,21 +85,22 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
|
||||
// Update FCM token if it changed
|
||||
if (fcmToken != null && user.fcmToken != fcmToken) {
|
||||
print('DEBUG: UserBloc - Updating FCM token in Firestore');
|
||||
LoggerService.info('UserBloc - Updating FCM token in Firestore');
|
||||
await _firestore.collection('users').doc(currentUser.uid).update({
|
||||
'fcmToken': fcmToken,
|
||||
});
|
||||
print('DEBUG: UserBloc - FCM token updated');
|
||||
LoggerService.info('UserBloc - FCM token updated');
|
||||
} else {
|
||||
print(
|
||||
'DEBUG: UserBloc - FCM token not updated. Local: $fcmToken, Firestore: ${user.fcmToken}',
|
||||
LoggerService.info(
|
||||
'UserBloc - FCM token not updated. Local: $fcmToken, Firestore: ${user.fcmToken}',
|
||||
);
|
||||
}
|
||||
|
||||
emit(state.UserLoaded(user));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.UserError('Error loading user: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('UserBloc', 'Error loading user: $e', stackTrace);
|
||||
emit(state.UserError('Impossible de charger l\'utilisateur'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +129,9 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
} else {
|
||||
emit(state.UserError('User not found'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(state.UserError('Error loading user: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError('UserBloc', 'Error loading user: $e', stackTrace);
|
||||
emit(state.UserError('Impossible de charger l\'utilisateur'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,8 +164,13 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||
});
|
||||
|
||||
emit(state.UserLoaded(updatedUser));
|
||||
} catch (e) {
|
||||
emit(state.UserError('Error updating user: $e'));
|
||||
} catch (e, stackTrace) {
|
||||
_errorService.logError(
|
||||
'UserBloc',
|
||||
'Error updating user: $e',
|
||||
stackTrace,
|
||||
);
|
||||
emit(state.UserError('Impossible de mettre à jour l\'utilisateur'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user