diff --git a/ShhShell.xcodeproj/project.pbxproj b/ShhShell.xcodeproj/project.pbxproj index 9c0e509..64db27e 100644 --- a/ShhShell.xcodeproj/project.pbxproj +++ b/ShhShell.xcodeproj/project.pbxproj @@ -341,8 +341,8 @@ A96C6B042E0C523E00F377FE /* Hosts */ = { isa = PBXGroup; children = ( - A985545C2E055D4D009051BD /* ConnectionView.swift */, A98554622E0587DF009051BD /* HostsView.swift */, + A985545C2E055D4D009051BD /* ConnectionView.swift */, ); path = Hosts; sourceTree = ""; diff --git a/ShhShell/Host/HostsManager.swift b/ShhShell/Host/HostsManager.swift index 4f4680d..e84f2c6 100644 --- a/ShhShell/Host/HostsManager.swift +++ b/ShhShell/Host/HostsManager.swift @@ -133,7 +133,8 @@ class HostsManager: ObservableObject, @unchecked Sendable { } } - func makeLabel(forHost: Host) -> String { + func makeLabel(forHost: Host?) -> String { + guard let forHost else { return "" } if forHost.name.isEmpty && forHost.address.isEmpty { return forHost.id.uuidString } else if forHost.name.isEmpty { diff --git a/ShhShell/SSH/SSHHandler.swift b/ShhShell/SSH/SSHHandler.swift index c079595..627d3ab 100644 --- a/ShhShell/SSH/SSHHandler.swift +++ b/ShhShell/SSH/SSHHandler.swift @@ -15,6 +15,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject { private var session: ssh_session? private var channel: ssh_channel? + @MainActor var container: TerminalViewContainer { + TerminalViewContainer.shared + } var sessionID: UUID? var scrollback: [String] = [] @@ -140,7 +143,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { if let sessionID { Task { @MainActor in - TerminalViewContainer.shared.removeValue(forKey: sessionID) + container.sessions.removeValue(forKey: sessionID) self.sessionID = nil } } diff --git a/ShhShell/Views/Hosts/HostsView.swift b/ShhShell/Views/Hosts/HostsView.swift index a6e4d39..743818e 100644 --- a/ShhShell/Views/Hosts/HostsView.swift +++ b/ShhShell/Views/Hosts/HostsView.swift @@ -17,36 +17,38 @@ struct HostsView: View { Text("Add your first Host!") } - ForEach(hostsManager.hosts) { host in - NavigationLink() { - ConnectionView( - handler: SSHHandler(host: host), - hostsManager: hostsManager, - keyManager: keyManager - ) - } label: { - SymbolPreview(symbol: host.symbol, label: host.label) - .frame(width: 40, height: 40) - Text(hostsManager.makeLabel(forHost: host)) - } - .id(host) - .animation(.default, value: host) - .swipeActions(edge: .trailing) { - Button(role: .destructive) { - hostsManager.removeHost(host) + Section("Hosts") { + ForEach(hostsManager.hosts) { host in + NavigationLink() { + ConnectionView( + handler: SSHHandler(host: host), + hostsManager: hostsManager, + keyManager: keyManager + ) } label: { - Label("Delete", systemImage: "trash") + SymbolPreview(symbol: host.symbol, label: host.label) + .frame(width: 40, height: 40) + Text(hostsManager.makeLabel(forHost: host)) } - Button() { - hostsManager.duplicateHost(host) - } label: { - Label("Duplicate", systemImage: "square.filled.on.square") + .id(host) + .animation(.default, value: host) + .swipeActions(edge: .trailing) { + Button(role: .destructive) { + hostsManager.removeHost(host) + } label: { + Label("Delete", systemImage: "trash") + } + Button() { + hostsManager.duplicateHost(host) + } label: { + Label("Duplicate", systemImage: "square.filled.on.square") + } } } + .onMove(perform: { + hostsManager.moveHost(from: $0, to: $1) + }) } - .onMove(perform: { - hostsManager.moveHost(from: $0, to: $1) - }) Section() { NavigationLink { diff --git a/ShhShell/Views/Sessions/SessionView.swift b/ShhShell/Views/Sessions/SessionView.swift index 5d1974c..0c1ce06 100644 --- a/ShhShell/Views/Sessions/SessionView.swift +++ b/ShhShell/Views/Sessions/SessionView.swift @@ -9,20 +9,34 @@ import SwiftUI struct SessionView: View { @ObservedObject var hostsManager: HostsManager + @ObservedObject var container = TerminalViewContainer.shared + @State var key: UUID @State var shellPresented: Bool = false + var host: Host { + container.sessions[key]?.handler.host ?? Host.blank + } + var body: some View { - Text(key.uuidString) - .onTapGesture { - shellPresented.toggle() - } - .fullScreenCover(isPresented: $shellPresented) { - ShellView( - handler: TerminalViewContainer.shared[key]!.handler, - hostsManager: hostsManager - ) - } + HStack { + Image(systemName: "apple.terminal") + .resizable().scaledToFit() + .frame(width: 40, height: 40) + .foregroundStyle(.terminalGreen) + SymbolPreview(symbol: host.symbol, label: host.label) + .frame(width: 40, height: 40) + Text(hostsManager.makeLabel(forHost: host)) + } + .onTapGesture { + shellPresented.toggle() + } + .fullScreenCover(isPresented: $shellPresented) { + ShellView( + handler: container.sessions[key]?.handler ?? SSHHandler(host: Host.blank), + hostsManager: hostsManager + ) + } } } diff --git a/ShhShell/Views/Sessions/SessionsListView.swift b/ShhShell/Views/Sessions/SessionsListView.swift index 0768ca1..69b55cd 100644 --- a/ShhShell/Views/Sessions/SessionsListView.swift +++ b/ShhShell/Views/Sessions/SessionsListView.swift @@ -13,10 +13,15 @@ struct SessionsListView: View { @ObservedObject var hostsManager: HostsManager @ObservedObject var keyManager: KeyManager + @ObservedObject var container = TerminalViewContainer.shared + var body: some View { - Section("Sessions") { - ForEach(TerminalViewContainer.shared.map {$0.key}, id: \.self) { key in - SessionView(hostsManager: hostsManager, key: key) + if !container.sessions.isEmpty { + Section("Sessions") { + ForEach(container.sessionIDs, id: \.self) { key in + SessionView(hostsManager: hostsManager, key: key) + .id(container.sessions[key]!.handler.connected) + } } } } diff --git a/ShhShell/Views/Terminal/ShellView.swift b/ShhShell/Views/Terminal/ShellView.swift index edf2424..2cd22d3 100644 --- a/ShhShell/Views/Terminal/ShellView.swift +++ b/ShhShell/Views/Terminal/ShellView.swift @@ -11,6 +11,8 @@ struct ShellView: View { @ObservedObject var handler: SSHHandler @ObservedObject var hostsManager: HostsManager + @ObservedObject var container = TerminalViewContainer.shared + @Environment(\.dismiss) var dismiss var body: some View { @@ -19,7 +21,7 @@ struct ShellView: View { TerminalController(handler: handler, hostsManager: hostsManager) .onAppear { if let sessionID = handler.sessionID { - TerminalViewContainer.shared[sessionID]?.terminalView.restoreScrollback() + container.sessions[sessionID]?.terminalView.restoreScrollback() } } diff --git a/ShhShell/Views/Terminal/TerminalController.swift b/ShhShell/Views/Terminal/TerminalController.swift index e8d167d..c762c34 100644 --- a/ShhShell/Views/Terminal/TerminalController.swift +++ b/ShhShell/Views/Terminal/TerminalController.swift @@ -14,9 +14,11 @@ struct TerminalController: UIViewRepresentable { @ObservedObject var handler: SSHHandler @ObservedObject var hostsManager: HostsManager + @ObservedObject var container = TerminalViewContainer.shared + func makeUIView(context: Context) -> TerminalView { if let sessionID = handler.sessionID { - if let existing = TerminalViewContainer.shared[sessionID] { + if let existing = container.sessions[sessionID] { return existing.terminalView } } @@ -30,7 +32,7 @@ struct TerminalController: UIViewRepresentable { tv.autoresizingMask = [.flexibleWidth, .flexibleHeight] if let sessionID = handler.sessionID { - TerminalViewContainer.shared[sessionID] = TerminalContainer( + container.sessions[sessionID] = TerminalContainer( handler: handler, terminalView: tv ) diff --git a/ShhShell/Views/Terminal/TerminalViewContainer.swift b/ShhShell/Views/Terminal/TerminalViewContainer.swift index 1b1f27c..6f8dbd1 100644 --- a/ShhShell/Views/Terminal/TerminalViewContainer.swift +++ b/ShhShell/Views/Terminal/TerminalViewContainer.swift @@ -7,10 +7,15 @@ import Foundation -public final class TerminalViewContainer { - @MainActor static var shared: [ - UUID: TerminalContainer - ] = [:] +@MainActor +public final class TerminalViewContainer: ObservableObject { + static let shared = TerminalViewContainer() + + @Published var sessions: [UUID: TerminalContainer] = [:] + + var sessionIDs: [UUID] { + return sessions.map({ $0.key }) + } } struct TerminalContainer {