document.addEventListener('DOMContentLoaded', () => { fetchFeeds(); fetchItems(); // Default to fetching recent items const searchInput = document.getElementById('search-input'); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { const query = searchInput.value.trim(); if (query) { document.getElementById('feed-title').textContent = `Search: ${query}`; document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); fetchItems(null, null, query); } } }); }); export async function fetchFeeds(apiBase = '') { try { const response = await fetch(`${apiBase}/api/feed/`); if (!response.ok) throw new Error('Failed to fetch feeds'); const feeds = await response.json(); renderFeeds(feeds); return feeds; } catch (err) { console.error(err); const nav = document.getElementById('feeds-nav'); if (nav) nav.innerHTML = '
Error loading feeds
'; throw err; } } export async function fetchItems(feedId = null, filter = null, query = null, apiBase = '') { const listEl = document.getElementById('entries-list'); if (listEl) listEl.innerHTML = '
Loading items...
'; let url = `${apiBase}/api/stream/`; const params = new URLSearchParams(); if (feedId) params.append('feed_id', feedId); if (filter === 'unread') params.append('read_filter', 'unread'); if (filter === 'starred') params.append('starred', 'true'); if (query) params.append('q', query); if ([...params].length > 0) { url += '?' + params.toString(); } try { const response = await fetch(url); if (!response.ok) throw new Error('Failed to fetch items'); const items = await response.json(); renderItems(items); return items; } catch (err) { console.error(err); if (listEl) listEl.innerHTML = '
Error loading items
'; throw err; } } export function renderFeeds(feeds) { const nav = document.getElementById('feeds-nav'); if (!nav) return; // Clear existing items but keep search container if present const searchContainer = nav.querySelector('.search-container'); nav.innerHTML = ''; if (searchContainer) nav.appendChild(searchContainer); const allLink = document.createElement('div'); allLink.className = 'feed-item'; allLink.textContent = 'All Items'; allLink.onclick = () => { document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); allLink.classList.add('active'); const title = document.getElementById('feed-title'); if (title) title.textContent = 'All Items'; fetchItems(); }; nav.appendChild(allLink); const unreadLink = document.createElement('div'); unreadLink.className = 'feed-item'; unreadLink.textContent = 'Unread Items'; unreadLink.onclick = () => { document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); unreadLink.classList.add('active'); const title = document.getElementById('feed-title'); if (title) title.textContent = 'Unread Items'; fetchItems(null, 'unread'); }; nav.appendChild(unreadLink); const starredLink = document.createElement('div'); starredLink.className = 'feed-item'; starredLink.textContent = 'Starred Items'; starredLink.onclick = () => { document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); starredLink.classList.add('active'); const title = document.getElementById('feed-title'); if (title) title.textContent = 'Starred Items'; fetchItems(null, 'starred'); }; nav.appendChild(starredLink); if (Array.isArray(feeds)) { feeds.forEach(feed => { const div = document.createElement('div'); div.className = 'feed-item'; div.textContent = feed.title || feed.url; div.title = feed.url; div.onclick = () => { document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); div.classList.add('active'); const title = document.getElementById('feed-title'); if (title) title.textContent = feed.title; fetchItems(feed.id); }; nav.appendChild(div); }); } } export function renderItems(items) { const list = document.getElementById('entries-list'); if (!list) return; list.innerHTML = ''; if (!items || items.length === 0) { list.innerHTML = '
No items found.
'; return; } items.forEach(item => { const article = document.createElement('article'); article.className = 'entry'; const date = new Date(item.published_at || item.created_at).toLocaleString(); article.innerHTML = `
${item.title}
${item.feed ? `${item.feed.title} • ` : ''} ${date}
${item.description || ''}
`; // Add event listeners programmatically to avoid inline onclick with modules const starBtn = article.querySelector('.btn-star'); starBtn.onclick = () => toggleStar(item.id, item.starred, starBtn); const readBtn = article.querySelector('.btn-read'); readBtn.onclick = () => toggleRead(item.id, item.read, readBtn); list.appendChild(article); }); } export async function toggleStar(id, currentStatus, btn, apiBase = '') { const newStatus = !currentStatus; try { const response = await fetch(`${apiBase}/api/item/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id, starred: newStatus }) }); if (!response.ok) throw new Error('Failed to toggle star'); // Update UI btn.textContent = newStatus ? '★' : '☆'; btn.classList.toggle('active'); btn.onclick = () => toggleStar(id, newStatus, btn, apiBase); // Update data attributes btn.dataset.starred = newStatus; return newStatus; } catch (err) { console.error(err); alert('Error toggling star'); throw err; } } export async function toggleRead(id, currentStatus, btn, apiBase = '') { const newStatus = !currentStatus; try { const response = await fetch(`${apiBase}/api/item/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id, read: newStatus }) }); if (!response.ok) throw new Error('Failed to toggle read'); // Update UI btn.textContent = newStatus ? 'Mark Unread' : 'Mark Read'; btn.classList.toggle('read'); btn.classList.toggle('unread'); btn.onclick = () => toggleRead(id, newStatus, btn, apiBase); // Update data attributes btn.dataset.read = newStatus; // Find title and dim it if read const header = btn.closest('.entry-header'); if (header) { const title = header.querySelector('.entry-title'); if (title) { if (newStatus) { title.classList.add('read'); } else { title.classList.remove('read'); } } } return newStatus; } catch (err) { console.error(err); alert('Error toggling read status'); throw err; } } export function init() { if (typeof document !== 'undefined') { // Only run if we're in a browser environment with these elements if (document.getElementById('feeds-nav')) { fetchFeeds(); fetchItems(); const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { const query = searchInput.value.trim(); if (query) { const title = document.getElementById('feed-title'); if (title) title.textContent = `Search: ${query}`; document.querySelectorAll('.feed-item').forEach(el => el.classList.remove('active')); fetchItems(null, null, query); } } }); } } } }