128 lines
3.5 KiB
Swift
128 lines
3.5 KiB
Swift
|
|
import SwiftUI
|
||
|
|
|
||
|
|
struct SettingsView: View {
|
||
|
|
@Bindable var settings: SettingsStore
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
Form {
|
||
|
|
Section("Backend") {
|
||
|
|
TextField("Backend URL", text: $settings.backendURLString)
|
||
|
|
.onSubmit {
|
||
|
|
settings.saveBackendURL()
|
||
|
|
}
|
||
|
|
.platformCredentialField()
|
||
|
|
|
||
|
|
Button("Apply Backend URL") {
|
||
|
|
settings.saveBackendURL()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Section("Session") {
|
||
|
|
if let user = settings.currentUser {
|
||
|
|
LabeledContent("Signed in as", value: user.displayName)
|
||
|
|
LabeledContent("Username", value: user.username)
|
||
|
|
|
||
|
|
Button("Sign out", role: .destructive) {
|
||
|
|
Task {
|
||
|
|
await settings.signOut()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
Picker("Mode", selection: $settings.authMode) {
|
||
|
|
ForEach(SettingsStore.AuthMode.allCases) { mode in
|
||
|
|
Text(mode == .login ? "Log in" : "Register").tag(mode)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.pickerStyle(.segmented)
|
||
|
|
|
||
|
|
if settings.authMode == .register {
|
||
|
|
TextField("Display name", text: $settings.displayName)
|
||
|
|
}
|
||
|
|
|
||
|
|
TextField("Username", text: $settings.username)
|
||
|
|
.platformCredentialField()
|
||
|
|
|
||
|
|
SecureField("Password", text: $settings.password)
|
||
|
|
|
||
|
|
Button(settings.authMode == .login ? "Authenticate" : "Create account") {
|
||
|
|
Task {
|
||
|
|
await settings.authenticate()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.disabled(settings.isBusy)
|
||
|
|
|
||
|
|
Button("Use access key") {
|
||
|
|
Task {
|
||
|
|
await settings.signInWithAccessKey()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.disabled(settings.isBusy)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Section("Access Keys") {
|
||
|
|
if settings.currentUser == nil {
|
||
|
|
Text("Sign in before registering or listing access keys.")
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
} else {
|
||
|
|
TextField("New access key label", text: $settings.accessKeyLabel)
|
||
|
|
Button("Register access key") {
|
||
|
|
Task {
|
||
|
|
await settings.registerAccessKey()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.disabled(settings.isBusy)
|
||
|
|
|
||
|
|
if settings.accessKeys.isEmpty {
|
||
|
|
Text("No access keys registered yet.")
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
} else {
|
||
|
|
ForEach(settings.accessKeys) { key in
|
||
|
|
VStack(alignment: .leading, spacing: 4) {
|
||
|
|
Text(key.label)
|
||
|
|
.font(.headline)
|
||
|
|
Text("Device: \(key.deviceType)\(key.backedUp ? " / backed up" : "")")
|
||
|
|
.font(.subheadline)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
Text("Transports: \(key.transports.isEmpty ? "unspecified" : key.transports.joined(separator: ", "))")
|
||
|
|
.font(.footnote)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
.padding(.vertical, 4)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if let infoMessage = settings.infoMessage {
|
||
|
|
Section("Status") {
|
||
|
|
Text(infoMessage)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if let errorMessage = settings.errorMessage {
|
||
|
|
Section("Error") {
|
||
|
|
Text(errorMessage)
|
||
|
|
.foregroundStyle(.red)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.formStyle(.grouped)
|
||
|
|
.padding()
|
||
|
|
.frame(minWidth: 420, minHeight: 520)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private extension View {
|
||
|
|
@ViewBuilder
|
||
|
|
func platformCredentialField() -> some View {
|
||
|
|
#if os(iOS)
|
||
|
|
textInputAutocapitalization(.never)
|
||
|
|
.autocorrectionDisabled()
|
||
|
|
#else
|
||
|
|
self
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
}
|