Skip to content

Commit 8b7ec78

Browse files
committed
feat: add coder connect startup progress messages
1 parent 4814176 commit 8b7ec78

File tree

8 files changed

+47
-3
lines changed

8 files changed

+47
-3
lines changed

Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ final class PreviewVPN: Coder_Desktop.VPNService {
3333
self.shouldFail = shouldFail
3434
}
3535

36+
@Published var progressMessage: String?
37+
3638
var startTask: Task<Void, Never>?
3739
func start() async {
3840
if await startTask?.value != nil {

Coder-Desktop/Coder-Desktop/VPN/VPNService.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import VPNLib
77
protocol VPNService: ObservableObject {
88
var state: VPNServiceState { get }
99
var menuState: VPNMenuState { get }
10+
var progressMessage: String? { get }
1011
func start() async
1112
func stop() async
1213
func configureTunnelProviderProtocol(proto: NETunnelProviderProtocol?)
@@ -72,6 +73,8 @@ final class CoderVPNService: NSObject, VPNService {
7273
return tunnelState
7374
}
7475

76+
@Published var progressMessage: String?
77+
7578
@Published var menuState: VPNMenuState = .init()
7679

7780
// Whether the VPN should start as soon as possible
@@ -155,6 +158,10 @@ final class CoderVPNService: NSObject, VPNService {
155158
}
156159
}
157160

161+
func onProgress(_ msg: String?) {
162+
progressMessage = msg
163+
}
164+
158165
func applyPeerUpdate(with update: Vpn_PeerUpdate) {
159166
// Delete agents
160167
update.deletedAgents.forEach { menuState.deleteAgent(withId: $0.id) }

Coder-Desktop/Coder-Desktop/Views/VPN/VPNState.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ struct VPNState<VPN: VPNService>: View {
66

77
let inspection = Inspection<Self>()
88

9+
var progressMessage: String {
10+
if let msg = vpn.progressMessage {
11+
msg
12+
} else {
13+
vpn.state == .connecting ? "Starting Coder Connect..." : "Stopping Coder Connect..."
14+
}
15+
}
16+
917
var body: some View {
1018
Group {
1119
switch (vpn.state, state.hasSession) {
@@ -24,9 +32,11 @@ struct VPNState<VPN: VPNService>: View {
2432
case (.connecting, _), (.disconnecting, _):
2533
HStack {
2634
Spacer()
27-
ProgressView(
28-
vpn.state == .connecting ? "Starting Coder Connect..." : "Stopping Coder Connect..."
29-
).padding()
35+
ProgressView {
36+
Text(progressMessage)
37+
.multilineTextAlignment(.center)
38+
}
39+
.padding()
3040
Spacer()
3141
}
3242
case let (.failed(vpnErr), _):

Coder-Desktop/Coder-Desktop/XPCInterface.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ import VPNLib
7171
}
7272
}
7373

74+
func onProgress(msg: String?) {
75+
Task { @MainActor in
76+
svc.onProgress(msg)
77+
}
78+
}
79+
7480
// The NE has verified the dylib and knows better than Gatekeeper
7581
func removeQuarantine(path: String, reply: @escaping (Bool) -> Void) {
7682
let reply = CallbackWrapper(reply)

Coder-Desktop/Coder-DesktopTests/Util.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class MockVPNService: VPNService, ObservableObject {
1010
@Published var state: Coder_Desktop.VPNServiceState = .disabled
1111
@Published var baseAccessURL: URL = .init(string: "https://dev.coder.com")!
1212
@Published var menuState: VPNMenuState = .init()
13+
@Published var progressMessage: String?
1314
var onStart: (() async -> Void)?
1415
var onStop: (() async -> Void)?
1516

Coder-Desktop/VPN/Manager.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ actor Manager {
3939
} catch {
4040
throw .download(error)
4141
}
42+
pushProgress(msg: "Fetching server version...")
4243
let client = Client(url: cfg.serverUrl)
4344
let buildInfo: BuildInfoResponse
4445
do {
@@ -49,6 +50,7 @@ actor Manager {
4950
guard let semver = buildInfo.semver else {
5051
throw .serverInfo("invalid version: \(buildInfo.version)")
5152
}
53+
pushProgress(msg: "Validating library...")
5254
do {
5355
try SignatureValidator.validate(path: dest, expectedVersion: semver)
5456
} catch {
@@ -59,11 +61,13 @@ actor Manager {
5961
// so it's safe to execute. However, the SE must be sandboxed, so we defer to the app.
6062
try await removeQuarantine(dest)
6163

64+
pushProgress(msg: "Opening library...")
6265
do {
6366
try tunnelHandle = TunnelHandle(dylibPath: dest)
6467
} catch {
6568
throw .tunnelSetup(error)
6669
}
70+
pushProgress(msg: "Setting up tunnel...")
6771
speaker = await Speaker<Vpn_ManagerMessage, Vpn_TunnelMessage>(
6872
writeFD: tunnelHandle.writeHandle,
6973
readFD: tunnelHandle.readHandle
@@ -158,6 +162,7 @@ actor Manager {
158162
}
159163

160164
func startVPN() async throws(ManagerError) {
165+
pushProgress(msg: nil)
161166
logger.info("sending start rpc")
162167
guard let tunFd = ptp.tunnelFileDescriptor else {
163168
logger.error("no fd")
@@ -234,6 +239,15 @@ actor Manager {
234239
}
235240
}
236241

242+
func pushProgress(msg: String?) {
243+
guard let conn = globalXPCListenerDelegate.conn else {
244+
logger.error("couldn't send progress message to app: no connection")
245+
return
246+
}
247+
logger.info("sending progress message to app: \(msg ?? "nil")")
248+
conn.onProgress(msg: msg)
249+
}
250+
237251
struct ManagerConfig {
238252
let apiToken: String
239253
let serverUrl: URL
@@ -312,6 +326,7 @@ private func removeQuarantine(_ dest: URL) async throws(ManagerError) {
312326
let file = NSURL(fileURLWithPath: dest.path)
313327
try? file.getResourceValue(&flag, forKey: kCFURLQuarantinePropertiesKey as URLResourceKey)
314328
if flag != nil {
329+
pushProgress(msg: "Unquarantining download...")
315330
// Try the privileged helper first (it may not even be registered)
316331
if await globalHelperXPCSpeaker.tryRemoveQuarantine(path: dest.path) {
317332
// Success!

Coder-Desktop/VPN/PacketTunnelProvider.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
9292
logger.info("vpn started")
9393
self.manager = manager
9494
completionHandler(nil)
95+
// Clear progress message
96+
pushProgress(msg: nil)
9597
} catch {
9698
logger.error("error starting manager: \(error.description, privacy: .public)")
9799
completionHandler(

Coder-Desktop/VPNLib/XPC.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ import Foundation
1010
@objc public protocol VPNXPCClientCallbackProtocol {
1111
// data is a serialized `Vpn_PeerUpdate`
1212
func onPeerUpdate(_ data: Data)
13+
func onProgress(msg: String?)
1314
func removeQuarantine(path: String, reply: @escaping (Bool) -> Void)
1415
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy