Files

122 lines
5.7 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import SwiftUI
import TermindApplication
import TermindDomain
public struct RemoteSessionPromptView: View {
private let coordinator: RemoteSessionCoordinator
private let cancel: () -> Void
@Environment(\.locale) private var locale
public init(coordinator: RemoteSessionCoordinator, cancel: @escaping () -> Void) {
self.coordinator = coordinator
self.cancel = cancel
}
public var body: some View {
if let prompt = coordinator.prompt {
VStack(alignment: .leading, spacing: 16) {
Text(AppStrings.SessionPersistence.title)
.font(.headline)
.fixedSize(horizontal: false, vertical: true)
if let failure = prompt.failure {
Text(message(for: failure))
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
} else {
if prompt.sessions.isEmpty {
Text(AppStrings.SessionPersistence.empty).foregroundStyle(.secondary)
} else {
ScrollView {
LazyVStack(alignment: .leading, spacing: 8) {
ForEach(prompt.sessions) { session in
Button {
coordinator.choose(.attach(session), for: prompt.id)
} label: {
VStack(alignment: .leading) {
Text(verbatim: session.name)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
if let windows = session.windows, let clients = session.clients {
Text(
verbatim:
AppStrings.SessionPersistence.details(
windows: windows, clients: clients, locale: locale)
)
.font(.caption).foregroundStyle(.secondary)
} else if let windows = session.windows {
Text(AppStrings.SessionPersistence.windows(windows))
.font(.caption).foregroundStyle(.secondary)
} else if let clients = session.clients {
Text(AppStrings.SessionPersistence.clients(clients))
.font(.caption).foregroundStyle(.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.buttonStyle(.bordered)
}
}
}.frame(maxHeight: 280)
}
}
Divider()
ViewThatFits(in: .horizontal) {
HStack {
cancelAction(for: prompt)
Spacer()
HStack {
skipAction(for: prompt)
primaryAction(for: prompt)
}
.fixedSize(horizontal: true, vertical: false)
}
VStack(alignment: .leading, spacing: 12) {
cancelAction(for: prompt)
skipAction(for: prompt)
primaryAction(for: prompt)
}
}
#if os(iOS)
.controlSize(.large)
#endif
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
private func cancelAction(for prompt: RemoteSessionCoordinator.Prompt) -> some View {
Button(AppStrings.Common.cancel.swiftUIKey, role: .cancel) {
coordinator.cancelSelection(for: prompt.id) { cancel() }
}
.keyboardShortcut(.cancelAction)
}
private func skipAction(for prompt: RemoteSessionCoordinator.Prompt) -> some View {
Button(AppStrings.Common.skip.swiftUIKey) {
coordinator.choose(.plainShell, for: prompt.id)
}
}
private func primaryAction(for prompt: RemoteSessionCoordinator.Prompt) -> some View {
Button(prompt.failure == nil ? AppStrings.Common.new.swiftUIKey : AppStrings.Common.retry.swiftUIKey) {
coordinator.choose(prompt.failure == nil ? .create : .retry, for: prompt.id)
}
.buttonStyle(.borderedProminent)
.fixedSize(horizontal: true, vertical: false)
}
private func message(for failure: RemoteSessionFailure) -> LocalizedStringResource {
switch failure {
case .missing: AppStrings.SessionPersistence.missing
case .unsupported: AppStrings.SessionPersistence.unsupported
case .startupUnavailable: AppStrings.SessionPersistence.startupUnavailable
case .timedOut: AppStrings.SessionPersistence.timedOut
case .sessionMissing, .ownershipMismatch: AppStrings.SessionPersistence.changed
case .invalidResponse, .commandFailed: AppStrings.SessionPersistence.failed
}
}
}