1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 12:00:09 +02:00
This commit is contained in:
2022-06-11 20:11:11 +02:00
parent f1feb11baa
commit ceeba611d0
17 changed files with 149 additions and 413 deletions

View File

@ -45,7 +45,6 @@
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
C40C5C9C2846A40600E28255 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
@ -182,7 +181,7 @@
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
C4B6091A2853AAD300C95265 /* MiniHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* MiniHeaderView.swift */; };
C4B6091D2853AB9700C95265 /* NXServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* NXServicesView.swift */; };
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* ServicesView.swift */; };
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
@ -230,8 +229,6 @@
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
@ -400,7 +397,7 @@
C4B5853C2770FE3900DA4FBE /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
C4B5853D2770FE3900DA4FBE /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
C4B609192853AAD300C95265 /* MiniHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniHeaderView.swift; sourceTree = "<group>"; };
C4B6091C2853AB9700C95265 /* NXServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NXServicesView.swift; sourceTree = "<group>"; };
C4B6091C2853AB9700C95265 /* ServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
@ -428,8 +425,6 @@
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = "<group>"; };
C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
C4EED88827A48778006D7272 /* InterAppHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterAppHandler.swift; sourceTree = "<group>"; };
@ -774,8 +769,6 @@
C42800A928452AA10099C999 /* StatusMenu+Items.swift */,
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
C4EC1E67279DE0540010F296 /* ServicesView.swift */,
C4EC1E65279DE0380010F296 /* ServicesView.xib */,
);
path = Menu;
sourceTree = "<group>";
@ -878,7 +871,7 @@
C4B609172853AA9E00C95265 /* Menu */ = {
isa = PBXGroup;
children = (
C4B6091C2853AB9700C95265 /* NXServicesView.swift */,
C4B6091C2853AB9700C95265 /* ServicesView.swift */,
C4709CA128524B3400088BB8 /* StatsView.swift */,
C4B609192853AAD300C95265 /* MiniHeaderView.swift */,
);
@ -1157,7 +1150,6 @@
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
C4F30B07278E195800755FCE /* brew-services.json in Resources */,
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */,
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */,
@ -1233,7 +1225,6 @@
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */,
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
@ -1300,7 +1291,7 @@
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */,
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
C4B6091D2853AB9700C95265 /* NXServicesView.swift in Sources */,
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */,
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */,
@ -1433,7 +1424,6 @@
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,

View File

@ -1,25 +0,0 @@
{
"images" : [
{
"filename" : "ServiceLoading.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ServiceLoading@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,25 +0,0 @@
{
"images" : [
{
"filename" : "ServiceOff.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ServiceOff@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,25 +0,0 @@
{
"images" : [
{
"filename" : "ServiceOn.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ServiceOn@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -37,4 +37,21 @@ struct HomebrewService: Decodable, Equatable {
}
}
}
/**
Dummy data for preview purposes.
*/
public static func dummy(named service: String, enabled: Bool) -> Self {
return HomebrewService(
name: service,
service_name: service,
running: enabled,
loaded: enabled,
pid: nil,
user: nil,
status: nil,
log_path: nil,
error_log_path: nil
)
}
}

View File

@ -17,8 +17,22 @@ class ServicesManager: ObservableObject {
func loadData() {
HomebrewService.loadAll { services in
self.services = Dictionary(uniqueKeysWithValues: services.map { ($0.name, $0) })
self.services = Dictionary(
uniqueKeysWithValues: services.map { ($0.name, $0) }
)
}
}
/**
Dummy data for preview purposes.
*/
func withDummyServices(_ services: [String: Bool]) -> Self {
for (service, enabled) in services {
let item = HomebrewService.dummy(named: service, enabled: enabled)
self.services[service] = item
}
return self
}
}

View File

@ -1,93 +0,0 @@
//
// StatsView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 04/02/2021.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import Cocoa
/**
The ServicesView is an example of a view that I consider to be "poorly" set up.
Why ship it like this, then? Well, it works that's reason number one, really.
However, I do believe this should be refactored at some point. Here's why:
this view is responsible for retaining the information about the services status.
The status of the services should live somewhere else, and the fetching of said
service information should also not happen in a view. Yet here we are.
*/
class ServicesView: NSView, XibLoadable {
@IBOutlet weak var imageViewPhp: NSImageView!
@IBOutlet weak var imageViewNginx: NSImageView!
@IBOutlet weak var imageViewDnsmasq: NSImageView!
@IBOutlet weak var textFieldPhp: NSTextField!
static var services: [String: HomebrewService] = [:]
static func asMenuItem() -> NSMenuItem {
let view = Self.createFromXib()!
[view.imageViewPhp, view.imageViewNginx, view.imageViewDnsmasq].forEach { imageView in
imageView?.contentTintColor = NSColor(named: "IconColorNormal")
}
let item = NSMenuItem()
item.view = view
item.target = self
NotificationCenter.default.addObserver(
view, selector: #selector(self.updateInformation),
name: Events.ServicesUpdated,
object: nil
)
return item
}
@objc func updateInformation() {
self.loadData()
}
func loadData() {
self.applyAllInfoFieldsFromCachedValue()
HomebrewService.loadAll { services in
ServicesView.services = Dictionary(uniqueKeysWithValues: services.map { ($0.name, $0) })
self.applyAllInfoFieldsFromCachedValue()
}
}
func applyAllInfoFieldsFromCachedValue() {
if ServicesView.services.keys.isEmpty {
return
}
DispatchQueue.main.async {
self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased()
self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp)
self.applyServiceStyling("nginx", self.imageViewNginx)
self.applyServiceStyling("dnsmasq", self.imageViewDnsmasq)
}
}
func applyServiceStyling(_ serviceName: String, _ imageView: NSImageView) {
if ServicesView.services[serviceName] == nil {
imageView.image = NSImage(named: "ServiceLoading")
imageView.contentTintColor = NSColor(named: "IconColorNormal")
return
}
if ServicesView.services[serviceName]!.running {
imageView.image = NSImage(named: "ServiceOn")
imageView.contentTintColor = NSColor(named: "IconColorNormal")
return
}
imageView.image = NSImage(named: "ServiceOff")
imageView.contentTintColor = NSColor(named: "IconColorRed")
}
deinit {
NotificationCenter.default.removeObserver(self, name: Events.ServicesUpdated, object: nil)
}
}

View File

@ -1,150 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView wantsLayer="YES" id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="330" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
<rect key="frame" x="30" y="3" width="270" height="40"/>
<subviews>
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
<rect key="frame" x="0.0" y="4" width="77" height="32"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
<rect key="frame" x="23" y="18" width="31" height="14"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="PHP" id="LKe-C4-jxo">
<font key="font" metaFont="systemMedium" size="11"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tko-cP-XSz">
<rect key="frame" x="26" y="0.0" width="24" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Fxu-6h-A2h"/>
<constraint firstAttribute="width" constant="24" id="hOc-Ur-dmA"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="vjB-6Z-3xR"/>
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
</imageView>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
<rect key="frame" x="97" y="4" width="76" height="32"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
<rect key="frame" x="18" y="18" width="40" height="14"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="NGINX" id="Qfq-Bl-yuh">
<font key="font" metaFont="systemMedium" size="11"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZqW-6d-vpe">
<rect key="frame" x="30" y="0.0" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="EPG-jm-7Xs"/>
<constraint firstAttribute="width" constant="16" id="iif-kT-phn"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="JmQ-dU-ip7"/>
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
</imageView>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
<rect key="frame" x="193" y="4" width="77" height="32"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
<rect key="frame" x="8" y="18" width="62" height="14"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="DNSMASQ" id="lGh-MT-TgI">
<font key="font" metaFont="systemMedium" size="11"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DcG-x3-lvy">
<rect key="frame" x="31" y="0.0" width="16" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="AKl-Gq-RtM"/>
<constraint firstAttribute="height" constant="16" id="q2g-Ua-eIJ"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="Ign-Cq-DKf"/>
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
</imageView>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="40" id="2EU-Fd-hMg"/>
<constraint firstItem="nWj-33-m8Q" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="CAY-Pw-B8n"/>
<constraint firstAttribute="bottom" secondItem="doH-ww-BDw" secondAttribute="bottom" constant="4" id="Dq4-M6-1Wf"/>
<constraint firstItem="g4d-4N-NkC" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="bls-fM-H4b"/>
<constraint firstAttribute="bottom" secondItem="nWj-33-m8Q" secondAttribute="bottom" constant="4" id="f6j-eI-wiH"/>
<constraint firstAttribute="bottom" secondItem="g4d-4N-NkC" secondAttribute="bottom" constant="4" id="faS-Mo-Qa2"/>
<constraint firstItem="doH-ww-BDw" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="gL3-5S-OKo"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="TnH-dX-qaQ" secondAttribute="trailing" constant="30" id="3dD-wf-5pS"/>
<constraint firstItem="TnH-dX-qaQ" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="3" id="JmY-D0-uAy"/>
<constraint firstItem="TnH-dX-qaQ" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="30" id="S8i-CD-j3h"/>
<constraint firstAttribute="bottom" secondItem="TnH-dX-qaQ" secondAttribute="bottom" constant="3" id="fDc-OY-YL0"/>
<constraint firstItem="TnH-dX-qaQ" firstAttribute="centerY" secondItem="c22-O7-iKe" secondAttribute="centerY" id="fFF-rl-3s4"/>
</constraints>
<connections>
<outlet property="imageViewDnsmasq" destination="DcG-x3-lvy" id="XxJ-kZ-bdO"/>
<outlet property="imageViewNginx" destination="ZqW-6d-vpe" id="Wil-Ug-8Kb"/>
<outlet property="imageViewPhp" destination="tko-cP-XSz" id="q7L-HK-7Pj"/>
<outlet property="textFieldPhp" destination="At1-ch-qv2" id="Guk-hr-f1T"/>
</connections>
<point key="canvasLocation" x="-64" y="195"/>
</customView>
</objects>
<resources>
<image name="ServiceLoading" width="17" height="16"/>
</resources>
</document>

View File

@ -34,7 +34,7 @@ class StatusMenu: NSMenu {
self.addSwitchToPhpMenuItems()
self.addItem(NSMenuItem.separator())
self.addItem(NXServicesView.asMenuItem())
self.addItem(ServicesView.asMenuItem())
self.addItem(NSMenuItem.separator())
}

View File

@ -1,79 +0,0 @@
//
// ServicesView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import SwiftUI
struct NXServicesView: View {
@ObservedObject var serviceManager: ServicesManager
@State var serviceNames: [String] = {
return [
PhpEnv.phpInstall.formula,
"nginx",
"dnsmasq"
]
}()
static func asMenuItem() -> NSMenuItem {
let item = NSMenuItem()
let view = NSHostingView(rootView: Self(serviceManager: ServicesManager.shared))
view.frame = CGRect(x: 0, y: 0, width: 330, height: 55)
item.view = view
return item
}
var body: some View {
HStack(alignment: .firstTextBaseline, spacing: 10) {
ForEach(serviceNames, id: \.self) { service in
VStack(alignment: .center, spacing: 3) {
MiniHeaderView(text: service.uppercased())
CheckmarkView(serviceName: service).environmentObject(serviceManager)
}.frame(width: 90)
}
}.padding(10)
}
}
struct CheckmarkView: View {
@State var serviceName: String
@EnvironmentObject var serviceManager: ServicesManager
public func hasAnyServices() -> Bool {
return !serviceManager.services.isEmpty
}
public func active() -> Bool {
guard let service = serviceManager.services[serviceName] else {
return false
}
return service.running
}
var body: some View {
if !hasAnyServices() {
Image(systemName: "questionmark.circle")
.resizable()
.frame(width: 16.0, height: 16.0)
.foregroundColor(.black)
} else {
Image(systemName: "checkmark.circle")
.resizable()
.frame(width: 16.0, height: 16.0)
.foregroundColor(active() ? Color.black : Color("IconColorRed"))
}
}
}
struct NXServicesView_Previews: PreviewProvider {
static var previews: some View {
NXServicesView(serviceManager: ServicesManager.shared)
}
}

View File

@ -0,0 +1,112 @@
//
// ServicesView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import SwiftUI
struct ServicesView: View {
@ObservedObject var serviceManager: ServicesManager
@State var servicesToDisplay: [String]
static func asMenuItem() -> NSMenuItem {
let item = NSMenuItem()
let view = NSHostingView(
rootView: Self(
serviceManager: ServicesManager.shared,
servicesToDisplay: [
PhpEnv.phpInstall.formula,
"nginx",
"dnsmasq"
]
)
)
view.frame = CGRect(x: 0, y: 0, width: 330, height: 45)
item.view = view
return item
}
var body: some View {
HStack(alignment: .firstTextBaseline, spacing: 10) {
ForEach(servicesToDisplay, id: \.self) { service in
VStack(alignment: .center, spacing: 3) {
MiniHeaderView(text: service.uppercased())
CheckmarkView(serviceName: service).environmentObject(serviceManager)
}.frame(minWidth: 0, maxWidth: .infinity)
}
}.padding(10)
}
}
struct CheckmarkView: View {
@State var serviceName: String
@EnvironmentObject var serviceManager: ServicesManager
public func hasAnyServices() -> Bool {
return !serviceManager.services.isEmpty
}
public func active() -> Bool {
guard let service = serviceManager.services[serviceName] else {
return false
}
return service.running
}
var body: some View {
if !hasAnyServices() {
Image(systemName: "questionmark.circle")
.resizable()
.frame(width: 16.0, height: 16.0)
.foregroundColor(.secondary)
} else {
Image(systemName: active() ? "checkmark.circle" : "exclamationmark.triangle")
.resizable()
.frame(width: 16.0, height: 16.0)
.foregroundColor(active() ? Color("IconColorGreen") : Color("IconColorRed"))
}
}
}
struct ServicesView_Previews: PreviewProvider {
static var previews: some View {
ServicesView(
serviceManager: ServicesManager()
.withDummyServices([:]),
servicesToDisplay: ["php", "nginx", "dnsmasq"]
)
.frame(width: 330.0)
.previewDisplayName("Loading")
ServicesView(
serviceManager: ServicesManager()
.withDummyServices([
"php": false,
"nginx": true,
"dnsmasq": true
]),
servicesToDisplay: ["php", "nginx", "dnsmasq"]
)
.frame(width: 330.0)
.previewDisplayName("Light Mode")
ServicesView(
serviceManager: ServicesManager()
.withDummyServices([
"php": false,
"nginx": true,
"dnsmasq": true,
"mysql": false
]),
servicesToDisplay: ["php", "nginx", "dnsmasq", "mysql"]
)
.frame(width: 330.0)
.previewDisplayName("Dark Mode")
.preferredColorScheme(.dark)
}
}