From edd6ffc1c328a067c8812f16dc62b043ef7b4967 Mon Sep 17 00:00:00 2001 From: Quentin Rouiller Date: Fri, 17 Apr 2026 18:00:00 +0200 Subject: [PATCH] =?UTF-8?q?Version=203.3.0=20=E2=80=94=20Corrections=20+?= =?UTF-8?q?=20raffinements=20(manifest.json=20corrig=C3=A9=20:=20=C3=A9tai?= =?UTF-8?q?t=20rest=C3=A9=20=C3=A0=203.2.0=20par=20oubli)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- viewer.js | 58 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/manifest.json b/manifest.json index 95600d1..56cb678 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Planning Techniciens — Vue claire", - "version": "3.2.0", + "version": "3.3.0", "description": "Vue claire du planning EasyVista (itsma.etat-de-vaud.ch et itsma.vd.ch) avec navigation par date, détection automatique des interventions closes et cache 7 jours.", "permissions": [ "activeTab", diff --git a/viewer.js b/viewer.js index 889b42b..9d2c718 100644 --- a/viewer.js +++ b/viewer.js @@ -450,12 +450,11 @@ async function loadForDate(isoDate, opts = {}) { }); console.log(`[load] 1er rendu (sans refs) à ${Math.round(performance.now() - t0)} ms`); - // 5. SÉQUENTIEL : xhr2 (lieu/contact de la bulle) EN PREMIER, - // puis fetches fiches (ref/statut/texte complet) APRÈS. - // Raison : le texte complet de la timeline (branche fiches) peut - // écraser bulleDescription/bulleContact/bulleLieu. Il ne faut pas - // que ça arrive AVANT que la bulle de base soit posée, sinon on se - // retrouve avec une popup vide ou incomplète pendant des secondes. + // 5. PARALLÈLE : xhr2 (lieu/contact) + fetches fiches (ref/statut) + // Avant v3.1.1 : séquentiel, on devait attendre les 34 xhr2 avant de + // lancer les 34 fiches. Résultat : première ref arrivait après ~1s. + // Maintenant : les deux démarrent en même temps, chacun met à jour + // la ligne correspondante via le rendu incrémental. const bulleNeeded = []; for (const tech of merged.techs) { for (const iv of tech.interventions) { @@ -476,38 +475,53 @@ async function loadForDate(isoDate, opts = {}) { ) ); - // Étape 5a : xhr2 d'abord (bulle de base : lieu/contact/texte court) + const promises = []; + if (bulleNeeded.length > 0 && !isRefreshAborted()) { const tBulles = performance.now(); console.log(`[load] fetch xhr2 pour ${bulleNeeded.length} interventions…`); - await fetchBullesForInterventions(bulleNeeded); - console.log(`[load] xhr2 finis en ${Math.round(performance.now() - tBulles)} ms`); - if (!isRefreshAborted()) { - renderFromData({ - techs: merged.techs, - targetDate: isoDate, - captureTime: Date.now(), - source: "fresh+bulles" - }); - } + promises.push( + fetchBullesForInterventions(bulleNeeded).then(() => { + console.log(`[load] xhr2 finis en ${Math.round(performance.now() - tBulles)} ms`); + if (!isRefreshAborted()) { + renderFromData({ + techs: merged.techs, + targetDate: isoDate, + captureTime: Date.now(), + source: "fresh+bulles" + }); + } + }) + ); } - // Étape 5b : fiches APRÈS les xhr2 (ref/statut + texte complet de timeline) if ((opts.doStatusRefresh || needFetch) && !isRefreshAborted()) { const tFiches = performance.now(); const nFiches = merged.techs.flatMap(t=>t.interventions).filter(i=>i.type==="AL-Intervention").length; console.log(`[load] début fetch des ${nFiches} fiches…`); - await refreshStatuses(merged.techs, isoDate); - console.log(`[load] fiches finies en ${Math.round(performance.now() - tFiches)} ms`); + promises.push( + refreshStatuses(merged.techs, isoDate).then(() => { + console.log(`[load] fiches finies en ${Math.round(performance.now() - tFiches)} ms`); + }) + ); } + // Race du Promise.all avec le signal d'annulation : dès que l'user clique + // Arrêter, loadForDate sort immédiatement (masque le bouton, fait un toast) + // sans attendre que les 15 workers en cours finissent leurs fetches. + // Les fetches continuent en arrière-plan mais le token a changé donc ils + // ne peuvent plus écrire le cache ni rafraîchir le DOM. + const abortPromise = makeAbortPromise(myToken); + const allDone = Promise.all(promises).then(() => "done"); + const raceResult = await Promise.race([allDone, abortPromise]); + // 6. Sauvegarder dans le cache (une seule fois, après que tout soit enrichi) // Uniquement si on est allé au bout (pas d'annulation). - if (!isRefreshAborted()) { + if (raceResult === "done" && !isRefreshAborted()) { await writeCache(isoDate, { techs: merged.techs }); } - if (!isRefreshAborted()) { + if (raceResult === "done" && !isRefreshAborted()) { showRefreshDone(); console.log(`[load] TOTAL: ${Math.round(performance.now() - t0)} ms`);