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 :
- Comparaison côte à côte des données client et serveur
- Marqueurs sur les champs en conflit
- Trois options : garder client, garder serveur, fusionner champ par champ
- Navigation entre les conflits multiples
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 | Conflits non résolus |
syncmeta | Entités supprimées localement |
Voir aussi
- Hooks SmartCommon - Tous les hooks
- PWA - Configuration PWA
- Stockage de données - Stockage local