Skip to content

Commit bd5638d

Browse files
committed
feat: add progress messages when creating sync sessions
1 parent e96fc49 commit bd5638d

File tree

7 files changed

+34
-24
lines changed

7 files changed

+34
-24
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import VPNLib
33
@MainActor
44
final class PreviewFileSync: FileSyncDaemon {
55
var logFile: URL = .init(filePath: "~/log.txt")!
6+
var lastPromptMessage: String?
67

78
var sessionState: [VPNLib.FileSyncSession] = []
89

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

+10-10
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ final class PreviewVPN: Coder_Desktop.VPNService {
66
@Published var state: Coder_Desktop.VPNServiceState = .connected
77
@Published var menuState: VPNMenuState = .init(agents: [
88
UUID(): Agent(id: UUID(), name: "dev", status: .error, hosts: ["asdf.coder"], wsName: "dogfood2",
9-
wsID: UUID()),
9+
wsID: UUID(), primaryHost: "asdf.coder"),
1010
UUID(): Agent(id: UUID(), name: "dev", status: .okay, hosts: ["asdf.coder"],
11-
wsName: "testing-a-very-long-name", wsID: UUID()),
11+
wsName: "testing-a-very-long-name", wsID: UUID(), primaryHost: "asdf.coder"),
1212
UUID(): Agent(id: UUID(), name: "dev", status: .warn, hosts: ["asdf.coder"], wsName: "opensrc",
13-
wsID: UUID()),
13+
wsID: UUID(), primaryHost: "asdf.coder"),
1414
UUID(): Agent(id: UUID(), name: "dev", status: .off, hosts: ["asdf.coder"], wsName: "gvisor",
15-
wsID: UUID()),
15+
wsID: UUID(), primaryHost: "asdf.coder"),
1616
UUID(): Agent(id: UUID(), name: "dev", status: .off, hosts: ["asdf.coder"], wsName: "example",
17-
wsID: UUID()),
17+
wsID: UUID(), primaryHost: "asdf.coder"),
1818
UUID(): Agent(id: UUID(), name: "dev", status: .error, hosts: ["asdf.coder"], wsName: "dogfood2",
19-
wsID: UUID()),
19+
wsID: UUID(), primaryHost: "asdf.coder"),
2020
UUID(): Agent(id: UUID(), name: "dev", status: .okay, hosts: ["asdf.coder"],
21-
wsName: "testing-a-very-long-name", wsID: UUID()),
21+
wsName: "testing-a-very-long-name", wsID: UUID(), primaryHost: "asdf.coder"),
2222
UUID(): Agent(id: UUID(), name: "dev", status: .warn, hosts: ["asdf.coder"], wsName: "opensrc",
23-
wsID: UUID()),
23+
wsID: UUID(), primaryHost: "asdf.coder"),
2424
UUID(): Agent(id: UUID(), name: "dev", status: .off, hosts: ["asdf.coder"], wsName: "gvisor",
25-
wsID: UUID()),
25+
wsID: UUID(), primaryHost: "asdf.coder"),
2626
UUID(): Agent(id: UUID(), name: "dev", status: .off, hosts: ["asdf.coder"], wsName: "example",
27-
wsID: UUID()),
27+
wsID: UUID(), primaryHost: "asdf.coder"),
2828
], workspaces: [:])
2929
let shouldFail: Bool
3030
let longError = "This is a long error to test the UI with long error messages"

Coder-Desktop/Coder-Desktop/VPN/MenuState.swift

+9-8
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ struct Agent: Identifiable, Equatable, Comparable, Hashable {
1818
return lhs.wsName.localizedCompare(rhs.wsName) == .orderedAscending
1919
}
2020

21-
// Hosts arrive sorted by length, the shortest looks best in the UI.
22-
var primaryHost: String? { hosts.first }
21+
let primaryHost: String
2322
}
2423

2524
enum AgentStatus: Int, Equatable, Comparable {
@@ -69,6 +68,9 @@ struct VPNMenuState {
6968
invalidAgents.append(agent)
7069
return
7170
}
71+
// Remove trailing dot if present
72+
let nonEmptyHosts = agent.fqdn.map { $0.hasSuffix(".") ? String($0.dropLast()) : $0 }
73+
7274
// An existing agent with the same name, belonging to the same workspace
7375
// is from a previous workspace build, and should be removed.
7476
agents.filter { $0.value.name == agent.name && $0.value.wsID == wsID }
@@ -81,10 +83,11 @@ struct VPNMenuState {
8183
name: agent.name,
8284
// If last handshake was not within last five minutes, the agent is unhealthy
8385
status: agent.lastHandshake.date > Date.now.addingTimeInterval(-300) ? .okay : .warn,
84-
// Remove trailing dot if present
85-
hosts: agent.fqdn.map { $0.hasSuffix(".") ? String($0.dropLast()) : $0 },
86+
hosts: nonEmptyHosts,
8687
wsName: workspace.name,
87-
wsID: wsID
88+
wsID: wsID,
89+
// Hosts arrive sorted by length, the shortest looks best in the UI.
90+
primaryHost: nonEmptyHosts.first!,
8891
)
8992
}
9093

@@ -135,9 +138,7 @@ struct VPNMenuState {
135138
return items.sorted()
136139
}
137140

138-
var onlineAgents: [Agent] {
139-
agents.map(\.value).filter { $0.primaryHost != nil }
140-
}
141+
var onlineAgents: [Agent] { agents.map(\.value) }
141142

142143
mutating func clear() {
143144
agents.removeAll()

Coder-Desktop/Coder-Desktop/Views/FileSync/FileSyncSessionModal.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct FileSyncSessionModal<VPN: VPNService, FS: FileSyncDaemon>: View {
4040
Section {
4141
Picker("Workspace", selection: $remoteHostname) {
4242
ForEach(agents, id: \.id) { agent in
43-
Text(agent.primaryHost!).tag(agent.primaryHost!)
43+
Text(agent.primaryHost).tag(agent.primaryHost)
4444
}
4545
// HACK: Silence error logs for no-selection.
4646
Divider().tag(nil as String?)
@@ -62,6 +62,9 @@ struct FileSyncSessionModal<VPN: VPNService, FS: FileSyncDaemon>: View {
6262
Divider()
6363
HStack {
6464
Spacer()
65+
if let lastMessage = fileSync.lastPromptMessage {
66+
Text(lastMessage).foregroundStyle(.secondary)
67+
}
6568
Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction)
6669
Button(existingSession == nil ? "Add" : "Save") { Task { await submit() }}
6770
.keyboardShortcut(.defaultAction)

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ struct MenuItemView: View {
6666

6767
private var itemName: AttributedString {
6868
let name = switch item {
69-
case let .agent(agent): agent.primaryHost ?? "\(item.wsName).\(state.hostnameSuffix)"
69+
case let .agent(agent): agent.primaryHost
7070
case .offlineWorkspace: "\(item.wsName).\(state.hostnameSuffix)"
7171
}
7272

@@ -103,10 +103,10 @@ struct MenuItemView: View {
103103
}
104104
Spacer()
105105
}.buttonStyle(.plain)
106-
if case let .agent(agent) = item, let copyableDNS = agent.primaryHost {
106+
if case let .agent(agent) = item {
107107
Button {
108108
NSPasteboard.general.clearContents()
109-
NSPasteboard.general.setString(copyableDNS, forType: .string)
109+
NSPasteboard.general.setString(agent.primaryHost, forType: .string)
110110
} label: {
111111
Image(systemName: "doc.on.doc")
112112
.symbolVariant(.fill)
@@ -143,7 +143,6 @@ struct MenuItemView: View {
143143
// If this menu item is an agent, and the user is logged in
144144
if case let .agent(agent) = item,
145145
let client = state.client,
146-
let host = agent.primaryHost,
147146
let baseAccessURL = state.baseAccessURL,
148147
// Like the CLI, we'll re-use the existing session token to populate the URL
149148
let sessionToken = state.sessionToken
@@ -166,7 +165,7 @@ struct MenuItemView: View {
166165
.flatMap(\.self)
167166
.first(where: { $0.id == agent.id })
168167
{
169-
apps = agentToApps(logger, wsAgent, host, baseAccessURL, sessionToken)
168+
apps = agentToApps(logger, wsAgent, agent.primaryHost, baseAccessURL, sessionToken)
170169
} else {
171170
logger.error("Could not find agent '\(agent.id)' in workspace '\(item.wsName)' resources")
172171
}

Coder-Desktop/VPNLib/FileSync/FileSyncDaemon.swift

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public protocol FileSyncDaemon: ObservableObject {
1111
var state: DaemonState { get }
1212
var sessionState: [FileSyncSession] { get }
1313
var logFile: URL { get }
14+
var lastPromptMessage: String? { get }
1415
func tryStart() async
1516
func stop() async
1617
func refreshSessions() async
@@ -47,6 +48,8 @@ public class MutagenDaemon: FileSyncDaemon {
4748

4849
public let logFile: URL
4950

51+
@Published public var lastPromptMessage: String?
52+
5053
// Managing sync sessions could take a while, especially with prompting
5154
let sessionMgmtReqTimeout: TimeAmount = .seconds(15)
5255

Coder-Desktop/VPNLib/FileSync/FileSyncPrompting.swift

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extension MutagenDaemon {
2828
try initResp.ensureValid(first: true, allowPrompts: allowPrompts)
2929

3030
Task.detached(priority: .background) {
31+
defer { Task { @MainActor in self.lastPromptMessage = nil } }
3132
do {
3233
while let msg = try await iter.next() {
3334
try msg.ensureValid(first: false, allowPrompts: allowPrompts)
@@ -39,6 +40,8 @@ extension MutagenDaemon {
3940
}
4041
// Any other messages that require a non-empty response will
4142
// cause the create op to fail, showing an error. This is ok for now.
43+
} else {
44+
Task { @MainActor in self.lastPromptMessage = msg.message }
4245
}
4346
try await stream.requestStream.send(reply)
4447
}

0 commit comments

Comments
 (0)