RSS_News / templates /index.html
broadfield-dev's picture
Create templates/index.html
1b3ff93 verified
raw
history blame
16.9 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News Feed Hub</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; color: #333; }
h1 { text-align: center; color: #2c3e50; }
.search-container { text-align: center; margin: 20px 0; }
.search-bar { width: 50%; padding: 10px; border: 2px solid #3498db; border-radius: 25px; }
.category-section { margin: 20px 0; }
.category-title { background-color: #3498db; color: white; padding: 10px; border-radius: 5px; cursor: pointer; }
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
.article-tile { background: white; height: 350px; overflow-y: clip; padding: 15px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
.article-tile img, .article-tile svg { width: 100%; height: 150px; object-fit: cover; border-radius: 5px; }
.title a { font-size: 1.1em; color: #2c3e50; text-decoration: none; }
.title a:hover { color: #3498db; }
.description { color: #555; font-size: 0.9em; height: 150px; overflow-y: clip; }
.published { font-size: 0.8em; color: #95a5a6; }
.no-articles { text-align: center; color: #2c3e50; margin-top: 20px; }
.loading-message { text-align: center; color: #3498db; margin: 10px 0; }
.loading-spinner { display: none; border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; margin-left: 10px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body>
<h1>News Feed Hub</h1>
<div class="search-container">
<form method="POST" action="/search" id="searchForm">
<input type="text" name="search" class="search-bar" placeholder="Search news...">
</form>
<button id="backButton" style="display: none; margin-top: 10px;" onclick="window.location.href='/back_to_main'">Back to Main</button>
</div>
{% if loading %}
<div class="loading-message">Fetching new RSS feeds in the background...</div>
{% endif %}
{% if has_articles %}
{% for category, articles in categorized_articles.items() %}
<div class="category-section">
<div class="category-title" onclick="toggleCategory('{{ category }}')">{{ category }} <span class="loading-spinner" id="spinner-{{ category }}"></span></div>
<div class="tiles" id="category-{{ category }}" data-last-update="0">
{% for article in articles %}
<div class="article-tile" data-published="{{ article.published }}" data-id="{{ loop.index }}">
{% if article.image != "svg" %}
<img src="{{ article.image }}" alt="Article Image">
{% else %}
<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#e0e0e0"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#666">No Image</text>
</svg>
{% endif %}
<div class="title"><a href="{{ article.link }}" target="_blank">{{ article.title }}</a></div>
<div class="description">{{ article.description }}</div>
<div class="published">Published: {{ article.published }}</div>
</div>
{% endfor %}
</div>
<div class="tiles" id="all-{{ category }}" style="display: none;"></div>
</div>
{% endfor %}
{% else %}
<div class="no-articles">No articles available yet. Loading new feeds...</div>
{% endif %}
{% if loading %}
<script>
let lastUpdate = 0;
let expandedCategories = new Set();
function toggleCategory(category) {
const spinner = document.getElementById(`spinner-${category}`);
const tilesDiv = document.getElementById(`category-${category}`);
const allTilesDiv = document.getElementById(`all-${category}`);
if (expandedCategories.has(category)) {
// Collapse: show only 10 articles
tilesDiv.style.display = 'grid';
allTilesDiv.style.display = 'none';
expandedCategories.delete(category);
} else {
// Expand: load all articles
spinner.style.display = 'inline-block';
fetch(`/get_all_articles/${category}`)
.then(response => response.json())
.then(data => {
spinner.style.display = 'none';
if (data.articles.length > 0) {
let html = '';
data.articles.sort((a, b) => new Date(b.published) - new Date(a.published));
data.articles.forEach((article, index) => {
html += `
<div class="article-tile" data-published="${article.published}" data-id="${index}">
${article.image !== "svg" ? `<img src="${article.image}" alt="Article Image">` : `
<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#e0e0e0"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#666">No Image</text>
</svg>
`}
<div class="title"><a href="${article.link}" target="_blank">${article.title}</a></div>
<div class="description">${article.description}</div>
<div class="published">Published: ${article.published}</div>
</div>
`;
});
tilesDiv.style.display = 'none';
allTilesDiv.innerHTML = html;
allTilesDiv.style.display = 'grid';
expandedCategories.add(category);
} else {
alert(`No additional articles found for ${category}.`);
}
})
.catch(error => {
spinner.style.display = 'none';
console.error(`Error loading all articles for ${category}:`, error);
alert(`Failed to load all articles for ${category}. Please try again.`);
});
}
}
function updateArticles() {
fetch('/get_updates')
.then(response => response.json())
.then(data => {
if (data.articles && data.last_update > lastUpdate) {
lastUpdate = data.last_update;
const newArticles = data.articles;
for (const [category, articles] of Object.entries(newArticles)) {
const tilesDiv = document.getElementById(`category-${category}`);
const allTilesDiv = document.getElementById(`all-${category}`);
if (tilesDiv) {
const existingArticles = Array.from(tilesDiv.querySelectorAll('.article-tile'));
let currentIds = new Set(existingArticles.map(a => a.dataset.id));
let newHtml = '';
// Sort new articles by published date
articles.sort((a, b) => new Date(b.published) - new Date(a.published));
// Add new articles to limited view (up to 10, keeping most recent)
articles.slice(0, 10).forEach((article, index) => {
if (!currentIds.has(index.toString())) {
newHtml += `
<div class="article-tile" data-published="${article.published}" data-id="${index}">
${article.image !== "svg" ? `<img src="${article.image}" alt="Article Image">` : `
<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#e0e0e0"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#666">No Image</text>
</svg>
`}
<div class="title"><a href="${article.link}" target="_blank">${article.title}</a></div>
<div class="description">${article.description}</div>
<div class="published">Published: ${article.published}</div>
</div>
`;
}
});
// Update limited view (up to 10)
if (newHtml) {
tilesDiv.innerHTML = (existingArticles.map(a => a.outerHTML).join('') + newHtml);
const allLimited = Array.from(tilesDiv.querySelectorAll('.article-tile'));
tilesDiv.innerHTML = allLimited.sort((a, b) => new Date(b.dataset.published) - new Date(a.dataset.published)).slice(0, 10).map(a => a.outerHTML).join('');
}
// Update expanded view if visible
if (allTilesDiv.style.display === 'grid') {
let allNewHtml = '';
articles.forEach((article, index) => {
allNewHtml += `
<div class="article-tile" data-published="${article.published}" data-id="${index}">
${article.image !== "svg" ? `<img src="${article.image}" alt="Article Image">` : `
<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#e0e0e0"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#666">No Image</text>
</svg>
`}
<div class="title"><a href="${article.link}" target="_blank">${article.title}</a></div>
<div class="description">${article.description}</div>
<div class="published">Published: ${article.published}</div>
</div>
`;
});
allTilesDiv.innerHTML = (Array.from(allTilesDiv.querySelectorAll('.article-tile')).map(a => a.outerHTML).join('') + allNewHtml);
const allExpanded = Array.from(allTilesDiv.querySelectorAll('.article-tile'));
allTilesDiv.innerHTML = allExpanded.sort((a, b) => new Date(b.dataset.published) - new Date(a.dataset.published)).map(a => a.outerHTML).join('');
}
}
}
document.querySelector('.loading-message').style.display = 'none';
}
setTimeout(updateArticles, 2000); // Check every 2 seconds
})
.catch(error => {
console.error('Error updating articles:', error);
setTimeout(updateArticles, 2000); // Retry on error
});
}
document.addEventListener('DOMContentLoaded', () => {
const tiles = document.querySelectorAll('.tiles');
tiles.forEach(tile => {
lastUpdate = Math.max(lastUpdate, parseFloat(tile.dataset.lastUpdate) || 0);
});
updateArticles();
// Handle semantic search
const form = document.getElementById('searchForm');
const backButton = document.getElementById('backButton');
form.addEventListener('submit', (event) => {
event.preventDefault();
fetch('/search', {
method: 'POST',
body: new FormData(form)
})
.then(response => response.json())
.then(data => {
if (data.has_articles) {
document.querySelector('.search-container').style.display = 'none';
backButton.style.display = 'block';
const categoriesHtml = Object.entries(data.categorized_articles).map(([cat, articles]) => `
<div class="category-section">
<div class="category-title" onclick="toggleCategory('${cat}')">${cat} <span class="loading-spinner" id="spinner-${cat}"></span></div>
<div class="tiles" id="category-${cat}" data-last-update="0">
${articles.map((article, index) => `
<div class="article-tile" data-published="${article.published}" data-id="${index}">
${article.image !== "svg" ? `<img src="${article.image}" alt="Article Image">` : `
<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#e0e0e0"/>
<text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#666">No Image</text>
</svg>
`}
<div class="title"><a href="${article.link}" target="_blank">${article.title}</a></div>
<div class="description">${article.description}</div>
<div class="published">Published: ${article.published}</div>
</div>
`).join('')}
</div>
<div class="tiles" id="all-${cat}" style="display: none;"></div>
</div>
`).join('');
document.querySelector('body').innerHTML = `
<h1>News Feed Hub</h1>
<div class="search-container" style="display: none;">
<form method="POST" action="/search" id="searchForm">
<input type="text" name="search" class="search-bar" placeholder="Search news...">
</form>
<button id="backButton" style="display: block; margin-top: 10px;" onclick="window.location.href='/back_to_main'">Back to Main</button>
</div>
${categoriesHtml}
${loading ? '<div class="loading-message">Fetching new RSS feeds in the background...</div>' : ''}
`;
} else {
alert('No results found.');
}
})
.catch(error => {
console.error('Error performing search:', error);
alert('Failed to perform search. Please try again.');
});
});
});
</script>
{% endif %}
</body>
</html>