From 74f033f5d1686ddea2e72b82347cdc41b42ae00e Mon Sep 17 00:00:00 2001 From: "ext.jeremy.guillot@maxicoffee.domains" Date: Sun, 15 Mar 2026 17:46:00 +0100 Subject: [PATCH] perf(reader): windowing + eager loading sur l'InfiniteReader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Windowing côté rendu : seules les pages dans une fenêtre de ±3 autour de la page visible sont montées en tant que ReaderPage ; les autres sont remplacées par des placeholders dimensionnés via aspect-ratio CSS pour maintenir la hauteur de scroll sans saut - IntersectionObserver utilise le minimum des indices intersectants pour éviter que les entrées simultanées au chargement ne décalent la fenêtre - Prop initialPage passé depuis ChapterReader pour ancrer la fenêtre sur la page courante dès le montage - loading="eager" sur les ReaderPage montés (le windowing est le mécanisme de lazy-loading, pas l'attribut HTML natif) - Prop loading bindé sur les 3 balises de ReaderPage --- TASK.md | 41 ++++++++++ .../presentation/components/ChapterReader.vue | 1 + .../components/InfiniteReader.vue | 78 +++++++++++++------ .../presentation/components/ReaderPage.vue | 7 ++ 4 files changed, 102 insertions(+), 25 deletions(-) diff --git a/TASK.md b/TASK.md index a9bc2cb..d585dc9 100644 --- a/TASK.md +++ b/TASK.md @@ -75,6 +75,47 @@ --- +## [Perf] Reader — Lazy-loading des pages (InfiniteReader) + +**Problème :** `readerStore.js` charge toutes les pages avec `itemsPerPage=9999`. `InfiniteReader.vue` monte tous les composants `ReaderPage` simultanément dans le DOM. Sur un chapitre de 200 pages, cela représente 200 composants actifs et autant d'images pré-chargées. + +- [ ] Implémenter un `IntersectionObserver` sur les wrappers de page pour ne charger les images qu'au moment où elles entrent dans le viewport (`loading="lazy"` ou src conditionnel) +- [ ] Limiter le nombre de composants montés simultanément (virtualisation ou windowing) : ne rendre que les pages proches de la page courante (ex. fenêtre de ±3 pages) +- [ ] Adapter `readerStore.js` : remplacer `itemsPerPage=9999` par la vraie pagination côté API si la virtualisation le justifie, sinon conserver le fetch unique mais différer le rendu +- [ ] Vérifier que le mode `single` n'est pas impacté (il affiche déjà une seule page) + +--- + +## [Bug] Reader — N+1 requêtes SQL dans `getChapterContext()` + +**Problème :** `LegacyChapterRepository::getChapterContext()` émet 5 requêtes SQL pour un seul chargement : la requête principale + 2 doublons dans `getPreviousChapterId()` / `getNextChapterId()` (chacune re-fetche le chapitre courant) + les 2 requêtes de navigation. + +- [ ] Refactorer `getPreviousChapterId()` et `getNextChapterId()` pour accepter l'entité `ChapterEntity` déjà chargée en paramètre (au lieu de re-fetcher par ID) +- [ ] Appeler ces méthodes depuis `getChapterContext()` en passant l'entité déjà disponible +- [ ] Résultat attendu : 3 requêtes maximum (1 pour le chapitre courant + 1 prev + 1 next), idéalement 1 seule avec une requête SQL combinée + +--- + +## [Bug] Reader — Division par zéro dans `ChapterPagesResponse::getTotalPages()` + +**Problème :** `ceil($totalItems / $itemsPerPage)` crashe si `itemsPerPage = 0`. Le test existant documente le bug avec un TODO et assert un HTTP 500 au lieu de corriger. + +- [ ] Ajouter une validation dans `ChapterPagesProvider` : rejeter la requête avec HTTP 400 si `itemsPerPage <= 0` +- [ ] Corriger le test `GetChapterPagesTest` pour vérifier HTTP 400 (et non 500) +- [ ] Supprimer le commentaire TODO du test une fois corrigé + +--- + +## [Bug] Reader — `totalPages` toujours égal à 0 dans `ChapterContext` + +**Problème :** `LegacyChapterRepository::getChapterContext()` hardcode `totalPages: 0`. La méthode `getTotalPagesForChapter()` existe mais n'est jamais appelée depuis `GetChapterContextHandler`. + +- [ ] Appeler `getTotalPagesForChapter()` dans `getChapterContext()` (ou dans le handler) pour calculer le vrai nombre de pages +- [ ] Vérifier que la valeur est correctement sérialisée dans la réponse API Platform (`ChapterContextResponse`) +- [ ] Adapter les tests existants qui pourraient asserter `totalPages: 0` + +--- + ## [Style] Page conversion CBR → CBZ — Simplification UI + notifications toast **Objectif :** Revoir le style de la page de conversion CBR → CBZ pour le simplifier, et remplacer le message statique "Conversion réussie" par les notifications toast de l'application. diff --git a/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue b/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue index 8e46e22..7a6a8e7 100644 --- a/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue +++ b/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue @@ -22,6 +22,7 @@ :pages="store.pages" :zoom="store.zoom" :double-page-mode="store.effectiveDoublePageMode" + :initial-page="store.currentPage" @page-visible="store.handlePageVisible" ref="infiniteReaderRef" /> diff --git a/assets/vue/app/domain/reader/presentation/components/InfiniteReader.vue b/assets/vue/app/domain/reader/presentation/components/InfiniteReader.vue index e6d4f0c..a28e088 100644 --- a/assets/vue/app/domain/reader/presentation/components/InfiniteReader.vue +++ b/assets/vue/app/domain/reader/presentation/components/InfiniteReader.vue @@ -1,10 +1,15 @@