refactor: Amélioration des performances et migration des Cloud Functions
Cette mise à jour majeure vise à améliorer significativement les performances de l'application, en particulier au démarrage, et à standardiser l'infrastructure backend. Les principaux changements incluent la migration de toutes les Cloud Functions vers une région européenne (`europe-west9`), l'optimisation du chargement des données, et l'introduction d'un moniteur de performance pour le débogage.
**Changements Backend (Cloud Functions) :**
- **Migration de la Région :**
- Toutes les Cloud Functions ont été déplacées de `us-central1` à `europe-west9` (Paris) pour réduire la latence pour les utilisateurs européens. Cela concerne les appels depuis le frontend (ex: `api_config.dart`, `email_service.dart`) et les définitions des fonctions elles-mêmes (`index.js`, etc.).
- **Standardisation des Fonctions :**
- La plupart des fonctions `onCall` (v1) ont été migrées vers le format `onRequest` (v2) avec une gestion d'authentification et de CORS unifiée, améliorant la robustesse et la cohérence.
- Les triggers Firestore (`onDocumentCreated`, `onDocumentUpdated`) et les tâches planifiées (`onSchedule`) ont été mis à jour pour spécifier explicitement la région `europe-west9`.
- **Mise à jour des Index Firestore :**
- Les index `firestore.indexes.json` ont été mis à jour pour supporter les nouvelles requêtes de l'application et optimiser les performances de filtrage.
**Améliorations des Performances Frontend :**
- **Chargement Asynchrone et Mis en Cache :**
- Le chargement des données utilisateur (`LocalUserProvider`) et des événements (`EventProvider`) a été optimisé pour utiliser un cache local à court terme (5 minutes pour l'utilisateur, 30 secondes pour les événements).
- Les données ne sont rechargées que si le cache a expiré ou si un rechargement est forcé, évitant des appels réseau redondants et accélérant la navigation.
- **Démarrage de l'Application Optimisé :**
- Le processus de connexion automatique (`main.dart`) a été revu. L'application navigue désormais immédiatement vers la page demandée sans attendre la fin du chargement des données utilisateur, qui s'effectue en arrière-plan.
- Un écran de chargement plus esthétique avec le logo de l'entreprise a été ajouté, remplaçant l'indicateur de chargement simple.
- **Chargement de la Page Calendrier :**
- Le chargement et la sélection de l'événement par défaut sur la page `CalendarPage` sont maintenant entièrement asynchrones, rendant l'affichage de la page quasi instantané.
**Nouveaux Outils et Améliorations UX :**
- **Moniteur de Performance :**
- Ajout d'un nouvel outil `PerformanceMonitor` (`lib/utils/performance_monitor.dart`) pour mesurer précisément le temps d'exécution des opérations critiques (appels API, parsing, etc.) en mode débogage. Il aide à identifier les goulots d'étranglement.
- **Amélioration du Formulaire de Connexion :**
- Les champs "Email" et "Mot de passe" sur la page de connexion (`LoginPage`) supportent désormais l'autocomplétion du navigateur (`AutofillGroup`).
- Appuyer sur "Entrée" dans l'un des champs déclenche désormais la connexion, améliorant l'ergonomie.
**Mise à jour de la version :**
- La version de l'application a été incrémentée à `1.0.9`.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:em2rp/providers/local_user_provider.dart';
|
||||
import 'package:em2rp/providers/event_provider.dart';
|
||||
import 'package:em2rp/utils/performance_monitor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:em2rp/views/widgets/nav/custom_app_bar.dart';
|
||||
import 'package:em2rp/views/widgets/nav/main_drawer.dart';
|
||||
@@ -35,52 +36,75 @@ class _CalendarPageState extends State<CalendarPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeDateFormatting('fr_FR', null);
|
||||
Future.microtask(() => _loadEvents());
|
||||
// Sélection automatique de l'événement le plus proche de maintenant
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final eventProvider = Provider.of<EventProvider>(context, listen: false);
|
||||
final events = eventProvider.events;
|
||||
if (events.isNotEmpty) {
|
||||
final now = DateTime.now();
|
||||
// Pour mobile : sélectionner le premier événement du jour ou le prochain événement à venir
|
||||
final todayEvents = events
|
||||
.where((e) =>
|
||||
e.startDateTime.year == now.year &&
|
||||
e.startDateTime.month == now.month &&
|
||||
e.startDateTime.day == now.day)
|
||||
.toList()
|
||||
..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
EventModel? selected;
|
||||
DateTime? selectedDay;
|
||||
if (todayEvents.isNotEmpty) {
|
||||
selected = todayEvents[0];
|
||||
selectedDay = DateTime(now.year, now.month, now.day);
|
||||
} else {
|
||||
// Chercher le prochain événement à venir
|
||||
final futureEvents = events
|
||||
.where((e) => e.startDateTime.isAfter(now))
|
||||
.toList()
|
||||
..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
if (futureEvents.isNotEmpty) {
|
||||
selected = futureEvents[0];
|
||||
selectedDay = DateTime(selected.startDateTime.year,
|
||||
selected.startDateTime.month, selected.startDateTime.day);
|
||||
} else {
|
||||
// Aucun événement à venir, prendre le plus proche dans le passé
|
||||
events.sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
selected = events.last;
|
||||
selectedDay = DateTime(selected.startDateTime.year,
|
||||
selected.startDateTime.month, selected.startDateTime.day);
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = selectedDay!;
|
||||
_selectedEventIndex = 0;
|
||||
_selectedEvent = selected;
|
||||
});
|
||||
// Charger les événements de manière asynchrone sans bloquer l'UI
|
||||
_loadEventsAsync();
|
||||
}
|
||||
|
||||
/// Charge les événements de manière asynchrone et sélectionne l'événement approprié
|
||||
Future<void> _loadEventsAsync() async {
|
||||
PerformanceMonitor.start('CalendarPage.loadEventsAsync');
|
||||
await _loadEvents();
|
||||
|
||||
// Sélectionner l'événement approprié après le chargement
|
||||
if (mounted) {
|
||||
PerformanceMonitor.start('CalendarPage.selectDefaultEvent');
|
||||
_selectDefaultEvent();
|
||||
PerformanceMonitor.end('CalendarPage.selectDefaultEvent');
|
||||
}
|
||||
PerformanceMonitor.end('CalendarPage.loadEventsAsync');
|
||||
}
|
||||
|
||||
/// Sélectionne automatiquement l'événement le plus proche de maintenant
|
||||
void _selectDefaultEvent() {
|
||||
final eventProvider = Provider.of<EventProvider>(context, listen: false);
|
||||
final events = eventProvider.events;
|
||||
|
||||
if (events.isEmpty) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
// Trouver les événements d'aujourd'hui
|
||||
final todayEvents = events.where((e) {
|
||||
final start = e.startDateTime;
|
||||
return start.year == now.year &&
|
||||
start.month == now.month &&
|
||||
start.day == now.day;
|
||||
}).toList()..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
|
||||
EventModel? selected;
|
||||
DateTime? selectedDay;
|
||||
|
||||
if (todayEvents.isNotEmpty) {
|
||||
selected = todayEvents[0];
|
||||
selectedDay = DateTime(now.year, now.month, now.day);
|
||||
} else {
|
||||
// Chercher le prochain événement à venir
|
||||
final futureEvents = events
|
||||
.where((e) => e.startDateTime.isAfter(now))
|
||||
.toList()..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
|
||||
if (futureEvents.isNotEmpty) {
|
||||
selected = futureEvents[0];
|
||||
final start = selected.startDateTime;
|
||||
selectedDay = DateTime(start.year, start.month, start.day);
|
||||
} else {
|
||||
// Aucun événement à venir, prendre le plus récent
|
||||
final sortedEvents = events.toList()
|
||||
..sort((a, b) => b.startDateTime.compareTo(a.startDateTime));
|
||||
selected = sortedEvents.first;
|
||||
final start = selected.startDateTime;
|
||||
selectedDay = DateTime(start.year, start.month, start.day);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = selectedDay!;
|
||||
_selectedEventIndex = 0;
|
||||
_selectedEvent = selected;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadEvents() async {
|
||||
|
||||
Reference in New Issue
Block a user