2018-08-04 21:49:32 +08:00
|
|
|
//
|
2018-11-30 22:14:20 +08:00
|
|
|
// MenuItemFactory.swift
|
2018-08-04 21:49:32 +08:00
|
|
|
// ClashX
|
|
|
|
//
|
|
|
|
// Created by CYC on 2018/8/4.
|
2018-08-08 13:47:38 +08:00
|
|
|
// Copyright © 2018年 yichengchen. All rights reserved.
|
2018-08-04 21:49:32 +08:00
|
|
|
//
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import RxCocoa
|
2019-10-20 13:40:50 +08:00
|
|
|
import SwiftyJSON
|
2018-08-04 21:49:32 +08:00
|
|
|
|
2018-11-30 22:14:20 +08:00
|
|
|
class MenuItemFactory {
|
2020-04-25 12:06:02 +08:00
|
|
|
private static var cachedProxyData: ClashProxyResp?
|
|
|
|
|
2023-07-12 14:39:53 +08:00
|
|
|
static let useViewToRenderProxy: Bool = AppDelegate.isAboveMacOS152
|
2020-02-26 17:26:12 +08:00
|
|
|
|
2020-02-22 12:35:25 +08:00
|
|
|
// MARK: - Public
|
2020-02-22 11:46:49 +08:00
|
|
|
|
2020-04-25 12:06:02 +08:00
|
|
|
static func refreshExistingMenuItems() {
|
2020-04-29 20:29:08 +08:00
|
|
|
ApiRequest.getMergedProxyData {
|
2020-04-25 12:06:02 +08:00
|
|
|
info in
|
2020-04-29 20:29:08 +08:00
|
|
|
if info?.proxiesMap.keys != cachedProxyData?.proxiesMap.keys {
|
2020-04-25 12:06:02 +08:00
|
|
|
// force update menu
|
|
|
|
refreshMenuItems(mergedData: info)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-20 11:48:21 +08:00
|
|
|
for proxy in info?.proxies ?? [] {
|
|
|
|
NotificationCenter.default.post(name: .proxyUpdate(for: proxy.name), object: proxy, userInfo: nil)
|
2020-04-25 12:06:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static func recreateProxyMenuItems() {
|
2020-04-29 20:29:08 +08:00
|
|
|
ApiRequest.getMergedProxyData {
|
2020-04-25 12:06:02 +08:00
|
|
|
proxyInfo in
|
2020-04-29 20:29:08 +08:00
|
|
|
cachedProxyData = proxyInfo
|
2020-04-25 12:06:02 +08:00
|
|
|
refreshMenuItems(mergedData: proxyInfo)
|
2019-10-06 12:22:21 +08:00
|
|
|
}
|
2020-04-25 12:06:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) {
|
2020-11-14 15:01:28 +08:00
|
|
|
let leftPadding = AppDelegate.shared.hasMenuSelected()
|
2020-04-25 12:06:02 +08:00
|
|
|
guard let proxyInfo = proxyInfo else { return }
|
|
|
|
var menuItems = [NSMenuItem]()
|
|
|
|
for proxy in proxyInfo.proxyGroups {
|
|
|
|
var menu: NSMenuItem?
|
|
|
|
switch proxy.type {
|
2020-11-14 15:01:28 +08:00
|
|
|
case .select: menu = generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo, leftPadding: leftPadding)
|
|
|
|
case .urltest, .fallback: menu = generateUrlTestFallBackMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo, leftPadding: leftPadding)
|
2020-04-25 12:06:02 +08:00
|
|
|
case .loadBalance:
|
2021-04-18 11:58:17 +08:00
|
|
|
menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo, leftPadding: leftPadding)
|
2020-04-25 12:06:02 +08:00
|
|
|
case .relay:
|
|
|
|
menu = generateListOnlyMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
|
|
|
|
default: continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if let menu = menu {
|
|
|
|
menuItems.append(menu)
|
|
|
|
menu.isEnabled = true
|
|
|
|
}
|
2020-02-22 11:46:49 +08:00
|
|
|
}
|
2020-04-25 12:06:02 +08:00
|
|
|
let items = Array(menuItems.reversed())
|
|
|
|
updateProxyList(withMenus: items)
|
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-05-10 23:36:43 +08:00
|
|
|
static func generateSwitchConfigMenuItems(complete: @escaping (([NSMenuItem]) -> Void)) {
|
|
|
|
let generateMenuItem: ((String) -> NSMenuItem) = {
|
|
|
|
config in
|
2020-04-25 12:06:02 +08:00
|
|
|
let item = NSMenuItem(title: config, action: #selector(MenuItemFactory.actionSelectConfig(sender:)), keyEquivalent: "")
|
|
|
|
item.target = MenuItemFactory.self
|
|
|
|
item.state = ConfigManager.selectConfigName == config ? .on : .off
|
2020-05-10 23:36:43 +08:00
|
|
|
return item
|
|
|
|
}
|
2022-06-19 14:48:24 +08:00
|
|
|
|
2021-12-08 20:18:35 +08:00
|
|
|
if RemoteControlManager.selectConfig != nil {
|
|
|
|
complete([])
|
|
|
|
return
|
|
|
|
}
|
2020-05-10 23:36:43 +08:00
|
|
|
|
2022-11-20 12:08:46 +08:00
|
|
|
if ICloudManager.shared.useiCloud.value {
|
2022-06-19 14:48:24 +08:00
|
|
|
ICloudManager.shared.getConfigFilesList {
|
2020-05-10 23:36:43 +08:00
|
|
|
complete($0.map { generateMenuItem($0) })
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
complete(ConfigManager.getConfigFilesList().map { generateMenuItem($0) })
|
2020-04-25 12:06:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Private
|
|
|
|
|
|
|
|
// MARK: Updaters
|
|
|
|
|
|
|
|
static func updateProxyList(withMenus menus: [NSMenuItem]) {
|
|
|
|
let app = AppDelegate.shared
|
|
|
|
let startIndex = app.statusMenu.items.firstIndex(of: app.separatorLineTop)! + 1
|
|
|
|
let endIndex = app.statusMenu.items.firstIndex(of: app.sepatatorLineEndProxySelect)!
|
|
|
|
app.sepatatorLineEndProxySelect.isHidden = menus.count == 0
|
|
|
|
for _ in 0..<endIndex - startIndex {
|
|
|
|
app.statusMenu.removeItem(at: startIndex)
|
|
|
|
}
|
|
|
|
for each in menus {
|
|
|
|
app.statusMenu.insertItem(each, at: startIndex)
|
2020-02-22 12:35:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 12:06:02 +08:00
|
|
|
// MARK: Generators
|
2020-02-22 12:35:25 +08:00
|
|
|
|
|
|
|
private static func generateSelectorMenuItem(proxyGroup: ClashProxy,
|
2020-11-14 15:01:28 +08:00
|
|
|
proxyInfo: ClashProxyResp,
|
2022-06-19 14:48:24 +08:00
|
|
|
leftPadding: Bool) -> NSMenuItem? {
|
2019-03-23 19:13:28 +08:00
|
|
|
let proxyMap = proxyInfo.proxiesMap
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-04-08 11:24:13 +08:00
|
|
|
let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global
|
|
|
|
if !isGlobalMode {
|
|
|
|
if proxyGroup.name == "GLOBAL" { return nil }
|
|
|
|
}
|
|
|
|
|
2019-03-17 22:00:47 +08:00
|
|
|
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
|
|
|
|
let selectedName = proxyGroup.now ?? ""
|
2023-07-12 14:39:53 +08:00
|
|
|
if !Settings.disableShowCurrentProxyInMenu {
|
2020-11-14 15:01:28 +08:00
|
|
|
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding)
|
2019-10-16 22:46:03 +08:00
|
|
|
}
|
2020-02-22 18:31:13 +08:00
|
|
|
let submenu = ProxyGroupMenu(title: proxyGroup.name)
|
2019-10-20 13:40:50 +08:00
|
|
|
|
|
|
|
for proxy in proxyGroup.all ?? [] {
|
|
|
|
guard let proxyModel = proxyMap[proxy] else { continue }
|
2020-02-24 19:58:09 +08:00
|
|
|
let proxyItem = ProxyMenuItem(proxy: proxyModel,
|
2020-04-26 16:40:29 +08:00
|
|
|
group: proxyGroup,
|
2020-04-26 18:32:20 +08:00
|
|
|
action: #selector(MenuItemFactory.actionSelectProxy(sender:)))
|
2018-11-30 22:14:20 +08:00
|
|
|
proxyItem.target = MenuItemFactory.self
|
2020-02-22 18:31:13 +08:00
|
|
|
submenu.add(delegate: proxyItem)
|
2018-08-05 19:45:37 +08:00
|
|
|
submenu.addItem(proxyItem)
|
2018-11-04 17:20:25 +08:00
|
|
|
}
|
2020-02-24 22:26:35 +08:00
|
|
|
|
2020-04-26 18:32:20 +08:00
|
|
|
if proxyGroup.isSpeedTestable && useViewToRenderProxy {
|
2020-02-24 22:26:35 +08:00
|
|
|
submenu.minimumWidth = proxyGroup.maxProxyNameLength + ProxyItemView.fixedPlaceHolderWidth
|
|
|
|
}
|
|
|
|
|
2019-10-16 22:46:03 +08:00
|
|
|
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
|
|
|
|
menu.submenu = submenu
|
2018-08-05 19:45:37 +08:00
|
|
|
return menu
|
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-11-14 15:01:28 +08:00
|
|
|
private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy,
|
|
|
|
proxyInfo: ClashProxyResp,
|
2022-06-19 14:48:24 +08:00
|
|
|
leftPadding: Bool) -> NSMenuItem? {
|
2019-03-23 19:13:28 +08:00
|
|
|
let proxyMap = proxyInfo.proxiesMap
|
2019-03-17 22:00:47 +08:00
|
|
|
let selectedName = proxyGroup.now ?? ""
|
2019-05-10 21:42:43 +08:00
|
|
|
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
|
2023-07-12 14:39:53 +08:00
|
|
|
if !Settings.disableShowCurrentProxyInMenu {
|
2020-11-14 15:01:28 +08:00
|
|
|
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding)
|
2019-10-16 22:46:03 +08:00
|
|
|
}
|
2019-03-17 22:00:47 +08:00
|
|
|
let submenu = NSMenu(title: proxyGroup.name)
|
2018-08-05 19:45:37 +08:00
|
|
|
|
2019-03-23 19:13:28 +08:00
|
|
|
for proxyName in proxyGroup.all ?? [] {
|
2019-10-20 13:40:50 +08:00
|
|
|
guard let proxy = proxyMap[proxyName] else { continue }
|
2020-04-26 18:32:20 +08:00
|
|
|
let proxyMenuItem = ProxyMenuItem(proxy: proxy, group: proxyGroup, action: #selector(empty), simpleItem: true)
|
2019-05-10 21:42:43 +08:00
|
|
|
proxyMenuItem.target = MenuItemFactory.self
|
2019-03-23 19:13:28 +08:00
|
|
|
if proxy.name == selectedName {
|
|
|
|
proxyMenuItem.state = .on
|
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-04-25 12:06:02 +08:00
|
|
|
proxyMenuItem.submenu = ProxyDelayHistoryMenu(proxy: proxy)
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2019-05-10 21:42:43 +08:00
|
|
|
submenu.addItem(proxyMenuItem)
|
2019-03-23 19:13:28 +08:00
|
|
|
}
|
2019-12-11 22:04:53 +08:00
|
|
|
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
|
2018-08-05 19:45:37 +08:00
|
|
|
menu.submenu = submenu
|
|
|
|
return menu
|
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-02-22 18:31:13 +08:00
|
|
|
private static func addSpeedTestMenuItem(_ menu: NSMenu, proxyGroup: ClashProxy) {
|
2019-10-20 13:40:50 +08:00
|
|
|
guard proxyGroup.speedtestAble.count > 0 else { return }
|
2019-10-17 17:07:47 +08:00
|
|
|
let speedTestItem = ProxyGroupSpeedTestMenuItem(group: proxyGroup)
|
2020-02-22 12:35:25 +08:00
|
|
|
let separator = NSMenuItem.separator()
|
2020-04-29 19:44:01 +08:00
|
|
|
menu.insertItem(separator, at: 0)
|
|
|
|
menu.insertItem(speedTestItem, at: 0)
|
2020-02-22 18:31:13 +08:00
|
|
|
(menu as? ProxyGroupMenu)?.add(delegate: speedTestItem)
|
2019-10-16 22:46:03 +08:00
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2021-04-18 11:58:17 +08:00
|
|
|
private static func generateLoadBalanceMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp, leftPadding: Bool) -> NSMenuItem? {
|
2019-03-23 19:13:28 +08:00
|
|
|
let proxyMap = proxyInfo.proxiesMap
|
|
|
|
|
2019-03-17 22:00:47 +08:00
|
|
|
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
|
2023-07-12 14:39:53 +08:00
|
|
|
if !Settings.disableShowCurrentProxyInMenu {
|
2021-04-18 11:58:17 +08:00
|
|
|
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: NSLocalizedString("Load Balance", comment: ""), hasLeftPadding: leftPadding, observeUpdate: false)
|
|
|
|
}
|
2020-02-22 18:31:13 +08:00
|
|
|
let submenu = ProxyGroupMenu(title: proxyGroup.name)
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2019-03-17 22:00:47 +08:00
|
|
|
for proxy in proxyGroup.all ?? [] {
|
2019-10-20 13:40:50 +08:00
|
|
|
guard let proxyModel = proxyMap[proxy] else { continue }
|
2019-03-17 23:32:37 +08:00
|
|
|
let proxyItem = ProxyMenuItem(proxy: proxyModel,
|
2020-04-26 16:40:29 +08:00
|
|
|
group: proxyGroup,
|
2020-04-26 18:32:20 +08:00
|
|
|
action: #selector(empty))
|
2019-03-23 19:13:28 +08:00
|
|
|
proxyItem.target = MenuItemFactory.self
|
2020-02-22 18:31:13 +08:00
|
|
|
submenu.add(delegate: proxyItem)
|
2019-02-15 22:47:17 +08:00
|
|
|
submenu.addItem(proxyItem)
|
|
|
|
}
|
2020-04-26 18:32:20 +08:00
|
|
|
if proxyGroup.isSpeedTestable && useViewToRenderProxy {
|
2020-02-24 22:26:35 +08:00
|
|
|
submenu.minimumWidth = proxyGroup.maxProxyNameLength + ProxyItemView.fixedPlaceHolderWidth
|
|
|
|
}
|
2019-12-11 22:04:53 +08:00
|
|
|
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
|
2019-02-15 22:47:17 +08:00
|
|
|
menu.submenu = submenu
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2019-02-15 22:47:17 +08:00
|
|
|
return menu
|
|
|
|
}
|
2020-03-24 23:34:57 +08:00
|
|
|
|
|
|
|
private static func generateListOnlyMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp) -> NSMenuItem? {
|
|
|
|
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
|
|
|
|
let submenu = ProxyGroupMenu(title: proxyGroup.name)
|
|
|
|
let proxyMap = proxyInfo.proxiesMap
|
|
|
|
|
|
|
|
for proxy in proxyGroup.all ?? [] {
|
|
|
|
guard let proxyModel = proxyMap[proxy] else { continue }
|
|
|
|
let proxyItem = ProxyMenuItem(proxy: proxyModel,
|
2020-04-26 16:40:29 +08:00
|
|
|
group: proxyGroup,
|
2020-03-24 23:34:57 +08:00
|
|
|
action: #selector(empty),
|
2020-04-26 18:32:20 +08:00
|
|
|
simpleItem: true)
|
2020-03-24 23:34:57 +08:00
|
|
|
proxyItem.target = MenuItemFactory.self
|
|
|
|
submenu.add(delegate: proxyItem)
|
|
|
|
submenu.addItem(proxyItem)
|
|
|
|
}
|
|
|
|
menu.submenu = submenu
|
|
|
|
return menu
|
|
|
|
}
|
2020-02-22 12:35:25 +08:00
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
2020-02-22 12:35:25 +08:00
|
|
|
// MARK: - Action
|
|
|
|
|
2018-11-30 22:14:20 +08:00
|
|
|
extension MenuItemFactory {
|
2019-10-20 13:40:50 +08:00
|
|
|
@objc static func actionSelectProxy(sender: ProxyMenuItem) {
|
|
|
|
guard let proxyGroup = sender.menu?.title else { return }
|
2018-12-09 00:46:36 +08:00
|
|
|
let proxyName = sender.proxyName
|
2019-10-20 13:40:50 +08:00
|
|
|
|
|
|
|
ApiRequest.updateProxyGroup(group: proxyGroup, selectProxy: proxyName) { success in
|
|
|
|
if success {
|
2018-08-04 21:49:32 +08:00
|
|
|
for items in sender.menu?.items ?? [NSMenuItem]() {
|
|
|
|
items.state = .off
|
|
|
|
}
|
|
|
|
sender.state = .on
|
2018-08-06 23:06:50 +08:00
|
|
|
// remember select proxy
|
2019-11-01 20:47:55 +08:00
|
|
|
let newModel = SavedProxyModel(group: proxyGroup, selected: proxyName, config: ConfigManager.selectConfigName)
|
2019-11-02 00:04:42 +08:00
|
|
|
ConfigManager.selectedProxyRecords.removeAll { model -> Bool in
|
2019-12-11 20:27:17 +08:00
|
|
|
return model.key == newModel.key
|
2019-11-01 20:47:55 +08:00
|
|
|
}
|
2019-11-02 00:04:42 +08:00
|
|
|
ConfigManager.selectedProxyRecords.append(newModel)
|
2019-10-28 14:47:47 +08:00
|
|
|
// terminal Connections for this group
|
|
|
|
ConnectionManager.closeConnection(for: proxyGroup)
|
2020-02-22 11:46:49 +08:00
|
|
|
// refresh menu items
|
2020-04-25 12:06:02 +08:00
|
|
|
MenuItemFactory.refreshExistingMenuItems()
|
2018-08-04 21:49:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-20 13:40:50 +08:00
|
|
|
|
|
|
|
@objc static func actionSelectConfig(sender: NSMenuItem) {
|
2018-11-30 22:14:20 +08:00
|
|
|
let config = sender.title
|
2019-10-28 14:47:47 +08:00
|
|
|
AppDelegate.shared.updateConfig(configName: config, showNotification: false) {
|
|
|
|
err in
|
|
|
|
if err == nil {
|
2019-11-21 22:43:46 +08:00
|
|
|
ConnectionManager.closeAllConnection()
|
2019-10-28 14:47:47 +08:00
|
|
|
}
|
|
|
|
}
|
2018-11-30 22:14:20 +08:00
|
|
|
}
|
2018-10-02 12:05:55 +08:00
|
|
|
|
2019-10-20 13:40:50 +08:00
|
|
|
@objc static func empty() {}
|
|
|
|
}
|