diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 0000000..0a9bf79 --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,376 @@ +let currentSort = 'new'; +let currentPosts = []; +let currentPostId = null; +let selectedFile = null; + +document.addEventListener('DOMContentLoaded', () => { + loadPosts(); + setupDragDrop(); +}); + +async function apiGet(endpoint) { + const res = await fetch(endpoint); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json(); +} + +async function apiPost(endpoint, formData = null, body = null) { + let res; + if (formData) { + res = await fetch(endpoint, { method: 'POST', body: formData }); + } else { + res = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }); + } + if (!res.ok) { + const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` })); + throw new Error(err.error || `HTTP ${res.status}`); + } + return res.json(); +} + +async function loadPosts() { + const loading = document.getElementById('loading'); + loading.style.display = 'block'; + try { + currentPosts = await apiGet(`/api/posts?sort=${currentSort}`); + renderGallery(); + } catch (err) { + showToast('Failed to load posts', 'error'); + } finally { + loading.style.display = 'none'; + } +} + +function renderGallery() { + const gallery = document.getElementById('gallery'); + const count = document.getElementById('postCount'); + count.textContent = `${currentPosts.length} posts`; + if (currentPosts.length === 0) { + gallery.innerHTML = '
No comments yet. Be the first!
'; + return; + } + const topLevel = postComments.filter(c => !c.parentId); + const replies = postComments.filter(c => c.parentId); + list.innerHTML = topLevel.map(c => { + const childReplies = replies.filter(r => r.parentId === c.id); + return ` +