92 lines
3.8 KiB
Swift
92 lines
3.8 KiB
Swift
import Foundation
|
|
import TermindApplication
|
|
import TermindDomain
|
|
|
|
/// A menu snapshot, built only in response to a context click, never during
|
|
/// SwiftUI row rendering. Actions retain the caller's corresponding targets.
|
|
@MainActor
|
|
struct SFTPFileContextMenu {
|
|
enum Action: Equatable {
|
|
case open
|
|
case openWith(URL)
|
|
case download, copy, copyNames, copyPaths, paste
|
|
case rename, editPermissions, delete, newFolder, upload, refresh
|
|
}
|
|
|
|
let file: RemoteFile
|
|
let targets: [RemoteFile]
|
|
let connected: Bool
|
|
let allowsRemoteMutations: Bool
|
|
let canPaste: Bool
|
|
let isLoading: Bool
|
|
let locale: Locale
|
|
|
|
func entries(
|
|
applications: @MainActor (String) -> [SFTPFileApplication] = SFTPApplicationOpener.applications(for:),
|
|
perform: @escaping (Action) -> Void
|
|
) -> [NativeMenuEntry] {
|
|
let single = targets.count == 1
|
|
var result: [NativeMenuEntry] = []
|
|
if single {
|
|
result.append(entry("Open", symbol: MenuSymbol.open) { perform(.open) })
|
|
if !file.isDirectory {
|
|
var openWith = SFTPOpenWithMenu(filename: file.name, locale: locale) {
|
|
perform(.openWith($0))
|
|
}.entry(applications: applications(file.name))
|
|
openWith.isEnabled = connected
|
|
result.append(openWith)
|
|
}
|
|
}
|
|
result.append(
|
|
entry(single ? "Download" : "Download \(targets.count) Items", symbol: MenuSymbol.download) {
|
|
perform(.download)
|
|
})
|
|
result.append(.separator)
|
|
result.append(
|
|
entry(single ? "Copy" : "Copy \(targets.count) Items", symbol: MenuSymbol.copy) { perform(.copy) })
|
|
result.append(
|
|
NativeMenuEntry(
|
|
title: localized("Copy As"), symbol: MenuSymbol.copy,
|
|
children: [
|
|
entry(single ? "Name" : "Names") { perform(.copyNames) },
|
|
entry(single ? "Path" : "Paths") { perform(.copyPaths) },
|
|
]))
|
|
result.append(entry("Paste", symbol: MenuSymbol.paste, enabled: connected && canPaste) { perform(.paste) })
|
|
if single {
|
|
result.append(
|
|
entry("Rename", symbol: MenuSymbol.rename, enabled: allowsRemoteMutations) { perform(.rename) })
|
|
if file.kind == .file || file.kind == .directory {
|
|
result.append(
|
|
entry("Edit Permissions", symbol: MenuSymbol.editPermissions, enabled: allowsRemoteMutations) {
|
|
perform(.editPermissions)
|
|
})
|
|
}
|
|
}
|
|
result.append(
|
|
entry(
|
|
single ? "Delete" : "Delete \(targets.count) Items", symbol: MenuSymbol.delete,
|
|
imageVisibility: .visible, enabled: allowsRemoteMutations
|
|
) { perform(.delete) })
|
|
result.append(.separator)
|
|
result.append(entry("New Folder", symbol: MenuSymbol.newFolder, enabled: connected) { perform(.newFolder) })
|
|
result.append(entry("Upload", symbol: MenuSymbol.upload, enabled: connected) { perform(.upload) })
|
|
result.append(
|
|
entry("Refresh", symbol: MenuSymbol.refresh, enabled: connected && !isLoading) { perform(.refresh) })
|
|
return result
|
|
}
|
|
|
|
private func localized(_ title: String.LocalizationValue) -> String {
|
|
AppLocalization.string(title, locale: locale)
|
|
}
|
|
|
|
private func entry(
|
|
_ title: String.LocalizationValue, symbol: String? = nil,
|
|
imageVisibility: MenuImageVisibility = .automatic, enabled: Bool = true,
|
|
action: @escaping () -> Void
|
|
) -> NativeMenuEntry {
|
|
NativeMenuEntry(
|
|
title: localized(title), symbol: symbol, imageVisibility: imageVisibility,
|
|
isEnabled: enabled, action: action)
|
|
}
|
|
}
|