mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 05:19:13 +00:00
using a strikethrough for disconnected sessions
add cancel to disconnected alert stop jumping to last session if disconnected re-add to TerminalViewContainer if reconnect happens move ssh cleanup stuff to cleanup func, only run if we indicate we want a new session
This commit is contained in:
@@ -59,7 +59,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
||||
return String(cString: cString)
|
||||
}
|
||||
|
||||
func go(id: UUID = UUID()) {
|
||||
func go(id: UUID? = nil) {
|
||||
guard !connected else { disconnect(); return }
|
||||
|
||||
do { try connect(id: id) } catch {
|
||||
@@ -108,10 +108,15 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
||||
setTitle("\(host.username)@\(host.address)")
|
||||
}
|
||||
|
||||
func connect(id: UUID) throws(SSHError) {
|
||||
func connect(id: UUID?) throws(SSHError) {
|
||||
guard !host.address.isEmpty else { throw .connectionFailed("No address to connect to.") }
|
||||
withAnimation { state = .connecting }
|
||||
sessionID = id
|
||||
if let id {
|
||||
sessionID = id
|
||||
} else {
|
||||
cleanup()
|
||||
sessionID = UUID()
|
||||
}
|
||||
|
||||
var verbosity: Int = 0
|
||||
// var verbosity: Int = SSH_LOG_FUNCTIONS
|
||||
@@ -145,21 +150,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
// Task {
|
||||
self.hostkeyChanged = false
|
||||
withAnimation { self.state = .idle }
|
||||
withAnimation { self.testSuceeded = nil }
|
||||
// }
|
||||
|
||||
if let sessionID {
|
||||
Task { @MainActor in
|
||||
container.sessions.removeValue(forKey: sessionID)
|
||||
self.sessionID = nil
|
||||
}
|
||||
}
|
||||
scrollback = []
|
||||
// scrollbackSize = 0
|
||||
|
||||
withAnimation { self.state = .idle }
|
||||
//send eof if open
|
||||
if ssh_channel_is_open(channel) == 1 {
|
||||
ssh_channel_send_eof(channel)
|
||||
@@ -174,6 +165,20 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
||||
self.session = nil
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
self.hostkeyChanged = false
|
||||
withAnimation { self.state = .idle }
|
||||
withAnimation { self.testSuceeded = nil }
|
||||
scrollback = []
|
||||
|
||||
if let sessionID {
|
||||
Task { @MainActor in
|
||||
container.sessions.removeValue(forKey: sessionID)
|
||||
self.sessionID = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkHostkey(recieved: String?) {
|
||||
guard host.key == recieved else {
|
||||
self.hostkeyChanged = true
|
||||
|
||||
@@ -101,13 +101,19 @@ final class SSHTerminalDelegate: TerminalView, Sendable, @preconcurrency Termina
|
||||
func startFeedLoop() {
|
||||
guard readTimer == nil else { return }
|
||||
readTimer = Timer(timeInterval: 0.01, repeats: true) { timer in
|
||||
Task(priority: .high) {
|
||||
guard let handler = await self.handler else { return }
|
||||
Task(priority: .high) { @MainActor in
|
||||
guard let handler = self.handler else { return }
|
||||
guard let sessionID = handler.sessionID else { return }
|
||||
if let read = handler.readFromChannel() {
|
||||
Task { @MainActor in
|
||||
self.feed(text: read)
|
||||
}
|
||||
}
|
||||
if !TerminalViewContainer.shared.sessionIDs.contains(sessionID) {
|
||||
Task(priority: .high) { @MainActor in
|
||||
TerminalViewContainer.shared.sessions[sessionID] = TerminalContainer(handler: handler, terminalView: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RunLoop.main.add(readTimer!, forMode: .common)
|
||||
|
||||
@@ -50,12 +50,12 @@ struct ShellTabView: View {
|
||||
background
|
||||
.ignoresSafeArea(.all)
|
||||
VStack(spacing: 0) {
|
||||
let oneTabWidth = max(100, (UIScreen.main.bounds.width)/CGFloat(container.sessionIDs.count))
|
||||
|
||||
//header
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
Button() {
|
||||
for session in container.sessions.values {
|
||||
session.handler.disconnect()
|
||||
session.handler.cleanup()
|
||||
}
|
||||
dismiss()
|
||||
} label: {
|
||||
@@ -73,12 +73,14 @@ struct ShellTabView: View {
|
||||
.foregroundStyle(foreground)
|
||||
.monospaced()
|
||||
.contentTransition(.numericText())
|
||||
.strikethrough(selectedHandler.state != .shellOpen)
|
||||
if container.sessionIDs.count == 1 {
|
||||
Text(selectedHandler.host.description)
|
||||
.bold()
|
||||
.foregroundStyle(foreground)
|
||||
.monospaced()
|
||||
.font(.caption2)
|
||||
.strikethrough(selectedHandler.state != .shellOpen)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
@@ -116,11 +118,14 @@ struct ShellTabView: View {
|
||||
.background(hostsManager.tint, ignoresSafeAreaEdges: .all)
|
||||
.frame(height: 40)
|
||||
|
||||
//tab strip
|
||||
if container.sessionIDs.count > 1 {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
let oneTabWidth: CGFloat = max(100, (UIScreen.main.bounds.width)/CGFloat(container.sessionIDs.count))
|
||||
HStack(spacing: 0) {
|
||||
ForEach(container.sessionIDs, id: \.self) { id in
|
||||
let selected: Bool = selectedID == id
|
||||
let thisHandler: SSHHandler = container.sessions[id]!.handler
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(selected ? hostsManager.tint : background)
|
||||
@@ -133,6 +138,7 @@ struct ShellTabView: View {
|
||||
.foregroundStyle(selected ? foreground : hostsManager.tint)
|
||||
.opacity(0.7)
|
||||
.font(.callout)
|
||||
.strikethrough(thisHandler.state != .shellOpen)
|
||||
}
|
||||
Text(container.sessions[id]!.handler.host.description)
|
||||
.foregroundStyle(selected ? foreground : hostsManager.tint)
|
||||
@@ -140,6 +146,7 @@ struct ShellTabView: View {
|
||||
.monospaced()
|
||||
.bold(selected)
|
||||
.font(.caption2)
|
||||
.strikethrough(thisHandler.state != .shellOpen)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
@@ -168,13 +175,6 @@ struct ShellTabView: View {
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !checkShell(session.handler.state) {
|
||||
if let lastSession = container.sessionIDs.last {
|
||||
withAnimation { self.selectedID = lastSession }
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
if container.sessions.isEmpty {
|
||||
Backgrounder.shared.stopBgTracking()
|
||||
|
||||
@@ -13,6 +13,8 @@ struct ShellView: View {
|
||||
@ObservedObject var hostsManager: HostsManager
|
||||
@ObservedObject var container = TerminalViewContainer.shared
|
||||
|
||||
@State private var forceDismissDisconnectAlert: Bool = false
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var body: some View {
|
||||
@@ -33,6 +35,9 @@ struct ShellView: View {
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
forceDismissDisconnectAlert = false
|
||||
}
|
||||
|
||||
Group {
|
||||
Color.gray.opacity(0.2)
|
||||
@@ -56,7 +61,7 @@ struct ShellView: View {
|
||||
}
|
||||
}
|
||||
|
||||
if !checkShell(handler.state) {
|
||||
if handler.state != .shellOpen && !forceDismissDisconnectAlert {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 25)
|
||||
.fill(hostsManager.selectedTheme.foreground.suiColor)
|
||||
@@ -74,7 +79,7 @@ struct ShellView: View {
|
||||
.font(.title)
|
||||
}
|
||||
Button {
|
||||
handler.go()
|
||||
try! handler.reconnect()
|
||||
} label: {
|
||||
Text("Connect")
|
||||
.foregroundStyle(hostsManager.selectedTheme.background.suiColor)
|
||||
@@ -83,6 +88,16 @@ struct ShellView: View {
|
||||
.background(.tint)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 15))
|
||||
}
|
||||
Button {
|
||||
forceDismissDisconnectAlert = true
|
||||
} label: {
|
||||
Text("Cancel")
|
||||
.foregroundStyle(hostsManager.selectedTheme.background.suiColor)
|
||||
.padding(5)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(.tint.opacity(0.5))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 15))
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user