1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 20:10:08 +02:00

🏗 SwiftUI experiments

This commit is contained in:
2022-12-23 19:20:04 +01:00
parent 923f0237e8
commit 44c1ea7de4
13 changed files with 395 additions and 153 deletions

View File

@ -143,6 +143,18 @@
C4570C3B28FC355300D18420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; }; C4570C3B28FC355300D18420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
C4570C3C28FC355400D18420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; }; C4570C3C28FC355400D18420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; }; C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; };
C45B9149295607F400F4EC78 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B9148295607F400F4EC78 /* ServiceWrapper.swift */; };
C45B914A295607F400F4EC78 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B9148295607F400F4EC78 /* ServiceWrapper.swift */; };
C45B914B295607F400F4EC78 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B9148295607F400F4EC78 /* ServiceWrapper.swift */; };
C45B914C295607F400F4EC78 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B9148295607F400F4EC78 /* ServiceWrapper.swift */; };
C45B914E295608E300F4EC78 /* ValetServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B914D295608E300F4EC78 /* ValetServicesManager.swift */; };
C45B914F295608E300F4EC78 /* ValetServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B914D295608E300F4EC78 /* ValetServicesManager.swift */; };
C45B9150295608E300F4EC78 /* ValetServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B914D295608E300F4EC78 /* ValetServicesManager.swift */; };
C45B9151295608E300F4EC78 /* ValetServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B914D295608E300F4EC78 /* ValetServicesManager.swift */; };
C45B91532956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45B91542956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45B91552956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471E7AF28F9B4940021E251 /* InternalSwitcherTest.swift */; }; C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471E7AF28F9B4940021E251 /* InternalSwitcherTest.swift */; };
C45E2A77291992DA005C7CFD /* FeatureTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */; }; C45E2A77291992DA005C7CFD /* FeatureTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */; };
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
@ -786,6 +798,9 @@
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; }; C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; };
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; }; C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; };
C45B9148295607F400F4EC78 /* ServiceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceWrapper.swift; sourceTree = "<group>"; };
C45B914D295608E300F4EC78 /* ValetServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetServicesManager.swift; sourceTree = "<group>"; };
C45B91522956123A00F4EC78 /* FakeServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeServicesManager.swift; sourceTree = "<group>"; };
C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureTestCase.swift; sourceTree = "<group>"; }; C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureTestCase.swift; sourceTree = "<group>"; };
C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; }; C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; };
C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; }; C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; };
@ -1275,6 +1290,17 @@
path = php; path = php;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C45B9147295607E200F4EC78 /* Services */ = {
isa = PBXGroup;
children = (
C45E76132854A65300B4FE0C /* ServicesManager.swift */,
C45B914D295608E300F4EC78 /* ValetServicesManager.swift */,
C45B9148295607F400F4EC78 /* ServiceWrapper.swift */,
C45B91522956123A00F4EC78 /* FakeServicesManager.swift */,
);
path = Services;
sourceTree = "<group>";
};
C464ADAA275A7A25003FCD53 /* DomainList */ = { C464ADAA275A7A25003FCD53 /* DomainList */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1423,6 +1449,7 @@
C4B13B1D25C4915000548C3A /* App */ = { C4B13B1D25C4915000548C3A /* App */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C45B9147295607E200F4EC78 /* Services */,
C41C1B3C22B0098000E7CF16 /* Main.storyboard */, C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */, C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */,
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */, C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */,
@ -1436,7 +1463,6 @@
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */, C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */, C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
C45E76132854A65300B4FE0C /* ServicesManager.swift */,
C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */, C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */,
); );
path = App; path = App;
@ -1954,8 +1980,10 @@
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */, C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */,
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
C45B9149295607F400F4EC78 /* ServiceWrapper.swift in Sources */,
5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */, 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */,
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
C45B91532956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */, C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */,
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */, C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
@ -1967,6 +1995,7 @@
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */, C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */, C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
C45B914E295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */, C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */,
C4A6957628D23EE300A14CF8 /* EnvironmentManager.swift in Sources */, C4A6957628D23EE300A14CF8 /* EnvironmentManager.swift in Sources */,
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
@ -2155,6 +2184,7 @@
C471E86328F9BB650021E251 /* PMTableView.swift in Sources */, C471E86328F9BB650021E251 /* PMTableView.swift in Sources */,
C471E86428F9BB650021E251 /* Warning.swift in Sources */, C471E86428F9BB650021E251 /* Warning.swift in Sources */,
C40175BA2903108900763A68 /* ValetInteractor.swift in Sources */, C40175BA2903108900763A68 /* ValetInteractor.swift in Sources */,
C45B9150295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
C471E86528F9BB650021E251 /* WarningManager.swift in Sources */, C471E86528F9BB650021E251 /* WarningManager.swift in Sources */,
C471E86628F9BB650021E251 /* WarningsWindowController.swift in Sources */, C471E86628F9BB650021E251 /* WarningsWindowController.swift in Sources */,
C471E86728F9BB650021E251 /* OnboardingWindowController.swift in Sources */, C471E86728F9BB650021E251 /* OnboardingWindowController.swift in Sources */,
@ -2196,6 +2226,7 @@
C471E88E28F9BB650021E251 /* KeyCombo.swift in Sources */, C471E88E28F9BB650021E251 /* KeyCombo.swift in Sources */,
C471E88F28F9BB650021E251 /* ModifierFlagsExtension.swift in Sources */, C471E88F28F9BB650021E251 /* ModifierFlagsExtension.swift in Sources */,
C471E7E928F9BAC20021E251 /* Paths.swift in Sources */, C471E7E928F9BAC20021E251 /* Paths.swift in Sources */,
C45B91552956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C471E7FE28F9BACE0021E251 /* HomebrewPackage.swift in Sources */, C471E7FE28F9BACE0021E251 /* HomebrewPackage.swift in Sources */,
C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */, C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */,
C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */, C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */,
@ -2225,6 +2256,7 @@
C471E7F228F9BAC70021E251 /* PhpEnv.swift in Sources */, C471E7F228F9BAC70021E251 /* PhpEnv.swift in Sources */,
C471E7E628F9BAC20021E251 /* Process.swift in Sources */, C471E7E628F9BAC20021E251 /* Process.swift in Sources */,
C471E81928F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */, C471E81928F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */,
C45B914B295607F400F4EC78 /* ServiceWrapper.swift in Sources */,
C471E7D928F9BA8F0021E251 /* TestableShell.swift in Sources */, C471E7D928F9BA8F0021E251 /* TestableShell.swift in Sources */,
C471E81428F9BAE80021E251 /* NSWindowExtension.swift in Sources */, C471E81428F9BAE80021E251 /* NSWindowExtension.swift in Sources */,
C471E7D328F9BA8F0021E251 /* ActiveShell.swift in Sources */, C471E7D328F9BA8F0021E251 /* ActiveShell.swift in Sources */,
@ -2277,6 +2309,7 @@
C471E8A528F9BB8F0021E251 /* AppDelegate+InterApp.swift in Sources */, C471E8A528F9BB8F0021E251 /* AppDelegate+InterApp.swift in Sources */,
C471E8A628F9BB8F0021E251 /* App.swift in Sources */, C471E8A628F9BB8F0021E251 /* App.swift in Sources */,
C471E8A728F9BB8F0021E251 /* App+ActivationPolicy.swift in Sources */, C471E8A728F9BB8F0021E251 /* App+ActivationPolicy.swift in Sources */,
C45B914C295607F400F4EC78 /* ServiceWrapper.swift in Sources */,
C471E8A828F9BB8F0021E251 /* App+GlobalHotkey.swift in Sources */, C471E8A828F9BB8F0021E251 /* App+GlobalHotkey.swift in Sources */,
C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */, C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */,
C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */, C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */,
@ -2309,6 +2342,7 @@
C4D36618291160A1006BD146 /* WIP.swift in Sources */, C4D36618291160A1006BD146 /* WIP.swift in Sources */,
C471E8C328F9BB8F0021E251 /* SelectionVC.swift in Sources */, C471E8C328F9BB8F0021E251 /* SelectionVC.swift in Sources */,
C471E8C428F9BB8F0021E251 /* AddSiteVC.swift in Sources */, C471E8C428F9BB8F0021E251 /* AddSiteVC.swift in Sources */,
C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C471E8C528F9BB8F0021E251 /* AddProxyVC.swift in Sources */, C471E8C528F9BB8F0021E251 /* AddProxyVC.swift in Sources */,
C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */, C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */,
C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */, C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */,
@ -2347,6 +2381,7 @@
C471E8EA28F9BB8F0021E251 /* SectionHeaderView.swift in Sources */, C471E8EA28F9BB8F0021E251 /* SectionHeaderView.swift in Sources */,
C4D36604291132B7006BD146 /* ValetScanners.swift in Sources */, C4D36604291132B7006BD146 /* ValetScanners.swift in Sources */,
C471E8EB28F9BB8F0021E251 /* HeaderView.swift in Sources */, C471E8EB28F9BB8F0021E251 /* HeaderView.swift in Sources */,
C45B9151295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
C471E8EC28F9BB8F0021E251 /* SwiftUIHelper.swift in Sources */, C471E8EC28F9BB8F0021E251 /* SwiftUIHelper.swift in Sources */,
C471E8EE28F9BB8F0021E251 /* HotKey.swift in Sources */, C471E8EE28F9BB8F0021E251 /* HotKey.swift in Sources */,
C471E8EF28F9BB8F0021E251 /* HotKeysController.swift in Sources */, C471E8EF28F9BB8F0021E251 /* HotKeysController.swift in Sources */,
@ -2465,6 +2500,7 @@
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */, C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
C45B914A295607F400F4EC78 /* ServiceWrapper.swift in Sources */,
C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */, C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */,
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */, C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
@ -2530,6 +2566,7 @@
C4D3660C29113F20006BD146 /* System.swift in Sources */, C4D3660C29113F20006BD146 /* System.swift in Sources */,
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */, C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
C4D36611291140BE006BD146 /* TestableFileSystemTest.swift in Sources */, C4D36611291140BE006BD146 /* TestableFileSystemTest.swift in Sources */,
C45B91542956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C4E2E84B28FC1E70003B070C /* DataExtension.swift in Sources */, C4E2E84B28FC1E70003B070C /* DataExtension.swift in Sources */,
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */, C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */,
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
@ -2559,6 +2596,7 @@
C4B585452770FE3900DA4FBE /* RealCommand.swift in Sources */, C4B585452770FE3900DA4FBE /* RealCommand.swift in Sources */,
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
C4F780B725D80B5D000DBC97 /* App.swift in Sources */, C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
C45B914F295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */, C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */, C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */,
C46EBC4828DB9644007ACC74 /* RealShell.swift in Sources */, C46EBC4828DB9644007ACC74 /* RealShell.swift in Sources */,

View File

@ -109,7 +109,7 @@
<EnvironmentVariable <EnvironmentVariable
key = "PAINT_PHPMON_SWIFTUI_VIEWS" key = "PAINT_PHPMON_SWIFTUI_VIEWS"
value = "" value = ""
isEnabled = "NO"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
</EnvironmentVariables> </EnvironmentVariables>
</LaunchAction> </LaunchAction>

View File

@ -69,7 +69,7 @@ class Actions {
public static func stopService(name: String) async { public static func stopService(name: String) async {
await brew( await brew(
"services stop \(name)", "services stop \(name)",
sudo: ServicesManager.shared.services[name]?.formula.elevated ?? false sudo: ServicesManager.shared[name]?.formula.elevated ?? false
) )
await ServicesManager.loadHomebrewServices() await ServicesManager.loadHomebrewServices()
} }
@ -77,7 +77,7 @@ class Actions {
public static func startService(name: String) async { public static func startService(name: String) async {
await brew( await brew(
"services start \(name)", "services start \(name)",
sudo: ServicesManager.shared.services[name]?.formula.elevated ?? false sudo: ServicesManager.shared[name]?.formula.elevated ?? false
) )
await ServicesManager.loadHomebrewServices() await ServicesManager.loadHomebrewServices()
} }

View File

@ -9,8 +9,18 @@
import Foundation import Foundation
class Homebrew { class Homebrew {
static var fake: Bool = false
struct Formulae { struct Formulae {
static var php: HomebrewFormula { static var php: HomebrewFormula {
if Homebrew.fake {
return HomebrewFormula("php", elevated: true)
}
if PhpEnv.shared.homebrewPackage == nil {
fatalError("You must either load the HomebrewPackage object or call `fake` on the Homebrew class.")
}
return HomebrewFormula(PhpEnv.phpInstall.formula, elevated: true) return HomebrewFormula(PhpEnv.phpInstall.formula, elevated: true)
} }
@ -26,7 +36,7 @@ class Homebrew {
} }
} }
class HomebrewFormula { class HomebrewFormula: Equatable, Hashable {
let name: String let name: String
let elevated: Bool let elevated: Bool
@ -34,4 +44,13 @@ class HomebrewFormula {
self.name = name self.name = name
self.elevated = elevated self.elevated = elevated
} }
static func == (lhs: HomebrewFormula, rhs: HomebrewFormula) -> Bool {
return lhs.elevated == rhs.elevated && lhs.name == rhs.name
}
public func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(elevated)
}
} }

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
struct HomebrewService: Decodable, Equatable { struct HomebrewService: Decodable, Equatable, Hashable {
let name: String let name: String
let service_name: String let service_name: String
let running: Bool let running: Bool
@ -35,4 +35,10 @@ struct HomebrewService: Decodable, Equatable {
error_log_path: nil error_log_path: nil
) )
} }
public func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(service_name)
hasher.combine(pid)
}
} }

View File

@ -45,7 +45,7 @@ class PhpEnv {
var cachedPhpInstallations: [String: PhpInstallation] = [:] var cachedPhpInstallations: [String: PhpInstallation] = [:]
/** Information about the currently linked PHP installation. */ /** Information about the currently linked PHP installation. */
var currentInstall: ActivePhpInstallation var currentInstall: ActivePhpInstallation!
/** /**
The version that the `php` formula via Brew is aliased to on the current system. The version that the `php` formula via Brew is aliased to on the current system.

View File

@ -0,0 +1,33 @@
//
// FakeServicesManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/12/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
class FakeServicesManager: ServicesManager {
override init() {
Log.warn("A fake services manager is being used, so Homebrew formula resolver is set to act in fake mode.")
Log.warn("If you do not want this behaviour, never instantiate FakeServicesManager!")
Homebrew.fake = true
}
override var formulae: [HomebrewFormula] {
var formulae = [
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
let additionalFormulae = ["mailhog", "coolio"].map({ name in
return HomebrewFormula(name, elevated: false)
})
formulae.append(contentsOf: additionalFormulae)
return formulae
}
}

View File

@ -0,0 +1,59 @@
//
// ServiceWrapper.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/12/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
/**
Whether a given service is active, inactive or PHP Monitor is still busy determining the status.
*/
public enum ServiceStatus: String {
case active
case inactive
case loading
case missing
}
/**
Service wrapper, that contains the Homebrew JSON output (if determined) and the formula.
This helps the app determine whether a service should run as an administrator or not.
*/
public class ServiceWrapper: ObservableObject, Identifiable, Hashable {
var formula: HomebrewFormula
var service: HomebrewService?
var isBusy: Bool = false
public var name: String {
return formula.name
}
public var status: ServiceStatus {
if isBusy {
return .loading
}
guard let service = self.service else {
return .missing
}
return service.running ? .active : .inactive
}
init(formula: HomebrewFormula) {
self.formula = formula
}
public static func == (lhs: ServiceWrapper, rhs: ServiceWrapper) -> Bool {
return lhs.service == rhs.service && lhs.formula == rhs.formula
}
public func hash(into hasher: inout Hasher) {
hasher.combine(formula)
hasher.combine(service)
}
}

View File

@ -0,0 +1,57 @@
//
// ServicesManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 11/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import SwiftUI
class ServicesManager: ObservableObject {
@ObservedObject static var shared: ServicesManager = ValetServicesManager()
@Published private(set) var services = [ServiceWrapper]()
subscript(name: String) -> ServiceWrapper? {
return self.services.first { wrapper in
wrapper.name == name
}
}
@available(*, deprecated, message: "Use a more specific method instead")
static func loadHomebrewServices() {
print(self.shared)
print("This method must be updated")
}
public func updateServices() {
fatalError("Must be implemented in child class")
}
var formulae: [HomebrewFormula] {
var formulae = [
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
let additionalFormulae = (Preferences.custom.services ?? []).map({ item in
return HomebrewFormula(item, elevated: false)
})
formulae.append(contentsOf: additionalFormulae)
return formulae
}
init() {
Log.info("The services manager will determine which Valet services exist on this system.")
services = formulae.map {
ServiceWrapper(formula: $0)
}
}
}

View File

@ -0,0 +1,70 @@
//
// ValetServicesManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/12/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
class ValetServicesManager: ServicesManager {
override init() {
super.init()
// Load the initial services state
self.updateServices()
}
override func updateServices() {
// TODO
}
}
// TODO
/*
public static func loadHomebrewServices() async {
await Self.shared.updateServicesList()
Task {
let rootServiceNames = Self.shared.formulae
.filter { $0.elevated }
.map { $0.name }
let rootJson = await Shell
.pipe("sudo \(Paths.brew) services info --all --json")
.out.data(using: .utf8)!
let rootServices = try! JSONDecoder()
.decode([HomebrewService].self, from: rootJson)
.filter({ return rootServiceNames.contains($0.name) })
Task { @MainActor in
for service in rootServices {
Self.shared.services[service.name]!.service = service
}
}
}
Task {
let userServiceNames = Self.shared.formulae
.filter { !$0.elevated }
.map { $0.name }
let normalJson = await Shell
.pipe("\(Paths.brew) services info --all --json")
.out.data(using: .utf8)!
let userServices = try! JSONDecoder()
.decode([HomebrewService].self, from: normalJson)
.filter({ return userServiceNames.contains($0.name) })
Task { @MainActor in
for service in userServices {
Self.shared.services[service.name]!.service = service
}
}
}
}
*/

View File

@ -1,125 +0,0 @@
//
// ServicesManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 11/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import SwiftUI
class ServicesManager: ObservableObject {
static var shared = ServicesManager()
@Published private(set) var formulae: [HomebrewFormula]
@Published private(set) var services: [String: ServiceWrapper] = [:]
init() {
Log.info("Initializing ServicesManager...")
formulae = [
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
let additionalFormulae = (Preferences.custom.services ?? []).map({ item in
return HomebrewFormula(item, elevated: false)
})
formulae.append(contentsOf: additionalFormulae)
services = Dictionary(uniqueKeysWithValues: formulae.map { ($0.name, ServiceWrapper(formula: $0)) })
}
public func updateServicesList() async {
Task { @MainActor in
formulae = [
Homebrew.Formulae.php,
Homebrew.Formulae.nginx,
Homebrew.Formulae.dnsmasq
]
let additionalFormulae = (Preferences.custom.services ?? []).map({ item in
return HomebrewFormula(item, elevated: false)
})
formulae.append(contentsOf: additionalFormulae)
services = Dictionary(uniqueKeysWithValues: formulae.map { ($0.name, ServiceWrapper(formula: $0)) })
}
}
public static func loadHomebrewServices() async {
await Self.shared.updateServicesList()
Task {
let rootServiceNames = Self.shared.formulae
.filter { $0.elevated }
.map { $0.name }
let rootJson = await Shell
.pipe("sudo \(Paths.brew) services info --all --json")
.out.data(using: .utf8)!
let rootServices = try! JSONDecoder()
.decode([HomebrewService].self, from: rootJson)
.filter({ return rootServiceNames.contains($0.name) })
Task { @MainActor in
for service in rootServices {
Self.shared.services[service.name]!.service = service
}
}
}
Task {
let userServiceNames = Self.shared.formulae
.filter { !$0.elevated }
.map { $0.name }
let normalJson = await Shell
.pipe("\(Paths.brew) services info --all --json")
.out.data(using: .utf8)!
let userServices = try! JSONDecoder()
.decode([HomebrewService].self, from: normalJson)
.filter({ return userServiceNames.contains($0.name) })
Task { @MainActor in
for service in userServices {
Self.shared.services[service.name]!.service = service
}
}
}
}
/**
Service wrapper, that contains the Homebrew JSON output (if determined) and the formula.
This helps the app determine whether a service should run as an administrator or not.
*/
public struct ServiceWrapper {
public var formula: HomebrewFormula
public var service: HomebrewService?
init(formula: HomebrewFormula) {
self.formula = formula
}
}
/**
Dummy data for preview purposes.
*/
func withDummyServices(_ services: [String: Bool]) -> Self {
for (service, enabled) in services {
var item = ServiceWrapper(formula: HomebrewFormula(service))
item.service = HomebrewService.dummy(named: service, enabled: enabled)
self.services[service] = item
}
return self
}
}

View File

@ -96,7 +96,7 @@ extension MainMenu {
Valet.notifyAboutUnsupportedTLD() Valet.notifyAboutUnsupportedTLD()
// Find out which services are active // Find out which services are active
await ServicesManager.loadHomebrewServices() Log.info("The services manager knows about \(ServicesManager.shared.services.count) services.")
// Start the background refresh timer // Start the background refresh timer
startSharedTimer() startSharedTimer()

View File

@ -10,44 +10,129 @@ import Foundation
import SwiftUI import SwiftUI
struct ServicesView: View { struct ServicesView: View {
static func asMenuItem(perRow: Int = 3) -> NSMenuItem { static func asMenuItem(perRow: Int = 4) -> NSMenuItem {
let item = NSMenuItem() let item = NSMenuItem()
let view = NSHostingView( let manager = ServicesManager.shared
rootView: Self()
let rootView = Self(
manager: manager,
perRow: perRow
) )
let view = NSHostingView(rootView: rootView)
view.autoresizingMask = [.width, .height] view.autoresizingMask = [.width, .height]
view.setFrameSize(
CGSize(width: view.frame.width, height: rootView.height)
)
view.focusRingType = .none
let height = CGFloat(45 * ["a", "b", "c", "d", "e", "f"]
.chunked(by: perRow).count)
view.setFrameSize(CGSize(width: view.frame.width, height: height))
item.view = view item.view = view
return item return item
} }
@ObservedObject var manager: ServicesManager
var perRow: Int
var height: CGFloat
var chunkCount: Int
init(manager: ServicesManager, perRow: Int, height: CGFloat? = nil) {
self.manager = manager
self.perRow = perRow
self.chunkCount = manager.services.chunked(by: perRow).count
self.height = CGFloat((50 * chunkCount) + (5 * perRow))
}
var body: some View { var body: some View {
Text("WIP") GeometryReader { geometry in
.padding(10) VStack {
.frame(minWidth: 0, maxWidth: .infinity) ForEach(manager.services.chunked(by: perRow), id: \.self) { chunk in
HStack {
ForEach(chunk) { service in
ServiceView(service: service)
.frame(width: abs((geometry.size.width - 15) / CGFloat(perRow)))
}
}
}
}
.padding(.top, 10)
}
.frame(height: self.height)
.background(Color.debug) .background(Color.debug)
} }
} }
struct ServiceView: View {
@ObservedObject var service: ServiceWrapper
var body: some View {
VStack(spacing: 0) {
Text(service.name.uppercased())
.font(.system(size: 10))
.frame(minWidth: 0, maxWidth: .infinity)
.padding(.bottom, 4)
.background(Color.debug)
if service.status == .loading {
ProgressView()
.scaleEffect(x: 0.5, y: 0.5, anchor: .center)
.frame(width: 16.0, height: 20.0)
}
if service.status == .missing {
Button { print("we pressed da button ")} label: {
Text("?")
}
.buttonStyle(BlueButton())
}
if service.status == .active {
Button {
// TODO
} label: {
Image(systemName: "checkmark")
.resizable()
.frame(width: 12.0, height: 12.0)
.foregroundColor(Color("IconColorGreen"))
}
}
if service.status == .inactive {
Button {
// TODO
} label: {
Image(systemName: "xmark")
.resizable()
.frame(width: 12.0, height: 12.0)
.foregroundColor(Color("IconColorRed"))
}
}
}
}
}
public struct BlueButton: ButtonStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.bottom, 5)
.padding(.top, 5)
.padding(.leading, 10)
.padding(.trailing, 10)
.background(Color(red: 0, green: 0, blue: 0.5))
.foregroundColor(.white)
.clipShape(Capsule())
}
}
struct ServicesView_Previews: PreviewProvider { struct ServicesView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ServicesView() ServicesView(manager: FakeServicesManager(), perRow: 3)
.frame(width: 330.0) .frame(width: 330.0)
.previewDisplayName("Loading") .previewDisplayName("Loading")
ServicesView() ServicesView(manager: FakeServicesManager(), perRow: 3)
.frame(width: 330.0) .frame(width: 330.0)
.previewDisplayName("Light Mode") .previewDisplayName("Light Mode")
ServicesView() ServicesView(manager: FakeServicesManager(), perRow: 3)
.frame(width: 330.0) .frame(width: 330.0)
.previewDisplayName("Dark Mode") .previewDisplayName("Dark Mode")
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
} }
} }