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:
619
lib/presentation/widgets/social/README.md
Normal file
619
lib/presentation/widgets/social/README.md
Normal file
@@ -0,0 +1,619 @@
|
||||
# Widgets Sociaux - Architecture Modulaire
|
||||
|
||||
Architecture de composants réutilisables pour les posts sociaux avec support d'images et vidéos.
|
||||
|
||||
## 📐 Architecture
|
||||
|
||||
### 🔹 Widgets Atomiques (Niveau 1)
|
||||
|
||||
Les plus petits composants réutilisables dans toute l'application.
|
||||
|
||||
#### `SocialActionButton`
|
||||
**Bouton d'action tout petit et uniforme**
|
||||
- Taille par défaut: 22px
|
||||
- Supporte tooltip
|
||||
- Animation scale: 0.85
|
||||
- Couleur personnalisable
|
||||
|
||||
```dart
|
||||
SocialActionButton(
|
||||
icon: Icons.favorite_rounded,
|
||||
onTap: () => handleLike(),
|
||||
color: Colors.red,
|
||||
tooltip: 'J\'aime',
|
||||
)
|
||||
```
|
||||
|
||||
#### `SocialActionButtonWithCount`
|
||||
**Bouton d'action avec compteur**
|
||||
- Affichage du nombre (1K, 1M formaté)
|
||||
- Support état actif/inactif
|
||||
- Couleur différente si actif
|
||||
|
||||
```dart
|
||||
SocialActionButtonWithCount(
|
||||
icon: Icons.favorite_rounded,
|
||||
count: 245,
|
||||
onTap: () => handleLike(),
|
||||
isActive: true,
|
||||
activeColor: Colors.red,
|
||||
)
|
||||
```
|
||||
|
||||
#### `SocialBadge`
|
||||
**Badge réutilisable générique**
|
||||
- Icône optionnelle
|
||||
- Couleurs personnalisables
|
||||
- Taille de police ajustable
|
||||
- Padding personnalisable
|
||||
|
||||
```dart
|
||||
SocialBadge(
|
||||
label: 'Nouveau',
|
||||
icon: Icons.fiber_new,
|
||||
fontSize: 11,
|
||||
)
|
||||
```
|
||||
|
||||
#### `VerifiedBadge`
|
||||
**Badge de compte vérifié**
|
||||
- Icône verified avec tooltip
|
||||
- Taille personnalisable
|
||||
|
||||
```dart
|
||||
VerifiedBadge(size: 16)
|
||||
```
|
||||
|
||||
#### `CategoryBadge`
|
||||
**Badge de catégorie**
|
||||
- Couleur secondaire
|
||||
- Icône optionnelle
|
||||
|
||||
```dart
|
||||
CategoryBadge(
|
||||
category: 'Sport',
|
||||
icon: Icons.sports_soccer,
|
||||
)
|
||||
```
|
||||
|
||||
#### `StatusBadge`
|
||||
**Badge de statut dynamique**
|
||||
- Couleurs automatiques selon le statut
|
||||
- "nouveau" → primaryContainer
|
||||
- "tendance" → errorContainer
|
||||
- "populaire" → tertiaryContainer
|
||||
|
||||
```dart
|
||||
StatusBadge(
|
||||
status: 'tendance',
|
||||
icon: Icons.trending_up,
|
||||
)
|
||||
```
|
||||
|
||||
#### `MediaCountBadge`
|
||||
**Badge de nombre de médias**
|
||||
- Pour images/vidéos
|
||||
- Affichage sur fond noir semi-transparent
|
||||
- Icône différente selon le type
|
||||
|
||||
```dart
|
||||
MediaCountBadge(
|
||||
count: 5,
|
||||
isVideo: false,
|
||||
)
|
||||
```
|
||||
|
||||
### 🔹 Widgets de Médias (Niveau 2)
|
||||
|
||||
Composants spécialisés pour la gestion des médias.
|
||||
|
||||
#### `PostMedia` (Model)
|
||||
**Modèle de données pour un média**
|
||||
```dart
|
||||
class PostMedia {
|
||||
final String url;
|
||||
final MediaType type; // image ou video
|
||||
final String? thumbnailUrl;
|
||||
final Duration? duration;
|
||||
}
|
||||
```
|
||||
|
||||
#### `PostMediaViewer`
|
||||
**Affichage de médias avec dispositions multiples**
|
||||
- 1 média: Aspect ratio 1:1
|
||||
- 2 médias: Côte à côte
|
||||
- 3 médias: 1 grand + 2 petits
|
||||
- 4+ médias: Grille 2x2 avec compteur "+N"
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Hero animation
|
||||
- Loading progressif
|
||||
- Gestion d'erreurs
|
||||
- Tap pour plein écran
|
||||
- Badge de durée pour vidéos
|
||||
|
||||
```dart
|
||||
PostMediaViewer(
|
||||
medias: [
|
||||
PostMedia(url: 'image1.jpg', type: MediaType.image),
|
||||
PostMedia(url: 'video1.mp4', type: MediaType.video, duration: Duration(minutes: 2, seconds: 30)),
|
||||
],
|
||||
postId: post.id,
|
||||
onTap: () => handleMediaTap(),
|
||||
)
|
||||
```
|
||||
|
||||
#### `MediaPicker`
|
||||
**Sélecteur de médias pour création de post**
|
||||
- Sélection depuis galerie (multi)
|
||||
- Capture photo depuis caméra
|
||||
- Sélection vidéo
|
||||
- Limite: 10 médias max (personnalisable)
|
||||
- Prévisualisation avec bouton supprimer
|
||||
- Indicateur vidéo sur thumbnails
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Compteur médias sélectionnés
|
||||
- Boutons désactivés si limite atteinte
|
||||
- Grille horizontale scrollable
|
||||
- Suppression individuelle
|
||||
|
||||
```dart
|
||||
MediaPicker(
|
||||
onMediasChanged: (medias) {
|
||||
setState(() => selectedMedias = medias);
|
||||
},
|
||||
maxMedias: 10,
|
||||
initialMedias: [],
|
||||
)
|
||||
```
|
||||
|
||||
### 🔹 Dialogues et Composants Complexes (Niveau 3)
|
||||
|
||||
#### `CreatePostDialog`
|
||||
**Dialogue de création de post avec médias**
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Avatar et nom utilisateur
|
||||
- Champ texte (3-8 lignes, max 500 caractères)
|
||||
- MediaPicker intégré
|
||||
- Validation formulaire
|
||||
- État de chargement pendant création
|
||||
- Compteur caractères
|
||||
- Visibilité (Public par défaut)
|
||||
- **Compression automatique** des images avant upload
|
||||
- **Upload progressif** avec indicateur de progression
|
||||
- **Nettoyage automatique** des fichiers temporaires
|
||||
|
||||
**Processus d'upload en 3 étapes:**
|
||||
1. Compression des images (0-50%)
|
||||
2. Upload des médias (50-100%)
|
||||
3. Création du post
|
||||
|
||||
**Présentation:**
|
||||
- Modal bottom sheet
|
||||
- Auto-focus sur le champ texte
|
||||
- Padding adapté au clavier
|
||||
- Actions: Annuler / Publier
|
||||
- Barre de progression avec statut textuel
|
||||
|
||||
```dart
|
||||
await CreatePostDialog.show(
|
||||
context: context,
|
||||
onPostCreated: (content, medias) async {
|
||||
await createPost(content, medias);
|
||||
},
|
||||
userName: 'Jean Dupont',
|
||||
userAvatarUrl: 'avatar.jpg',
|
||||
);
|
||||
```
|
||||
|
||||
#### `EditPostDialog`
|
||||
**Dialogue d'édition de post existant**
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Pré-remplissage avec contenu existant
|
||||
- Détection automatique des changements
|
||||
- MediaPicker pour modifier les médias
|
||||
- Validation formulaire
|
||||
- Bouton "Enregistrer" désactivé si aucun changement
|
||||
- État de chargement pendant mise à jour
|
||||
|
||||
**Présentation:**
|
||||
- Modal bottom sheet
|
||||
- Auto-focus sur le champ texte
|
||||
- Icône d'édition dans l'en-tête
|
||||
- Actions: Annuler / Enregistrer
|
||||
|
||||
```dart
|
||||
await EditPostDialog.show(
|
||||
context: context,
|
||||
post: existingPost,
|
||||
onPostUpdated: (content, medias) async {
|
||||
await updatePost(existingPost.id, content, medias);
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
#### `FullscreenVideoPlayer`
|
||||
**Lecteur vidéo plein écran avec contrôles**
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Lecture automatique au démarrage
|
||||
- Orientation paysage forcée
|
||||
- Mode immersif (barre système cachée)
|
||||
- Contrôles tactiles :
|
||||
- Tap pour afficher/masquer contrôles
|
||||
- Play/Pause
|
||||
- Reculer de 10s
|
||||
- Avancer de 10s
|
||||
- Barre de progression interactive (scrubbing)
|
||||
- Affichage durée actuelle / totale
|
||||
- Hero animation pour transition fluide
|
||||
|
||||
**Présentation:**
|
||||
- Fond noir
|
||||
- Header avec bouton fermer et titre
|
||||
- Contrôles centraux (reculer/play/avancer)
|
||||
- Footer avec barre de progression
|
||||
- Gradient overlay sur header/footer
|
||||
|
||||
```dart
|
||||
await FullscreenVideoPlayer.show(
|
||||
context: context,
|
||||
videoUrl: 'https://example.com/video.mp4',
|
||||
heroTag: 'post_media_123_0',
|
||||
title: 'Ma vidéo',
|
||||
);
|
||||
```
|
||||
|
||||
#### `SocialCardRefactored`
|
||||
**Card modulaire de post social**
|
||||
|
||||
**Composants internes:**
|
||||
- `_PostHeader`: Avatar gradient, nom, badge vérifié, timestamp, menu
|
||||
- `_UserAvatar`: Avatar avec bordure gradient
|
||||
- `_PostTimestamp`: Formatage relatif (Il y a X min/h/j)
|
||||
- `_PostMenu`: PopupMenu (Modifier, Supprimer)
|
||||
- `_PostActions`: Like, Comment, Share, Bookmark
|
||||
- `_PostStats`: Nombre de likes
|
||||
- `_PostContent`: Texte enrichi (hashtags #, mentions @)
|
||||
- `_CommentsLink`: Lien vers commentaires
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Support médias via PostMediaViewer
|
||||
- Hashtags et mentions cliquables (couleur primaire)
|
||||
- "Voir plus/Voir moins" pour contenu long (>150 caractères)
|
||||
- Badge de catégorie optionnel
|
||||
- Badge vérifié optionnel
|
||||
- AnimatedCard avec elevation
|
||||
|
||||
```dart
|
||||
SocialCardRefactored(
|
||||
post: post,
|
||||
onLike: () => handleLike(),
|
||||
onComment: () => handleComment(),
|
||||
onShare: () => handleShare(),
|
||||
onDeletePost: () => handleDelete(),
|
||||
onEditPost: () => handleEdit(),
|
||||
showVerifiedBadge: true,
|
||||
showCategory: true,
|
||||
category: 'Sport',
|
||||
)
|
||||
```
|
||||
|
||||
## 🔧 Services (lib/data/services/)
|
||||
|
||||
### `ImageCompressionService`
|
||||
**Service de compression d'images avant upload**
|
||||
|
||||
**Configurations prédéfinies:**
|
||||
- `CompressionConfig.post` : 85% qualité, 1920x1920px
|
||||
- `CompressionConfig.thumbnail` : 70% qualité, 400x400px
|
||||
- `CompressionConfig.story` : 90% qualité, 1080x1920px
|
||||
- `CompressionConfig.avatar` : 80% qualité, 500x500px
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Compression avec qualité ajustable (0-100)
|
||||
- Redimensionnement automatique
|
||||
- Support formats: JPEG, PNG, WebP
|
||||
- Compression parallèle pour plusieurs images
|
||||
- Callback de progression
|
||||
- Statistiques de réduction de taille
|
||||
- Nettoyage automatique des fichiers temporaires
|
||||
|
||||
**Utilisation:**
|
||||
|
||||
```dart
|
||||
final compressionService = ImageCompressionService();
|
||||
|
||||
// Compression d'une image
|
||||
final compressed = await compressionService.compressImage(
|
||||
imageFile,
|
||||
config: CompressionConfig.post,
|
||||
);
|
||||
|
||||
// Compression multiple avec progression
|
||||
final compressedList = await compressionService.compressMultipleImages(
|
||||
imageFiles,
|
||||
config: CompressionConfig.thumbnail,
|
||||
onProgress: (processed, total) {
|
||||
print('Compression: $processed/$total');
|
||||
},
|
||||
);
|
||||
|
||||
// Créer un thumbnail
|
||||
final thumbnail = await compressionService.createThumbnail(imageFile);
|
||||
|
||||
// Nettoyer les fichiers temporaires
|
||||
await compressionService.cleanupTempFiles();
|
||||
```
|
||||
|
||||
**Logs (si EnvConfig.enableDetailedLogs = true):**
|
||||
```
|
||||
[ImageCompression] Compression de: photo.jpg
|
||||
[ImageCompression] Taille originale: 4.2 MB
|
||||
[ImageCompression] Taille compressée: 1.8 MB
|
||||
[ImageCompression] Réduction: 57.1%
|
||||
```
|
||||
|
||||
### `MediaUploadService`
|
||||
**Service d'upload de médias vers le backend**
|
||||
|
||||
**Fonctionnalités:**
|
||||
- Upload d'images et vidéos
|
||||
- Upload parallèle de plusieurs médias
|
||||
- Callback de progression
|
||||
- Génération automatique de thumbnails pour vidéos
|
||||
- Support vidéos locales et réseau
|
||||
- Suppression de médias
|
||||
|
||||
**Modèle de résultat:**
|
||||
```dart
|
||||
class MediaUploadResult {
|
||||
final String url; // URL du média uploadé
|
||||
final String? thumbnailUrl; // URL du thumbnail (vidéo)
|
||||
final String type; // 'image' ou 'video'
|
||||
final Duration? duration; // Durée (vidéo seulement)
|
||||
}
|
||||
```
|
||||
|
||||
**Utilisation:**
|
||||
|
||||
```dart
|
||||
final uploadService = MediaUploadService(http.Client());
|
||||
|
||||
// Upload d'un média
|
||||
final result = await uploadService.uploadMedia(imageFile);
|
||||
print('URL: ${result.url}');
|
||||
|
||||
// Upload multiple avec progression
|
||||
final results = await uploadService.uploadMultipleMedias(
|
||||
mediaFiles,
|
||||
onProgress: (uploaded, total) {
|
||||
print('Upload: $uploaded/$total');
|
||||
},
|
||||
);
|
||||
|
||||
// Générer thumbnail pour vidéo
|
||||
final thumbnailUrl = await uploadService.generateVideoThumbnail(videoUrl);
|
||||
|
||||
// Supprimer un média
|
||||
await uploadService.deleteMedia(mediaUrl);
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
Le endpoint d'upload est configuré dans `EnvConfig.mediaUploadEndpoint`.
|
||||
|
||||
## 📦 Utilisation
|
||||
|
||||
### Import Simple
|
||||
```dart
|
||||
import 'package:afterwork/presentation/widgets/social/social_widgets.dart';
|
||||
```
|
||||
|
||||
Ceci importe tous les widgets nécessaires :
|
||||
- Boutons d'action
|
||||
- Badges
|
||||
- Media picker
|
||||
- Post media viewer
|
||||
- Create post dialog
|
||||
- Social card
|
||||
|
||||
### Exemple Complet
|
||||
|
||||
```dart
|
||||
// 1. Créer un post avec compression et upload automatiques
|
||||
await CreatePostDialog.show(
|
||||
context: context,
|
||||
onPostCreated: (content, medias) async {
|
||||
// Les médias sont déjà compressés et uploadés par le dialogue
|
||||
await apiService.createPost(
|
||||
content: content,
|
||||
mediaFiles: medias,
|
||||
);
|
||||
},
|
||||
userName: currentUser.name,
|
||||
userAvatarUrl: currentUser.avatar,
|
||||
);
|
||||
|
||||
// 2. Créer un post manuellement avec services
|
||||
final compressionService = ImageCompressionService();
|
||||
final uploadService = MediaUploadService(http.Client());
|
||||
|
||||
// Compresser les images
|
||||
final compressedMedias = await compressionService.compressMultipleImages(
|
||||
selectedMedias,
|
||||
config: CompressionConfig.post,
|
||||
onProgress: (processed, total) {
|
||||
print('Compression: $processed/$total');
|
||||
},
|
||||
);
|
||||
|
||||
// Uploader les médias
|
||||
final uploadResults = await uploadService.uploadMultipleMedias(
|
||||
compressedMedias,
|
||||
onProgress: (uploaded, total) {
|
||||
print('Upload: $uploaded/$total');
|
||||
},
|
||||
);
|
||||
|
||||
// Créer le post avec les URLs
|
||||
await apiService.createPost(
|
||||
content: contentController.text,
|
||||
mediaUrls: uploadResults.map((r) => r.url).toList(),
|
||||
);
|
||||
|
||||
// Nettoyer les fichiers temporaires
|
||||
await compressionService.cleanupTempFiles();
|
||||
|
||||
// 3. Afficher un post avec médias
|
||||
SocialCardRefactored(
|
||||
post: SocialPost(
|
||||
id: '123',
|
||||
content: 'Super soirée avec les amis ! #afterwork @john',
|
||||
imageUrl: '', // Géré par medias maintenant
|
||||
likesCount: 42,
|
||||
commentsCount: 5,
|
||||
sharesCount: 2,
|
||||
isLikedByCurrentUser: false,
|
||||
// ...
|
||||
),
|
||||
medias: [
|
||||
PostMedia(
|
||||
url: 'https://example.com/photo1.jpg',
|
||||
type: MediaType.image,
|
||||
),
|
||||
PostMedia(
|
||||
url: 'https://example.com/video1.mp4',
|
||||
type: MediaType.video,
|
||||
thumbnailUrl: 'https://example.com/video1_thumb.jpg',
|
||||
duration: Duration(minutes: 2, seconds: 30),
|
||||
),
|
||||
],
|
||||
onLike: () {
|
||||
setState(() {
|
||||
post = post.copyWith(
|
||||
isLikedByCurrentUser: !post.isLikedByCurrentUser,
|
||||
likesCount: post.isLikedByCurrentUser
|
||||
? post.likesCount - 1
|
||||
: post.likesCount + 1,
|
||||
);
|
||||
});
|
||||
},
|
||||
onComment: () {
|
||||
Navigator.push(/* CommentsScreen */);
|
||||
},
|
||||
onEditPost: () async {
|
||||
await EditPostDialog.show(
|
||||
context: context,
|
||||
post: post,
|
||||
onPostUpdated: (content, medias) async {
|
||||
await apiService.updatePost(post.id, content, medias);
|
||||
},
|
||||
);
|
||||
},
|
||||
onDeletePost: () async {
|
||||
await apiService.deletePost(post.id);
|
||||
},
|
||||
showVerifiedBadge: user.isVerified,
|
||||
showCategory: true,
|
||||
category: post.category,
|
||||
);
|
||||
|
||||
// 4. Afficher une vidéo en plein écran
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
FullscreenVideoPlayer.show(
|
||||
context: context,
|
||||
videoUrl: 'https://example.com/video.mp4',
|
||||
heroTag: 'post_media_${post.id}_0',
|
||||
title: 'Ma vidéo',
|
||||
);
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Image.network(videoThumbnail),
|
||||
Center(
|
||||
child: Icon(Icons.play_circle_outline, size: 64),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
## 🎨 Principes de Design
|
||||
|
||||
### Uniformité
|
||||
- Tous les boutons icônes: 20-22px
|
||||
- Tous les badges: fontSize 10-11px
|
||||
- Spacing: DesignSystem constants (4, 8, 12, 16px)
|
||||
- Border radius: DesignSystem (4, 10, 16px)
|
||||
|
||||
### Réutilisabilité
|
||||
- Chaque composant fait UNE chose
|
||||
- Props claires et typées
|
||||
- Valeurs par défaut sensées
|
||||
- Documentation inline
|
||||
|
||||
### Performances
|
||||
- Widgets const quand possible
|
||||
- Keys pour listes
|
||||
- Lazy loading des images
|
||||
- AnimatedCard pour hover effects
|
||||
|
||||
### Accessibilité
|
||||
- Tooltips sur tous les boutons
|
||||
- Couleurs avec contraste suffisant
|
||||
- Tailles tactiles minimales respectées (44x44)
|
||||
|
||||
## 🔧 Personnalisation
|
||||
|
||||
### Thème
|
||||
Tous les widgets utilisent le ThemeData du contexte :
|
||||
- `theme.colorScheme.primary`
|
||||
- `theme.colorScheme.onSurface`
|
||||
- `theme.textTheme.bodyMedium`
|
||||
|
||||
### DesignSystem
|
||||
Les constantes sont centralisées :
|
||||
- `DesignSystem.spacingSm` (8px)
|
||||
- `DesignSystem.spacingMd` (12px)
|
||||
- `DesignSystem.spacingLg` (16px)
|
||||
- `DesignSystem.radiusSm` (4px)
|
||||
- `DesignSystem.radiusMd` (10px)
|
||||
- `DesignSystem.radiusLg` (16px)
|
||||
|
||||
## 📝 Statut des Fonctionnalités
|
||||
|
||||
### ✅ Complété
|
||||
|
||||
- [x] **Support de plusieurs médias par post** - PostMediaViewer supporte 1-10+ médias avec dispositions adaptatives
|
||||
- [x] **Upload des médias vers le backend** - MediaUploadService avec progression et support image/vidéo
|
||||
- [x] **Lecteur vidéo en plein écran** - FullscreenVideoPlayer avec contrôles complets
|
||||
- [x] **Édition de post avec médias** - EditPostDialog avec détection de changements
|
||||
- [x] **Compression d'images avant upload** - ImageCompressionService avec 4 configs prédéfinies
|
||||
- [x] **Architecture modulaire** - Widgets atomiques, réutilisables et bien documentés
|
||||
- [x] **Animations et interactions** - Like, bookmark, hero transitions, scale effects
|
||||
- [x] **Hashtags et mentions cliquables** - Parsing et styling automatiques
|
||||
- [x] **Progress tracking** - Indicateurs de progression pour compression et upload
|
||||
|
||||
### 🚧 À Venir
|
||||
|
||||
- [ ] **Stories avec médias** - Système de stories éphémères (24h) avec images/vidéos
|
||||
- [ ] **Filtres sur les photos** - Filtres Instagram-style pour édition d'images
|
||||
- [ ] **GIF support** - Support des GIFs animés dans les posts
|
||||
- [ ] **Commentaires imbriqués** - Système de réponses aux commentaires
|
||||
- [ ] **Réactions étendues** - Plus de réactions au-delà du simple like (❤️ 😂 😮 😢 😡)
|
||||
- [ ] **Partage vers stories** - Partager un post dans sa story
|
||||
- [ ] **Brouillons** - Sauvegarder des posts en brouillon
|
||||
- [ ] **Planification** - Programmer la publication d'un post
|
||||
|
||||
### 📊 Métriques
|
||||
|
||||
- **13 widgets** créés (atomiques + composites)
|
||||
- **2 services** (compression + upload)
|
||||
- **5 types de badges** réutilisables
|
||||
- **4 layouts** pour affichage médias (1, 2, 3, 4+)
|
||||
- **3 étapes** de processus d'upload (compression, upload, création)
|
||||
- **10 médias max** par post (configurable)
|
||||
Reference in New Issue
Block a user