Files
Mangarr/TASK.md
ext.jeremy.guillot@maxicoffee.domains 74f033f5d1 perf(reader): windowing + eager loading sur l'InfiniteReader
- 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 <img> de ReaderPage
2026-03-15 17:46:00 +01:00

6.9 KiB

TASK.md — Tâches à venir

[Feature] Découvrir — Suggestions de mangas via MangaDex

Objectif : Page "Découvrir" qui propose des mangas populaires/récents depuis l'API MangaDex, en excluant ceux déjà présents en base (comparaison via externalId = ID MangaDex).

Backend

  • Consulter la doc API MangaDex pour identifier le(s) endpoint(s) pertinents (mangas populaires, récemment mis à jour, tendances…) et les paramètres disponibles (filtres langue, statut, contentRating, etc.)
  • Étendre le client MangaDex existant pour exposer le(s) nouvel(aux) endpoint(s) identifiés (nouveau(x) méthode(s) dans le client + adapter le contrat d'interface si besoin)
  • Query GetDiscoverMangaListQuery + handler qui appelle le client MangaDex et filtre les résultats dont l'externalId est déjà en base
  • Response DTO DiscoverMangaListResponse avec les champs nécessaires à l'affichage (id MangaDex, titre, couverture, genres, statut…)
  • State Provider API Platform sur la route GET /api/manga/discover

Frontend

  • Page DiscoverPage.vue avec grille de cards (réutiliser MangaCard.vue ou créer DiscoverMangaCard.vue)
  • Composable TanStack Query useDiscoverMangaList
  • Route Vue Router /discover
  • Entrée dans la Sidebar

[Domain] Créer le domaine "System"

Objectif : Poser la structure DDD hexagonale du nouveau domaine System qui servira de socle aux fonctionnalités Status et Logs.

  • Créer l'arborescence src/Domain/System/Domain/, Application/, Infrastructure/
  • Créer l'arborescence frontend assets/vue/app/domain/system/
  • Vérifier la conformité avec phparkitect.php (ajouter le domaine si nécessaire)

[Feature] System — Page "Status"

Objectif : Page de monitoring affichant l'état général de l'application.

Backend

  • Query GetSystemStatusQuery + handler qui agrège :
    • Version de l'application (depuis composer.json ou variable d'env)
    • Statut des services critiques (base de données, Messenger workers, stockage)
    • Poids total des images (scan du dossier IMAGE_DATA_PATH)
    • Poids total des CBZ (scan du dossier MANGA_DATA_PATH)
    • Liens / chemins vers les dossiers de stockage configurés
  • Response DTO SystemStatusResponse
  • State Provider API Platform sur la route GET /api/system/status

Frontend

  • Page StatusPage.vue avec sections (Général, Stockage, Services)
  • Composable TanStack Query useSystemStatus
  • Route Vue Router /system/status

[Feature] System — Page "Logs"

Objectif : Page de consultation des logs d'erreur des workers Messenger, avec filtres.

Backend

  • Définir le contrat WorkerLogRepositoryInterface dans System/Domain/Contract/Repository/
  • Implémenter DoctrineWorkerLogRepository (ou lecture des logs Monolog selon la stratégie retenue) dans Infrastructure/
  • Query GetWorkerLogsQuery avec paramètres de filtrage (date début/fin, source, niveau, worker/transport) + handler
  • Response DTO WorkerLogListResponse (liste paginée)
  • State Provider API Platform sur la route GET /api/system/logs

Frontend

  • Page LogsPage.vue avec tableau paginé + panneau de filtres
  • Filtres disponibles : plage de dates, source (transport Messenger), niveau d'erreur, manga associé (source préférée)
  • Composable TanStack Query useWorkerLogs (avec paramètres de filtre réactifs)
  • Route Vue Router /system/logs

[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.

  • Auditer le composant/template actuel de la page de conversion
  • Simplifier la mise en page (réduire la complexité visuelle, harmoniser avec le reste de l'UI)
  • Supprimer l'affichage inline "Conversion réussie"
  • Brancher les notifications toast existantes pour signaler le succès (et l'échec) de la conversion