// Fetch and display feeds async function fetchFeeds() { try { const response = await fetch('/feeds'); if (response.ok) { const feeds = await response.json(); return feeds; } else { console.error('Failed to load feeds:', response.status); return null; } } catch (error) { console.error('Error loading feeds:', error); return null; } } // Close any open feed menus function closeAllFeedMenus() { document.querySelectorAll('.feed-menu.show').forEach(menu => { menu.classList.remove('show'); }); } // Add click handler to close menus when clicking outside document.addEventListener('click', (e) => { if (!e.target.closest('.feed-menu') && !e.target.closest('.feed-menu-button')) { closeAllFeedMenus(); } }); function renderFeedItem(feed) { const container = document.createElement('div'); container.style.position = 'relative'; const name = document.createElement('span'); name.className = 'feed-name'; name.textContent = feed.name; name.onclick = () => { // TODO: Handle feed click console.log('Feed clicked:', feed); }; const menuButton = document.createElement('button'); menuButton.className = 'feed-menu-button'; menuButton.innerHTML = '⋮'; menuButton.title = 'Feed options'; menuButton.onclick = (e) => { e.stopPropagation(); closeAllFeedMenus(); menu.classList.toggle('show'); }; const menu = document.createElement('div'); menu.className = 'feed-menu'; const deleteItem = document.createElement('a'); deleteItem.className = 'feed-menu-item delete'; deleteItem.textContent = 'Remove Feed'; deleteItem.onclick = async (e) => { e.stopPropagation(); if (confirm(`Are you sure you want to delete "${feed.name}"?`)) { try { const response = await fetch(`/feeds/${feed.feed_id}`, { method: 'DELETE', }); if (response.ok) { handleFeeds(); } else { console.error('Failed to delete feed:', response.status); } } catch (error) { console.error('Error deleting feed:', error); } } menu.classList.remove('show'); }; menu.appendChild(deleteItem); name.appendChild(menuButton); name.appendChild(menu); container.appendChild(name); return container; } async function handleFeeds() { const feeds = await fetchFeeds(); const feedList = document.getElementById('feedList'); if (feeds) { feedList.innerHTML = ''; if (feeds.length === 0) { const emptyMessage = document.createElement('div'); emptyMessage.className = 'feed-empty'; emptyMessage.textContent = 'No feeds added yet'; feedList.appendChild(emptyMessage); } else { feeds.forEach(feed => { feedList.appendChild(renderFeedItem(feed)); }); } } else { feedList.innerHTML = '
Failed to load feeds
'; } } // Load feeds when page loads document.addEventListener('DOMContentLoaded', handleFeeds); // Logout functionality document.getElementById('logoutButton').addEventListener('click', async () => { try { const response = await fetch('/logout', { method: 'POST', }); if (response.ok) { window.location.href = '/login'; } } catch (error) { console.error('Logout failed:', error); } }); // Modal functionality const modal = document.getElementById('addFeedModal'); const addFeedButton = document.getElementById('addFeedButton'); const cancelButton = document.getElementById('cancelAddFeed'); const confirmButton = document.getElementById('confirmAddFeed'); const addFeedForm = document.getElementById('addFeedForm'); const errorMessage = document.getElementById('feedErrorMessage'); const loadingMessage = document.getElementById('loadingMessage'); function showModal() { modal.classList.add('show'); errorMessage.style.display = 'none'; loadingMessage.style.display = 'none'; addFeedForm.reset(); confirmButton.disabled = false; } function hideModal() { modal.classList.remove('show'); errorMessage.style.display = 'none'; loadingMessage.style.display = 'none'; addFeedForm.reset(); confirmButton.disabled = false; } function showError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; loadingMessage.style.display = 'none'; confirmButton.disabled = false; } function showLoading() { loadingMessage.style.display = 'block'; errorMessage.style.display = 'none'; confirmButton.disabled = true; } addFeedButton.addEventListener('click', showModal); cancelButton.addEventListener('click', hideModal); // Close modal when clicking outside modal.addEventListener('click', (e) => { if (e.target === modal) { hideModal(); } }); confirmButton.addEventListener('click', async () => { const url = document.getElementById('feedUrl').value.trim(); if (!url) { showError('Please enter a feed URL'); return; } showLoading(); try { const response = await fetch('/feeds', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ url }), }); if (response.ok) { hideModal(); // Refresh the feed list handleFeeds(); } else { switch (response.status) { case 409: showError('You already have this feed added'); break; case 422: showError('Invalid feed URL. Please make sure it\'s a valid RSS or Atom feed.'); break; default: showError('Failed to add feed. Please try again.'); } } } catch (error) { console.error('Add feed failed:', error); showError('An error occurred. Please try again.'); } });