diff --git a/Flixy/Session/HomeFeed.swift b/Flixy/Session/HomeFeed.swift index ba8388f..3c3a12d 100644 --- a/Flixy/Session/HomeFeed.swift +++ b/Flixy/Session/HomeFeed.swift @@ -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 } diff --git a/Flixy/Views/Settings/HomeLayoutView.swift b/Flixy/Views/Settings/HomeLayoutView.swift index 8dd01cf..4d54225 100644 --- a/Flixy/Views/Settings/HomeLayoutView.swift +++ b/Flixy/Views/Settings/HomeLayoutView.swift @@ -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 diff --git a/Packages/FlixyKit/Sources/MediaKit/HeroSelection.swift b/Packages/FlixyKit/Sources/MediaKit/HeroSelection.swift index 0a96e9c..9a7484d 100644 --- a/Packages/FlixyKit/Sources/MediaKit/HeroSelection.swift +++ b/Packages/FlixyKit/Sources/MediaKit/HeroSelection.swift @@ -42,10 +42,19 @@ public enum HeroSelection { limitedTo libraries: Set ) -> [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. diff --git a/Packages/FlixyKit/Tests/MediaKitTests/HeroSelectionTests.swift b/Packages/FlixyKit/Tests/MediaKitTests/HeroSelectionTests.swift index 0f7c6ac..a3b1097 100644 --- a/Packages/FlixyKit/Tests/MediaKitTests/HeroSelectionTests.swift +++ b/Packages/FlixyKit/Tests/MediaKitTests/HeroSelectionTests.swift @@ -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")