Les réponses récentes prennent 2 secondes pour s'afficher

Y a-t-il un moyen d’afficher instantanément les dernières réponses ? Actuellement, j’utilise ce code :

  api.onPageChange(() = {
    if (window.location.pathname === "/") {
      const container = document.querySelector(".latest-topic-list");
      if (!container || container.dataset.modified === "true") return;

      fetch("/posts.json?order=created")
        .then(res = res.json())
        .then(data = {
          const replies = data.latest_posts
            .filter(p = p.post_number  1  !p.topic_slug.includes("private-message"))
            .slice(0, 15);

          const topicFetches = replies.map(post =
            fetch(`/t/${post.topic_id}.json`)
              .then(res = res.json())
              .then(topic = {
                return {
                  post,
                  category: topic.category_id ? topic.category_name : null,
                  tags: topic.tags || []
                };
              })
          );

          Promise.all(topicFetches).then(results = {
            const rows = results.map(({ post, category, tags }) = {
              const url = `/t/${post.topic_slug}/${post.topic_id}/${post.post_number}`;
              const avatarUrl = post.avatar_template.replace("{size}", "45");
              const excerpt = post.excerpt?.replace(/<\/?[^\/>]+(>|$)/g, "")?.slice(0, 120) + (post.excerpt?.length e 120 ? '...' : '') || '';

              const categoryHtml = category
                ? `span style="font-size: 0.85em; color: #666;"Catégorie : strong${category}/strong/spanbr`
                : '';

              const tagsHtml = tags.length
                ? `span style="font-size: 0.85em; color: #666;"Tags : ${tags.map(tag = `span style="background:#eee; padding:2px 6px; border-radius:3px; margin-right:4px;"${tag}/span`).join("")}/span`
                : '';

              return `
                tr class="topic-list-item"
                  td class="main-link clearfix"
                    div style="display: flex; align-items: center; gap: 16px; padding: 8px 0;"
                      div style="flex-shrink: 0;"
                        a class="avatar-link" href="/u/${post.username}"
                          img loading="lazy" width="45" height="45" src="${avatarUrl}" class="avatar" alt="${post.username}"
                        /a
                      /div
                      div style="display: flex; flex-direction: column; justify-content: center; padding-top: 8px; padding-bottom: 8px;"
                        span class="link-top-line" style="margin-bottom: 6px;"
                          a href="${url}" class="title raw-link"${excerpt}/a
                        /span
                        div class="link-bottom-line"
                          ${categoryHtml}
                          ${tagsHtml}
                        /div
                      /div
                    /div
                  /td
                /tr
              `;
            }).join("");

            // Crée le conteneur de la section des derniers commentaires
            const latestRepliesContainer = document.createElement("div");
            latestRepliesContainer.className = "latest-replies-container";
            latestRepliesContainer.style.marginTop = "2em";

            latestRepliesContainer.innerHTML = `
              table class="topic-list latest-topic-list"
                thead
                  tr
                    th class="default"Derniers Commentaires/th
                  /tr
                /thead
                tbody
                  ${rows}
                /tbody
              /table
            `;

            container.parentNode.insertBefore(latestRepliesContainer, container.nextSibling);
            container.dataset.modified = "true";
          });
        })
        .catch(error = {
          console.error("Erreur lors de la récupération des derniers commentaires :", error);
        });
    }
  });
/script```

mais il faut en moyenne 2 secondes pour que le contenu apparaisse. La même chose se produit avec les blocs de la barre latérale droite avec "réponses récentes". Est-ce normal ?

Pourquoi ajoutez-vous du code ?

Cela fonctionne sans rien de spécial ; est-ce qu’il ne fonctionne pas pour vous ?

C’est son problème.

Je dirais que c’est attendu car le code effectue plusieurs requêtes API.
Il récupère les derniers messages, puis effectue une requête par ID de sujet (15 ici) pour récupérer le nom de la catégorie.

Pour le moment, je ne sais pas s’il existe une autre solution que d’utiliser un plugin et de faire une requête SQL personnalisée, par exemple.

C’est exact.
Mais les Blocs de barre latérale droite font aussi des requêtes ? Cela me donne les mêmes résultats pour les réponses récentes. Cela prend 2 secondes pour apparaître.

Je vais voir comment créer un plugin. Merci.

Oui, il fait la même chose pour récupérer les derniers messages, mais c’est tout. Il n’essaie pas d’obtenir le nom de la catégorie, c’est la différence.

Je comprends. Merci pour votre aide.

Je vais essayer de trouver une autre solution

Enfin, j’utilise ceci pour l’instant :

<script type="text/discourse-plugin" version="0.11.3">
  api.onPageChange(() => {
    if (window.location.pathname === "/") {
      const container = document.querySelector(".latest-topic-list");
      if (!container) return;
      
      // Évite les initialisations multiples
      if (window.latestRepliesInitialized) return;
      window.latestRepliesInitialized = true;
      
      // Paramètres
      const POLLING_INTERVAL = 2000; // 2 secondes
      const COMMENTS_TO_SHOW = 15;
      const CACHE_DURATION = 30 * 60 * 1000; // 30 minutes
      
      // Clés de cache
      const CACHE_KEY = "discourse_latest_replies_data";
      const CACHE_TIMESTAMP_KEY = "discourse_latest_replies_timestamp";
      const CACHE_LAST_ID_KEY = "discourse_latest_replies_last_id";
      
      // Stocke le dernier ID de publication vu pour comparaison
      let lastSeenPostId = parseInt(localStorage.getItem(CACHE_LAST_ID_KEY) || "0");
      let pollingIntervalId = null;
      
      console.log(`Initialisation du plugin des derniers commentaires (dernier ID en cache : ${lastSeenPostId})`);
      
      // Fonction pour charger les commentaires
      function loadLatestReplies(silent = false, forceRefresh = false) {
        // Vérifie d'abord le cache, sauf si mise à jour forcée
        if (!forceRefresh) {
          const cachedData = localStorage.getItem(CACHE_KEY);
          const cacheTimestamp = localStorage.getItem(CACHE_TIMESTAMP_KEY);
          const now = Date.now();
          
          // Si nous avons des données en cache valides
          if (cachedData && cacheTimestamp && now - parseInt(cacheTimestamp) < CACHE_DURATION) {
            try {
              const results = JSON.parse(cachedData);
              console.log(`Utilisation des données en cache (${results.length} commentaires, cache de ${Math.round((now - parseInt(cacheTimestamp)) / 1000 / 60)} minutes)`);
              renderLatestReplies(results, false);
              
              // Si ce n'est pas silencieux, nous n'avons rien d'autre à faire
              if (!silent) {
                return;
              }
              
              // Si c'est silencieux, nous continuons pour vérifier les mises à jour
            } catch (e) {
              console.error("Erreur lors du traitement du cache :", e);
              // En cas d'erreur dans le cache, nous continuons pour récupérer de nouvelles données
            }
          } else if (cachedData) {
            console.log("Cache expiré, récupération de nouvelles données");
          } else {
            console.log("Aucun cache trouvé, récupération de nouvelles données");
          }
        } else {
          console.log("Forçage de la mise à jour, ignore le cache");
        }
        
        // Si ce n'est pas silencieux, affiche l'indicateur de chargement
        if (!silent) {
          // Si un conteneur de commentaires existe déjà, n'affiche pas l'indicateur
          const existingContainer = document.querySelector(".latest-replies-container");
          if (!existingContainer) {
            let loadingIndicator = document.getElementById("latest-replies-loading");
            if (!loadingIndicator) {
              loadingIndicator = document.createElement("div");
              loadingIndicator.id = "latest-replies-loading";
              loadingIndicator.innerHTML = `
                <div style="text-align: center; padding: 20px;">
                  <span class="spinner small"></span>
                  <span style="margin-left: 10px;">Chargement des commentaires récents...</span>
                </div>
              `;
              container.parentNode.insertBefore(loadingIndicator, container.nextSibling);
            }
          }
        }
        
        // Récupère les données les plus récentes
        fetch("/posts.json?order=created")
          .then(res => res.json())
          .then(data => {
            // Journal pour le diagnostic
            if (!silent) {
              console.log("Données reçues de l'API :", data.latest_posts.length);
            }
            
            const replies = data.latest_posts
              .filter(p => p.post_number > 1 && !p.topic_slug.includes("private-message"))
              .slice(0, COMMENTS_TO_SHOW);
            
            // Vérifie s'il y a de nouvelles publications
            const maxId = replies.length > 0 ? Math.max(...replies.map(post => post.id)) : 0;
            const hasNewPosts = maxId > lastSeenPostId;
            
            // Journal pour le diagnostic
            if (hasNewPosts && !silent) {
              console.log(`Nouvelles publications détectées. Dernier ID : ${lastSeenPostId}, Nouveau maximum ID : ${maxId}`);
            }
            
            // Met à jour le dernier ID vu
            if (maxId > lastSeenPostId) {
              lastSeenPostId = maxId;
              localStorage.setItem(CACHE_LAST_ID_KEY, lastSeenPostId.toString());
            }
            
            // S'il n'y a pas de nouvelles publications et que c'est une vérification silencieuse, ne fait rien
            if (!hasNewPosts && silent && !forceRefresh) {
              return;
            }
            
            // Récupère les détails des sujets
            const topicPromises = replies.map(post => {
              // Vérifie si nous avons le sujet en cache
              const topicCacheKey = `discourse_topic_${post.topic_id}`;
              const cachedTopic = localStorage.getItem(topicCacheKey);
              
              if (cachedTopic && !forceRefresh) {
                try {
                  return Promise.resolve(JSON.parse(cachedTopic));
                } catch (e) {
                  console.error(`Erreur lors du traitement du cache du sujet ${post.topic_id}:`, e);
                  // En cas d'erreur dans le cache, nous récupérons depuis le serveur
                }
              }
              
              return fetch(`/t/${post.topic_id}.json`)
                .then(res => res.json())
                .then(topic => {
                  // Stocke le sujet en cache
                  localStorage.setItem(topicCacheKey, JSON.stringify(topic));
                  return topic;
                })
                .catch(error => {
                  console.error(`Erreur lors de la récupération du sujet ${post.topic_id}:`, error);
                  return { category_id: null, category_name: null, tags: [] };
                });
            });
            
            Promise.all(topicPromises)
              .then(topics => {
                const results = replies.map((post, index) => {
                  const topic = topics[index];
                  return {
                    post,
                    category: topic.category_id ? topic.category_name : null,
                    tags: topic.tags || []
                  };
                });
                
                // Stocke les résultats en cache
                localStorage.setItem(CACHE_KEY, JSON.stringify(results));
                localStorage.setItem(CACHE_TIMESTAMP_KEY, Date.now().toString());
                
                // Affiche les résultats
                renderLatestReplies(results, hasNewPosts || forceRefresh);
              })
              .catch(error => {
                console.error("Erreur lors du traitement des sujets :", error);
              });
          })
          .catch(error => {
            console.error("Erreur lors de la récupération des derniers commentaires :", error);
            // Supprime l'indicateur de chargement en cas d'erreur
            if (!silent) {
              const loadingElement = document.getElementById("latest-replies-loading");
              if (loadingElement) loadingElement.remove();
            }
          });
      }
      
      // Fonction pour afficher les résultats
      function renderLatestReplies(results, animate = false) {
        // Supprime l'indicateur de chargement
        const loadingElement = document.getElementById("latest-replies-loading");
        if (loadingElement) loadingElement.remove();
        
        // S'il n'y a pas de résultats, ne fait rien
        if (!results || results.length === 0) {
          console.log("Aucun commentaire trouvé à afficher");
          return;
        }
        
        const rows = results.map(({ post, category, tags }) => {
          const url = `/t/${post.topic_slug}/${post.topic_id}/${post.post_number}`;
          const avatarUrl = post.avatar_template.replace("{size}", "45");
          const excerpt = post.excerpt?.replace(/<\/?[^>]+(>|$)/g, "")?.slice(0, 120) + (post.excerpt?.length > 120 ? '...' : '') || '';

          const categoryHtml = category
            ? `<span style="font-size: 0.85em; color: #666;">Catégorie : <strong>${category}</strong></span><br>`
            : '';

          const tagsHtml = tags.length
            ? `<span style="font-size: 0.85em; color: #666;">Tags : ${tags.map(tag => `<span style="background:#eee; padding:2px 6px; border-radius:3px; margin-right:4px;">${tag}</span>`).join("")}</span>`
            : '';

          const animationClass = animate ? 'new-comment' : '';

          return `
            <tr class="topic-list-item ${animationClass}" data-post-id="${post.id}">
              <td class="main-link clearfix">
                <div style="display: flex; align-items: center; gap: 16px; padding: 8px 0;">
                  <div style="flex-shrink: 0;">
                    <a class="avatar-link" href="/u/${post.username}">
                      <img loading="lazy" width="45" height="45" src="${avatarUrl}" class="avatar" alt="${post.username}">
                    </a>
                  </div>
                  <div style="display: flex; flex-direction: column; justify-content: center; padding-top: 8px; padding-bottom: 8px;">
                    <span class="link-top-line" style="margin-bottom: 6px;">
                      <a href="${url}" class="title raw-link">${excerpt}</a>
                    </span>
                    <div class="link-bottom-line">
                      ${categoryHtml}
                      ${tagsHtml}
                    </div>
                  </div>
                </div>
              </td>
            </tr>
          `;
        }).join("");

        // Ajoute le style d'animation s'il n'existe pas encore
        if (!document.getElementById('latest-replies-style')) {
          const style = document.createElement('style');
          style.id = 'latest-replies-style';
          style.textContent = `
            @keyframes highlightNew {
              0% { background-color: rgba(255, 255, 0, 0.3); }
              100% { background-color: transparent; }
            }
            .new-comment {
              animation: highlightNew 2s ease-out;
            }
          `;
          document.head.appendChild(style);
        }

        // Supprime le conteneur existant, s'il y en a un
        const existingContainer = document.querySelector(".latest-replies-container");
        if (existingContainer) {
          existingContainer.remove();
        }

        // Crée le conteneur de la section des derniers commentaires
        const latestRepliesContainer = document.createElement("div");
        latestRepliesContainer.className = "latest-replies-container";
        latestRepliesContainer.style.marginTop = "2em";

        // Ajoute le bouton de mise à jour manuelle et l'indicateur de statut
        const cacheTime = new Date(parseInt(localStorage.getItem(CACHE_TIMESTAMP_KEY) || Date.now()));
        const formattedTime = cacheTime.toLocaleTimeString();
        
        latestRepliesContainer.innerHTML = `
          <table class="topic-list latest-topic-list">
            <thead>
              <tr>
                <th class="default">
                  Derniers Commentaires
                  <span id="comments-status" style="font-size: 0.8em; font-weight: normal; margin-left: 10px;">
                  </span>
                  <button id="refresh-comments" class="btn btn-flat no-text btn-icon" style="float: right;" title="Actualiser les commentaires">
                    <svg class="fa d-icon d-icon-sync svg-icon svg-string" width="16" height="16" aria-hidden="true"><use xlink:href="#sync"></use></svg>
                  </button>
                </th>
              </tr>
            </thead>
            <tbody id="latest-replies-tbody">
              ${rows}
            </tbody>
          </table>
        `;

        container.parentNode.insertBefore(latestRepliesContainer, container.nextSibling);
        container.dataset.modified = "true";
        
        // Ajoute l'événement de clic au bouton de mise à jour
        document.getElementById("refresh-comments").addEventListener("click", function() {
          // Met à jour le texte de statut
          const statusElement = document.getElementById("comments-status");
          if (statusElement) {
            statusElement.textContent = "(actualisation...)";
          }
          
          // Force la mise à jour
          loadLatestReplies(false, true);
        });
        
        console.log(`${results.length} commentaires affichés`);
      }
      
      // Démarre le chargement initial (en utilisant le cache)
      loadLatestReplies(false, false);
      
      // Configure le sondage haute fréquence
      pollingIntervalId = setInterval(() => {
        // Ne met à jour que si l'utilisateur est sur la page d'accueil
        if (window.location.pathname === "/") {
          loadLatestReplies(true, false); // Silencieux, utilise le cache si disponible
        }
      }, POLLING_INTERVAL);
      
      console.log(`Sondage configuré toutes les ${POLLING_INTERVAL}ms`);
      
      // Nettoie l'intervalle lorsque l'utilisateur quitte la page
      api.onPageChange((url) => {
        if (url !== "/") {
          console.log("Quittant la page d'accueil, nettoyage des ressources");
          
          if (pollingIntervalId) {
            clearInterval(pollingIntervalId);
            pollingIntervalId = null;
          }
          
          window.latestRepliesInitialized = false;
        }
      });
    }
  });
</script>

Cela fonctionne comme prévu. Il ajoute automatiquement les nouveaux commentaires, puis les ajoute au cache. Je sais que ce n’est pas l’idéal. Je vais bientôt essayer un plugin.

L’objectif ici est : au lieu d’une catégorie avec le style de page des derniers sujets, j’aimerais une catégorie avec les dernières réponses.