Presearch google activities.

This commit is contained in:
Dayron
2025-11-13 09:27:37 +01:00
parent d17df621f4
commit 236327f6fa
6 changed files with 156 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import '../../models/activity.dart'; import '../../models/activity.dart';
import '../../repositories/activity_repository.dart'; import '../../repositories/activity_repository.dart';
import '../../services/activity_places_service.dart'; import '../../services/activity_places_service.dart';
import '../../services/activity_cache_service.dart';
import '../../services/error_service.dart'; import '../../services/error_service.dart';
import 'activity_event.dart'; import 'activity_event.dart';
import 'activity_state.dart'; import 'activity_state.dart';
@@ -34,6 +35,7 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
on<ClearSearchResults>(_onClearSearchResults); on<ClearSearchResults>(_onClearSearchResults);
on<UpdateActivity>(_onUpdateActivity); on<UpdateActivity>(_onUpdateActivity);
on<ToggleActivityFavorite>(_onToggleActivityFavorite); on<ToggleActivityFavorite>(_onToggleActivityFavorite);
on<RestoreCachedSearchResults>(_onRestoreCachedSearchResults);
} }
/// Handles loading activities for a trip /// Handles loading activities for a trip
@@ -88,6 +90,9 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
finalResults = searchResults; finalResults = searchResults;
} }
// Mettre en cache les résultats
ActivityCacheService().setCachedActivities(event.tripId, finalResults);
emit(ActivitySearchResults( emit(ActivitySearchResults(
searchResults: finalResults, searchResults: finalResults,
query: event.category?.displayName ?? 'Toutes les activités', query: event.category?.displayName ?? 'Toutes les activités',
@@ -132,6 +137,9 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
finalResults = activities; finalResults = activities;
} }
// Mettre en cache les résultats
ActivityCacheService().setCachedActivities(event.tripId, finalResults);
emit(ActivitySearchResults( emit(ActivitySearchResults(
searchResults: finalResults, searchResults: finalResults,
query: event.category?.displayName ?? 'Toutes les activités', query: event.category?.displayName ?? 'Toutes les activités',
@@ -157,6 +165,9 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
tripId: event.tripId, tripId: event.tripId,
); );
// Mettre en cache les résultats
ActivityCacheService().setCachedActivities(event.tripId, searchResults);
emit(ActivitySearchResults( emit(ActivitySearchResults(
searchResults: searchResults, searchResults: searchResults,
query: event.query, query: event.query,
@@ -470,4 +481,16 @@ class ActivityBloc extends Bloc<ActivityEvent, ActivityState> {
return filtered; return filtered;
} }
/// Restores cached search results
Future<void> _onRestoreCachedSearchResults(
RestoreCachedSearchResults event,
Emitter<ActivityState> emit,
) async {
emit(ActivitySearchResults(
searchResults: event.searchResults,
query: 'cached',
isLoading: false,
));
}
} }

View File

@@ -185,3 +185,13 @@ class ToggleActivityFavorite extends ActivityEvent {
@override @override
List<Object> get props => [activityId, userId]; List<Object> get props => [activityId, userId];
} }
/// Event to restore cached search results
class RestoreCachedSearchResults extends ActivityEvent {
final List<Activity> searchResults;
const RestoreCachedSearchResults({required this.searchResults});
@override
List<Object?> get props => [searchResults];
}

View File

@@ -5,6 +5,7 @@ import '../../blocs/activity/activity_event.dart';
import '../../blocs/activity/activity_state.dart'; import '../../blocs/activity/activity_state.dart';
import '../../models/trip.dart'; import '../../models/trip.dart';
import '../../models/activity.dart'; import '../../models/activity.dart';
import '../../services/activity_cache_service.dart';
import '../activities/add_activity_bottom_sheet.dart'; import '../activities/add_activity_bottom_sheet.dart';
class ActivitiesPage extends StatefulWidget { class ActivitiesPage extends StatefulWidget {
@@ -20,6 +21,7 @@ class _ActivitiesPageState extends State<ActivitiesPage>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
late TabController _tabController; late TabController _tabController;
final TextEditingController _searchController = TextEditingController(); final TextEditingController _searchController = TextEditingController();
final ActivityCacheService _cacheService = ActivityCacheService();
String _selectedCategory = 'Toutes les catégories'; String _selectedCategory = 'Toutes les catégories';
String _selectedPrice = 'Prix'; String _selectedPrice = 'Prix';
String _selectedRating = 'Note'; String _selectedRating = 'Note';
@@ -79,7 +81,18 @@ class _ActivitiesPageState extends State<ActivitiesPage>
void _handleTabChange() { void _handleTabChange() {
// Si on va sur l'onglet suggestions Google et qu'aucune recherche n'a été faite // Si on va sur l'onglet suggestions Google et qu'aucune recherche n'a été faite
if (_tabController.index == 2 && !_googleSearchPerformed) { if (_tabController.index == 2 && !_googleSearchPerformed) {
_searchGoogleActivities(); // Vérifier si on a des activités en cache
final cachedActivities = _cacheService.getCachedActivities(widget.trip.id!);
if (cachedActivities != null && cachedActivities.isNotEmpty) {
// Restaurer les activités en cache dans le BLoC
context.read<ActivityBloc>().add(
RestoreCachedSearchResults(searchResults: cachedActivities),
);
_googleSearchPerformed = true;
} else {
// Sinon, faire une nouvelle recherche
_searchGoogleActivities();
}
} }
} }

View File

@@ -29,7 +29,7 @@ class ActivityCard extends StatelessWidget {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -74,7 +74,7 @@ class ActivityCard extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7), color: Colors.black.withValues(alpha: 0.7),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(

View File

@@ -1,11 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:travel_mate/blocs/trip/trip_bloc.dart'; import 'package:travel_mate/blocs/trip/trip_bloc.dart';
import 'package:travel_mate/blocs/trip/trip_event.dart'; import 'package:travel_mate/blocs/trip/trip_event.dart';
import 'package:travel_mate/blocs/activity/activity_bloc.dart';
import 'package:travel_mate/blocs/activity/activity_event.dart';
import 'package:travel_mate/components/home/create_trip_content.dart'; import 'package:travel_mate/components/home/create_trip_content.dart';
import 'package:travel_mate/models/trip.dart'; import 'package:travel_mate/models/trip.dart';
import 'package:travel_mate/components/map/map_content.dart'; import 'package:travel_mate/components/map/map_content.dart';
import 'package:travel_mate/services/error_service.dart'; import 'package:travel_mate/services/error_service.dart';
import 'package:travel_mate/services/activity_cache_service.dart';
import 'package:travel_mate/components/activities/activities_page.dart'; import 'package:travel_mate/components/activities/activities_page.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -19,6 +23,47 @@ class ShowTripDetailsContent extends StatefulWidget {
class _ShowTripDetailsContentState extends State<ShowTripDetailsContent> { class _ShowTripDetailsContentState extends State<ShowTripDetailsContent> {
final ErrorService _errorService = ErrorService(); final ErrorService _errorService = ErrorService();
final ActivityCacheService _cacheService = ActivityCacheService();
@override
void initState() {
super.initState();
// Lancer la recherche d'activités Google en arrière-plan
_preloadGoogleActivities();
}
/// Précharger les activités Google en arrière-plan
void _preloadGoogleActivities() {
// Attendre un moment avant de lancer la recherche pour ne pas bloquer l'UI
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted && widget.trip.id != null) {
// Vérifier si on a déjà des activités en cache
if (_cacheService.hasCachedActivities(widget.trip.id!)) {
return; // Utiliser le cache
}
// Sinon, lancer la recherche
context.read<ActivityBloc>().add(
widget.trip.hasCoordinates
? SearchActivitiesWithCoordinates(
tripId: widget.trip.id!,
latitude: widget.trip.latitude!,
longitude: widget.trip.longitude!,
category: null,
maxResults: 6,
reset: true,
)
: SearchActivities(
tripId: widget.trip.id!,
destination: widget.trip.location,
category: null,
maxResults: 6,
reset: true,
),
);
}
});
}
// Calculer les jours restants avant le voyage // Calculer les jours restants avant le voyage
int get daysUntilTrip { int get daysUntilTrip {

View File

@@ -0,0 +1,62 @@
import '../models/activity.dart';
class ActivityCacheService {
static final ActivityCacheService _instance = ActivityCacheService._internal();
factory ActivityCacheService() {
return _instance;
}
ActivityCacheService._internal();
// Cache : tripId -> liste d'activités
final Map<String, List<Activity>> _cache = {};
// Timestamps pour invalider le cache après 30 minutes
final Map<String, DateTime> _cacheTimestamps = {};
static const Duration _cacheValidityDuration = Duration(minutes: 30);
/// Stocker les activités Google pour un voyage
void setCachedActivities(String tripId, List<Activity> activities) {
_cache[tripId] = activities;
_cacheTimestamps[tripId] = DateTime.now();
}
/// Récupérer les activités en cache si elles sont toujours valides
List<Activity>? getCachedActivities(String tripId) {
if (!_cache.containsKey(tripId)) {
return null;
}
final timestamp = _cacheTimestamps[tripId];
if (timestamp != null) {
final age = DateTime.now().difference(timestamp);
if (age > _cacheValidityDuration) {
// Cache expiré, nettoyer
_cache.remove(tripId);
_cacheTimestamps.remove(tripId);
return null;
}
}
return _cache[tripId];
}
/// Vérifier si des activités sont en cache pour ce voyage
bool hasCachedActivities(String tripId) {
return getCachedActivities(tripId) != null;
}
/// Nettoyer le cache pour un voyage spécifique
void clearCache(String tripId) {
_cache.remove(tripId);
_cacheTimestamps.remove(tripId);
}
/// Nettoyer tout le cache
void clearAllCache() {
_cache.clear();
_cacheTimestamps.clear();
}
}