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
This commit is contained in:
dahoud
2026-01-10 10:43:17 +00:00
parent 06031b01f2
commit 92612abbd7
321 changed files with 43137 additions and 4285 deletions

View File

@@ -0,0 +1,244 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../../data/services/realtime_notification_service.dart';
/// Widget qui écoute les streams de notifications et affiche des toasts.
///
/// Ce widget doit wrapper l'application pour avoir accès au BuildContext
/// et pouvoir afficher les notifications visuelles (SnackBar, Dialog, etc.).
///
/// **Usage :**
/// ```dart
/// MaterialApp(
/// home: RealtimeNotificationHandler(
/// realtimeService: _realtimeService,
/// child: HomeScreen(),
/// ),
/// );
/// ```
class RealtimeNotificationHandler extends StatefulWidget {
const RealtimeNotificationHandler({
required this.child,
required this.realtimeService,
super.key,
});
final Widget child;
final RealtimeNotificationService realtimeService;
@override
State<RealtimeNotificationHandler> createState() => _RealtimeNotificationHandlerState();
}
class _RealtimeNotificationHandlerState extends State<RealtimeNotificationHandler> {
StreamSubscription<FriendRequestNotification>? _friendRequestSub;
StreamSubscription<SystemNotification>? _systemNotificationSub;
StreamSubscription<MessageAlert>? _messageAlertSub;
@override
void initState() {
super.initState();
_setupListeners();
}
void _setupListeners() {
// Écouter les demandes d'amitié
_friendRequestSub = widget.realtimeService.friendRequestStream.listen((notification) {
_showToast(notification.toDisplayMessage(), type: _getNotificationType(notification.type));
});
// Écouter les notifications système
_systemNotificationSub = widget.realtimeService.systemNotificationStream.listen((notification) {
_showSystemNotification(notification);
});
// Écouter les alertes de messages
_messageAlertSub = widget.realtimeService.messageAlertStream.listen((alert) {
_showToast('Nouveau message de ${alert.senderName}', type: NotificationType.info);
});
}
/// Détermine le type de notification pour le style du toast.
NotificationType _getNotificationType(String type) {
switch (type) {
case 'accepted':
return NotificationType.success;
case 'rejected':
return NotificationType.warning;
case 'received':
default:
return NotificationType.info;
}
}
/// Affiche un toast (SnackBar) avec le message.
void _showToast(String message, {NotificationType type = NotificationType.info}) {
if (!mounted) return;
final color = _getColorForType(type);
final icon = _getIconForType(type);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(icon, color: Colors.white, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
message,
style: const TextStyle(color: Colors.white),
),
),
],
),
backgroundColor: color,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 4),
action: SnackBarAction(
label: 'OK',
textColor: Colors.white,
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
),
);
}
/// Affiche une notification système plus élaborée.
void _showSystemNotification(SystemNotification notification) {
if (!mounted) return;
final color = _getColorForSystemNotificationType(notification.type);
final icon = _getIconForSystemNotificationType(notification.type);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: Colors.white, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
notification.title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
],
),
if (notification.message.isNotEmpty) ...[
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
notification.message,
style: const TextStyle(color: Colors.white70, fontSize: 12),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
],
),
backgroundColor: color,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 5),
action: SnackBarAction(
label: 'OK',
textColor: Colors.white,
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
),
);
}
/// Retourne la couleur appropriée selon le type de notification.
Color _getColorForType(NotificationType type) {
switch (type) {
case NotificationType.success:
return Colors.green.shade600;
case NotificationType.warning:
return Colors.orange.shade600;
case NotificationType.error:
return Colors.red.shade600;
case NotificationType.info:
default:
return Colors.blue.shade600;
}
}
/// Retourne l'icône appropriée selon le type de notification.
IconData _getIconForType(NotificationType type) {
switch (type) {
case NotificationType.success:
return Icons.check_circle;
case NotificationType.warning:
return Icons.warning;
case NotificationType.error:
return Icons.error;
case NotificationType.info:
default:
return Icons.info;
}
}
/// Retourne la couleur appropriée selon le type de notification système.
Color _getColorForSystemNotificationType(String type) {
switch (type.toLowerCase()) {
case 'event':
return Colors.blue.shade600;
case 'friend':
return Colors.green.shade600;
case 'reminder':
return Colors.orange.shade600;
default:
return Colors.grey.shade700;
}
}
/// Retourne l'icône appropriée selon le type de notification système.
IconData _getIconForSystemNotificationType(String type) {
switch (type.toLowerCase()) {
case 'event':
return Icons.event;
case 'friend':
return Icons.people;
case 'reminder':
return Icons.notifications;
default:
return Icons.info;
}
}
@override
void dispose() {
_friendRequestSub?.cancel();
_systemNotificationSub?.cancel();
_messageAlertSub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.child;
}
/// Enum pour les types de notifications toast.
enum NotificationType {
success,
warning,
error,
info,
}