misc: swift lint
# Conflicts: # ClashX.xcodeproj/project.pbxproj # ClashX/General/ApiRequest.swift # ClashX/General/Managers/ProManager.swift # ClashX/General/Managers/Settings.swift # ClashX/General/Managers/TunManager.swift # ClashX/General/Utils/NetworkChangeNotifier.swift # ClashX/ViewControllers/ClashWebViewContoller.swift
This commit is contained in:
parent
6df282db23
commit
c6c74dd888
19
.swiftlint.yml
Normal file
19
.swiftlint.yml
Normal file
@ -0,0 +1,19 @@
|
||||
# By default, SwiftLint uses a set of sensible default rules you can adjust:
|
||||
disabled_rules: # rule identifiers turned on by default to exclude from running
|
||||
- colon
|
||||
- identifier_name
|
||||
- force_cast
|
||||
- closure_parameter_position
|
||||
- file_length
|
||||
- large_tuple
|
||||
- type_body_length
|
||||
included: # paths to include during linting. `--path` is ignored if present.
|
||||
- ClashX
|
||||
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
||||
- ClashX/Vendor
|
||||
- Pods
|
||||
analyzer_rules: # Rules run by `swiftlint analyze`
|
||||
- explicit_self
|
||||
# implicitly
|
||||
line_length: 300
|
||||
# reporter: "xcode"
|
@ -53,7 +53,7 @@
|
||||
49B445162457CDF000B27E3E /* ClashStatusTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B445152457CDF000B27E3E /* ClashStatusTool.swift */; };
|
||||
49B4575D244F4A2A00463C39 /* PrivilegedHelperManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */; };
|
||||
49B4575F244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */; };
|
||||
49BB31E7246853EA008A4CB0 /* iCloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BB31E6246853EA008A4CB0 /* iCloudManager.swift */; };
|
||||
49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BB31E6246853EA008A4CB0 /* ICloudManager.swift */; };
|
||||
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; };
|
||||
49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; };
|
||||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; };
|
||||
@ -176,7 +176,7 @@
|
||||
49B445152457CDF000B27E3E /* ClashStatusTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashStatusTool.swift; sourceTree = "<group>"; };
|
||||
49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivilegedHelperManager.swift; sourceTree = "<group>"; };
|
||||
49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrivilegedHelperManager+Legacy.swift"; sourceTree = "<group>"; };
|
||||
49BB31E6246853EA008A4CB0 /* iCloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudManager.swift; sourceTree = "<group>"; };
|
||||
49BB31E6246853EA008A4CB0 /* ICloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICloudManager.swift; sourceTree = "<group>"; };
|
||||
49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||
49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = "<group>"; };
|
||||
49CF3B1D20CD7463001EBF94 /* ClashX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClashX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -299,7 +299,7 @@
|
||||
F9E754CF239CC21F00CEE7CC /* WebPortalManager.swift */,
|
||||
49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */,
|
||||
49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */,
|
||||
49BB31E6246853EA008A4CB0 /* iCloudManager.swift */,
|
||||
49BB31E6246853EA008A4CB0 /* ICloudManager.swift */,
|
||||
499ADAFC2498CC5900C488FE /* RemoteControlManager.swift */,
|
||||
4929F67E258CE04700A435F6 /* Settings.swift */,
|
||||
);
|
||||
@ -751,7 +751,7 @@
|
||||
F976275C23634DF8000EDEFE /* LoginServiceKit.swift in Sources */,
|
||||
4966E9E6211824F300A391FB /* NSImage+extension.swift in Sources */,
|
||||
F92D0B2E236D35C000575E15 /* ProxyItemView.swift in Sources */,
|
||||
49BB31E7246853EA008A4CB0 /* iCloudManager.swift in Sources */,
|
||||
49BB31E7246853EA008A4CB0 /* ICloudManager.swift in Sources */,
|
||||
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */,
|
||||
F92D0B2C236C7C3600575E15 /* MenuItemBaseView.swift in Sources */,
|
||||
499ADAFD2498CC5900C488FE /* RemoteControlManager.swift in Sources */,
|
||||
|
@ -1,10 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "export PATH="$PATH:/opt/homebrew/bin" if which swiftlint > /dev/null; then cd $PROJECT_DIR && swiftlint --fix && swiftlint else echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" fi ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "49CF3B1C20CD7463001EBF94"
|
||||
BuildableName = "ClashX Pro.app"
|
||||
BlueprintName = "ClashX Pro"
|
||||
ReferencedContainer = "container:ClashX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
|
@ -15,7 +15,6 @@ import RxSwift
|
||||
import AppCenter
|
||||
import AppCenterAnalytics
|
||||
|
||||
|
||||
private let statusItemLengthWithSpeed: CGFloat = 72
|
||||
|
||||
@NSApplicationMain
|
||||
@ -90,7 +89,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
setupStatusMenuItemData()
|
||||
AppVersionUtil.showUpgradeAlert()
|
||||
iCloudManager.shared.setup()
|
||||
ICloudManager.shared.setup()
|
||||
setupExperimentalMenuItem()
|
||||
|
||||
// install proxy helper
|
||||
@ -121,7 +120,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
RemoteConfigManager.shared.autoUpdateCheck()
|
||||
|
||||
|
||||
setupNetworkNotifier()
|
||||
}
|
||||
|
||||
@ -181,7 +179,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
Logger.log("ClashX will terminate")
|
||||
if NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
|
||||
NetworkChangeNotifier.hasInterfaceProxySetToClash() {
|
||||
Logger.log("Need Reset Proxy Setting again",level: .error)
|
||||
Logger.log("Need Reset Proxy Setting again", level: .error)
|
||||
SystemProxyManager.shared.disableProxy()
|
||||
}
|
||||
}
|
||||
@ -271,13 +269,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
|
||||
}.disposed(by: disposeBag)
|
||||
|
||||
|
||||
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
|
||||
ConfigManager.shared.proxyPortAutoSet {
|
||||
PrivilegedHelperManager.shared.isHelperCheckFinished
|
||||
.filter({$0})
|
||||
.take(1)
|
||||
.take(while:{_ in ConfigManager.shared.proxyPortAutoSet})
|
||||
.take(while: {_ in ConfigManager.shared.proxyPortAutoSet})
|
||||
.observe(on: MainScheduler.instance)
|
||||
.bind(onNext: { _ in
|
||||
SystemProxyManager.shared.enableProxy()
|
||||
@ -285,7 +283,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
} else if ConfigManager.shared.proxyPortAutoSet {
|
||||
SystemProxyManager.shared.enableProxy()
|
||||
}
|
||||
|
||||
|
||||
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value {
|
||||
proxySettingMenuItem.target = nil
|
||||
PrivilegedHelperManager.shared.isHelperCheckFinished
|
||||
@ -424,7 +422,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
} else {
|
||||
ConfigManager.shared.isRunning = false
|
||||
proxyModeMenuItem.isEnabled = false
|
||||
Logger.log(string,level: .error)
|
||||
Logger.log(string, level: .error)
|
||||
NSUserNotificationCenter.default.postConfigErrorNotice(msg: string)
|
||||
}
|
||||
Logger.log("Start proxy done")
|
||||
@ -492,7 +490,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
if WebPortalManager.hasWebProtal {
|
||||
WebPortalManager.shared.addWebProtalMenuItem(&statusMenu)
|
||||
}
|
||||
iCloudManager.shared.addEnableMenuItem(&experimentalMenu)
|
||||
ICloudManager.shared.addEnableMenuItem(&experimentalMenu)
|
||||
AutoUpgardeManager.shared.setup()
|
||||
AutoUpgardeManager.shared.addChanelMenuItem(&experimentalMenu)
|
||||
updateExperimentalFeatureStatus()
|
||||
@ -524,23 +522,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
ApiRequest.getMergedProxyData {
|
||||
proxyResp in
|
||||
guard let proxyResp = proxyResp else {return}
|
||||
|
||||
|
||||
var providers = Set<ClashProxyName>()
|
||||
|
||||
|
||||
let groups = proxyResp.proxyGroups.filter({$0.type.isAutoGroup})
|
||||
for group in groups {
|
||||
group.all?.compactMap{
|
||||
group.all?.compactMap {
|
||||
proxyResp.proxiesMap[$0]?.enclosingProvider?.name
|
||||
}.forEach{
|
||||
}.forEach {
|
||||
providers.insert($0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for group in groups {
|
||||
Logger.log("Start auto health check for group \(group.name)")
|
||||
ApiRequest.healthCheck(proxy: group.name)
|
||||
}
|
||||
|
||||
|
||||
for provider in providers {
|
||||
Logger.log("Start auto health check for provider \(provider)")
|
||||
ApiRequest.healthCheck(proxy: provider)
|
||||
@ -590,7 +588,7 @@ extension AppDelegate {
|
||||
}
|
||||
let config = ConfigManager.shared.currentConfig?.copy()
|
||||
config?.mode = mode
|
||||
ApiRequest.updateOutBoundMode(mode: mode) { success in
|
||||
ApiRequest.updateOutBoundMode(mode: mode) { _ in
|
||||
ConfigManager.shared.currentConfig = config
|
||||
ConfigManager.selectOutBoundMode = mode
|
||||
MenuItemFactory.recreateProxyMenuItems()
|
||||
@ -697,8 +695,8 @@ extension AppDelegate {
|
||||
|
||||
extension AppDelegate {
|
||||
@IBAction func openConfigFolder(_ sender: Any) {
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
iCloudManager.shared.getUrl() {
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ICloudManager.shared.getUrl {
|
||||
url in
|
||||
if let url = url {
|
||||
NSWorkspace.shared.open(url)
|
||||
@ -728,7 +726,7 @@ extension AppDelegate {
|
||||
@IBAction func actionUpdateRemoteConfig(_ sender: Any) {
|
||||
RemoteConfigManager.shared.updateCheck(ignoreTimeLimit: true, showNotification: true)
|
||||
}
|
||||
|
||||
|
||||
@IBAction func actionSetUpdateInterval(_ sender: Any) {
|
||||
RemoteConfigManager.showAdd()
|
||||
}
|
||||
@ -747,6 +745,7 @@ extension AppDelegate {
|
||||
@IBAction func actionUpdateProxyGroupMenu(_ sender: Any) {
|
||||
ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu
|
||||
updateExperimentalFeatureStatus()
|
||||
print("211")
|
||||
MenuItemFactory.recreateProxyMenuItems()
|
||||
}
|
||||
|
||||
@ -780,10 +779,10 @@ extension AppDelegate {
|
||||
#else
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
|
||||
AppCenter.start(withAppSecret: "dce6e9a3-b6e3-4fd2-9f2d-35c767a99663", services: [
|
||||
Analytics.self,
|
||||
Analytics.self
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -805,7 +804,7 @@ extension AppDelegate {
|
||||
UserDefaults.standard.removePersistentDomain(forName: domain)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed",notiOnly: false)
|
||||
NSUserNotificationCenter.default.post(title: "Fail on launch protect", info: "You origin Config has been renamed", notiOnly: false)
|
||||
}
|
||||
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + Double(Int64(5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {
|
||||
x.set(0, forKey: "launch_fail_times")
|
||||
@ -842,8 +841,8 @@ extension AppDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
iCloudManager.shared.getConfigFilesList { list in
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ICloudManager.shared.getConfigFilesList { list in
|
||||
action(list)
|
||||
}
|
||||
} else {
|
||||
|
@ -16,7 +16,7 @@ class Logger {
|
||||
#if DEBUG
|
||||
DDLog.add(DDOSLogger.sharedInstance)
|
||||
#endif
|
||||
//default time zone is "UTC"
|
||||
// default time zone is "UTC"
|
||||
let dataFormatter = DateFormatter()
|
||||
dataFormatter.setLocalizedDateFormatFromTemplate("YYYY/MM/dd HH:mm:ss:SSS")
|
||||
fileLogger.logFormatter = DDLogFileFormatterDefault.init(dateFormatter: dataFormatter)
|
||||
|
@ -16,7 +16,7 @@ extension NSImage {
|
||||
|
||||
color.set()
|
||||
|
||||
let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
|
||||
let imageRect = NSRect(origin: NSPoint.zero, size: image.size)
|
||||
imageRect.fill(using: .sourceIn)
|
||||
|
||||
image.unlockFocus()
|
||||
|
@ -27,7 +27,7 @@ extension NSUserNotificationCenter {
|
||||
self?.postNotification(title: title, info: info, identifier: identifier)
|
||||
}
|
||||
case .notDetermined:
|
||||
notificationCenter.requestAuthorization(options: .alert) { granted, err in
|
||||
notificationCenter.requestAuthorization(options: .alert) { granted, _ in
|
||||
if granted {
|
||||
DispatchQueue.main.async {
|
||||
self?.postNotification(title: title, info: info, identifier: identifier)
|
||||
@ -49,9 +49,9 @@ extension NSUserNotificationCenter {
|
||||
postNotification(title: title, info: info, identifier: identifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func postNotification(title: String, info: String, identifier: String? = nil) {
|
||||
var userInfo:[String : Any] = [:]
|
||||
var userInfo: [String: Any] = [:]
|
||||
if let identifier = identifier {
|
||||
userInfo = ["identifier": identifier]
|
||||
}
|
||||
@ -60,7 +60,7 @@ extension NSUserNotificationCenter {
|
||||
notificationCenter.delegate = UserNotificationCenterDelegate.shared
|
||||
notificationCenter.removeAllDeliveredNotifications()
|
||||
notificationCenter.removeAllPendingNotificationRequests()
|
||||
let content = UNMutableNotificationContent();
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = title
|
||||
content.body = info
|
||||
content.userInfo = userInfo
|
||||
@ -83,7 +83,7 @@ extension NSUserNotificationCenter {
|
||||
deliver(notification)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func postNotificationAlert(title: String, info: String, identifier: String? = nil) {
|
||||
if Settings.disableNoti {
|
||||
return
|
||||
@ -97,41 +97,41 @@ extension NSUserNotificationCenter {
|
||||
UserNotificationCenterDelegate.shared.handleNotificationActive(with: identifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func postConfigFileChangeDetectionNotice() {
|
||||
post(title: NSLocalizedString("Config file have been changed", comment: ""),
|
||||
info: NSLocalizedString("Tap to reload config", comment: ""),
|
||||
identifier: "postConfigFileChangeDetectionNotice")
|
||||
}
|
||||
|
||||
|
||||
func postStreamApiConnectFail(api: String) {
|
||||
post(title: "\(api) api connect error!",
|
||||
info: NSLocalizedString("Use reload config to try reconnect.", comment: ""))
|
||||
}
|
||||
|
||||
|
||||
func postConfigErrorNotice(msg: String) {
|
||||
let configName = ConfigManager.selectConfigName.count > 0 ?
|
||||
Paths.configFileName(for: ConfigManager.selectConfigName) : ""
|
||||
|
||||
|
||||
let message = "\(configName): \(msg)"
|
||||
postNotificationAlert(title: NSLocalizedString("Config loading Fail!", comment: ""), info: message)
|
||||
}
|
||||
|
||||
|
||||
func postSpeedTestBeginNotice() {
|
||||
post(title: NSLocalizedString("Benchmark", comment: ""),
|
||||
info: NSLocalizedString("Benchmark has begun, please wait.", comment: ""))
|
||||
}
|
||||
|
||||
|
||||
func postSpeedTestingNotice() {
|
||||
post(title: NSLocalizedString("Benchmark", comment: ""),
|
||||
info: NSLocalizedString("Benchmark is processing, please wait.", comment: ""))
|
||||
}
|
||||
|
||||
|
||||
func postSpeedTestFinishNotice() {
|
||||
post(title: NSLocalizedString("Benchmark", comment: ""),
|
||||
info: NSLocalizedString("Benchmark Finished!", comment: ""),notiOnly: false)
|
||||
info: NSLocalizedString("Benchmark Finished!", comment: ""), notiOnly: false)
|
||||
}
|
||||
|
||||
|
||||
func postProxyChangeByOtherAppNotice() {
|
||||
post(title: NSLocalizedString("System Proxy Changed", comment: ""),
|
||||
info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true)
|
||||
@ -140,33 +140,33 @@ extension NSUserNotificationCenter {
|
||||
|
||||
class UserNotificationCenterDelegate: NSObject, NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate {
|
||||
static let shared = UserNotificationCenterDelegate()
|
||||
|
||||
|
||||
func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
|
||||
if let identifier = notification.userInfo?["identifier"] as? String {
|
||||
handleNotificationActive(with: identifier)
|
||||
}
|
||||
center.removeAllDeliveredNotifications()
|
||||
}
|
||||
|
||||
|
||||
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@available(macOS 10.14, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
|
||||
|
||||
if let identifier = response.notification.request.content.userInfo["identifier"] as? String {
|
||||
handleNotificationActive(with: identifier)
|
||||
}
|
||||
center.removeAllDeliveredNotifications()
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
|
||||
@available(macOS 10.14, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
completionHandler(.alert)
|
||||
}
|
||||
|
||||
|
||||
func handleNotificationActive(with identifier: String) {
|
||||
switch identifier {
|
||||
case "postConfigFileChangeDetectionNotice":
|
||||
@ -176,4 +176,3 @@ class UserNotificationCenterDelegate: NSObject, NSUserNotificationCenterDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,8 @@ class AutoUpgardeManager: NSObject {
|
||||
}
|
||||
return items
|
||||
}()
|
||||
|
||||
private var allowSelectChannel:Bool {
|
||||
|
||||
private var allowSelectChannel: Bool {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "SUDisallowSelectChannel") as? Bool != true
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import Alamofire
|
||||
import AppKit
|
||||
import Foundation
|
||||
@ -69,7 +68,7 @@ extension ClashResourceManager {
|
||||
|
||||
@objc private static func updateGeoIP() {
|
||||
guard let url = showCustomAlert() else { return }
|
||||
AF.download(url, to: { (_, _) in
|
||||
AF.download(url, to: { (_, _) in
|
||||
let path = kConfigFolderPath.appending("/Country.mmdb")
|
||||
return (URL(fileURLWithPath: path), .removePreviousFile)
|
||||
}).response { res in
|
||||
@ -92,7 +91,7 @@ extension ClashResourceManager {
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static func showCustomAlert() -> String? {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("Custom your GEOIP MMDB download address.", comment: "")
|
||||
|
@ -55,8 +55,8 @@ class ConfigManager {
|
||||
}
|
||||
|
||||
static func watchCurrentConfigFile() {
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
iCloudManager.shared.getUrl { url in
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ICloudManager.shared.getUrl { url in
|
||||
guard let url = url else { return }
|
||||
let configUrl = url.appendingPathComponent(Paths.configFileName(for: selectConfigName))
|
||||
ConfigFileManager.shared.watchFile(path: configUrl.path)
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class iCloudManager {
|
||||
static let shared = iCloudManager()
|
||||
class ICloudManager {
|
||||
static let shared = ICloudManager()
|
||||
private let queue = DispatchQueue(label: "com.clashx.icloud")
|
||||
private var metaQuery: NSMetadataQuery?
|
||||
private var enableMenuItem: NSMenuItem?
|
||||
@ -104,7 +104,7 @@ class iCloudManager {
|
||||
}
|
||||
}
|
||||
|
||||
extension iCloudManager {
|
||||
extension ICloudManager {
|
||||
func addEnableMenuItem(_ menu: inout NSMenu) {
|
||||
let item = NSMenuItem(title: NSLocalizedString("Use iCloud", comment: ""), action: #selector(enableMenuItemTap(sender:)), keyEquivalent: "")
|
||||
menu.addItem(item)
|
@ -77,14 +77,14 @@ class MenuItemFactory {
|
||||
item.state = ConfigManager.selectConfigName == config ? .on : .off
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
if RemoteControlManager.selectConfig != nil {
|
||||
complete([])
|
||||
return
|
||||
}
|
||||
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
iCloudManager.shared.getConfigFilesList {
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ICloudManager.shared.getConfigFilesList {
|
||||
complete($0.map { generateMenuItem($0) })
|
||||
}
|
||||
} else {
|
||||
@ -113,8 +113,7 @@ class MenuItemFactory {
|
||||
|
||||
private static func generateSelectorMenuItem(proxyGroup: ClashProxy,
|
||||
proxyInfo: ClashProxyResp,
|
||||
leftPadding: Bool) -> NSMenuItem?
|
||||
{
|
||||
leftPadding: Bool) -> NSMenuItem? {
|
||||
let proxyMap = proxyInfo.proxiesMap
|
||||
|
||||
let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global
|
||||
@ -150,8 +149,7 @@ class MenuItemFactory {
|
||||
|
||||
private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy,
|
||||
proxyInfo: ClashProxyResp,
|
||||
leftPadding: Bool) -> NSMenuItem?
|
||||
{
|
||||
leftPadding: Bool) -> NSMenuItem? {
|
||||
let proxyMap = proxyInfo.proxiesMap
|
||||
let selectedName = proxyGroup.now ?? ""
|
||||
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
|
||||
|
@ -14,7 +14,7 @@ extension PrivilegedHelperManager {
|
||||
let bash = """
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
|
||||
plistPath=/Library/LaunchDaemons/\(PrivilegedHelperManager.machServiceName).plist
|
||||
rm -rf /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)
|
||||
if [ -e ${plistPath} ]; then
|
||||
@ -22,10 +22,10 @@ extension PrivilegedHelperManager {
|
||||
rm ${plistPath}
|
||||
fi
|
||||
launchctl remove \(PrivilegedHelperManager.machServiceName) || true
|
||||
|
||||
|
||||
mkdir -p /Library/PrivilegedHelperTools/
|
||||
rm -f /Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)
|
||||
|
||||
|
||||
cp "\(appPath)/Contents/Library/LaunchServices/\(PrivilegedHelperManager.machServiceName)" "/Library/PrivilegedHelperTools/\(PrivilegedHelperManager.machServiceName)"
|
||||
|
||||
echo '
|
||||
@ -49,7 +49,7 @@ extension PrivilegedHelperManager {
|
||||
</dict>
|
||||
</plist>
|
||||
' > ${plistPath}
|
||||
|
||||
|
||||
launchctl load -w ${plistPath}
|
||||
"""
|
||||
return bash
|
||||
|
@ -30,7 +30,7 @@ class PrivilegedHelperManager {
|
||||
|
||||
func checkInstall() {
|
||||
Logger.log("checkInstall", level: .debug)
|
||||
|
||||
|
||||
getHelperStatus { [weak self] installed in
|
||||
guard let self = self else {return}
|
||||
if !installed {
|
||||
@ -112,8 +112,6 @@ class PrivilegedHelperManager {
|
||||
return .success
|
||||
}
|
||||
|
||||
|
||||
|
||||
func helper(failture: (() -> Void)? = nil) -> ProxyConfigRemoteProcessProtocol? {
|
||||
connection = NSXPCConnection(machServiceName: PrivilegedHelperManager.machServiceName, options: NSXPCConnection.Options.privileged)
|
||||
connection?.remoteObjectInterface = NSXPCInterface(with: ProxyConfigRemoteProcessProtocol.self)
|
||||
@ -127,17 +125,17 @@ class PrivilegedHelperManager {
|
||||
}) as? ProxyConfigRemoteProcessProtocol else { return nil }
|
||||
return helper
|
||||
}
|
||||
|
||||
|
||||
var timer: Timer?
|
||||
private func getHelperStatus(callback:@escaping ((Bool)->Void)) {
|
||||
private func getHelperStatus(callback:@escaping ((Bool) -> Void)) {
|
||||
var called = false
|
||||
let reply:((Bool)->Void) = {
|
||||
let reply: ((Bool) -> Void) = {
|
||||
installed in
|
||||
if called {return}
|
||||
called = true
|
||||
callback(installed)
|
||||
}
|
||||
|
||||
|
||||
let helperURL = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LaunchServices/" + PrivilegedHelperManager.machServiceName)
|
||||
guard
|
||||
let helperBundleInfo = CFBundleCopyInfoDictionaryForURL(helperURL as CFURL) as? [String: Any],
|
||||
@ -153,12 +151,12 @@ class PrivilegedHelperManager {
|
||||
}
|
||||
let timeout: TimeInterval = helperFileExists ? 15 : 5
|
||||
let time = Date()
|
||||
|
||||
|
||||
timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in
|
||||
Logger.log("check helper timeout time: \(timeout)")
|
||||
reply(false)
|
||||
}
|
||||
|
||||
|
||||
helper()?.getVersion { [weak timer] installedHelperVersion in
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
@ -225,7 +223,7 @@ extension PrivilegedHelperManager {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct AppAuthorizationRights {
|
||||
private struct AppAuthorizationRights {
|
||||
static let rightName: NSString = "\(PrivilegedHelperManager.machServiceName).config" as NSString
|
||||
static let rightDefaultRule: Dictionary = adminRightsRule
|
||||
static let rightDescription: CFString = "ProxyConfigHelper wants to configure your proxy setting'" as CFString
|
||||
@ -235,7 +233,7 @@ fileprivate struct AppAuthorizationRights {
|
||||
"version": 1]
|
||||
}
|
||||
|
||||
fileprivate enum DaemonInstallResult {
|
||||
private enum DaemonInstallResult {
|
||||
case success
|
||||
case authorizationFail
|
||||
case getAdminFail
|
||||
|
@ -173,7 +173,7 @@ class RemoteConfigManager {
|
||||
}
|
||||
config.isPlaceHolderName = false
|
||||
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ConfigFileManager.shared.stopWatchConfigFile()
|
||||
} else if config.name == ConfigManager.selectConfigName {
|
||||
ConfigFileManager.shared.pauseForNextChange()
|
||||
@ -192,8 +192,8 @@ class RemoteConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
if iCloudManager.shared.isICloudEnable() {
|
||||
iCloudManager.shared.getUrl { url in
|
||||
if ICloudManager.shared.isICloudEnable() {
|
||||
ICloudManager.shared.getUrl { url in
|
||||
guard let url = url else { return }
|
||||
let saveUrl = url.appendingPathComponent(Paths.configFileName(for: config.name))
|
||||
saveAction(saveUrl.path)
|
||||
@ -214,7 +214,7 @@ class RemoteConfigManager {
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func showAdd() {
|
||||
let alertView = NSAlert()
|
||||
alertView.addButton(withTitle: NSLocalizedString("OK", comment: ""))
|
||||
|
@ -28,7 +28,6 @@ class RemoteControlManager {
|
||||
static var selected: String
|
||||
}
|
||||
|
||||
|
||||
static let shared = RemoteControlManager()
|
||||
static var configs: [RemoteControl] = loadConfig() {
|
||||
didSet {
|
||||
@ -44,7 +43,7 @@ class RemoteControlManager {
|
||||
Recorder.selected = selectConfig?.uuid ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static var menuSeparator: NSMenuItem?
|
||||
|
||||
static func loadConfig() -> [RemoteControl] {
|
||||
@ -67,7 +66,7 @@ class RemoteControlManager {
|
||||
RemoteControlManager.recoverSelection()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static private func recoverSelection() {
|
||||
if Recorder.selected != "" {
|
||||
if let config = configs.first(where: { $0.uuid == Recorder.selected }) {
|
||||
|
@ -10,16 +10,16 @@ import Foundation
|
||||
enum Settings {
|
||||
@UserDefault("mmdbDownloadUrl", defaultValue: "")
|
||||
static var mmdbDownloadUrl:String
|
||||
|
||||
|
||||
@UserDefault("filterInterface", defaultValue: true)
|
||||
static var filterInterface:Bool
|
||||
|
||||
|
||||
@UserDefault("disableNoti", defaultValue: false)
|
||||
static var disableNoti:Bool
|
||||
|
||||
@UserDefault("usePacMode", defaultValue: false)
|
||||
static var usePacMode:Bool
|
||||
|
||||
|
||||
@UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60)
|
||||
static var configAutoUpdateInterval:TimeInterval
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class ClashStatusTool {
|
||||
}
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class JsBridgeUtil {
|
||||
|
||||
bridge.setWebViewDelegate(delegate)
|
||||
|
||||
bridge.registerHandler("isSystemProxySet") { anydata, responseCallback in
|
||||
bridge.registerHandler("isSystemProxySet") { _, responseCallback in
|
||||
responseCallback?(ConfigManager.shared.proxyPortAutoSet)
|
||||
}
|
||||
|
||||
@ -89,13 +89,13 @@ class JsBridgeUtil {
|
||||
let data = [
|
||||
"host": host,
|
||||
"port": port,
|
||||
"secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret,
|
||||
"secret": ConfigManager.shared.overrideSecret ?? ConfigManager.shared.apiSecret
|
||||
]
|
||||
callback?(data)
|
||||
}
|
||||
|
||||
// ping-pong
|
||||
bridge.registerHandler("ping") { [weak bridge] anydata, responseCallback in
|
||||
bridge.registerHandler("ping") { [weak bridge] _, responseCallback in
|
||||
bridge?.callHandler("pong")
|
||||
responseCallback?(true)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class NetworkChangeNotifier {
|
||||
name: NSWorkspace.didWakeNotification, object: nil
|
||||
)
|
||||
|
||||
let changed: SCDynamicStoreCallBack = { dynamicStore, _, _ in
|
||||
let changed: SCDynamicStoreCallBack = { _, _, _ in
|
||||
NotificationCenter.default.post(name: .systemNetworkStatusDidChange, object: nil)
|
||||
}
|
||||
var dynamicContext = SCDynamicStoreContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||
@ -43,7 +43,7 @@ class NetworkChangeNotifier {
|
||||
}
|
||||
|
||||
private static func startIPChangeWatch() {
|
||||
let changed: SCDynamicStoreCallBack = { dynamicStore, _, _ in
|
||||
let changed: SCDynamicStoreCallBack = { _, _, _ in
|
||||
NotificationCenter.default.post(name: .systemNetworkStatusIPUpdate, object: nil)
|
||||
}
|
||||
var dynamicContext = SCDynamicStoreContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||
@ -91,12 +91,12 @@ class NetworkChangeNotifier {
|
||||
return http == currentPort && https == currentPort && socks == currentSocks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func hasInterfaceProxySetToClash() -> Bool {
|
||||
let currentPort = ConfigManager.shared.currentConfig?.usedHttpPort
|
||||
let currentSocks = ConfigManager.shared.currentConfig?.usedSocksPort
|
||||
if let prefRef = SCPreferencesCreate(nil, "ClashX" as CFString, nil),
|
||||
let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices){
|
||||
let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices) {
|
||||
for key in sets.allKeys {
|
||||
let dict = sets[key] as? NSDictionary
|
||||
let proxySettings = dict?["Proxies"] as? [String:Any]
|
||||
|
@ -18,5 +18,5 @@ extension Notification.Name {
|
||||
static func proxyUpdate(for name: ClashProxyName) -> Notification.Name {
|
||||
return Notification.Name("kProxyUpdate_\(name)")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ class ClashProxySpeedHistory: Codable {
|
||||
let time: Date
|
||||
let delay: Int
|
||||
|
||||
class hisDateFormaterInstance {
|
||||
static let shared = hisDateFormaterInstance()
|
||||
class HisDateFormaterInstance {
|
||||
static let shared = HisDateFormaterInstance()
|
||||
lazy var formater: DateFormatter = {
|
||||
var f = DateFormatter()
|
||||
f.dateFormat = "HH:mm"
|
||||
@ -75,7 +75,7 @@ class ClashProxySpeedHistory: Codable {
|
||||
}()
|
||||
|
||||
lazy var dateDisplay: String = {
|
||||
return hisDateFormaterInstance.shared.formater.string(from: time)
|
||||
return HisDateFormaterInstance.shared.formater.string(from: time)
|
||||
}()
|
||||
|
||||
lazy var displayString: String = "\(dateDisplay) \(delayDisplay)"
|
||||
@ -87,8 +87,8 @@ class ClashProxy: Codable {
|
||||
let all: [ClashProxyName]?
|
||||
let history: [ClashProxySpeedHistory]
|
||||
let now: ClashProxyName?
|
||||
weak var enclosingResp: ClashProxyResp? = nil
|
||||
weak var enclosingProvider: ClashProvider? = nil
|
||||
weak var enclosingResp: ClashProxyResp?
|
||||
weak var enclosingProvider: ClashProvider?
|
||||
|
||||
enum SpeedtestAbleItem {
|
||||
case proxy(name: ClashProxyName)
|
||||
|
@ -15,7 +15,7 @@ class ClashRule: Codable {
|
||||
}
|
||||
|
||||
class ClashRuleResponse: Codable {
|
||||
var rules: [ClashRule]? = nil
|
||||
var rules: [ClashRule]?
|
||||
|
||||
static func empty() -> ClashRuleResponse {
|
||||
return ClashRuleResponse()
|
||||
|
5
ClashX/Vendor/UserDefaultWrapper.swift
vendored
5
ClashX/Vendor/UserDefaultWrapper.swift
vendored
@ -15,13 +15,13 @@ public struct UserDefault<Value: PropertyListValue> {
|
||||
let key: String
|
||||
let defaultValue: Value
|
||||
var userDefaults: UserDefaults
|
||||
|
||||
|
||||
public init(_ key: String, defaultValue: Value, userDefaults: UserDefaults = .standard) {
|
||||
self.key = key
|
||||
self.defaultValue = defaultValue
|
||||
self.userDefaults = userDefaults
|
||||
}
|
||||
|
||||
|
||||
public var wrappedValue: Value {
|
||||
get {
|
||||
return userDefaults.object(forKey: key) as? Value ?? defaultValue
|
||||
@ -64,7 +64,6 @@ extension UInt64: PropertyListValue {}
|
||||
extension Double: PropertyListValue {}
|
||||
extension Float: PropertyListValue {}
|
||||
|
||||
|
||||
extension Array: PropertyListValue where Element: PropertyListValue {}
|
||||
|
||||
extension Dictionary: PropertyListValue where Key == String, Value: PropertyListValue {}
|
||||
|
@ -64,7 +64,7 @@ class HyperlinkTextField: NSTextField {
|
||||
super.awakeFromNib()
|
||||
let attributes: [NSAttributedString.Key: Any] = [
|
||||
NSAttributedString.Key.foregroundColor: NSColor.linkColor,
|
||||
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject,
|
||||
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject
|
||||
]
|
||||
attributedStringValue = NSAttributedString(string: stringValue, attributes: attributes)
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class ExternalControlAddView: NSView {
|
||||
nameField.leadingAnchor.constraint(equalTo: urlTextField.leadingAnchor),
|
||||
nameLabel.centerYAnchor.constraint(equalTo: nameField.centerYAnchor),
|
||||
nameLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
|
||||
nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 5),
|
||||
nameField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 5)
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
import AppKit
|
||||
|
||||
@objc protocol ProxyGroupMenuHighlightDelegate: class {
|
||||
@objc protocol ProxyGroupMenuHighlightDelegate: AnyObject {
|
||||
func highlight(item: NSMenuItem?)
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class ProxyGroupMenuItemView: MenuItemBaseView {
|
||||
return [groupNameLabel.cell, selectProxyLabel.cell, arrowLabel.cell]
|
||||
}
|
||||
|
||||
init(group: ClashProxyName, targetProxy: ClashProxyName, hasLeftPadding: Bool, observeUpdate:Bool = true) {
|
||||
init(group: ClashProxyName, targetProxy: ClashProxyName, hasLeftPadding: Bool, observeUpdate: Bool = true) {
|
||||
groupNameLabel = VibrancyTextField(labelWithString: group)
|
||||
selectProxyLabel = VibrancyTextField(labelWithString: targetProxy)
|
||||
super.init(autolayout: true)
|
||||
|
@ -47,12 +47,12 @@ class ProxyGroupSpeedTestMenuItem: NSMenuItem {
|
||||
ApiRequest.getMergedProxyData { [weak self] proxyResp in
|
||||
guard let self = self else { return }
|
||||
var providers = Set<ClashProxyName>()
|
||||
self.proxyGroup.all?.compactMap{
|
||||
self.proxyGroup.all?.compactMap {
|
||||
proxyResp?.proxiesMap[$0]?.enclosingProvider?.name
|
||||
}.forEach{
|
||||
}.forEach {
|
||||
providers.insert($0)
|
||||
}
|
||||
providers.forEach{
|
||||
providers.forEach {
|
||||
ApiRequest.healthCheck(proxy: $0)
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ extension ProxyGroupSpeedTestMenuItem: ProxyGroupMenuHighlightDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView {
|
||||
private class ProxyGroupSpeedTestMenuItemView: MenuItemBaseView {
|
||||
private let label: NSTextField
|
||||
|
||||
init(_ title: String) {
|
||||
|
@ -108,7 +108,7 @@ extension ProxyMenuItem {
|
||||
func getAttributedTitle(name: String, delay: String?) -> NSAttributedString {
|
||||
let paragraph = NSMutableParagraphStyle()
|
||||
paragraph.tabStops = [
|
||||
NSTextTab(textAlignment: .right, location: 65 + maxProxyNameLength, options: [:]),
|
||||
NSTextTab(textAlignment: .right, location: 65 + maxProxyNameLength, options: [:])
|
||||
]
|
||||
let proxyName = name.replacingOccurrences(of: "\t", with: " ")
|
||||
let str: String
|
||||
@ -122,7 +122,7 @@ extension ProxyMenuItem {
|
||||
string: str,
|
||||
attributes: [
|
||||
NSAttributedString.Key.paragraphStyle: paragraph,
|
||||
NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14),
|
||||
NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14)
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -14,11 +14,11 @@ class RemoteConfigUpdateIntervalSettingView: NSView {
|
||||
super.init(frame: .zero)
|
||||
setup()
|
||||
}
|
||||
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
let stackView = NSStackView()
|
||||
let textfield = NSTextField()
|
||||
func setup() {
|
||||
|
@ -71,7 +71,7 @@ class StatusItemView: NSView {
|
||||
imageView.image = menuImage.tint(color: enableProxy ? selectedColor : unselectedColor)
|
||||
updateStatusItemView()
|
||||
}
|
||||
|
||||
|
||||
func getSpeedString(for byte: Int) -> String {
|
||||
let kb = byte / 1024
|
||||
if kb < 1024 {
|
||||
@ -88,7 +88,6 @@ class StatusItemView: NSView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func updateSpeedLabel(up: Int, down: Int) {
|
||||
guard !speedContainerView.isHidden else { return }
|
||||
|
Loading…
Reference in New Issue
Block a user