diff --git a/web/app/src/style.css b/web/app/src/style.css index 1c2600d..72fba16 100644 --- a/web/app/src/style.css +++ b/web/app/src/style.css @@ -649,6 +649,8 @@ h2 { font-size: var(--t-abschnitt); font-weight: 700; letter-spacing: -.015em; m border-radius: 50%; animation: dreh .7s linear infinite; } @keyframes dreh { to { transform: rotate(360deg); } } +/* Derselbe Kreis, nur neben Text statt über einer leeren Fläche. */ +.spinner.klein { width: 14px; height: 14px; margin: 0; flex: none; } .attribution { display: flex; align-items: center; gap: var(--s-snug); @@ -988,6 +990,38 @@ h2 { font-size: var(--t-abschnitt); font-weight: 700; letter-spacing: -.015em; m .dlg-breit { width: min(860px, 100vw - 48px); } +/* ---------- Ein Vorschlag im Zuordnungsdialog ---------- + * + * Mit Plakat: „Dark" von 2017 und „Dark Shadows" von 1966 sind an einer + * Textzeile kaum zu unterscheiden, am Bild sofort. + */ +.vorschlag { + display: flex; gap: var(--s-regular); align-items: flex-start; width: 100%; + background: var(--raised); border: 1px solid transparent; + border-radius: var(--r-karte); padding: var(--s-regular); text-align: left; +} +.vorschlag:hover { background: var(--elevated); border-color: var(--line); } +.vorschlag:disabled { opacity: .4; } +/* Die laufende Zeile bleibt hell, während die anderen zurücktreten — sonst + * sähe genau die, auf die man geklickt hat, am totesten aus. */ +.vorschlag.laeuft { opacity: 1; border-color: var(--accent); background: var(--elevated); } + +.vorschlag-bild { + width: 54px; aspect-ratio: 2 / 3; flex: none; + border-radius: var(--r-knopf); overflow: hidden; background: var(--placeholder); +} +.vorschlag-bild img { width: 100%; height: 100%; object-fit: cover; opacity: 0; } +.vorschlag-bild.geladen img { opacity: 1; transition: opacity .18s ease; } + +.vorschlag-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; } +.vorschlag-text .hinweis { margin: 0; } + +.vorschlag-lauf { + display: flex; align-items: center; gap: var(--s-snug); flex: none; + color: var(--accent); font-size: var(--t-klein); font-weight: 600; + align-self: center; +} + /* Die Herkunft im Zuordnungsdialog — der Pfad bricht um statt abzuschneiden, * weil ausgerechnet das Ende die Antwort trägt. */ .quelle { diff --git a/web/app/src/zuordnung.ts b/web/app/src/zuordnung.ts index 9af5ae6..43181ef 100644 --- a/web/app/src/zuordnung.ts +++ b/web/app/src/zuordnung.ts @@ -106,24 +106,66 @@ export function zuordnungsdialog( treffer.innerHTML = ''; if (liste.length === 0) { treffer.appendChild(leer('Nichts gefunden.')); return; } + const zeilen: HTMLButtonElement[] = []; + liste.forEach(v => { const zeile = document.createElement('button'); - zeile.className = 'einstellung waehlbar'; - zeile.innerHTML = + zeile.className = 'vorschlag'; + + // **Mit Plakat.** „Dark" von 2017 und „Dark Shadows" von 1966 sind an + // einer Textzeile kaum zu unterscheiden, am Plakat in einer + // Zehntelsekunde. Der Server liefert die Adresse längst mit; sie wurde + // bisher weggeworfen. + const bild = document.createElement('div'); + bild.className = 'vorschlag-bild'; + if (v.posterUrl) { + const img = document.createElement('img'); + img.src = v.posterUrl; + img.alt = ''; + img.loading = 'lazy'; + img.onload = () => bild.classList.add('geladen'); + bild.appendChild(img); + } + + const text = document.createElement('div'); + text.className = 'vorschlag-text'; + text.innerHTML = `
${esc(v.title)}` + `${esc(v.year ?? '')} · TMDB ${v.tmdbId}
` + `
${esc((v.overview ?? '').slice(0, 160))}
`; + + zeile.append(bild, text); + zeile.onclick = async () => { - zeile.disabled = true; + // **Sichtbar machen, dass etwas passiert.** + // + // Ein Klick löste einen Abruf bei TMDB aus, der Bilder nachlädt und + // mehrere Sekunden braucht — und die Oberfläche sah aus wie vorher. + // Wer nichts sieht, klickt noch einmal, und dann laufen zwei. + zeilen.forEach(z => { z.disabled = true; }); + zeile.classList.add('laeuft'); + + const lauf = document.createElement('span'); + lauf.className = 'vorschlag-lauf'; + lauf.innerHTML = 'Wird zugeordnet…'; + zeile.appendChild(lauf); + try { await zuordnen(art, id, v.tmdbId); + // Erst melden, dann schliessen: sonst blitzt nur der Dialog weg, + // und es bleibt offen, ob es geklappt hat. + lauf.innerHTML = 'Übernommen.'; d.close(); fertig?.(); } catch (fehler) { + lauf.remove(); + zeile.classList.remove('laeuft'); + zeilen.forEach(z => { z.disabled = false; }); treffer.appendChild(fehlerzeile(meldung(fehler))); - zeile.disabled = false; } }; + + zeilen.push(zeile); treffer.appendChild(zeile); }); } catch (fehler) {