From 982a23f7496a7b447c33f0b225de1b6bdc52c19f Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Wed, 19 Mar 2025 13:57:14 +1100 Subject: [PATCH] chore: prompt for sign in when turning VPN on if signed out --- .../Preview Content/PreviewVPN.swift | 2 ++ .../Coder-Desktop/VPN/VPNService.swift | 1 + Coder-Desktop/Coder-Desktop/Views/VPNMenu.swift | 17 ++++++++++++++--- Coder-Desktop/Coder-DesktopTests/Util.swift | 1 + .../Coder-DesktopTests/VPNMenuTests.swift | 2 +- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift b/Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift index 4faa10fb..a3ef51e5 100644 --- a/Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift +++ b/Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift @@ -78,4 +78,6 @@ final class PreviewVPN: Coder_Desktop.VPNService { func configureTunnelProviderProtocol(proto _: NETunnelProviderProtocol?) { state = .connecting } + + var startWhenReady: Bool = false } diff --git a/Coder-Desktop/Coder-Desktop/VPN/VPNService.swift b/Coder-Desktop/Coder-Desktop/VPN/VPNService.swift index 22a3ad8b..50078d5f 100644 --- a/Coder-Desktop/Coder-Desktop/VPN/VPNService.swift +++ b/Coder-Desktop/Coder-Desktop/VPN/VPNService.swift @@ -10,6 +10,7 @@ protocol VPNService: ObservableObject { func start() async func stop() async func configureTunnelProviderProtocol(proto: NETunnelProviderProtocol?) + var startWhenReady: Bool { get set } } enum VPNServiceState: Equatable { diff --git a/Coder-Desktop/Coder-Desktop/Views/VPNMenu.swift b/Coder-Desktop/Coder-Desktop/Views/VPNMenu.swift index 352123de..c3c44dba 100644 --- a/Coder-Desktop/Coder-Desktop/Views/VPNMenu.swift +++ b/Coder-Desktop/Coder-Desktop/Views/VPNMenu.swift @@ -4,6 +4,7 @@ struct VPNMenu: View { @EnvironmentObject var vpn: VPN @EnvironmentObject var state: AppState @Environment(\.openSettings) private var openSettings + @Environment(\.openWindow) private var openWindow let inspection = Inspection() @@ -16,7 +17,18 @@ struct VPNMenu: View { Toggle(isOn: Binding( get: { vpn.state == .connected || vpn.state == .connecting }, set: { isOn in Task { - if isOn { await vpn.start() } else { await vpn.stop() } + if isOn { + // Clicking the toggle while logged out should + // open the login window, then start the VPN asap + if !state.hasSession { + vpn.startWhenReady = true + openWindow(id: .login) + } else { + await vpn.start() + } + } else { + await vpn.stop() + } } } )) { @@ -86,8 +98,7 @@ struct VPNMenu: View { } private var vpnDisabled: Bool { - !state.hasSession || - vpn.state == .connecting || + vpn.state == .connecting || vpn.state == .disconnecting || // Prevent starting the VPN before the user has approved the system extension. vpn.state == .failed(.systemExtensionError(.needsUserApproval)) diff --git a/Coder-Desktop/Coder-DesktopTests/Util.swift b/Coder-Desktop/Coder-DesktopTests/Util.swift index 4b1d0e7c..c41f5c19 100644 --- a/Coder-Desktop/Coder-DesktopTests/Util.swift +++ b/Coder-Desktop/Coder-DesktopTests/Util.swift @@ -23,6 +23,7 @@ class MockVPNService: VPNService, ObservableObject { } func configureTunnelProviderProtocol(proto _: NETunnelProviderProtocol?) {} + var startWhenReady: Bool = false } extension Inspection: @unchecked Sendable, @retroactive InspectionEmissary {} diff --git a/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift b/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift index c38a062d..616e3c53 100644 --- a/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift +++ b/Coder-Desktop/Coder-DesktopTests/VPNMenuTests.swift @@ -23,7 +23,7 @@ struct VPNMenuTests { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in let toggle = try view.find(ViewType.Toggle.self) - #expect(toggle.isDisabled()) + #expect(!toggle.isDisabled()) #expect(throws: Never.self) { try view.find(text: "Sign in to use Coder Desktop") } #expect(throws: Never.self) { try view.find(button: "Sign in") } }