- 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>
100 lines
3.9 KiB
Swift
100 lines
3.9 KiB
Swift
import SwiftUI
|
|
|
|
/// Vereinheitlichte **„Über"-Sektion** für den Konto-/Einstellungen-Tab —
|
|
/// das Pendant zur ``ManaSignInCTA`` (Login) und ``ManaFeedbackUI/ManaFeedbackCard``
|
|
/// (Feedback): App-Name, Version + Build (automatisch aus dem Bundle) und eine
|
|
/// konfigurierbare Liste externer Links (Homepage, Datenschutz, Vereins-Landing).
|
|
///
|
|
/// Vorher baute jede App diese Sektion von Hand via `LabeledContent` +
|
|
/// `Bundle.main.infoDictionary` — uneinheitlich (comicello: App/Version/Build +
|
|
/// 3 Links; wordeck: nur Server-/Auth-Hosts; memoro: gar keine). Diese
|
|
/// Komponente macht Platzierung + Optik über alle Apps gleich.
|
|
///
|
|
/// Wird typischerweise nicht von Hand platziert, sondern von ``ManaAccountView``
|
|
/// an **fester Position** (ganz unten, nach den App-Sektionen) gerendert, wenn
|
|
/// beim Aufruf eine ``Config`` übergeben wird. Für Stand-alone-Einsatz (eigener
|
|
/// Settings-Screen) direkt in eine `Form` legen — die View *ist* eine `Section`:
|
|
///
|
|
/// ```swift
|
|
/// Form {
|
|
/// ManaAboutSection(.init(
|
|
/// appName: "Seepuls",
|
|
/// links: [
|
|
/// .init("seepuls.com", URL(string: "https://seepuls.com")!),
|
|
/// .init("Datenschutz", URL(string: "https://verein.mana.how/datenschutz")!),
|
|
/// ]
|
|
/// ))
|
|
/// }
|
|
/// ```
|
|
public struct ManaAboutSection: View {
|
|
/// Was die „Über"-Sektion zeigt. `Sendable`, damit sie als
|
|
/// ``ManaAccountView``-Init-Parameter durchgereicht werden kann.
|
|
public struct Config: Sendable {
|
|
public var appName: String
|
|
public var links: [Link]
|
|
|
|
/// - Parameters:
|
|
/// - appName: Anzeigename der App (z.B. „Seepuls").
|
|
/// - links: externe Links (Homepage, Datenschutz, Vereins-Landing …).
|
|
public init(appName: String, links: [Link] = []) {
|
|
self.appName = appName
|
|
self.links = links
|
|
}
|
|
}
|
|
|
|
/// Ein benannter Link in der „Über"-Sektion. Standardmäßig öffnet er extern
|
|
/// (System-Browser); mit `inApp: true` bleibt er **in der App**
|
|
/// (iOS: `SFSafariViewController`, macOS: System-Browser-Fallback) — für
|
|
/// rechtliche/redaktionelle Seiten (Impressum, Datenschutz, Lizenz), die man
|
|
/// nicht in einer separaten App öffnen will.
|
|
public struct Link: Identifiable, Sendable {
|
|
public var id: String {
|
|
title
|
|
}
|
|
|
|
public let title: String
|
|
public let url: URL
|
|
public let inApp: Bool
|
|
|
|
public init(_ title: String, _ url: URL, inApp: Bool = false) {
|
|
self.title = title
|
|
self.url = url
|
|
self.inApp = inApp
|
|
}
|
|
}
|
|
|
|
private let config: Config
|
|
private let version: String
|
|
private let build: String
|
|
|
|
/// - Parameters:
|
|
/// - config: App-Name + Links.
|
|
/// - bundle: Quelle für `CFBundleShortVersionString` (Version) und
|
|
/// `CFBundleVersion` (Build). Default `.main`; injizierbar für Previews/Tests.
|
|
public init(_ config: Config, bundle: Bundle = .main) {
|
|
self.config = config
|
|
version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? "—"
|
|
build = bundle.infoDictionary?["CFBundleVersion"] as? String ?? "—"
|
|
}
|
|
|
|
public var body: some View {
|
|
Section("Über") {
|
|
LabeledContent("App", value: config.appName)
|
|
LabeledContent("Version", value: versionText)
|
|
ForEach(config.links) { link in
|
|
if link.inApp {
|
|
ManaSafariLink(link.title, link.url)
|
|
} else {
|
|
// SwiftUI.Link explizit — der verschachtelte Typ `Link` würde
|
|
// sonst den gleichnamigen SwiftUI-View überschatten.
|
|
SwiftUI.Link(link.title, destination: link.url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// „1.4.2 (87)" wenn Build vorhanden, sonst nur die Version.
|
|
private var versionText: String {
|
|
build == "—" ? version : "\(version) (\(build))"
|
|
}
|
|
}
|