Heute habe ich beschlossen, Claude um Hilfe dabei zu bitten.
Das ist das Ergebnis nach viel Testen und Korrigieren (es lädt nur ÖFFNENE Themen, was für mich sinnvoller ist):

Wenn man auf „Weitere zufällige Themen laden“ klickt, werden 5 weitere Themen unter den aktuellen geladen. Ein erneutes Klicken auf den Button fügt weitere 5 hinzu, bis keine Themen mehr vorhanden sind:

Um die zufälligen Themen zu laden, besuchen Sie einfach:
ihrewebsite.com/?random
So implementieren Sie es:
1 - Erstellen Sie eine Komponente
2 - Fügen Sie dies in den JS-Tab ein:
import { apiInitializer } from "discourse/lib/api";
// Inhalt sofort ausblenden, wenn ?random in der URL ist
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 ausblenden und Ladeanzeige einblenden
button.style.display = 'none';
const loadingDiv = document.createElement('div');
loadingDiv.className = 'loading-more';
loadingDiv.textContent = 'Lädt...';
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 = 'Keine weiteren Themen zum Laden';
listDiv.appendChild(noMoreDiv);
return;
}
// Bereits geladene Themen herausfiltern
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 = 'Keine weiteren Themen zum Laden';
listDiv.appendChild(noMoreDiv);
return;
}
// Mischen und bis zu 5 zufällige Themen auswählen
const shuffled = unloadedTopics.sort(() => 0.5 - Math.random());
const selected = shuffled.slice(0, Math.min(5, unloadedTopics.length));
// Kategorie aus den Discourse-Seitendaten abrufen
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);
// Vor dem Button einfügen
listDiv.insertBefore(itemDiv, button);
});
// Prüfen, ob alle verfügbaren Themen geladen wurden
if (selected.length < 5 || loadedTopicIds.size >= data.topics.length) {
button.remove();
const noMoreDiv = document.createElement('div');
noMoreDiv.className = 'no-more-topics';
noMoreDiv.textContent = 'Keine weiteren Themen zum Laden';
listDiv.appendChild(noMoreDiv);
} else {
button.style.display = 'block';
}
})
.catch((err) => {
console.error("Fetch error:", err);
loadingDiv.remove();
button.style.display = 'block';
});
}
function loadRandomTopics(container) {
// Geladene Themen zurücksetzen
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>Keine Themen gefunden</p>';
return;
}
// Mischen und 5 zufällige Themen auswählen
const shuffled = data.topics.sort(() => 0.5 - Math.random());
const selected = shuffled.slice(0, 5);
// HTML erstellen
const listDiv = document.createElement('div');
listDiv.className = 'random-topics-list';
const heading = document.createElement('h2');
heading.textContent = 'Zufällige offene Themen';
listDiv.appendChild(heading);
// Kategorie aus den Discourse-Seitendaten abrufen
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 = 'Weitere zufällige Themen laden';
button.addEventListener('click', () => {
addRandomTopics(listDiv, button);
});
listDiv.appendChild(button);
container.innerHTML = '';
container.appendChild(listDiv);
})
.catch((err) => {
console.error("Fetch error:", err);
container.innerHTML = '<p>Fehler beim Laden der Themen</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');
// Wenn wir auf der Zufallsseite waren, aber jetzt nicht mehr, erzwinge Neuladen
if (wasOnRandomPage && !isOnRandomPage) {
window.location.reload();
return;
}
wasOnRandomPage = isOnRandomPage;
if (!isOnRandomPage) {
return;
}
// Inhalt anzeigen und ersetzen
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 - Fügen Sie dies in den CSS-Tab ein:
.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); }
}
Wenn jemand weiß, wie man die normale Discourse-Ansicht anstelle einer benutzerdefinierten HTML-Seite verwendet, wäre das großartig! Ich bin mit der Funktion selbst zufrieden, aber das Standardlayout wäre besser, falls jemand zeigen kann, wie das geht?