diff --git a/TFI/Features/Workspaces/MobileWorkspaceListView.swift b/TFI/Features/Workspaces/MobileWorkspaceListView.swift index f1f3266c..33dcae44 100644 --- a/TFI/Features/Workspaces/MobileWorkspaceListView.swift +++ b/TFI/Features/Workspaces/MobileWorkspaceListView.swift @@ -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) } ) } } diff --git a/TFI/Features/Workspaces/MobileWorkspaceView.swift b/TFI/Features/Workspaces/MobileWorkspaceView.swift index 321bbc1c..45da7c24 100644 --- a/TFI/Features/Workspaces/MobileWorkspaceView.swift +++ b/TFI/Features/Workspaces/MobileWorkspaceView.swift @@ -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) } } } diff --git a/TFITests/MobileAISettingsToolbarTests.swift b/TFITests/MobileAISettingsToolbarTests.swift index 41e4f8e8..42bce914 100644 --- a/TFITests/MobileAISettingsToolbarTests.swift +++ b/TFITests/MobileAISettingsToolbarTests.swift @@ -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") } diff --git a/TFITests/MobileDropTests.swift b/TFITests/MobileDropTests.swift index f1e72c91..6198277f 100644 --- a/TFITests/MobileDropTests.swift +++ b/TFITests/MobileDropTests.swift @@ -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 }) diff --git a/TFITests/MobileWorkspaceMenuTests.swift b/TFITests/MobileWorkspaceMenuTests.swift index 2cfeb65f..8baf74e8 100644 --- a/TFITests/MobileWorkspaceMenuTests.swift +++ b/TFITests/MobileWorkspaceMenuTests.swift @@ -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") }