feat: Add TripImageService for automatic trip image management
- Implemented TripImageService to load missing images for trips, reload images, and clean up unused images. - Added functionality to get image statistics and clean up duplicate images. - Created utility scripts for manual image cleanup and diagnostics in Firebase Storage. - Introduced tests for image loading optimization and photo quality algorithms. - Updated dependencies in pubspec.yaml and pubspec.lock for image handling.
This commit is contained in:
@@ -19,6 +19,7 @@ import '../../services/user_service.dart';
|
||||
import '../../repositories/group_repository.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import '../../services/place_image_service.dart';
|
||||
|
||||
/// Create trip content widget for trip creation and editing functionality.
|
||||
///
|
||||
@@ -69,6 +70,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
/// Services for user and group operations
|
||||
final _userService = UserService();
|
||||
final _groupRepository = GroupRepository();
|
||||
final _placeImageService = PlaceImageService();
|
||||
|
||||
/// Trip date variables
|
||||
DateTime? _startDate;
|
||||
@@ -77,6 +79,8 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
/// Loading and state management variables
|
||||
bool _isLoading = false;
|
||||
String? _createdTripId;
|
||||
String? _selectedImageUrl;
|
||||
bool _isLoadingImage = false;
|
||||
|
||||
/// Google Maps API key for location services
|
||||
static final String _apiKey = dotenv.env['GOOGLE_MAPS_API_KEY'] ?? '';
|
||||
@@ -111,6 +115,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
_budgetController.text = trip.budget?.toString() ?? '';
|
||||
_startDate = trip.startDate;
|
||||
_endDate = trip.endDate;
|
||||
_selectedImageUrl = trip.imageUrl; // Charger l'image existante
|
||||
});
|
||||
|
||||
await _loadParticipantEmails(trip.participants);
|
||||
@@ -250,6 +255,40 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
setState(() {
|
||||
_placeSuggestions = [];
|
||||
});
|
||||
|
||||
// Charger l'image du lieu sélectionné
|
||||
_loadPlaceImage(suggestion.description);
|
||||
}
|
||||
|
||||
/// Charge l'image du lieu depuis Google Places API
|
||||
Future<void> _loadPlaceImage(String location) async {
|
||||
print('CreateTripContent: Chargement de l\'image pour: $location');
|
||||
setState(() {
|
||||
_isLoadingImage = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final imageUrl = await _placeImageService.getPlaceImageUrl(location);
|
||||
print('CreateTripContent: Image URL reçue: $imageUrl');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_selectedImageUrl = imageUrl;
|
||||
_isLoadingImage = false;
|
||||
});
|
||||
print('CreateTripContent: État mis à jour avec imageUrl: $_selectedImageUrl');
|
||||
}
|
||||
} catch (e) {
|
||||
print('CreateTripContent: Erreur lors du chargement de l\'image: $e');
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoadingImage = false;
|
||||
});
|
||||
_errorService.logError(
|
||||
'create_trip_content.dart',
|
||||
'Erreur lors du chargement de l\'image: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadParticipantEmails(List<String> participantIds) async {
|
||||
@@ -420,6 +459,64 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Aperçu de l'image du lieu
|
||||
if (_isLoadingImage || _selectedImageUrl != null) ...[
|
||||
_buildSectionTitle('Aperçu de la destination'),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey[300]!),
|
||||
),
|
||||
child: _isLoadingImage
|
||||
? const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 8),
|
||||
Text('Chargement de l\'image...'),
|
||||
],
|
||||
),
|
||||
)
|
||||
: _selectedImageUrl != null
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.network(
|
||||
_selectedImageUrl!,
|
||||
width: double.infinity,
|
||||
height: 200,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.error, color: Colors.grey),
|
||||
Text('Erreur de chargement'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildSectionTitle('Dates du voyage'),
|
||||
@@ -891,6 +988,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
|
||||
participants: participantIds,
|
||||
createdAt: isEditing ? widget.tripToEdit!.createdAt : DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
imageUrl: _selectedImageUrl, // Ajouter l'URL de l'image
|
||||
);
|
||||
|
||||
if (isEditing) {
|
||||
|
||||
Reference in New Issue
Block a user