Nachgesehen — und dabei vier Fehler gefunden, zwei davon meine neuen
Der Simulator läuft wieder. Ich habe alles durchgeklickt, was ich dir in den letzten beiden Runden ungeprüft gegeben habe. Es war nicht in Ordnung. **Zwei Navigationsleisten übereinander.** Auf dem iPhone hat die Leiste unten fünf Plätze; alles darüber schiebt das System in ein „Mehr"-Verzeichnis — und dessen Einträge bekamen zwei Zurück-Pfeile, einen vom System und einen von uns. Statt das zu flicken, gibt es jetzt die **Bibliothek**, die du ohnehin wolltest: auf dem iPhone ein Eintrag, hinter dem alle Mediatheken stehen, auf dem iPad weiter jede einzeln in der Seitenleiste. Damit bleibt die Leiste bei fünf Einträgen, und das „Mehr" entfällt. **Kacheln in der Mediathek öffneten nichts.** Kein Fehler, keine Meldung — Antippen führte einfach ins Leere. Der Grund: in einem Stapel dürfen nicht beide Arten von Verknüpfung nebeneinander vorkommen. Sobald ein Bildschirm über eine **Ansichts**-Verknüpfung geschoben wurde, fanden die **Wert**-Verknüpfungen darin ihr Ziel nicht mehr. Jetzt ist alles wertbasiert, und das Ziel für Detailseiten steht **einmal je Stapel** an dessen Wurzel statt in jedem Bildschirm einzeln. **Der Lupenknopf in der Kopfzeile zeigte auf ein Ziel, das es nie gab.** Kein Bildschirm hat je ein `SearchRoute`-Ziel angemeldet — der Knopf tat seit jeher nichts. Er ist weg; die Suche hat ihren eigenen Platz in der Leiste. **Der Download-Dialog zeigte Schlüssel statt Namen** — „quality.hd" statt „720p". Die drei Übersetzungen fehlten schlicht. **Die Kontokarte wuchs aus dem Bild heraus.** Die Leiste unten in der Seitenleiste hat eine feste Höhe; meine aufklappende Karte schob die Zeile mit dem Namen nach unten hinaus. Jetzt hängt sie als Popover darüber — die Zeile bleibt stehen, wie bei JellyTV. Verkleinerte Downloads bei Jellyfin laufen: Stufen erscheinen, die Adresse stimmt, die Datei kommt an. Dass die Attrappe nur 28 kB liefert und die App das als „keine Videodatei" zurückweist, ist richtig so — die Prüfung tut, was sie soll. Zwei Gruben in der Attrappe nebenbei gestopft: sie kannte `/stream.mp4` nicht und meldete ihr Testbild als 360p, womit es gar keine kleineren Stufen geben konnte. 263 Tests grün. Diesmal alles angesehen: iPhone und iPad, Einstellungen, Bibliothek, Detailseite, Download-Dialog, Kontokarte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
dace5d1543
commit
95723588c2
@@ -2417,6 +2417,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraryHub.empty": {
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Auf diesem Server ist keine Mediathek zu sehen."
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "No library is visible on this server."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"liveTV.channelCount %@": {
|
||||
"comment": "A label displaying the number of channels. The argument is the number of channels.",
|
||||
"isCommentAutoGenerated": true
|
||||
@@ -3376,6 +3392,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality.fullHD": {
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "1080p"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "1080p"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality.hd": {
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "720p"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "720p"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality.high": {
|
||||
"extractionState": "stale",
|
||||
"localizations": {
|
||||
@@ -3428,6 +3476,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality.lowRes": {
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "480p"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "480p"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality.medium": {
|
||||
"extractionState": "stale",
|
||||
"localizations": {
|
||||
@@ -4594,6 +4658,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.library": {
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Bibliothek"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Library"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.liveTV": {
|
||||
"comment": "Eintrag in der Seitenleiste",
|
||||
"localizations": {
|
||||
|
||||
@@ -49,8 +49,6 @@ struct HomeView: View {
|
||||
.toolbarBackground(.hidden, for: .navigationBar)
|
||||
// Helle Symbole, weil darunter ein Bild liegt und kein heller Grund.
|
||||
.toolbarColorScheme(.dark, for: .navigationBar)
|
||||
.toolbar { SearchToolbarItem() }
|
||||
.navigationDestination(for: MediaRoute.self) { MediaDetailView(route: $0) }
|
||||
.task(id: session.serverName) { await load() }
|
||||
.refreshable { await load() }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import SwiftUI
|
||||
import FlixyDesign
|
||||
import MediaKit
|
||||
|
||||
/// Alle Mediatheken auf einem Bildschirm.
|
||||
///
|
||||
/// Auf dem iPad hat die Seitenleiste Platz für jede Mediathek einzeln. Auf dem
|
||||
/// iPhone hat die Leiste unten fünf Plätze — und sobald es mehr Ziele gibt,
|
||||
/// schiebt das System den Rest in ein „Mehr"-Verzeichnis. Das sah nicht nur
|
||||
/// gedrängt aus, es war kaputt: die Einträge dort bekamen **zwei**
|
||||
/// Navigationsleisten übereinander, eine vom System und eine von uns.
|
||||
///
|
||||
/// Dieser Bildschirm fasst sie zu einem Ziel zusammen. Damit bleibt die Leiste
|
||||
/// unter fünf Einträgen, und die Mediatheken stehen trotzdem alle da — mit
|
||||
/// mehr Platz für ihre Namen, als eine Leiste je hätte.
|
||||
struct LibraryHubView: View {
|
||||
@Environment(FlixySession.self) private var session
|
||||
@Environment(\.flixyAccent) private var accent
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
if session.libraries.isEmpty {
|
||||
Text("libraryHub.empty")
|
||||
.foregroundStyle(Color.flixyTextSecondary)
|
||||
.listRowBackground(Color.flixySurface)
|
||||
} else {
|
||||
Section("sidebar.libraries") {
|
||||
ForEach(session.libraries) { library in
|
||||
NavigationLink(value: LibraryRoute(id: library.id)) {
|
||||
Label(library.title, systemImage: symbol(for: library.kind))
|
||||
}
|
||||
.listRowBackground(Color.flixySurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if session.supportsLiveTV {
|
||||
Section {
|
||||
NavigationLink(value: LiveTVRoute()) {
|
||||
Label("sidebar.liveTV", systemImage: "dot.radiowaves.left.and.right")
|
||||
}
|
||||
.listRowBackground(Color.flixySurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
.tint(accent)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.flixyCanvas)
|
||||
.navigationTitle(Text("sidebar.library"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private func symbol(for kind: MediaLibrary.Kind) -> String {
|
||||
switch kind {
|
||||
case .movie: "film"
|
||||
case .show: "tv"
|
||||
default: "rectangle.stack"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ struct LibraryView: View {
|
||||
.sheet(isPresented: $showsFilters) {
|
||||
LibraryFilterSheet(query: $query, libraryKind: library.kind)
|
||||
}
|
||||
.navigationDestination(for: MediaRoute.self) { MediaDetailView(route: $0) }
|
||||
.task(id: library.id) { await reload() }
|
||||
// Jede Änderung an Filter oder Sortierung verwirft die bisherige
|
||||
// Liste — Nachladen mit altem Versatz würde sonst Einträge aus zwei
|
||||
@@ -141,7 +140,6 @@ struct LibraryView: View {
|
||||
.accessibilityLabel(Text("library.toggleLayout"))
|
||||
}
|
||||
|
||||
SearchToolbarItem()
|
||||
}
|
||||
|
||||
// MARK: - Laden
|
||||
|
||||
@@ -95,3 +95,17 @@ struct MediaRoute: Hashable {
|
||||
/// Für die Titelzeile, solange noch geladen wird.
|
||||
let title: String
|
||||
}
|
||||
|
||||
/// Der Weg zu einer Mediathek.
|
||||
///
|
||||
/// Ein Wert statt einer Ansicht, und das ist kein Geschmack: in einem Stapel
|
||||
/// dürfen nicht beide Arten von Verknüpfung nebeneinander vorkommen. Sobald
|
||||
/// eine Ansicht über eine **Ansichts**-Verknüpfung geschoben wurde, fanden die
|
||||
/// **Wert**-Verknüpfungen darin ihr Ziel nicht mehr — jedes Antippen einer
|
||||
/// Kachel führte ins Leere, ohne Fehlermeldung.
|
||||
struct LibraryRoute: Hashable {
|
||||
let id: String
|
||||
}
|
||||
|
||||
/// Der Weg zum Programmführer.
|
||||
struct LiveTVRoute: Hashable {}
|
||||
|
||||
@@ -8,11 +8,14 @@ import FlixyDesign
|
||||
/// Einstellungen, statt dass sie als gleichrangiger Eintrag zwischen den
|
||||
/// Mediatheken stehen.
|
||||
///
|
||||
/// **Eine Karte, die nach oben aufklappt**, kein Systemmenü. Der Unterschied
|
||||
/// ist nicht nur Geschmack: ein Menü legt sich als eigene Ebene über alles und
|
||||
/// springt dorthin, wo Platz ist. Die Karte bleibt, wo sie ist, wächst nach
|
||||
/// oben und lässt die Zeile mit dem Namen die ganze Zeit stehen — man sieht
|
||||
/// also weiter, um wessen Konto es geht.
|
||||
/// **Eine Karte, die über der Zeile aufgeht**, kein Systemmenü: mit denselben
|
||||
/// Abständen und derselben Schrift wie der Rest der App, statt der grauen
|
||||
/// Standardliste.
|
||||
///
|
||||
/// Sie hängt als Popover an der Zeile und wächst nicht in der Leiste selbst.
|
||||
/// Das war der erste Versuch, und er ging schief: die Leiste unten hat eine
|
||||
/// feste Höhe, die Karte wuchs nach unten aus dem Bild heraus, und die Zeile
|
||||
/// mit dem Namen verschwand dabei.
|
||||
///
|
||||
/// Auf dem iPhone gibt es diese Leiste nicht; dort haben die Einstellungen
|
||||
/// ihren eigenen Eintrag.
|
||||
@@ -35,30 +38,31 @@ struct AccountBar: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
if isExpanded {
|
||||
action("account.settings", symbol: "gearshape") {
|
||||
collapse()
|
||||
selection = .settings
|
||||
accountRow
|
||||
.popover(isPresented: $isExpanded, arrowEdge: .bottom) {
|
||||
VStack(spacing: 0) {
|
||||
action("account.settings", symbol: "gearshape") {
|
||||
isExpanded = false
|
||||
selection = .settings
|
||||
}
|
||||
separator
|
||||
action(
|
||||
"action.signOut",
|
||||
symbol: "rectangle.portrait.and.arrow.right",
|
||||
isDestructive: true
|
||||
) {
|
||||
isExpanded = false
|
||||
showsSignOutConfirmation = true
|
||||
}
|
||||
}
|
||||
separator
|
||||
|
||||
action(
|
||||
"action.signOut",
|
||||
symbol: "rectangle.portrait.and.arrow.right",
|
||||
isDestructive: true
|
||||
) {
|
||||
collapse()
|
||||
showsSignOutConfirmation = true
|
||||
}
|
||||
separator
|
||||
.frame(minWidth: 240)
|
||||
.background(Color.flixySurface)
|
||||
// Auf dem iPhone gäbe es diese Leiste gar nicht; die Angabe
|
||||
// steht trotzdem da, damit aus dem Popover dort kein
|
||||
// bildschirmfüllendes Blatt wird.
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
accountRow
|
||||
}
|
||||
.glassEffect(.regular, in: .rect(cornerRadius: FlixyRadius.sheet))
|
||||
.animation(.snappy(duration: 0.25), value: isExpanded)
|
||||
.confirmationDialog(
|
||||
.confirmationDialog(
|
||||
Text("settings.signOutConfirm"),
|
||||
isPresented: $showsSignOutConfirmation,
|
||||
titleVisibility: .visible
|
||||
@@ -72,7 +76,7 @@ struct AccountBar: View {
|
||||
|
||||
private var accountRow: some View {
|
||||
Button {
|
||||
withAnimation(.snappy(duration: 0.25)) { isExpanded.toggle() }
|
||||
isExpanded.toggle()
|
||||
} label: {
|
||||
HStack(spacing: FlixySpacing.snug) {
|
||||
avatar
|
||||
@@ -93,8 +97,8 @@ struct AccountBar: View {
|
||||
|
||||
Spacer(minLength: FlixySpacing.snug)
|
||||
|
||||
// Zeigt nach oben, wenn zugeklappt: dorthin wächst die Karte.
|
||||
Image(systemName: isExpanded ? "chevron.down" : "chevron.up")
|
||||
// Zeigt nach oben: dorthin geht die Karte auf.
|
||||
Image(systemName: "chevron.up")
|
||||
.font(.caption2.weight(.semibold))
|
||||
.foregroundStyle(Color.flixyTextMuted)
|
||||
}
|
||||
@@ -141,8 +145,4 @@ struct AccountBar: View {
|
||||
.frame(width: 26, height: 26)
|
||||
.background(accent, in: .circle)
|
||||
}
|
||||
|
||||
private func collapse() {
|
||||
withAnimation(.snappy(duration: 0.25)) { isExpanded = false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,16 @@ struct RootView: View {
|
||||
// bleiben, die es auf dem neuen Server gar nicht gibt.
|
||||
if newPhase == .ready, case .library = selection { selection = .home }
|
||||
}
|
||||
.onChange(of: sizeClass) { _, _ in
|
||||
// Beim Drehen oder Teilen des Fensters wechselt die Form der
|
||||
// Navigation — und mit ihr, welche Ziele es überhaupt gibt. Eine
|
||||
// Auswahl, die auf der anderen Seite nicht vorkommt, zeigte sonst
|
||||
// einen leeren Bereich.
|
||||
switch selection {
|
||||
case .library, .libraryHub, .liveTV: selection = .home
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wechselt den laufenden Titel.
|
||||
@@ -163,35 +173,52 @@ struct RootView: View {
|
||||
private var library: some View {
|
||||
TabView(selection: $selection) {
|
||||
Tab("sidebar.home", systemImage: "house", value: SidebarDestination.home) {
|
||||
NavigationStack { HomeView() }
|
||||
mediaStack { HomeView() }
|
||||
}
|
||||
.customizationID("flixy.tab.home")
|
||||
|
||||
TabSection {
|
||||
ForEach(session.libraries) { library in
|
||||
Tab(
|
||||
library.title,
|
||||
systemImage: symbol(for: library.kind),
|
||||
value: SidebarDestination.library(key: library.id, title: library.title)
|
||||
) {
|
||||
NavigationStack { LibraryView(library: library) }
|
||||
// Auf dem iPad jede Mediathek einzeln, auf dem iPhone alle
|
||||
// zusammen hinter einem Eintrag. Der Grund ist nicht Geschmack:
|
||||
// die Leiste unten hat fünf Plätze, und alles darüber schiebt das
|
||||
// System in ein „Mehr"-Verzeichnis — dessen Einträge dann zwei
|
||||
// Navigationsleisten übereinander tragen, eine vom System und eine
|
||||
// von uns.
|
||||
if sizeClass == .regular {
|
||||
TabSection {
|
||||
ForEach(session.libraries) { library in
|
||||
Tab(
|
||||
library.title,
|
||||
systemImage: symbol(for: library.kind),
|
||||
value: SidebarDestination.library(key: library.id, title: library.title)
|
||||
) {
|
||||
mediaStack { LibraryView(library: library) }
|
||||
}
|
||||
.customizationID("flixy.tab.library.\(library.id)")
|
||||
}
|
||||
.customizationID("flixy.tab.library.\(library.id)")
|
||||
} header: {
|
||||
Text("sidebar.libraries")
|
||||
}
|
||||
} header: {
|
||||
Text("sidebar.libraries")
|
||||
}
|
||||
.customizationID("flixy.tab.libraries")
|
||||
.customizationID("flixy.tab.libraries")
|
||||
|
||||
if session.supportsLiveTV {
|
||||
Tab(
|
||||
"sidebar.liveTV",
|
||||
systemImage: "dot.radiowaves.left.and.right",
|
||||
value: SidebarDestination.liveTV
|
||||
) {
|
||||
NavigationStack { LiveTVView() }
|
||||
if session.supportsLiveTV {
|
||||
Tab(
|
||||
"sidebar.liveTV",
|
||||
systemImage: "dot.radiowaves.left.and.right",
|
||||
value: SidebarDestination.liveTV
|
||||
) {
|
||||
mediaStack { LiveTVView() }
|
||||
}
|
||||
.customizationID("flixy.tab.liveTV")
|
||||
}
|
||||
.customizationID("flixy.tab.liveTV")
|
||||
} else {
|
||||
Tab(
|
||||
"sidebar.library",
|
||||
systemImage: "books.vertical",
|
||||
value: SidebarDestination.libraryHub
|
||||
) {
|
||||
mediaStack { LibraryHubView() }
|
||||
}
|
||||
.customizationID("flixy.tab.libraryHub")
|
||||
}
|
||||
|
||||
Tab(
|
||||
@@ -199,12 +226,12 @@ struct RootView: View {
|
||||
systemImage: "arrow.down.circle",
|
||||
value: SidebarDestination.downloads
|
||||
) {
|
||||
NavigationStack { DownloadsView() }
|
||||
mediaStack { DownloadsView() }
|
||||
}
|
||||
.customizationID("flixy.tab.downloads")
|
||||
|
||||
Tab("sidebar.settings", systemImage: "gearshape", value: SidebarDestination.settings) {
|
||||
NavigationStack { SettingsView() }
|
||||
mediaStack { SettingsView() }
|
||||
}
|
||||
.customizationID("flixy.tab.settings")
|
||||
|
||||
@@ -212,7 +239,7 @@ struct RootView: View {
|
||||
// getrennt von den übrigen Einträgen dar, so wie man es aus jeder
|
||||
// anderen App kennt.
|
||||
Tab(value: SidebarDestination.search, role: .search) {
|
||||
NavigationStack { SearchView() }
|
||||
mediaStack { SearchView() }
|
||||
}
|
||||
.customizationID("flixy.tab.search")
|
||||
}
|
||||
@@ -227,6 +254,26 @@ struct RootView: View {
|
||||
.miniPlayer(player, isVisible: playback.presentation == .mini)
|
||||
}
|
||||
|
||||
/// Ein Navigationsstapel, in dem Detailseiten aufgehen.
|
||||
///
|
||||
/// Das Ziel wird **einmal je Stapel** angemeldet, an seiner Wurzel. Vorher
|
||||
/// tat es jeder Bildschirm für sich — und in dem Moment, in dem zwei davon
|
||||
/// im selben Stapel übereinanderlagen (die Mediathek in der Bibliothek),
|
||||
/// führte kein Antippen einer Kachel mehr irgendwohin. SwiftUI meldet das
|
||||
/// nicht; es passiert einfach nichts.
|
||||
private func mediaStack<Content: View>(@ViewBuilder content: () -> Content) -> some View {
|
||||
NavigationStack {
|
||||
content()
|
||||
.navigationDestination(for: MediaRoute.self) { MediaDetailView(route: $0) }
|
||||
.navigationDestination(for: LibraryRoute.self) { route in
|
||||
if let library = session.libraries.first(where: { $0.id == route.id }) {
|
||||
LibraryView(library: library)
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: LiveTVRoute.self) { _ in LiveTVView() }
|
||||
}
|
||||
}
|
||||
|
||||
private func symbol(for kind: MediaLibrary.Kind) -> String {
|
||||
switch kind {
|
||||
case .movie: "film"
|
||||
|
||||
@@ -7,6 +7,8 @@ import Foundation
|
||||
enum SidebarDestination: Hashable {
|
||||
case home
|
||||
case library(key: String, title: String)
|
||||
/// Alle Mediatheken zusammen — auf schmalen Bildschirmen an ihrer Stelle.
|
||||
case libraryHub
|
||||
case search
|
||||
case liveTV
|
||||
case downloads
|
||||
@@ -21,6 +23,7 @@ enum SidebarDestination: Hashable {
|
||||
switch self {
|
||||
case .home: Branding.appName
|
||||
case .library(_, let title): title
|
||||
case .libraryHub: String(localized: "sidebar.library")
|
||||
case .search: String(localized: "search.title")
|
||||
case .liveTV: String(localized: "sidebar.liveTV")
|
||||
case .downloads: String(localized: "sidebar.downloads")
|
||||
@@ -40,6 +43,7 @@ enum SidebarDestination: Hashable {
|
||||
switch self {
|
||||
case .home: "house"
|
||||
case .library: "film"
|
||||
case .libraryHub: "books.vertical"
|
||||
case .search: "magnifyingglass"
|
||||
case .liveTV: "dot.radiowaves.left.and.right"
|
||||
case .downloads: "arrow.down.circle"
|
||||
|
||||
@@ -2,20 +2,6 @@ import SwiftUI
|
||||
import FlixyDesign
|
||||
import MediaKit
|
||||
|
||||
/// Der Knopf, der zur Suche führt. Steht in mehreren Werkzeugleisten.
|
||||
struct SearchToolbarItem: ToolbarContent {
|
||||
var body: some ToolbarContent {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
NavigationLink(value: SearchRoute()) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
}
|
||||
.accessibilityLabel(Text("search.title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchRoute: Hashable {}
|
||||
|
||||
/// Sucht über alle Mediatheken des Servers.
|
||||
struct SearchView: View {
|
||||
@Environment(FlixySession.self) private var session
|
||||
@@ -42,7 +28,6 @@ struct SearchView: View {
|
||||
.navigationTitle(Text("search.title"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.searchable(text: $text, placement: .navigationBarDrawer(displayMode: .always))
|
||||
.navigationDestination(for: MediaRoute.self) { MediaDetailView(route: $0) }
|
||||
// Jeder Tastendruck löst eine Anfrage aus — aber erst nach einer
|
||||
// kurzen Pause. Ohne sie schickt „Blade Runner" zwölf Anfragen los,
|
||||
// von denen elf verworfen werden.
|
||||
|
||||
Reference in New Issue
Block a user