Files
afterwork/lib/core/utils/validators.dart
dahoud 92612abbd7 fix(chat): Correction race condition + Implémentation TODOs
## Corrections Critiques

### Race Condition - Statuts de Messages
- Fix : Les icônes de statut (✓, ✓✓, ✓✓ bleu) ne s'affichaient pas
- Cause : WebSocket delivery confirmations arrivaient avant messages locaux
- Solution : Pattern Optimistic UI dans chat_bloc.dart
  - Création message temporaire immédiate
  - Ajout à la liste AVANT requête HTTP
  - Remplacement par message serveur à la réponse
- Fichier : lib/presentation/state_management/chat_bloc.dart

## Implémentation TODOs (13/21)

### Social (social_header_widget.dart)
-  Copier lien du post dans presse-papiers
-  Partage natif via Share.share()
-  Dialogue de signalement avec 5 raisons

### Partage (share_post_dialog.dart)
-  Interface sélection d'amis avec checkboxes
-  Partage externe via Share API

### Média (media_upload_service.dart)
-  Parsing JSON réponse backend
-  Méthode deleteMedia() pour suppression
-  Génération miniature vidéo

### Posts (create_post_dialog.dart, edit_post_dialog.dart)
-  Extraction URL depuis uploads
-  Documentation chargement médias

### Chat (conversations_screen.dart)
-  Navigation vers notifications
-  ConversationSearchDelegate pour recherche

## Nouveaux Fichiers

### Configuration
- build-prod.ps1 : Script build production avec dart-define
- lib/core/constants/env_config.dart : Gestion environnements

### Documentation
- TODOS_IMPLEMENTED.md : Documentation complète TODOs

## Améliorations

### Architecture
- Refactoring injection de dépendances
- Amélioration routing et navigation
- Optimisation providers (UserProvider, FriendsProvider)

### UI/UX
- Amélioration thème et couleurs
- Optimisation animations
- Meilleure gestion erreurs

### Services
- Configuration API avec env_config
- Amélioration datasources (events, users)
- Optimisation modèles de données
2026-01-10 10:43:17 +00:00

244 lines
6.9 KiB
Dart

/// Classe utilitaire pour la validation des champs de formulaire.
///
/// Cette classe fournit des méthodes statiques pour valider différents
/// types de données d'entrée utilisateur.
///
/// **Usage:**
/// ```dart
/// final emailError = Validators.validateEmail(emailController.text);
/// if (emailError != null) {
/// // Afficher l'erreur
/// }
/// ```
class Validators {
/// Constructeur privé pour empêcher l'instanciation
Validators._();
/// Expression régulière pour valider les emails
static final RegExp _emailRegex = RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
);
/// Expression régulière pour valider les mots de passe forts
/// (au moins 8 caractères, une majuscule, une minuscule, un chiffre)
static final RegExp _strongPasswordRegex = RegExp(
r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$',
);
/// Valide une adresse email.
///
/// [value] La valeur à valider
///
/// Returns `null` si l'email est valide, sinon un message d'erreur.
///
/// **Exemple:**
/// ```dart
/// final error = Validators.validateEmail('user@example.com');
/// // Retourne null si valide
/// ```
static String? validateEmail(String? value) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez entrer votre email';
}
final trimmedValue = value.trim();
if (!_emailRegex.hasMatch(trimmedValue)) {
return 'Veuillez entrer un email valide';
}
// Validation supplémentaire de la longueur
if (trimmedValue.length > 254) {
return 'L\'email est trop long (maximum 254 caractères)';
}
return null;
}
/// Valide un mot de passe.
///
/// [value] La valeur à valider
/// [minLength] Longueur minimale requise (par défaut: 6)
/// [requireStrong] Si true, exige un mot de passe fort (par défaut: false)
///
/// Returns `null` si le mot de passe est valide, sinon un message d'erreur.
///
/// **Exemple:**
/// ```dart
/// final error = Validators.validatePassword('password123', minLength: 8);
/// // Retourne null si valide
/// ```
static String? validatePassword(
String? value, {
int minLength = 6,
bool requireStrong = false,
}) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer votre mot de passe';
}
if (value.length < minLength) {
return 'Le mot de passe doit comporter au moins $minLength caractères';
}
if (requireStrong && !_strongPasswordRegex.hasMatch(value)) {
return 'Le mot de passe doit contenir au moins une majuscule, '
'une minuscule et un chiffre';
}
// Validation de la longueur maximale
if (value.length > 128) {
return 'Le mot de passe est trop long (maximum 128 caractères)';
}
return null;
}
/// Valide que deux mots de passe correspondent.
///
/// [password] Le premier mot de passe
/// [confirmPassword] Le mot de passe de confirmation
///
/// Returns `null` si les mots de passe correspondent, sinon un message d'erreur.
///
/// **Exemple:**
/// ```dart
/// final error = Validators.validatePasswordMatch(
/// 'password123',
/// 'password123',
/// );
/// // Retourne null si correspond
/// ```
static String? validatePasswordMatch(String? password, String? confirmPassword) {
if (password == null || password.isEmpty) {
return 'Veuillez entrer votre mot de passe';
}
if (confirmPassword == null || confirmPassword.isEmpty) {
return 'Veuillez confirmer votre mot de passe';
}
if (password != confirmPassword) {
return 'Les mots de passe ne correspondent pas';
}
return null;
}
/// Valide un nom (prénom ou nom de famille).
///
/// [value] La valeur à valider
/// [fieldName] Le nom du champ (pour le message d'erreur)
///
/// Returns `null` si le nom est valide, sinon un message d'erreur.
static String? validateName(String? value, {String fieldName = 'ce champ'}) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez entrer $fieldName';
}
final trimmedValue = value.trim();
if (trimmedValue.length < 2) {
return '$fieldName doit contenir au moins 2 caractères';
}
if (trimmedValue.length > 100) {
return '$fieldName est trop long (maximum 100 caractères)';
}
// Validation des caractères (lettres, espaces, tirets, apostrophes)
if (!RegExp(r"^[a-zA-ZÀ-ÿ\s\-']+$").hasMatch(trimmedValue)) {
return '$fieldName ne doit contenir que des lettres';
}
return null;
}
/// Valide un numéro de téléphone.
///
/// [value] La valeur à valider
///
/// Returns `null` si le numéro est valide, sinon un message d'erreur.
static String? validatePhoneNumber(String? value) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez entrer votre numéro de téléphone';
}
final trimmedValue = value.trim().replaceAll(RegExp(r'[\s\-\(\)]'), '');
// Validation du format (10 chiffres pour la France)
if (!RegExp(r'^\+?[0-9]{10,15}$').hasMatch(trimmedValue)) {
return 'Veuillez entrer un numéro de téléphone valide';
}
return null;
}
/// Valide une URL.
///
/// [value] La valeur à valider
///
/// Returns `null` si l'URL est valide, sinon un message d'erreur.
static String? validateUrl(String? value) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez entrer une URL';
}
final trimmedValue = value.trim();
try {
final uri = Uri.parse(trimmedValue);
if (!uri.hasScheme || !uri.hasAuthority) {
return 'Veuillez entrer une URL valide';
}
return null;
} catch (e) {
return 'Veuillez entrer une URL valide';
}
}
/// Valide qu'un champ n'est pas vide.
///
/// [value] La valeur à valider
/// [fieldName] Le nom du champ (pour le message d'erreur)
///
/// Returns `null` si le champ n'est pas vide, sinon un message d'erreur.
static String? validateRequired(String? value, {String fieldName = 'ce champ'}) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez remplir $fieldName';
}
return null;
}
/// Valide la longueur d'une chaîne.
///
/// [value] La valeur à valider
/// [minLength] Longueur minimale
/// [maxLength] Longueur maximale
/// [fieldName] Le nom du champ (pour le message d'erreur)
///
/// Returns `null` si la longueur est valide, sinon un message d'erreur.
static String? validateLength(
String? value, {
int? minLength,
int? maxLength,
String fieldName = 'ce champ',
}) {
if (value == null) {
return 'Veuillez remplir $fieldName';
}
final length = value.length;
if (minLength != null && length < minLength) {
return '$fieldName doit contenir au moins $minLength caractères';
}
if (maxLength != null && length > maxLength) {
return '$fieldName ne doit pas dépasser $maxLength caractères';
}
return null;
}
}