93 lines
3.5 KiB
Swift
93 lines
3.5 KiB
Swift
// Sources/UI/Sessions/SessionSwitcher.swift
|
|
// T-2.6: Sheet UI for listing, selecting, and creating sessions.
|
|
|
|
import SwiftUI
|
|
|
|
struct SessionSwitcher: View {
|
|
@ObservedObject var registry: SessionRegistry
|
|
let credential: SidecarCredential
|
|
let onSelect: (SessionInfo) -> Void
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
@State private var showNewSessionAlert = false
|
|
@State private var newSessionName = ""
|
|
@State private var spawnError: String? = nil
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if registry.isLoading {
|
|
ProgressView("Loading sessions…")
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else {
|
|
List(registry.sessions) { session in
|
|
Button {
|
|
onSelect(session)
|
|
dismiss()
|
|
} label: {
|
|
SessionRow(session: session)
|
|
}
|
|
.foregroundStyle(.primary)
|
|
}
|
|
.overlay {
|
|
if registry.sessions.isEmpty {
|
|
ContentUnavailableView(
|
|
"No Sessions",
|
|
systemImage: "terminal",
|
|
description: Text("Tap + to create a session.")
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Sessions")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Done") { dismiss() }
|
|
}
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button {
|
|
newSessionName = ""
|
|
spawnError = nil
|
|
showNewSessionAlert = true
|
|
} label: {
|
|
Image(systemName: "plus")
|
|
}
|
|
}
|
|
}
|
|
.alert("New Session", isPresented: $showNewSessionAlert) {
|
|
TextField("Session name", text: $newSessionName)
|
|
.autocorrectionDisabled()
|
|
Button("Create") {
|
|
let name = newSessionName.trimmingCharacters(in: .whitespaces)
|
|
guard !name.isEmpty else { return }
|
|
Task {
|
|
do {
|
|
try await registry.spawnSession(name: name, credential: credential)
|
|
await registry.refresh(credential: credential)
|
|
} catch {
|
|
spawnError = error.localizedDescription
|
|
}
|
|
}
|
|
}
|
|
Button("Cancel", role: .cancel) {}
|
|
} message: {
|
|
Text("Enter a name for the new session.")
|
|
}
|
|
.safeAreaInset(edge: .bottom) {
|
|
if let err = registry.error ?? spawnError {
|
|
Text(err)
|
|
.font(.caption)
|
|
.foregroundStyle(.red)
|
|
.padding(8)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(Color(uiColor: .secondarySystemBackground))
|
|
}
|
|
}
|
|
}
|
|
.task { await registry.refresh(credential: credential) }
|
|
}
|
|
}
|