diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 1814c118..f434e31d 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -50,11 +50,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { object: nil ) Task { - // If there's no NE config, then the user needs to sign in. - // However, they might have a session from a previous install, so we - // need to clear it. + // If there's no NE config, but the user is logged in, such as + // from a previous install, then we need to reconfigure. if await !vpn.loadNetworkExtensionConfig() { - state.clearSession() + state.reconfigure() } } } diff --git a/Coder Desktop/Coder Desktop/MenuBarIconController.swift b/Coder Desktop/Coder Desktop/MenuBarIconController.swift index 867e1837..09c73812 100644 --- a/Coder Desktop/Coder Desktop/MenuBarIconController.swift +++ b/Coder Desktop/Coder Desktop/MenuBarIconController.swift @@ -13,6 +13,8 @@ class MenuBarController { init(menuBarExtra: FluidMenuBarExtra) { self.menuBarExtra = menuBarExtra + // Off by default, as `vpnDidUpdate` isn't called until the VPN is configured + menuBarExtra.setOpacity(offOpacity) } func vpnDidUpdate(_ connection: NETunnelProviderSession) { diff --git a/Coder Desktop/Coder Desktop/State.swift b/Coder Desktop/Coder Desktop/State.swift index ae63f4c5..a8404ff6 100644 --- a/Coder Desktop/Coder Desktop/State.swift +++ b/Coder Desktop/Coder Desktop/State.swift @@ -32,7 +32,7 @@ class AppState: ObservableObject { @Published var useLiteralHeaders: Bool = UserDefaults.standard.bool(forKey: Keys.useLiteralHeaders) { didSet { - if let onChange { onChange(tunnelProviderProtocol()) } + reconfigure() guard persistent else { return } UserDefaults.standard.set(useLiteralHeaders, forKey: Keys.useLiteralHeaders) } @@ -40,7 +40,7 @@ class AppState: ObservableObject { @Published var literalHeaders: [LiteralHeader] { didSet { - if let onChange { onChange(tunnelProviderProtocol()) } + reconfigure() guard persistent else { return } try? UserDefaults.standard.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders) } @@ -70,9 +70,13 @@ class AppState: ObservableObject { private let keychain: Keychain private let persistent: Bool - // This closure must be called when any property used to configure the VPN changes let onChange: ((NETunnelProviderProtocol?) -> Void)? + // reconfigure must be called when any property used to configure the VPN changes + public func reconfigure() { + if let onChange { onChange(tunnelProviderProtocol()) } + } + public init(onChange: ((NETunnelProviderProtocol?) -> Void)? = nil, persistent: Bool = true) { @@ -97,13 +101,13 @@ class AppState: ObservableObject { hasSession = true self.baseAccessURL = baseAccessURL self.sessionToken = sessionToken - if let onChange { onChange(tunnelProviderProtocol()) } + reconfigure() } public func clearSession() { hasSession = false sessionToken = nil - if let onChange { onChange(tunnelProviderProtocol()) } + reconfigure() } private func keychainGet(for key: String) -> String? { diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index acebb070..881c1a87 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -38,7 +38,7 @@ struct LoginForm: View { .animation(.easeInOut, value: currentPage) .onAppear { baseAccessURL = state.baseAccessURL?.absoluteString ?? baseAccessURL - sessionToken = "" + sessionToken = state.sessionToken ?? sessionToken } .alert("Error", isPresented: Binding( get: { loginError != nil }, @@ -122,7 +122,7 @@ struct LoginForm: View { ).disabled(true) } Section { - SecureField("Session Token", text: $sessionToken, prompt: Text("●●●●●●●●")) + SecureField("Session Token", text: $sessionToken) .autocorrectionDisabled() .privacySensitive() .focused($focusedField, equals: .sessionToken) diff --git a/Coder Desktop/VPN/Manager.swift b/Coder Desktop/VPN/Manager.swift index f1e5cdfb..f074abb8 100644 --- a/Coder Desktop/VPN/Manager.swift +++ b/Coder Desktop/VPN/Manager.swift @@ -30,6 +30,10 @@ actor Manager { let sessionConfig = URLSessionConfiguration.default // The tunnel might be asked to start before the network interfaces have woken up from sleep sessionConfig.waitsForConnectivity = true + // URLSession's waiting for connectivity sometimes hangs even when + // the network is up so this is deliberately short (15s) to avoid a + // poor UX where it appears stuck. + sessionConfig.timeoutIntervalForResource = 15 try await download(src: dylibPath, dest: dest, urlSession: URLSession(configuration: sessionConfig)) } catch { throw .download(error)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: