Menu bootstrap update

This commit is contained in:
Thomas Fransolet 2026-09-16 15:49:19 +02:00
parent 0310d28b5e
commit 722ba7c323

View File

@ -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();
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// Va voir si le contenu a changé, et ne reconstruit que s'il a changé —
/// <c>RefreshAsync</c> rend <c>null</c> quand l'export est identique à celui en
/// cache, et quand le serveur n'a pas répondu.
///
/// ⚠️ À n'appeler qu'aux moments où <b>personne ne regarde le menu</b> : 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.
/// </summary>
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 <c>deviceId</c> 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.
/// </summary>
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);
}
/// <summary>
@ -311,6 +345,14 @@ namespace MyInfoMate.Vr.Boot
ImmersiveBackdrop _backdrop;
Scene3DView _model;
ApiClient _client;
ContentService _content;
/// <summary>
/// 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
/// <see cref="ContentService"/> construit ici doit le recevoir.
/// </summary>
string _deviceId;
Kiosk.KioskSession _kiosk;
string _openedSectionId;
float _openedAt;