From b333965cdb41c07d8ab82cfb94590d6e008facd2 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Thu, 16 Jan 2025 18:34:02 +1100 Subject: [PATCH 1/4] feat: support user-supplied literal headers --- .../Coder Desktop.xcodeproj/project.pbxproj | 174 ++++++++++++++---- .../xcshareddata/swiftpm/Package.resolved | 24 ++- Coder Desktop/Coder Desktop/About.swift | 10 +- .../Coder Desktop/Coder_DesktopApp.swift | 23 ++- .../{Session.swift => State.swift} | 46 +++++ .../Coder Desktop/Views/LoginForm.swift | 3 +- .../Views/Settings/GeneralTab.swift | 16 ++ .../Views/Settings/LiteralHeaderModal.swift | 45 +++++ .../Settings/LiteralHeadersSection.swift | 71 +++++++ .../Views/Settings/NetworkTab.swift | 14 ++ .../Views/Settings/Settings.swift | 26 +++ Coder Desktop/Coder Desktop/Views/Util.swift | 1 + .../Coder Desktop/Views/VPNMenu.swift | 8 + Coder Desktop/Coder Desktop/Windows.swift | 10 +- .../LiteralHeadersSettingTests.swift | 48 +++++ .../Coder DesktopTests/LoginFormTests.swift | 9 +- Makefile | 3 +- 17 files changed, 467 insertions(+), 64 deletions(-) rename Coder Desktop/Coder Desktop/{Session.swift => State.swift} (70%) create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift create mode 100644 Coder Desktop/Coder Desktop/Views/Settings/Settings.swift create mode 100644 Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index 6a26723..1f36fec 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 961679322CFF117300B2B6DF /* NetworkExtension.framework */; }; 9616793D2CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 961679302CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = AA2C690E2D34F6920059AFAF /* LaunchAtLogin */; }; AA3B3DA92D2D23860099996A /* VPNLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3B3DA12D2D23860099996A /* VPNLib.framework */; }; AA3B3DBF2D2D23AB0099996A /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */; }; AA3B3DC12D2D23AB0099996A /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DC02D2D23AB0099996A /* SwiftProtobufPluginLibrary */; }; @@ -24,6 +25,7 @@ AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; }; AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; }; AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; }; + AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8EECF62D3A22320049DD09 /* SettingsAccess */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -229,7 +231,9 @@ files = ( AA3B40A42D2FC8560099996A /* CoderSDK.framework in Frameworks */, AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */, + AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */, AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */, + AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -368,7 +372,7 @@ buildRules = ( ); dependencies = ( - AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */, + AA2C698C2D354A800059AFAF /* PBXTargetDependency */, 9616793C2CFF117300B2B6DF /* PBXTargetDependency */, AA3B40A32D2FC8560099996A /* PBXTargetDependency */, ); @@ -379,6 +383,8 @@ packageProductDependencies = ( AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */, AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */, + AA2C690E2D34F6920059AFAF /* LaunchAtLogin */, + AA8EECF62D3A22320049DD09 /* SettingsAccess */, ); productName = "Coder Desktop"; productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */; @@ -395,6 +401,7 @@ buildRules = ( ); dependencies = ( + AA2C698E2D354A840059AFAF /* PBXTargetDependency */, 961679112CFF100E00B2B6DF /* PBXTargetDependency */, AA3B40BA2D2FDA5C0099996A /* PBXTargetDependency */, ); @@ -421,6 +428,7 @@ buildRules = ( ); dependencies = ( + AA2C69902D354A880059AFAF /* PBXTargetDependency */, 9616791B2CFF100E00B2B6DF /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -445,6 +453,7 @@ buildRules = ( ); dependencies = ( + AA2C69922D354A8B0059AFAF /* PBXTargetDependency */, AA3B3DD02D2D249F0099996A /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -469,6 +478,7 @@ buildRules = ( ); dependencies = ( + AA2C69942D354A8E0059AFAF /* PBXTargetDependency */, AA3B40C32D2FE7760099996A /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( @@ -494,6 +504,7 @@ buildRules = ( ); dependencies = ( + AA2C69962D354A910059AFAF /* PBXTargetDependency */, AA3B3DAB2D2D23860099996A /* PBXTargetDependency */, AA3B3DAD2D2D23860099996A /* PBXTargetDependency */, ); @@ -520,6 +531,7 @@ buildRules = ( ); dependencies = ( + AA2C69982D354A940059AFAF /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( AA3B40922D2FC8560099996A /* CoderSDK */, @@ -542,6 +554,7 @@ buildRules = ( ); dependencies = ( + AA2C699A2D354A970059AFAF /* PBXTargetDependency */, AA3B409B2D2FC8560099996A /* PBXTargetDependency */, AA3B409D2D2FC8560099996A /* PBXTargetDependency */, ); @@ -607,11 +620,13 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */, - AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */, AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */, 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */, AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */, + AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */, + AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, + AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */, ); preferredProjectObjectVersion = 77; productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */; @@ -764,6 +779,38 @@ target = 9616792F2CFF117300B2B6DF /* VPN */; targetProxy = 9616793B2CFF117300B2B6DF /* PBXContainerItemProxy */; }; + AA2C698C2D354A800059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C698E2D354A840059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69902D354A880059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69922D354A8B0059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69942D354A8E0059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69962D354A910059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C69982D354A940059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */; + }; + AA2C699A2D354A970059AFAF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */; + }; AA3B3DAB2D2D23860099996A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = AA3B3DA02D2D23860099996A /* VPNLib */; @@ -804,10 +851,6 @@ target = AA3B40902D2FC8560099996A /* CoderSDK */; targetProxy = AA3B40C22D2FE7760099996A /* PBXContainerItemProxy */; }; - AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -955,7 +998,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -987,7 +1030,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1006,7 +1049,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1025,7 +1068,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1043,7 +1086,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1061,7 +1104,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1091,7 +1134,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN"; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; @@ -1123,7 +1166,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPN"; PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)"; @@ -1157,7 +1200,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1194,7 +1237,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1216,7 +1259,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1233,7 +1276,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1265,7 +1308,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1302,7 +1345,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1324,7 +1367,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1341,7 +1384,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.CoderSDKTests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1446,6 +1489,22 @@ version = 1.28.2; }; }; + AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-modern"; + requirement = { + kind = exactVersion; + version = 1.1.0; + }; + }; + AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.58.0; + }; + }; AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/WeTransfer/Mocker"; @@ -1462,14 +1521,6 @@ minimumVersion = 0.10.0; }; }; - AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.57.1; - }; - }; AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/lfroms/fluid-menu-bar-extra"; @@ -1486,9 +1537,62 @@ kind = branch; }; }; + AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/orchetect/SettingsAccess"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.1.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + AA2C690E2D34F6920059AFAF /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */; + productName = LaunchAtLogin; + }; + AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; + AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; + productName = "plugin:SwiftLintBuildToolPlugin"; + }; AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */ = { isa = XCSwiftPackageProductDependency; package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; @@ -1519,11 +1623,6 @@ package = AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */; productName = ViewInspector; }; - AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */ = { - isa = XCSwiftPackageProductDependency; - package = AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */; - productName = "plugin:SwiftLintBuildToolPlugin"; - }; AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */ = { isa = XCSwiftPackageProductDependency; package = AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */; @@ -1534,6 +1633,11 @@ package = AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; + AA8EECF62D3A22320049DD09 /* SettingsAccess */ = { + isa = XCSwiftPackageProductDependency; + package = AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */; + productName = SettingsAccess; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 961678F42CFF100D00B2B6DF /* Project object */; diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5a69cd2..c362f1f 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ec40e522ec1a2416e8e8f5cbe97424ab3e4a614e6ef453c10ea28e84e88b6771", + "originHash" : "b52ef58779afac669f0b78fbf402855ebb45d016ab69ee39b5470c9442c12823", "pins" : [ { "identity" : "fluid-menu-bar-extra", @@ -18,6 +18,15 @@ "revision" : "e0c7eebc5a4465a3c4680764f26b7a61f567cdaf" } }, + { + "identity" : "launchatlogin-modern", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin-modern", + "state" : { + "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc", + "version" : "1.1.0" + } + }, { "identity" : "mocker", "kind" : "remoteSourceControl", @@ -27,6 +36,15 @@ "version" : "3.0.2" } }, + { + "identity" : "settingsaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/orchetect/SettingsAccess", + "state" : { + "revision" : "08e80c35501f273afa2f5d6f737429bbe395ff81", + "version" : "2.1.0" + } + }, { "identity" : "swift-protobuf", "kind" : "remoteSourceControl", @@ -41,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", "state" : { - "revision" : "f9731bef175c3eea3a0ca960f1be78fcc2bc7853", - "version" : "0.57.1" + "revision" : "fac0c3d3ac69b15ea5382275dbbd5e583a2e05fa", + "version" : "0.58.0" } }, { diff --git a/Coder Desktop/Coder Desktop/About.swift b/Coder Desktop/Coder Desktop/About.swift index 4bff9d6..3771175 100644 --- a/Coder Desktop/Coder Desktop/About.swift +++ b/Coder Desktop/Coder Desktop/About.swift @@ -32,15 +32,7 @@ enum About { @MainActor static func open() { - #if compiler(>=5.9) && canImport(AppKit) - if #available(macOS 14, *) { - NSApp.activate() - } else { - NSApp.activate(ignoringOtherApps: true) - } - #else - NSApp.activate(ignoringOtherApps: true) - #endif + appActivate() NSApp.orderFrontStandardAboutPanel(options: [ .credits: credits, ]) diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 940f5cc..0758f38 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -14,6 +14,11 @@ struct DesktopApp: App { LoginForm().environmentObject(appDelegate.session) } .windowResizability(.contentSize) + SwiftUI.Settings { SettingsView() + .environmentObject(appDelegate.vpn) + .environmentObject(appDelegate.settings) + } + .windowResizability(.contentSize) } } @@ -22,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var menuBarExtra: FluidMenuBarExtra? let vpn: PreviewVPN let session: PreviewSession + let settings: Settings override init() { - // TODO: Replace with real implementations + // TODO: Replace with real implementation vpn = PreviewVPN() + settings = Settings() session = PreviewSession() } @@ -34,6 +41,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { VPNMenu().frame(width: 256) .environmentObject(self.vpn) .environmentObject(self.session) + .environmentObject(self.settings) } } @@ -49,3 +57,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { false } } + +@MainActor +func appActivate() { + #if compiler(>=5.9) && canImport(AppKit) + if #available(macOS 14, *) { + NSApp.activate() + } else { + NSApp.activate(ignoringOtherApps: true) + } + #else + NSApp.activate(ignoringOtherApps: true) + #endif +} diff --git a/Coder Desktop/Coder Desktop/Session.swift b/Coder Desktop/Coder Desktop/State.swift similarity index 70% rename from Coder Desktop/Coder Desktop/Session.swift rename to Coder Desktop/Coder Desktop/State.swift index 2e39ada..f3d7094 100644 --- a/Coder Desktop/Coder Desktop/Session.swift +++ b/Coder Desktop/Coder Desktop/State.swift @@ -1,6 +1,8 @@ +import CoderSDK import Foundation import KeychainAccess import NetworkExtension +import SwiftUI protocol Session: ObservableObject { var hasSession: Bool { get } @@ -89,3 +91,47 @@ class SecureSession: ObservableObject, Session { static let sessionToken = "sessionToken" } } + +class Settings: ObservableObject { + let store: UserDefaults + @AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false + + @Published var literalHeaders: [LiteralHeader] { + didSet { + try? store.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders) + } + } + + init(store: UserDefaults = UserDefaults.standard) { + self.store = store + _literalHeaders = Published( + initialValue: UserDefaults.standard.data( + forKey: Keys.literalHeaders + ).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? [] + ) + } + + enum Keys { + static let useLiteralHeaders = "UseLiteralHeaders" + static let literalHeaders = "LiteralHeaders" + } +} + +struct LiteralHeader: Hashable, Identifiable, Equatable, Codable { + var header: String + var value: String + var id: String { + "\(header):\(value)" + } + + init(header: String, value: String) { + self.header = header + self.value = value + } +} + +extension LiteralHeader { + func toSDKHeader() -> HTTPHeader { + return .init(header: header, value: value) + } +} diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index 776cee9..10fa6b0 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -3,6 +3,7 @@ import SwiftUI struct LoginForm: View { @EnvironmentObject var session: S + @EnvironmentObject var settings: Settings @Environment(\.dismiss) private var dismiss @State private var baseAccessURL: String = "" @@ -68,7 +69,7 @@ struct LoginForm: View { } loading = true defer { loading = false } - let client = Client(url: url, token: sessionToken) + let client = Client(url: url, token: sessionToken, headers: settings.literalHeaders.map { $0.toSDKHeader() }) do { _ = try await client.user("me") } catch { diff --git a/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift new file mode 100644 index 0000000..0c1bb9e --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift @@ -0,0 +1,16 @@ +import LaunchAtLogin +import SwiftUI + +struct GeneralTab: View { + var body: some View { + Form { + Section { + LaunchAtLogin.Toggle("Launch at Login") + } + }.formStyle(.grouped) + } +} + +#Preview { + GeneralTab() +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift new file mode 100644 index 0000000..9e2ea2a --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeaderModal.swift @@ -0,0 +1,45 @@ +import SwiftUI + +struct LiteralHeaderModal: View { + var existingHeader: LiteralHeader? + + @EnvironmentObject var settings: Settings + @Environment(\.dismiss) private var dismiss + + @State private var header: String = "" + @State private var value: String = "" + + var body: some View { + VStack(spacing: 0) { + Form { + Section { + TextField("Header", text: $header) + TextField("Value", text: $value) + } + }.formStyle(.grouped).scrollDisabled(true).padding(.horizontal) + Divider() + HStack { + Spacer() + Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction) + Button(existingHeader == nil ? "Add" : "Save", action: submit) + .keyboardShortcut(.defaultAction) + }.padding(20) + }.onAppear { + if let existingHeader { + self.header = existingHeader.header + self.value = existingHeader.value + } + } + } + + func submit() { + defer { dismiss() } + if let existingHeader { + settings.literalHeaders.removeAll { $0 == existingHeader } + } + let newHeader = LiteralHeader(header: header, value: value) + if !settings.literalHeaders.contains(newHeader) { + settings.literalHeaders.append(newHeader) + } + } +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift new file mode 100644 index 0000000..aa27203 --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/LiteralHeadersSection.swift @@ -0,0 +1,71 @@ +import SwiftUI + +struct LiteralHeadersSection: View { + @EnvironmentObject var vpn: VPN + @EnvironmentObject var settings: Settings + + @State private var selectedHeader: LiteralHeader.ID? + @State private var editingHeader: LiteralHeader? + @State private var addingNewHeader = false + + let inspection = Inspection() + + var body: some View { + Section { + Toggle(isOn: settings.$useLiteralHeaders) { + Text("HTTP Headers") + Text("When enabled, these headers will be included on all outgoing HTTP requests.") + if vpn.state != .disabled { Text("Cannot be modified while Coder VPN is enabled.") } + } + .controlSize(.large) + + Table(settings.literalHeaders, selection: $selectedHeader) { + TableColumn("Header", value: \.header) + TableColumn("Value", value: \.value) + }.opacity(settings.useLiteralHeaders ? 1 : 0.5) + .frame(minWidth: 400, minHeight: 200) + .padding(.bottom, 25) + .overlay(alignment: .bottom) { + VStack(alignment: .leading, spacing: 0) { + Divider() + HStack(spacing: 0) { + Button { + addingNewHeader = true + } label: { + Image(systemName: "plus") + .frame(width: 24, height: 24) + } + Divider() + Button { + settings.literalHeaders.removeAll { $0.id == selectedHeader } + selectedHeader = nil + } label: { + Image(systemName: "minus") + .frame(width: 24, height: 24) + }.disabled(selectedHeader == nil) + } + .buttonStyle(.borderless) + } + .background(.primary.opacity(0.04)) + .fixedSize(horizontal: false, vertical: true) + } + .background(.primary.opacity(0.04)) + .contextMenu(forSelectionType: LiteralHeader.ID.self, menu: { _ in }, + primaryAction: { selectedHeaders in + if let firstHeader = selectedHeaders.first { + editingHeader = settings.literalHeaders.first(where: { $0.id == firstHeader }) + } + }) + .disabled(!settings.useLiteralHeaders) + } + .sheet(isPresented: $addingNewHeader) { + LiteralHeaderModal() + } + .sheet(item: $editingHeader) { header in + LiteralHeaderModal(existingHeader: header) + }.onTapGesture { + selectedHeader = nil + }.disabled(vpn.state != .disabled) + .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector + } +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift new file mode 100644 index 0000000..5e1d7ef --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift @@ -0,0 +1,14 @@ +import SwiftUI + +struct NetworkTab: View { + var body: some View { + Form { + LiteralHeadersSection() + } + .formStyle(.grouped) + } +} + +#Preview { + NetworkTab() +} diff --git a/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift new file mode 100644 index 0000000..8aac9a0 --- /dev/null +++ b/Coder Desktop/Coder Desktop/Views/Settings/Settings.swift @@ -0,0 +1,26 @@ +import SwiftUI + +struct SettingsView: View { + @AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general + + var body: some View { + TabView(selection: $selection) { + GeneralTab() + .tabItem { + Label("General", systemImage: "gearshape") + }.tag(SettingsTab.general) + NetworkTab() + .tabItem { + Label("Network", systemImage: "dot.radiowaves.left.and.right") + }.tag(SettingsTab.network) + }.frame(width: 600) + .frame(maxHeight: 500) + .scrollContentBackground(.hidden) + .fixedSize() + } +} + +enum SettingsTab: Int { + case general + case network +} diff --git a/Coder Desktop/Coder Desktop/Views/Util.swift b/Coder Desktop/Coder Desktop/Views/Util.swift index 96fb246..ce61c66 100644 --- a/Coder Desktop/Coder Desktop/Views/Util.swift +++ b/Coder Desktop/Coder Desktop/Views/Util.swift @@ -1,4 +1,5 @@ import Combine +import SwiftUI // This is required for inspecting stateful views final class Inspection { diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index 8788936..5c6cc17 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -1,3 +1,4 @@ +import SettingsAccess import SwiftUI struct VPNMenu: View { @@ -21,6 +22,8 @@ struct VPNMenu: View { )) { Text("CoderVPN") .frame(maxWidth: .infinity, alignment: .leading) + .font(.body.bold()) + .foregroundColor(.primary) }.toggleStyle(.switch) .disabled(vpnDisabled) } @@ -50,6 +53,11 @@ struct VPNMenu: View { TrayDivider() } AuthButton() + SettingsLink { + ButtonRowView { Text("Settings") } + } preAction: {} postAction: { + appActivate() + }.buttonStyle(.plain) Button { About.open() } label: { diff --git a/Coder Desktop/Coder Desktop/Windows.swift b/Coder Desktop/Coder Desktop/Windows.swift index e82680c..61ac4ef 100644 --- a/Coder Desktop/Coder Desktop/Windows.swift +++ b/Coder Desktop/Coder Desktop/Windows.swift @@ -8,15 +8,7 @@ enum Windows: String { extension OpenWindowAction { // Type-safe wrapper for opening windows that also focuses the new window func callAsFunction(id: Windows) { - #if compiler(>=5.9) && canImport(AppKit) - if #available(macOS 14, *) { - NSApp.activate() - } else { - NSApp.activate(ignoringOtherApps: true) - } - #else - NSApp.activate(ignoringOtherApps: true) - #endif + appActivate() callAsFunction(id: id.rawValue) // The arranging behaviour is flakey without this NSApp.arrangeInFront(nil) diff --git a/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift new file mode 100644 index 0000000..6d68a74 --- /dev/null +++ b/Coder Desktop/Coder DesktopTests/LiteralHeadersSettingTests.swift @@ -0,0 +1,48 @@ +@testable import Coder_Desktop +import SwiftUI +import Testing +import ViewInspector + +@MainActor +@Suite(.timeLimit(.minutes(1))) +struct LiteralHeadersSettingTests { + let vpn: MockVPNService + let sut: LiteralHeadersSection + let view: any View + + init() { + vpn = MockVPNService() + sut = LiteralHeadersSection() + let store = UserDefaults(suiteName: #file)! + store.removePersistentDomain(forName: #file) + view = sut.environmentObject(vpn).environmentObject(Settings(store: store)) + } + + @Test + func testToggleDisabledWhenVPNEnabled() async throws { + vpn.state = .connected + + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in + let toggle = try view.find(ViewType.Toggle.self) + #expect(toggle.isDisabled()) + #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") } + } + } + } + + @Test + func testToggleEnabledWhenVPNDisabled() async throws { + vpn.state = .disabled + + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in + let toggle = try view.find(ViewType.Toggle.self) + #expect(!toggle.isDisabled()) + #expect(throws: Never.self) { try toggle.labelView().find(text: "HTTP Headers") } + } + } + } + + // TODO: More tests, ViewInspector cannot currently inspect Tables +} diff --git a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift index 912f409..6ba1154 100644 --- a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift +++ b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift @@ -15,7 +15,9 @@ struct LoginTests { init() { session = MockSession() sut = LoginForm() - view = sut.environmentObject(session) + let store = UserDefaults(suiteName: #file)! + store.removePersistentDomain(forName: #file) + view = sut.environmentObject(session).environmentObject(Settings(store: store)) } @Test @@ -70,12 +72,11 @@ struct LoginTests { @Test func testFailedAuthentication() async throws { - let login = LoginForm() let url = URL(string: "https://testFailedAuthentication.com")! Mock(url: url.appendingPathComponent("/api/v2/users/me"), statusCode: 401, data: [.get: Data()]).register() - try await ViewHosting.host(login.environmentObject(session)) { - try await login.inspection.inspect { view in + try await ViewHosting.host(view) { + try await sut.inspection.inspect { view in try view.find(ViewType.TextField.self).setInput(url.absoluteString) try view.find(button: "Next").tap() #expect(throws: Never.self) { try view.find(text: "Session Token") } diff --git a/Makefile b/Makefile index b746639..a428f06 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,7 @@ test: -testPlan $(SCHEME) \ -skipPackagePluginValidation \ CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO \ - | LC_ALL="en_US.UTF-8" xcpretty + CODE_SIGNING_ALLOWED=NO | xcbeautify lint: swiftlint \ From 81754a22b13a875713a43db53c4dd0346af13301 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 21 Jan 2025 13:13:01 +1100 Subject: [PATCH 2/4] use passed store --- Coder Desktop/Coder Desktop/State.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Coder Desktop/Coder Desktop/State.swift b/Coder Desktop/Coder Desktop/State.swift index f3d7094..7a58808 100644 --- a/Coder Desktop/Coder Desktop/State.swift +++ b/Coder Desktop/Coder Desktop/State.swift @@ -93,7 +93,7 @@ class SecureSession: ObservableObject, Session { } class Settings: ObservableObject { - let store: UserDefaults + private let store: UserDefaults @AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false @Published var literalHeaders: [LiteralHeader] { @@ -102,10 +102,10 @@ class Settings: ObservableObject { } } - init(store: UserDefaults = UserDefaults.standard) { + init(store: UserDefaults = .standard) { self.store = store _literalHeaders = Published( - initialValue: UserDefaults.standard.data( + initialValue: store.data( forKey: Keys.literalHeaders ).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? [] ) From dc6dd4f292829a70c7a3240964a9fd674619b4ef Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Wed, 22 Jan 2025 13:42:26 +1100 Subject: [PATCH 3/4] remove settingsaccess --- .../Coder Desktop.xcodeproj/project.pbxproj | 17 ----------------- .../xcshareddata/swiftpm/Package.resolved | 11 +---------- Coder Desktop/Coder Desktop/Views/VPNMenu.swift | 9 +++++---- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index 1f36fec..e68a8c4 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; }; AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; }; AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; }; - AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8EECF62D3A22320049DD09 /* SettingsAccess */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -233,7 +232,6 @@ AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */, AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */, AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */, - AA8EECF72D3A22320049DD09 /* SettingsAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -384,7 +382,6 @@ AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */, AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */, AA2C690E2D34F6920059AFAF /* LaunchAtLogin */, - AA8EECF62D3A22320049DD09 /* SettingsAccess */, ); productName = "Coder Desktop"; productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */; @@ -626,7 +623,6 @@ AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */, AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */, AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */, - AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */, ); preferredProjectObjectVersion = 77; productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */; @@ -1537,14 +1533,6 @@ kind = branch; }; }; - AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/orchetect/SettingsAccess"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.1.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1633,11 +1621,6 @@ package = AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; - AA8EECF62D3A22320049DD09 /* SettingsAccess */ = { - isa = XCSwiftPackageProductDependency; - package = AA8EECF52D3A22320049DD09 /* XCRemoteSwiftPackageReference "SettingsAccess" */; - productName = SettingsAccess; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 961678F42CFF100D00B2B6DF /* Project object */; diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c362f1f..37e6bc5 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "b52ef58779afac669f0b78fbf402855ebb45d016ab69ee39b5470c9442c12823", + "originHash" : "c41f63aa01c78f450e2232efbefcd30874995ad120db77fa5942062d6f813891", "pins" : [ { "identity" : "fluid-menu-bar-extra", @@ -36,15 +36,6 @@ "version" : "3.0.2" } }, - { - "identity" : "settingsaccess", - "kind" : "remoteSourceControl", - "location" : "https://github.com/orchetect/SettingsAccess", - "state" : { - "revision" : "08e80c35501f273afa2f5d6f737429bbe395ff81", - "version" : "2.1.0" - } - }, { "identity" : "swift-protobuf", "kind" : "remoteSourceControl", diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index 5c6cc17..5ba76e0 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -1,9 +1,9 @@ -import SettingsAccess import SwiftUI struct VPNMenu: View { @EnvironmentObject var vpn: VPN @EnvironmentObject var session: S + @Environment(\.openSettings) private var openSettings let inspection = Inspection() @@ -53,10 +53,11 @@ struct VPNMenu: View { TrayDivider() } AuthButton() - SettingsLink { - ButtonRowView { Text("Settings") } - } preAction: {} postAction: { + Button { + openSettings() appActivate() + } label: { + ButtonRowView { Text("Settings") } }.buttonStyle(.plain) Button { About.open() From a3adfc6cf69516c6b303eb2b261bca8bb6af8764 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Wed, 22 Jan 2025 13:45:14 +1100 Subject: [PATCH 4/4] remove unused compiler directives --- Coder Desktop/Coder Desktop/Coder_DesktopApp.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 0758f38..c45e632 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -60,13 +60,5 @@ class AppDelegate: NSObject, NSApplicationDelegate { @MainActor func appActivate() { - #if compiler(>=5.9) && canImport(AppKit) - if #available(macOS 14, *) { - NSApp.activate() - } else { - NSApp.activate(ignoringOtherApps: true) - } - #else - NSApp.activate(ignoringOtherApps: true) - #endif + NSApp.activate() }