added a prompt to TextBox

fix right alignment in textbox
added quick connect
fix blank keys being returned from getKeys()
safely unwrap keys in keydetailview
swap buttons on shellview's titlbar
This commit is contained in:
neon443
2025-06-30 14:34:57 +01:00
parent 24bf52ff16
commit 94ad2fa661
6 changed files with 58 additions and 30 deletions

View File

@@ -185,6 +185,7 @@ class HostsManager: ObservableObject, @unchecked Sendable {
func getKeys() -> [Keypair] {
var result: [Keypair] = []
for host in hosts {
guard host.privateKey != nil && host.publicKey != nil else { continue }
let keypair = Keypair(publicKey: host.publicKey, privateKey: host.privateKey)
if !result.contains(keypair) {
result.append(keypair)

View File

@@ -51,34 +51,35 @@ struct ConnectionView: View {
.id(handler.host)
.frame(width: 60, height: 60)
TextBox(label: "Icon Text", text: $handler.host.label)
TextBox(label: "Icon Text", text: $handler.host.label, prompt: "a few letters in the icon")
}
}
Section {
Text("\(handler.state)")
.foregroundStyle(handler.state.color)
TextBox(label: "Name", text: $handler.host.name)
TextBox(label: "Name", text: $handler.host.name, prompt: "defaults to host address")
TextBox(label: "Address", text: $handler.host.address)
TextBox(label: "Address", text: $handler.host.address, prompt: "required")
TextBox(label: "Port", text: Binding(
get: { String(handler.host.port) },
set: {
if let input = Int($0) {
handler.host.port = input
}
}),
get: { String(handler.host.port) },
set: {
if let input = Int($0) {
handler.host.port = input
}
}),
prompt: "most likely 22",
keyboardType: .numberPad
)
}
Section {
TextBox(label: "Username", text: $handler.host.username)
TextBox(label: "Username", text: $handler.host.username, prompt: "required")
TextBox(label: "Password", text: $handler.host.password, secure: true)
TextBox(label: "Password", text: $handler.host.password, prompt: "not required if using publickeys", secure: true)
TextBox(label: "Publickey", text: $pubkeyStr)
TextBox(label: "Publickey", text: $pubkeyStr, prompt: "in openssh format")
.onChange(of: pubkeyStr) { _ in
let newStr = pubkeyStr.replacingOccurrences(of: "\r\n", with: "")
handler.host.publicKey = Data(newStr.utf8)
@@ -88,7 +89,7 @@ struct ConnectionView: View {
handler.host.publicKey = Data(newStr.utf8)
}
TextBox(label: "Privatekey", text: $privkeyStr, secure: true)
TextBox(label: "Privatekey", text: $privkeyStr, prompt: "required if using publickeys", secure: true)
.onSubmit {
let newStr = privkeyStr.replacingOccurrences(of: "\r\n", with: "")
handler.host.privateKey = Data(newStr.utf8)
@@ -98,7 +99,7 @@ struct ConnectionView: View {
handler.host.privateKey = Data(newStr.utf8)
}
TextBox(label: "Passphrase", text: $handler.host.passphrase)
TextBox(label: "Passphrase", text: $handler.host.passphrase, prompt: "optional")
}
Button() {

View File

@@ -32,6 +32,24 @@ struct HostsView: View {
}
.id(host)
.animation(.default, value: host)
.fullScreenCover(
isPresented: Binding(
get: { checkShell(handler.state) },
set: { newValue in
handler.go()
}
)
) {
ShellView(handler: handler, hostsManager: hostsManager)
}
.swipeActions(edge: .leading) {
Button() {
handler.go()
} label: {
Label("Quick Connect", systemImage: "power")
}
.foregroundStyle(.green)
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
hostsManager.removeHost(host)
@@ -43,6 +61,7 @@ struct HostsView: View {
} label: {
Label("Duplicate", systemImage: "square.filled.on.square")
}
.foregroundStyle(.blue)
}
}
.onMove(perform: {

View File

@@ -12,6 +12,13 @@ struct KeyDetailView: View {
@State var keypair: Keypair
@State private var reveal: Bool = false
var publicKey: Data {
return keypair.publicKey ?? "".data(using: .utf8)!
}
var privateKey: Data {
return keypair.privateKey ?? "".data(using: .utf8)!
}
var body: some View {
ZStack {
hostsManager.selectedTheme.background.suiColor.opacity(0.7)
@@ -31,14 +38,14 @@ struct KeyDetailView: View {
VStack(alignment: .leading) {
Text("Public key")
.bold()
Text(String(data: keypair.publicKey!, encoding: .utf8) ?? "nil")
Text(String(data: publicKey, encoding: .utf8) ?? "nil")
}
VStack(alignment: .leading) {
Text("Private key")
.bold()
.frame(maxWidth: .infinity)
ZStack(alignment: .center) {
Text(String(data: keypair.privateKey!, encoding: .utf8) ?? "nil")
Text(String(data: privateKey, encoding: .utf8) ?? "nil")
.blur(radius: reveal ? 0 : 5)
VStack {
Image(systemName: "eye.slash.fill")
@@ -62,9 +69,7 @@ struct KeyDetailView: View {
Button {
Task {
guard await hostsManager.authWithBiometrics() else { return }
if let privateKey = keypair.privateKey {
UIPasteboard.general.string = String(data: privateKey, encoding: .utf8)
}
UIPasteboard.general.string = String(data: privateKey, encoding: .utf8)
}
} label: {
CenteredLabel(title: "Copy private key", systemName: "document.on.document")

View File

@@ -10,22 +10,24 @@ import SwiftUI
struct TextBox: View {
@State var label: String
@Binding var text: String
@State var prompt: String
@State var secure: Bool = false
@State var keyboardType: UIKeyboardType = .default
var body: some View {
HStack {
Text(label)
Spacer()
if secure {
SecureField("", text: $text)
SecureField("", text: $text, prompt: Text(prompt))
.multilineTextAlignment(.trailing)
} else {
TextField("", text: $text)
TextField("", text: $text, prompt: Text(prompt))
.multilineTextAlignment(.trailing)
}
}
}
}
#Preview {
TextBox(label: "Label", text: .constant("asdflkajsdl"))
TextBox(label: "Label", text: .constant("asdflkajsdl"), prompt: "")
}

View File

@@ -47,13 +47,6 @@ struct ShellView: View {
.navigationTitle(handler.title)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button() {
dismiss()
} label: {
Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
}
}
ToolbarItem(placement: .cancellationAction) {
Button() {
handler.disconnect()
@@ -62,6 +55,13 @@ struct ShellView: View {
Label("Disconnect", systemImage: "xmark.app.fill")
}
}
ToolbarItem(placement: .cancellationAction) {
Button() {
dismiss()
} label: {
Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
}
}
}
}
}