diff --git a/ShhShell/Host/HostsManager.swift b/ShhShell/Host/HostsManager.swift index e84f2c6..4a69887 100644 --- a/ShhShell/Host/HostsManager.swift +++ b/ShhShell/Host/HostsManager.swift @@ -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) diff --git a/ShhShell/Views/Hosts/ConnectionView.swift b/ShhShell/Views/Hosts/ConnectionView.swift index 2f121f1..23f3da9 100644 --- a/ShhShell/Views/Hosts/ConnectionView.swift +++ b/ShhShell/Views/Hosts/ConnectionView.swift @@ -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() { diff --git a/ShhShell/Views/Hosts/HostsView.swift b/ShhShell/Views/Hosts/HostsView.swift index 743818e..28e24e0 100644 --- a/ShhShell/Views/Hosts/HostsView.swift +++ b/ShhShell/Views/Hosts/HostsView.swift @@ -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: { diff --git a/ShhShell/Views/Keys/KeyDetailView.swift b/ShhShell/Views/Keys/KeyDetailView.swift index fbd7e62..81669bc 100644 --- a/ShhShell/Views/Keys/KeyDetailView.swift +++ b/ShhShell/Views/Keys/KeyDetailView.swift @@ -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") diff --git a/ShhShell/Views/Misc/TextBox.swift b/ShhShell/Views/Misc/TextBox.swift index 52b230a..a150471 100644 --- a/ShhShell/Views/Misc/TextBox.swift +++ b/ShhShell/Views/Misc/TextBox.swift @@ -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: "") } diff --git a/ShhShell/Views/Terminal/ShellView.swift b/ShhShell/Views/Terminal/ShellView.swift index 015dbb6..1939dc7 100644 --- a/ShhShell/Views/Terminal/ShellView.swift +++ b/ShhShell/Views/Terminal/ShellView.swift @@ -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") + } + } } } }