feat: integrate ErrorService for consistent error display and standardize bloc error messages.

This commit is contained in:
Van Leemput Dayron
2025-12-02 13:59:40 +01:00
parent 1e70b9e09f
commit 6757cb013a
24 changed files with 927 additions and 608 deletions

View File

@@ -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];
}
}