124 lines
4.2 KiB
HTML
124 lines
4.2 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>Thread Posts — Liste</title>
|
|
<style>
|
|
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;line-height:1.4;margin:0;padding:20px;background:#f7f7f8;color:#111}
|
|
.wrap{max-width:900px;margin:0 auto}
|
|
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
|
|
h1{font-size:18px;margin:0}
|
|
.controls{display:flex;gap:8px}
|
|
.post{background:#fff;border:1px solid #e3e3e6;padding:12px 14px;margin-bottom:12px;border-radius:6px}
|
|
.meta{font-size:13px;color:#555;margin-bottom:8px;display:flex;gap:12px;flex-wrap:wrap}
|
|
.text{white-space:pre-wrap;font-size:15px}
|
|
.small{font-size:13px;color:#666}
|
|
.file-load{display:none;margin-top:8px}
|
|
footer{margin-top:20px;color:#666;font-size:13px}
|
|
button{padding:6px 10px;border-radius:6px;border:1px solid #bbb;background:#fff;cursor:pointer}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<header>
|
|
<h1>Thread Posts — alle Beiträge untereinander</h1>
|
|
<div class="controls">
|
|
<button id="reload">Neu laden</button>
|
|
<button id="openFileBtn">Lokale JSON laden</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div id="status" class="small">Versuche, <code>thread_posts.json</code> zu laden…</div>
|
|
|
|
<div id="fileLoad" class="file-load">
|
|
<input id="fileInput" type="file" accept="application/json">
|
|
</div>
|
|
|
|
<main id="list"></main>
|
|
|
|
<footer>Hinweis: Wenn Sie die Datei lokal öffnen und `fetch` fehlschlägt, nutzen Sie bitte die Schaltfläche "Lokale JSON laden" oder starten einen kleinen HTTP-Server.</footer>
|
|
</div>
|
|
|
|
<script>
|
|
const status = document.getElementById('status');
|
|
const list = document.getElementById('list');
|
|
const fileLoad = document.getElementById('fileLoad');
|
|
const fileInput = document.getElementById('fileInput');
|
|
const reloadBtn = document.getElementById('reload');
|
|
const openFileBtn = document.getElementById('openFileBtn');
|
|
|
|
function renderPosts(posts){
|
|
list.innerHTML = '';
|
|
if(!Array.isArray(posts)){
|
|
status.textContent = 'JSON hat kein Array von Posts.';
|
|
return;
|
|
}
|
|
|
|
status.textContent = `Beiträge: ${posts.length}`;
|
|
|
|
posts.forEach(p => {
|
|
const el = document.createElement('article');
|
|
el.className = 'post';
|
|
|
|
const meta = document.createElement('div');
|
|
meta.className = 'meta';
|
|
meta.innerHTML = `
|
|
<strong>${escapeHtml(String(p.author||'—'))}</strong>
|
|
<span>${escapeHtml(String(p.timestamp||'—'))}</span>
|
|
<span>Likes: ${escapeHtml(String(p.likes||0))}</span>
|
|
<span>ID: ${escapeHtml(String(p.post_id||'—'))}</span>
|
|
<span>Seite: ${escapeHtml(String(p.page||1))}</span>
|
|
`;
|
|
|
|
const body = document.createElement('div');
|
|
body.className = 'text';
|
|
body.textContent = p.text || '';
|
|
|
|
el.appendChild(meta);
|
|
el.appendChild(body);
|
|
list.appendChild(el);
|
|
});
|
|
}
|
|
|
|
function escapeHtml(s){
|
|
return s.replace(/[&<>\"]/g, c => ({'&':'&','<':'<','>':'>','"':'"'}[c]));
|
|
}
|
|
|
|
async function tryFetch(){
|
|
try{
|
|
const resp = await fetch('thread_posts.json', {cache: 'no-store'});
|
|
if(!resp.ok) throw new Error('HTTP ' + resp.status);
|
|
const data = await resp.json();
|
|
renderPosts(data);
|
|
}catch(e){
|
|
console.warn('fetch failed', e);
|
|
status.textContent = 'Laden per fetch fehlgeschlagen — wähle Datei manuell.';
|
|
fileLoad.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
fileInput.addEventListener('change', e => {
|
|
const f = e.target.files && e.target.files[0];
|
|
if(!f) return;
|
|
const r = new FileReader();
|
|
r.onload = ev => {
|
|
try{
|
|
const data = JSON.parse(ev.target.result);
|
|
renderPosts(data);
|
|
}catch(err){
|
|
status.textContent = 'Ungültiges JSON.';
|
|
}
|
|
};
|
|
r.readAsText(f, 'utf-8');
|
|
});
|
|
|
|
openFileBtn.addEventListener('click', ()=> fileInput.click());
|
|
reloadBtn.addEventListener('click', ()=> { status.textContent = 'Neu laden…'; tryFetch(); });
|
|
|
|
// initial
|
|
tryFetch();
|
|
</script>
|
|
</body>
|
|
</html>
|