'use strict'; const tweetsEl = document.getElementById('tweets'); const refreshBtn = document.getElementById('refresh'); const tweetForm = document.getElementById('tweet-form'); const statusEl = document.getElementById('status'); const template = document.getElementById('tweet-template'); function fmtTime(iso) { const d = new Date(iso); return d.toLocaleString(); } async function api(path, options = {}) { const res = await fetch(path, { headers: { 'Content-Type': 'application/json' }, ...options }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || `http_${res.status}`); return data; } function renderTweets(tweets) { tweetsEl.innerHTML = ''; for (const tweet of tweets) { const node = template.content.firstElementChild.cloneNode(true); node.dataset.tweetId = String(tweet.id); node.querySelector('.author').textContent = `@${tweet.author}`; node.querySelector('.time').textContent = fmtTime(tweet.createdAt); node.querySelector('.tweet-text').textContent = tweet.text; node.querySelector('.likes').textContent = `${tweet.likes} likes`; const likeBtn = node.querySelector('.like-btn'); likeBtn.addEventListener('click', async () => { try { await api(`/api/tweets/${tweet.id}/like`, { method: 'POST' }); await loadTweets(); } catch (err) { statusEl.textContent = `Like failed: ${err.message}`; } }); const repliesEl = node.querySelector('.replies'); for (const r of tweet.replies || []) { const li = document.createElement('li'); li.textContent = `@${r.author}: ${r.text}`; repliesEl.appendChild(li); } const replyForm = node.querySelector('.reply-form'); replyForm.addEventListener('submit', async (e) => { e.preventDefault(); const author = replyForm.querySelector('.reply-author').value.trim(); const text = replyForm.querySelector('.reply-text').value.trim(); if (!author || !text) return; try { await api(`/api/tweets/${tweet.id}/reply`, { method: 'POST', body: JSON.stringify({ author, text }) }); await loadTweets(); } catch (err) { statusEl.textContent = `Reply failed: ${err.message}`; } }); tweetsEl.appendChild(node); } } async function loadTweets() { const data = await api('/api/tweets'); renderTweets(data.tweets || []); } refreshBtn.addEventListener('click', async () => { statusEl.textContent = ''; try { await loadTweets(); } catch (err) { statusEl.textContent = `Refresh failed: ${err.message}`; } }); tweetForm.addEventListener('submit', async (e) => { e.preventDefault(); const form = new FormData(tweetForm); const author = String(form.get('author') || '').trim(); const text = String(form.get('text') || '').trim(); if (!author || !text) return; statusEl.textContent = 'Posting...'; try { await api('/api/tweets', { method: 'POST', body: JSON.stringify({ author, text }) }); tweetForm.reset(); statusEl.textContent = 'Tweet posted.'; await loadTweets(); } catch (err) { statusEl.textContent = `Post failed: ${err.message}`; } }); loadTweets().catch(err => { statusEl.textContent = `Initial load failed: ${err.message}`; });