docs: design de l'extension Read More

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vincent BourdonandClaude Opus 5.5 committed 2026-11-06 18:45:53 +01:00
commit 4cf2ab3b08
1 file changed
+165
@@ -0,0 +1,165 @@
# FreshRSS « Read More » — conception
Date : 2026-11-06
Dépôt : https://git.siteop.biz/vincentb99/freshrss-read-more.git
## Intention
En **mode lecture** de FreshRSS, les articles s'affichent en entier les uns sous les autres.
Pour des newsletters de veille lues sur smartphone, juger si un sujet intéresse demande
seulement **le titre et un paragraphe** ; quand il n'intéresse pas, il faut aujourd'hui
scroller tout l'article pour atteindre le suivant.
L'extension replie chaque article à une hauteur réduite avec un bouton **« Lire plus »**
qui déroule l'article complet sur place. Un article non intéressant se traverse en un
geste de scroll.
**Contrainte centrale :** le passage automatique en « lu » au scroll de FreshRSS doit
continuer de fonctionner. C'est tout l'intérêt de l'extension : on scrolle, ce qu'on a
dépassé est lu.
## Objectifs
- Réduire le scroll nécessaire pour parcourir une liste d'articles en mode lecture.
- Permettre de lire l'article complet en un tap (« Lire plus »).
- Préserver strictement le marquage « lu » au scroll.
## Existant
Aucune extension équivalente trouvée (2026-11-06) dans la liste officielle
(FreshRSS/Extensions). La plus proche, *Quick Collapse*, bascule tout le flux entre titres
seuls et articles complets, uniquement en vue normale — pas de repli partiel ni de mode lecture.
## Non-objectifs (v1)
- Pas d'effet sur la vue normale ni la vue globale.
- Pas de réglage par flux / catégorie.
- Pas de bouton « Réduire » après dépliage.
- Pas de mémorisation de l'état déplié entre deux chargements de page.
## Anti-objectifs (échec même si « ça marche »)
- Un article replié qui n'est plus marqué « lu » au scroll.
- Un clic sur « Lire plus » qui fait sauter la page, recharge, ou change l'état lu/non lu.
- Des articles chargés dynamiquement (scroll infini) qui ne sont pas repliés.
- Un bouton « Lire plus » sur un article qui tient déjà presque entièrement.
## Faits vérifiés dans le code FreshRSS (master au 2026-11-06)
**Version cible : FreshRSS 1.30.4** (production). Comparaison 1.30.4 ↔ master : aucune
différence sur `onScroll()`, les événements `freshrss:load-more` / `globalContextLoaded`,
le hook `JsVars`, le markup du mode lecture ni le chargement des extensions. Les faits
ci-dessous valent donc pour 1.30.4.
- **Marquage lu au scroll** — `p/scripts/main.js`, `onScroll()` : un `.not_read:not(.keep_unread)`
est marqué lu quand `div.offsetHeight > 0` et que son bord bas
(`offsetParent.offsetTop + offsetTop + offsetHeight`) passe au-dessus du haut de la zone
scrollée (−5 px). La hauteur **réelle affichée** est utilisée : replier l'article le fait
passer en lu plus tôt, ce qui est le comportement souhaité.
⚠️ Un article de hauteur 0 (`display:none`) n'est jamais marqué : ne jamais masquer
l'article entier.
Le scroll est suivi sur `document.scrollingElement` (sauf vue globale : `#panel`), avec
throttle 500 ms.
- Ce comportement dépend de l'option utilisateur FreshRSS « marquer comme lu au défilement »
(`context.auto_mark_scroll`) — l'extension ne la modifie pas.
- **Markup mode lecture** — `app/views/index/reader.phtml` : chaque article est un
`div.flux` (classes `not_read`, `keep_unread`…) contenant
`article.flux_content > div.content > … > div.text` (`app/views/helpers/index/article.phtml`).
- **Vue courante côté JS** — `context.current_view === 'reader'`.
- **Chargement de nouveaux articles** — événement `freshrss:load-more` dispatché sur
`document.body` (bubbles) à chaque lot chargé.
- **Passage de config au JS** — hook `Minz_HookType::JsVars` ; les données retournées
apparaissent dans `context.extensions`.
- **Découverte des extensions** — tout dossier de `extensions/` (`THIRDPARTY_EXTENSIONS_PATH`)
contenant un `metadata.json` valide ; pas de préfixe de nom imposé. En Docker :
`/var/www/FreshRSS/extensions` (volume `freshrss_extensions` dans la doc officielle).
## Comportement décidé
| Sujet | Décision | Origine |
|---|---|---|
| Vue concernée | Mode lecture uniquement | utilisateur |
| Écrans | Même repli sur mobile et desktop | utilisateur |
| Critère de repli | Hauteur fixe en px (pas « N paragraphes » : logos/bannières de newsletter en tête) | proposé, validé |
| Hauteur par défaut | 250 px, réglable dans la config de l'extension | proposé, validé |
| Seuil | Ne pas replier si la hauteur du texte < 1,5 × la hauteur repliée | proposé, validé |
| Indicateur | Dégradé en bas de la zone repliée + bouton « Lire plus » dessous | proposé, validé |
| Dépliage | Sur place, sans rechargement, sans saut de scroll | proposé, validé |
| Après dépliage | Reste déplié ; pas de « Réduire » | proposé, validé |
| État lu | Le bouton ne touche pas à l'état lu ; seul le scroll natif marque | proposé, validé |
| Portée | Tous les flux | proposé, validé |
| Articles chargés au scroll | Repliés aussi | contrainte technique |
| Langue | FR + EN | proposé, validé |
## Architecture
Extension de type `user` (activable par utilisateur), sans base de données ni build.
```
metadata.json name "Read More", entrypoint "ReadMore", type "user"
extension.php class ReadMoreExtension extends Minz_Extension
init(): registerTranslates(), appendScript/appendStyle (static/)
registerHook(JsVars) -> ['read_more' => ['height' => …, libellés]]
handleConfigureAction(): valide et enregistre `collapsed_height` (int, bornes raisonnables)
configure.phtml formulaire : hauteur repliée (px)
i18n/fr/ext.php, i18n/en/ext.php libellés (« Lire plus », config)
static/read-more.js logique côté client
static/read-more.css styles (max-height, overflow hidden, dégradé, bouton)
README.md, LICENSE
```
Le **dépôt git est directement le dossier de l'extension** (cloné dans `extensions/`).
### Logique JS (principe)
1. Ne rien faire si `context.current_view !== 'reader'`. Attendre que `context` soit prêt
(événement `freshrss:globalContextLoaded` ou équivalent selon l'ordre de chargement).
2. Pour chaque `.flux` non encore traité : sur son `.text`, si `scrollHeight ≥ 1,5 × hauteur`,
ajouter une classe de repli (CSS `max-height` + `overflow:hidden` + dégradé) et insérer
un bouton « Lire plus » après le `.text`. Marquer le `.flux` comme traité.
3. Clic sur le bouton : retirer la classe de repli, supprimer le bouton ; `preventDefault`
et `stopPropagation` pour éviter que FreshRSS interprète le clic (ouverture/marquage).
4. Rejouer l'étape 2 sur `freshrss:load-more`.
5. Les images chargées après coup font grandir le contenu : un article d'abord jugé court
peut dépasser le seuil — réévaluer (ex. événements `load` des images ou
`ResizeObserver`), au choix du développeur.
## Étapes pour créer et utiliser l'extension
1. **Développement** : écrire les fichiers ci-dessus dans ce dépôt.
2. **Test local** : lancer un FreshRSS Docker de test avec le dépôt monté en volume :
`docker run -d -p 8080:80 -v "$PWD":/var/www/FreshRSS/extensions/freshrss-read-more freshrss/freshrss:1.30.4`
(image épinglée sur la version de production),
ajouter quelques flux (dont une newsletter longue), activer « marquer comme lu au
défilement », passer en mode lecture.
3. **Activation** : Configuration → Extensions → « Read More » → activer ; régler la hauteur
via l'icône engrenage.
4. **Vérification** (voir critères ci-dessous) sur desktop et en mode mobile du navigateur,
puis sur un vrai smartphone.
5. **Publication** : `git push` vers `git.siteop.biz/vincentb99/freshrss-read-more`.
6. **Installation sur le serveur** : dans le volume extensions du conteneur de production,
`git clone https://git.siteop.biz/vincentb99/freshrss-read-more.git`
(ou `docker exec` dans `/var/www/FreshRSS/extensions`), puis activer dans l'interface.
Mises à jour : `git pull` dans ce dossier, puis rechargement du navigateur.
## Critères d'acceptation
- En mode lecture, un article long est replié à la hauteur configurée, avec dégradé et
bouton « Lire plus ».
- Un article court (< 1,5 × hauteur) n'est pas replié et n'a pas de bouton.
- Scroller au-delà d'un article replié non lu le marque « lu » (option FreshRSS activée).
- Scroller au-delà d'un article déplié le marque « lu » une fois son bas dépassé.
- « Lire plus » déplie sur place, sans saut de scroll, sans changer l'état lu.
- Les articles chargés par le scroll infini sont repliés.
- Vue normale et vue globale : aucun changement.
- Changer la hauteur dans la config change le repli après rechargement.
- Désactiver l'extension rétablit l'affichage d'origine.
## Laissé au développeur
- Indiquer dans le README la version minimale testée (1.30.4).
- Bornes de validation de la hauteur (ex. 100–2000 px).
- Mécanisme exact de réévaluation après chargement des images.
- Style précis du bouton (s'appuyer sur les classes `btn` de FreshRSS pour suivre le thème).
- Méthode d'injection de la config dans le CSS (variable CSS posée par le JS recommandée).