"use strict"; const api = { async get(url) { const r = await fetch(url, { credentials: "same-origin" }); return r.json(); }, async post(url, formData) { const r = await fetch(url, { method: "POST", body: formData, credentials: "same-origin", }); return { ok: r.ok, status: r.status, data: await r.json().catch(() => ({})) }; }, }; // --- Headerbar (eingeloggt?) --- let loggedIn = false; async function refreshHeader() { const h = await api.get("/auth/headerbar"); loggedIn = !!h.loggedin; const bar = document.getElementById("headerbar"); document.getElementById("auth-box").hidden = loggedIn; document.getElementById("compose").hidden = !loggedIn; if (loggedIn) { bar.innerHTML = ``; document.getElementById("logout-btn").addEventListener("click", async () => { await api.get("/auth/logout"); await refreshHeader(); }); } else { bar.innerHTML = ""; } } // --- Login --- document.getElementById("login-form").addEventListener("submit", async (e) => { e.preventDefault(); const res = await api.post("/auth/login", new FormData(e.target)); const msg = document.getElementById("auth-msg"); if (res.ok) { msg.textContent = ""; await refreshHeader(); } else { msg.textContent = res.data.error || "Login fehlgeschlagen."; } }); // --- Neuer Beitrag --- document.getElementById("entry-form").addEventListener("submit", async (e) => { e.preventDefault(); const res = await api.post("/entry/create", new FormData(e.target)); const msg = document.getElementById("entry-msg"); if (res.ok) { msg.textContent = ""; e.target.reset(); resetFeed(); } else { msg.textContent = res.data.error || "Beitrag fehlgeschlagen."; } }); // --- Feed mit Infinite Scroll --- let page = 0; let loading = false; let done = false; function renderEntry(e) { const el = document.createElement("article"); el.className = "entry"; const when = new Date(e.created_at * 1000).toLocaleString("de-DE"); let media = ""; if (e.filepath && e.filepath !== "none") { media = ``; } el.innerHTML = `
${escapeHtml(e.username)} · ${when}
${linkify(e.content)}
${media}
– / –
`; setupVoting(el, e.pid); return el; } function setupVoting(el, pid) { const box = el.querySelector(".interactions"); const counts = box.querySelector(".counts"); const buttons = box.querySelectorAll(".vote"); function render(state) { if (state.error) { counts.textContent = state.error; return; } counts.textContent = `${state.left} / ${state.right}`; buttons.forEach((b) => b.classList.toggle("selected", b.dataset.mode === state.selected) ); } buttons.forEach((b) => b.addEventListener("click", async () => { render(await api.get(`/entry/${pid}/interactions/${b.dataset.mode}`)); }) ); // Anfangszustand laden, ohne zu werten. api.get(`/entry/${pid}/interactions/none`).then(render); } async function loadMore() { if (loading || done) return; loading = true; const entries = await api.get(`/entry/feed/${page}`); const feed = document.getElementById("feed"); if (!entries.length) { done = true; const end = document.createElement("p"); end.className = "end"; end.textContent = "YOU REACHED THE END!"; feed.appendChild(end); } else { entries.forEach((e) => feed.appendChild(renderEntry(e))); page++; } loading = false; } function resetFeed() { page = 0; done = false; document.getElementById("feed").innerHTML = ""; loadMore(); } const observer = new IntersectionObserver((items) => { if (items.some((i) => i.isIntersecting)) loadMore(); }); observer.observe(document.getElementById("sentinel")); // --- Helpers --- function escapeHtml(s) { const d = document.createElement("div"); d.textContent = s ?? ""; return d.innerHTML; } function linkify(s) { const escaped = escapeHtml(s); const url = /https?:\/\/[^\s<]+/g; return escaped .replace(url, (m) => `${m}`) .replace(/\n/g, "
"); } // --- Start --- refreshHeader(); loadMore();