General Overhaul of the Frontend + Functionallity for Room Searching
All checks were successful
Build Tauri App (Linux + Windows exe) / build (push) Successful in 11m34s
All checks were successful
Build Tauri App (Linux + Windows exe) / build (push) Successful in 11m34s
This commit is contained in:
262
src/rss.js
Normal file
262
src/rss.js
Normal file
@@ -0,0 +1,262 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user