From 3e713b8561cf38ca92804f0877252826732307e8 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:05:57 +0100 Subject: [PATCH] added history view added history loading and saving functions added a thingy that will combine multiple entries like the phone app added history data struct fix hosts with no name just bneing called " copy" when duplicated remove the showterminal button fix crash when closing the terminal --- ShhShell.xcodeproj/project.pbxproj | 8 ++++ ShhShell/Host/History.swift | 15 +++++++ ShhShell/Host/HostsManager.swift | 35 +++++++++++++++- ShhShell/Views/ContentView.swift | 5 +++ ShhShell/Views/Hosts/ConnectionView.swift | 8 +--- ShhShell/Views/Hosts/RecentsView.swift | 48 ++++++++++++++++++++++ ShhShell/Views/Terminal/ShellTabView.swift | 2 +- 7 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 ShhShell/Host/History.swift create mode 100644 ShhShell/Views/Hosts/RecentsView.swift diff --git a/ShhShell.xcodeproj/project.pbxproj b/ShhShell.xcodeproj/project.pbxproj index c48d324..23649a7 100644 --- a/ShhShell.xcodeproj/project.pbxproj +++ b/ShhShell.xcodeproj/project.pbxproj @@ -83,6 +83,8 @@ A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C060EC2E3FBCD000CA9374 /* SnippetPicker.swift */; }; A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4140B2E096DB7005E3047 /* SSHError.swift */; }; A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */; }; + A9CC786B2E4E681400FAEE58 /* RecentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CC786A2E4E681400FAEE58 /* RecentsView.swift */; }; + A9CC786D2E4F534600FAEE58 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CC786C2E4F534600FAEE58 /* History.swift */; }; A9D819292E0E904200442D38 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D819282E0E904200442D38 /* Theme.swift */; }; A9D8192D2E0E9EB500442D38 /* ThemeManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */; }; A9D8192F2E0F1BEE00442D38 /* ThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192E2E0F1BEE00442D38 /* ThemeButton.swift */; }; @@ -214,6 +216,8 @@ A9C060EC2E3FBCD000CA9374 /* SnippetPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetPicker.swift; sourceTree = ""; }; A9C4140B2E096DB7005E3047 /* SSHError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHError.swift; sourceTree = ""; }; A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHHandler.swift; sourceTree = ""; }; + A9CC786A2E4E681400FAEE58 /* RecentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentsView.swift; sourceTree = ""; }; + A9CC786C2E4F534600FAEE58 /* History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = History.swift; sourceTree = ""; }; A9D819282E0E904200442D38 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManagerView.swift; sourceTree = ""; }; A9D8192E2E0F1BEE00442D38 /* ThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeButton.swift; sourceTree = ""; }; @@ -521,6 +525,7 @@ A98554622E0587DF009051BD /* HostsView.swift */, A985545C2E055D4D009051BD /* ConnectionView.swift */, A98CAB432E4229F7005E4C42 /* HostSymbolPicker.swift */, + A9CC786A2E4E681400FAEE58 /* RecentsView.swift */, ); path = Hosts; sourceTree = ""; @@ -542,6 +547,7 @@ A93143BF2DF61B3200FCD5DB /* Host.swift */, A98554602E058433009051BD /* HostsManager.swift */, A9DA97702E0D30ED00142DDC /* HostSymbol.swift */, + A9CC786C2E4F534600FAEE58 /* History.swift */, ); path = Host; sourceTree = ""; @@ -792,6 +798,8 @@ A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */, A9FD37692E16A6BF005319A8 /* ShellTabView.swift in Sources */, A9DA97732E0D40C100142DDC /* HostSymbolPreview.swift in Sources */, + A9CC786B2E4E681400FAEE58 /* RecentsView.swift in Sources */, + A9CC786D2E4F534600FAEE58 /* History.swift in Sources */, A90B38342E3EA046002B56FC /* Bundle.swift in Sources */, A9FD376B2E16DABF005319A8 /* AnsiPickerView.swift in Sources */, A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */, diff --git a/ShhShell/Host/History.swift b/ShhShell/Host/History.swift new file mode 100644 index 0000000..7ef77a6 --- /dev/null +++ b/ShhShell/Host/History.swift @@ -0,0 +1,15 @@ +// +// History.swift +// ShhShell +// +// Created by neon443 on 15/08/2025. +// + +import Foundation + +struct History: Identifiable { + var id: UUID = UUID() + + var host: Host + var count: Int +} diff --git a/ShhShell/Host/HostsManager.swift b/ShhShell/Host/HostsManager.swift index 1ccddb6..c430188 100644 --- a/ShhShell/Host/HostsManager.swift +++ b/ShhShell/Host/HostsManager.swift @@ -24,6 +24,8 @@ class HostsManager: ObservableObject, @unchecked Sendable { @Published var snippets: [Snippet] = [] + @Published var history: [Host] = [] + var tint: SwiftUI.Color { selectedTheme.ansi[selectedAnsi].suiColor } @@ -33,6 +35,37 @@ class HostsManager: ObservableObject, @unchecked Sendable { loadThemes() loadFonts() loadSnippets() + loadHistory() + } + + func loadHistory() { + guard let data = userDefaults.data(forKey: "history") else { return } + guard let decoded = try? JSONDecoder().decode([Host].self, from: data) else { return } + withAnimation { self.history = decoded } + } + + func formatHistory() -> [History] { + var result: [History] = [] + for host in history { + if result.last?.host == host { + guard var lastOne = result.popLast() else { return result } + lastOne.count += 1 + result.append(lastOne) + } else { + result.append(History(host: host, count: 1)) + } + } + return result + } + + func saveHistory() { + let data = try? JSONEncoder().encode(history) + userDefaults.set(data, forKey: "history") + } + + func removeFromHistory(_ toRemove: Host) { + history.removeAll(where: { $0.id == toRemove.id }) + saveHistory() } func addSnippet(_ toAdd: Snippet) { @@ -239,7 +272,7 @@ class HostsManager: ObservableObject, @unchecked Sendable { func duplicateHost(_ hostToDup: Host) { var hostNewID = hostToDup hostNewID.id = UUID() - hostNewID.name.append(" copy") + hostNewID.name = hostToDup.description.appending(" copy") if let index = hosts.firstIndex(where: { $0 == hostToDup }) { hosts.insert(hostNewID, at: index+1) Haptic.medium.trigger() diff --git a/ShhShell/Views/ContentView.swift b/ShhShell/Views/ContentView.swift index 85889c8..f9feea6 100644 --- a/ShhShell/Views/ContentView.swift +++ b/ShhShell/Views/ContentView.swift @@ -24,6 +24,11 @@ struct ContentView: View { keyManager: keyManager ) + RecentsView( + hostsManager: hostsManager, + keyManager: keyManager + ) + HostsView( handler: handler, hostsManager: hostsManager, diff --git a/ShhShell/Views/Hosts/ConnectionView.swift b/ShhShell/Views/Hosts/ConnectionView.swift index 561b438..68deefb 100644 --- a/ShhShell/Views/Hosts/ConnectionView.swift +++ b/ShhShell/Views/Hosts/ConnectionView.swift @@ -81,13 +81,6 @@ struct ConnectionView: View { } } - Button() { - showTerminal.toggle() - } label: { - Label("Show Terminal", systemImage: "apple.terminal") - } - .disabled(!checkShell(handler.state)) - Button() { handler.testExec() } label: { @@ -153,6 +146,7 @@ Hostkey fingerprint is \(handler.getHostkey() ?? "nil") handler.go() showTerminal = checkShell(handler.state) if showTerminal { + hostsManager.history.append(handler.host) handler.writeToChannel(hostsManager.snippets.first(where: { $0.id == handler.host.startupSnippetID })?.content) } } label: { diff --git a/ShhShell/Views/Hosts/RecentsView.swift b/ShhShell/Views/Hosts/RecentsView.swift new file mode 100644 index 0000000..d4a6493 --- /dev/null +++ b/ShhShell/Views/Hosts/RecentsView.swift @@ -0,0 +1,48 @@ +// +// RecentsView.swift +// ShhShell +// +// Created by neon443 on 14/08/2025. +// + +import SwiftUI + +struct RecentsView: View { + @ObservedObject var hostsManager: HostsManager + @ObservedObject var keyManager: KeyManager + + var body: some View { + if !hostsManager.history.isEmpty { + Section("Recents") { + ForEach(hostsManager.formatHistory()) { history in + NavigationLink() { + ConnectionView( + handler: SSHHandler( + host: history.host, + keyManager: keyManager + ), + hostsManager: hostsManager, + keyManager: keyManager + ) + } label: { + Text(history.host.description) + Text("\(history.count)") + } + .swipeActions { + Button("Remove", systemImage: "trash", role: .destructive) { + hostsManager.removeFromHistory(history.host) + } + .tint(.red) + } + } + } + } + } +} + +#Preview { + RecentsView( + hostsManager: HostsManager(), + keyManager: KeyManager() + ) +} diff --git a/ShhShell/Views/Terminal/ShellTabView.swift b/ShhShell/Views/Terminal/ShellTabView.swift index f97d32d..745415d 100644 --- a/ShhShell/Views/Terminal/ShellTabView.swift +++ b/ShhShell/Views/Terminal/ShellTabView.swift @@ -14,7 +14,7 @@ struct ShellTabView: View { @ObservedObject var container = TerminalViewContainer.shared @State var selectedID: UUID? var selectedHandler: SSHHandler { - container.sessions[selectedID ?? UUID()]?.handler ?? handler! + container.sessions[selectedID ?? UUID()]?.handler ?? handler ?? SSHHandler(host: Host.blank, keyManager: nil) } @State var showSnippetPicker: Bool = false