fix: init robuste avant <body>, pas de repli tardif au-dessus de l'écran, README
- écoute freshrss:load-more sur document (script async possiblement exécuté avant le parsing de <body>) - ne replie jamais après coup un article qui commence au-dessus de l'écran : la page sauterait et des articles non vus seraient marqués lus - README : l'image officielle n'a pas git, installation depuis l'hôte Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ccb230d514
commit
66d997cc6e
4 files changed
+66
-10
No files matched your search
@@ -17,21 +17,34 @@ replié est simplement plus court, il est donc marqué lu plus tôt.
|
||||
|
||||
## Installation (Docker)
|
||||
|
||||
Le dépôt est directement le dossier de l'extension. Dans le volume monté sur
|
||||
`/var/www/FreshRSS/extensions` :
|
||||
Le dépôt est directement le dossier de l'extension. L'image officielle
|
||||
`freshrss/freshrss` ne contient pas `git` : on clone **depuis l'hôte** dans le
|
||||
volume monté sur `/var/www/FreshRSS/extensions`.
|
||||
|
||||
**Volume monté depuis un dossier de l'hôte** (`./extensions:/var/www/FreshRSS/extensions`) :
|
||||
|
||||
```sh
|
||||
docker exec -it <conteneur-freshrss> sh -c \
|
||||
'cd /var/www/FreshRSS/extensions && git clone https://git.siteop.biz/vincentb99/freshrss-read-more.git'
|
||||
git clone https://git.siteop.biz/vincentb99/freshrss-read-more.git ./extensions/freshrss-read-more
|
||||
```
|
||||
|
||||
(ou cloner depuis l'hôte dans le dossier du volume). Puis dans FreshRSS :
|
||||
*Configuration → Extensions → Read More → activer*.
|
||||
**Volume Docker nommé** (ex. `freshrss_extensions`) :
|
||||
|
||||
```sh
|
||||
docker run --rm -v freshrss_extensions:/ext alpine/git \
|
||||
clone https://git.siteop.biz/vincentb99/freshrss-read-more.git /ext/freshrss-read-more
|
||||
```
|
||||
|
||||
Les fichiers doivent être lisibles par le serveur web du conteneur (c'est le cas par
|
||||
défaut d'un clone). Puis dans FreshRSS : *Configuration → Extensions → Read More → activer*.
|
||||
|
||||
### Mise à jour
|
||||
|
||||
Même principe, depuis l'hôte :
|
||||
|
||||
```sh
|
||||
cd /var/www/FreshRSS/extensions/freshrss-read-more && git pull
|
||||
git -C ./extensions/freshrss-read-more pull
|
||||
# ou, volume nommé :
|
||||
docker run --rm -v freshrss_extensions:/ext alpine/git -C /ext/freshrss-read-more pull
|
||||
```
|
||||
|
||||
puis recharger la page dans le navigateur.
|
||||
|
||||
@@ -68,7 +68,7 @@ ci-dessous valent donc pour 1.30.4.
|
||||
- **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
|
||||
- **Passage de config au JS** — hook `Minz_HookType::JsVars` (clé `read_more.collapsed_height`, `read_more.label`) ; 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 :
|
||||
@@ -99,7 +99,7 @@ Extension de type `user` (activable par utilisateur), sans base de données ni b
|
||||
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]]
|
||||
registerHook(JsVars) -> ['read_more' => ['collapsed_height' => …, 'label' => …]]
|
||||
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)
|
||||
|
||||
+7
-1
@@ -42,6 +42,11 @@
|
||||
if (state !== undefined && state !== STATE.short) {
|
||||
return;
|
||||
}
|
||||
// Re-evaluating a short entry: never shrink one that starts above the viewport,
|
||||
// the page would jump up and unseen articles could be marked read on next scroll.
|
||||
if (state === STATE.short && flux.getBoundingClientRect().top < 0) {
|
||||
return;
|
||||
}
|
||||
const text = flux.querySelector('.text');
|
||||
if (!text) {
|
||||
return;
|
||||
@@ -71,7 +76,8 @@
|
||||
};
|
||||
const doc = win.document;
|
||||
processEntries(doc, opts);
|
||||
doc.body.addEventListener('freshrss:load-more', () => processEntries(doc, opts));
|
||||
// Listen on document: async scripts may run before <body> exists; the event bubbles.
|
||||
doc.addEventListener('freshrss:load-more', () => processEntries(doc, opts));
|
||||
// Late-loading images can make a "short" article long enough to collapse.
|
||||
doc.addEventListener('load', function (ev) {
|
||||
const target = ev.target;
|
||||
|
||||
@@ -143,3 +143,40 @@ test('init handles load-more', () => {
|
||||
assert.equal(document.querySelectorAll('.read-more-btn').length, 2);
|
||||
assert.equal(document.getElementById('flux_2').dataset.readMore, 'collapsed');
|
||||
});
|
||||
|
||||
test('init works before body is parsed', () => {
|
||||
const { window, document, ReadMore } = setup([]);
|
||||
document.documentElement.removeChild(document.body);
|
||||
setContext(window, 'reader');
|
||||
assert.doesNotThrow(() => ReadMore.init(window, measure));
|
||||
const body = document.createElement('body');
|
||||
body.innerHTML = entry(1, 1000);
|
||||
document.documentElement.appendChild(body);
|
||||
body.dispatchEvent(new window.Event('freshrss:load-more', { bubbles: true }));
|
||||
assert.equal(document.getElementById('flux_1').dataset.readMore, 'collapsed');
|
||||
});
|
||||
|
||||
test('late image load does not collapse entry already above viewport top', () => {
|
||||
const { window, document, ReadMore } = setup([entry(1, 300)]);
|
||||
setContext(window, 'reader');
|
||||
ReadMore.init(window, measure);
|
||||
const flux = document.getElementById('flux_1');
|
||||
flux.getBoundingClientRect = () => ({ top: -100, bottom: 200 });
|
||||
const text = flux.querySelector('.text');
|
||||
text.dataset.h = '1000';
|
||||
text.querySelector('img').dispatchEvent(new window.Event('load'));
|
||||
assert.ok(!text.classList.contains('read-more-collapsed'));
|
||||
assert.equal(flux.querySelectorAll('.read-more-btn').length, 0);
|
||||
});
|
||||
|
||||
test('load-more does not collapse short entry already above viewport top', () => {
|
||||
const { window, document, ReadMore } = setup([entry(1, 300)]);
|
||||
setContext(window, 'reader');
|
||||
ReadMore.init(window, measure);
|
||||
const flux = document.getElementById('flux_1');
|
||||
flux.getBoundingClientRect = () => ({ top: -100, bottom: 200 });
|
||||
flux.querySelector('.text').dataset.h = '1000';
|
||||
document.body.dispatchEvent(new window.Event('freshrss:load-more', { bubbles: true }));
|
||||
assert.equal(flux.dataset.readMore, 'short');
|
||||
assert.equal(flux.querySelectorAll('.read-more-btn').length, 0);
|
||||
});
|
||||
Reference in new issue
Block a user