HormoneTrack-web/js/util/changelog.js
Siphonight 491efef8eb v1.4.10 — portage initial de l'app Android v1.4.10
Portage navigateur complet de l'app Android HormoneTrack v1.4.10 :
- moteur PK fidèle (Estrannaise tables ODS, Transfem Science V3C,
  WHSAH fit Mona, Bateman) — paramètres identiques, invariants Android
  préservés (fixes #19-#23, #35, #52-#62), 121 tests Node épinglés
- calibration par période d'ester et par modèle affiché (#60/#61)
- UI 6 écrans + éditeur, CurveChart Canvas (zoom/pan fractionnaire,
  prévision, pics/creux, fuseau configurable), rappels web
  (Notification API), seuils d'alerte, i18n FR/EN
- sauvegarde JSON v2 compatible Android bidirectionnelle (rétrocompat v1)
- 100 % local : localStorage, aucun serveur applicatif, aucune télémétrie
- processus : scripts/check.sh (syntaxe, i18n, tests, E2E navigateur,
  smoke HTTP), docs séparées (README + DEVELOPPEMENT + CHANGELOG)
- dépôt GIT SÉPARÉ de l'Android : historique 100 % propre, versions
  alignées sur l'Android porté, changelogs indépendants

Non porté (impossible dans un navigateur, documenté §12) : agenda
récurrent, notifications onglet fermé, montre.
2026-09-08 14:34:58 +02:00

76 lines
3.5 KiB
JavaScript

/**
* ─────────────────────────────────────────────────────────────────────────────
* ChangelogHelper (portage de `settings/ChangelogHelper.kt`, v1.3.0) :
* alimente le dialog « Nouveautés » affiché après chaque mise à jour.
*
* Le contenu vient de `docs/CHANGELOG.md` (relatif à la racine web, fetché au
* démarrage — le fichier sert aussi de source pour les releases git ; c'est
* l'équivalent de l'asset changelog.md embarqué côté Android, synchronisé par
* la tâche Gradle copyChangelog).
*
* PUR → testable en Node (web/tests/changelog.test.js).
* ─────────────────────────────────────────────────────────────────────────────
*/
/**
* Compare deux versions SemVer « X.Y.Z » NUMÉRIQUEMENT (composant par
* composant) — une comparaison lexicographique considérerait à tort
* « 1.2.9 > 1.2.10 » (piège #38 côté Android : `v1.2.10 < v1.2.5` en
* comparaison de chaînes).
*
* @param {string} current
* @param {string|null|undefined} reference null = première installation → true
* @returns {boolean}
*/
export function isVersionNewer(current, reference) {
if (reference === null || reference === undefined || reference.trim() === '') return true;
const c = parse(current);
const r = parse(reference);
if (!c) return true; // version courante non parsable → considérer plus neuve (défensif)
if (!r) return true;
const len = Math.max(c.length, r.length);
for (let i = 0; i < len; i++) {
const a = c[i] ?? 0;
const b = r[i] ?? 0;
if (a !== b) return a > b;
}
return false; // identiques
}
/** @private Parse "v1.2.3" → [1,2,3] ; null si format non numérique. */
function parse(version) {
const cleaned = String(version).trim().replace(/^v/, '');
if (!/^\d+(\.\d+)*$/.test(cleaned)) return null;
return cleaned.split('.').map(Number);
}
/**
* Extrait de la CHANGELOG (markdown) les sections des versions STRICTEMENT
* plus récentes que `lastSeenVersion` (null = première installation → la
* version courante seule), jusqu'à `currentVersion` incluse.
*
* Format attendu : des sections « ## [X.Y.Z] — … » avec leur corps, la plus
* récente EN PREMIER (convention Keep a Changelog du dépôt).
*
* @param {string} markdown
* @param {string|null} lastSeenVersion
* @param {string} currentVersion
* @returns {string} markdown concaténé des nouveautés ("" si rien de nouveau)
*/
export function sectionsSince(markdown, lastSeenVersion, currentVersion) {
const pattern = /## \[(\d+\.\d+\.\d+)\][^\n]*\n([\s\S]*?)(?=\n## \[|\s*$)/g;
let out = '';
for (const match of markdown.matchAll(pattern)) {
const version = match[1];
// Sections triées de la plus récente à la plus ancienne :
// - inclure ce qui est STRICTEMENT plus récent que la dernière version
// vue (lastSeen null = première installation → section courante seule) ;
// - s'arrêter dès qu'on descend sous lastSeen (déjà vu).
if (lastSeenVersion !== null && !isVersionNewer(version, lastSeenVersion)) break;
if (lastSeenVersion === null && version !== currentVersion) continue;
if (out !== '') out += '\n\n---\n\n';
out += `## Version ${version}\n\n${match[2].trim()}`;
}
return out.trim();
}