加载随机主题的类别?

我尝试在谷歌上搜索过,但找不到任何相关内容。
我认为有些主题非常有价值,但用户往往更关注最新的主题(除非他们通过谷歌搜索等方式来到论坛)。

是否有可能,也许通过一个组件/插件,创建一个类别或类似的东西,在那里自动加载随机主题?这将重新激活一些可能在三年前没有太多参与度但仍然有价值的旧帖子,也许现在有更多的用户,它们可以得到应有的关注。

需要说明的是,我不想将随机主题限制在单个类别中,尽管那样也可能不错。我想到的是一个全局功能,任何类别中的任何主题都可以加载到该部分。基本上,就像我访问 https://meta.discourse.org/ 的主页时,可以看到所有最新主题的列表,而不管类别如何,但在我的情况下,它会加载任何类别的随机主题,但不侧重于主题的日期或最新的回复。

希望这有道理……

2 个赞

我喜欢随机话题的想法,但我想应该可以进行一些调整。
我的论坛已经有20多年的历史了,如果出现一个页面充斥着十多年前的随机话题,那会很奇怪。

如果你想针对参与度低的话题,应该有一个参数,比如显示的最大回复数。

即便如此,这并不意味着所有这些参与度低的话题都很有趣。

你想根据某些标准来定位话题,还是……随机的?

现有的最接近的功能在于分类设置:

它曾经(也许现在仍然是?)在这里的#support中使用,因为#support话题的理想状态是已解决/已关闭。这里开放的话题通常意味着没有提供解决方案,所以它给了参与度低的话题一个被关注的机会,即使是在几个月或几年之后——这总是有价值的。

1 个赞

随机的。

它可以是一个特定的类别,例如,甚至是一个随机化按钮。
或者也许只是在最新帖子底部的一个区域,可以加载 3 或 4 篇旧的随机帖子?

即使是您 20 多年前的论坛帖子,今天也可能仍然相关,但也许当它们被创建时,没有人真正注意到,现在它们可以得到一些“关注” :wink:

2 个赞

今天我决定请 Claude 帮我解决这个问题。
经过大量的测试和修复(它只加载 OPEN 的主题,这对我来说更有意义),我们得出了以下结果:

image

点击“加载更多随机主题”会在当前主题下方加载 5 个更多主题。再次点击该按钮,会再添加 5 个,直到没有更多主题为止:

image

要加载随机主题,只需访问:
yourwebsite.com/?random


以下是实施方法:

1 - 创建一个组件
2 - 将以下内容添加到 JS 选项卡:

import { apiInitializer } from "discourse/lib/api";
// 如果 URL 中有 ?random,则立即隐藏内容
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) {
  // 隐藏按钮并显示加载中
  button.style.display = 'none';
  const loadingDiv = document.createElement('div');
  loadingDiv.className = 'loading-more';
  loadingDiv.textContent = 'Loading...';
  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 = 'No more topics to load';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // 过滤掉已加载的主题
      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 = 'No more topics to load';
        listDiv.appendChild(noMoreDiv);
        return;
      }
      
      // 随机排序并选择最多 5 个随机主题
      const shuffled = unloadedTopics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, Math.min(5, unloadedTopics.length));
      
      // 从 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.insertBefore(itemDiv, button);
      });
      
      // 检查是否已加载所有可用主题
      if (selected.length < 5 || loadedTopicIds.size >= data.topics.length) {
        button.remove();
        const noMoreDiv = document.createElement('div');
        noMoreDiv.className = 'no-more-topics';
        noMoreDiv.textContent = 'No more topics to load';
        listDiv.appendChild(noMoreDiv);
      } else {
        button.style.display = 'block';
      }
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      loadingDiv.remove();
      button.style.display = 'block';
    });
}

function loadRandomTopics(container) {
  // 重置已加载的主题
  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>No topics found</p>';
        return;
      }
      
      // 随机排序并选择 5 个随机主题
      const shuffled = data.topics.sort(() => 0.5 - Math.random());
      const selected = shuffled.slice(0, 5);
      
      // 构建 HTML
      const listDiv = document.createElement('div');
      listDiv.className = 'random-topics-list';
      
      const heading = document.createElement('h2');
      heading.textContent = 'Random Open Topics';
      listDiv.appendChild(heading);
      
      // 从 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 = 'Load more random topics';
      button.addEventListener('click', () => {
        addRandomTopics(listDiv, button);
      });
      
      listDiv.appendChild(button);
      container.innerHTML = '';
      container.appendChild(listDiv);
    })
    .catch((err) => {
      console.error("Fetch error:", err);
      container.innerHTML = '<p>Error loading topics</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');
    
    // 如果之前在随机页面,但现在不在,则强制重新加载
    if (wasOnRandomPage && !isOnRandomPage) {
      window.location.reload();
      return;
    }
    
    wasOnRandomPage = isOnRandomPage;
    
    if (!isOnRandomPage) {
      return;
    }
    
    // 显示并替换内容
    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 - 将以下内容添加到 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); }
}

如果有人知道如何使用正常的 Discourse 视图而不是使用 HTML 自定义页面,那将非常棒!我对这个功能本身很满意,但如果有人能分享如何做,使用默认布局会更好?