mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-07 20:10:08 +02:00
🏗 WIP: Use objectWillChange.send()
This commit is contained in:
@ -91,7 +91,7 @@
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_working.json"
|
||||
isEnabled = "NO">
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_broken.json"
|
||||
|
@ -71,7 +71,7 @@ class Actions {
|
||||
"services stop \(name)",
|
||||
sudo: ServicesManager.shared[name]?.formula.elevated ?? false
|
||||
)
|
||||
await ServicesManager.loadHomebrewServices()
|
||||
await ServicesManager.shared.updateServices()
|
||||
}
|
||||
|
||||
public static func startService(name: String) async {
|
||||
@ -79,7 +79,7 @@ class Actions {
|
||||
"services start \(name)",
|
||||
sudo: ServicesManager.shared[name]?.formula.elevated ?? false
|
||||
)
|
||||
await ServicesManager.loadHomebrewServices()
|
||||
await ServicesManager.shared.updateServices()
|
||||
}
|
||||
|
||||
// MARK: - Finding Config Files
|
||||
|
@ -8,21 +8,43 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct HomebrewService: Decodable, Equatable, Hashable {
|
||||
class HomebrewService: Decodable, Equatable, Hashable {
|
||||
let name: String
|
||||
let service_name: String
|
||||
let running: Bool
|
||||
let loaded: Bool
|
||||
let pid: Int?
|
||||
let user: String?
|
||||
let status: String?
|
||||
let log_path: String?
|
||||
let error_log_path: String?
|
||||
var running: Bool
|
||||
var loaded: Bool
|
||||
var pid: Int?
|
||||
var user: String?
|
||||
var status: String?
|
||||
var log_path: String?
|
||||
var error_log_path: String?
|
||||
|
||||
init(
|
||||
name: String,
|
||||
service_name: String,
|
||||
running: Bool,
|
||||
loaded: Bool,
|
||||
pid: Int? = nil,
|
||||
user: String? = nil,
|
||||
status: String? = nil,
|
||||
log_path: String? = nil,
|
||||
error_log_path: String? = nil
|
||||
) {
|
||||
self.name = name
|
||||
self.service_name = service_name
|
||||
self.running = running
|
||||
self.loaded = loaded
|
||||
self.pid = pid
|
||||
self.user = user
|
||||
self.status = status
|
||||
self.log_path = log_path
|
||||
self.error_log_path = error_log_path
|
||||
}
|
||||
|
||||
/**
|
||||
Dummy data for preview purposes.
|
||||
*/
|
||||
public static func dummy(named service: String, enabled: Bool) -> Self {
|
||||
public static func dummy(named service: String, enabled: Bool) -> HomebrewService {
|
||||
return HomebrewService(
|
||||
name: service,
|
||||
service_name: service,
|
||||
@ -41,4 +63,10 @@ struct HomebrewService: Decodable, Equatable, Hashable {
|
||||
hasher.combine(service_name)
|
||||
hasher.combine(pid)
|
||||
}
|
||||
|
||||
static func == (lhs: HomebrewService, rhs: HomebrewService) -> Bool {
|
||||
return lhs.name == rhs.name
|
||||
&& lhs.pid == rhs.pid
|
||||
&& lhs.status == rhs.status
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ public struct TestableConfiguration: Codable {
|
||||
ActiveCommand.useTestable(commandOutput)
|
||||
Log.info("Applying fake scanner...")
|
||||
ValetScanner.useFake()
|
||||
Log.info("Applying fake services manager...")
|
||||
Homebrew.fake = true
|
||||
ServicesManager.useFake()
|
||||
Log.info("Applying fake Valet domain interactor...")
|
||||
ValetInteractor.useFake()
|
||||
}
|
||||
|
@ -21,13 +21,14 @@ class FakeServicesManager: ServicesManager {
|
||||
super.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!")
|
||||
Log.warn("If you do not want this behaviour, do not make use of a `FakeServicesManager`!")
|
||||
|
||||
self.fixedFormulae = formulae
|
||||
self.fixedStatus = status
|
||||
|
||||
self.services = self.formulae.map {
|
||||
self.serviceWrappers = self.formulae.map {
|
||||
let wrapper = ServiceWrapper(formula: $0)
|
||||
wrapper.isBusy = (status == .loading)
|
||||
wrapper.service = HomebrewService.dummy(named: $0.name, enabled: true)
|
||||
return wrapper
|
||||
}
|
||||
@ -38,4 +39,16 @@ class FakeServicesManager: ServicesManager {
|
||||
return HomebrewFormula.init(formula, elevated: false)
|
||||
}
|
||||
}
|
||||
|
||||
override func updateServices() async {
|
||||
await delay(seconds: 0.3)
|
||||
|
||||
for formula in self.serviceWrappers {
|
||||
formula.service?.running = true
|
||||
formula.isBusy = false
|
||||
}
|
||||
|
||||
print("Sending the update!")
|
||||
broadcastServicesUpdated()
|
||||
}
|
||||
}
|
||||
|
@ -13,20 +13,31 @@ class ServicesManager: ObservableObject {
|
||||
|
||||
@ObservedObject static var shared: ServicesManager = ValetServicesManager()
|
||||
|
||||
@Published var services = [ServiceWrapper]()
|
||||
@Published var serviceWrappers = [ServiceWrapper]()
|
||||
|
||||
public static func useFake() {
|
||||
ServicesManager.shared = FakeServicesManager.init(
|
||||
formulae: ["php", "nginx", "dnsmasq"],
|
||||
status: .loading
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
The order of services is important, so easy access is accomplished
|
||||
without much fanfare through subscripting.
|
||||
*/
|
||||
subscript(name: String) -> ServiceWrapper? {
|
||||
return self.services.first { wrapper in
|
||||
return self.serviceWrappers.first { wrapper in
|
||||
wrapper.name == name
|
||||
}
|
||||
}
|
||||
|
||||
public var statusMessage: String {
|
||||
if self.services.isEmpty {
|
||||
if self.serviceWrappers.isEmpty {
|
||||
return "Loading..."
|
||||
}
|
||||
|
||||
let statuses = self.services[0...2].map { $0.status }
|
||||
let statuses = self.serviceWrappers[0...2].map { $0.status }
|
||||
if statuses.contains(.loading) {
|
||||
return "Determining Valet status..."
|
||||
}
|
||||
@ -41,11 +52,11 @@ class ServicesManager: ObservableObject {
|
||||
}
|
||||
|
||||
public var statusColor: Color {
|
||||
if self.services.isEmpty {
|
||||
if self.serviceWrappers.isEmpty {
|
||||
return .yellow
|
||||
}
|
||||
|
||||
let statuses = self.services[0...2].map { $0.status }
|
||||
let statuses = self.serviceWrappers[0...2].map { $0.status }
|
||||
if statuses.contains(.loading) {
|
||||
return .orange
|
||||
}
|
||||
@ -61,14 +72,29 @@ class ServicesManager: ObservableObject {
|
||||
|
||||
@available(*, deprecated, message: "Use a more specific method instead")
|
||||
static func loadHomebrewServices() {
|
||||
print(self.shared)
|
||||
// print(self.shared)
|
||||
print("This method must be updated")
|
||||
}
|
||||
|
||||
public func updateServices() {
|
||||
public func updateServices() async {
|
||||
fatalError("Must be implemented in child class")
|
||||
}
|
||||
|
||||
public func broadcastServicesUpdated() {
|
||||
Task { @MainActor in
|
||||
self.serviceWrappers.forEach { wrapper in
|
||||
guard let service = wrapper.service else {
|
||||
return
|
||||
}
|
||||
|
||||
Log.perf("\(service.name): \(wrapper.status)")
|
||||
wrapper.objectWillChange.send()
|
||||
}
|
||||
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var formulae: [HomebrewFormula] {
|
||||
var formulae = [
|
||||
Homebrew.Formulae.php,
|
||||
@ -88,7 +114,7 @@ class ServicesManager: ObservableObject {
|
||||
init() {
|
||||
Log.info("The services manager will determine which Valet services exist on this system.")
|
||||
|
||||
services = formulae.map {
|
||||
serviceWrappers = formulae.map {
|
||||
ServiceWrapper(formula: $0)
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ class ValetServicesManager: ServicesManager {
|
||||
super.init()
|
||||
|
||||
// Load the initial services state
|
||||
self.updateServices()
|
||||
Task { await self.updateServices() }
|
||||
}
|
||||
|
||||
override func updateServices() {
|
||||
override func updateServices() async {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
if behaviours.contains(.broadcastServicesUpdate) {
|
||||
Task { await ServicesManager.loadHomebrewServices() }
|
||||
Task { await ServicesManager.shared.updateServices() }
|
||||
}
|
||||
|
||||
if error != nil {
|
||||
|
@ -96,7 +96,7 @@ extension MainMenu {
|
||||
Valet.notifyAboutUnsupportedTLD()
|
||||
|
||||
// Find out which services are active
|
||||
Log.info("The services manager knows about \(ServicesManager.shared.services.count) services.")
|
||||
Log.info("The services manager knows about \(ServicesManager.shared.serviceWrappers.count) services.")
|
||||
|
||||
// Start the background refresh timer
|
||||
startSharedTimer()
|
||||
|
@ -94,7 +94,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
||||
self.refreshActiveInstallation()
|
||||
self.refreshIcon()
|
||||
self.rebuild()
|
||||
await ServicesManager.loadHomebrewServices()
|
||||
await ServicesManager.shared.updateServices()
|
||||
Log.perf("The menu has been reloaded!")
|
||||
}
|
||||
}
|
||||
@ -184,7 +184,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
||||
// Make sure the shortcut key does not trigger this when the menu is open
|
||||
App.shared.shortcutHotkey?.isPaused = true
|
||||
Task { // Reload Homebrew services information asynchronously
|
||||
await ServicesManager.loadHomebrewServices()
|
||||
await ServicesManager.shared.updateServices()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,14 @@ struct ServicesView: View {
|
||||
init(manager: ServicesManager, perRow: Int, height: CGFloat? = nil) {
|
||||
self.manager = manager
|
||||
self.perRow = perRow
|
||||
self.chunkCount = manager.services.chunked(by: perRow).count
|
||||
self.chunkCount = manager.serviceWrappers.chunked(by: perRow).count
|
||||
self.height = CGFloat((50 * chunkCount) + (5 * perRow))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
VStack(alignment: .leading) {
|
||||
ForEach(manager.services.chunked(by: perRow), id: \.self) { chunk in
|
||||
ForEach(manager.serviceWrappers.chunked(by: perRow), id: \.self) { chunk in
|
||||
HStack {
|
||||
ForEach(chunk) { service in
|
||||
ServiceView(service: service).frame(minWidth: 70)
|
||||
@ -150,9 +150,16 @@ struct ServicesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ServicesView(manager: FakeServicesManager(
|
||||
formulae: ["php", "nginx", "dnsmasq"],
|
||||
status: .active
|
||||
status: .loading
|
||||
), perRow: 4)
|
||||
.frame(width: 330.0)
|
||||
.previewDisplayName("Loading")
|
||||
|
||||
ServicesView(manager: FakeServicesManager(
|
||||
formulae: ["php", "nginx", "dnsmasq"],
|
||||
status: .active
|
||||
), perRow: 4)
|
||||
.frame(width: 330.0)
|
||||
.previewDisplayName("Active")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user