async function loadRSS() { const container = document.getElementById("rss-feed"); const showLegacyContainer = document.getElementById("show-legacy-container"); const showLegacyBtn = document.getElementById("show-legacy-btn"); if (!container) { console.error("RSS feed container not found"); return; } // Show loading state container.innerHTML = "
Loading release notes...
"; const rssUrl = "https://rattatwinko.servecounterstrike.com/gitea/rattatwinko/bytechat-desktop/releases.rss"; try { // Try direct fetch first let response; let xmlText; try { response = await fetch(rssUrl, { method: 'GET', headers: { 'Accept': 'application/rss+xml, application/xml, text/xml, */*', 'Cache-Control': 'no-cache' }, mode: 'cors' }); if (response.ok) { xmlText = await response.text(); } else { throw new Error(`HTTP ${response.status}`); } } catch (corsError) { console.warn("Direct fetch failed, trying proxy...", corsError); // Try CORS proxy const proxyUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(rssUrl)}`; const proxyResponse = await fetch(proxyUrl); if (!proxyResponse.ok) { throw new Error(`Proxy failed: ${proxyResponse.status}`); } const proxyData = await proxyResponse.json(); if (!proxyData.contents) { throw new Error("No content from proxy"); } xmlText = proxyData.contents; } // Parse the XML const parser = new DOMParser(); const xml = parser.parseFromString(xmlText, "application/xml"); // Check for parsing errors const parserError = xml.querySelector("parsererror"); if (parserError) { throw new Error("XML parsing failed"); } const items = parseRSSItems(xml); displayRSSItems(items, container, showLegacyContainer, showLegacyBtn); } catch (err) { console.error("Failed to load RSS:", err); // Show error with retry and direct link container.innerHTML = ` `; } } function parseRSSItems(xml) { console.log("Parsing RSS XML..."); const items = Array.from(xml.querySelectorAll("item")).map(item => { const title = item.querySelector("title")?.textContent?.trim() || "Untitled Release"; const link = item.querySelector("link")?.textContent?.trim() || "#"; const pubDate = item.querySelector("pubDate")?.textContent?.trim() || ""; const author = item.querySelector("author")?.textContent?.trim() || "Unknown"; const description = item.querySelector("description")?.textContent?.trim() || ""; // Extract content from CDATA if available const contentEncoded = item.querySelector("content\\:encoded"); let content = ""; if (contentEncoded) { content = contentEncoded.textContent.trim(); // Remove HTML tags for a clean preview content = content.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); // Limit to first 150 characters if (content.length > 150) { content = content.substring(0, 150) + "..."; } } console.log("Parsed item:", { title, pubDate, author }); return { title, link, pubDate, author, description, content }; }); console.log(`Found ${items.length} releases`); // Sort by date (newest first) return items.sort((a, b) => { if (!a.pubDate || !b.pubDate) return 0; return new Date(b.pubDate) - new Date(a.pubDate); }); } function displayRSSItems(items, container, showLegacyContainer, showLegacyBtn) { if (!items.length) { container.innerHTML = "No releases found.
"; return; } console.log("Displaying items:", items); const visibleItems = 2; const hasMoreItems = items.length > visibleItems; let html = "