Files
EM2_ERP/em2rp/lib/services/email_service.dart
T
ElPoyo af5ecaeee1 feat: optimisation du démarrage de l'application et de la gestion de l'authentification
- **Refonte du démarrage** : Mise en place d'un `AppInitializer` pour gérer l'initialisation asynchrone de Firebase et du cache en arrière-plan, réduisant le travail synchrone au lancement.
- **Sécurisation de l'authentification** :
    - Création d'un `AppStartGate` pour gérer proprement la restauration de la session Firebase Auth et les erreurs potentielles sur le Web.
    - Amélioration du `LocalUserProvider` avec un "bootstrap léger" permettant de rendre l'UID disponible immédiatement avant le chargement complet du profil.
    - Ajout de protections contre les erreurs d'accès à `FirebaseAuth.instance` (notamment pour les problèmes d'interop JS sur le Web).
- **Optimisation de l'UI** :
    - Remplacement du `AutoLoginWrapper` par une gestion plus robuste de la navigation post-authentification.
    - Amélioration de l'`AuthGuard` pour permettre l'affichage de certains écrans (comme le calendrier) pendant le chargement des données utilisateur (`allowWhileLoading`).
    - Ajout d'un écran de splash screen uniformisé (`StartupSplashScreen`).
- **Services & Cache** :
    - Introduction de `CacheService` utilisant `shared_preferences` pour le stockage local léger.
    - Refactoring des services (`AlertService`, `EmailService`, `FirebaseStorageManager`) pour accéder aux instances Firebase de manière plus flexible via des getters.
    - Mise à jour des dépendances dans `pubspec.yaml` pour inclure `shared_preferences`.
- **Calendrier** : Ajout d'une logique de chargement initial différé des événements (`_scheduleInitialEventsLoad`) pour éviter les appels redondants au démarrage.
- **Maintenance** : Mise à jour de la version de l'application à `1.1.23` et nettoyage des fichiers de cache de déploiement.
2026-05-05 12:25:45 +02:00

150 lines
4.6 KiB
Dart

import 'package:cloud_functions/cloud_functions.dart';
import 'package:em2rp/models/alert_model.dart';
import 'package:em2rp/utils/debug_log.dart';
import 'package:firebase_auth/firebase_auth.dart';
/// Service d'envoi d'emails via Cloud Functions
class EmailService {
FirebaseFunctions get _functions => FirebaseFunctions.instanceFor(region: 'europe-west9');
/// Envoie un email d'alerte à un utilisateur
///
/// [alert] : L'alerte à envoyer
/// [userId] : ID de l'utilisateur destinataire
/// [templateType] : Type de template à utiliser (par défaut: 'alert-individual')
Future<bool> sendAlertEmail({
required AlertModel alert,
required String userId,
String templateType = 'alert-individual',
}) async {
try {
// Vérifier que l'utilisateur est authentifié
final currentUser = FirebaseAuth.instance.currentUser;
if (currentUser == null) {
DebugLog.error('[EmailService] Utilisateur non authentifié');
return false;
}
DebugLog.info('[EmailService] Envoi email alerte ${alert.id} à $userId');
final result = await _functions.httpsCallable('sendAlertEmail').call({
'alertId': alert.id,
'userId': userId,
'templateType': templateType,
});
final data = result.data as Map<String, dynamic>;
final success = data['success'] as bool? ?? false;
final skipped = data['skipped'] as bool? ?? false;
if (skipped) {
final reason = data['reason'] as String? ?? 'unknown';
DebugLog.info('[EmailService] Email non envoyé: $reason');
return false;
}
if (success) {
DebugLog.info('[EmailService] Email envoyé avec succès');
return true;
}
return false;
} catch (e) {
DebugLog.error('[EmailService] Erreur envoi email', e);
return false;
}
}
/// Envoie un email d'alerte à plusieurs utilisateurs
///
/// [alert] : L'alerte à envoyer
/// [userIds] : Liste des IDs des utilisateurs destinataires
Future<Map<String, bool>> sendAlertEmailToMultipleUsers({
required AlertModel alert,
required List<String> userIds,
String templateType = 'alert-individual',
}) async {
final results = <String, bool>{};
DebugLog.info('[EmailService] Envoi emails à ${userIds.length} utilisateurs');
// Envoyer en parallèle (max 5 à la fois pour éviter surcharge)
final batches = <List<String>>[];
for (var i = 0; i < userIds.length; i += 5) {
batches.add(userIds.sublist(
i,
i + 5 > userIds.length ? userIds.length : i + 5,
));
}
for (final batch in batches) {
final futures = batch.map((userId) => sendAlertEmail(
alert: alert,
userId: userId,
templateType: templateType,
));
final batchResults = await Future.wait(futures);
for (var i = 0; i < batch.length; i++) {
results[batch[i]] = batchResults[i];
}
}
final successCount = results.values.where((v) => v).length;
DebugLog.info('[EmailService] $successCount/${ userIds.length} emails envoyés');
return results;
}
/// Détermine si une alerte doit être envoyée immédiatement ou en digest
///
/// [alert] : L'alerte à vérifier
/// Returns: true si immédiat, false si digest
bool shouldSendImmediate(AlertModel alert) {
// Les alertes critiques sont envoyées immédiatement
if (alert.severity == AlertSeverity.critical) {
return true;
}
// Types d'alertes toujours immédiates
const immediateTypes = [
AlertType.lost, // Équipement perdu
AlertType.eventCancelled, // Événement annulé
];
return immediateTypes.contains(alert.type);
}
/// Envoie un email d'alerte en tenant compte des préférences
///
/// [alert] : L'alerte à envoyer
/// [userIds] : Liste des IDs des utilisateurs destinataires
Future<void> sendAlertWithPreferences({
required AlertModel alert,
required List<String> userIds,
}) async {
if (userIds.isEmpty) {
DebugLog.warning('[EmailService] Aucun utilisateur à notifier');
return;
}
final immediate = shouldSendImmediate(alert);
if (immediate) {
DebugLog.info('[EmailService] Envoi immédiat (alerte critique)');
await sendAlertEmailToMultipleUsers(
alert: alert,
userIds: userIds,
templateType: 'alert-individual',
);
} else {
DebugLog.info('[EmailService] Ajout au digest (alerte non critique)');
// Les alertes non critiques seront envoyées dans le digest quotidien
// La Cloud Function sendDailyDigest s'en occupera
// Rien à faire ici, les alertes sont déjà dans Firestore
}
}
}