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,382 @@
import 'package:flutter/material.dart';
import '../constants/design_system.dart';
/// Transitions de page personnalisées pour une navigation fluide
///
/// Utilisez ces transitions au lieu de MaterialPageRoute standard
/// pour une meilleure expérience utilisateur.
///
/// **Types de transitions:**
/// - Fade: Fondu simple
/// - Slide: Glissement depuis une direction
/// - Scale: Zoom in/out
/// - Rotation: Rotation 3D
/// - SlideFromBottom: Bottom sheet style
/// Énumération des types de transitions
enum PageTransitionType {
fade,
slideRight,
slideLeft,
slideUp,
slideDown,
scale,
rotation,
fadeScale,
}
/// Route personnalisée avec transitions fluides
class CustomPageRoute<T> extends PageRoute<T> {
CustomPageRoute({
required this.builder,
this.transitionType = PageTransitionType.slideRight,
this.duration,
this.reverseDuration,
this.curve,
this.reverseCurve,
super.settings,
});
final WidgetBuilder builder;
final PageTransitionType transitionType;
final Duration? duration;
final Duration? reverseDuration;
final Curve? curve;
final Curve? reverseCurve;
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
@override
bool get maintainState => true;
@override
Duration get transitionDuration =>
duration ?? DesignSystem.durationMedium;
@override
Duration get reverseTransitionDuration =>
reverseDuration ?? DesignSystem.durationMedium;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return builder(context);
}
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
final effectiveCurve = curve ?? DesignSystem.curveDecelerate;
final effectiveReverseCurve = reverseCurve ?? DesignSystem.curveDecelerate;
final curvedAnimation = CurvedAnimation(
parent: animation,
curve: effectiveCurve,
reverseCurve: effectiveReverseCurve,
);
switch (transitionType) {
case PageTransitionType.fade:
return FadeTransition(
opacity: curvedAnimation,
child: child,
);
case PageTransitionType.slideRight:
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.slideLeft:
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.slideUp:
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.slideDown:
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, -1),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.scale:
return ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: child,
),
);
case PageTransitionType.rotation:
return RotationTransition(
turns: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: child,
),
);
case PageTransitionType.fadeScale:
return FadeTransition(
opacity: curvedAnimation,
child: ScaleTransition(
scale: Tween<double>(
begin: 0.95,
end: 1.0,
).animate(curvedAnimation),
child: child,
),
);
}
}
}
/// Extension pour faciliter la navigation avec transitions
extension NavigatorExtensions on BuildContext {
/// Navigate avec fade transition
Future<T?> pushFade<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.fade,
),
);
}
/// Navigate avec slide from right
Future<T?> pushSlideRight<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.slideRight,
),
);
}
/// Navigate avec slide from left
Future<T?> pushSlideLeft<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.slideLeft,
),
);
}
/// Navigate avec slide from bottom
Future<T?> pushSlideUp<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.slideUp,
curve: DesignSystem.curveSharp,
),
);
}
/// Navigate avec scale transition
Future<T?> pushScale<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.scale,
),
);
}
/// Navigate avec fade + scale (élégant)
Future<T?> pushFadeScale<T>(Widget page) {
return Navigator.of(this).push<T>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.fadeScale,
),
);
}
/// Navigate et remplace avec transition
Future<T?> pushReplacementFade<T, TO>(Widget page) {
return Navigator.of(this).pushReplacement<T, TO>(
CustomPageRoute(
builder: (_) => page,
transitionType: PageTransitionType.fade,
),
);
}
}
/// Transition de Hero personnalisée
///
/// Pour des animations de shared element plus fluides.
class CustomHeroTransition extends RectTween {
CustomHeroTransition({
required Rect? begin,
required Rect? end,
}) : super(begin: begin, end: end);
@override
Rect? lerp(double t) {
final elasticCurveValue = Curves.easeInOutCubic.transform(t);
return Rect.fromLTRB(
lerpDouble(begin!.left, end!.left, elasticCurveValue),
lerpDouble(begin!.top, end!.top, elasticCurveValue),
lerpDouble(begin!.right, end!.right, elasticCurveValue),
lerpDouble(begin!.bottom, end!.bottom, elasticCurveValue),
);
}
double lerpDouble(double a, double b, double t) {
return a + (b - a) * t;
}
}
/// Bottom sheet avec animation personnalisée
Future<T?> showCustomBottomSheet<T>({
required BuildContext context,
required Widget Function(BuildContext) builder,
bool isScrollControlled = true,
bool useRootNavigator = false,
bool isDismissible = true,
Color? backgroundColor,
double? elevation,
}) {
return showModalBottomSheet<T>(
context: context,
builder: builder,
isScrollControlled: isScrollControlled,
useRootNavigator: useRootNavigator,
isDismissible: isDismissible,
backgroundColor: backgroundColor ?? Colors.transparent,
elevation: elevation ?? 0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(DesignSystem.radiusXl),
),
),
transitionAnimationController: _createBottomSheetController(context),
);
}
AnimationController _createBottomSheetController(BuildContext context) {
return BottomSheet.createAnimationController(Navigator.of(context))
..duration = DesignSystem.durationMedium
..reverseDuration = DesignSystem.durationFast;
}
/// Dialog avec animation personnalisée
Future<T?> showCustomDialog<T>({
required BuildContext context,
required Widget child,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
}) {
return showGeneralDialog<T>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor ?? Colors.black54,
barrierLabel: barrierLabel,
transitionDuration: DesignSystem.durationMedium,
transitionBuilder: (context, animation, secondaryAnimation, child) {
return ScaleTransition(
scale: Tween<double>(
begin: 0.8,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: DesignSystem.curveDecelerate,
),
),
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
pageBuilder: (context, animation, secondaryAnimation) {
return child;
},
);
}
/// Shared Element Transition (Hero avec contrôle fin)
class SharedElementTransition extends StatelessWidget {
const SharedElementTransition({
required this.tag,
required this.child,
this.transitionOnUserGestures = false,
super.key,
});
final Object tag;
final Widget child;
final bool transitionOnUserGestures;
@override
Widget build(BuildContext context) {
return Hero(
tag: tag,
transitionOnUserGestures: transitionOnUserGestures,
flightShuttleBuilder: (
flightContext,
animation,
flightDirection,
fromHeroContext,
toHeroContext,
) {
final Hero toHero = toHeroContext.widget as Hero;
return ScaleTransition(
scale: Tween<double>(
begin: 0.95,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: DesignSystem.curveDecelerate,
),
),
child: toHero.child,
);
},
child: child,
);
}
}