import SwiftUI import FlixyDesign /// Was beim letzten Abspielversuch passiert ist. /// /// Gedacht als Antwort auf „es läuft nicht": hier steht, welche Engine gewählt /// wurde, über welche Verbindung, und wie lange jeder Schritt gedauert hat. /// Ein Screenshot davon sagt mehr als jede Beschreibung. struct DiagnosticsView: View { @Environment(FlixySession.self) private var session @Environment(\.flixyAccent) private var accent @Environment(PlaybackDiagnostics.self) private var diagnostics var body: some View { Form { Section("diagnostics.connection") { if session.backend != nil { LabeledContent("settings.serverName", value: session.serverName) if let address = session.serverAddress { LabeledContent("settings.address", value: address) .font(.flixyCaption) } if let beschreibung = session.connectionDescription { LabeledContent("settings.connection", value: beschreibung) .font(.flixyCaption) } } else { Text("diagnostics.noServer").foregroundStyle(Color.flixyTextMuted) } } if let attempt = diagnostics.lastAttempt { Section("diagnostics.lastAttempt") { LabeledContent("diagnostics.title.label", value: attempt.title) LabeledContent("diagnostics.method", value: attempt.method) LabeledContent("diagnostics.connectionUsed", value: attempt.connection) VStack(alignment: .leading, spacing: 2) { Text("diagnostics.source") .font(.flixyCaption) .foregroundStyle(Color.flixyTextMuted) Text(attempt.sourceDescription) .font(.system(.caption2, design: .monospaced)) .foregroundStyle(Color.flixyTextSecondary) } } Section("diagnostics.steps") { ForEach(Array(attempt.steps.enumerated()), id: \.offset) { _, step in Text(step) .font(.system(.caption, design: .monospaced)) .foregroundStyle(Color.flixyTextSecondary) } } Section { Button { FlixyClipboard.copy(report(for: attempt)) } label: { Label("diagnostics.copy", systemImage: "doc.on.doc") } } footer: { Text("diagnostics.copyHint") } } else { Section("diagnostics.lastAttempt") { Text("diagnostics.nothingYet").foregroundStyle(Color.flixyTextMuted) } } Section("settings.app") { LabeledContent("settings.version", value: Branding.version) LabeledContent("settings.build", value: Branding.buildStamp) } } // Ausdrücklich, nicht nur geerbt. // // Listen und Formulare zeichnen ihre Zeilen teilweise in einem // eigenen Darstellungskontext, in dem eigene Umgebungswerte verloren // gehen können — sichtbar an Picker-Werten, die in der Standardfarbe // leuchteten, während die Schalter daneben schon die gewählte trugen. .tint(accent) .flixyForm() .scrollContentBackground(.hidden) .background(Color.flixyCanvas) .navigationTitle(Text("diagnostics.title")) .flixyInlineTitle() } /// Der Bericht als Text zum Weitergeben. /// /// Enthält bewusst keinen Token: die Quelladresse ist schon ohne /// Abfrageteil abgelegt, und der Servername verrät nichts Vertrauliches. private func report(for attempt: PlaybackDiagnostics.Attempt) -> String { """ Flixy \(Branding.version) · Build \(Branding.buildStamp) Verbindung: \(attempt.connection) Titel: \(attempt.title) Weg: \(attempt.method) Quelle: \(attempt.sourceDescription) \(attempt.steps.joined(separator: "\n")) """ } }