All checks were successful
Build Tauri App (Linux + Windows exe) / build (push) Successful in 11m34s
262 lines
10 KiB
JavaScript
262 lines
10 KiB
JavaScript
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 = "<p>Loading release notes...</p>";
|
|
|
|
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 = `
|
|
<div style="padding: 1rem; background: rgba(255, 100, 100, 0.1); border: 1px solid rgba(255, 100, 100, 0.3); border-radius: 6px; text-align: center;">
|
|
<p style="margin: 0 0 0.5rem 0;"><strong>Unable to load release notes</strong></p>
|
|
<p style="font-size: 0.9em; margin: 0.5rem 0; color: #ccc;">
|
|
Network issue detected.
|
|
</p>
|
|
<div style="display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; margin-top: 1rem;">
|
|
<button onclick="loadRSS()" style="padding: 0.4rem 0.8rem; background: #00ff88; color: black; border: none; border-radius: 4px; cursor: pointer; font-weight: 500;">
|
|
Retry
|
|
</button>
|
|
<a href="${rssUrl}" target="_blank" rel="noopener" style="padding: 0.4rem 0.8rem; background: #333; color: #00ff88; text-decoration: none; border-radius: 4px; display: inline-block;">
|
|
View Direct
|
|
</a>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
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 = "<p style='text-align: center; color: #888;'>No releases found.</p>";
|
|
return;
|
|
}
|
|
|
|
console.log("Displaying items:", items);
|
|
|
|
const visibleItems = 2;
|
|
const hasMoreItems = items.length > visibleItems;
|
|
|
|
let html = "<div class='release-list' style='display: flex; flex-direction: column; gap: 1rem;'>";
|
|
|
|
items.forEach((item, index) => {
|
|
const isHidden = index >= visibleItems;
|
|
|
|
// Format the date nicely
|
|
let formattedDate = "";
|
|
if (item.pubDate) {
|
|
try {
|
|
const date = new Date(item.pubDate);
|
|
formattedDate = date.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
} catch (e) {
|
|
formattedDate = item.pubDate;
|
|
}
|
|
}
|
|
|
|
html += `
|
|
<div class="release-item ${isHidden ? 'legacy-release' : ''}"
|
|
style="
|
|
${isHidden ? 'display: none;' : ''}
|
|
padding: 1rem;
|
|
background: linear-gradient(135deg, rgba(0, 255, 136, 0.05) 0%, rgba(0, 255, 136, 0.02) 100%);
|
|
border: 1px solid rgba(0, 255, 136, 0.2);
|
|
border-radius: 8px;
|
|
border-left: 4px solid #00ff88;
|
|
transition: all 0.3s ease;
|
|
"
|
|
onmouseover="this.style.background='linear-gradient(135deg, rgba(0, 255, 136, 0.08) 0%, rgba(0, 255, 136, 0.04) 100%)'; this.style.transform='translateY(-1px)'"
|
|
onmouseout="this.style.background='linear-gradient(135deg, rgba(0, 255, 136, 0.05) 0%, rgba(0, 255, 136, 0.02) 100%)'; this.style.transform='translateY(0px)'">
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem; flex-wrap: wrap; gap: 0.5rem;">
|
|
<h4 style="margin: 0; color: #00ff88; font-size: 1.1rem; font-weight: 600;">
|
|
<a href="${item.link}"
|
|
target="_blank"
|
|
rel="noopener"
|
|
style="color: inherit; text-decoration: none;"
|
|
onmouseover="this.style.textDecoration='underline'"
|
|
onmouseout="this.style.textDecoration='none'">
|
|
${item.title}
|
|
</a>
|
|
</h4>
|
|
${formattedDate ? `
|
|
<span style="color: #888; font-size: 0.85rem; white-space: nowrap;">
|
|
${formattedDate}
|
|
</span>
|
|
` : ''}
|
|
</div>
|
|
|
|
${item.author !== 'Unknown' ? `
|
|
<div style="color: #666; font-size: 0.8rem; margin-bottom: 0.5rem;">
|
|
by ${item.author}
|
|
</div>
|
|
` : ''}
|
|
|
|
${item.content ? `
|
|
<div style="color: #ccc; font-size: 0.9rem; line-height: 1.4; margin-top: 0.5rem;">
|
|
${item.content}
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
html += "</div>";
|
|
container.innerHTML = html;
|
|
|
|
// Setup legacy releases toggle
|
|
if (hasMoreItems && showLegacyContainer && showLegacyBtn) {
|
|
showLegacyContainer.style.display = 'block';
|
|
|
|
// Remove existing listeners by cloning
|
|
const newBtn = showLegacyBtn.cloneNode(true);
|
|
showLegacyBtn.parentNode.replaceChild(newBtn, showLegacyBtn);
|
|
|
|
let showingLegacy = false;
|
|
newBtn.addEventListener('click', function() {
|
|
showingLegacy = !showingLegacy;
|
|
const legacyItems = container.querySelectorAll('.legacy-release');
|
|
|
|
legacyItems.forEach(item => {
|
|
if (showingLegacy) {
|
|
item.style.display = 'block';
|
|
item.style.animation = 'fadeIn 0.3s ease-in';
|
|
} else {
|
|
item.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
newBtn.textContent = showingLegacy ?
|
|
'Hide Legacy Releases' : `Show ${items.length - visibleItems} More Releases`;
|
|
});
|
|
|
|
// Update button text to show count
|
|
newBtn.textContent = `Show ${items.length - visibleItems} More Releases`;
|
|
}
|
|
|
|
console.log("RSS items displayed successfully");
|
|
}
|
|
|
|
// Add some CSS for animations
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(-10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
// Initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', loadRSS);
|
|
} else {
|
|
// DOM is already loaded
|
|
loadRSS();
|
|
}
|
|
|
|
// Make loadRSS available globally for retry button
|
|
window.loadRSS = loadRSS; |