Files
Flaro/FlixyTV/Views/TVRootView.swift
T
ScarriffleandClaude Opus 5 1552e5f2b9 Am Fernseher steht der Code vorn, nicht das Passwort
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>
2026-09-29 17:56:50 +02:00

83 lines
2.9 KiB
Swift

import SwiftUI
import FlixyDesign
/// Was auf dem Fernseher zu sehen ist.
///
/// **Nur der Zustandswechsel, keine Navigation.** Das ist alles, was von
/// `RootView` auf tvOS übrig bleibt: dort sind es 556 Zeilen Seitenleiste,
/// Tab-Anpassung, gemessene Statusleistenhöhe und Mini-Player-Übergang, und
/// nichts davon gibt es hier. Deshalb eine eigene Datei und kein `#if` in der
/// bestehenden — ein `#if` wäre kein Zweig gewesen, sondern ein zweiter Rumpf
/// in derselben Datei.
struct TVRootView: View {
@Environment(FlixySession.self) private var session
var body: some View {
ZStack {
Color.flixyCanvas.ignoresSafeArea()
switch session.phase {
case .launching:
ProgressView()
case .signedOut, .authenticating, .choosingServer:
// Plex bietet die erste Fassung nicht an: die Anmeldung dort
// läuft über ein Browserfenster, und das gibt es auf tvOS
// nicht. Alle drei Zustände führen deshalb zur
// Jellyfin-Anmeldung.
TVSignInView()
case .connecting(let name):
verbinden(mit: name)
case .failed(let meldung):
fehlgeschlagen(meldung)
case .ready:
TVHomeView()
}
}
.task { await session.restore() }
}
private func verbinden(mit name: String) -> some View {
VStack(spacing: FlixySpacing.regular) {
ProgressView()
Text(name).font(.flixyRowHeader).foregroundStyle(.white)
}
}
private func fehlgeschlagen(_ meldung: String) -> some View {
VStack(spacing: FlixySpacing.regular) {
Text(meldung).font(.flixyBody).foregroundStyle(.white)
HStack(spacing: FlixySpacing.regular) {
Button("action.retry") { Task { await session.retry() } }
Button("action.signOut") { session.signOut() }
}
}
.frame(maxWidth: 900)
}
}
/// Die Startseite — vorerst nur die Bestätigung, dass die Anmeldung steht.
///
/// Sie kommt in Schritt vier; hier steht sie, damit `TVRootView` schon
/// vollständig ist und die Anmeldung für sich prüfbar bleibt.
struct TVHomeView: View {
@Environment(FlixySession.self) private var session
var body: some View {
VStack(alignment: .leading, spacing: FlixySpacing.regular) {
Text(session.serverName.isEmpty ? Branding.appName : session.serverName).font(.flixyHero)
ForEach(session.libraries, id: \.id) { mediathek in
Text(mediathek.title).font(.flixyRowHeader)
}
Button("action.signOut") { session.signOut() }
.padding(.top, FlixySpacing.section)
}
.foregroundStyle(.white)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(FlixySpacing.screenMargin)
}
}