Simplify iOS workspace close actions

This commit is contained in:
Kaede Akino committed 2026-09-22 21:47:05 +08:00
1 parent 738b14920c
commit e42fe7a548
5 files changed
+28 -24

No files matched your search

@@ -84,8 +84,7 @@ struct MobileWorkspaceListView: View {
canCloseMultipleWorkspaces: scene.canCloseMultipleWorkspaces,
openSameDirectory: scene.openSameDirectory,
closeWorkspace: scene.closeWorkspace,
closeOtherWorkspaces: scene.closeOtherWorkspaces,
closeAllWorkspaces: scene.closeAllWorkspaces
closeOtherWorkspaces: scene.closeOtherWorkspaces
)
// The title menu replaces this pushed detail in place. A new identity tears down
// SFTP's native search state before an SSH detail is mounted.
@@ -171,8 +170,7 @@ struct MobileWorkspaceListView: View {
canCloseMultipleWorkspaces: scene.canCloseMultipleWorkspaces,
rename: { scene.beginWorkspaceRename(item.descriptor) },
close: { scene.closeWorkspace(item.id) },
closeOthers: { scene.closeOtherWorkspaces(than: item.id) },
closeAll: scene.closeAllWorkspaces
closeOthers: { scene.closeOtherWorkspaces(than: item.id) }
)
}
}
@@ -29,7 +29,6 @@ struct MobileWorkspaceView: View {
let openSameDirectory: (MobileWorkspaceDirectoryLaunch) -> Void
let closeWorkspace: (UUID) -> Void
let closeOtherWorkspaces: (UUID) -> Void
let closeAllWorkspaces: () -> Void
@State private var terminalSheet: TerminalSheet?
@State private var importing = false
@State private var directoryName = ""
@@ -245,8 +244,7 @@ struct MobileWorkspaceView: View {
canCloseMultipleWorkspaces: canCloseMultipleWorkspaces,
rename: { renameWorkspace(workspace.descriptor) },
close: { closeWorkspace(workspace.id) },
closeOthers: { closeOtherWorkspaces(workspace.id) },
closeAll: closeAllWorkspaces
closeOthers: { closeOtherWorkspaces(workspace.id) }
)
}
@@ -751,15 +749,13 @@ struct MobileWorkspaceLifecycleMenuActions: View {
let rename: () -> Void
let close: () -> Void
let closeOthers: () -> Void
let closeAll: () -> Void
var body: some View {
Button("Rename", systemImage: "pencil", action: rename)
Divider()
Button("Close", systemImage: "xmark", role: .destructive, action: close)
if canCloseMultipleWorkspaces {
Button("Close Other Workspaces", role: .destructive, action: closeOthers)
Button("Close All Workspaces", systemImage: "xmark", role: .destructive, action: closeAll)
Divider()
Button("Close Other Workspaces", systemImage: "xmark", role: .destructive, action: closeOthers)
}
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ private struct WorkspaceTabBarHarness: View {
selectWorkspace: { _ in }, renameWorkspace: { _ in },
canCloseMultipleWorkspaces: false,
openSameDirectory: { _ in }, closeWorkspace: { _ in },
closeOtherWorkspaces: { _ in }, closeAllWorkspaces: {})
closeOtherWorkspaces: { _ in })
} else {
Text("Workspaces")
}
+2 -2
View File
@@ -180,7 +180,7 @@ struct MobileWorkspaceDropTests {
workspace: workspace, workspaces: [workspace.descriptor], selectWorkspace: { _ in },
renameWorkspace: { _ in }, canCloseMultipleWorkspaces: false,
openSameDirectory: { _ in }, closeWorkspace: { _ in },
closeOtherWorkspaces: { _ in }, closeAllWorkspaces: {})
closeOtherWorkspaces: { _ in })
}
.environment(\.scenePhase, .active))
window.rootViewController = controller
@@ -240,7 +240,7 @@ struct MobileWorkspaceDropTests {
workspace: workspace, workspaces: [workspace.descriptor], selectWorkspace: { _ in },
renameWorkspace: { _ in }, canCloseMultipleWorkspaces: false,
openSameDirectory: { _ in }, closeWorkspace: { _ in },
closeOtherWorkspaces: { _ in }, closeAllWorkspaces: {})
closeOtherWorkspaces: { _ in })
}
.environment(\.scenePhase, .active)
.transaction { $0.disablesAnimations = true })
+20 -10
View File
@@ -18,13 +18,13 @@ struct MobileWorkspaceMenuTests {
library: app.library, pro: app.pro, commandGenerator: app.commandGenerator,
workspace: workspace, workspaces: [opened, dormant],
selectWorkspace: { _ in }, renameWorkspace: { _ in }, canCloseMultipleWorkspaces: true,
openSameDirectory: { _ in }, closeWorkspace: { _ in }, closeOtherWorkspaces: { _ in },
closeAllWorkspaces: {}
openSameDirectory: { _ in }, closeWorkspace: { _ in }, closeOtherWorkspaces: { _ in }
)
.environment(\.mobileToolbarLayout, MobileToolbarLayout(isTopBar: true))
) { navigation in
let button = try #require(toolbarButton(named: "Workspaces", in: navigation))
let actions = try await resolvedMenuActions(#require(button.menu), in: navigation.view)
let menu = try await resolvedMenu(#require(button.menu), in: navigation.view)
let actions = menuActions(menu)
#expect(actions.map(\.title) == ["Opened", "Dormant"])
}
}
@@ -37,14 +37,24 @@ struct MobileWorkspaceMenuTests {
LifecycleMenuHost(canCloseMultipleWorkspaces: canCloseMultipleWorkspaces)
) { navigation in
let button = try #require(toolbarButton(named: "Workspace Actions", in: navigation))
let actions = try await resolvedMenuActions(#require(button.menu), in: navigation.view)
let menu = try await resolvedMenu(#require(button.menu), in: navigation.view)
let actions = menuActions(menu)
let expected =
canCloseMultipleWorkspaces
? ["Rename", "Close", "Close Other Workspaces", "Close All Workspaces"]
? ["Rename", "Close", "Close Other Workspaces"]
: ["Rename", "Close"]
#expect(actions.map(\.title) == expected)
#expect(!actions[0].attributes.contains(.destructive))
#expect(actions.dropFirst().allSatisfy { $0.attributes.contains(.destructive) })
#expect(actions.contains { $0.title == "Close All Workspaces" } == false)
if canCloseMultipleWorkspaces {
let closeOthers = try #require(actions.last)
#expect(closeOthers.image == UIImage(systemName: "xmark"))
}
let separatedActions = menu.children.compactMap { $0 as? UIMenu }
.filter { $0.options.contains(.displayInline) }
.map { menuActions($0).map(\.title) }
#expect(separatedActions == (canCloseMultipleWorkspaces ? [["Close Other Workspaces"]] : []))
}
}
@@ -69,7 +79,7 @@ struct MobileWorkspaceMenuTests {
try await check(navigation)
}
private func resolvedMenuActions(_ menu: UIMenu, in view: UIView) async throws -> [UIAction] {
private func resolvedMenu(_ menu: UIMenu, in view: UIView) async throws -> UIMenu {
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 44, height: 44))
button.menu = menu
button.showsMenuAsPrimaryAction = true
@@ -78,14 +88,14 @@ struct MobileWorkspaceMenuTests {
button.performPrimaryAction()
try await Task.sleep(for: .milliseconds(500))
let interaction = try #require(button.contextMenuInteraction)
var actions: [UIAction] = []
var resolvedMenu: UIMenu?
interaction.updateVisibleMenu { resolved in
actions = menuActions(resolved)
resolvedMenu = resolved
return resolved
}
interaction.dismissMenu()
try await Task.sleep(for: .milliseconds(300))
return actions
return try #require(resolvedMenu)
}
private func menuActions(_ menu: UIMenu) -> [UIAction] {
@@ -132,7 +142,7 @@ private struct LifecycleMenuHost: View {
Menu {
MobileWorkspaceLifecycleMenuActions(
canCloseMultipleWorkspaces: canCloseMultipleWorkspaces,
rename: {}, close: {}, closeOthers: {}, closeAll: {})
rename: {}, close: {}, closeOthers: {})
} label: {
Label("Workspace Actions", systemImage: "ellipsis")
}