diff --git a/unity/MyInfoMateVR/Assets/Scripts/Boot/MenuBootstrap.cs b/unity/MyInfoMateVR/Assets/Scripts/Boot/MenuBootstrap.cs
index 4561604..c4c0cc5 100644
--- a/unity/MyInfoMateVR/Assets/Scripts/Boot/MenuBootstrap.cs
+++ b/unity/MyInfoMateVR/Assets/Scripts/Boot/MenuBootstrap.cs
@@ -77,9 +77,10 @@ namespace MyInfoMate.Vr.Boot
_client = new ApiClient(pairing.BaseUrl, pairing.ApiKey);
var client = _client;
- var content = new ContentService(client, pairing.ConfigurationId, pairing.DeviceId);
+ _deviceId = pairing.DeviceId;
+ _content = new ContentService(client, pairing.ConfigurationId, _deviceId);
- var loaded = await content.LoadAsync();
+ var loaded = await _content.LoadAsync();
if (!loaded.Ok)
{
Show(loaded.Error);
@@ -95,27 +96,50 @@ namespace MyInfoMate.Vr.Boot
if (fleet != null) fleet.ConfigurationChanged += OnConfigurationChanged;
- BuildMenu(loaded.Export);
- await _menu.LoadImagesAsync(loaded.Export, client);
- await ApplyBackdrop(loaded.Export, client);
-
- // E4 — le reste de la visite part sur le disque pendant que le visiteur
- // regarde le menu. Sans await : c'est long, et personne ne doit l'attendre.
- _ = ContentPreloader.RunAsync(loaded.Export, client);
+ await ApplyAsync(loaded.Export);
if (!loaded.FromCache) return;
// Le menu est déjà utilisable : le rafraîchissement ne fait attendre personne,
// et ne se voit que s'il apporte quelque chose.
- var fresh = await content.RefreshAsync();
+ await RefreshContentAsync();
+ }
+
+ ///
+ /// Pose un contenu : le menu, ses vignettes, le décor, puis le téléchargement du
+ /// reste. Les trois chemins qui changent de contenu — démarrage, rafraîchissement,
+ /// changement de configuration — passent tous par ici, sinon l'un des quatre gestes
+ /// finit par manquer à l'un d'eux.
+ ///
+ async Task ApplyAsync(ConfigurationExport export)
+ {
+ BuildMenu(export);
+ await _menu.LoadImagesAsync(export, _client);
+ await ApplyBackdrop(export, _client);
+
+ // E4 — le reste de la visite part sur le disque pendant que le visiteur
+ // regarde le menu. Sans await : c'est long, et personne ne doit l'attendre.
+ _ = ContentPreloader.RunAsync(export, _client);
+ }
+
+ ///
+ /// Va voir si le contenu a changé, et ne reconstruit que s'il a changé —
+ /// RefreshAsync rend null quand l'export est identique à celui en
+ /// cache, et quand le serveur n'a pas répondu.
+ ///
+ /// ⚠️ À n'appeler qu'aux moments où personne ne regarde le menu : au
+ /// démarrage, et entre deux visites. Un menu qui se reconstruit sous les yeux d'un
+ /// visiteur rejoue la cascade d'apparition et déplace ce qu'il visait.
+ ///
+ async Task RefreshContentAsync()
+ {
+ if (_content == null) return;
+
+ var fresh = await _content.RefreshAsync();
if (fresh == null || _menu == null) return;
Debug.Log("[Menu] Contenu mis à jour, le menu se reconstruit.");
- BuildMenu(fresh);
- await _menu.LoadImagesAsync(fresh, client);
- await ApplyBackdrop(fresh, client);
-
- _ = ContentPreloader.RunAsync(fresh, client);
+ await ApplyAsync(fresh);
}
void BuildMenu(ConfigurationExport export)
@@ -145,6 +169,12 @@ namespace MyInfoMate.Vr.Boot
_menu?.Recenter();
_telemetry?.StartNewSession();
Debug.Log("[Kiosk] Nouvelle visite.");
+
+ // Le seul moment où reconstruire le menu ne dérange personne. Sans ça, un
+ // casque allumé toute la journée montre le contenu qu'il avait au démarrage :
+ // le battement de FleetReporter ne voit qu'un changement de configuration,
+ // pas une modification de son contenu.
+ _ = RefreshContentAsync();
}
async void OnSectionChosen(string sectionId)
@@ -225,13 +255,18 @@ namespace MyInfoMate.Vr.Boot
/// ⚠️ On ferme ce qui est ouvert d'abord. Un visiteur qui regardait une galerie
/// au moment du changement doit revenir au menu — pas rester dans un contenu qui
/// n'appartient plus à sa visite.
+ ///
+ /// ⚠️ Le deviceId repart avec : c'est lui qui désigne le lien de ce casque,
+ /// donc son agencement — bento, ordre des sections, sections masquées pour le seul
+ /// canal VR. Sans lui, le casque bascule sur la configuration nue et perd sa mise
+ /// en page jusqu'au prochain démarrage.
///
async void OnConfigurationChanged(string configurationId)
{
CloseSection();
- var content = new ContentService(_client, configurationId);
- var loaded = await content.LoadAsync();
+ _content = new ContentService(_client, configurationId, _deviceId);
+ var loaded = await _content.LoadAsync();
if (!loaded.Ok)
{
@@ -239,14 +274,13 @@ namespace MyInfoMate.Vr.Boot
return;
}
- BuildMenu(loaded.Export);
+ // Avant l'application : recentrer ne dépend que du menu déjà en place, et
+ // attendre le chargement des vignettes le laisserait le temps d'une requête
+ // dans le dos du visiteur.
_menu?.Recenter();
_telemetry?.StartNewSession();
- await _menu.LoadImagesAsync(loaded.Export, _client);
- await ApplyBackdrop(loaded.Export, _client);
-
- _ = ContentPreloader.RunAsync(loaded.Export, _client);
+ await ApplyAsync(loaded.Export);
}
///
@@ -311,6 +345,14 @@ namespace MyInfoMate.Vr.Boot
ImmersiveBackdrop _backdrop;
Scene3DView _model;
ApiClient _client;
+ ContentService _content;
+
+ ///
+ /// Ce casque, gardé pour toute la vie de l'app : il désigne le lien de
+ /// configuration, donc l'agencement propre au canal VR. Tout
+ /// construit ici doit le recevoir.
+ ///
+ string _deviceId;
Kiosk.KioskSession _kiosk;
string _openedSectionId;
float _openedAt;