705 lines
22 KiB
Dart
705 lines
22 KiB
Dart
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import '../../models/activity.dart';
|
|
import '../../repositories/activity_repository.dart';
|
|
import '../../services/activity_places_service.dart';
|
|
import '../../services/activity_cache_service.dart';
|
|
import '../../services/error_service.dart';
|
|
import 'activity_event.dart';
|
|
import 'activity_state.dart';
|
|
|
|
/// BLoC for managing activity-related state and operations
|
|
class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
|
|
final ActivityRepository _repository;
|
|
final ActivityPlacesService _placesService;
|
|
final ErrorService _errorService;
|
|
|
|
ActivityBloc({
|
|
required ActivityRepository repository,
|
|
required ActivityPlacesService placesService,
|
|
required ErrorService errorService,
|
|
}) : _repository = repository,
|
|
_placesService = placesService,
|
|
_errorService = errorService,
|
|
super(const ActivityInitial()) {
|
|
on<LoadActivities>(_onLoadActivities);
|
|
on<LoadTripActivitiesPreservingSearch>(
|
|
_onLoadTripActivitiesPreservingSearch,
|
|
);
|
|
on<SearchActivities>(_onSearchActivities);
|
|
on<SearchActivitiesWithCoordinates>(_onSearchActivitiesWithCoordinates);
|
|
on<SearchActivitiesByText>(_onSearchActivitiesByText);
|
|
on<AddActivity>(_onAddActivity);
|
|
on<AddActivitiesBatch>(_onAddActivitiesBatch);
|
|
on<VoteForActivity>(_onVoteForActivity);
|
|
on<DeleteActivity>(_onDeleteActivity);
|
|
on<FilterActivities>(_onFilterActivities);
|
|
on<RefreshActivities>(_onRefreshActivities);
|
|
on<ClearSearchResults>(_onClearSearchResults);
|
|
on<UpdateActivity>(_onUpdateActivity);
|
|
on<ToggleActivityFavorite>(_onToggleActivityFavorite);
|
|
on<RestoreCachedSearchResults>(_onRestoreCachedSearchResults);
|
|
on<RemoveFromSearchResults>(_onRemoveFromSearchResults);
|
|
on<AddActivityAndRemoveFromSearch>(_onAddActivityAndRemoveFromSearch);
|
|
on<UpdateActivityDate>(_onUpdateActivityDate);
|
|
}
|
|
|
|
/// Handles loading activities for a trip
|
|
Future<void> _onLoadActivities(
|
|
LoadActivities event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
emit(const ActivityLoading());
|
|
|
|
final activities = await _repository.getActivitiesByTrip(event.tripId);
|
|
|
|
emit(
|
|
ActivityLoaded(activities: activities, filteredActivities: activities),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError(
|
|
'activity_bloc',
|
|
'Erreur chargement activités: $e',
|
|
);
|
|
emit(const ActivityError('Impossible de charger les activités'));
|
|
}
|
|
}
|
|
|
|
/// Handles loading trip activities while preserving search results state
|
|
Future<void> _onLoadTripActivitiesPreservingSearch(
|
|
LoadTripActivitiesPreservingSearch event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
final activities = await _repository.getActivitiesByTrip(event.tripId);
|
|
|
|
// Si on a un état de recherche actif, on le préserve
|
|
if (state is ActivitySearchResults) {
|
|
// On garde l'état de recherche inchangé, pas besoin d'émettre
|
|
return;
|
|
}
|
|
|
|
// Sinon, on charge normalement
|
|
emit(
|
|
ActivityLoaded(activities: activities, filteredActivities: activities),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError(
|
|
'activity_bloc',
|
|
'Erreur chargement activités: $e',
|
|
);
|
|
emit(const ActivityError('Impossible de charger les activités'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onUpdateActivityDate(
|
|
UpdateActivityDate event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
final activity = await _repository.getActivity(
|
|
event.tripId,
|
|
event.activityId,
|
|
);
|
|
|
|
if (activity != null) {
|
|
final updatedActivity = activity.copyWith(
|
|
date: event.date,
|
|
clearDate: event.date == null,
|
|
);
|
|
await _repository.updateActivity(updatedActivity);
|
|
|
|
// Recharger les activités pour mettre à jour l'UI
|
|
add(LoadActivities(event.tripId));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur mise à jour date: $e');
|
|
emit(const ActivityError('Impossible de mettre à jour la date'));
|
|
}
|
|
}
|
|
|
|
/// Handles searching activities using Google Places API
|
|
Future<void> _onSearchActivities(
|
|
SearchActivities event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Si c'est un append (charger plus), on garde l'état actuel et on met isLoading à true
|
|
if (event.appendToExisting && state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
emit(currentState.copyWith(isLoading: true));
|
|
} else {
|
|
emit(const ActivitySearching());
|
|
}
|
|
|
|
final searchResults = await _placesService.searchActivities(
|
|
destination: event.destination,
|
|
tripId: event.tripId,
|
|
category: event.category,
|
|
maxResults:
|
|
event.maxResults ??
|
|
20, // Par défaut 20, ou utiliser la valeur spécifiée
|
|
offset: event.offset ?? 0, // Par défaut 0
|
|
);
|
|
|
|
List<Activity> finalResults;
|
|
|
|
// Si on doit ajouter aux résultats existants
|
|
if (event.appendToExisting && state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
finalResults = [...currentState.searchResults, ...searchResults];
|
|
} else {
|
|
finalResults = searchResults;
|
|
}
|
|
|
|
// Mettre en cache les résultats
|
|
ActivityCacheService().setCachedActivities(event.tripId, finalResults);
|
|
|
|
emit(
|
|
ActivitySearchResults(
|
|
searchResults: finalResults,
|
|
query: event.category?.displayName ?? 'Toutes les activités',
|
|
isLoading: false,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur recherche activités: $e');
|
|
emit(const ActivityError('Impossible de rechercher les activités'));
|
|
}
|
|
}
|
|
|
|
/// Handles searching activities using coordinates directly (bypasses geocoding)
|
|
Future<void> _onSearchActivitiesWithCoordinates(
|
|
SearchActivitiesWithCoordinates event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Si c'est un append (charger plus), on garde l'état actuel et on met isLoading à true
|
|
if (event.appendToExisting && state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
emit(currentState.copyWith(isLoading: true));
|
|
} else {
|
|
emit(const ActivitySearching());
|
|
}
|
|
|
|
final searchResults = await _placesService.searchActivitiesPaginated(
|
|
latitude: event.latitude,
|
|
longitude: event.longitude,
|
|
tripId: event.tripId,
|
|
category: event.category,
|
|
pageSize: event.maxResults ?? 20,
|
|
);
|
|
|
|
final activities = searchResults['activities'] as List<Activity>;
|
|
List<Activity> finalResults;
|
|
|
|
// Si on doit ajouter aux résultats existants
|
|
if (event.appendToExisting && state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
finalResults = [...currentState.searchResults, ...activities];
|
|
} else {
|
|
finalResults = activities;
|
|
}
|
|
|
|
// Mettre en cache les résultats
|
|
ActivityCacheService().setCachedActivities(event.tripId, finalResults);
|
|
|
|
emit(
|
|
ActivitySearchResults(
|
|
searchResults: finalResults,
|
|
query: event.category?.displayName ?? 'Toutes les activités',
|
|
isLoading: false,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError(
|
|
'activity_bloc',
|
|
'Erreur recherche activités avec coordonnées: $e',
|
|
);
|
|
emit(const ActivityError('Impossible de rechercher les activités'));
|
|
}
|
|
}
|
|
|
|
/// Handles text-based activity search
|
|
Future<void> _onSearchActivitiesByText(
|
|
SearchActivitiesByText event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
emit(const ActivitySearching());
|
|
|
|
final searchResults = await _placesService.searchActivitiesByText(
|
|
query: event.query,
|
|
destination: event.destination,
|
|
tripId: event.tripId,
|
|
);
|
|
|
|
// Mettre en cache les résultats
|
|
ActivityCacheService().setCachedActivities(event.tripId, searchResults);
|
|
|
|
emit(
|
|
ActivitySearchResults(searchResults: searchResults, query: event.query),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur recherche textuelle: $e');
|
|
emit(const ActivityError('Impossible de rechercher les activités'));
|
|
}
|
|
}
|
|
|
|
/// Handles adding a single activity
|
|
Future<void> _onAddActivity(
|
|
AddActivity event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Check if activity already exists
|
|
if (event.activity.placeId != null) {
|
|
final existing = await _repository.findExistingActivity(
|
|
event.activity.tripId,
|
|
event.activity.placeId!,
|
|
);
|
|
|
|
if (existing != null) {
|
|
emit(const ActivityError('Cette activité a déjà été ajoutée'));
|
|
return;
|
|
}
|
|
}
|
|
|
|
final activityId = await _repository.addActivity(event.activity);
|
|
|
|
if (activityId != null) {
|
|
// Si on est en état de recherche (suggestions Google), préserver cet état
|
|
if (state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
// On garde l'état de recherche inchangé mais on ajoute l'info de l'activité ajoutée
|
|
emit(
|
|
currentState.copyWith(
|
|
newlyAddedActivity: event.activity.copyWith(id: activityId),
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Sinon, émettre l'état d'ajout réussi
|
|
emit(
|
|
ActivityAdded(
|
|
activity: event.activity.copyWith(id: activityId),
|
|
message: 'Activité ajoutée avec succès',
|
|
),
|
|
);
|
|
// Reload activities while preserving search results
|
|
add(LoadTripActivitiesPreservingSearch(event.activity.tripId));
|
|
} else {
|
|
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur ajout activité: $e');
|
|
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
|
}
|
|
}
|
|
|
|
/// Handles adding an activity and removing it from search results in one action
|
|
Future<void> _onAddActivityAndRemoveFromSearch(
|
|
AddActivityAndRemoveFromSearch event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Check if activity already exists
|
|
if (event.activity.placeId != null) {
|
|
final existing = await _repository.findExistingActivity(
|
|
event.activity.tripId,
|
|
event.activity.placeId!,
|
|
);
|
|
|
|
if (existing != null) {
|
|
emit(const ActivityError('Cette activité a déjà été ajoutée'));
|
|
return;
|
|
}
|
|
}
|
|
|
|
final activityId = await _repository.addActivity(event.activity);
|
|
|
|
if (activityId != null) {
|
|
// Si on est en état de recherche (suggestions Google), préserver cet état
|
|
// en supprimant l'activité des résultats
|
|
if (state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
final updatedResults = currentState.searchResults
|
|
.where((activity) => activity.id != event.googleActivityId)
|
|
.toList();
|
|
|
|
emit(
|
|
ActivitySearchResults(
|
|
searchResults: updatedResults,
|
|
query: currentState.query,
|
|
isLoading: false,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Sinon, émettre l'état d'ajout réussi
|
|
emit(
|
|
ActivityAdded(
|
|
activity: event.activity.copyWith(id: activityId),
|
|
message: 'Activité ajoutée avec succès',
|
|
),
|
|
);
|
|
// Reload activities while preserving search results
|
|
add(LoadTripActivitiesPreservingSearch(event.activity.tripId));
|
|
} else {
|
|
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur ajout activité: $e');
|
|
emit(const ActivityError('Impossible d\'ajouter l\'activité'));
|
|
}
|
|
}
|
|
|
|
/// Handles adding multiple activities in batch
|
|
Future<void> _onAddActivitiesBatch(
|
|
AddActivitiesBatch event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Filter out existing activities
|
|
final filteredActivities = <Activity>[];
|
|
|
|
emit(
|
|
ActivityBatchAdding(
|
|
activitiesToAdd: event.activities,
|
|
progress: 0,
|
|
total: event.activities.length,
|
|
),
|
|
);
|
|
|
|
for (int i = 0; i < event.activities.length; i++) {
|
|
final activity = event.activities[i];
|
|
|
|
if (activity.placeId != null) {
|
|
final existing = await _repository.findExistingActivity(
|
|
activity.tripId,
|
|
activity.placeId!,
|
|
);
|
|
|
|
if (existing == null) {
|
|
filteredActivities.add(activity);
|
|
}
|
|
} else {
|
|
filteredActivities.add(activity);
|
|
}
|
|
|
|
// Update progress
|
|
emit(
|
|
ActivityBatchAdding(
|
|
activitiesToAdd: event.activities,
|
|
progress: i + 1,
|
|
total: event.activities.length,
|
|
),
|
|
);
|
|
}
|
|
|
|
if (filteredActivities.isEmpty) {
|
|
emit(const ActivityError('Toutes les activités ont déjà été ajoutées'));
|
|
return;
|
|
}
|
|
|
|
final addedIds = await _repository.addActivitiesBatch(filteredActivities);
|
|
|
|
if (addedIds.isNotEmpty) {
|
|
emit(
|
|
ActivityOperationSuccess(
|
|
'${addedIds.length} activité(s) ajoutée(s) avec succès',
|
|
operationType: 'batch_add',
|
|
),
|
|
);
|
|
// Reload activities
|
|
add(LoadActivities(event.activities.first.tripId));
|
|
} else {
|
|
emit(const ActivityError('Impossible d\'ajouter les activités'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur ajout en lot: $e');
|
|
emit(const ActivityError('Impossible d\'ajouter les activités'));
|
|
}
|
|
}
|
|
|
|
/// Handles voting for an activity
|
|
Future<void> _onVoteForActivity(
|
|
VoteForActivity event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// Show voting state
|
|
if (state is ActivityLoaded) {
|
|
final currentState = state as ActivityLoaded;
|
|
emit(
|
|
ActivityVoting(
|
|
activityId: event.activityId,
|
|
activities: currentState.activities,
|
|
),
|
|
);
|
|
}
|
|
|
|
final success = await _repository.voteForActivity(
|
|
event.activityId,
|
|
event.userId,
|
|
event.vote,
|
|
);
|
|
|
|
if (success) {
|
|
emit(
|
|
ActivityVoteRecorded(
|
|
activityId: event.activityId,
|
|
vote: event.vote,
|
|
userId: event.userId,
|
|
),
|
|
);
|
|
|
|
// Reload activities to reflect the new vote
|
|
if (state is ActivityLoaded) {
|
|
final currentState = state as ActivityLoaded;
|
|
final activities = await _repository.getActivitiesByTrip(
|
|
currentState.activities.first.tripId,
|
|
);
|
|
|
|
emit(
|
|
currentState.copyWith(
|
|
activities: activities,
|
|
filteredActivities: _applyFilters(
|
|
activities,
|
|
currentState.activeFilter,
|
|
currentState.minRating,
|
|
currentState.showVotedOnly,
|
|
event.userId,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
emit(const ActivityError('Impossible d\'enregistrer le vote'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur vote: $e');
|
|
emit(const ActivityError('Impossible d\'enregistrer le vote'));
|
|
}
|
|
}
|
|
|
|
/// Handles deleting an activity
|
|
Future<void> _onDeleteActivity(
|
|
DeleteActivity event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
final success = await _repository.deleteActivity(event.activityId);
|
|
|
|
if (success) {
|
|
emit(
|
|
ActivityDeleted(
|
|
activityId: event.activityId,
|
|
message: 'Activité supprimée avec succès',
|
|
),
|
|
);
|
|
|
|
// Reload if we're on the activity list
|
|
if (state is ActivityLoaded) {
|
|
final currentState = state as ActivityLoaded;
|
|
if (currentState.activities.isNotEmpty) {
|
|
add(LoadActivities(currentState.activities.first.tripId));
|
|
}
|
|
}
|
|
} else {
|
|
emit(const ActivityError('Impossible de supprimer l\'activité'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur suppression: $e');
|
|
emit(const ActivityError('Impossible de supprimer l\'activité'));
|
|
}
|
|
}
|
|
|
|
/// Handles filtering activities
|
|
Future<void> _onFilterActivities(
|
|
FilterActivities event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
if (state is ActivityLoaded) {
|
|
final currentState = state as ActivityLoaded;
|
|
|
|
final filteredActivities = _applyFilters(
|
|
currentState.activities,
|
|
event.category,
|
|
event.minRating,
|
|
event.showVotedOnly ?? false,
|
|
'', // UserId would be needed for showVotedOnly filter
|
|
);
|
|
|
|
emit(
|
|
currentState.copyWith(
|
|
filteredActivities: filteredActivities,
|
|
activeFilter: event.category,
|
|
minRating: event.minRating,
|
|
showVotedOnly: event.showVotedOnly ?? false,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Handles refreshing activities
|
|
Future<void> _onRefreshActivities(
|
|
RefreshActivities event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
add(LoadActivities(event.tripId));
|
|
}
|
|
|
|
/// Handles clearing search results
|
|
Future<void> _onClearSearchResults(
|
|
ClearSearchResults event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
if (state is ActivitySearchResults) {
|
|
emit(const ActivityInitial());
|
|
}
|
|
}
|
|
|
|
/// Handles updating an activity
|
|
Future<void> _onUpdateActivity(
|
|
UpdateActivity event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
if (state is ActivityLoaded) {
|
|
final currentState = state as ActivityLoaded;
|
|
emit(
|
|
ActivityUpdating(
|
|
activityId: event.activity.id,
|
|
activities: currentState.activities,
|
|
),
|
|
);
|
|
}
|
|
|
|
final success = await _repository.updateActivity(event.activity);
|
|
|
|
if (success) {
|
|
emit(
|
|
const ActivityOperationSuccess(
|
|
'Activité mise à jour avec succès',
|
|
operationType: 'update',
|
|
),
|
|
);
|
|
|
|
// Reload activities
|
|
add(LoadActivities(event.activity.tripId));
|
|
} else {
|
|
emit(const ActivityError('Impossible de mettre à jour l\'activité'));
|
|
}
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur mise à jour: $e');
|
|
emit(const ActivityError('Impossible de mettre à jour l\'activité'));
|
|
}
|
|
}
|
|
|
|
/// Handles toggling activity favorite status
|
|
Future<void> _onToggleActivityFavorite(
|
|
ToggleActivityFavorite event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
try {
|
|
// This would require extending the Activity model to include favorites
|
|
// For now, we'll use the voting system as a favorite system
|
|
add(
|
|
VoteForActivity(
|
|
activityId: event.activityId,
|
|
userId: event.userId,
|
|
vote: 1,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
_errorService.logError('activity_bloc', 'Erreur favori: $e');
|
|
emit(const ActivityError('Impossible de modifier les favoris'));
|
|
}
|
|
}
|
|
|
|
/// Applies filters to the activities list
|
|
List<Activity> _applyFilters(
|
|
List<Activity> activities,
|
|
String? category,
|
|
double? minRating,
|
|
bool showVotedOnly,
|
|
String userId,
|
|
) {
|
|
var filtered = activities;
|
|
|
|
if (category != null) {
|
|
filtered = filtered.where((a) {
|
|
// Check exact match (internal value)
|
|
if (a.category == category) return true;
|
|
|
|
// Check display name match
|
|
// Find the enum that matches the filter category (which is a display name)
|
|
try {
|
|
final categoryEnum = ActivityCategory.values.firstWhere(
|
|
(e) => e.displayName == category,
|
|
);
|
|
// Check if activity category matches the enum's google type or display name
|
|
return a.category == categoryEnum.googlePlaceType ||
|
|
a.category == categoryEnum.displayName ||
|
|
a.category.toLowerCase() == categoryEnum.name.toLowerCase();
|
|
} catch (_) {
|
|
// If no matching enum found, fallback to simple string comparison
|
|
return a.category.toLowerCase() == category.toLowerCase();
|
|
}
|
|
}).toList();
|
|
}
|
|
|
|
if (minRating != null) {
|
|
filtered = filtered.where((a) => (a.rating ?? 0) >= minRating).toList();
|
|
}
|
|
|
|
if (showVotedOnly && userId.isNotEmpty) {
|
|
filtered = filtered.where((a) => a.hasUserVoted(userId)).toList();
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
/// Removes an activity from search results
|
|
Future<void> _onRemoveFromSearchResults(
|
|
RemoveFromSearchResults event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
// Si on est actuellement dans un état de résultats de recherche
|
|
if (state is ActivitySearchResults) {
|
|
final currentState = state as ActivitySearchResults;
|
|
|
|
// Filtrer l'activité à retirer
|
|
final updatedResults = currentState.searchResults
|
|
.where((activity) => activity.id != event.activityId)
|
|
.toList();
|
|
|
|
// Émettre le nouvel état avec l'activité retirée
|
|
emit(
|
|
ActivitySearchResults(
|
|
searchResults: updatedResults,
|
|
query: currentState.query,
|
|
isLoading: false,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Restores cached search results
|
|
Future<void> _onRestoreCachedSearchResults(
|
|
RestoreCachedSearchResults event,
|
|
Emitter<ActivityState> emit,
|
|
) async {
|
|
emit(
|
|
ActivitySearchResults(
|
|
searchResults: event.searchResults,
|
|
query: 'cached',
|
|
isLoading: false,
|
|
),
|
|
);
|
|
}
|
|
}
|