refactor: Clean up code by removing unnecessary whitespace and improving readability
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,7 @@ import '../../services/error_service.dart';
|
|||||||
class AddActivityBottomSheet extends StatefulWidget {
|
class AddActivityBottomSheet extends StatefulWidget {
|
||||||
final Trip trip;
|
final Trip trip;
|
||||||
|
|
||||||
const AddActivityBottomSheet({
|
const AddActivityBottomSheet({super.key, required this.trip});
|
||||||
Key? key,
|
|
||||||
required this.trip,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddActivityBottomSheet> createState() => _AddActivityBottomSheetState();
|
State<AddActivityBottomSheet> createState() => _AddActivityBottomSheetState();
|
||||||
@@ -26,7 +23,7 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
final _descriptionController = TextEditingController();
|
final _descriptionController = TextEditingController();
|
||||||
final _addressController = TextEditingController();
|
final _addressController = TextEditingController();
|
||||||
final ErrorService _errorService = ErrorService();
|
final ErrorService _errorService = ErrorService();
|
||||||
|
|
||||||
ActivityCategory _selectedCategory = ActivityCategory.attraction;
|
ActivityCategory _selectedCategory = ActivityCategory.attraction;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
@@ -43,16 +40,11 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final keyboardHeight = mediaQuery.viewInsets.bottom;
|
final keyboardHeight = mediaQuery.viewInsets.bottom;
|
||||||
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
height: mediaQuery.size.height * 0.85,
|
height: mediaQuery.size.height * 0.85,
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 16),
|
||||||
left: 16,
|
|
||||||
right: 16,
|
|
||||||
top: 16,
|
|
||||||
bottom: 16,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.cardColor,
|
color: theme.cardColor,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -167,7 +159,9 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
side: BorderSide(color: theme.colorScheme.outline),
|
side: BorderSide(
|
||||||
|
color: theme.colorScheme.outline,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
@@ -193,7 +187,9 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
height: 20,
|
height: 20,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const Text('Ajouter'),
|
: const Text('Ajouter'),
|
||||||
@@ -214,9 +210,9 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
Widget _buildSectionTitle(String title) {
|
Widget _buildSectionTitle(String title) {
|
||||||
return Text(
|
return Text(
|
||||||
title,
|
title,
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: Theme.of(
|
||||||
fontWeight: FontWeight.w600,
|
context,
|
||||||
),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,17 +236,17 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isDarkMode
|
color: isDarkMode
|
||||||
? Colors.white.withOpacity(0.2)
|
? Colors.white.withOpacity(0.2)
|
||||||
: Colors.black.withOpacity(0.2),
|
: Colors.black.withOpacity(0.2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: isDarkMode
|
color: isDarkMode
|
||||||
? Colors.white.withOpacity(0.2)
|
? Colors.white.withOpacity(0.2)
|
||||||
: Colors.black.withOpacity(0.2),
|
: Colors.black.withOpacity(0.2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
@@ -265,15 +261,13 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
|
|
||||||
Widget _buildCategorySelector() {
|
Widget _buildCategorySelector() {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.colorScheme.surface,
|
color: theme.colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(color: theme.colorScheme.outline.withOpacity(0.5)),
|
||||||
color: theme.colorScheme.outline.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -290,7 +284,7 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: ActivityCategory.values.map((category) {
|
children: ActivityCategory.values.map((category) {
|
||||||
final isSelected = _selectedCategory == category;
|
final isSelected = _selectedCategory == category;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -298,12 +292,17 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? Colors.blue : Colors.transparent,
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? Colors.blue : theme.colorScheme.outline,
|
color: isSelected
|
||||||
|
? Colors.blue
|
||||||
|
: theme.colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -312,17 +311,17 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
Icon(
|
Icon(
|
||||||
_getCategoryIcon(category),
|
_getCategoryIcon(category),
|
||||||
size: 16,
|
size: 16,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: theme.colorScheme.onSurface,
|
: theme.colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(
|
Text(
|
||||||
category.displayName,
|
category.displayName,
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: theme.colorScheme.onSurface,
|
: theme.colorScheme.onSurface,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -361,19 +360,18 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
name: _nameController.text.trim(),
|
name: _nameController.text.trim(),
|
||||||
description: _descriptionController.text.trim(),
|
description: _descriptionController.text.trim(),
|
||||||
category: _selectedCategory.displayName,
|
category: _selectedCategory.displayName,
|
||||||
address: _addressController.text.trim().isNotEmpty
|
address: _addressController.text.trim().isNotEmpty
|
||||||
? _addressController.text.trim()
|
? _addressController.text.trim()
|
||||||
: null,
|
: null,
|
||||||
votes: {},
|
votes: {},
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
context.read<ActivityBloc>().add(AddActivity(activity));
|
context.read<ActivityBloc>().add(AddActivity(activity));
|
||||||
|
|
||||||
// Fermer le bottom sheet
|
// Fermer le bottom sheet
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_errorService.showSnackbar(
|
_errorService.showSnackbar(
|
||||||
message: 'Erreur lors de l\'ajout de l\'activité',
|
message: 'Erreur lors de l\'ajout de l\'activité',
|
||||||
@@ -412,4 +410,4 @@ class _AddActivityBottomSheetState extends State<AddActivityBottomSheet> {
|
|||||||
return Icons.spa;
|
return Icons.spa;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ class TripCard extends StatefulWidget {
|
|||||||
final Trip trip;
|
final Trip trip;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const TripCard({
|
const TripCard({super.key, required this.trip, this.onTap});
|
||||||
super.key,
|
|
||||||
required this.trip,
|
|
||||||
this.onTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TripCard> createState() => _TripCardState();
|
State<TripCard> createState() => _TripCardState();
|
||||||
@@ -29,7 +25,7 @@ class _TripCardState extends State<TripCard> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_currentImageUrl = widget.trip.imageUrl;
|
_currentImageUrl = widget.trip.imageUrl;
|
||||||
|
|
||||||
// Si aucune image n'est disponible, essayer de la charger
|
// Si aucune image n'est disponible, essayer de la charger
|
||||||
if (_currentImageUrl == null || _currentImageUrl!.isEmpty) {
|
if (_currentImageUrl == null || _currentImageUrl!.isEmpty) {
|
||||||
_loadImageForTrip();
|
_loadImageForTrip();
|
||||||
@@ -38,28 +34,29 @@ class _TripCardState extends State<TripCard> {
|
|||||||
|
|
||||||
Future<void> _loadImageForTrip() async {
|
Future<void> _loadImageForTrip() async {
|
||||||
if (_hasTriedLoading || _isLoadingImage) return;
|
if (_hasTriedLoading || _isLoadingImage) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingImage = true;
|
_isLoadingImage = true;
|
||||||
_hasTriedLoading = true;
|
_hasTriedLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// D'abord vérifier si une image existe déjà dans le Storage
|
// D'abord vérifier si une image existe déjà dans le Storage
|
||||||
String? imageUrl = await _placeImageService.getExistingImageUrl(widget.trip.location);
|
String? imageUrl = await _placeImageService.getExistingImageUrl(
|
||||||
|
widget.trip.location,
|
||||||
|
);
|
||||||
|
|
||||||
// Si aucune image n'existe, en télécharger une nouvelle
|
// Si aucune image n'existe, en télécharger une nouvelle
|
||||||
if (imageUrl == null) {
|
imageUrl ??= await _placeImageService.getPlaceImageUrl(
|
||||||
imageUrl = await _placeImageService.getPlaceImageUrl(widget.trip.location);
|
widget.trip.location,
|
||||||
}
|
);
|
||||||
|
|
||||||
if (mounted && imageUrl != null) {
|
if (mounted && imageUrl != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentImageUrl = imageUrl;
|
_currentImageUrl = imageUrl;
|
||||||
_isLoadingImage = false;
|
_isLoadingImage = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mettre à jour le voyage dans la base de données avec l'imageUrl
|
// Mettre à jour le voyage dans la base de données avec l'imageUrl
|
||||||
_updateTripWithImage(imageUrl);
|
_updateTripWithImage(imageUrl);
|
||||||
} else {
|
} else {
|
||||||
@@ -84,7 +81,7 @@ class _TripCardState extends State<TripCard> {
|
|||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mettre à jour dans la base de données
|
// Mettre à jour dans la base de données
|
||||||
await _tripRepository.updateTrip(widget.trip.id!, updatedTrip);
|
await _tripRepository.updateTrip(widget.trip.id!, updatedTrip);
|
||||||
}
|
}
|
||||||
@@ -95,7 +92,7 @@ class _TripCardState extends State<TripCard> {
|
|||||||
|
|
||||||
Widget _buildImageWidget() {
|
Widget _buildImageWidget() {
|
||||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
|
||||||
if (_isLoadingImage) {
|
if (_isLoadingImage) {
|
||||||
return Container(
|
return Container(
|
||||||
color: isDarkMode ? Colors.grey[700] : Colors.grey[200],
|
color: isDarkMode ? Colors.grey[700] : Colors.grey[200],
|
||||||
@@ -111,21 +108,20 @@ class _TripCardState extends State<TripCard> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentImageUrl != null && _currentImageUrl!.isNotEmpty) {
|
if (_currentImageUrl != null && _currentImageUrl!.isNotEmpty) {
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
imageUrl: _currentImageUrl!,
|
imageUrl: _currentImageUrl!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
placeholder: (context, url) => Container(
|
placeholder: (context, url) => Container(
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
child: const Center(
|
child: const Center(child: CircularProgressIndicator()),
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
errorWidget: (context, url, error) => _buildPlaceholderImage(isDarkMode),
|
errorWidget: (context, url, error) =>
|
||||||
|
_buildPlaceholderImage(isDarkMode),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _buildPlaceholderImage(isDarkMode);
|
return _buildPlaceholderImage(isDarkMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,9 +136,7 @@ class _TripCardState extends State<TripCard> {
|
|||||||
elevation: 4,
|
elevation: 4,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
color: cardColor,
|
color: cardColor,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -153,7 +147,9 @@ class _TripCardState extends State<TripCard> {
|
|||||||
children: [
|
children: [
|
||||||
// Image du voyage
|
// Image du voyage
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12),
|
||||||
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@@ -178,7 +174,7 @@ class _TripCardState extends State<TripCard> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
// Dates, participants et bouton voir
|
// Dates, participants et bouton voir
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -207,14 +203,17 @@ class _TripCardState extends State<TripCard> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Bouton Voir
|
// Bouton Voir
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: widget.onTap,
|
onPressed: widget.onTap,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
@@ -229,8 +228,8 @@ class _TripCardState extends State<TripCard> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -261,4 +260,4 @@ class _TripCardState extends State<TripCard> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class Activity {
|
|||||||
/// Vérifie si tous les participants du voyage ont voté positivement pour cette activité
|
/// Vérifie si tous les participants du voyage ont voté positivement pour cette activité
|
||||||
bool isApprovedByAllParticipants(List<String> tripParticipants) {
|
bool isApprovedByAllParticipants(List<String> tripParticipants) {
|
||||||
if (tripParticipants.isEmpty) return false;
|
if (tripParticipants.isEmpty) return false;
|
||||||
|
|
||||||
// Tous les participants doivent avoir voté
|
// Tous les participants doivent avoir voté
|
||||||
for (String participantId in tripParticipants) {
|
for (String participantId in tripParticipants) {
|
||||||
if (!votes.containsKey(participantId)) {
|
if (!votes.containsKey(participantId)) {
|
||||||
@@ -80,7 +80,7 @@ class Activity {
|
|||||||
return false; // Quelqu'un a voté négativement ou neutre
|
return false; // Quelqu'un a voté négativement ou neutre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true; // Tous ont voté positivement
|
return true; // Tous ont voté positivement
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ class Activity {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'Activity(id: $id, name: $name, category: $category, votes: ${totalVotes})';
|
return 'Activity(id: $id, name: $name, category: $category, votes: $totalVotes)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -245,4 +245,4 @@ enum PriceLevel {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,27 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import '../lib/services/trip_image_service.dart';
|
import 'package:travel_mate/services/trip_image_service.dart';
|
||||||
|
|
||||||
/// Script utilitaire pour nettoyer les images inutilisées
|
/// Script utilitaire pour nettoyer les images inutilisées
|
||||||
/// À exécuter manuellement si nécessaire
|
/// À exécuter manuellement si nécessaire
|
||||||
void main() async {
|
void main() async {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final tripImageService = TripImageService();
|
final tripImageService = TripImageService();
|
||||||
|
|
||||||
// Remplacez par votre ID utilisateur
|
// Remplacez par votre ID utilisateur
|
||||||
// Vous pouvez le récupérer depuis Firebase Auth dans votre app
|
// Vous pouvez le récupérer depuis Firebase Auth dans votre app
|
||||||
const userId = 'YOUR_USER_ID_HERE';
|
const userId = 'YOUR_USER_ID_HERE';
|
||||||
|
|
||||||
if (userId == 'YOUR_USER_ID_HERE') {
|
if (userId == 'YOUR_USER_ID_HERE') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final stats = await tripImageService.getImageStatistics(userId);
|
final stats = await tripImageService.getImageStatistics(userId);
|
||||||
|
|
||||||
|
|
||||||
if (stats['tripsWithImages'] > 0) {
|
if (stats['tripsWithImages'] > 0) {
|
||||||
await tripImageService.cleanupUnusedImages(userId);
|
await tripImageService.cleanupUnusedImages(userId);
|
||||||
|
|
||||||
final newStats = await tripImageService.getImageStatistics(userId);
|
final newStats = await tripImageService.getImageStatistics(userId);
|
||||||
} else {
|
} else {}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -42,4 +37,4 @@ Instructions d'utilisation:
|
|||||||
4. Récupérez votre User ID affiché dans la console
|
4. Récupérez votre User ID affiché dans la console
|
||||||
5. Remplacez 'YOUR_USER_ID_HERE' par votre ID dans ce script
|
5. Remplacez 'YOUR_USER_ID_HERE' par votre ID dans ce script
|
||||||
6. Exécutez: dart run scripts/cleanup_images.dart
|
6. Exécutez: dart run scripts/cleanup_images.dart
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,48 +1,47 @@
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
import '../lib/firebase_options.dart';
|
import 'package:travel_mate/firebase_options.dart';
|
||||||
|
|
||||||
/// Script de diagnostic pour analyser les images dans Firebase Storage
|
/// Script de diagnostic pour analyser les images dans Firebase Storage
|
||||||
void main() async {
|
void main() async {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialiser Firebase
|
// Initialiser Firebase
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptions.currentPlatform,
|
options: DefaultFirebaseOptions.currentPlatform,
|
||||||
);
|
);
|
||||||
|
|
||||||
final storage = FirebaseStorage.instance;
|
final storage = FirebaseStorage.instance;
|
||||||
|
|
||||||
final listResult = await storage.ref('trip_images').listAll();
|
final listResult = await storage.ref('trip_images').listAll();
|
||||||
|
|
||||||
if (listResult.items.isEmpty) {
|
if (listResult.items.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final Map<String, List<Map<String, dynamic>>> locationGroups = {};
|
final Map<String, List<Map<String, dynamic>>> locationGroups = {};
|
||||||
|
|
||||||
for (int i = 0; i < listResult.items.length; i++) {
|
for (int i = 0; i < listResult.items.length; i++) {
|
||||||
final item = listResult.items[i];
|
final item = listResult.items[i];
|
||||||
final fileName = item.name;
|
final fileName = item.name;
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Récupérer les métadonnées
|
// Récupérer les métadonnées
|
||||||
final metadata = await item.getMetadata();
|
final metadata = await item.getMetadata();
|
||||||
final customMeta = metadata.customMetadata ?? {};
|
final customMeta = metadata.customMetadata ?? {};
|
||||||
|
|
||||||
final location = customMeta['location'] ?? 'Inconnue';
|
final location = customMeta['location'] ?? 'Inconnue';
|
||||||
final normalizedLocation = customMeta['normalizedLocation'] ?? 'Non définie';
|
final normalizedLocation =
|
||||||
|
customMeta['normalizedLocation'] ?? 'Non définie';
|
||||||
final source = customMeta['source'] ?? 'Inconnue';
|
final source = customMeta['source'] ?? 'Inconnue';
|
||||||
final uploadedAt = customMeta['uploadedAt'] ?? 'Inconnue';
|
final uploadedAt = customMeta['uploadedAt'] ?? 'Inconnue';
|
||||||
|
|
||||||
|
|
||||||
// Récupérer l'URL de téléchargement
|
// Récupérer l'URL de téléchargement
|
||||||
final downloadUrl = await item.getDownloadURL();
|
final downloadUrl = await item.getDownloadURL();
|
||||||
|
|
||||||
// Grouper par location normalisée
|
// Grouper par location normalisée
|
||||||
final groupKey = normalizedLocation != 'Non définie' ? normalizedLocation : location.toLowerCase();
|
final groupKey = normalizedLocation != 'Non définie'
|
||||||
|
? normalizedLocation
|
||||||
|
: location.toLowerCase();
|
||||||
if (!locationGroups.containsKey(groupKey)) {
|
if (!locationGroups.containsKey(groupKey)) {
|
||||||
locationGroups[groupKey] = [];
|
locationGroups[groupKey] = [];
|
||||||
}
|
}
|
||||||
@@ -53,14 +52,12 @@ void main() async {
|
|||||||
'uploadedAt': uploadedAt,
|
'uploadedAt': uploadedAt,
|
||||||
'downloadUrl': downloadUrl,
|
'downloadUrl': downloadUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
// Essayer de deviner la location depuis le nom du fichier
|
// Essayer de deviner la location depuis le nom du fichier
|
||||||
final parts = fileName.split('_');
|
final parts = fileName.split('_');
|
||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
final guessedLocation = parts.take(parts.length - 1).join('_');
|
final guessedLocation = parts.take(parts.length - 1).join('_');
|
||||||
|
|
||||||
if (!locationGroups.containsKey(guessedLocation)) {
|
if (!locationGroups.containsKey(guessedLocation)) {
|
||||||
locationGroups[guessedLocation] = [];
|
locationGroups[guessedLocation] = [];
|
||||||
}
|
}
|
||||||
@@ -73,30 +70,24 @@ void main() async {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyser les doublons
|
// Analyser les doublons
|
||||||
|
|
||||||
int totalDuplicates = 0;
|
int totalDuplicates = 0;
|
||||||
for (final entry in locationGroups.entries) {
|
for (final entry in locationGroups.entries) {
|
||||||
final location = entry.key;
|
final location = entry.key;
|
||||||
final images = entry.value;
|
final images = entry.value;
|
||||||
|
|
||||||
if (images.length > 1) {
|
if (images.length > 1) {
|
||||||
totalDuplicates += images.length - 1;
|
totalDuplicates += images.length - 1;
|
||||||
|
|
||||||
for (int i = 0; i < images.length; i++) {
|
for (int i = 0; i < images.length; i++) {
|
||||||
final image = images[i];
|
final image = images[i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (totalDuplicates > 0) {}
|
||||||
if (totalDuplicates > 0) {
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,43 +4,44 @@ void main() {
|
|||||||
group('Image Loading Logic Tests', () {
|
group('Image Loading Logic Tests', () {
|
||||||
test('should demonstrate the new flow without duplicate downloads', () {
|
test('should demonstrate the new flow without duplicate downloads', () {
|
||||||
// Simulation du nouveau flux de chargement d'images
|
// Simulation du nouveau flux de chargement d'images
|
||||||
|
|
||||||
// Scénario 1: Premier chargement (aucune image existante)
|
// Scénario 1: Premier chargement (aucune image existante)
|
||||||
print('=== Scénario 1: Premier chargement ===');
|
print('=== Scénario 1: Premier chargement ===');
|
||||||
String? existingImage = null; // Aucune image dans le Storage
|
String? existingImage; // Aucune image dans le Storage
|
||||||
|
|
||||||
if (existingImage == null) {
|
if (existingImage == null) {
|
||||||
print('✓ Aucune image existante trouvée');
|
print('✓ Aucune image existante trouvée');
|
||||||
print('✓ Téléchargement d\'une nouvelle image depuis Google Places');
|
print('✓ Téléchargement d\'une nouvelle image depuis Google Places');
|
||||||
existingImage = 'https://storage.googleapis.com/image1.jpg';
|
existingImage = 'https://storage.googleapis.com/image1.jpg';
|
||||||
print('✓ Image sauvée: $existingImage');
|
print('✓ Image sauvée: $existingImage');
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(existingImage, isNotNull);
|
expect(existingImage, isNotNull);
|
||||||
|
|
||||||
// Scénario 2: Rechargement (image existante)
|
// Scénario 2: Rechargement (image existante)
|
||||||
print('\n=== Scénario 2: Rechargement avec image existante ===');
|
print('\n=== Scénario 2: Rechargement avec image existante ===');
|
||||||
String? cachedImage = existingImage; // Image déjà dans le Storage
|
String? cachedImage = existingImage; // Image déjà dans le Storage
|
||||||
|
|
||||||
print('✓ Image existante trouvée: $cachedImage');
|
print('✓ Image existante trouvée: $cachedImage');
|
||||||
print('✓ PAS de nouveau téléchargement');
|
print('✓ PAS de nouveau téléchargement');
|
||||||
print('✓ Réutilisation de l\'image existante');
|
print('✓ Réutilisation de l\'image existante');
|
||||||
|
|
||||||
expect(cachedImage, equals(existingImage));
|
expect(cachedImage, equals(existingImage));
|
||||||
|
|
||||||
// Scénario 3: Différente destination
|
// Scénario 3: Différente destination
|
||||||
print('\n=== Scénario 3: Destination différente ===');
|
print('\n=== Scénario 3: Destination différente ===');
|
||||||
String? differentLocationImage = null; // Pas d'image pour cette nouvelle destination
|
String?
|
||||||
|
differentLocationImage; // Pas d'image pour cette nouvelle destination
|
||||||
|
|
||||||
if (differentLocationImage == null) {
|
if (differentLocationImage == null) {
|
||||||
print('✓ Nouvelle destination, aucune image existante');
|
print('✓ Nouvelle destination, aucune image existante');
|
||||||
print('✓ Téléchargement autorisé pour cette nouvelle destination');
|
print('✓ Téléchargement autorisé pour cette nouvelle destination');
|
||||||
differentLocationImage = 'https://storage.googleapis.com/image2.jpg';
|
differentLocationImage = 'https://storage.googleapis.com/image2.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(differentLocationImage, isNotNull);
|
expect(differentLocationImage, isNotNull);
|
||||||
expect(differentLocationImage, isNot(equals(existingImage)));
|
expect(differentLocationImage, isNot(equals(existingImage)));
|
||||||
|
|
||||||
print('\n=== Résumé ===');
|
print('\n=== Résumé ===');
|
||||||
print('• Image pour destination 1: $existingImage');
|
print('• Image pour destination 1: $existingImage');
|
||||||
print('• Image pour destination 2: $differentLocationImage');
|
print('• Image pour destination 2: $differentLocationImage');
|
||||||
@@ -52,9 +53,15 @@ void main() {
|
|||||||
final testCases = [
|
final testCases = [
|
||||||
{'input': 'Paris, France', 'expected': 'paris_france'},
|
{'input': 'Paris, France', 'expected': 'paris_france'},
|
||||||
{'input': 'New York City', 'expected': 'new_york_city'},
|
{'input': 'New York City', 'expected': 'new_york_city'},
|
||||||
{'input': 'São Paulo', 'expected': 's_o_paulo'}, // Caractères spéciaux remplacés
|
{
|
||||||
|
'input': 'São Paulo',
|
||||||
|
'expected': 's_o_paulo',
|
||||||
|
}, // Caractères spéciaux remplacés
|
||||||
{'input': 'Londres, Royaume-Uni', 'expected': 'londres_royaume_uni'},
|
{'input': 'Londres, Royaume-Uni', 'expected': 'londres_royaume_uni'},
|
||||||
{'input': 'Tokyo (東京)', 'expected': 'tokyo'}, // Caractères non-latins supprimés
|
{
|
||||||
|
'input': 'Tokyo (東京)',
|
||||||
|
'expected': 'tokyo',
|
||||||
|
}, // Caractères non-latins supprimés
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final testCase in testCases) {
|
for (final testCase in testCases) {
|
||||||
@@ -66,28 +73,28 @@ void main() {
|
|||||||
|
|
||||||
test('should demonstrate memory and performance benefits', () {
|
test('should demonstrate memory and performance benefits', () {
|
||||||
// Simulation des bénéfices de performance
|
// Simulation des bénéfices de performance
|
||||||
|
|
||||||
final oldSystem = {
|
final oldSystem = {
|
||||||
'apiCalls': 4, // 4 appels à chaque chargement
|
'apiCalls': 4, // 4 appels à chaque chargement
|
||||||
'storageWrites': 4, // 4 écritures dans le Storage
|
'storageWrites': 4, // 4 écritures dans le Storage
|
||||||
'storageReads': 0, // Pas de vérification existante
|
'storageReads': 0, // Pas de vérification existante
|
||||||
'dataUsage': '4.8 MB', // 4 images × 1.2 MB chacune
|
'dataUsage': '4.8 MB', // 4 images × 1.2 MB chacune
|
||||||
};
|
};
|
||||||
|
|
||||||
final newSystem = {
|
final newSystem = {
|
||||||
'apiCalls': 2, // Seulement pour les nouvelles destinations
|
'apiCalls': 2, // Seulement pour les nouvelles destinations
|
||||||
'storageWrites': 2, // Seulement pour les nouvelles images
|
'storageWrites': 2, // Seulement pour les nouvelles images
|
||||||
'storageReads': 2, // Vérifications d'existence
|
'storageReads': 2, // Vérifications d'existence
|
||||||
'dataUsage': '2.4 MB', // Seulement 2 images nécessaires
|
'dataUsage': '2.4 MB', // Seulement 2 images nécessaires
|
||||||
};
|
};
|
||||||
|
|
||||||
print('=== Comparaison de performance ===');
|
print('=== Comparaison de performance ===');
|
||||||
print('Ancien système:');
|
print('Ancien système:');
|
||||||
oldSystem.forEach((key, value) => print(' $key: $value'));
|
oldSystem.forEach((key, value) => print(' $key: $value'));
|
||||||
|
|
||||||
print('\nNouveau système:');
|
print('\nNouveau système:');
|
||||||
newSystem.forEach((key, value) => print(' $key: $value'));
|
newSystem.forEach((key, value) => print(' $key: $value'));
|
||||||
|
|
||||||
print('\nAméliorations:');
|
print('\nAméliorations:');
|
||||||
print(' • API calls: -50%');
|
print(' • API calls: -50%');
|
||||||
print(' • Storage writes: -50%');
|
print(' • Storage writes: -50%');
|
||||||
@@ -113,4 +120,4 @@ String _normalizeLocationName(String location) {
|
|||||||
.replaceAll(RegExp(r'[^a-z0-9]'), '_')
|
.replaceAll(RegExp(r'[^a-z0-9]'), '_')
|
||||||
.replaceAll(RegExp(r'_+'), '_')
|
.replaceAll(RegExp(r'_+'), '_')
|
||||||
.replaceAll(RegExp(r'^_|_$'), '');
|
.replaceAll(RegExp(r'^_|_$'), '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,42 +11,47 @@ void main() {
|
|||||||
{'width': 1200, 'height': 800, 'photo_reference': 'horizontal_hd'},
|
{'width': 1200, 'height': 800, 'photo_reference': 'horizontal_hd'},
|
||||||
{'width': 600, 'height': 400, 'photo_reference': 'horizontal2'},
|
{'width': 600, 'height': 400, 'photo_reference': 'horizontal2'},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Appliquer l'algorithme de tri de qualité
|
// Appliquer l'algorithme de tri de qualité
|
||||||
photos.sort((a, b) => _sortPhotosByQuality(a, b));
|
photos.sort((a, b) => _sortPhotosByQuality(a, b));
|
||||||
|
|
||||||
// Vérifications
|
// Vérifications
|
||||||
expect(photos.first['photo_reference'], 'horizontal_hd');
|
expect(photos.first['photo_reference'], 'horizontal_hd');
|
||||||
expect(photos.first['width'], 1200);
|
expect(photos.first['width'], 1200);
|
||||||
expect(photos.first['height'], 800);
|
expect(photos.first['height'], 800);
|
||||||
|
|
||||||
// La photo verticale devrait être parmi les dernières
|
// La photo verticale devrait être parmi les dernières
|
||||||
final lastPhotos = photos.sublist(photos.length - 2);
|
final lastPhotos = photos.sublist(photos.length - 2);
|
||||||
expect(lastPhotos.any((p) => p['photo_reference'] == 'vertical1'), true);
|
expect(lastPhotos.any((p) => p['photo_reference'] == 'vertical1'), true);
|
||||||
|
|
||||||
print('Photos triées par qualité (meilleure en premier):');
|
print('Photos triées par qualité (meilleure en premier):');
|
||||||
for (var photo in photos) {
|
for (var photo in photos) {
|
||||||
final width = photo['width'] as int;
|
final width = photo['width'] as int;
|
||||||
final height = photo['height'] as int;
|
final height = photo['height'] as int;
|
||||||
final ratio = width / height;
|
final ratio = width / height;
|
||||||
final resolution = width * height;
|
final resolution = width * height;
|
||||||
print('${photo['photo_reference']}: ${width}x${height} '
|
print(
|
||||||
'(ratio: ${ratio.toStringAsFixed(2)}, résolution: $resolution)');
|
'${photo['photo_reference']}: ${width}x$height '
|
||||||
|
'(ratio: ${ratio.toStringAsFixed(2)}, résolution: $resolution)',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should generate correct search terms for Paris', () {
|
test('should generate correct search terms for Paris', () {
|
||||||
final searchTerms = _generateSearchTerms('Paris');
|
final searchTerms = _generateSearchTerms('Paris');
|
||||||
|
|
||||||
// Vérifier que les termes spécifiques à Paris sont présents
|
// Vérifier que les termes spécifiques à Paris sont présents
|
||||||
expect(searchTerms.any((term) => term.contains('Tour Eiffel')), true);
|
expect(searchTerms.any((term) => term.contains('Tour Eiffel')), true);
|
||||||
expect(searchTerms.any((term) => term.contains('Arc de Triomphe')), true);
|
expect(searchTerms.any((term) => term.contains('Arc de Triomphe')), true);
|
||||||
expect(searchTerms.any((term) => term.contains('Notre-Dame')), true);
|
expect(searchTerms.any((term) => term.contains('Notre-Dame')), true);
|
||||||
|
|
||||||
// Vérifier que les termes génériques sont aussi présents
|
// Vérifier que les termes génériques sont aussi présents
|
||||||
expect(searchTerms.any((term) => term.contains('attractions touristiques')), true);
|
expect(
|
||||||
|
searchTerms.any((term) => term.contains('attractions touristiques')),
|
||||||
|
true,
|
||||||
|
);
|
||||||
expect(searchTerms.any((term) => term.contains('landmarks')), true);
|
expect(searchTerms.any((term) => term.contains('landmarks')), true);
|
||||||
|
|
||||||
print('Termes de recherche pour Paris:');
|
print('Termes de recherche pour Paris:');
|
||||||
for (var term in searchTerms) {
|
for (var term in searchTerms) {
|
||||||
print('- $term');
|
print('- $term');
|
||||||
@@ -55,12 +60,12 @@ void main() {
|
|||||||
|
|
||||||
test('should generate correct search terms for London', () {
|
test('should generate correct search terms for London', () {
|
||||||
final searchTerms = _generateSearchTerms('London');
|
final searchTerms = _generateSearchTerms('London');
|
||||||
|
|
||||||
// Vérifier que les termes spécifiques à Londres sont présents
|
// Vérifier que les termes spécifiques à Londres sont présents
|
||||||
expect(searchTerms.any((term) => term.contains('Big Ben')), true);
|
expect(searchTerms.any((term) => term.contains('Big Ben')), true);
|
||||||
expect(searchTerms.any((term) => term.contains('Tower Bridge')), true);
|
expect(searchTerms.any((term) => term.contains('Tower Bridge')), true);
|
||||||
expect(searchTerms.any((term) => term.contains('London Eye')), true);
|
expect(searchTerms.any((term) => term.contains('London Eye')), true);
|
||||||
|
|
||||||
print('Termes de recherche pour Londres:');
|
print('Termes de recherche pour Londres:');
|
||||||
for (var term in searchTerms) {
|
for (var term in searchTerms) {
|
||||||
print('- $term');
|
print('- $term');
|
||||||
@@ -69,15 +74,18 @@ void main() {
|
|||||||
|
|
||||||
test('should handle unknown cities gracefully', () {
|
test('should handle unknown cities gracefully', () {
|
||||||
final searchTerms = _generateSearchTerms('Petite Ville Inconnue');
|
final searchTerms = _generateSearchTerms('Petite Ville Inconnue');
|
||||||
|
|
||||||
// Devrait au moins avoir des termes génériques
|
// Devrait au moins avoir des termes génériques
|
||||||
expect(searchTerms.isNotEmpty, true);
|
expect(searchTerms.isNotEmpty, true);
|
||||||
expect(searchTerms.any((term) => term.contains('attractions touristiques')), true);
|
expect(
|
||||||
|
searchTerms.any((term) => term.contains('attractions touristiques')),
|
||||||
|
true,
|
||||||
|
);
|
||||||
expect(searchTerms.any((term) => term.contains('landmarks')), true);
|
expect(searchTerms.any((term) => term.contains('landmarks')), true);
|
||||||
|
|
||||||
// Le terme original devrait être en dernier
|
// Le terme original devrait être en dernier
|
||||||
expect(searchTerms.last, 'Petite Ville Inconnue');
|
expect(searchTerms.last, 'Petite Ville Inconnue');
|
||||||
|
|
||||||
print('Termes de recherche pour ville inconnue:');
|
print('Termes de recherche pour ville inconnue:');
|
||||||
for (var term in searchTerms) {
|
for (var term in searchTerms) {
|
||||||
print('- $term');
|
print('- $term');
|
||||||
@@ -92,39 +100,39 @@ int _sortPhotosByQuality(Map<String, dynamic> a, Map<String, dynamic> b) {
|
|||||||
final aHeight = a['height'] as int;
|
final aHeight = a['height'] as int;
|
||||||
final bWidth = b['width'] as int;
|
final bWidth = b['width'] as int;
|
||||||
final bHeight = b['height'] as int;
|
final bHeight = b['height'] as int;
|
||||||
|
|
||||||
final aRatio = aWidth / aHeight;
|
final aRatio = aWidth / aHeight;
|
||||||
final bRatio = bWidth / bHeight;
|
final bRatio = bWidth / bHeight;
|
||||||
|
|
||||||
// 1. Privilégier les photos horizontales (ratio > 1)
|
// 1. Privilégier les photos horizontales (ratio > 1)
|
||||||
if (aRatio > 1 && bRatio <= 1) return -1;
|
if (aRatio > 1 && bRatio <= 1) return -1;
|
||||||
if (bRatio > 1 && aRatio <= 1) return 1;
|
if (bRatio > 1 && aRatio <= 1) return 1;
|
||||||
|
|
||||||
// 2. Si les deux sont horizontales ou les deux ne le sont pas,
|
// 2. Si les deux sont horizontales ou les deux ne le sont pas,
|
||||||
// privilégier la résolution plus élevée
|
// privilégier la résolution plus élevée
|
||||||
final aResolution = aWidth * aHeight;
|
final aResolution = aWidth * aHeight;
|
||||||
final bResolution = bWidth * bHeight;
|
final bResolution = bWidth * bHeight;
|
||||||
|
|
||||||
if (aResolution != bResolution) {
|
if (aResolution != bResolution) {
|
||||||
return bResolution.compareTo(aResolution);
|
return bResolution.compareTo(aResolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. En cas d'égalité de résolution, privilégier le meilleur ratio (plus proche de 1.5)
|
// 3. En cas d'égalité de résolution, privilégier le meilleur ratio (plus proche de 1.5)
|
||||||
final idealRatio = 1.5;
|
final idealRatio = 1.5;
|
||||||
final aDiff = (aRatio - idealRatio).abs();
|
final aDiff = (aRatio - idealRatio).abs();
|
||||||
final bDiff = (bRatio - idealRatio).abs();
|
final bDiff = (bRatio - idealRatio).abs();
|
||||||
|
|
||||||
return aDiff.compareTo(bDiff);
|
return aDiff.compareTo(bDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reproduit l'algorithme de génération de termes de recherche
|
/// Reproduit l'algorithme de génération de termes de recherche
|
||||||
List<String> _generateSearchTerms(String location) {
|
List<String> _generateSearchTerms(String location) {
|
||||||
final terms = <String>[];
|
final terms = <String>[];
|
||||||
|
|
||||||
// Ajouter des termes spécifiques pour les villes connues
|
// Ajouter des termes spécifiques pour les villes connues
|
||||||
final citySpecificTerms = _getCitySpecificTerms(location.toLowerCase());
|
final citySpecificTerms = _getCitySpecificTerms(location.toLowerCase());
|
||||||
terms.addAll(citySpecificTerms);
|
terms.addAll(citySpecificTerms);
|
||||||
|
|
||||||
// Termes génériques avec attractions
|
// Termes génériques avec attractions
|
||||||
terms.addAll([
|
terms.addAll([
|
||||||
'$location attractions touristiques monuments',
|
'$location attractions touristiques monuments',
|
||||||
@@ -136,14 +144,14 @@ List<String> _generateSearchTerms(String location) {
|
|||||||
'$location skyline',
|
'$location skyline',
|
||||||
location, // Terme original en dernier
|
location, // Terme original en dernier
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return terms;
|
return terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reproduit les termes spécifiques pour des villes connues
|
/// Reproduit les termes spécifiques pour des villes connues
|
||||||
List<String> _getCitySpecificTerms(String location) {
|
List<String> _getCitySpecificTerms(String location) {
|
||||||
final specific = <String>[];
|
final specific = <String>[];
|
||||||
|
|
||||||
if (location.contains('paris')) {
|
if (location.contains('paris')) {
|
||||||
specific.addAll([
|
specific.addAll([
|
||||||
'Tour Eiffel Paris',
|
'Tour Eiffel Paris',
|
||||||
@@ -182,6 +190,6 @@ List<String> _getCitySpecificTerms(String location) {
|
|||||||
'Tokyo Skytree',
|
'Tokyo Skytree',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return specific;
|
return specific;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user