Die Sprache lässt sich jetzt auch je Serie und je Mediathek sagen
Das Regelwerk stand schon und wurde abgefragt — es gab nur keinen Ort, an dem man etwas hineinschreiben konnte. Jetzt steht die Wahl auf der Serienseite und im Filter-Sheet der Mediathek, beide über denselben Auswahlknopf: zwei mit demselben Inhalt liefen unweigerlich auseinander. Bei der Mediathek in einem eigenen Abschnitt, nicht zwischen den Filtern. Ein Filter schränkt ein, was zu sehen ist; das hier bestimmt, was zu hören ist — dazwischengestellt läse es sich als „nur Titel mit deutscher Tonspur zeigen". Die Rangfolge Serie → Mediathek → allgemein ist dabei von den Einstellungen zu `AudioTrackChooser.resolve` gewandert. Sie ist eine Regel, keine Ablage, und im App-Ziel gibt es kein Unit-Test-Ziel — dort liesse sie sich nicht durchspielen. Sechs Tests decken sie jetzt ab, darunter der Fall, an dem man sonst lange sucht: eine leere Zeichenkette steht in den Einstellungen für „soll der Server entscheiden" und darf nicht als Sprache gelten, sonst schaltet sie die Ebene darüber stillschweigend ab. Nebenbei bekommt das Filter-Sheet die Mediathek selbst statt nur ihrer Art. Die Genres wurden vorher über irgendeine Mediathek dieser Art nachgeschlagen — bei zwei Filmmediatheken also über die falsche. Und der Puffer steht auf einer Minute statt auf 25 Sekunden. Es ist ein Mindestwunsch, keine Obergrenze; der harte Deckel liegt ohnehin woanders, nämlich darin, wie weit der Transcoder schon produziert hat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
596a143545
commit
983015b9ad
@@ -0,0 +1,88 @@
|
||||
import SwiftUI
|
||||
import FlixyDesign
|
||||
|
||||
/// „Welche Tonsprache gilt hier?" — an genau zwei Orten dieselbe Frage.
|
||||
///
|
||||
/// **Warum ein gemeinsamer Typ.** Die Frage steht einmal auf der Serienseite
|
||||
/// und einmal bei der Mediathek, und die Antwort bedeutet beide Male dasselbe:
|
||||
/// diese Ebene setzt sich gegen die darüber durch, oder sie tut es nicht. Zwei
|
||||
/// Auswahlknöpfe mit demselben Inhalt liefen unweigerlich auseinander — einer
|
||||
/// bekäme eine Sprache dazu, der andere nicht.
|
||||
///
|
||||
/// **Ein Menü, kein `Picker`.** Die Serienseite ist eine `ScrollView`, kein
|
||||
/// `Form`; dort hätte ein `Picker` kein Aussehen, auf das man sich verlassen
|
||||
/// kann. Ein Menü sieht überall gleich aus.
|
||||
struct AudioLanguagePicker: View {
|
||||
/// Für welche Ebene gewählt wird.
|
||||
enum Scope {
|
||||
case series(String)
|
||||
case library(String)
|
||||
}
|
||||
|
||||
let scope: Scope
|
||||
let title: LocalizedStringKey
|
||||
|
||||
@Environment(LanguagePreferences.self) private var languages
|
||||
@Environment(\.flixyAccent) private var accent
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: FlixySpacing.regular) {
|
||||
Text(title)
|
||||
.font(.flixyBody)
|
||||
.foregroundStyle(Color.flixyTextPrimary)
|
||||
|
||||
Spacer(minLength: FlixySpacing.regular)
|
||||
|
||||
Menu {
|
||||
// „Wie allgemein" zuerst und als Vorgabe: die meisten Ebenen
|
||||
// sollen nichts Eigenes sagen, sondern die Frage nach oben
|
||||
// durchreichen.
|
||||
Button {
|
||||
set(nil)
|
||||
} label: {
|
||||
Label("languages.inherit", systemImage: current == nil ? "checkmark" : "")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
ForEach(LanguagePreferences.offeredCodes, id: \.self) { code in
|
||||
Button {
|
||||
set(code)
|
||||
} label: {
|
||||
Label(name(of: code), systemImage: current == code ? "checkmark" : "")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack(spacing: FlixySpacing.tight) {
|
||||
Text(currentLabel)
|
||||
.font(.flixyBody)
|
||||
Image(systemName: "chevron.up.chevron.down")
|
||||
.font(.flixyCaption)
|
||||
}
|
||||
.foregroundStyle(accent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var current: String? {
|
||||
switch scope {
|
||||
case .series(let id): languages.audioLanguage(forSeries: id)
|
||||
case .library(let id): languages.audioLanguage(forLibrary: id)
|
||||
}
|
||||
}
|
||||
|
||||
private var currentLabel: String {
|
||||
current.map(name(of:)) ?? String(localized: "languages.inherit")
|
||||
}
|
||||
|
||||
private func set(_ code: String?) {
|
||||
switch scope {
|
||||
case .series(let id): languages.setAudioLanguage(code, forSeries: id)
|
||||
case .library(let id): languages.setAudioLanguage(code, forLibrary: id)
|
||||
}
|
||||
}
|
||||
|
||||
private func name(of code: String) -> String {
|
||||
Locale.current.localizedString(forLanguageCode: code)?.capitalized ?? code.uppercased()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user