From c927771795d3ebffceadfbfffa3aa4bd949d2480 Mon Sep 17 00:00:00 2001 From: bach Date: Tue, 8 Sep 2026 14:22:39 +0200 Subject: [PATCH] Corrige un deadlock dans onFilterChanged() qui gelait le tableau MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensureScrollable() (appelé par onFilterChanged() après chaque changement de filtre) appelle lui-même loadOlder()/loadNewer(), qui passent par le même _queueWindowOp -- en le chaînant *à l'intérieur* de l'opération déjà mise en file par onFilterChanged(), la file d'attente se retrouvait à attendre sa propre continuation dès qu'un filtre laissait trop peu de lignes pour remplir l'écran, gelant purement et simplement le tableau (recherche qui ne charge plus les lignes précédentes en scrollant, et même effacer le filtre ensuite ne faisait plus rien -- tout attendait derrière l'opération bloquée). Corrigé en chaînant ensureScrollable() après la résolution de l'opération mise en file, pas dedans -- ses propres appels à loadOlder()/loadNewer() s'empilent alors normalement sur la file, sans dépendance circulaire. Reproduit et vérifié en conditions réelles : recherche "Assurance local" (47 correspondances de 2023 à 2026) qui chargeait bien 2026 mais bloquait en scrollant vers le haut -- après correctif, chaque scroll vers le haut déclenche bien un nouveau chargement (vérifié sur 2 scrolls successifs), et effacer le filtre recharge immédiatement la vue complète. --- .../custom/figli_compta_ledger/js/home.js | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/web/modules/custom/figli_compta_ledger/js/home.js b/web/modules/custom/figli_compta_ledger/js/home.js index 9de192b..e27e3b3 100644 --- a/web/modules/custom/figli_compta_ledger/js/home.js +++ b/web/modules/custom/figli_compta_ledger/js/home.js @@ -1455,14 +1455,24 @@ // Every toolbar filter change routes through here: with filtering // now server-side (see fetchFilteredLignes()), there's no more // "just recompute a client-side view" -- the currently loaded - // window has to be re-fetched with the new filter applied. Queued - // through the same chain as loadOlder()/loadNewer() (see - // _queueWindowOp) since several filters can change in the same - // tick (e.g. mounted() restoring them all from the URL hash at - // once), and interleaving their fetches would race on `rows` the - // same way parallel loadOlder()/loadNewer() calls used to. + // window has to be re-fetched with the new filter applied. The + // reload itself is queued through the same chain as loadOlder()/ + // loadNewer() (see _queueWindowOp) since several filters can change + // in the same tick (e.g. mounted() restoring them all from the URL + // hash at once), and interleaving their fetches would race on + // `rows` the same way parallel loadOlder()/loadNewer() calls used + // to. ensureScrollable() is deliberately chained AFTER that queued + // op settles, not passed into it: ensureScrollable() itself calls + // loadOlder()/loadNewer(), which each enqueue their own op onto the + // very same chain -- queuing it *inside* the op currently occupying + // that chain made the chain await its own continuation (the queued + // op can't finish until its child call, appended behind it on the + // same chain, finishes first) and deadlocked solid the moment a + // filter actually left too few rows to fill the viewport, wedging + // every future filter change and scroll-triggered load right along + // with it. onFilterChanged() { - this._queueWindowOp(() => this.reloadWindow().then(() => this.ensureScrollable())); + this._queueWindowOp(() => this.reloadWindow()).then(() => this.ensureScrollable()); this.syncHash(); }, // Keeps extending the window (both directions) as long as a filter