Skip to content

Commit e9ddfbc

Browse files
committed
codervpn abstraction
1 parent e8de69e commit e9ddfbc

File tree

4 files changed

+103
-30
lines changed

4 files changed

+103
-30
lines changed

Desktop/CoderVPN.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import SwiftUI
2+
3+
protocol CoderVPN: ObservableObject {
4+
var state: CoderVPNState { get }
5+
var data: [AgentRow] { get }
6+
func start() async
7+
func stop() async
8+
}
9+
10+
enum CoderVPNState: Equatable {
11+
case disabled
12+
case connecting
13+
case disconnecting
14+
case connected
15+
case failed(String)
16+
}

Desktop/DesktopApp.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import SwiftUI
44
struct DesktopApp: App {
55
var body: some Scene {
66
MenuBarExtra {
7-
VPNMenu(workspaces: [
8-
WorkspaceRowContents(name: "dogfood2", status: .red, copyableDNS: "asdf.coder"),
9-
WorkspaceRowContents(name: "testing-a-very-long-name", status: .green, copyableDNS: "asdf.coder"),
10-
WorkspaceRowContents(name: "opensrc", status: .yellow, copyableDNS: "asdf.coder"),
11-
WorkspaceRowContents(name: "gvisor", status: .gray, copyableDNS: "asdf.coder"),
12-
WorkspaceRowContents(name: "example", status: .gray, copyableDNS: "asdf.coder")
13-
]).frame(width: 256)
7+
VPNMenu(vpnService: PreviewVPN()).frame(width: 256)
148
} label: {
159
let image: NSImage = {
1610
let ratio = $0.size.height / $0.size.width
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import SwiftUI
2+
3+
class PreviewVPN: Desktop.CoderVPN {
4+
@Published var state: Desktop.CoderVPNState = .disabled
5+
@Published var data: [Desktop.AgentRow] = [
6+
AgentRow(id: UUID(), name: "dogfood2", status: .red, copyableDNS: "asdf.coder"),
7+
AgentRow(id: UUID(), name: "testing-a-very-long-name", status: .green, copyableDNS: "asdf.coder"),
8+
AgentRow(id: UUID(), name: "opensrc", status: .yellow, copyableDNS: "asdf.coder"),
9+
AgentRow(id: UUID(), name: "gvisor", status: .gray, copyableDNS: "asdf.coder"),
10+
AgentRow(id: UUID(), name: "example", status: .gray, copyableDNS: "asdf.coder")
11+
]
12+
func start() async {
13+
await MainActor.run {
14+
state = .connecting
15+
}
16+
do {
17+
try await Task.sleep(nanoseconds: 1_000_000_000)
18+
} catch {
19+
await MainActor.run {
20+
state = .failed("Timed out starting CoderVPN")
21+
}
22+
return
23+
}
24+
await MainActor.run {
25+
state = .connected
26+
}
27+
}
28+
29+
func stop() async {
30+
await MainActor.run {
31+
state = .disconnecting
32+
}
33+
do {
34+
try await Task.sleep(nanoseconds: 1_000_000_000) // Simulate network delay
35+
} catch {
36+
await MainActor.run {
37+
state = .failed("Timed out stopping CoderVPN")
38+
}
39+
return
40+
}
41+
await MainActor.run {
42+
state = .disabled
43+
}
44+
}
45+
}

Desktop/VPNMenu.swift

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,45 @@
11
import SwiftUI
22

3-
struct VPNMenu: View {
4-
@State private var isVPNOn: Bool = false
5-
let workspaces: [WorkspaceRowContents]
3+
struct VPNMenu<Conn: CoderVPN>: View {
4+
@ObservedObject var vpnService: Conn
5+
66
var body: some View {
77
// Main stack
88
VStack(alignment: .leading) {
99
// CoderVPN Stack
1010
VStack(alignment: .leading, spacing: 10) {
1111
HStack {
12-
Toggle(isOn: self.$isVPNOn) {
12+
Toggle(isOn: Binding(
13+
get: { self.vpnService.state == .connected || self.vpnService.state == .connecting },
14+
set: { isOn in Task {
15+
if isOn { await self.vpnService.start() } else { await self.vpnService.stop() }
16+
}
17+
}
18+
)) {
1319
Text("CoderVPN")
1420
.frame(maxWidth: .infinity, alignment: .leading)
1521
}.toggleStyle(.switch)
22+
.disabled(self.vpnService.state == .connecting || self.vpnService.state == .disconnecting)
1623
}
1724
Divider()
18-
Text("Workspaces")
25+
Text("Workspace Agents")
1926
.font(.headline)
2027
.foregroundColor(.gray)
21-
if !isVPNOn {
22-
Text("Enable CoderVPN to see workspaces").font(.body).foregroundColor(.gray)
28+
if self.vpnService.state == .disabled {
29+
Text("Enable CoderVPN to see agents").font(.body).foregroundColor(.gray)
30+
} else if self.vpnService.state == .connecting || self.vpnService.state == .disconnecting {
31+
HStack {
32+
Spacer()
33+
ProgressView(
34+
self.vpnService.state == .connecting ? "Starting CoderVPN..." : "Stopping CoderVPN..."
35+
).padding()
36+
Spacer()
37+
}
2338
}
2439
}.padding([.horizontal, .top], 15)
25-
if isVPNOn {
26-
ForEach(workspaces) { workspace in
27-
WorkspaceRowView(workspace: workspace).padding(.horizontal, 5)
40+
if self.vpnService.state == .connected {
41+
ForEach(self.vpnService.data) { workspace in
42+
AgentRowView(workspace: workspace).padding(.horizontal, 5)
2843
}
2944
}
3045
// Trailing stack
@@ -33,32 +48,39 @@ struct VPNMenu: View {
3348
RowButtonView {
3449
Text("Create workspace")
3550
EmptyView()
51+
} action: {
52+
// TODO
3653
}
3754
Divider().padding([.horizontal], 10).padding(.vertical, 4)
3855
RowButtonView {
3956
Text("About")
57+
} action: {
58+
// TODO
4059
}
4160
RowButtonView {
4261
Text("Preferences")
62+
} action: {
63+
// TODO
4364
}
4465
RowButtonView {
4566
Text("Sign out")
67+
} action: {
68+
// TODO
4669
}
4770
}.padding([.horizontal, .bottom], 5)
4871
}.padding(.bottom, 5)
49-
5072
}
5173
}
5274

53-
struct WorkspaceRowContents: Identifiable {
54-
let id = UUID()
75+
struct AgentRow: Identifiable {
76+
let id: UUID
5577
let name: String
5678
let status: Color
5779
let copyableDNS: String
5880
}
5981

60-
struct WorkspaceRowView: View {
61-
let workspace: WorkspaceRowContents
82+
struct AgentRowView: View {
83+
let workspace: AgentRow
6284
@State private var nameIsSelected: Bool = false
6385
@State private var copyIsSelected: Bool = false
6486

@@ -117,9 +139,11 @@ struct WorkspaceRowView: View {
117139
struct RowButtonView<Label: View>: View {
118140
@State private var isSelected: Bool = false
119141
@ViewBuilder var label: () -> Label
142+
var action: () -> Void
143+
120144
var body: some View {
121145
Button {
122-
// TODO: Action
146+
action()
123147
} label: {
124148
HStack(spacing: 0) {
125149
label()
@@ -137,11 +161,5 @@ struct RowButtonView<Label: View>: View {
137161
}
138162

139163
#Preview {
140-
VPNMenu(workspaces: [
141-
WorkspaceRowContents(name: "dogfood2", status: .red, copyableDNS: "asdf.coder"),
142-
WorkspaceRowContents(name: "testing-a-very-long-name", status: .green, copyableDNS: "asdf.coder"),
143-
WorkspaceRowContents(name: "opensrc", status: .yellow, copyableDNS: "asdf.coder"),
144-
WorkspaceRowContents(name: "gvisor", status: .gray, copyableDNS: "asdf.coder"),
145-
WorkspaceRowContents(name: "example", status: .gray, copyableDNS: "asdf.coder")
146-
]).frame(width: 256)
164+
VPNMenu(vpnService: PreviewVPN()).frame(width: 256)
147165
}

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