Der Aufmacher war weg, weil die Mediathek unterwegs verlorenging

Zwei Schalter, die im selben Bildschirm untereinander stehen, ergaben
zusammen gar keinen Aufmacher mehr: „Gesehenes aus Neuzugängen
ausblenden" und eine angehakte Mediathek.

Der Grund steht in `HomeFeed.prepared`. Der Zweig für das Ausblenden baut
die betroffenen Reihen neu — und baute sie **ohne `libraryID`** neu, weil
der neue Parameter dort nicht mitgegeben war. Getroffen hat es dabei
ausgerechnet die Reihen `latest.<mediathek>`, die als einzige überhaupt
eine tragen. Danach warf der Filter des Aufmachers jede Reihe ohne
Mediathek weg, und übrig blieb nichts.

Dasselbe an zweiter Stelle: `combiningContinueRows` verlor `libraryID`
**und** `moreKey` — Letzteres ein eigener, älterer Fehler, durch den bei
zusammengelegten Weiterschauen-Reihen „Alle anzeigen" wegfiel.

**Dazu eine Regel, die ich zurücknehme.** Gestern stand hier: trifft die
Auswahl nichts, bleibt nichts. Das war als Ehrlichkeit gedacht und ist in
der Praxis der schlechtere Fehler — ein Aufmacher, der lautlos
verschwindet, wird überall gesucht, nur nicht in einer Einstellung zwei
Bildschirme weiter. Trifft die Auswahl keine einzige Reihe, gilt sie
jetzt als wirkungslos. Das deckt auch den Fall ab, dass eine gespeicherte
Kennung nach einem Serverwechsel ins Leere zeigt.

Und damit solche Kennungen nicht unsichtbar weiterwirken, werden sie beim
Öffnen des Bildschirms gegen die vorhandenen Mediatheken abgeglichen —
vorher standen sie in keiner Zeile und liessen sich nicht abwählen.

Geprüft im Simulator: „Gesehenes ausblenden" an, „Filme" angehakt — der
Aufmacher ist da und zeigt einen Film. Vorher: nichts.

iOS und tvOS bauen, 469 Tests grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-01 09:26:18 +02:00
co-authored by Claude Opus 5
parent 3a6a6c4242
commit ac62e8bf19
4 changed files with 63 additions and 9 deletions
+19 -1
View File
@@ -62,6 +62,15 @@ final class HomeFeed {
id: row.id,
title: row.title,
moreKey: row.moreKey,
// **Die Mediathek muss mit.** Sie fehlte hier, und das
// war teuer: dieser Zweig baut genau die Reihen neu, die
// als einzige eine tragen — und der Aufmacher wirft seit
// Neuestem jede Reihe ohne Mediathek weg, sobald eine
// ausgewählt ist. „Gesehenes ausblenden" und „Mediathek
// für den Aufmacher" zusammen ergaben deshalb gar keinen
// Aufmacher mehr. Beide Schalter stehen im selben
// Bildschirm untereinander.
libraryID: row.libraryID,
items: row.items.filter { !$0.isWatched }
)
}
@@ -93,7 +102,16 @@ final class HomeFeed {
continue
}
guard row.id == first.id else { continue }
result.append(MediaRowGroup(id: row.id, title: row.title, items: merged))
// `moreKey` und `libraryID` wie oben: was die Reihe mitbrachte,
// soll sie behalten. Ohne `moreKey` fiel bei zusammengelegten
// Reihen „Alle anzeigen" weg — ein eigener, älterer Fehler.
result.append(MediaRowGroup(
id: row.id,
title: row.title,
moreKey: row.moreKey,
libraryID: row.libraryID,
items: merged
))
}
return result
}
+18
View File
@@ -172,8 +172,26 @@ struct HomeLayoutView: View {
}
}
/// Wirft gespeicherte Kennungen weg, die es nicht mehr gibt.
///
/// **Sonst wirken sie unsichtbar weiter.** Nach einem Serverwechsel
/// bleibt in den Voreinstellungen eine Mediathekskennung stehen, die zu
/// nichts mehr passt — sie steht in keiner Zeile, lässt sich also nicht
/// abwählen, und schränkt den Aufmacher trotzdem ein.
///
/// Nur, wenn überhaupt Mediatheken geladen sind: eine leere Liste heisst
/// „noch nicht da", nicht „gibt es nicht".
private func pruneHeroLibraries() {
guard !session.libraries.isEmpty else { return }
let vorhanden = Set(session.libraries.map(\.id))
let bereinigt = layout.heroLibraries.intersection(vorhanden)
guard bereinigt != layout.heroLibraries else { return }
layout.heroLibraries = bereinigt
}
private func load() async {
defer { isLoading = false }
pruneHeroLibraries()
guard let backend = session.backend else { return }
// Die Reihen kommen vom Server — es gibt keine feste Liste, die sich
// vorweg anzeigen ließe. Was hier steht, hängt davon ab, welche
@@ -42,10 +42,19 @@ public enum HeroSelection {
limitedTo libraries: Set<String>
) -> [MediaRowGroup] {
guard !libraries.isEmpty else { return rows }
return rows.filter { row in
let übrig = rows.filter { row in
guard let libraryID = row.libraryID else { return false }
return libraries.contains(libraryID)
}
// **Bleibt nichts übrig, gilt die Auswahl als wirkungslos.**
//
// Ein Aufmacher, der lautlos verschwindet, ist der schlechtere
// Fehler — man sucht ihn überall, nur nicht in einer Einstellung
// zwei Bildschirme weiter. Und es gibt Fälle, in denen das kein
// Versehen ist, sondern die Datenlage: eine gespeicherte Kennung
// nach einem Serverwechsel, oder ein Server, dessen Reihen gar
// keine Mediathek nennen.
return übrig.isEmpty ? rows : übrig
}
/// Ordnet die Titel, ohne die Reihenfolge fest vorzugeben.
@@ -152,13 +152,22 @@ struct HeroLibraryFilterTests {
#expect(übrig == ["latest.filme"])
}
/// Eine Mediathek, die es nicht mehr gibt — umbenannt, gelöscht, Server
/// gewechselt. Dann bleibt nichts übrig, und der Aufmacher fällt auf den
/// ersten Titel der Reihen zurück; das ist ehrlicher, als heimlich doch
/// etwas anderes zu zeigen.
@Test("Eine unbekannte Kennung lässt nichts übrig")
func unknownSelectionLeavesNothing() {
#expect(HeroSelection.rows(alle, limitedTo: ["gibtsnicht"]).isEmpty)
/// **Der Fall, der den Aufmacher verschwinden liess.** Eine Mediathek,
/// die es nicht mehr gibt — umbenannt, gelöscht, Server gewechselt —,
/// oder ein Server, dessen Reihen gar keine Mediathek nennen. Bliebe es
/// bei „nichts übrig", stünde oben nichts mehr, und niemand brächte das
/// mit einer Einstellung zwei Bildschirme weiter in Verbindung.
@Test("Trifft die Auswahl nichts, gilt sie als wirkungslos")
func unknownSelectionFallsBack() {
#expect(HeroSelection.rows(alle, limitedTo: ["gibtsnicht"]).map(\.id) == alle.map(\.id))
}
/// Reihen ohne Mediathek allein sind kein Rückfall: gibt es daneben
/// passende, bleiben nur die. Sonst wäre die Ausnahme oben die Regel.
@Test("Der Rückfall greift nur, wenn wirklich nichts passt")
func fallbackOnlyWhenEmpty() {
let übrig = HeroSelection.rows(alle, limitedTo: ["serien"]).map(\.id)
#expect(übrig == ["latest.serien"])
}
@Test("Mehrere gewählte Mediatheken kommen alle durch")