- ManaThemeUI: ManaAppearanceSection — geteilter „Darstellung"-Block (ManaAppearancePicker + ManaThemeGallery + einheitlicher Footer) als eine Section. Bindings + Theme-Pass (unlocked/onLockedTap) werden durchgereicht; Persistenz bleibt App-Sache. Ersetzt den pro App handgebauten Section-Rahmen. - ManaAuthUI: ManaSafariLink — öffnet URLs in-App (iOS SFSafariViewController, macOS System-Browser-Fallback) für rechtliche/redaktionelle Seiten. - ManaAuthUI: ManaAboutSection.Link.inApp — neuer additiver Parameter; true öffnet den Link via ManaSafariLink. Eine Sektion deckt App-Info + Rechtliches ab, statt einer separaten Legal-Sektion pro App. iOS-Build (Seepuls, Release) verifiziert beide neuen Komponenten. Kein API-Bruch (alles additiv). CHANGELOG aktualisiert. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2 KiB
Swift
57 lines
2 KiB
Swift
import SwiftUI
|
|
#if canImport(SafariServices)
|
|
import SafariServices
|
|
#endif
|
|
|
|
/// Öffnet eine URL **in-App** statt im externen Browser — auf iOS via
|
|
/// `SFSafariViewController` (User bleibt in der App, mit Reader/Teilen/„Fertig"),
|
|
/// auf macOS gibt es kein SFSafari, daher Fallback auf den System-Browser
|
|
/// (`@Environment(\.openURL)` → `NSWorkspace`).
|
|
///
|
|
/// Gedacht für **rechtliche & redaktionelle** Seiten (Impressum, Datenschutz,
|
|
/// Lizenz, „Über die App im Web") in der ``ManaAboutSection`` — alles, was man
|
|
/// nicht in einer separaten Browser-App aufreißen will. Rendert als
|
|
/// vollbreite, antippbare `Form`-Zeile, optisch wie ein `Link`.
|
|
///
|
|
/// Typisch nicht direkt platziert, sondern über ``ManaAboutSection/Link`` mit
|
|
/// `inApp: true` erzeugt.
|
|
public struct ManaSafariLink: View {
|
|
private let title: String
|
|
private let url: URL
|
|
@Environment(\.openURL) private var openURL
|
|
@State private var isPresented = false
|
|
|
|
/// - Parameters:
|
|
/// - title: Beschriftung der Zeile.
|
|
/// - url: Ziel-URL (http/https).
|
|
public init(_ title: String, _ url: URL) {
|
|
self.title = title
|
|
self.url = url
|
|
}
|
|
|
|
public var body: some View {
|
|
#if os(iOS)
|
|
Button(title) { isPresented = true }
|
|
.sheet(isPresented: $isPresented) {
|
|
SafariSheet(url: url)
|
|
.ignoresSafeArea()
|
|
}
|
|
#else
|
|
// macOS: kein SFSafariViewController — extern öffnen.
|
|
Button(title) { openURL(url) }
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if os(iOS)
|
|
/// Dünner `UIViewControllerRepresentable`-Wrapper um `SFSafariViewController`.
|
|
private struct SafariSheet: UIViewControllerRepresentable {
|
|
let url: URL
|
|
|
|
func makeUIViewController(context _: Context) -> SFSafariViewController {
|
|
SFSafariViewController(url: url)
|
|
}
|
|
|
|
func updateUIViewController(_: SFSafariViewController, context _: Context) {}
|
|
}
|
|
#endif
|