Catégorie qui charge des sujets aléatoires?

J’ai essayé de chercher sur Google, mais je n’ai rien trouvé à ce sujet.
Je pense que certains sujets peuvent être très précieux, mais les utilisateurs ont tendance à se concentrer davantage sur les plus récents (sauf s’ils arrivent sur un forum via une recherche Google, par exemple).

Est-il possible, peut-être via un composant/plugin, d’avoir une catégorie ou quelque chose de similaire, où des sujets aléatoires seraient chargés automatiquement ? Cela redonnerait vie à d’anciens messages qui, il y a peut-être 3 ans, n’avaient pas beaucoup d’engagement, mais qui étaient toujours précieux, et peut-être qu’avec plus d’utilisateurs maintenant, ils pourraient recevoir l’attention qu’ils méritent.

Pour être clair, je ne veux pas que les sujets aléatoires soient limités à une seule catégorie, même si cela pourrait aussi être bien. Je pensais plutôt à une fonctionnalité globale où n’importe quel sujet de n’importe quelle catégorie pourrait être chargé dans cette section. C’est essentiellement la même façon que lorsque je vais sur la page d’accueil de https://meta.discourse.org/, je peux voir une liste de tous les sujets les plus récents, quelle que soit la catégorie, mais dans mon cas, cela chargerait des sujets aléatoires également de n’importe quelle catégorie, mais sans se concentrer sur la date des sujets ou même les dernières réponses.

J’espère que cela a du sens…

2 « J'aime »

J’aime l’idée des sujets aléatoires, mais je suppose qu’il faudrait y apporter des ajustements.
Mon forum a plus de 20 ans et il serait étrange d’avoir une page avec un tas de sujets aléatoires vieux de plus d’une décennie.

Si vous voulez cibler des sujets avec peu d’engagement, il devrait y avoir un paramètre comme le nombre maximum de réponses à afficher.

Même ainsi, cela ne signifie pas que tous ces sujets à faible engagement sont intéressants.

Voulez-vous cibler des sujets selon certains critères, ou juste… des sujets aléatoires ?

La chose la plus proche qui existe se trouve dans les paramètres de catégorie :

Cela a été (peut-être l’est-ce encore ?) utilisé ici sur Support, car l’état idéal des sujets de Support est résolu/fermé. Les sujets ouverts ici signifient généralement qu’aucune solution n’a été fournie, ce qui donne une chance aux sujets à faible engagement d’être examinés, même si c’est des mois ou des années plus tard - cela peut toujours être précieux.

1 « J'aime »

Au hasard.

Cela pourrait être une catégorie spécifique, par exemple, ou même un bouton pour mélanger.
Ou peut-être juste une section en bas des derniers messages qui chargerait 3 ou 4 anciens messages aléatoires ?

Même les messages de votre forum vieux de plus de 20 ans peuvent être pertinents aujourd’hui, mais peut-être que lorsqu’ils ont été créés, personne n’y a vraiment prêté attention et ils pourraient recevoir un peu d’“amour” maintenant :wink:

2 « J'aime »

Aujourd’hui, j’ai décidé de demander à Claude de m’aider avec ceci.
Voici ce que nous avons trouvé, après beaucoup de tests et de corrections (il ne charge que les sujets OUVERTS, ce qui a plus de sens pour moi) :

image

Lorsque vous cliquez sur « Charger plus de sujets aléatoires », 5 sujets supplémentaires sont chargés sous ceux qui sont actuellement affichés. Cliquer à nouveau sur le bouton ajoute 5 sujets de plus, jusqu’à ce qu’il n’y ait plus de sujets :

image

Pour charger les sujets aléatoires, visitez simplement :
votresite.com/?random


Voici comment l’implémenter :

1 - Créez un composant
2 - Ajoutez ceci dans l’onglet JS :

import { apiInitializer } from "discourse/lib/api";
// Masquer le contenu immédiatement si ?random est dans l'URL
if (new URLSearchParams(window.location.search).has('random')) {
  const style = document.createElement('style');
  style.textContent = '#main-outlet { display: none; }';
  document.head.appendChild(style);
}

let loadedTopicIds = new Set();

function addRandomTopics(listDiv, button) {
  // Masquer le bouton et afficher le chargement
  button.style.display = 'none';
  const loadingDiv = document.createElement('div');
  loadingDiv.className = 'loading-more';
  loadingDiv.textContent = 'Chargement...';
  listDiv.appendChild(loadingDiv);
  
  const query = `status:open`;
  
  fetch(`/search.json?q=${encodeURIComponent(query)}`)
    .then((response) => response.json())
    .then((data) => {
      loadingDiv.remove();
      
      if (!data || !data.topics || data.topics.length === 0) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = 'Plus de sujets à charger';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // Filtrer les sujets déjà chargés
      const unloadedTopics = data.topics.filter(topic => !loadedTopicIds.has(topic.id));
      
      if (unloadedTopics.length === 0) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = 'Plus de sujets à charger';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // Mélanger et sélectionner jusqu'à 5 sujets aléatoires
      const shuffled = unloadedTopics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, Math.min(5, unloadedTopics.length));
      
      // Obtenir la catégorie à partir des données du site Discourse
      const site = Discourse.__container__.lookup('site:main');
      
      selected.forEach(topic => {
        loadedTopicIds.add(topic.id);
        
        const date = new Date(topic.created_at);
        const formattedDate = date.toLocaleDateString('en-US', { 
          year: 'numeric', 
          month: 'short', 
          day: 'numeric' 
        });
        
        const itemDiv = document.createElement('div');
        itemDiv.className = 'random-topic-item';
        
        const linkDiv = document.createElement('div');
        linkDiv.className = 'random-topic-link';
        
        const link = document.createElement('a');
        link.href = `/t/${topic.slug}/${topic.id}`;
        link.textContent = topic.unicode_title || topic.title;
        
        linkDiv.appendChild(link);
        
        const metaDiv = document.createElement('div');
        metaDiv.className = 'random-topic-meta';
        
        const category = site.categories.find(cat => cat.id === topic.category_id);
        const categoryName = category?.name || 'Uncategorized';
        
        metaDiv.textContent = `${categoryName} • ${formattedDate}`;
        
        itemDiv.appendChild(linkDiv);
        itemDiv.appendChild(metaDiv);
        
        // Insérer avant le bouton
        listDiv.insertBefore(itemDiv, button);
      });
      
      // Vérifier si nous avons chargé tous les sujets disponibles
      if (selected.length < 5 || loadedTopicIds.size >= data.topics.length) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = 'Plus de sujets à charger';
        listDiv.appendChild(noMoreDiv);
      } else {
        button.style.display = 'block';
      }
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      loadingDiv.remove();
      button.style.display = 'block';
    });
}

function loadRandomTopics(container) {
  // Réinitialiser les sujets chargés
  loadedTopicIds = new Set();
  
  container.innerHTML = '<div class="spinner"></div>';
  
  const query = `status:open`;
  
  fetch(`/search.json?q=${encodeURIComponent(query)}`)
    .then((response) => response.json())
    .then((data) => {
      if (!data || !data.topics || data.topics.length === 0) {
        container.innerHTML = '<p>Aucun sujet trouvé</p>';
        return;
      }
      
      // Mélanger et sélectionner 5 sujets aléatoires
      const shuffled = data.topics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, 5);
      
      // Construire le HTML
      const listDiv = document.createElement('div');
      listDiv.className = 'random-topics-list';
      
      const heading = document.createElement('h2');
      heading.textContent = 'Sujets ouverts aléatoires';
      listDiv.appendChild(heading);
      
      // Obtenir la catégorie à partir des données du site Discourse
      const site = Discourse.__container__.lookup('site:main');
      
      selected.forEach(topic => {
        loadedTopicIds.add(topic.id);
        
        const date = new Date(topic.created_at);
        const formattedDate = date.toLocaleDateString('en-US', { 
          year: 'numeric', 
          month: 'short', 
          day: 'numeric' 
        });
        
        const itemDiv = document.createElement('div');
        itemDiv.className = 'random-topic-item';
        
        const linkDiv = document.createElement('div');
        linkDiv.className = 'random-topic-link';
        
        const link = document.createElement('a');
        link.href = `/t/${topic.slug}/${topic.id}`;
        link.textContent = topic.unicode_title || topic.title;
        
        linkDiv.appendChild(link);
        
        const metaDiv = document.createElement('div');
        metaDiv.className = 'random-topic-meta';
        
        const category = site.categories.find(cat => cat.id === topic.category_id);
        const categoryName = category?.name || 'Uncategorized';
        
        metaDiv.textContent = `${categoryName} • ${formattedDate}`;
        
        itemDiv.appendChild(linkDiv);
        itemDiv.appendChild(metaDiv);
        listDiv.appendChild(itemDiv);
      });
      
      const button = document.createElement('button');
      button.className = 'btn btn-primary load-more-random';
      button.textContent = 'Charger plus de sujets aléatoires';
      button.addEventListener('click', () => {
        addRandomTopics(listDiv, button);
      });
      
      listDiv.appendChild(button);
      container.innerHTML = '';
      container.appendChild(listDiv);
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      container.innerHTML = '<p>Erreur lors du chargement des sujets</p>';
    });
}

export default apiInitializer("1.8.0", (api) => {
  let wasOnRandomPage = new URLSearchParams(window.location.search).has('random');
  
  api.onPageChange(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const isOnRandomPage = urlParams.has('random');
    
    // Si nous étions sur la page aléatoire mais que nous n'y sommes plus, forcer le rechargement
    if (wasOnRandomPage && !isOnRandomPage) {
      window.location.reload();
      return;
    }
    
    wasOnRandomPage = isOnRandomPage;
    
    if (!isOnRandomPage) {
      return;
    }
    
    // Afficher et remplacer le contenu
    const mainContent = document.querySelector('#main-outlet');
    if (mainContent) {
      mainContent.style.display = 'block';
      mainContent.innerHTML = '<div id="random-topics-container"></div>';
      
      const container = document.querySelector('#random-topics-container');
      loadRandomTopics(container);
    }
  });
});

3 - Ajoutez ceci dans l’onglet CSS :

.random-topics-list {
  max-width: 800px;
  margin: 40px auto;
  padding: 20px;
}

.random-topics-list h2 {
  margin-bottom: 30px;
  font-size: 2em;
}

.random-topic-item {
  margin-bottom: 25px;
}

.random-topic-link a {
  color: #E9E9E9;
  text-decoration: underline;
  font-size: 1.1em;
}

.random-topic-link a:hover {
  color: #229ED7;
}

.random-topic-meta {
  color: #808080;
  font-size: 0.85em;
  margin-top: 4px;
}

.load-more-random {
  margin-top: 30px;
}

.loading-more {
  text-align: center;
  margin-top: 20px;
  color: #808080;
}

.no-more-topics {
  text-align: center;
  margin-top: 30px;
  color: #808080;
  font-style: italic;
}

.spinner {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  animation: spin 1s linear infinite;
  margin: 100px auto;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

Si quelqu’un sait comment utiliser la vue Discourse normale au lieu d’utiliser une page personnalisée HTML, ce serait génial ! Je suis satisfait de la fonctionnalité elle-même, mais avoir la mise en page par défaut serait mieux, si quelqu’un peut partager comment faire ?