diff --git a/phpmon/Assets.xcassets/LockIcon.imageset/Contents.json b/phpmon/Assets.xcassets/GreenLock.imageset/Contents.json similarity index 100% rename from phpmon/Assets.xcassets/LockIcon.imageset/Contents.json rename to phpmon/Assets.xcassets/GreenLock.imageset/Contents.json diff --git a/phpmon/Assets.xcassets/LockIcon.imageset/GreenLock.svg b/phpmon/Assets.xcassets/GreenLock.imageset/GreenLock.svg similarity index 100% rename from phpmon/Assets.xcassets/LockIcon.imageset/GreenLock.svg rename to phpmon/Assets.xcassets/GreenLock.imageset/GreenLock.svg diff --git a/phpmon/Assets.xcassets/RedLock.imageset/RedLock.svg b/phpmon/Assets.xcassets/RedLock.imageset/RedLock.svg index 66eeb55..cedd503 100644 --- a/phpmon/Assets.xcassets/RedLock.imageset/RedLock.svg +++ b/phpmon/Assets.xcassets/RedLock.imageset/RedLock.svg @@ -2,4 +2,12 @@ + + + + + + + + diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index 1b11da0..730c2bc 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -313,18 +313,18 @@ Gw - - + + - + - + - + @@ -368,10 +368,18 @@ Gw - + - - + + + + + + + + + - - - - - - - - + - - + + - - + @@ -437,6 +437,9 @@ Gw + + + @@ -444,6 +447,6 @@ Gw - + diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 9941174..48cfe8f 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -15,8 +15,7 @@ class Valet { var version: String var config: Valet.Configuration - var parkedSites: [Site] = [] - var linkedSites: [Site] = [] + var sites: [Site] = [] init() { self.version = Actions.valet("--version") @@ -34,22 +33,21 @@ class Valet { print("PHP Monitor should scan the following paths:") print(self.config.paths) - resolvePaths() + resolvePaths(tld: self.config.tld) } - private func resolvePaths() { - self.linkedSites = [] - self.parkedSites = [] + private func resolvePaths(tld: String) { + self.sites = [] for path in self.config.paths { let entries = try! FileManager.default.contentsOfDirectory(atPath: path) for entry in entries { - self.resolvePath(entry, forPath: path) + self.resolvePath(entry, forPath: path, tld: tld) } } } - private func resolvePath(_ entry: String, forPath path: String) { + private func resolvePath(_ entry: String, forPath path: String, tld: String) { let siteDir = path + "/" + entry // See if the file is a symlink, if so, resolve it @@ -59,11 +57,9 @@ class Valet { let type = attrs[FileAttributeKey.type] as! FileAttributeType if type == FileAttributeType.typeSymbolicLink { - self.linkedSites.append(Site(aliasPath: siteDir)) + self.sites.append(Site(aliasPath: siteDir, tld: tld)) } else if type == FileAttributeType.typeDirectory { - self.parkedSites.append(Site(absolutePath: siteDir)) - } else { - print("The item at: `\(siteDir)` was neither a symlink nor a directory. Skipping.") + self.sites.append(Site(absolutePath: siteDir, tld: tld)) } } @@ -74,19 +70,20 @@ class Valet { var absolutePath: String var aliasPath: String? + var secured: Bool - init(absolutePath: String) { + init(absolutePath: String, tld: String) { self.absolutePath = absolutePath self.aliasPath = nil self.name = URL(string: absolutePath)!.lastPathComponent - self.detectSiteProperties() + self.secured = Shell.fileExists("~/.config/valet/Certificates/\(self.name).\(tld).key") } - convenience init(aliasPath: String) { + convenience init(aliasPath: String, tld: String) { // Resolve the symlink let absolutePath = try! FileManager.default .destinationOfSymbolicLink(atPath: aliasPath) - self.init(absolutePath: absolutePath) + self.init(absolutePath: absolutePath, tld: tld) // TODO: Make sure the destination is a valid directory! @@ -95,10 +92,9 @@ class Valet { // Update the alias' path self.aliasPath = aliasPath - } - - private func detectSiteProperties() { - // TODO: Determine additional information, like Composer status and PHP version? + + // Make sure we check again, this time for the aliased file + self.secured = Shell.fileExists("~/.config/valet/Certificates/\(self.name).\(tld).key") } } diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index 069f329..0d5f1e7 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -12,6 +12,10 @@ import Carbon class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { + // MARK: - Outlets + + @IBOutlet weak var tableView: NSTableView! + // MARK: - Display public static func show(delegate: NSWindowDelegate? = nil) { @@ -33,6 +37,13 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { // MARK: - Lifecycle + override func viewDidLoad() { + let menu = NSMenu() + // menu.addItem(withTitle: "Secure", action: #selector(self.action), keyEquivalent: "L") + menu.addItem(withTitle: "Open in Browser...", action: #selector(self.openInBrowser), keyEquivalent: "O") + tableView.menu = menu + } + override func viewWillAppear() { } @@ -44,23 +55,45 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { // MARK: - Table View func numberOfRows(in tableView: NSTableView) -> Int { - return Valet.shared.linkedSites.count + return Valet.shared.sites.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { guard let userCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "siteItem"), owner: self) as? SiteListCell else { return nil } - let item = Valet.shared.linkedSites[row] + let item = Valet.shared.sites[row] + /// Make sure to show the TLD userCell.labelSiteName.stringValue = "\(item.name).\(Valet.shared.config.tld)" - userCell.labelPathName.stringValue = item.absolutePath - userCell.labelSiteType.isHidden = true - userCell.labelPhpVersion.isHidden = true + /// Show the absolute path, except make sure to replace the /Users/username segment with ~ for readability + userCell.labelPathName.stringValue = item.absolutePath + .replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~") + + /// If the `aliasPath` is nil, we're dealing with a parked site. Otherwise, it's a link that was explicitly created. + userCell.labelSiteType.stringValue = item.aliasPath == nil + ? "Parked Site" + : "Linked Site" + + /// Show the green or red lock based on whether the site was secured + userCell.imageViewLock.image = NSImage(named: item.secured ? "GreenLock" : "RedLock") + + /// TODO: Load the correct PHP version (not determined as of yet) + userCell.labelPhpVersion.stringValue = "PHP 8.0" return userCell } - + + @objc public func openInBrowser() { + if self.tableView.selectedRow == -1 { + return + } + + let site = Valet.shared.sites[self.tableView.selectedRow] + let prefix = site.secured ? "https://" : "http://" + let url = "\(prefix)\(site.name).\(Valet.shared.config.tld)" + NSWorkspace.shared.open(URL(string: url)!) + } // MARK: - Deinitialization diff --git a/phpmon/Domain/SiteList/SiteListWC.swift b/phpmon/Domain/SiteList/SiteListWC.swift index 2a3122a..cefe3ae 100644 --- a/phpmon/Domain/SiteList/SiteListWC.swift +++ b/phpmon/Domain/SiteList/SiteListWC.swift @@ -14,4 +14,18 @@ class SiteListWC: NSWindowController { super.windowDidLoad() } + /** + Allow users to close the window using Cmd-W, a shortcut I definitely use a lot. + */ + override func keyDown(with event: NSEvent) { + if event.modifierFlags.contains(.command) { + switch event.charactersIgnoringModifiers! { + case "w": + self.window?.close() + default: + break + } + } + } + }