Files
voice-cat/clients/apple/iOS/VoiceCatiOS/Views/AccountsView.swift

154 lines
5.5 KiB
Swift
Raw Normal View History

import SwiftUI
import VoiceCatCore
struct AccountsView: View {
@Bindable var session: SessionState
@State private var showCreateAccount = false
@State private var newUsername = ""
@State private var newPassword = ""
@State private var accountToDelete: Account?
@State private var showResetPassword = false
@State private var resetForAccount: Account?
@State private var resetPassword = ""
var body: some View {
List {
ForEach(session.accounts, id: \.username) { account in
AccountRowView(account: account)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
accountToDelete = account
} label: {
Label("Delete", systemImage: "trash")
}
Button {
resetForAccount = account
showResetPassword = true
} label: {
Label("Reset PW", systemImage: "key")
}
.tint(.orange)
}
}
}
.navigationTitle("Accounts")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button {
showCreateAccount = true
} label: {
Image(systemName: "plus")
}
.accessibilityLabel("Create account")
}
}
.refreshable {
session.fetchAccountList()
}
.onAppear {
session.fetchAccountList()
}
.confirmationDialog("Delete account?", isPresented: Binding(
get: { accountToDelete != nil },
set: { if !$0 { accountToDelete = nil } }
)) {
if let a = accountToDelete {
Button("Delete \(a.username)", role: .destructive) {
session.deleteAccount(username: a.username)
accountToDelete = nil
}
}
Button("Cancel", role: .cancel) { accountToDelete = nil }
}
.sheet(isPresented: $showCreateAccount) {
CreateAccountSheet(session: session)
}
.alert("Reset Password", isPresented: $showResetPassword) {
SecureField("New password", text: $resetPassword)
.accessibilityLabel("New password for account")
Button("Reset") {
if let a = resetForAccount, !resetPassword.isEmpty {
session.resetPassword(username: a.username, newPassword: resetPassword)
}
resetPassword = ""
resetForAccount = nil
}
Button("Cancel", role: .cancel) {
resetPassword = ""
resetForAccount = nil
}
} message: {
Text("Enter a new password for \(resetForAccount?.username ?? "").")
}
}
}
private struct AccountRowView: View {
let account: Account
private var joinedDate: String {
let date = Date(timeIntervalSince1970: Double(account.createdAtUnixMs) / 1000)
return date.formatted(.dateTime.year().month().day())
}
var body: some View {
VStack(alignment: .leading, spacing: 2) {
HStack {
Text(account.username)
.fontWeight(.medium)
if account.isAdmin {
Text("admin")
.font(.caption2)
.padding(.horizontal, 6).padding(.vertical, 2)
.background(.orange.opacity(0.2), in: Capsule())
.foregroundStyle(.orange)
}
}
Text("Created \(joinedDate)")
.font(.caption)
.foregroundStyle(.secondary)
}
.accessibilityElement(children: .combine)
.accessibilityLabel("\(account.username)\(account.isAdmin ? ", administrator" : ""), created \(joinedDate)")
}
}
private struct CreateAccountSheet: View {
@Bindable var session: SessionState
@Environment(\.dismiss) private var dismiss
@State private var username = ""
@State private var password = ""
var body: some View {
NavigationStack {
Form {
Section {
TextField("Username", text: $username)
.textContentType(.username)
.autocorrectionDisabled()
.textInputAutocapitalization(.never)
.accessibilityLabel("Username")
SecureField("Password", text: $password)
.textContentType(.newPassword)
.accessibilityLabel("Password")
}
}
.navigationTitle("Create Account")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { dismiss() }
}
ToolbarItem(placement: .confirmationAction) {
Button("Create") {
session.createAccount(username: username, password: password)
dismiss()
}
.disabled(username.isEmpty || password.isEmpty)
}
}
}
}
}