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

🏗 WIP: Use objectWillChange.send()

This commit is contained in:
2023-01-03 16:11:55 +01:00
parent 296bc486c4
commit 3bcf52bf0a
11 changed files with 109 additions and 32 deletions

View File

@ -91,7 +91,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "--configuration:~/.phpmon_fconf_working.json"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "--configuration:~/.phpmon_fconf_broken.json"

View File

@ -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

View File

@ -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
}
}

View File

@ -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()
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -74,7 +74,7 @@ extension MainMenu {
}
if behaviours.contains(.broadcastServicesUpdate) {
Task { await ServicesManager.loadHomebrewServices() }
Task { await ServicesManager.shared.updateServices() }
}
if error != nil {

View File

@ -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()

View File

@ -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()
}
}

View File

@ -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")
}
}