feat: Implement activity management feature with Firestore integration
- Added AddActivityBottomSheet for adding custom activities to trips. - Created Activity model to represent tourist activities. - Developed ActivityRepository for managing activities in Firestore. - Integrated ActivityPlacesService for searching activities via Google Places API. - Updated ShowTripDetailsContent to navigate to activities page. - Enhanced main.dart to include ActivityBloc and necessary repositories.
This commit is contained in:
240
lib/blocs/activity/activity_state.dart
Normal file
240
lib/blocs/activity/activity_state.dart
Normal file
@@ -0,0 +1,240 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../models/activity.dart';
|
||||
|
||||
/// Base class for all activity-related states
|
||||
abstract class ActivityState extends Equatable {
|
||||
const ActivityState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
/// Initial state when no activities have been loaded
|
||||
class ActivityInitial extends ActivityState {
|
||||
const ActivityInitial();
|
||||
}
|
||||
|
||||
/// State when activities are being loaded
|
||||
class ActivityLoading extends ActivityState {
|
||||
const ActivityLoading();
|
||||
}
|
||||
|
||||
/// State when activities are being searched
|
||||
class ActivitySearching extends ActivityState {
|
||||
const ActivitySearching();
|
||||
}
|
||||
|
||||
/// State when activities have been loaded successfully
|
||||
class ActivityLoaded extends ActivityState {
|
||||
final List<Activity> activities;
|
||||
final List<Activity> filteredActivities;
|
||||
final String? activeFilter;
|
||||
final double? minRating;
|
||||
final bool showVotedOnly;
|
||||
|
||||
const ActivityLoaded({
|
||||
required this.activities,
|
||||
required this.filteredActivities,
|
||||
this.activeFilter,
|
||||
this.minRating,
|
||||
this.showVotedOnly = false,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
activities,
|
||||
filteredActivities,
|
||||
activeFilter,
|
||||
minRating,
|
||||
showVotedOnly,
|
||||
];
|
||||
|
||||
/// Creates a copy of the current state with optional modifications
|
||||
ActivityLoaded copyWith({
|
||||
List<Activity>? activities,
|
||||
List<Activity>? filteredActivities,
|
||||
String? activeFilter,
|
||||
double? minRating,
|
||||
bool? showVotedOnly,
|
||||
}) {
|
||||
return ActivityLoaded(
|
||||
activities: activities ?? this.activities,
|
||||
filteredActivities: filteredActivities ?? this.filteredActivities,
|
||||
activeFilter: activeFilter ?? this.activeFilter,
|
||||
minRating: minRating ?? this.minRating,
|
||||
showVotedOnly: showVotedOnly ?? this.showVotedOnly,
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets activities by category
|
||||
List<Activity> getActivitiesByCategory(String category) {
|
||||
return activities.where((activity) => activity.category == category).toList();
|
||||
}
|
||||
|
||||
/// Gets top rated activities
|
||||
List<Activity> getTopRatedActivities({int limit = 10}) {
|
||||
final sorted = List<Activity>.from(activities);
|
||||
sorted.sort((a, b) {
|
||||
final aScore = a.totalVotes;
|
||||
final bScore = b.totalVotes;
|
||||
|
||||
if (aScore != bScore) {
|
||||
return bScore.compareTo(aScore);
|
||||
}
|
||||
|
||||
return (b.rating ?? 0).compareTo(a.rating ?? 0);
|
||||
});
|
||||
|
||||
return sorted.take(limit).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// State when search results are available
|
||||
class ActivitySearchResults extends ActivityState {
|
||||
final List<Activity> searchResults;
|
||||
final String query;
|
||||
final bool isLoading;
|
||||
|
||||
const ActivitySearchResults({
|
||||
required this.searchResults,
|
||||
required this.query,
|
||||
this.isLoading = false,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [searchResults, query, isLoading];
|
||||
|
||||
/// Creates a copy with optional modifications
|
||||
ActivitySearchResults copyWith({
|
||||
List<Activity>? searchResults,
|
||||
String? query,
|
||||
bool? isLoading,
|
||||
}) {
|
||||
return ActivitySearchResults(
|
||||
searchResults: searchResults ?? this.searchResults,
|
||||
query: query ?? this.query,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// State when an operation has completed successfully
|
||||
class ActivityOperationSuccess extends ActivityState {
|
||||
final String message;
|
||||
final String? operationType;
|
||||
|
||||
const ActivityOperationSuccess(
|
||||
this.message, {
|
||||
this.operationType,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message, operationType];
|
||||
}
|
||||
|
||||
/// State when an error has occurred
|
||||
class ActivityError extends ActivityState {
|
||||
final String message;
|
||||
final String? errorCode;
|
||||
final dynamic error;
|
||||
|
||||
const ActivityError(
|
||||
this.message, {
|
||||
this.errorCode,
|
||||
this.error,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message, errorCode, error];
|
||||
}
|
||||
|
||||
/// State when voting is in progress
|
||||
class ActivityVoting extends ActivityState {
|
||||
final String activityId;
|
||||
final List<Activity> activities;
|
||||
|
||||
const ActivityVoting({
|
||||
required this.activityId,
|
||||
required this.activities,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activityId, activities];
|
||||
}
|
||||
|
||||
/// State when activity is being updated
|
||||
class ActivityUpdating extends ActivityState {
|
||||
final String activityId;
|
||||
final List<Activity> activities;
|
||||
|
||||
const ActivityUpdating({
|
||||
required this.activityId,
|
||||
required this.activities,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activityId, activities];
|
||||
}
|
||||
|
||||
/// State when activities are being added in batch
|
||||
class ActivityBatchAdding extends ActivityState {
|
||||
final List<Activity> activitiesToAdd;
|
||||
final int progress;
|
||||
final int total;
|
||||
|
||||
const ActivityBatchAdding({
|
||||
required this.activitiesToAdd,
|
||||
required this.progress,
|
||||
required this.total,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activitiesToAdd, progress, total];
|
||||
|
||||
/// Gets the progress as a percentage
|
||||
double get progressPercentage => total > 0 ? progress / total : 0.0;
|
||||
}
|
||||
|
||||
/// State when an activity has been successfully added
|
||||
class ActivityAdded extends ActivityState {
|
||||
final Activity activity;
|
||||
final String message;
|
||||
|
||||
const ActivityAdded({
|
||||
required this.activity,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activity, message];
|
||||
}
|
||||
|
||||
/// State when an activity has been successfully deleted
|
||||
class ActivityDeleted extends ActivityState {
|
||||
final String activityId;
|
||||
final String message;
|
||||
|
||||
const ActivityDeleted({
|
||||
required this.activityId,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activityId, message];
|
||||
}
|
||||
|
||||
/// State when vote has been successfully recorded
|
||||
class ActivityVoteRecorded extends ActivityState {
|
||||
final String activityId;
|
||||
final int vote;
|
||||
final String userId;
|
||||
|
||||
const ActivityVoteRecorded({
|
||||
required this.activityId,
|
||||
required this.vote,
|
||||
required this.userId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [activityId, vote, userId];
|
||||
}
|
||||
Reference in New Issue
Block a user