add initial code

This commit is contained in:
Matthias Jacob
2025-12-29 14:40:01 +01:00
parent d7dcd0cbc3
commit 2adf89dfba
5 changed files with 1726 additions and 2 deletions

123
thread_posts.html Normal file
View File

@@ -0,0 +1,123 @@
<!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 => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[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>