1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-11-09 13:10:24 +01:00

🔥 Cleanup

This commit is contained in:
2022-02-07 18:00:35 +01:00
parent 776c2095e6
commit 0f464f5814
20 changed files with 4 additions and 42 deletions

View File

@@ -0,0 +1,81 @@
//
// Alert.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class Alert {
public static func present(
messageText: String,
informativeText: String,
buttonTitle: String = "OK",
secondButtonTitle: String = "",
style: NSAlert.Style = .informational
) -> Bool {
let alert = NSAlert.init()
alert.alertStyle = style
alert.messageText = messageText
alert.informativeText = informativeText
alert.addButton(withTitle: buttonTitle)
if (!secondButtonTitle.isEmpty) {
alert.addButton(withTitle: secondButtonTitle)
}
return alert.runModal() == .alertFirstButtonReturn
}
public static func confirm(
onWindow window: NSWindow,
messageText: String,
informativeText: String,
buttonTitle: String = "OK",
secondButtonTitle: String = "Cancel",
style: NSAlert.Style = .warning,
onFirstButtonPressed: @escaping (() -> Void)
) {
let alert = NSAlert.init()
alert.alertStyle = style
alert.messageText = messageText
alert.informativeText = informativeText
alert.addButton(withTitle: buttonTitle)
if (!secondButtonTitle.isEmpty) {
alert.addButton(withTitle: secondButtonTitle)
}
alert.beginSheetModal(for: window) { response in
if response == .alertFirstButtonReturn {
onFirstButtonPressed()
}
}
}
/**
Notify the user about something by showing an alert.
*/
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
_ = present(
messageText: message,
informativeText: info,
buttonTitle: "OK",
secondButtonTitle: "",
style: style
)
}
/**
Notify the user about a particular error (which must be `Alertable`)
by showing an alert.
*/
public static func notify(about error: Error & AlertableError) {
let key = error.getErrorMessageKey()
_ = present(
messageText: "\(key).title".localized,
informativeText: "\(key).description".localized,
buttonTitle: "OK",
secondButtonTitle: "",
style: .critical
)
}
}

View File

@@ -0,0 +1,63 @@
//
// Editor.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 07/12/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
/// An application that is capable of opening a particular directory (usually of a PHP project).
/// In most cases this is going to be a code editor, but it could also be another application
/// that supports opening those directories, like a visual Git client or a terminal app.
class Application {
enum AppType {
case editor, browser, git_gui, terminal, user_supplied
}
/// Name of the app. Used for display purposes and to determine `name.app` exists.
let name: String
/// Application type. Depending on the type, a different action might occur.
let type: AppType
/// Initializer. Used to detect a specific app of a specific type.
init(_ name: String, _ type: AppType) {
self.name = name
self.type = type
}
/**
Attempt to open a specific directory in the app of choice.
(This will open the app if it isn't open yet.)
*/
@objc public func openDirectory(file: String) {
return Shell.run("/usr/bin/open -a \"\(name)\" \"\(file)\"")
}
/** Checks if the app is installed. */
func isInstalled() -> Bool {
// If this script does not complain, the app exists!
return Shell.user.executeSynchronously(
"/usr/bin/open -Ra \"\(name)\"",
requiresPath: false
).task.terminationStatus == 0
}
/**
Detect which apps are available to open a specific directory.
*/
static public func detectPresetApplications() -> [Application] {
return [
Application("PhpStorm", .editor),
Application("Visual Studio Code", .editor),
Application("Sublime Text", .editor),
Application("Sublime Merge", .git_gui),
Application("iTerm", .terminal)
].filter {
return $0.isInstalled()
}
}
}

View File

@@ -0,0 +1,29 @@
//
// Async.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/01/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
/**
This generic async helper is something I'd like to use in more places.
The `DispatchQueue.global` into `DispatchQueue.main.async` logic is common in the app.
I could also use try `async` support which was introduced in Swift but that would
require too much refactoring at this time to consider. I also need to read up on async
in order to properly grasp all the gotchas. Looking into that later at some point.
*/
public func runAsync(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
{
DispatchQueue.global(qos: .userInitiated).async {
execute()
DispatchQueue.main.async {
completion()
}
}
}

View File

@@ -0,0 +1,23 @@
//
// FileSystem.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 07/12/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class Filesystem {
/**
Checks if a file exists at the provided path.
Uses `FileManager`.
*/
public static func fileExists(_ path: String) -> Bool {
return FileManager.default.fileExists(
atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
)
}
}

View File

@@ -0,0 +1,33 @@
//
// LocalNotification.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
import UserNotifications
class LocalNotification {
public static func send(title: String, subtitle: String) {
let content = UNMutableNotificationContent()
content.title = title
content.body = subtitle
let uuidString = UUID().uuidString
let request = UNNotificationRequest(
identifier: uuidString,
content: content,
trigger: nil
)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
Log.err(error!)
}
}
}
}

View File

@@ -0,0 +1,138 @@
//
// ImageGenerator.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class MenuBarImageGenerator {
/**
Takes a string and converts it to an image that can be displayed in the menu bar.
The width of the NSImage depends on the length of the text.
*/
public static func textToImage(text: String) -> NSImage {
let font = NSFont.systemFont(ofSize: 14, weight: .medium)
let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
let textFontAttributes = [
NSAttributedString.Key.font: font,
NSAttributedString.Key.foregroundColor: NSColor.black,
NSAttributedString.Key.paragraphStyle: textStyle
]
let padding : CGFloat = 2.0;
// Create an attributed string so we'll know how wide the item will need to be
let attributedString = NSAttributedString(string: text, attributes: textFontAttributes)
let textSize = attributedString.size()
// Add padding to the width of the menu bar item
let size = NSSize(width: textSize.width + (2 * padding), height: textSize.height)
let image = NSImage(size: size)
// Set the image rect with the appropriate dimensions
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
// Position the text inside the image rect
let textRect = CGRect(x: padding, y: 0.5, width: image.size.width, height: image.size.height)
let targetImage: NSImage = NSImage(size: image.size)
let representation: NSBitmapImageRep = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(image.size.width),
pixelsHigh: Int(image.size.height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: NSColorSpaceName.calibratedRGB,
bytesPerRow: 0,
bitsPerPixel: 0
)!
targetImage.addRepresentation(representation)
targetImage.lockFocus()
image.draw(in: imageRect)
text.draw(in: textRect, withAttributes: textFontAttributes)
targetImage.unlockFocus()
return targetImage
}
/**
The same as before, but also attempts to add an icon to the left.
*/
public static func textToImageWithIcon(text: String) -> NSImage {
// We'll start out with the image containing the text
let textImage = self.textToImage(text: text)
// Then we'll fetch the image we want on the left
var iconType = Preferences.preferences[.iconTypeToDisplay] as? String
if iconType == nil {
Log.warn("Invalid icon type found, using the default")
iconType = MenuBarIcon.iconPhp.rawValue
}
let iconImage = NSImage(named: "MenuBar_\(iconType!)")!
// We'll need to reference the width of the icon a bunch of times
let iconWidthSize = iconImage.size.width
// There will also be an additional divider between the image and the text (image)
let divider: CGFloat = 3
// Use a fixed size for the height of the menu bar (18pt)
let imageRect = CGRect(
x: 0,
y: 0,
width: textImage.size.width + iconWidthSize + divider,
height: 18
)
// Create a new image, we'll draw the text and our icon in there
let image: NSImage = NSImage(size: imageRect.size)
image.lockFocus()
// Calculate the offset between the image and the text
let offset = imageRect.size.width - textImage.size.width
// Draw the text with a negative x offset (so there is room on the left for the icon)
textImage.draw(
in: imageRect,
from: NSRect(
x: -offset,
y: 0,
width: textImage.size.width + offset,
height: textImage.size.height
),
operation: .overlay,
fraction: 1
)
// Draw the icon directly in the left of the imageRect (where we left space)
iconImage.draw(
in: imageRect,
from: NSRect(
x: 0,
y: 0,
width: imageRect.size.width,
height: imageRect.size.height
),
operation: .overlay,
fraction: 1
)
// We're done with this image
image.unlockFocus()
return image
}
}

View File

@@ -0,0 +1,52 @@
//
// PMWindowController.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 05/12/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
/**
This window class keeps track of which windows are currently visible, and reports this info back to the App class.
For more information, check the `windows` property on `App`.
- Note: This class does make a simple assumption: each window controller corresponds to a single view.
*/
class PMWindowController: NSWindowController, NSWindowDelegate {
public var windowName: String {
fatalError("Please specify a window name")
}
override func showWindow(_ sender: Any?) {
super.showWindow(sender)
App.shared.register(window: windowName)
}
func windowWillClose(_ notification: Notification) {
App.shared.remove(window: windowName)
}
deinit {
Log.perf("Window controller '\(windowName)' was deinitialized")
}
}
extension NSWindowController {
public func positionWindowInTopLeftCorner() {
guard let frame = NSScreen.main?.frame else { return }
guard let window = self.window else { return }
window.setFrame(NSRect(
x: frame.size.width - window.frame.size.width - 20,
y: frame.size.height - window.frame.size.height - 40,
width: window.frame.width,
height: window.frame.height
), display: true)
}
}

View File

@@ -0,0 +1,44 @@
//
// VersionExtractor.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 16/12/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
class VersionExtractor {
/**
This attempts to extract the version number from the command line output of Valet.
*/
public static func from(_ string: String) -> String? {
do {
let regex = try NSRegularExpression(
pattern: #"Laravel Valet (?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
options: []
)
let match = regex.matches(
in: string,
options: [],
range: NSMakeRange(0, string.count)
).first
guard let match = match else {
return nil
}
let range = Range(
match.range(withName: "version"),
in: string
)!
return String(string[range])
} catch {
return nil
}
}
}