Add urltest for menu extras
This commit is contained in:
@@ -1925,6 +1925,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Open Settings" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Other methods" : {
|
"Other methods" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -2877,6 +2880,16 @@
|
|||||||
"URL" : {
|
"URL" : {
|
||||||
"shouldTranslate" : false
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
|
"URLTest" : {
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "URL 测试"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"User" : {
|
"User" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"zh-Hans" : {
|
"zh-Hans" : {
|
||||||
|
|||||||
@@ -16,8 +16,14 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
private var menu: NSMenu?
|
private var menu: NSMenu?
|
||||||
private var headerItem: NSMenuItem?
|
private var headerItem: NSMenuItem?
|
||||||
private var headerView: StatusBarHeaderView?
|
private var headerView: StatusBarHeaderView?
|
||||||
|
private var urlTestAllItem: NSMenuItem?
|
||||||
|
private var urlTestAllView: StatusBarURLTestView?
|
||||||
|
private var urlTestGroupViews: [String: StatusBarURLTestView] = [:]
|
||||||
private var groupsItem: NSMenuItem?
|
private var groupsItem: NSMenuItem?
|
||||||
private var profilesItem: NSMenuItem?
|
private var profilesItem: NSMenuItem?
|
||||||
|
private var currentGroups: [LibboxOutboundGroup] = []
|
||||||
|
private var isURLTestingAll = false
|
||||||
|
private var urlTestingGroups = Set<String>()
|
||||||
|
|
||||||
public init(environments: ExtensionEnvironments) {
|
public init(environments: ExtensionEnvironments) {
|
||||||
self.environments = environments
|
self.environments = environments
|
||||||
@@ -71,6 +77,12 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
menu = nil
|
menu = nil
|
||||||
headerView = nil
|
headerView = nil
|
||||||
|
urlTestAllView = nil
|
||||||
|
urlTestAllItem = nil
|
||||||
|
urlTestGroupViews.removeAll()
|
||||||
|
currentGroups = []
|
||||||
|
isURLTestingAll = false
|
||||||
|
urlTestingGroups.removeAll()
|
||||||
commandClient?.disconnect()
|
commandClient?.disconnect()
|
||||||
commandClient = nil
|
commandClient = nil
|
||||||
}
|
}
|
||||||
@@ -162,6 +174,7 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
if isConnected {
|
if isConnected {
|
||||||
environments.commandClient.connect()
|
environments.commandClient.connect()
|
||||||
}
|
}
|
||||||
|
updateURLTestAvailability()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadProfiles() async {
|
private func loadProfiles() async {
|
||||||
@@ -198,19 +211,41 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateGroupsMenu(_ groups: [LibboxOutboundGroup]?) {
|
private func updateGroupsMenu(_ groups: [LibboxOutboundGroup]?) {
|
||||||
|
currentGroups = groups ?? []
|
||||||
guard let submenu = groupsItem?.submenu else { return }
|
guard let submenu = groupsItem?.submenu else { return }
|
||||||
submenu.removeAllItems()
|
submenu.removeAllItems()
|
||||||
|
urlTestGroupViews.removeAll()
|
||||||
|
|
||||||
guard let groups else { return }
|
let groups = currentGroups
|
||||||
|
|
||||||
let selectableGroups = groups.filter(\.selectable)
|
let selectableGroups = groups.filter(\.selectable)
|
||||||
if selectableGroups.isEmpty {
|
if selectableGroups.isEmpty {
|
||||||
groupsItem?.isHidden = true
|
groupsItem?.isHidden = true
|
||||||
|
updateURLTestAvailability()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
groupsItem?.isHidden = environments.extensionProfile?.status.isConnected != true
|
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 font = NSFont.menuFont(ofSize: 0)
|
||||||
let attrs: [NSAttributedString.Key: Any] = [.font: font]
|
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 groupItem = NSMenuItem(title: group.tag, action: nil, keyEquivalent: "")
|
||||||
let groupSubmenu = NSMenu()
|
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 outboundData: [(LibboxOutboundGroupItem, NSMenuItem)] = []
|
||||||
var maxTagWidth: CGFloat = 0
|
var maxTagWidth: CGFloat = 0
|
||||||
var maxDelayWidth: CGFloat = 0
|
var maxDelayWidth: CGFloat = 0
|
||||||
@@ -275,6 +319,8 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
groupItem.submenu = groupSubmenu
|
groupItem.submenu = groupSubmenu
|
||||||
submenu.addItem(groupItem)
|
submenu.addItem(groupItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateURLTestAvailability()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func selectProfile(_ sender: NSMenuItem) {
|
@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() {
|
@objc private func openApp() {
|
||||||
NSApp.setActivationPolicy(.regular)
|
NSApp.setActivationPolicy(.regular)
|
||||||
if let window = NSApp.windows.first(where: { $0.identifier?.rawValue == "main" }) {
|
if let window = NSApp.windows.first(where: { $0.identifier?.rawValue == "main" }) {
|
||||||
@@ -365,6 +419,77 @@ public class StatusBarController: NSObject, NSMenuDelegate {
|
|||||||
alert.runModal()
|
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
|
// MARK: - NSMenuDelegate
|
||||||
|
|
||||||
public func menuWillOpen(_: NSMenu) {
|
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
|
// MARK: - StatusBarHeaderView
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
|||||||
Reference in New Issue
Block a user