122 lines
5.7 KiB
Swift
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
|
|
}
|
|
}
|
|
}
|