Skip to content

Commit 2638e0c

Browse files
committed
disable header settings while vpn is on
1 parent cea139b commit 2638e0c

File tree

13 files changed

+174
-82
lines changed

13 files changed

+174
-82
lines changed

Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; };
2626
AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; };
2727
AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; };
28+
AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8EECF62D3A22320049DD09 /* SettingsAccess */; };
2829
/* End PBXBuildFile section */
2930

3031
/* Begin PBXContainerItemProxy section */
@@ -232,6 +233,7 @@
232233
AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */,
233234
AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */,
234235
AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */,
236+
AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */,
235237
);
236238
runOnlyForDeploymentPostprocessing = 0;
237239
};
@@ -382,6 +384,7 @@
382384
AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */,
383385
AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */,
384386
AA2C690E2D34F6920059AFAF /* LaunchAtLogin */,
387+
AA8EECF62D3A22320049DD09 /* SettingsAccess */,
385388
);
386389
productName = "Coder Desktop";
387390
productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */;
@@ -623,6 +626,7 @@
623626
AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */,
624627
AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */,
625628
AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
629+
AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */,
626630
);
627631
preferredProjectObjectVersion = 77;
628632
productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */;
@@ -1533,6 +1537,14 @@
15331537
kind = branch;
15341538
};
15351539
};
1540+
AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = {
1541+
isa = XCRemoteSwiftPackageReference;
1542+
repositoryURL = "https://github.com/orchetect/SettingsAccess";
1543+
requirement = {
1544+
kind = upToNextMajorVersion;
1545+
minimumVersion = 2.1.0;
1546+
};
1547+
};
15361548
/* End XCRemoteSwiftPackageReference section */
15371549

15381550
/* Begin XCSwiftPackageProductDependency section */
@@ -1621,6 +1633,11 @@
16211633
package = AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */;
16221634
productName = KeychainAccess;
16231635
};
1636+
AA8EECF62D3A22320049DD09 /* SettingsAccess */ = {
1637+
isa = XCSwiftPackageProductDependency;
1638+
package = AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */;
1639+
productName = SettingsAccess;
1640+
};
16241641
/* End XCSwiftPackageProductDependency section */
16251642
};
16261643
rootObject = 961678F42CFF100D00B2B6DF /* Project object */;

Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"originHash" : "c41f63aa01c78f450e2232efbefcd30874995ad120db77fa5942062d6f813891",
2+
"originHash" : "b52ef58779afac669f0b78fbf402855ebb45d016ab69ee39b5470c9442c12823",
33
"pins" : [
44
{
55
"identity" : "fluid-menu-bar-extra",
@@ -36,6 +36,15 @@
3636
"version" : "3.0.2"
3737
}
3838
},
39+
{
40+
"identity" : "settingsaccess",
41+
"kind" : "remoteSourceControl",
42+
"location" : "https://github.com/orchetect/SettingsAccess",
43+
"state" : {
44+
"revision" : "08e80c35501f273afa2f5d6f737429bbe395ff81",
45+
"version" : "2.1.0"
46+
}
47+
},
3948
{
4049
"identity" : "swift-protobuf",
4150
"kind" : "remoteSourceControl",

Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

+10-13
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ struct DesktopApp: App {
1212
EmptyView()
1313
}
1414
Window("Sign In", id: Windows.login.rawValue) {
15-
LoginForm<PreviewSession>().environmentObject(appDelegate.session)
15+
LoginForm<SecureSession>().environmentObject(appDelegate.session)
16+
}
17+
.windowResizability(.contentSize)
18+
SwiftUI.Settings { SettingsView<PreviewVPN>()
19+
.environmentObject(appDelegate.vpn)
20+
.environmentObject(appDelegate.settings)
1621
}
17-
.windowResizability(.contentSize)
18-
Settings { SettingsView() }.modelContainer(appDelegate.modelContainer)
1922
}
2023
}
2124

@@ -24,27 +27,21 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2427
private var menuBarExtra: FluidMenuBarExtra?
2528
let vpn: PreviewVPN
2629
let session: PreviewSession
27-
let modelContainer: ModelContainer
30+
let settings: Settings
2831

2932
override init() {
30-
// TODO: Replace with real implementations
33+
// TODO: Replace with real implementation
3134
vpn = PreviewVPN()
35+
settings = Settings()
3236
session = PreviewSession()
33-
modelContainer = try! ModelContainer( // swiftlint:disable:this force_try
34-
for: LiteralHeader.self,
35-
configurations: ModelConfiguration(
36-
url: .applicationSupportDirectory
37-
.appending(component: Bundle.main.bundleIdentifier!)
38-
.appending(component: "Store.sqlite")
39-
)
40-
)
4137
}
4238

4339
func applicationDidFinishLaunching(_: Notification) {
4440
menuBarExtra = FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") {
4541
VPNMenu<PreviewVPN, PreviewSession>().frame(width: 256)
4642
.environmentObject(self.vpn)
4743
.environmentObject(self.session)
44+
.environmentObject(self.settings)
4845
}
4946
}
5047

Coder Desktop/Coder Desktop/Models/LiteralHeader.swift

-19
This file was deleted.

Coder Desktop/Coder Desktop/Session.swift Coder Desktop/Coder Desktop/State.swift

+46
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import CoderSDK
12
import Foundation
23
import KeychainAccess
34
import NetworkExtension
5+
import SwiftUI
46

57
protocol Session: ObservableObject {
68
var hasSession: Bool { get }
@@ -89,3 +91,47 @@ class SecureSession: ObservableObject, Session {
8991
static let sessionToken = "sessionToken"
9092
}
9193
}
94+
95+
class Settings: ObservableObject {
96+
let store: UserDefaults
97+
@AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false
98+
99+
@Published var literalHeaders: [LiteralHeader] {
100+
didSet {
101+
try? store.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders)
102+
}
103+
}
104+
105+
init(store: UserDefaults = UserDefaults.standard) {
106+
self.store = store
107+
_literalHeaders = Published(
108+
initialValue: UserDefaults.standard.data(
109+
forKey: Keys.literalHeaders
110+
).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? []
111+
)
112+
}
113+
114+
enum Keys {
115+
static let useLiteralHeaders = "UseLiteralHeaders"
116+
static let literalHeaders = "LiteralHeaders"
117+
}
118+
}
119+
120+
struct LiteralHeader: Hashable, Identifiable, Equatable, Codable {
121+
var header: String
122+
var value: String
123+
var id: String {
124+
"\(header):\(value)"
125+
}
126+
127+
init(header: String, value: String) {
128+
self.header = header
129+
self.value = value
130+
}
131+
}
132+
133+
extension LiteralHeader {
134+
func toSDKHeader() -> HTTPHeader {
135+
return .init(header: header, value: value)
136+
}
137+
}

Coder Desktop/Coder Desktop/Views/LoginForm.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SwiftUI
44

55
struct LoginForm<S: Session>: View {
66
@EnvironmentObject var session: S
7+
@EnvironmentObject var settings: Settings
78
@Environment(\.dismiss) private var dismiss
89

910
@State private var baseAccessURL: String = ""
@@ -12,7 +13,6 @@ struct LoginForm<S: Session>: View {
1213
@State private var currentPage: LoginPage = .serverURL
1314
@State private var loading: Bool = false
1415
@FocusState private var focusedField: LoginField?
15-
@Query private var headers: [LiteralHeader]
1616

1717
let inspection = Inspection<Self>()
1818

@@ -70,7 +70,7 @@ struct LoginForm<S: Session>: View {
7070
}
7171
loading = true
7272
defer { loading = false }
73-
let client = Client(url: url, token: sessionToken, headers: headers.map { $0.toSDKHeader() })
73+
let client = Client(url: url, token: sessionToken, headers: settings.literalHeaders.map { $0.toSDKHeader() })
7474
do {
7575
_ = try await client.user("me")
7676
} catch {

Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift

+10-11
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import SwiftUI
33

44
struct LiteralHeaderModal: View {
55
var existingHeader: LiteralHeader?
6+
7+
@EnvironmentObject var settings: Settings
8+
@Environment(\.dismiss) private var dismiss
9+
610
@State private var header: String = ""
711
@State private var value: String = ""
8-
@Environment(\.dismiss) private var dismiss
9-
@Environment(\.modelContext) private var modelContext
1012

1113
var body: some View {
1214
VStack(spacing: 0) {
@@ -32,16 +34,13 @@ struct LiteralHeaderModal: View {
3234
}
3335

3436
func submit() {
37+
defer { dismiss() }
3538
if let existingHeader {
36-
existingHeader.header = header
37-
existingHeader.value = value
38-
} else {
39-
modelContext.insert(LiteralHeader(header: header, value: value))
39+
settings.literalHeaders.removeAll { $0 == existingHeader }
40+
}
41+
let newHeader = LiteralHeader(header: header, value: value)
42+
if !settings.literalHeaders.contains(newHeader) {
43+
settings.literalHeaders.append(newHeader)
4044
}
41-
dismiss()
4245
}
4346
}
44-
45-
#Preview {
46-
LiteralHeaderModal()
47-
}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
import SwiftData
22
import SwiftUI
33

4-
struct LiteralHeadersSection: View {
5-
@AppStorage("UseLiteralHeaders") private var useLiteralHeaders = false
6-
@Environment(\.modelContext) private var modelContext
4+
struct LiteralHeadersSection<VPN: VPNService>: View {
5+
@EnvironmentObject var vpn: VPN
6+
@EnvironmentObject var settings: Settings
77

8-
@State private var selectedHeader: PersistentIdentifier?
8+
@State private var selectedHeader: LiteralHeader.ID?
99
@State private var editingHeader: LiteralHeader?
1010
@State private var addingNewHeader = false
11-
@Query private var headers: [LiteralHeader]
11+
12+
let inspection = Inspection<Self>()
1213

1314
var body: some View {
1415
Section {
15-
Toggle(isOn: $useLiteralHeaders) {
16+
Toggle(isOn: settings.$useLiteralHeaders) {
1617
Text("HTTP Headers")
1718
Text("When enabled, these headers will be included on all outgoing HTTP requests.")
19+
if vpn.state != .disabled { Text("Cannot be modified while Coder VPN is enabled.") }
1820
}
1921
.controlSize(.large)
2022

21-
Table(headers, selection: $selectedHeader) {
23+
Table(settings.literalHeaders, selection: $selectedHeader) {
2224
TableColumn("Header", value: \.header)
2325
TableColumn("Value", value: \.value)
24-
}.opacity(useLiteralHeaders ? 1 : 0.5)
26+
}.opacity(settings.useLiteralHeaders ? 1 : 0.5)
2527
.frame(minWidth: 400, minHeight: 200)
2628
.padding(.bottom, 25)
2729
.overlay(alignment: .bottom) {
@@ -36,7 +38,8 @@ struct LiteralHeadersSection: View {
3638
}
3739
Divider()
3840
Button {
39-
removeSelection()
41+
settings.literalHeaders.removeAll { $0.id == selectedHeader }
42+
selectedHeader = nil
4043
} label: {
4144
Image(systemName: "minus")
4245
.frame(width: 24, height: 24)
@@ -48,13 +51,13 @@ struct LiteralHeadersSection: View {
4851
.fixedSize(horizontal: false, vertical: true)
4952
}
5053
.background(.primary.opacity(0.04))
51-
.contextMenu(forSelectionType: PersistentIdentifier.self, menu: { _ in },
54+
.contextMenu(forSelectionType: LiteralHeader.ID.self, menu: { _ in },
5255
primaryAction: { selectedHeaders in
5356
if let firstHeader = selectedHeaders.first {
54-
editingHeader = headers.first(where: { $0.id == firstHeader })
57+
editingHeader = settings.literalHeaders.first(where: { $0.id == firstHeader })
5558
}
5659
})
57-
.disabled(!useLiteralHeaders)
60+
.disabled(!settings.useLiteralHeaders)
5861
}
5962
.sheet(isPresented: $addingNewHeader) {
6063
LiteralHeaderModal()
@@ -63,13 +66,7 @@ struct LiteralHeadersSection: View {
6366
LiteralHeaderModal(existingHeader: header)
6467
}.onTapGesture {
6568
selectedHeader = nil
66-
}
67-
}
68-
69-
func removeSelection() {
70-
if let selectedHeader, let header = headers.first(where: { $0.id == selectedHeader }) {
71-
modelContext.delete(header)
72-
self.selectedHeader = nil
73-
}
69+
}.disabled(vpn.state != .disabled)
70+
.onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector
7471
}
7572
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import SwiftData
22
import SwiftUI
33

4-
struct NetworkTab: View {
4+
struct NetworkTab<VPN: VPNService>: View {
55
var body: some View {
66
Form {
7-
LiteralHeadersSection()
7+
LiteralHeadersSection<VPN>()
88
}
99
.formStyle(.grouped)
1010
}
1111
}
1212

1313
#Preview {
14-
NetworkTab()
14+
NetworkTab<PreviewVPN>()
1515
}

Coder Desktop/Coder Desktop/Views/Settings/Settings.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import SwiftUI
22

3-
struct SettingsView: View {
3+
struct SettingsView<VPN: VPNService>: View {
44
@AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general
55

66
var body: some View {
@@ -9,7 +9,7 @@ struct SettingsView: View {
99
.tabItem {
1010
Label("General", systemImage: "gearshape")
1111
}.tag(SettingsTab.general)
12-
NetworkTab()
12+
NetworkTab<VPN>()
1313
.tabItem {
1414
Label("Network", systemImage: "dot.radiowaves.left.and.right")
1515
}.tag(SettingsTab.network)

0 commit comments

Comments
 (0)