128 lines
6.4 KiB
Swift
128 lines
6.4 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
import Sparkle
|
|
|
|
struct SettingsView: View {
|
|
@EnvironmentObject private var app: AppModel
|
|
@AppStorage("reader.route") private var defaultRoute = "tonuino"
|
|
@State private var acoustIDClientKey = ""
|
|
@State private var acoustIDKeyMessage: String?
|
|
@State private var profileStorageMessage: String?
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("Standardwerte") {
|
|
Picker("Bevorzugter Kartenleser", selection: $defaultRoute) {
|
|
Text("TonUINO über USB").tag("tonuino")
|
|
Text("ACR122U").tag("acr122u")
|
|
}
|
|
Toggle("Update-Prüfung beim Start", isOn: Binding(
|
|
get: { AppDelegate.updater?.updater.automaticallyChecksForUpdates ?? true },
|
|
set: { AppDelegate.updater?.updater.automaticallyChecksForUpdates = $0 }
|
|
))
|
|
}
|
|
Section("Hardware") {
|
|
LabeledContent("TonUINO-Verbindung", value: app.cards.serialConnected ? app.cards.selectedSerialPort : "Nicht verbunden")
|
|
LabeledContent("Verfügbare serielle Ports", value: "\(app.cards.serialPorts.count)")
|
|
LabeledContent("PC/SC Reader", value: "\(app.cards.readerNames.count)")
|
|
Button("Geräte erneut suchen", systemImage: "arrow.clockwise") { app.cards.refreshSerialPorts(); app.cards.refreshReaders() }
|
|
}
|
|
Section("Lokale Profile") {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("Speicherort")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
Text(app.library.profileStorageURL.path)
|
|
.textSelection(.enabled)
|
|
.font(.system(.body, design: .monospaced))
|
|
.lineLimit(3)
|
|
}
|
|
if let error = app.library.profileStorageError {
|
|
Label("Profilspeicher nicht erreichbar", systemImage: "externaldrive.badge.exclamationmark")
|
|
.foregroundStyle(.orange)
|
|
Text("\(error) Die vorhandenen Profile bleiben gespeichert; verbinde das Laufwerk oder wähle einen anderen Speicherort.")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
HStack {
|
|
Button("Speicherort ändern…", systemImage: "folder") { chooseProfileStorageLocation() }
|
|
if app.library.isProfileStorageCustom {
|
|
Button("Standardort verwenden", systemImage: "arrow.uturn.backward") {
|
|
changeProfileStorage(to: nil)
|
|
}
|
|
}
|
|
}
|
|
.disabled(app.library.isSyncing)
|
|
if app.library.isSyncing {
|
|
ProgressView(app.library.syncProgress)
|
|
.font(.caption)
|
|
}
|
|
if let profileStorageMessage {
|
|
Text(profileStorageMessage)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
Text("Profile und ihre Audiodateien werden beim Wechsel vollständig kopiert. Ein externes Laufwerk muss beim Start verfügbar sein; nach dem erneuten Anschließen wird der Speicher automatisch neu eingelesen.")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
Section("Musikerkennung") {
|
|
SecureField("AcoustID-Anwendungsschlüssel", text: $acoustIDClientKey)
|
|
.textContentType(.password)
|
|
HStack {
|
|
Button("Schlüssel speichern", systemImage: "key") {
|
|
do {
|
|
try AcoustIDCredentials.saveClientKey(acoustIDClientKey)
|
|
acoustIDKeyMessage = acoustIDClientKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "Schlüssel entfernt." : "Schlüssel sicher im macOS-Schlüsselbund gespeichert."
|
|
} catch { acoustIDKeyMessage = error.localizedDescription }
|
|
}
|
|
if let acoustIDKeyMessage { Text(acoustIDKeyMessage).font(.caption).foregroundStyle(.secondary) }
|
|
}
|
|
Text("Registriere die App bei AcoustID und trage hier den Anwendungsschlüssel ein. Er wird im macOS-Schlüsselbund gespeichert.")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
Link("AcoustID-Anwendung registrieren", destination: URL(string: "https://acoustid.org/application")!)
|
|
}
|
|
Section("Updates") {
|
|
Text("Version \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unbekannt")")
|
|
.foregroundStyle(.secondary)
|
|
Button("Auf Updates prüfen", systemImage: "arrow.clockwise") { AppDelegate.updater?.checkForUpdates(nil) }
|
|
.disabled(AppDelegate.updater?.updater.canCheckForUpdates != true)
|
|
}
|
|
Section("Über die App") {
|
|
Text("Tonuino Manager für macOS")
|
|
Text("Verwaltet TonUINO Musik-SD-Karten und programmiert RFID/NFC-Karten.").foregroundStyle(.secondary)
|
|
Link("TonUINO-TNG Firmware", destination: URL(string: "https://github.com/tonuino/TonUINO-TNG")!)
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
.padding(.top, 10)
|
|
.navigationTitle("Einstellungen")
|
|
.onAppear { acoustIDClientKey = AcoustIDCredentials.loadClientKey() ?? "" }
|
|
}
|
|
|
|
private func chooseProfileStorageLocation() {
|
|
let panel = NSOpenPanel()
|
|
panel.title = "Speicherort für lokale Profile auswählen"
|
|
panel.prompt = "Diesen Ordner verwenden"
|
|
panel.canChooseFiles = false
|
|
panel.canChooseDirectories = true
|
|
panel.canCreateDirectories = true
|
|
panel.allowsMultipleSelection = false
|
|
if panel.runModal() == .OK, let url = panel.url {
|
|
changeProfileStorage(to: url)
|
|
}
|
|
}
|
|
|
|
private func changeProfileStorage(to url: URL?) {
|
|
profileStorageMessage = nil
|
|
Task {
|
|
do {
|
|
try await app.library.changeProfileStorageBase(to: url)
|
|
profileStorageMessage = "Speicherort geändert."
|
|
} catch {
|
|
profileStorageMessage = error.localizedDescription
|
|
}
|
|
}
|
|
}
|
|
}
|