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

👌 Popover with SwiftUI view

This commit is contained in:
2022-06-08 15:38:18 +02:00
parent 2229f01eb0
commit 655cd52ac4
10 changed files with 215 additions and 55 deletions

View File

@ -108,6 +108,8 @@
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
@ -356,6 +358,8 @@
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; };
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; };
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; };
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
@ -996,10 +1000,12 @@
C4EE55B027708BB2001DF387 /* SwiftUI */ = {
isa = PBXGroup;
children = (
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */,
C49E171E27A5736E00787921 /* PMServicesView.swift */,
C4EE55A627708B9E001DF387 /* PMHeaderView.swift */,
C4EE55A827708B9E001DF387 /* PMStatsView.swift */,
C4EE55A727708B9E001DF387 /* Preview.swift */,
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
@ -1224,6 +1230,7 @@
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */,
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */,
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
@ -1262,6 +1269,7 @@
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,

View File

@ -6,7 +6,7 @@
"components" : {
"alpha" : "1.000",
"blue" : "0.988",
"green" : "0.723",
"green" : "0.580",
"red" : "0.277"
}
},
@ -19,6 +19,15 @@
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.988",
"green" : "0.723",
"red" : "0.277"
}
},
"idiom" : "universal"
}
],

View File

@ -91,7 +91,7 @@ public struct PhpVersionNumberCollection: Equatable {
}
}
public struct PhpVersionNumber: Equatable {
public struct PhpVersionNumber: Equatable, Hashable {
let major: Int
let minor: Int
let patch: Int?

View File

@ -8,6 +8,7 @@
import Cocoa
import AppKit
import SwiftUI
class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
static let reusableName = "domainListPhpCell"
@ -20,6 +21,9 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
func populateCell(with site: ValetSite) {
self.site = site
buttonPhpVersion.isHidden = false
imageViewPhpVersionOK.isHidden = false
buttonPhpVersion.title = " PHP \(site.servingPhpVersion)"
imageViewPhpVersionOK.toolTip = nil
@ -33,9 +37,6 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp)
}
buttonPhpVersion.isHidden = false
imageViewPhpVersionOK.isHidden = false
}
func populateCell(with proxy: ValetProxy) {
@ -47,56 +48,25 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
@IBAction func pressedPhpVersion(_ sender: Any) {
guard let site = self.site else { return }
let alert = NSAlert.init()
alert.alertStyle = .informational
var validPhpSuggestions: [PhpVersionNumber] {
if site.isolatedPhpVersion != nil {
return []
}
var information = ""
if self.site?.isolatedPhpVersion != nil {
information += "alert.composer_php_isolated.desc".localized(
self.site!.isolatedPhpVersion!.versionNumber.homebrewVersion,
PhpEnv.phpInstall.version.short
)
information += "\n\n"
}
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
.localized
alert.messageText = "alert.composer_php_requirement.title"
.localized("\(site.name).\(Valet.shared.config.tld)", site.composerPhp)
alert.informativeText = information
alert.addButton(withTitle: "site_link.close".localized)
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
var map: [Int: String] = [:]
if site.isolatedPhpVersion == nil {
// Determine which installed versions would be ideal to switch to,
// but make sure to exclude the currently linked version
PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
version.homebrewVersion != PhpEnv.phpInstall.version.short
}).forEach { version in
alert.addButton(withTitle: "site_link.switch_to_php".localized(version.homebrewVersion))
map[mapIndex] = version.homebrewVersion
mapIndex += 1
}
// Site is not isolated, show options to switch global PHP version
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!) { response in
if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue {
if map.keys.contains(response.rawValue) {
let version = map[response.rawValue]!
Log.info("Pressed button to switch to \(version)")
MainMenu.shared.switchToPhpVersion(version)
}
}
}
} else {
// Site is isolated, do not show any options to switch
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!)
})
}
let button = self.buttonPhpVersion!
let popover = NSPopover()
let view = VersionPopoverView(site: site, validPhpVersions: validPhpSuggestions, parent: popover)
popover.contentViewController = NSHostingController(rootView: view)
popover.behavior = .transient
popover.animates = true
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .maxY)
}
}

View File

@ -23,6 +23,7 @@ extension ValetSite {
self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false)
self.secured = secure
self.composerPhp = constraint
self.composerPhpSource = constraint != "" ? .require : .unknown
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
.map { string in
@ -33,6 +34,7 @@ extension ValetSite {
self.driver = driver
self.driverDeterminedByComposer = true
if linked {
self.aliasPath = self.absolutePath
}

View File

@ -11,6 +11,11 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
static let shared = MainMenu()
override init() {
super.init()
statusItem.isVisible = !isRunningSwiftUIPreview
}
weak var menuDelegate: NSMenuDelegate?
/**

View File

@ -98,7 +98,7 @@ extension StatusMenu {
let presets = NSMenuItem(title: "mi_presets_title".localized, action: nil, keyEquivalent: "")
let presetsMenu = NSMenu()
presetsMenu.addItem(NSMenuItem.separator())
presetsMenu.addItem(HeaderView.asMenuItem(text: "mi_apply_presets_title".localized))

View File

@ -0,0 +1,14 @@
//
// SwiftUIHelper.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 08/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
var isRunningSwiftUIPreview: Bool {
return ProcessInfo.processInfo
.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil
}

View File

@ -0,0 +1,147 @@
//
// VersionPopoverView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 08/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct VersionPopoverView: View {
@State var site: ValetSite
@State var validPhpVersions: [PhpVersionNumber]
@State var parent: NSPopover!
func getTitle() -> String {
if site.composerPhpSource == .unknown {
return "alert.composer_php_requirement.unable_to_determine".localized
}
return "alert.composer_php_requirement.title".localized(
"\(site.name).\(Valet.shared.config.tld)",
site.composerPhp
)
}
func getSource() -> String {
var information = ""
if site.isolatedPhpVersion != nil {
information += "alert.composer_php_isolated.desc".localized(
site.isolatedPhpVersion!.versionNumber.homebrewVersion,
PhpEnv.phpInstall.version.short
)
information += "\n\n"
}
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
.localized
return information
}
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(getTitle())
.fontWeight(.bold)
.fixedSize(horizontal: false, vertical: true)
Text(getSource())
.fixedSize(horizontal: false, vertical: true)
.font(.subheadline)
if !validPhpVersions.isEmpty {
// Suggestions for alternative PHP versions
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .center, spacing: 5) {
Image(systemName: "info.circle.fill")
.renderingMode(.template)
.foregroundColor(Color("AppColor"))
Text("alert.php_suggestions".localized)
.font(.subheadline)
.foregroundColor(Color("AppColor"))
}.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0))
HStack {
ForEach(validPhpVersions, id: \.self) { version in
Button("site_link.switch_to_php".localized(version.homebrewVersion), action: {
MainMenu.shared.switchToPhpVersion(version.homebrewVersion)
parent?.close()
})
}
}
}
} else {
if site.composerPhpSource != .unknown {
HStack(alignment: .center, spacing: 5) {
Image(systemName: "checkmark.seal.fill")
.renderingMode(.template)
.foregroundColor(Color("IconColorGreen"))
Text("alert.php_version_ideal".localized)
.font(.subheadline)
.foregroundColor(Color("IconColorGreen"))
}.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0))
} else {
HStack(alignment: .firstTextBaseline, spacing: 5) {
Image(systemName: "questionmark.circle.fill")
.renderingMode(.template)
.foregroundColor(Color.secondary)
Text("alert.unable_to_determine_is_fine".localized)
.font(.subheadline)
.foregroundColor(Color.secondary)
}.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0))
}
}
}.frame(width: 400, height: nil, alignment: .center)
.padding(20)
.background(
Color(NSColor.windowBackgroundColor).padding(-80)
)
}
}
struct VersionPopoverView_Previews: PreviewProvider {
static var previews: some View {
VersionPopoverView(
site: ValetSite(
fakeWithName: "amazingwebsite",
tld: "test",
secure: true,
path: "/path/to/site",
linked: true,
constraint: ""
),
validPhpVersions: [],
parent: nil
)
VersionPopoverView(
site: ValetSite(
fakeWithName: "amazingwebsite",
tld: "test",
secure: true,
path: "/path/to/site",
linked: true,
constraint: "^8.1"
),
validPhpVersions: [],
parent: nil
)
VersionPopoverView(
site: ValetSite(
fakeWithName: "anothersite",
tld: "test",
secure: true,
path: "/path/to/site",
linked: true,
constraint: "^8.0"
),
validPhpVersions: [
PhpVersionNumber(major: 8, minor: 0, patch: 0),
PhpVersionNumber(major: 8, minor: 1, patch: 0)
],
parent: nil
)
}
}

View File

@ -302,12 +302,17 @@ problem manually, using your own Terminal app (this just shows you the output)."
"alert.composer_success.info" = "Your global Composer dependencies have been successfully updated.";
// Composer Version
"alert.composer_php_isolated.desc" = "This site has been isolated, which means that Valet serves PHP %@ for this site specifically (the global version is %@).";
"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: %@.";
"alert.composer_php_requirement.type.unknown" = "The required PHP version is a mystery.";
"alert.composer_php_requirement.title" = "'%@' requires PHP %@.";
"alert.composer_php_requirement.unable_to_determine" = "Unable to determine PHP requirement";
"alert.composer_php_requirement.type.unknown" = "PHP Monitor was unable to determine which version of PHP is required for this domain. The constraint may be determined if you have a `composer.json` or a `.valetphprc` file in your project's directory.";
"alert.composer_php_requirement.type.require" = "This required PHP version was determined by checking the `require` field in the `composer.json` file when the site list was last refreshed.";
"alert.composer_php_requirement.type.platform" = "This required PHP version was determined by checking the `platform` field in the `composer.json` file when the site list was last refreshed.";
"alert.composer_php_requirement.type.valetphprc" = "This required PHP version was determined by checking the .valetphprc file in your project's directory.";
"alert.unable_to_determine_is_fine" = "If you have a simple project, there may not be a specified PHP version set as a requirement. In that case, you are free to ignore this warning.";
"alert.php_version_ideal" = "The currently active PHP version is ideal for this site.";
"alert.php_suggestions" = "There may be a different PHP version which is closer to the constraint.";
// Suggest Fix My Valet
"alert.php_switch_failed.title" = "Switching to PHP %@ seems to have failed.";