diff --git a/extension/liste.css b/extension/liste.css
index 5bfee0a..a2d5c68 100644
--- a/extension/liste.css
+++ b/extension/liste.css
@@ -27,3 +27,25 @@ h1 {
color: #666;
font-style: italic;
}
+
+#etat {
+ margin-bottom: 1.5rem;
+}
+
+.etat-neutre {
+ color: #666;
+}
+
+.etat-erreur {
+ color: #b3261e;
+}
+
+#etat a {
+ display: inline-block;
+ margin-right: 1rem;
+}
+
+#etat button {
+ font: inherit;
+ cursor: pointer;
+}
diff --git a/extension/liste.html b/extension/liste.html
index a19bc08..fdb6481 100644
--- a/extension/liste.html
+++ b/extension/liste.html
@@ -9,6 +9,7 @@
Écho du Huit
+
diff --git a/extension/liste.js b/extension/liste.js
index 10a2141..82d662b 100644
--- a/extension/liste.js
+++ b/extension/liste.js
@@ -1,8 +1,69 @@
-// Squelette : la récupération des deux sources (Atelier du Huit, ville de
-// Cugnaux) arrivera dans des tâches ultérieures. Pour l'instant, état vide.
-const liste = document.querySelector("#liste");
+import {
+ compterEvenements,
+ recupererFluxCalendrier,
+} from "./nextcloud.js";
-const vide = document.createElement("li");
-vide.className = "liste-vide";
-vide.textContent = "Aucune donnée pour l'instant.";
-liste.append(vide);
+const URL_NEXTCLOUD = "https://atelier-huit.frama.space/";
+
+// Mapping type d'erreur → message FR non technique et actionnable.
+const MESSAGES_ERREUR = {
+ "non-connecte":
+ "Tu n'es pas connecté·e à Nextcloud. Ouvre Nextcloud dans un onglet, connecte-toi, puis reviens et clique sur Réessayer.",
+ reseau:
+ "Impossible de joindre Nextcloud. Vérifie ta connexion internet, puis réessaie.",
+ serveur:
+ "Nextcloud a renvoyé une erreur inattendue. Réessaie dans un moment.",
+};
+
+const etat = document.querySelector("#etat");
+
+function rendreNeutre(texte) {
+ etat.replaceChildren();
+ const p = document.createElement("p");
+ p.className = "etat-neutre";
+ p.textContent = texte;
+ etat.append(p);
+}
+
+function rendreSucces(flux) {
+ const nombre = compterEvenements(flux);
+ rendreNeutre(`${nombre} événement${nombre > 1 ? "s" : ""} lu${nombre > 1 ? "s" : ""}`);
+}
+
+function rendreErreur(typeErreur) {
+ etat.replaceChildren();
+
+ const message = document.createElement("p");
+ message.className = "etat-erreur";
+ message.textContent = MESSAGES_ERREUR[typeErreur];
+ etat.append(message);
+
+ if (typeErreur === "non-connecte") {
+ const lien = document.createElement("a");
+ lien.href = URL_NEXTCLOUD;
+ lien.target = "_blank";
+ lien.rel = "noopener";
+ lien.textContent = "Ouvrir Nextcloud";
+ etat.append(lien);
+ }
+
+ const reessayer = document.createElement("button");
+ reessayer.type = "button";
+ reessayer.textContent = "Réessayer";
+ reessayer.addEventListener("click", charger);
+ etat.append(reessayer);
+}
+
+async function charger() {
+ rendreNeutre("Lecture du calendrier…");
+ // Wrapper obligatoire : un `fetch` nu détaché de `window` lève
+ // « Illegal invocation » dans le navigateur.
+ const res = await recupererFluxCalendrier((u, o) => fetch(u, o));
+ if (res.ok) {
+ rendreSucces(res.flux);
+ } else {
+ rendreErreur(res.erreur);
+ }
+}
+
+charger();