Add urltest for menu extras
This commit is contained in:
+14
-1
@@ -1925,6 +1925,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Open Settings" : {
|
||||
|
||||
},
|
||||
"Other methods" : {
|
||||
"localizations" : {
|
||||
@@ -2877,6 +2880,16 @@
|
||||
"URL" : {
|
||||
"shouldTranslate" : false
|
||||
},
|
||||
"URLTest" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "URL 测试"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"User" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
@@ -2969,4 +2982,4 @@
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,14 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
private var menu: NSMenu?
|
||||
private var headerItem: NSMenuItem?
|
||||
private var headerView: StatusBarHeaderView?
|
||||
private var urlTestAllItem: NSMenuItem?
|
||||
private var urlTestAllView: StatusBarURLTestView?
|
||||
private var urlTestGroupViews: [String: StatusBarURLTestView] = [:]
|
||||
private var groupsItem: NSMenuItem?
|
||||
private var profilesItem: NSMenuItem?
|
||||
private var currentGroups: [LibboxOutboundGroup] = []
|
||||
private var isURLTestingAll = false
|
||||
private var urlTestingGroups = Set<String>()
|
||||
|
||||
public init(environments: ExtensionEnvironments) {
|
||||
self.environments = environments
|
||||
@@ -71,6 +77,12 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
}
|
||||
menu = nil
|
||||
headerView = nil
|
||||
urlTestAllView = nil
|
||||
urlTestAllItem = nil
|
||||
urlTestGroupViews.removeAll()
|
||||
currentGroups = []
|
||||
isURLTestingAll = false
|
||||
urlTestingGroups.removeAll()
|
||||
commandClient?.disconnect()
|
||||
commandClient = nil
|
||||
}
|
||||
@@ -162,6 +174,7 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
if isConnected {
|
||||
environments.commandClient.connect()
|
||||
}
|
||||
updateURLTestAvailability()
|
||||
}
|
||||
|
||||
private func loadProfiles() async {
|
||||
@@ -198,19 +211,41 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
}
|
||||
|
||||
private func updateGroupsMenu(_ groups: [LibboxOutboundGroup]?) {
|
||||
currentGroups = groups ?? []
|
||||
guard let submenu = groupsItem?.submenu else { return }
|
||||
submenu.removeAllItems()
|
||||
urlTestGroupViews.removeAll()
|
||||
|
||||
guard let groups else { return }
|
||||
let groups = currentGroups
|
||||
|
||||
let selectableGroups = groups.filter(\.selectable)
|
||||
if selectableGroups.isEmpty {
|
||||
groupsItem?.isHidden = true
|
||||
updateURLTestAvailability()
|
||||
return
|
||||
}
|
||||
|
||||
groupsItem?.isHidden = environments.extensionProfile?.status.isConnected != true
|
||||
|
||||
let urlTestAllView = StatusBarURLTestView(title: NSLocalizedString("URLTest", comment: "")) { [weak self] in
|
||||
self?.performURLTestForAllGroups()
|
||||
}
|
||||
let urlTestAllItem = NSMenuItem()
|
||||
urlTestAllItem.view = urlTestAllView
|
||||
self.urlTestAllView = urlTestAllView
|
||||
self.urlTestAllItem = urlTestAllItem
|
||||
submenu.addItem(urlTestAllItem)
|
||||
|
||||
let closeAllItem = NSMenuItem(
|
||||
title: NSLocalizedString("Close All Connections", comment: ""),
|
||||
action: #selector(closeAllConnections),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
closeAllItem.target = self
|
||||
submenu.addItem(closeAllItem)
|
||||
|
||||
submenu.addItem(NSMenuItem.separator())
|
||||
|
||||
let font = NSFont.menuFont(ofSize: 0)
|
||||
let attrs: [NSAttributedString.Key: Any] = [.font: font]
|
||||
|
||||
@@ -218,6 +253,15 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
let groupItem = NSMenuItem(title: group.tag, action: nil, keyEquivalent: "")
|
||||
let groupSubmenu = NSMenu()
|
||||
|
||||
let groupURLTestView = StatusBarURLTestView(title: NSLocalizedString("URLTest", comment: "")) { [weak self] in
|
||||
self?.performURLTestForGroup(group.tag)
|
||||
}
|
||||
let groupURLTestItem = NSMenuItem()
|
||||
groupURLTestItem.view = groupURLTestView
|
||||
groupSubmenu.addItem(groupURLTestItem)
|
||||
groupSubmenu.addItem(NSMenuItem.separator())
|
||||
urlTestGroupViews[group.tag] = groupURLTestView
|
||||
|
||||
var outboundData: [(LibboxOutboundGroupItem, NSMenuItem)] = []
|
||||
var maxTagWidth: CGFloat = 0
|
||||
var maxDelayWidth: CGFloat = 0
|
||||
@@ -275,6 +319,8 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
groupItem.submenu = groupSubmenu
|
||||
submenu.addItem(groupItem)
|
||||
}
|
||||
|
||||
updateURLTestAvailability()
|
||||
}
|
||||
|
||||
@objc private func selectProfile(_ sender: NSMenuItem) {
|
||||
@@ -308,6 +354,14 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func closeAllConnections() {
|
||||
do {
|
||||
try LibboxNewStandaloneCommandClient()!.closeConnections()
|
||||
} catch {
|
||||
showAlert(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func openApp() {
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
if let window = NSApp.windows.first(where: { $0.identifier?.rawValue == "main" }) {
|
||||
@@ -365,6 +419,77 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
alert.runModal()
|
||||
}
|
||||
|
||||
private func updateURLTestAvailability() {
|
||||
let isConnected = environments.extensionProfile?.status.isConnected == true
|
||||
let hasGroups = currentGroups.contains(where: \.selectable)
|
||||
let isAnyGroupTesting = !urlTestingGroups.isEmpty
|
||||
let allEnabled = isConnected && hasGroups && !isURLTestingAll && !isAnyGroupTesting
|
||||
urlTestAllView?.setLoading(isURLTestingAll)
|
||||
urlTestAllView?.setEnabled(allEnabled)
|
||||
urlTestAllItem?.isHidden = !isConnected || !hasGroups
|
||||
|
||||
for (tag, view) in urlTestGroupViews {
|
||||
let isGroupTesting = urlTestingGroups.contains(tag)
|
||||
view.setLoading(isGroupTesting)
|
||||
view.setEnabled(isConnected && !isURLTestingAll && !isGroupTesting)
|
||||
}
|
||||
}
|
||||
|
||||
private func performURLTestForAllGroups() {
|
||||
let groups = currentGroups.filter(\.selectable)
|
||||
guard !groups.isEmpty else { return }
|
||||
guard environments.extensionProfile?.status.isConnected == true else { return }
|
||||
guard !isURLTestingAll else { return }
|
||||
setURLTestAllRunning(true)
|
||||
|
||||
Task {
|
||||
do {
|
||||
let client = LibboxNewStandaloneCommandClient()!
|
||||
for group in groups {
|
||||
try await client.urlTest(group.tag)
|
||||
}
|
||||
} catch {
|
||||
await showAlert(error: error)
|
||||
}
|
||||
await MainActor.run {
|
||||
self.setURLTestAllRunning(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func performURLTestForGroup(_ tag: String) {
|
||||
guard environments.extensionProfile?.status.isConnected == true else { return }
|
||||
guard !isURLTestingAll else { return }
|
||||
guard !urlTestingGroups.contains(tag) else { return }
|
||||
setURLTestGroupRunning(tag, true)
|
||||
|
||||
Task {
|
||||
do {
|
||||
let client = LibboxNewStandaloneCommandClient()!
|
||||
try await client.urlTest(tag)
|
||||
} catch {
|
||||
await showAlert(error: error)
|
||||
}
|
||||
await MainActor.run {
|
||||
self.setURLTestGroupRunning(tag, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setURLTestAllRunning(_ running: Bool) {
|
||||
isURLTestingAll = running
|
||||
updateURLTestAvailability()
|
||||
}
|
||||
|
||||
private func setURLTestGroupRunning(_ tag: String, _ running: Bool) {
|
||||
if running {
|
||||
urlTestingGroups.insert(tag)
|
||||
} else {
|
||||
urlTestingGroups.remove(tag)
|
||||
}
|
||||
updateURLTestAvailability()
|
||||
}
|
||||
|
||||
// MARK: - NSMenuDelegate
|
||||
|
||||
public func menuWillOpen(_: NSMenu) {
|
||||
@@ -375,6 +500,77 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StatusBarURLTestView
|
||||
|
||||
@MainActor
|
||||
private class StatusBarURLTestView: NSView {
|
||||
private let button: NSButton
|
||||
private let progressIndicator: NSProgressIndicator
|
||||
private let action: () -> Void
|
||||
private var isEnabled = true
|
||||
private var isLoading = false
|
||||
|
||||
init(title: String, action: @escaping () -> Void) {
|
||||
self.action = action
|
||||
button = NSButton(title: title, target: nil, action: nil)
|
||||
progressIndicator = NSProgressIndicator()
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 250, height: 30))
|
||||
setupView()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setupView() {
|
||||
button.target = self
|
||||
button.action = #selector(buttonClicked)
|
||||
button.bezelStyle = .texturedRounded
|
||||
button.controlSize = .small
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(button)
|
||||
|
||||
progressIndicator.style = .spinning
|
||||
progressIndicator.controlSize = .small
|
||||
progressIndicator.isDisplayedWhenStopped = false
|
||||
progressIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(progressIndicator)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
|
||||
button.trailingAnchor.constraint(lessThanOrEqualTo: progressIndicator.leadingAnchor, constant: -8),
|
||||
button.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
|
||||
progressIndicator.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
|
||||
progressIndicator.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
func setEnabled(_ enabled: Bool) {
|
||||
isEnabled = enabled
|
||||
updateState()
|
||||
}
|
||||
|
||||
func setLoading(_ loading: Bool) {
|
||||
isLoading = loading
|
||||
updateState()
|
||||
}
|
||||
|
||||
private func updateState() {
|
||||
button.isEnabled = isEnabled && !isLoading
|
||||
if isLoading {
|
||||
progressIndicator.startAnimation(nil)
|
||||
} else {
|
||||
progressIndicator.stopAnimation(nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonClicked() {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StatusBarHeaderView
|
||||
|
||||
@MainActor
|
||||
|
||||
Reference in New Issue
Block a user