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