Table des matières

Synchronisation offline

SmartCommon fournit un module de synchronisation complet pour gérer le fonctionnement hors-ligne des applications PWA.

Architecture

Le module sync est composé de :

Élément Type Description
useSyncClient Hook React Interface principale pour la synchronisation
SyncEngine Classe Moteur de synchronisation (push/pull/conflits)
SyncStorage Classe Couche IndexedDB pour le stockage local (Dexie)
SyncApi Classe Client HTTP avec auth JWT et retry automatique
ConflictResolver Composant React Interface de résolution de conflits

useSyncClient

Voir Hooks - useSyncClient pour la documentation complète du hook.

Flux de synchronisation

Push (local vers serveur)

1. L'utilisateur modifie des données localement (create/update/remove)
2. Les changements sont stockés dans IndexedDB (pending_changes)
3. Au prochain sync.push(), les changements sont envoyés au serveur
4. Le serveur confirme ou signale des conflits
5. Les temp_id locaux sont remplacés par les ID serveur

Pull (serveur vers local)

1. sync.pull() demande les changements depuis lastSyncTime
2. Le serveur renvoie les entités modifiées
3. Les entités locales sont mises à jour
4. En cas de conflit (modification locale + serveur), un conflit est créé

Gestion des conflits

Quand une entité est modifiée localement et sur le serveur, un conflit est créé.

Résolution programmatique

const conflicts = await sync.getConflicts();

for (const conflict of conflicts) {
  // Garder la version client
  await sync.resolveConflict(conflict.conflict_id, 'client');

  // Garder la version serveur
  await sync.resolveConflict(conflict.conflict_id, 'server');

  // Fusionner manuellement
  await sync.resolveConflict(conflict.conflict_id, {
    ...conflict.server_data,
    label: conflict.client_data.label  // garder le label local
  });
}

Résolution avec ConflictResolver

import { ConflictResolver } from '@cap-rel/smartcommon';

const MyConflictPage = () => {
  const sync = useSyncClient({ /* ... */ });
  const [conflicts, setConflicts] = useState([]);

  useEffect(() => {
    sync.getConflicts().then(setConflicts);
  }, []);

  if (conflicts.length === 0) return null;

  return (
    <ConflictResolver
      conflicts={conflicts}
      onResolve={async (conflictId, resolution) => {
        await sync.resolveConflict(conflictId, resolution);
        setConflicts(prev => prev.filter(c => c.conflict_id !== conflictId));
      }}
      onCancel={() => setConflicts([])}
    />
  );
};

Le composant ConflictResolver affiche :

Props de ConflictResolver

Prop Type Description
conflicts array Tableau de conflits (conflictid, table, objectid, clientdata, serverdata, field_conflicts)
onResolve function Appelée quand un conflit est résolu
onCancel function Appelée pour fermer le résolveur
renderField function Rendu personnalisé d'un champ (optionnel)
labels object Libellés de l'interface (optionnel, français par défaut)

Schéma IndexedDB

Le module sync utilise les stores suivants :

Store Description
entities Données synchronisées
pendingchanges | Changements locaux en attente de push | | pendingconflicts Conflits non résolus
syncmeta | Métadonnées de synchronisation (lastSyncTime, etc.) | | localtombstones Entités supprimées localement

Voir aussi