diff --git a/web/frontend/index.html b/web/frontend/index.html index 2e2a1f2..1695d51 100644 --- a/web/frontend/index.html +++ b/web/frontend/index.html @@ -306,6 +306,18 @@ img { display: block; } } .hexrow input:focus { outline: none; border-color: var(--accent); } .hint { font-size: 12px; color: var(--text-3); margin: var(--s-snug) 0 0; min-height: 16px; } +.rowline { + display: flex; align-items: center; gap: var(--s-snug); + padding: var(--s-snug) 0; border-bottom: 1px solid var(--separator); +} +.rowline:last-child { border-bottom: none; } +.rowline.off span { color: var(--text-3); text-decoration: line-through; } +.tiny { + width: 30px; height: 30px; border-radius: var(--r-control); + background: var(--elevated); color: var(--text-2); font-size: 14px; padding: 0; +} +.tiny:hover:not(:disabled) { color: var(--text); } +.tiny:disabled { opacity: .3; cursor: default; } .setting { background: var(--surface); border: 1px solid var(--separator); border-radius: var(--r-sheet); padding: var(--s-loose); margin-bottom: var(--s-regular); @@ -517,6 +529,10 @@ const P = { heroHeight: +(localStorage.getItem('flixy.heroHeight') || 82), combineContinue: localStorage.getItem('flixy.combineContinue') !== 'false', hideWatched: localStorage.getItem('flixy.hideWatched') === 'true', + // Welche Reihen ausgeblendet sind und in welcher Reihenfolge der Rest + // steht — dieselbe Aufteilung wie `Arrangement` in der App. + hiddenRows: JSON.parse(localStorage.getItem('flixy.hiddenRows') || '[]'), + rowOrder: JSON.parse(localStorage.getItem('flixy.rowOrder') || '[]'), }; function applyPrefs() { @@ -531,10 +547,31 @@ function applyPrefs() { function setPref(key, value) { P[key] = value; - localStorage.setItem('flixy.' + key, value); + // Listen müssen als JSON abgelegt werden — ein Array landete sonst als + // "a,b,c" im Speicher und käme als Zeichenkette zurück. + localStorage.setItem('flixy.' + key, Array.isArray(value) ? JSON.stringify(value) : value); applyPrefs(); } +/* Wendet Auswahl und Reihenfolge auf die Reihen an. + ---------------------------------------------------------------------------- + Dieselbe Regel wie `Arrangement` in der App, samt ihrer wichtigsten + Eigenschaft: **unbekannte Einträge landen am Ende statt zu verschwinden.** + Sie zu verwerfen wäre die einfachere Regel — und der Grund dafür, dass eine + neu angelegte Mediathek nie aufgetaucht wäre. */ +function applyArrangement(rows) { + const visible = rows.filter((r) => !P.hiddenRows.includes(r.id)); + if (!P.rowOrder.length) return visible; + + const at = new Map(P.rowOrder.map((id, i) => [id, i])); + return visible + .map((row, i) => ({ row, known: at.has(row.id) ? at.get(row.id) : Infinity, i })) + // Bei gleichem Rang gilt die ursprüngliche Folge — sonst springen zwei + // unbekannte Reihen bei jedem Laden gegeneinander. + .sort((a, b) => (a.known - b.known) || (a.i - b.i)) + .map((x) => x.row); +} + /* ---------- Navigation ---------- */ let views = [], current = null; @@ -816,6 +853,13 @@ function renderSettings() { Gesehenes aus den Neuzugängen ausblenden +
Welche Reihen erscheinen und in welcher Folge. Was der Server neu + anlegt, kommt ans Ende, statt zu fehlen.
+ +Erst sichtbar, wenn die Startseite einmal geladen war.
'; + return; + } + + // In der Folge, die gerade gilt — ausgeblendete hinten dran, damit man sie + // wiederfindet. + const ordered = applyArrangement(knownRows) + .concat(knownRows.filter((r) => P.hiddenRows.includes(r.id))); + + host.innerHTML = ''; + ordered.forEach((row, index) => { + const hidden = P.hiddenRows.includes(row.id); + const line = document.createElement('div'); + line.className = 'rowline' + (hidden ? ' off' : ''); + line.innerHTML = ` + + + `; + + line.querySelector('input').onchange = (e) => { + const next = e.target.checked + ? P.hiddenRows.filter((id) => id !== row.id) + : [...P.hiddenRows, row.id]; + setPref('hiddenRows', next); + renderRowArrangement(); + }; + + const move = (by) => { + // Die aktuelle Folge festschreiben, bevor darin verschoben wird: ohne + // gespeicherte Folge gäbe es nichts, worin man tauschen könnte. + const ids = ordered.map((r) => r.id); + const to = index + by; + if (to < 0 || to >= ids.length) return; + [ids[index], ids[to]] = [ids[to], ids[index]]; + setPref('rowOrder', ids); + renderRowArrangement(); + }; + const [up, down] = line.querySelectorAll('button'); + up.onclick = () => move(-1); + down.onclick = () => move(1); + + host.appendChild(line); + }); +} + /* ---------- Mediathek ---------- */ async function renderLibrary(id) { const c = el('content');