import { statistik, bildAdresse, type Statistik } from '../../api'; import { esc, fehlerzeile, meldung } from '../../util'; /** Was man geschaut hat — die eigenen Zahlen, und nur die eigenen. * * **Jeder sieht sich selbst, auch ein Administrator.** Das ist die Linie, die * im Server schon steht: wer wann welchen Film anschaut, ist eine Auskunft * über Menschen und nicht über den Server. */ export function geschautseite(): HTMLElement { const wurzel = document.createElement('div'); wurzel.className = 'profil-zahlen'; void zahlenLaden(wurzel); return wurzel; } async function zahlenLaden(ziel: HTMLElement): Promise { ziel.innerHTML = '
'; let stand: Statistik; try { stand = await statistik(); } catch (fehler) { ziel.innerHTML = ''; ziel.appendChild(fehlerzeile(meldung(fehler))); return; } ziel.innerHTML = ''; const h = document.createElement('h2'); h.textContent = 'Geschaut'; ziel.appendChild(h); // **Drei Zahlen, und jede sagt, woher sie kommt.** // // Die Gesamtzeit stammt aus dem Protokoll und fängt an dem Tag an, an dem // es eingebaut wurde. Die Zahl gesehener Titel steht seit jeher fest. Das // zu einer Zahl zu verrühren hiesse, die eine zu behaupten. const kacheln = document.createElement('div'); kacheln.className = 'zahlenreihe'; kacheln.append( zahl(stunden(stand.loggedSeconds), 'aufgezeichnet', stand.since ? `seit ${new Date(stand.since).toLocaleDateString('de-CH', { dateStyle: 'medium' })}` : 'noch nichts aufgezeichnet'), zahl(String(stand.watchedTitles), stand.watchedTitles === 1 ? 'Titel gesehen' : 'Titel gesehen', 'mit Häkchen, auch von früher'), zahl(stunden(stand.watchedRuntimeSeconds), 'Laufzeit davon', 'die Länge aller abgehakten Titel'), ); ziel.appendChild(kacheln); ziel.appendChild(wochenkurve(stand)); if (stand.genres.length > 0) ziel.appendChild(genreliste(stand)); if (stand.recent.length > 0) ziel.appendChild(zuletztliste(stand)); } /** Die Wochenkurve als Balken — zwölf Werte brauchen keine Bibliothek. */ function wochenkurve(stand: Statistik): HTMLElement { const block = document.createElement('div'); block.className = 'wochen'; const hoechstwert = Math.max(...stand.weeks.map(w => w.seconds), 1); stand.weeks.forEach(w => { const saeule = document.createElement('div'); saeule.className = 'woche'; const balken = document.createElement('div'); balken.className = 'woche-balken'; // **In Pixeln, nicht in Prozent.** Eine Prozenthöhe in einer Flexspalte // löst sich nicht gegen das auf, was man erwartet — gemessen: aus „2 %" // wurden 48 Pixel. Hundert Pixel sind der Platz, der da ist. // Mindestens zwei, damit eine leere Woche als leer zu sehen ist und // nicht als fehlend. balken.style.height = `${Math.max(2, Math.round((w.seconds / hoechstwert) * 100))}px`; // **`woche-leer`, nicht `leer`.** Den kurzen Namen gibt es schon: er // gehört der Fläche „hier ist nichts" und bringt 24 Pixel Polster mit. // Der Balken wurde dadurch 48 Pixel hoch, egal welche Höhe dastand. if (w.seconds === 0) balken.classList.add('woche-leer'); const datum = new Date(w.start); saeule.title = `Woche ab ${datum.toLocaleDateString('de-CH', { dateStyle: 'medium' })}: ` + (w.seconds > 0 ? stunden(w.seconds) : 'nichts'); const marke = document.createElement('span'); marke.className = 'woche-marke'; marke.textContent = datum.toLocaleDateString('de-CH', { day: '2-digit', month: '2-digit' }); saeule.append(balken, marke); block.appendChild(saeule); }); return block; } function genreliste(stand: Statistik): HTMLElement { const block = document.createElement('div'); block.className = 'genres'; const groesste = Math.max(...stand.genres.map(g => g.seconds), 1); stand.genres.forEach(g => { const zeileG = document.createElement('div'); zeileG.className = 'genre'; zeileG.innerHTML = `${esc(g.name)}` + `` + `${esc(stunden(g.seconds))}`; block.appendChild(zeileG); }); return block; } function zuletztliste(stand: Statistik): HTMLElement { const block = document.createElement('div'); block.className = 'zuletzt'; const h = document.createElement('h3'); h.textContent = 'Zuletzt gesehen'; block.appendChild(h); stand.recent.slice(0, 8).forEach(e => { const k = document.createElement('button'); k.type = 'button'; k.className = 'zuletzt-zeile'; const adresse = bildAdresse(e.posterImage); k.innerHTML = (adresse ? `` : '') + `${esc(e.title)}` + (e.subTitle ? `${esc(e.subTitle)}` : '') + `` + `${esc(new Date(e.at).toLocaleDateString('de-CH', { dateStyle: 'medium' }))}`; k.onclick = () => { location.hash = `#/${e.kind === 'episode' ? 'serien' : 'filme'}/${e.id}`; }; block.appendChild(k); }); return block; } function zahl(wert: string, was: string, hinweis: string): HTMLElement { const d = document.createElement('div'); d.className = 'zahlkachel'; d.innerHTML = `${esc(wert)}${esc(was)}` + `${esc(hinweis)}`; return d; } /* ---------- Bild und Name ---------- */ /** „12 Std. 30 Min." — oder „0 Min.", solange nichts dasteht. */ function stunden(sekunden: number): string { if (sekunden < 60) return '0 Min.'; const h = Math.floor(sekunden / 3600); const m = Math.round((sekunden % 3600) / 60); return h > 0 ? `${h} Std. ${m} Min.` : `${m} Min.`; }