let currentQuery = 'tag:inbox'; let isLoading = false; async function api(endpoint) { console.log('API call:', endpoint); const res = await fetch(`/api/${endpoint}`); console.log('Response status:', res.status); if (!res.ok) { const text = await res.text(); console.error('API error response:', text); throw new Error(`API error: ${res.status}`); } const data = await res.json(); console.log('API response:', data); return data; } function setStatus(state) { const status = document.getElementById('status'); status.className = state === 'loading' ? 'status-loading' : state === 'ok' ? 'status-ok' : 'status-error'; } function setLoading(loading) { isLoading = loading; const btn = document.querySelector('.search-bar button'); btn.disabled = loading; btn.textContent = loading ? 'Loading...' : 'Search'; } async function search() { if (isLoading) return; const query = document.getElementById('search').value; currentQuery = query; history.pushState({ query }, '', `/?q=${encodeURIComponent(query)}`); await loadThreads(query); } async function loadThreads(query) { setLoading(true); setStatus('loading'); const list = document.getElementById('thread-list'); try { const threads = await api(`query/${encodeURIComponent(query)}`); if (!threads || threads.length === 0) { list.innerHTML = '
No threads found
'; } else { list.innerHTML = threads.map(t => `
${escapeHtml(t.subject)}
${escapeHtml(t.authors)}
${new Date(t.newest_date * 1000).toLocaleString()}
`).join(''); } setStatus('ok'); } catch (e) { console.error('Error in loadThreads:', e); list.innerHTML = `
Error loading threads: ${escapeHtml(e.message)}
`; setStatus('error'); } finally { setLoading(false); } } async function loadThread(threadId) { setStatus('loading'); const view = document.getElementById('message-view'); view.innerHTML = '
Loading messages...
'; try { const messages = await api(`thread/${threadId}`); view.innerHTML = messages.map(m => `
From: ${escapeHtml(m.from || '')}
To: ${escapeHtml(m.to || '')}
Date: ${escapeHtml(m.date || '')}
Subject: ${escapeHtml(m.subject || '')}
`).join(''); setStatus('ok'); } catch (e) { console.error('Error in loadThread:', e); view.innerHTML = `
Error loading thread: ${escapeHtml(e.message)}
`; setStatus('error'); } } async function loadMessageContent(messageId) { setStatus('loading'); const div = document.getElementById(`msg-${messageId}`); div.innerHTML = '
Loading content...
'; try { const msg = await api(`message/${messageId}`); div.innerHTML = `
${escapeHtml(msg.content_type)}
${msg.content_type === 'text/html' ? msg.content : `
${escapeHtml(msg.content)}
`}
${msg.attachments.length ? `
Attachments: ${msg.attachments.map(a => escapeHtml(a.filename)).join(', ')}
` : ''} `; setStatus('ok'); } catch (e) { console.error('Error in loadMessageContent:', e); div.innerHTML = `
Error loading message: ${escapeHtml(e.message)}
`; setStatus('error'); } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize window.addEventListener('DOMContentLoaded', () => { console.log('App initialized'); const params = new URLSearchParams(location.search); const query = params.get('q') || 'tag:inbox'; document.getElementById('search').value = query; loadThreads(query); // Allow Enter key to search document.getElementById('search').addEventListener('keypress', (e) => { if (e.key === 'Enter') search(); }); }); window.addEventListener('popstate', (e) => { if (e.state?.query) { document.getElementById('search').value = e.state.query; loadThreads(e.state.query); } });