Files
geo/app/docs/TODO-MAP-CACHE-WEB.md
pierre 2f5946a184 feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD)
  * DEV: Clés TEST Pierre (mode test)
  * REC: Clés TEST Client (mode test)
  * PROD: Clés LIVE Client (mode live)
- Ajout de la gestion des bases de données immeubles/bâtiments
  * Configuration buildings_database pour DEV/REC/PROD
  * Service BuildingService pour enrichissement des adresses
- Optimisations pages et améliorations ergonomie
- Mises à jour des dépendances Composer
- Nettoyage des fichiers obsolètes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 18:26:27 +01:00

12 KiB

TODO - Cache Web pour tuiles Mapbox

Contexte

Problème : En PROD, les admins travaillent intensivement sur la plateforme web et la carte. Actuellement, les tuiles Mapbox ne sont pas mises en cache sur le web, ce qui provoque :

  • Téléchargements répétés des mêmes tuiles
  • Consommation excessive de bande passante
  • Navigation peu fluide
  • Coûts API Mapbox potentiellement élevés

Cause : Mapbox envoie des headers Cache-Control: no-cache qui empêchent le cache navigateur par défaut, et flutter_map_cache ne supporte pas la plateforme web.

Solution : Implémenter un cache manuel des tuiles via IndexedDB en Dart, uniquement pour le web.

Architecture technique

Flux de données

flutter_map demande une tuile
    ↓
CachedWebTileProvider (nouveau)
    ↓
WebTileCacheService.getTile()
    ↓
Vérifie IndexedDB
    ↓
┌─────────────┴─────────────┐
│                           │
Cache HIT                Cache MISS
│                           │
Retourne image            Télécharge depuis Mapbox
depuis IndexedDB              ↓
                          Stocke dans IndexedDB
                              ↓
                          Retourne image

Composants

  1. WebTileCacheService (Singleton)

    • Gère IndexedDB pour stocker les tuiles
    • Méthodes : getTile(), storeTile(), clearCache(), clearExpiredTiles()
    • Durée de cache : 30 jours (aligné avec mobile/desktop)
  2. CachedWebTileProvider (Custom TileProvider)

    • Implémente TileProvider de flutter_map
    • Utilise WebTileCacheService pour récupérer/stocker
    • Fallback sur téléchargement direct si erreur
  3. MapboxMap (Modifié)

    • Utilise CachedWebTileProvider sur web
    • Garde HiveCacheStore sur mobile/desktop

Fichiers à créer/modifier

Fichiers à créer

1. lib/core/services/web_tile_cache_service.dart

Service singleton pour gérer le cache IndexedDB des tuiles Mapbox.

Responsabilités :

  • Initialiser la base IndexedDB mapbox_tiles_cache
  • Stocker les tuiles avec clé unique (URL de la tuile)
  • Récupérer les tuiles depuis le cache
  • Gérer l'expiration (30 jours)
  • Nettoyer les tuiles expirées

Structure IndexedDB :

Database: mapbox_tiles_cache
ObjectStore: tiles
  - key: String (URL de la tuile)
  - value: Object {
      data: Uint8List (bytes de l'image)
      timestamp: int (millisecondsSinceEpoch)
    }

API publique :

class WebTileCacheService {
  static final instance = WebTileCacheService._();

  Future<void> initialize();
  Future<Uint8List?> getTile(String url);
  Future<void> storeTile(String url, Uint8List data);
  Future<void> clearCache();
  Future<void> clearExpiredTiles();
}

Dépendances :

  • dart:indexed_db (natif web)
  • dart:typed_data
  • package:flutter/foundation.dart (kIsWeb)

2. lib/presentation/widgets/cached_web_tile_provider.dart

TileProvider custom qui utilise le cache IndexedDB.

Responsabilités :

  • Implémenter TileProvider de flutter_map
  • Vérifier le cache avant téléchargement
  • Télécharger et stocker si absent du cache
  • Fallback sur comportement par défaut si erreur

API publique :

class CachedWebTileProvider extends TileProvider {
  final String accessToken;
  final String styleId;

  CachedWebTileProvider({
    required this.accessToken,
    required this.styleId,
  });

  @override
  ImageProvider getImage(TileCoordinates coordinates, TileLayer options);
}

Logique :

  1. Construire l'URL de la tuile
  2. Appeler WebTileCacheService.getTile(url)
  3. Si trouvée → MemoryImage(cachedData)
  4. Sinon → Télécharger → Stocker → MemoryImage(downloadedData)

Dépendances :

  • flutter_map
  • WebTileCacheService
  • dart:typed_data
  • package:http/http.dart (ou Dio si préféré)

Fichiers à modifier

3. lib/presentation/widgets/mapbox_map.dart

Modifier pour utiliser le nouveau provider sur web.

Modifications :

  • Lignes 96-139 : Refactoriser _initializeCache()
  • Utiliser CachedWebTileProvider quand kIsWeb == true
  • Garder HiveCacheStore pour mobile/desktop
  • Initialiser WebTileCacheService au démarrage sur web

Avant (lignes 98-103) :

if (kIsWeb) {
  // Pas de cache sur Web (non supporté)
  setState(() {
    _cacheInitialized = true;
  });
  return;
}

Après :

if (kIsWeb) {
  // Cache manuel via IndexedDB
  await WebTileCacheService.instance.initialize();
  setState(() {
    _cacheInitialized = true;
  });
  return;
}

TileLayer config (ligne ~180) :

TileLayer(
  urlTemplate: 'https://api.mapbox.com/styles/v1/mapbox/$styleId/tiles/256/{z}/{x}/{y}@2x?access_token=$accessToken',
  tileProvider: kIsWeb
      ? CachedWebTileProvider(
          accessToken: accessToken,
          styleId: styleId,
        )
      : (_cacheStore != null
          ? CachedTileProvider(store: _cacheStore!)
          : NetworkTileProvider()),
  // ...
),

Étapes d'implémentation

Phase 1 : Service de cache IndexedDB

Tâche 1.1 : Créer web_tile_cache_service.dart

  • Créer la classe singleton
  • Implémenter initialize() avec création de la DB IndexedDB
  • Implémenter getTile(url) avec vérification d'expiration
  • Implémenter storeTile(url, data) avec timestamp
  • Implémenter clearCache() pour vider toute la DB
  • Implémenter clearExpiredTiles() pour le nettoyage automatique
  • Ajouter des logs de debug (debugPrint)

Tâche 1.2 : Gestion des erreurs

  • Try/catch sur toutes les opérations IndexedDB
  • Fallback gracieux si IndexedDB indisponible
  • Logs d'erreur explicites

Tâche 1.3 : Tests unitaires

  • Tester initialize() crée bien la DB
  • Tester storeTile() + getTile() roundtrip
  • Tester expiration (mock timestamp)
  • Tester clearCache()

Phase 2 : TileProvider custom

Tâche 2.1 : Créer cached_web_tile_provider.dart

  • Créer la classe qui extend TileProvider
  • Implémenter getImage() avec logique de cache
  • Gérer le téléchargement des tuiles manquantes
  • Stocker les tuiles téléchargées dans le cache
  • Ajouter des logs de performance (cache hit/miss)

Tâche 2.2 : Gestion des erreurs réseau

  • Try/catch sur téléchargement
  • Retry logic (3 tentatives max)
  • Placeholder si échec total
  • Logs d'erreur explicites

Tâche 2.3 : Tests

  • Tester avec tuile en cache → Pas de requête réseau
  • Tester avec tuile absente → Téléchargement + stockage
  • Tester avec erreur réseau → Fallback gracieux

Phase 3 : Intégration dans MapboxMap

Tâche 3.1 : Modifier mapbox_map.dart

  • Importer CachedWebTileProvider et WebTileCacheService
  • Modifier _initializeCache() pour initialiser le service web
  • Modifier TileLayer pour utiliser le bon provider selon la plateforme
  • Tester sur web que le nouveau provider est bien utilisé

Tâche 3.2 : Validation visuelle

  • Lancer l'app en mode web DEV
  • Ouvrir DevTools → Console pour voir les logs
  • Naviguer sur la carte → Vérifier logs "Cache MISS" au premier passage
  • Revenir sur la même zone → Vérifier logs "Cache HIT"
  • Vérifier dans DevTools → Application → IndexedDB que les tuiles sont stockées

Tâche 3.3 : Tests de performance

  • Mesurer le temps de chargement initial (sans cache)
  • Mesurer le temps de chargement avec cache
  • Vérifier qu'il n'y a plus de requêtes réseau pour les tuiles en cache
  • Tester sur une session longue (plusieurs déplacements sur la carte)

Phase 4 : Nettoyage et documentation

Tâche 4.1 : Nettoyage automatique

  • Appeler clearExpiredTiles() au démarrage de l'app (dans initialize())
  • Ajouter un bouton admin pour vider manuellement le cache (optionnel)

Tâche 4.2 : Logs et monitoring

  • Ajouter des statistiques de cache (hit rate, taille totale)
  • Logger les performances (temps de chargement moyen)

Tâche 4.3 : Documentation

  • Commenter le code (dartdoc)
  • Mettre à jour FLOW-*.md si nécessaire
  • Ajouter section dans README technique

Tests de validation

Tests fonctionnels

  1. Cache vide → Premier chargement

    • Ouvrir l'app web en mode incognito
    • Naviguer sur la carte
    • Vérifier que les tuiles se chargent
    • Vérifier les logs "Cache MISS" + "Storing tile"
  2. Cache rempli → Rechargement

    • Recharger la page (F5)
    • Revenir sur la même zone de carte
    • Vérifier que les tuiles se chargent instantanément
    • Vérifier les logs "Cache HIT"
    • Vérifier dans Network tab : pas de requêtes Mapbox pour les tuiles en cache
  3. Navigation étendue

    • Se déplacer sur plusieurs zones de la carte
    • Revenir sur les zones précédentes
    • Vérifier mix de "Cache HIT" et "Cache MISS"
  4. Gestion de l'expiration

    • Modifier temporairement l'expiration à 10 secondes (pour test)
    • Charger des tuiles
    • Attendre 15 secondes
    • Recharger → Vérifier que les tuiles sont re-téléchargées
  5. Gestion d'erreur réseau

    • Désactiver le réseau en plein chargement
    • Vérifier que les tuiles en cache s'affichent quand même
    • Vérifier que les tuiles manquantes ne bloquent pas l'app

Tests de performance

  1. Mesure de bande passante

    • Vider le cache
    • Naviguer sur une zone avec ~100 tuiles visibles
    • Noter la bande passante consommée (DevTools Network)
    • Recharger la page et revenir sur la même zone
    • Vérifier que la bande passante est ~0 KB
  2. Mesure de vitesse

    • Mesurer le temps de chargement initial : ____ ms
    • Mesurer le temps de chargement avec cache : ____ ms
    • Objectif : >50% de réduction

Tests multi-plateformes

  1. Mobile/Desktop non régressé

    • Lancer sur Android → Vérifier que HiveCacheStore fonctionne toujours
    • Lancer sur iOS → Vérifier que HiveCacheStore fonctionne toujours
    • Vérifier les logs "Cache initialized with HiveCacheStore"
  2. Web multi-navigateurs

    • Tester sur Chrome
    • Tester sur Firefox
    • Tester sur Safari (si IndexedDB supporté)
    • Tester sur Edge

Checklist de validation finale

  • Aucune nouvelle dépendance ajoutée au pubspec.yaml
  • Code compatible web uniquement (guards kIsWeb)
  • Mobile/Desktop non impactés (toujours HiveCacheStore)
  • Logs de debug présents et clairs
  • Gestion d'erreur complète (try/catch)
  • Expiration automatique (30 jours)
  • Performance mesurée et améliorée
  • Tests manuels passés
  • Code documenté (dartdoc)
  • Pas de régression sur les autres plateformes

Notes techniques

Pourquoi IndexedDB et pas localStorage ?

  • localStorage : Limite de 5-10 MB → Insuffisant pour des tuiles (1 tuile = ~60 KB)
  • IndexedDB : Limite de ~50% de l'espace disque disponible → Largement suffisant

Structure de la clé de cache

Format recommandé : {styleId}_{z}_{x}_{y}@{scale}x

Exemple : streets-v11_15_16234_11378@2x

Cela permet :

  • Identification unique de chaque tuile
  • Support multi-styles (streets, satellite, etc.)
  • Support multi-résolutions (@1x, @2x)

Taille estimée du cache

  • 1 tuile Mapbox @2x : ~60 KB
  • Carte complète niveau zoom 15 : ~500 tuiles
  • Total pour une zone : ~30 MB
  • IndexedDB peut stocker plusieurs GB → Pas de souci

Maintenance du cache

Le nettoyage automatique (clearExpiredTiles()) s'exécute :

  • Au démarrage de l'app
  • Évite l'accumulation de vieilles tuiles
  • Garde le cache sous contrôle

Dépendances

Packages Flutter (déjà présents)

  • flutter_map: ^7.0.2 → Fournit TileProvider
  • flutter_map_cache: ^1.5.1 → Pour mobile/desktop uniquement

Bibliothèques Dart natives (aucun ajout)

  • dart:indexed_db → Pour IndexedDB (web seulement)
  • dart:typed_data → Pour manipuler les bytes
  • package:flutter/foundation.dart → Pour kIsWeb

Pas de nouvelle dépendance externe

Estimation

  • Temps de développement : 2-3 heures
  • Complexité : Moyenne
  • Risque : Faible (fallback sur comportement actuel)
  • Gain : Important pour les admins PROD

Prochaine étape

Attente de validation utilisateur pour démarrer l'implémentation.