/* =================================================================== Der eigene Steckbrief -- Bild, ein Satz ueber sich, die Kanaele. Laeuft auf ZWEI Seiten: steckbrief.html fuer Scouts, Manager und DogFather -- dort ist er der ganze Inhalt profil.html fuer Creator -- dort steht er ueber der eigenen Betreuungsakte Warum eine eigene Datei: Vorher hing er hinten an profil.js. Damit war er an die Creator-Akte gekettet, und auf der Profilseite standen zwei Personen gleichzeitig -- oben "Profil: SpongBobSchwammKopf" und mittendrin "MEIN PROFIL: Dogfather". Er gehoert aber nicht zur Akte, sondern zur angemeldeten Person. Der Block laeuft fuer sich: Faellt die Akte aus, steht der Steckbrief weiterhin -- und umgekehrt. =================================================================== */ (() => { 'use strict'; const $ = (id) => document.getElementById(id); const block = $('steckbrief'); if (!block) return; /* Auf profil.html traegt der Block data-nur-creator: Dort steht die Akte eines ANDEREN Menschen, und der eigene Steckbrief hat daneben nichts zu suchen -- ausser bei einem Creator, der nur diese eine Seite hat. Wer ihn nicht sehen soll, bekommt ihn gar nicht erst geladen; die Abfrage unterbleibt ebenfalls. */ let nurCreator = block.dataset.nurCreator === 'ja'; let stand = null; async function hole(pfad, optionen = {}) { const a = await fetch(pfad, { credentials: 'same-origin', ...optionen }); if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); } return a; } const melde = (t) => { $('sb-fehler').textContent = t || ''; }; const sage = (t) => { $('sb-stand').textContent = t || ''; if (t) setTimeout(() => { if ($('sb-stand').textContent === t) $('sb-stand').textContent = ''; }, 4000); }; function bildZeigen(quelle, name) { const feld = $('sb-bild'); feld.textContent = ''; if (quelle) { const b = document.createElement('img'); b.src = quelle; b.alt = ''; /* Fällt das Bild aus, erscheint der Buchstabe -- nicht das kaputte Bildsymbol des Browsers. */ b.addEventListener('error', () => { b.remove(); feld.textContent = anfang(name); }); feld.append(b); $('sb-bild-weg').hidden = false; } else { feld.textContent = anfang(name); $('sb-bild-weg').hidden = true; } } const anfang = (n) => (n || '?').trim().charAt(0).toUpperCase(); function kanaeleBauen(kanaele, vorhanden) { const ziel = $('sb-kanaele'); ziel.textContent = ''; for (const k of kanaele) { const zeile = document.createElement('div'); zeile.className = 'kanal'; zeile.dataset.art = k.art; const schild = document.createElement('label'); schild.className = 'kanal__name'; schild.setAttribute('for', 'sb-' + k.art); schild.textContent = k.name; const feld = document.createElement('div'); feld.className = 'kanal__feld'; const at = document.createElement('span'); at.className = 'kanal__at'; at.setAttribute('aria-hidden', 'true'); at.textContent = '@'; const eingabe = document.createElement('input'); eingabe.id = 'sb-' + k.art; eingabe.type = 'text'; eingabe.maxLength = 40; eingabe.autocomplete = 'off'; eingabe.placeholder = 'dein Name'; const da = vorhanden.find((v) => v.art === k.art); eingabe.value = da ? da.handle : ''; feld.append(at, eingabe); zeile.append(schild, feld); /* Ein Verweis erscheint erst, wenn wirklich ein Name da ist -- ein Knopf, der ins Leere führt, ist schlimmer als keiner. */ if (da) { const a = document.createElement('a'); a.className = 'kanal__auf'; a.href = da.adresse; a.target = '_blank'; a.rel = 'noopener noreferrer'; a.textContent = 'öffnen'; zeile.append(a); } ziel.append(zeile); } } function zeichne(sb, kanaele) { stand = sb; $('sb-name').textContent = sb.name; $('sb-rolle').textContent = sb.rolle_name; $('sb-rolle').dataset.rolle = sb.rolle; $('sb-ueber').value = sb.ueber_mich || ''; bildZeigen(sb.bild, sb.name); kanaeleBauen(kanaele, sb.kanaele); block.hidden = false; } /* ---------- Bild hochladen --------------------------------------------- Roh im Rumpf statt als multipart -- dasselbe Verfahren wie in der Dateiablage. Kein Parsen von multipart im Server, keine zusätzliche Abhängigkeit. */ $('sb-datei').addEventListener('change', async (e) => { const datei = e.target.files?.[0]; if (!datei) return; melde(''); if (datei.size > 4 * 1024 * 1024) { melde('Das Bild ist größer als 4 MB. Bitte ein kleineres nehmen.'); e.target.value = ''; return; } sage('Bild wird hochgeladen …'); try { const a = await hole('/workspace/api/steckbrief/bild', { method: 'POST', headers: { 'Content-Type': 'application/octet-stream' }, body: datei, }); const antwort = await a.json().catch(() => ({})); if (!a.ok) { melde(antwort.fehler || 'Das Hochladen ging nicht.'); sage(''); return; } bildZeigen(antwort.bild, stand?.name); sage('Bild gespeichert.'); /* Auch oben rechts in der Kopfleiste soll sofort das neue Bild stehen -- sonst müsste man die Seite neu laden, um es zu sehen. */ const oben = document.querySelector('.wer__zeichen'); if (oben) { oben.querySelector('.wer__bild')?.remove(); const b = document.createElement('img'); b.className = 'wer__bild'; b.src = antwort.bild; b.alt = ''; oben.append(b); } } catch { /* umgeleitet */ } finally { e.target.value = ''; } }); $('sb-bild-weg').addEventListener('click', async () => { if (!confirm('Profilbild entfernen?')) return; try { const a = await hole('/workspace/api/steckbrief/bild', { method: 'DELETE' }); if (!a.ok) { melde('Entfernen ging nicht.'); return; } bildZeigen(null, stand?.name); document.querySelector('.wer__bild')?.remove(); sage('Bild entfernt.'); } catch { /* umgeleitet */ } }); /* ---------- Speichern ---------------------------------------------------- */ $('sb-speichern').addEventListener('click', async () => { melde(''); const knopf = $('sb-speichern'); const koerper = { ueber_mich: $('sb-ueber').value }; for (const zeile of document.querySelectorAll('#sb-kanaele .kanal')) { koerper[zeile.dataset.art] = zeile.querySelector('input').value; } knopf.disabled = true; try { const a = await hole('/workspace/api/steckbrief/mein', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(koerper), }); const antwort = await a.json().catch(() => ({})); if (!a.ok) { melde(antwort.fehler || 'Speichern ging nicht.'); return; } /* Neu zeichnen, damit die "öffnen"-Verweise zu den frisch eingetragenen Namen sofort erscheinen. */ zeichne(antwort.steckbrief, KANAELE); sage('Profil gespeichert.'); } catch { /* umgeleitet */ } finally { knopf.disabled = false; } }); let KANAELE = []; (async () => { try { const ich = await (await hole('/workspace/api/ich')).json().catch(() => null); if (nurCreator && ich && ich.rolle !== 'creator') return; const a = await hole('/workspace/api/steckbrief/mein'); if (!a.ok) return; const { steckbrief, kanaele } = await a.json(); KANAELE = kanaele; zeichne(steckbrief, kanaele); } catch { /* umgeleitet oder nicht verfügbar -- die Seite bleibt nutzbar */ } })(); })();