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:
@ -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 */,
|
||||
|
@ -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"
|
||||
}
|
||||
],
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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?
|
||||
|
||||
/**
|
||||
|
@ -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))
|
||||
|
||||
|
14
phpmon/Domain/SwiftUI/SwiftUIHelper.swift
Normal file
14
phpmon/Domain/SwiftUI/SwiftUIHelper.swift
Normal 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
|
||||
}
|
147
phpmon/Domain/SwiftUI/VersionPopoverView.swift
Normal file
147
phpmon/Domain/SwiftUI/VersionPopoverView.swift
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
@ -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.";
|
||||
|
Reference in New Issue
Block a user