Schritt drei: anmelden. Die Oberfläche ist neu, das Modell dahinter ist dasselbe wie am Telefon — `JellyfinSignInModel`, unverändert bis auf einen Wert im Initialisierer. **Die eine Verhaltensänderung.** Nach gefundenem Server sprang der Weg bisher immer zu Benutzername und Passwort, mit der Begründung, ein Passwortspeicher fülle beide Felder in einer Sekunde. Vor einem Fernseher stimmt daran kein Wort: es gibt keinen Passwortspeicher, man tippt mit dem Steuerkreuz, und das Gerät, auf dem man den Code bestätigt, liegt sowieso auf dem Sofa. Also `preferring: .quickConnect` — ein Wert im Initialisierer und keine Plattformbedingung mitten in `findServer()`. Welcher Fall gilt, weiß die Oberfläche; das Modell nicht. Der Vorgabewert hält iOS unverändert. Der sechsstellige Code steht in 96 Punkt mit Sperrung: er wird aus drei Metern abgelesen und in ein Telefon getippt. In Fliesstextgrösse wäre das eine Zumutung. Beide Wege bleiben erreichbar — für Server, auf denen Quick Connect abgeschaltet ist, und für den Fall, dass gerade kein zweites Gerät zur Hand ist. **Und der Beweis, dass die geteilte Schicht trägt.** Gegen die Jellyfin-Attrappe im Simulator: die App verbindet sich, lädt die drei Mediatheken und zeigt sie. FlixySession, JellyfinBackend, die Zustandsmaschine der Anmeldung — alles unverändert, alles über Pfadeinträge geteilt, keine Zeile kopiert. Die Startseite ist noch eine Liste von Namen. Sie kommt in Schritt vier; hier steht sie, damit die Wurzel vollständig ist und die Anmeldung für sich prüfbar bleibt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
78 lines
3.2 KiB
Swift
78 lines
3.2 KiB
Swift
import SwiftUI
|
|
import FlixyDesign
|
|
import FlixyPlayback
|
|
import MediaKit
|
|
import JellyfinKit
|
|
import PlexKit
|
|
|
|
/// Die App auf dem Fernseher.
|
|
///
|
|
/// **Eine eigene Wurzel statt eines Zweigs in `FlixyApp`.** Der Unterschied
|
|
/// zwischen Telefon und Fernseher ist keine Feinheit der Darstellung, sondern
|
|
/// die Bedienart: kein Finger, keine Grössenklasse, keine Werkzeugleiste, kein
|
|
/// Ziehen zum Aktualisieren, keine halbhohen Sheets — und dafür ein Fokus, den
|
|
/// es auf iOS gar nicht gibt. Ein `#if os(tvOS)` in der bestehenden Wurzel
|
|
/// wäre kein Zweig, sondern ein zweiter Rumpf in derselben Datei, und der
|
|
/// nächste, der die iOS-Seite ändert, bräche die andere, ohne es zu merken.
|
|
///
|
|
/// **Die Modelle dagegen werden geteilt**, nicht kopiert: `Flixy/Session/`
|
|
/// kommt über einen Pfadeintrag in `project.yml` mit. Die Naht verläuft
|
|
/// zwischen Modell und Ansicht, nicht zwischen den Plattformen.
|
|
///
|
|
/// **Kein SwiftData und kein Download-Koordinator.** Der `ModelContainer` der
|
|
/// iOS-App trägt ausschliesslich Downloads, und die gibt es hier nicht — damit
|
|
/// fällt beides weg.
|
|
@main
|
|
struct FlixyTVApp: App {
|
|
@State private var session: FlixySession
|
|
@State private var theme = FlixyTheme()
|
|
@State private var playback = PlaybackCoordinator()
|
|
@State private var playbackSettings = PlaybackSettings()
|
|
@State private var languages = LanguagePreferences()
|
|
@State private var quickPlay = QuickPlay()
|
|
@State private var subtitleAppearance = SubtitleAppearance()
|
|
@State private var homeLayout = HomeLayoutSettings()
|
|
@State private var diagnostics = PlaybackDiagnostics()
|
|
/// Beobachtet die Verbindung. Lebt hier, damit der Beobachter einmal läuft
|
|
/// statt bei jedem Öffnen des Players neu zu starten.
|
|
///
|
|
/// Ein Apple TV hängt fast immer am Kabel oder an festem WLAN — aber
|
|
/// „fast" ist kein Grund, die Entscheidungen des Players zu raten.
|
|
@State private var network = NetworkConditions()
|
|
|
|
init() {
|
|
_session = State(initialValue: FlixySession(
|
|
identity: PlexClientIdentity.current(
|
|
product: Branding.plexProduct,
|
|
version: Branding.version
|
|
),
|
|
jellyfinIdentity: JellyfinIdentity.current(
|
|
client: Branding.appName,
|
|
version: Branding.version
|
|
),
|
|
tokenStore: KeychainTokenStore(service: Branding.keychainService)
|
|
))
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
TVRootView()
|
|
.environment(session)
|
|
.environment(theme)
|
|
.environment(playback)
|
|
.environment(playbackSettings)
|
|
.environment(languages)
|
|
.environment(quickPlay)
|
|
.environment(subtitleAppearance)
|
|
.environment(homeLayout)
|
|
.environment(diagnostics)
|
|
.environment(network)
|
|
.flixyAccent(theme)
|
|
.tint(theme.accentColor)
|
|
// Ein Fernseher ist immer dunkel. Der Schalter in der
|
|
// Info.plist gilt fürs System, diese Zeile für die Vorschauen.
|
|
.preferredColorScheme(.dark)
|
|
}
|
|
}
|
|
}
|