Skip to content

Commit 09bcf7c

Browse files
committed
show less/more + display error
1 parent 840df63 commit 09bcf7c

File tree

8 files changed

+94
-52
lines changed

8 files changed

+94
-52
lines changed

.swiftlint.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
disabled_rules:
2-
- todo
2+
- todo
3+
- trailing_comma

Desktop/AgentRow.swift

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

3-
struct AgentRow: Identifiable {
3+
struct AgentRow: Identifiable, Equatable {
44
let id: UUID
55
let name: String
66
let status: Color
@@ -55,11 +55,11 @@ struct AgentRowView: View {
5555
.padding(3)
5656
}.foregroundStyle(copyIsSelected ? Color.white : .primary)
5757
.imageScale(.small)
58-
.background(copyIsSelected ? Color.accentColor.opacity(0.8) : .clear)
59-
.clipShape(.rect(cornerRadius: 4))
60-
.onHover { hovering in copyIsSelected = hovering }
61-
.buttonStyle(.plain)
62-
.padding(.trailing, 5)
58+
.background(copyIsSelected ? Color.accentColor.opacity(0.8) : .clear)
59+
.clipShape(.rect(cornerRadius: 4))
60+
.onHover { hovering in copyIsSelected = hovering }
61+
.buttonStyle(.plain)
62+
.padding(.trailing, 5)
6363
}
6464
}
6565
}

Desktop/CoderVPN.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,20 @@ protocol CoderVPN: ObservableObject {
88
}
99

1010
enum CoderVPNState: Equatable {
11-
case disabled
12-
case connecting
13-
case disconnecting
14-
case connected
15-
case failed(String)
11+
case disabled
12+
case connecting
13+
case disconnecting
14+
case connected
15+
case failed(CoderVPNError)
16+
}
17+
18+
enum CoderVPNError: Error {
19+
case exampleError
20+
21+
var description: String {
22+
switch self {
23+
case .exampleError:
24+
return "This is a long error to test the UI with long errors"
25+
}
26+
}
1627
}

Desktop/Preview Content/PreviewVPN.swift

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,48 @@ class PreviewVPN: Desktop.CoderVPN {
77
AgentRow(id: UUID(), name: "testing-a-very-long-name", status: .green, copyableDNS: "asdf.coder"),
88
AgentRow(id: UUID(), name: "opensrc", status: .yellow, copyableDNS: "asdf.coder"),
99
AgentRow(id: UUID(), name: "gvisor", status: .gray, copyableDNS: "asdf.coder"),
10-
AgentRow(id: UUID(), name: "example", status: .gray, copyableDNS: "asdf.coder")
10+
AgentRow(id: UUID(), name: "example", status: .gray, copyableDNS: "asdf.coder"),
11+
AgentRow(id: UUID(), name: "dogfood2", status: .red, copyableDNS: "asdf.coder"),
12+
AgentRow(id: UUID(), name: "testing-a-very-long-name", status: .green, copyableDNS: "asdf.coder"),
13+
AgentRow(id: UUID(), name: "opensrc", status: .yellow, copyableDNS: "asdf.coder"),
14+
AgentRow(id: UUID(), name: "gvisor", status: .gray, copyableDNS: "asdf.coder"),
15+
AgentRow(id: UUID(), name: "example", status: .gray, copyableDNS: "asdf.coder"),
1116
]
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-
}
17+
let shouldFail: Bool
2818

29-
func stop() async {
19+
init(shouldFail: Bool = false) {
20+
self.shouldFail = shouldFail
21+
}
22+
23+
private func setState(_ newState: Desktop.CoderVPNState) async {
3024
await MainActor.run {
31-
state = .disconnecting
25+
self.state = newState
3226
}
27+
}
28+
29+
func start() async {
30+
await setState(.connecting)
3331
do {
34-
try await Task.sleep(nanoseconds: 1_000_000_000) // Simulate network delay
32+
try await Task.sleep(nanoseconds: 1000000000)
3533
} catch {
36-
await MainActor.run {
37-
state = .failed("Timed out stopping CoderVPN")
38-
}
34+
await setState(.failed(.exampleError))
3935
return
4036
}
41-
await MainActor.run {
42-
state = .disabled
37+
if shouldFail {
38+
await setState(.failed(.exampleError))
39+
} else {
40+
await setState(.connected)
41+
}
42+
}
43+
44+
func stop() async {
45+
await setState(.disconnecting)
46+
do {
47+
try await Task.sleep(nanoseconds: 1000000000) // Simulate network delay
48+
} catch {
49+
await setState(.failed(.exampleError))
50+
return
4351
}
52+
await setState(.disabled)
4453
}
4554
}

Desktop/VPNMenu.swift

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@ import SwiftUI
22

33
struct VPNMenu<VPN: CoderVPN>: View {
44
@ObservedObject var vpnService: VPN
5+
@State var viewAll = false
6+
7+
private let defaultVisibleRows = 5
58

69
var body: some View {
710
// Main stack
8-
VStack(alignment: .leading) {
11+
VStackLayout(alignment: .leading) {
912
// CoderVPN Stack
1013
VStack(alignment: .leading, spacing: 10) {
1114
HStack {
1215
Toggle(isOn: Binding(
1316
get: { self.vpnService.state == .connected || self.vpnService.state == .connecting },
1417
set: { isOn in Task {
15-
if isOn { await self.vpnService.start() } else { await self.vpnService.stop() }
16-
}
18+
if isOn { await self.vpnService.start() } else { await self.vpnService.stop() }
19+
}
1720
}
1821
)) {
1922
Text("CoderVPN")
2023
.frame(maxWidth: .infinity, alignment: .leading)
2124
}.toggleStyle(.switch)
22-
.disabled(self.vpnService.state == .connecting || self.vpnService.state == .disconnecting)
25+
.disabled(self.vpnService.state == .connecting || self.vpnService.state == .disconnecting)
2326
}
2427
Divider()
2528
Text("Workspace Agents")
@@ -35,12 +38,34 @@ struct VPNMenu<VPN: CoderVPN>: View {
3538
).padding()
3639
Spacer()
3740
}
41+
} else if case let .failed(vpnErr) = self.vpnService.state {
42+
Text("\(vpnErr.description)")
43+
.font(.headline)
44+
.foregroundColor(.red)
45+
.multilineTextAlignment(.center)
46+
.fixedSize(horizontal: false, vertical: true)
47+
.padding(.horizontal, 15)
48+
.padding(.top, 5)
49+
.frame(maxWidth: .infinity)
3850
}
3951
}.padding([.horizontal, .top], 15)
52+
// Workspaces List
4053
if self.vpnService.state == .connected {
41-
ForEach(self.vpnService.data) { workspace in
54+
let visibleData = viewAll ? vpnService.data : Array(vpnService.data.prefix(defaultVisibleRows))
55+
ForEach(visibleData) { workspace in
4256
AgentRowView(workspace: workspace).padding(.horizontal, 5)
4357
}
58+
if vpnService.data.count > defaultVisibleRows {
59+
Button(action: {
60+
viewAll.toggle()
61+
}, label: {
62+
Text(viewAll ? "Show Less" : "Show All")
63+
.font(.headline)
64+
.foregroundColor(.gray)
65+
.padding(.horizontal, 15)
66+
.padding(.top, 5)
67+
}).buttonStyle(.plain)
68+
}
4469
}
4570
// Trailing stack
4671
VStack(alignment: .leading, spacing: 3) {
@@ -49,29 +74,29 @@ struct VPNMenu<VPN: CoderVPN>: View {
4974
Text("Create workspace")
5075
EmptyView()
5176
} action: {
52-
// TODO
77+
// TODO:
5378
}
5479
Divider().padding([.horizontal], 10).padding(.vertical, 4)
5580
ButtonRowView {
5681
Text("About")
5782
} action: {
58-
// TODO
83+
// TODO:
5984
}
6085
ButtonRowView {
6186
Text("Preferences")
6287
} action: {
63-
// TODO
88+
// TODO:
6489
}
6590
ButtonRowView {
6691
Text("Sign out")
6792
} action: {
68-
// TODO
93+
// TODO:
6994
}
7095
}.padding([.horizontal, .bottom], 5)
7196
}.padding(.bottom, 5)
7297
}
7398
}
7499

75100
#Preview {
76-
VPNMenu(vpnService: PreviewVPN()).frame(width: 256)
101+
VPNMenu(vpnService: PreviewVPN(shouldFail: true)).frame(width: 256)
77102
}

DesktopTests/DesktopTests.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import Testing
21
@testable import Desktop
2+
import Testing
33

44
struct DesktopTests {
5-
65
@Test func example() async throws {
76
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
87
}
9-
108
}

DesktopUITests/DesktopUITests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import XCTest
22

33
final class DesktopUITests: XCTestCase {
4-
54
override func setUpWithError() throws {
65
// Put setup code here. This method is called before the invocation of each test method in the class.
76

DesktopUITests/DesktopUITestsLaunchTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import XCTest
22

33
final class DesktopUITestsLaunchTests: XCTestCase {
4-
54
override class var runsForEachTargetApplicationUIConfiguration: Bool {
65
true
76
}

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