Files
Flaro/Flixy/Views/SignIn/SignInView.swift
T
GuidoandClaude Opus 5 ac0c27c026 Fünf Punkte aus dem Praxistest
iPad meldete sich nie an
  Die Ursache ist unangenehm speziell: Wer im Browser schon bei Plex
  angemeldet ist, wird von app.plex.tv/auth sofort auf watch.plex.tv
  weitergeschickt — OHNE dass der Code verknüpft wird. Man sieht sich
  angemeldet, die App wartet vergeblich, und es gibt keinerlei Hinweis.

  Jetzt wird plex.tv/link mit eingetragenem Code geöffnet. Diese Seite
  tut nur eine Sache: den Code bestätigen. Die Gerätekennung steckt
  ohnehin schon im PIN.

  Dazu ein Ausweg: bleibt es nach fünfzehn Sekunden ohne Token, erscheint
  der Code im Anmeldebildschirm zum Abtippen. Schließt man das Fenster
  ohne Erfolg, bleibt er stehen statt abzumelden — er gilt weiter.

Download fragte nicht nach der Qualität
  Jetzt Original, Full HD, HD oder Sparsam, jeweils mit geschätzter
  Größe. Die Größe ist das eigentliche Entscheidungskriterium — „Full HD"
  allein sagt nicht, ob es aufs Telefon passt.
  Die komprimierten Stufen laufen über den Universal-Transcoder und sind
  weiterhin NICHT am Server geprüft; steht so im Quelltext.

Schwarzes Bild ohne Anzeige
  Der Player kannte nur „spielt" und „spielt nicht". Wartete er auf
  Daten, stand man vor einem Standbild ohne Hinweis. Es gibt jetzt einen
  eigenen Pufferzustand mit Anzeige.

Aufmacher war beschnitten
  Er stand auf 3:4, Plex liefert als `art` aber 16:9 — mit scaledToFill
  ging links und rechts je ein Viertel verloren, ohne dass man es dem
  Bild ansah. Die Seitenverhältnisse liegen jetzt an einer Stelle in
  FlixyAspectRatio.

Schwarze Leiste über dem Aufmacher
  .ultraThinMaterial statt undurchsichtigem Grund: das Bild läuft
  unscharf hinter der Leiste weiter, statt an einer harten Kante zu
  enden. Weichgezeichnet, nicht abgedunkelt.

139 Tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 22:06:51 +02:00

110 lines
3.8 KiB
Swift

import SwiftUI
import FlixyDesign
import PlexKit
/// Der Anmeldebildschirm.
///
/// Ein Knopf, mehr nicht. Die Anmeldemaske kommt von Plex und öffnet sich in
/// einem Fenster innerhalb der App; das Passwort wird dort eingegeben und
/// erreicht Flixy nie. Nach dem Anmelden schließt sich das Fenster von selbst.
struct SignInView: View {
let phase: FlixySession.Phase
/// Der Code der laufenden Anmeldung, sobald er gezeigt werden soll.
let manualCode: String?
let onSignIn: () -> Void
let onCancel: () -> Void
@Environment(\.flixyAccent) private var accent
private var isAuthenticating: Bool {
if case .authenticating = phase { return true }
return false
}
var body: some View {
VStack(spacing: FlixySpacing.section) {
Spacer()
VStack(spacing: FlixySpacing.regular) {
Image(systemName: "play.rectangle.on.rectangle")
.font(.system(size: 52))
.foregroundStyle(accent)
Text(Branding.appName)
.font(.flixyHero)
.foregroundStyle(Color.flixyTextPrimary)
Text("signIn.tagline")
.font(.flixyBody)
.foregroundStyle(Color.flixyTextSecondary)
.multilineTextAlignment(.center)
}
Spacer()
VStack(spacing: FlixySpacing.regular) {
Button(action: onSignIn) {
Group {
if isAuthenticating {
ProgressView().tint(Color.flixyCanvas)
} else {
Text("signIn.action").font(.flixyRowHeader)
}
}
.frame(maxWidth: .infinity)
.padding(.vertical, FlixySpacing.regular)
}
.buttonStyle(.borderedProminent)
.tint(accent)
.disabled(isAuthenticating)
if let manualCode {
manualFallback(code: manualCode)
} else {
Text("signIn.privacy")
.font(.flixyCaption)
.foregroundStyle(Color.flixyTextMuted)
.multilineTextAlignment(.center)
}
}
Spacer().frame(height: FlixySpacing.section)
}
.padding(.horizontal, FlixySpacing.section)
.frame(maxWidth: 420)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.flixyCanvas)
}
/// Der Ausweg, wenn die Browser-Seite den Code nicht verknüpft.
///
/// Das passiert, wenn man dort schon bei Plex angemeldet ist: die Seite
/// springt weiter, ohne zu verknüpfen, und die App wartete vergeblich.
/// Der Code gilt weiter — abtippen führt immer zum Ziel.
@ViewBuilder
private func manualFallback(code: String) -> some View {
VStack(spacing: FlixySpacing.regular) {
Divider().overlay(Color.flixySeparator)
Text("signIn.manualHint")
.font(.flixyCaption)
.foregroundStyle(Color.flixyTextSecondary)
.multilineTextAlignment(.center)
Text(code)
.font(.system(.title, design: .monospaced, weight: .semibold))
.tracking(6)
.foregroundStyle(Color.flixyTextPrimary)
.textSelection(.enabled)
.padding(.vertical, FlixySpacing.snug)
.frame(maxWidth: .infinity)
.background(Color.flixyElevated, in: .rect(cornerRadius: FlixyRadius.control))
Button("action.cancel", action: onCancel)
.font(.flixyCaption)
.foregroundStyle(Color.flixyTextSecondary)
}
.transition(.opacity)
}
}