ClashX.Meta/ClashX/General/Managers/MenuItemFactory.swift

252 lines
9.4 KiB
Swift
Raw Normal View History

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 {
private static var cachedProxyMenuItem: [NSMenuItem]?
private static var showSpeedTestItemAtTop: Bool = UserDefaults.standard.bool(forKey: "kShowSpeedTestItemAtTop") {
didSet {
UserDefaults.standard.set(showSpeedTestItemAtTop, forKey: "kShowSpeedTestItemAtTop")
}
}
// MARK: - Public
static func refreshMenuItems(completionHandler: (([NSMenuItem]) -> Void)? = nil) {
2019-10-06 12:22:21 +08:00
if ConfigManager.shared.currentConfig?.mode == .direct {
completionHandler?([])
return
2019-10-06 12:22:21 +08:00
}
if let cached = cachedProxyMenuItem {
completionHandler?(cached)
}
2019-10-20 13:40:50 +08:00
2020-02-05 21:45:11 +08:00
ApiRequest.requestProxyProviderList {
proxyprovider in
2020-02-05 21:45:11 +08:00
ApiRequest.requestProxyGroupList {
proxyInfo in
proxyInfo.updateProvider(proxyprovider)
var menuItems = [NSMenuItem]()
for proxy in proxyInfo.proxyGroups {
var menu: NSMenuItem?
switch proxy.type {
case .select: menu = self.generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .urltest, .fallback: menu = generateUrlTestFallBackMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .loadBalance:
menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
default: continue
}
if let menu = menu {
menuItems.append(menu)
menu.isEnabled = true
}
}
let items = Array(menuItems.reversed())
cachedProxyMenuItem = items
completionHandler?(items)
2018-08-04 21:49:32 +08:00
}
}
}
2019-10-20 13:40:50 +08:00
static func generateSwitchConfigMenuItems() -> [NSMenuItem] {
var items = [NSMenuItem]()
for config in ConfigManager.getConfigFilesList() {
let item = NSMenuItem(title: config, action: #selector(MenuItemFactory.actionSelectConfig(sender:)), keyEquivalent: "")
item.target = MenuItemFactory.self
item.state = ConfigManager.selectConfigName == config ? .on : .off
items.append(item)
}
return items
}
// MARK: - Private
private static func generateSelectorMenuItem(proxyGroup: ClashProxy,
proxyInfo: ClashProxyResp) -> NSMenuItem? {
let proxyMap = proxyInfo.proxiesMap
2019-10-20 13:40:50 +08:00
let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global
if isGlobalMode {
2019-10-20 13:40:50 +08:00
if proxyGroup.name != "GLOBAL" { return nil }
} else {
2019-10-20 13:40:50 +08:00
if proxyGroup.name == "GLOBAL" { return nil }
}
2019-10-20 13:40:50 +08:00
2019-03-17 22:00:47 +08:00
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
let selectedName = proxyGroup.now ?? ""
2019-10-16 22:46:03 +08:00
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
}
2019-03-17 22:00:47 +08:00
let submenu = NSMenu(title: proxyGroup.name)
var hasSelected = false
2019-10-20 13:40:50 +08:00
for proxy in proxyGroup.all ?? [] {
guard let proxyModel = proxyMap[proxy] else { continue }
if isGlobalMode && proxyModel.type == .select {
2019-03-17 22:00:47 +08:00
continue
}
let proxyItem = ProxyMenuItem(proxy: proxyModel, action: #selector(MenuItemFactory.actionSelectProxy(sender:)),
2019-10-20 13:40:50 +08:00
maxProxyNameLength: proxyGroup.maxProxyNameLength)
2018-11-30 22:14:20 +08:00
proxyItem.target = MenuItemFactory.self
2019-03-17 22:00:47 +08:00
proxyItem.isSelected = proxy == selectedName
2019-10-20 13:40:50 +08:00
if proxyItem.isSelected { hasSelected = true }
submenu.addItem(proxyItem)
}
2019-10-20 13:40:50 +08:00
if !hasSelected && submenu.items.count > 0 {
actionSelectProxy(sender: submenu.items[0] as! ProxyMenuItem)
}
2019-10-16 22:46:03 +08:00
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
menu.submenu = submenu
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
}
return menu
}
2019-10-20 13:40:50 +08:00
private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp) -> NSMenuItem? {
let proxyMap = proxyInfo.proxiesMap
2019-03-17 22:00:47 +08:00
let selectedName = proxyGroup.now ?? ""
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
2019-10-16 22:46:03 +08:00
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
}
2019-03-17 22:00:47 +08:00
let submenu = NSMenu(title: proxyGroup.name)
for proxyName in proxyGroup.all ?? [] {
2019-10-20 13:40:50 +08:00
guard let proxy = proxyMap[proxyName] else { continue }
let proxyMenuItem = NSMenuItem(title: proxy.name, action: #selector(empty), keyEquivalent: "")
proxyMenuItem.target = MenuItemFactory.self
if proxy.name == selectedName {
proxyMenuItem.state = .on
}
2019-10-20 13:40:50 +08:00
if let historyMenu = generateHistoryMenu(proxy) {
proxyMenuItem.submenu = historyMenu
}
2019-10-20 13:40:50 +08:00
submenu.addItem(proxyMenuItem)
}
2019-12-11 22:04:53 +08:00
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
menu.submenu = submenu
return menu
}
2019-10-20 13:40:50 +08:00
private static func addSpeedTestMenuItem(_ menus: NSMenu, proxyGroup: ClashProxy) {
2019-10-20 13:40:50 +08:00
guard proxyGroup.speedtestAble.count > 0 else { return }
let speedTestItem = ProxyGroupSpeedTestMenuItem(group: proxyGroup)
let separator = NSMenuItem.separator()
if showSpeedTestItemAtTop {
menus.insertItem(separator, at: 0)
menus.insertItem(speedTestItem, at: 0)
} else {
menus.addItem(separator)
menus.addItem(speedTestItem)
}
2019-10-16 22:46:03 +08:00
}
2019-10-20 13:40:50 +08:00
private static func generateHistoryMenu(_ proxy: ClashProxy) -> NSMenu? {
let historyMenu = NSMenu(title: "")
for his in proxy.history {
historyMenu.addItem(
NSMenuItem(title: "\(his.dateDisplay) \(his.delayDisplay)", action: nil, keyEquivalent: ""))
}
return historyMenu.items.count > 0 ? historyMenu : nil
}
2019-10-20 13:40:50 +08:00
private static func generateLoadBalanceMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp) -> NSMenuItem? {
let proxyMap = proxyInfo.proxiesMap
2019-03-17 22:00:47 +08:00
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
let submenu = NSMenu(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 }
let proxyItem = ProxyMenuItem(proxy: proxyModel,
2019-10-20 13:40:50 +08:00
action: #selector(empty),
maxProxyNameLength: proxyGroup.maxProxyNameLength)
proxyItem.isSelected = false
proxyItem.target = MenuItemFactory.self
submenu.addItem(proxyItem)
}
2019-12-11 22:04:53 +08:00
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
menu.submenu = submenu
2019-10-20 13:40:50 +08:00
return menu
}
}
2019-10-20 13:40:50 +08:00
// MARK: - Experimental
extension MenuItemFactory {
static func addExperimentalMenuItem(_ menu: inout NSMenu) {
let item = NSMenuItem(title: NSLocalizedString("Show speedTest at top", comment: ""), action: #selector(optionMenuItemTap(sender:)), keyEquivalent: "")
item.target = self
menu.addItem(item)
updateMenuItemStatus(item)
}
static func updateMenuItemStatus(_ item: NSMenuItem) {
item.state = showSpeedTestItemAtTop ? .on : .off
}
@objc static func optionMenuItemTap(sender: NSMenuItem) {
showSpeedTestItemAtTop = !showSpeedTestItemAtTop
updateMenuItemStatus(sender)
refreshMenuItems()
2018-11-30 22:14:20 +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 }
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
// remember select proxy
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-02 00:04:42 +08:00
ConfigManager.selectedProxyRecords.append(newModel)
// terminal Connections for this group
ConnectionManager.closeConnection(for: proxyGroup)
// refresh menu items
MenuItemFactory.refreshMenuItems()
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
AppDelegate.shared.updateConfig(configName: config, showNotification: false) {
err in
if err == nil {
ConnectionManager.closeAllConnection()
}
}
2018-11-30 22:14:20 +08:00
}
2019-10-20 13:40:50 +08:00
@objc static func empty() {}
}