# Categoria que carrega tópicos aleatórios?

**URL:** <https://meta.discourse.org/t/category-that-loads-random-topics/356933>\
**Category:** Product Support\
**Created:** [12 Março , 2025 10:42 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933 "2025-03-12T10:42:09Z")\
**Posts on this page:** 1\
**Showing post:** 5

<div class="post-metadata">

**Author:** ![alltiagocom](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alltiagocom/32/492709_2.png) [@alltiagocom](https://meta.discourse.org/u/alltiagocom)\
**Post date:** [27 Dezembro , 2025 16:18 UTC](https://meta.discourse.org/t/category-that-loads-random-topics/356933/5 "2025-12-27T16:18:25Z")

</div>

Hoje decidi pedir ajuda ao Claude para isto.  
Foi o que conseguimos, após muitos testes e correções (ele carrega apenas tópicos ABERTOS, o que faz mais sentido para mim):

![image](https://global.discourse-cdn.com/meta/original/4X/4/8/1/481cb678a28a1d6bac93021211e9a7c48229c06e.png)

Ao clicar em “Load more random topics” (Carregar mais tópicos aleatórios), ele carrega mais 5 tópicos abaixo dos atuais. Clicar no botão novamente adiciona mais 5, até que não haja mais tópicos:

![image](https://global.discourse-cdn.com/meta/original/4X/8/b/3/8b3ad734645511cb098ece29081c2404f175055b.png)

Para carregar os tópicos aleatórios, basta visitar:  
`seuwebsite.com/?random`

* * *

Veja como implementar:

1 - Crie um componente  
2 - Adicione isto na aba JS:

```js
import { apiInitializer } from "discourse/lib/api";
// Oculta o conteúdo imediatamente se ?random estiver na 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) {
  // Oculta o botão e mostra o carregamento
  button.style.display = 'none';
  const loadingDiv = document.createElement('div');
  loadingDiv.className = 'loading-more';
  loadingDiv.textContent = 'Carregando...';
  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 = 'Não há mais tópicos para carregar';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // Filtra tópicos já carregados
      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 = 'Não há mais tópicos para carregar';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // Embaralha e seleciona até 5 tópicos aleatórios
      const shuffled = unloadedTopics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, Math.min(5, unloadedTopics.length));
      
      // Obtém a categoria dos dados do 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);
        
        // Insere antes do botão
        listDiv.insertBefore(itemDiv, button);
      });
      
      // Verifica se carregamos todos os tópicos disponíveis
      if (selected.length < 5 || loadedTopicIds.size >= data.topics.length) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = 'Não há mais tópicos para carregar';
        listDiv.appendChild(noMoreDiv);
      } else {
        button.style.display = 'block';
      }
    })
    .catch((err) => {
      console.error("Erro de busca:", err);
      loadingDiv.remove();
      button.style.display = 'block';
    });
}

function loadRandomTopics(container) {
  // Reseta os tópicos carregados
  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>Nenhum tópico encontrado</p>';
        return;
      }
      
      // Embaralha e seleciona 5 tópicos aleatórios
      const shuffled = data.topics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, 5);
      
      // Constrói o HTML
      const listDiv = document.createElement('div');
      listDiv.className = 'random-topics-list';
      
      const heading = document.createElement('h2');
      heading.textContent = 'Tópicos Abertos Aleatórios';
      listDiv.appendChild(heading);
      
      // Obtém a categoria dos dados do 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 = 'Carregar mais tópicos aleatórios';
      button.addEventListener('click', () => {
        addRandomTopics(listDiv, button);
      });
      
      listDiv.appendChild(button);
      container.innerHTML = '';
      container.appendChild(listDiv);
    })
    .catch((err) => {
      console.error("Erro de busca:", err);
      container.innerHTML = '<p>Erro ao carregar tópicos</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');
    
    // Se estávamos na página aleatória, mas agora não estamos mais, força recarregamento
    if (wasOnRandomPage && !isOnRandomPage) {
      window.location.reload();
      return;
    }
    
    wasOnRandomPage = isOnRandomPage;
    
    if (!isOnRandomPage) {
      return;
    }
    
    // Mostra e substitui o conteúdo
    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 - Adicione isto na aba CSS:

```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); }
}

```

Se alguém souber como usar a visualização normal do Discourse em vez de usar uma página HTML personalizada, isso seria ótimo! Estou satisfeito com o recurso em si, mas ter o layout padrão seria melhor, se alguém puder compartilhar como fazer?

---

_[View the full topic](https://meta.discourse.org/t/category-that-loads-random-topics/356933)._
