diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 09635221a..026481e50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: uses: robinraju/release-downloader@v1.10 with: repository: 'MetaCubeX/mihomo' - tag: "v1.18.8" + tag: "v1.18.9" fileName: ".*darwin.*64-v.*.gz" # releaseId: "62870807" diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index b2ffdbf1a..388e55f50 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 0106179F2AF38EFA005C7877 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49FEC6682AD9369C00BAD9F5 /* Command.swift */; }; 0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; }; + 0197255A2CA15D6400C14E49 /* UserNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019725592CA15D6400C14E49 /* UserNotificationCenter.swift */; }; + 0197255C2CA15FC600C14E49 /* NSWorkspace+openFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0197255B2CA15FC600C14E49 /* NSWorkspace+openFile.swift */; }; 019A239628657A7A00AE5698 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019A239528657A7A00AE5698 /* main.swift */; }; 01BC9ABE2928EB5A00F9B177 /* MetaDNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */; }; 01BCDAAC2C9ECB3A0028FA94 /* DSFSparkline in Frameworks */ = {isa = PBXBuildFile; productRef = 01BCDAAB2C9ECB3A0028FA94 /* DSFSparkline */; }; @@ -122,7 +124,6 @@ 01F336082AD10D0B0048AF77 /* ProxyGroupMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176AA23575BB20093DD7B /* ProxyGroupMenuItemView.swift */; }; 01F336092AD10D0B0048AF77 /* PrivilegedHelperManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575C244F4A2A00463C39 /* PrivilegedHelperManager.swift */; }; 01F3360A2AD10D0B0048AF77 /* PrivilegedHelperManager+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B4575E244FD4D100463C39 /* PrivilegedHelperManager+Legacy.swift */; }; - 01F3360B2AD10D0B0048AF77 /* NSUserNotificationCenter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */; }; 01F3360C2AD10D0B0048AF77 /* NSAlert+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E754D1239CC28D00CEE7CC /* NSAlert+Extension.swift */; }; 01F3360D2AD10D0B0048AF77 /* ClashWebViewContoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499976C721359F0400E7BF83 /* ClashWebViewContoller.swift */; }; 01F3360E2AD10D0B0048AF77 /* ProxyGroupSpeedTestMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D176A8235614340093DD7B /* ProxyGroupSpeedTestMenuItem.swift */; }; @@ -219,6 +220,8 @@ 016BEAAF29D80102001586C5 /* AlphaMetaDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlphaMetaDownloader.swift; sourceTree = ""; }; 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = ""; }; 01943258287D19BC008CC51A /* ClashRuleProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashRuleProvider.swift; sourceTree = ""; }; + 019725592CA15D6400C14E49 /* UserNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationCenter.swift; sourceTree = ""; }; + 0197255B2CA15FC600C14E49 /* NSWorkspace+openFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+openFile.swift"; sourceTree = ""; }; 019A239528657A7A00AE5698 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 01B009AC2854533200B93618 /* geoip.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geoip.dat.gz; sourceTree = ""; }; 01B009AD2854533300B93618 /* geosite.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geosite.dat.gz; sourceTree = ""; }; @@ -299,7 +302,6 @@ 495A44D220D267D000888A0A /* LaunchAtLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLogin.swift; sourceTree = ""; }; 495BFB8721919B9800C8779D /* RemoteConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigManager.swift; sourceTree = ""; }; 496322212AA5D89E00854231 /* UpdateExternalResourceAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateExternalResourceAction.swift; sourceTree = ""; }; - 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserNotificationCenter+Extension.swift"; sourceTree = ""; }; 496BDEDF21196F1E00C5207F /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 496C16462A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; 496C16472A3418C80052064A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; @@ -572,7 +574,6 @@ isa = PBXGroup; children = ( 499A486422EEA3FC00F6C675 /* Array+Safe.swift */, - 4966E9E22118153A00A391FB /* NSUserNotificationCenter+Extension.swift */, 499A485B22ED793C00F6C675 /* NSView+Nib.swift */, 499A485D22ED9B7C00F6C675 /* NSTableView+Reload.swift */, 4982F51E2344A216008804B0 /* Cgo+Convert.swift */, @@ -582,6 +583,8 @@ F9E8F34523A12B89002DE5E8 /* String+Encode.swift */, F939724D23A4DB0600FE5A3F /* DateFormatter+.swift */, 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */, + 019725592CA15D6400C14E49 /* UserNotificationCenter.swift */, + 0197255B2CA15FC600C14E49 /* NSWorkspace+openFile.swift */, ); path = Extensions; sourceTree = ""; @@ -929,7 +932,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1600; ORGANIZATIONNAME = west2online; TargetAttributes = { F9A7C0682306E874007163C7 = { @@ -1043,6 +1046,7 @@ 01F335EF2AD10D0B0048AF77 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */, 01F335F02AD10D0B0048AF77 /* AppDelegate+..swift in Sources */, 01F335F12AD10D0B0048AF77 /* NSView+Nib.swift in Sources */, + 0197255C2CA15FC600C14E49 /* NSWorkspace+openFile.swift in Sources */, 01F335F22AD10D0B0048AF77 /* ProxyDelayHistoryMenu.swift in Sources */, 01F335F32AD10D0B0048AF77 /* NSColor+Extension.swift in Sources */, 01F335F42AD10D0B0048AF77 /* ClashConfig.swift in Sources */, @@ -1059,6 +1063,7 @@ 01F335FF2AD10D0B0048AF77 /* Array+Safe.swift in Sources */, 01F336002AD10D0B0048AF77 /* SavedProxyModel.swift in Sources */, 01F336012AD10D0B0048AF77 /* NSTextField+Vibrancy.swift in Sources */, + 0197255A2CA15D6400C14E49 /* UserNotificationCenter.swift in Sources */, 01F336022AD10D0B0048AF77 /* SSIDSuspendTool.swift in Sources */, 01FBC6312B9C2B0800810BFF /* ClashProcess.swift in Sources */, 01F336032AD10D0B0048AF77 /* ProxyGroupMenu.swift in Sources */, @@ -1069,7 +1074,6 @@ 01F336082AD10D0B0048AF77 /* ProxyGroupMenuItemView.swift in Sources */, 01F336092AD10D0B0048AF77 /* PrivilegedHelperManager.swift in Sources */, 01F3360A2AD10D0B0048AF77 /* PrivilegedHelperManager+Legacy.swift in Sources */, - 01F3360B2AD10D0B0048AF77 /* NSUserNotificationCenter+Extension.swift in Sources */, 01F3360C2AD10D0B0048AF77 /* NSAlert+Extension.swift in Sources */, 01F3360D2AD10D0B0048AF77 /* ClashWebViewContoller.swift in Sources */, 01F3360E2AD10D0B0048AF77 /* ProxyGroupSpeedTestMenuItem.swift in Sources */, @@ -1281,6 +1285,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1344,6 +1349,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme index 5ae0d46d0..0f487a449 100644 --- a/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme +++ b/ClashX.xcodeproj/xcshareddata/xcschemes/ClashX Meta.xcscheme @@ -1,6 +1,6 @@ Color { - let httpsTest = true + let httpsTest = ConfigManager.shared.benchMarkUrl.hasPrefix("https://") switch delay { case 0: diff --git a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift b/ClashX/Extensions/NSUserNotificationCenter+Extension.swift deleted file mode 100644 index eee475373..000000000 --- a/ClashX/Extensions/NSUserNotificationCenter+Extension.swift +++ /dev/null @@ -1,186 +0,0 @@ -// -// NSUserNotificationCenter+Extension.swift -// ClashX -// -// Created by CYC on 2018/8/6. -// Copyright © 2018年 yichengchen. All rights reserved. -// - -import Cocoa -import UserNotifications - -extension NSUserNotificationCenter { - func post(title: String, info: String, identifier: String? = nil, notiOnly: Bool = true) { - if #available(OSX 10.14, *) { - let notificationCenter = UNUserNotificationCenter.current() - notificationCenter.delegate = UserNotificationCenterDelegate.shared - notificationCenter.getNotificationSettings { - [weak self] settings in - switch settings.authorizationStatus { - case .denied: - guard !notiOnly else { return } - DispatchQueue.main.async { - self?.postNotificationAlert(title: title, info: info, identifier: identifier) - } - case .authorized, .provisional: - DispatchQueue.main.async { - self?.postNotification(title: title, info: info, identifier: identifier) - } - case .notDetermined: - notificationCenter.requestAuthorization(options: .alert) { granted, _ in - if granted { - DispatchQueue.main.async { - self?.postNotification(title: title, info: info, identifier: identifier) - } - } else { - guard !notiOnly else { return } - DispatchQueue.main.async { - self?.postNotificationAlert(title: title, info: info, identifier: identifier) - } - } - } - @unknown default: - DispatchQueue.main.async { - self?.postNotification(title: title, info: info, identifier: identifier) - } - } - } - } else { - postNotification(title: title, info: info, identifier: identifier) - } - } - - private func postNotification(title: String, info: String, identifier: String? = nil) { - var userInfo: [String: Any] = [:] - if let identifier = identifier { - userInfo = ["identifier": identifier] - } - if #available(OSX 10.14, *) { - let notificationCenter = UNUserNotificationCenter.current() - notificationCenter.delegate = UserNotificationCenterDelegate.shared - notificationCenter.removeAllDeliveredNotifications() - notificationCenter.removeAllPendingNotificationRequests() - let content = UNMutableNotificationContent() - content.title = title - content.body = info - content.userInfo = userInfo - let uuidString = UUID().uuidString - let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: nil) - notificationCenter.add(request) { error in - if let err = error { - Logger.log("send noti fail: \(String(describing: err))") - DispatchQueue.main.async { - self.postNotificationAlert(title: title, info: info, identifier: identifier) - } - } - } - } else { - let notification = NSUserNotification() - notification.title = title - notification.informativeText = info - notification.userInfo = userInfo - delegate = UserNotificationCenterDelegate.shared - deliver(notification) - } - } - - func postNotificationAlert(title: String, info: String, identifier: String? = nil) { - if Settings.disableNoti { - return - } - let alert = NSAlert() - alert.messageText = title - alert.informativeText = info - alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) - alert.runModal() - if let identifier = identifier { - 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 postMetaErrorNotice(msg: String) { - let message = "Meta Core: \(msg)" - postNotificationAlert(title: NSLocalizedString("Start Meta Fail!", comment: ""), info: message) - } - - func postConfigErrorNotice(msg: String) { - let configName = ConfigManager.selectConfigName.isEmpty ? "" : - 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) - } - - 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) - } - - func postUpdateNotice(msg: String) { - postNotificationAlert(title: "Update ClashX Meta", info: msg) - } -} - -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": - AppDelegate.shared.updateConfig() - default: - break - } - } -} diff --git a/ClashX/Extensions/NSWorkspace+openFile.swift b/ClashX/Extensions/NSWorkspace+openFile.swift new file mode 100644 index 000000000..76d5de6c9 --- /dev/null +++ b/ClashX/Extensions/NSWorkspace+openFile.swift @@ -0,0 +1,15 @@ +// +// NSWorkspace+openFile.swift +// ClashX Meta +// +// Copyright © 2024 west2online. All rights reserved. +// + +import Cocoa + +extension NSWorkspace { + func openFilePath(_ path: String) { + open(.init(fileURLWithPath: path)) + } + +} diff --git a/ClashX/Extensions/UserNotificationCenter.swift b/ClashX/Extensions/UserNotificationCenter.swift new file mode 100644 index 000000000..405f5b087 --- /dev/null +++ b/ClashX/Extensions/UserNotificationCenter.swift @@ -0,0 +1,160 @@ +// +// UserNotificationCenter.swift +// ClashX Meta +// +// Copyright © 2024 west2online. All rights reserved. +// + +import Cocoa +import UserNotifications + + +class UserNotificationCenter: NSObject { + static let shared = UserNotificationCenter() + + private override init() { + super.init() + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.delegate = self + } + + func post(title: String, info: String, identifier: String? = nil, notiOnly: Bool = true) { + Task { @MainActor in + do { + let notificationCenter = UNUserNotificationCenter.current() + let settings = await notificationCenter.notificationSettings() + + switch settings.authorizationStatus { + case .denied: + guard !notiOnly else { return } + postNotificationAlert(title: title, info: info, identifier: identifier) + case .authorized, .provisional: + postNotification(title: title, info: info, identifier: identifier) + case .notDetermined: + let granted = try await notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) + + if granted { + postNotification(title: title, info: info, identifier: identifier) + } else { + guard !notiOnly else { return } + postNotificationAlert(title: title, info: info, identifier: identifier) + } + @unknown default: + postNotification(title: title, info: info, identifier: identifier) + } + } catch { + Logger.log("Request notification authorization failed, \(error)", level: .error) + } + } + } + + private func postNotification(title: String, info: String, identifier: String? = nil) { + var userInfo: [String: Any] = [:] + if let identifier = identifier { + userInfo = ["identifier": identifier] + } + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.removeAllDeliveredNotifications() + notificationCenter.removeAllPendingNotificationRequests() + let content = UNMutableNotificationContent() + content.title = title + content.body = info + content.userInfo = userInfo + let uuidString = UUID().uuidString + let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: nil) + notificationCenter.add(request) { error in + if let err = error { + Logger.log("send noti fail: \(String(describing: err))") + DispatchQueue.main.async { + self.postNotificationAlert(title: title, info: info, identifier: identifier) + } + } + } + } + + func postNotificationAlert(title: String, info: String, identifier: String? = nil) { + if Settings.disableNoti { + return + } + let alert = NSAlert() + alert.messageText = title + alert.informativeText = info + alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + alert.runModal() + if let identifier = identifier { + 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 postMetaErrorNotice(msg: String) { + let message = "Meta Core: \(msg)" + postNotificationAlert(title: NSLocalizedString("Start Meta Fail!", comment: ""), info: message) + } + + func postConfigErrorNotice(msg: String) { + let configName = ConfigManager.selectConfigName.isEmpty ? "" : + 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) + } + + 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) + } + + func postUpdateNotice(msg: String) { + postNotificationAlert(title: "Update ClashX Meta", info: msg) + } +} + +extension UserNotificationCenter: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { + if let identifier = response.notification.request.content.userInfo["identifier"] as? String { + handleNotificationActive(with: identifier) + } + center.removeAllDeliveredNotifications() + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { + [.banner, .sound] + } + + func handleNotificationActive(with identifier: String) { + switch identifier { + case "postConfigFileChangeDetectionNotice": + DispatchQueue.main.async { + AppDelegate.shared.updateConfig() + } + default: + break + } + } +} diff --git a/ClashX/General/AlphaMetaDownloader.swift b/ClashX/General/AlphaMetaDownloader.swift index 590e6d626..0c08fc0a6 100644 --- a/ClashX/General/AlphaMetaDownloader.swift +++ b/ClashX/General/AlphaMetaDownloader.swift @@ -7,7 +7,6 @@ import Cocoa import Alamofire -import PromiseKit class AlphaMetaDownloader: NSObject { @@ -76,92 +75,77 @@ class AlphaMetaDownloader: NSObject { return identifier } - static func alphaAsset() -> Promise { - Promise { resolver in - let assetName = assetName() - AF.request("https://api.github.com/repos/MetaCubeX/mihomo/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) { - guard let assets = $0.value?.assets else { - resolver.reject(errors.downloadFailed) - return - } - - guard let assetName, - let asset = assets.first(where: { - guard $0.state == "uploaded", $0.contentType == "application/gzip" else { return false } - - let names = $0.name.split(separator: "-").map(String.init) - guard names.count > 4, - names[0] == "mihomo", - names[1] == "darwin", - names[2] == assetName, - names[3] == "alpha" else { return false } - - return true - }) else { - resolver.reject(errors.decodeReleaseInfoFailed) - return - } - resolver.fulfill(asset) - } + static func alphaAsset() async throws -> ReleasesResp.Asset { + let resp = try? await AF.request("https://api.github.com/repos/MetaCubeX/mihomo/releases/tags/Prerelease-Alpha").serializingDecodable(ReleasesResp.self).value + + guard let resp else { + throw errors.downloadFailed + } + + let assets = resp.assets + + guard let assetName = assetName(), + let asset = assets.first(where: { + guard $0.state == "uploaded", $0.contentType == "application/gzip" else { return false } + + let names = $0.name.split(separator: "-").map(String.init) + guard names.count > 4, + names[0] == "mihomo", + names[1] == "darwin", + names[2] == assetName, + names[3] == "alpha" else { return false } + + return true + }) else { + throw errors.decodeReleaseInfoFailed } + + return asset } - static func checkVersion(_ asset: ReleasesResp.Asset) -> Promise { - Promise { resolver in - guard let path = Paths.alphaCorePath()?.path, - let ad = NSApplication.shared.delegate as? AppDelegate else { - resolver.reject(errors.unknownError) - return - } - if let v = ad.clashProcess.verifyCoreFile(path), - asset.name.contains(v.version) { - resolver.reject(errors.notFoundUpdate) - } - resolver.fulfill(asset) + static func checkVersion(_ asset: ReleasesResp.Asset) throws -> ReleasesResp.Asset { + guard let path = Paths.alphaCorePath()?.path else { + throw errors.unknownError + } + if let v = AppDelegate.shared.clashProcess.verifyCoreFile(path), + asset.name.contains(v.version) { + throw errors.notFoundUpdate } + return asset } - static func downloadCore(_ asset: ReleasesResp.Asset) -> Promise { - Promise { resolver in - let fm = FileManager.default - AF.download(asset.downloadUrl).response { - guard let gzPath = $0.fileURL?.path, - let contentData = fm.contents(atPath: gzPath) - else { - resolver.reject(errors.downloadFailed) - return - } - resolver.fulfill(contentData) - } + static func downloadCore(_ asset: ReleasesResp.Asset) async throws -> Data { + let fm = FileManager.default + let data = try? await AF.download(asset.downloadUrl).serializingData().value + + if let data { + return data + } else { + throw errors.downloadFailed } } - static func replaceCore(_ gzData: Data) -> Promise { - Promise { resolver in - let fm = FileManager.default + static func replaceCore(_ gzData: Data) throws -> String { + let fm = FileManager.default - guard let helperURL = Paths.alphaCorePath(), - let ad = NSApplication.shared.delegate as? AppDelegate else { - resolver.reject(errors.unknownError) - return - } + guard let helperURL = Paths.alphaCorePath() else { + throw errors.unknownError + } - try fm.createDirectory(at: helperURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) + try fm.createDirectory(at: helperURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) - let cachePath = Paths.tempPath().appending("/\(UUID().uuidString).newcore") - try gzData.gunzipped().write(to: .init(fileURLWithPath: cachePath)) - - Logger.log("save alpha core in \(cachePath)") + let cachePath = Paths.tempPath().appending("/\(UUID().uuidString).newcore") + try gzData.gunzipped().write(to: .init(fileURLWithPath: cachePath)) + + Logger.log("save alpha core in \(cachePath)") - guard let version = ad.clashProcess.verifyCoreFile(cachePath)?.version else { - resolver.reject(errors.testFailed) - return - } + guard let version = AppDelegate.shared.clashProcess.verifyCoreFile(cachePath)?.version else { + throw errors.testFailed + } - try? fm.removeItem(at: helperURL) - try fm.moveItem(atPath: cachePath, toPath: helperURL.path) + try? fm.removeItem(at: helperURL) + try fm.moveItem(atPath: cachePath, toPath: helperURL.path) - resolver.fulfill(version) - } + return version } } diff --git a/ClashX/General/ApiRequest.swift b/ClashX/General/ApiRequest.swift index 414658cb1..c7f1bb5f9 100644 --- a/ClashX/General/ApiRequest.swift +++ b/ClashX/General/ApiRequest.swift @@ -107,6 +107,7 @@ class ApiRequest { case let .success(ver): completeHandler(ver) case let .failure(err): + Logger.log("Request Version failed, \(err)", level: .error) completeHandler(nil) } } @@ -120,7 +121,7 @@ class ApiRequest { completeHandler(config) case let .failure(err): Logger.log(err.localizedDescription) - NSUserNotificationCenter.default.post(title: "Error", info: err.localizedDescription) + UserNotificationCenter.shared.post(title: "Error", info: err.localizedDescription) } } } @@ -153,14 +154,13 @@ class ApiRequest { static func requestConfigUpdate(configPath: String, callback: @escaping ((ErrorString?) -> Void)) { let placeHolderErrorDesp = "Error occoured, Please try to fix it by restarting ClashX. " - - req("/configs", method: .put, parameters: ["Path": configPath], encoding: JSONEncoding.default).responseJSON { res in + req("/configs", method: .put, parameters: ["Path": configPath], encoding: JSONEncoding.default).responseData { res in if res.response?.statusCode == 204 { ConfigManager.shared.isRunning = true callback(nil) } else { - let errorJson = try? res.result.get() - let err = JSON(errorJson ?? "")["message"].string ?? placeHolderErrorDesp + let errorData = try? res.result.get() + let err = JSON(errorData ?? Data())["message"].string ?? placeHolderErrorDesp Logger.log(err) callback(err) } @@ -305,9 +305,6 @@ class ApiRequest { static func getRules(completeHandler: @escaping ([ClashRule]) -> Void) { req("/rules").responseData { res in guard let data = try? res.result.get() else { return } - - ClashRuleProviderResp.init() - let rule = ClashRuleResponse.fromData(data) completeHandler(rule.rules ?? []) } diff --git a/ClashX/General/ClashProcess.swift b/ClashX/General/ClashProcess.swift index 2b101c978..d8907c837 100644 --- a/ClashX/General/ClashProcess.swift +++ b/ClashX/General/ClashProcess.swift @@ -292,8 +292,7 @@ class ClashProcess: NSObject { UserDefaults.standard.set(true, forKey: udString) - NSUserNotificationCenter.default - .postNotificationAlert(title: "Update Tips", info: info) + UserNotificationCenter.shared.postNotificationAlert(title: "Update Tips", info: info) } // MARK: launch path diff --git a/ClashX/General/Managers/ConfigFileManager.swift b/ClashX/General/Managers/ConfigFileManager.swift index a18c63012..07bacbf8c 100644 --- a/ClashX/General/Managers/ConfigFileManager.swift +++ b/ClashX/General/Managers/ConfigFileManager.swift @@ -29,10 +29,8 @@ class ConfigFileManager { } for event in events { if event.flags.contains(.ItemModified) || event.flags.contains(.ItemRenamed) { - NSUserNotificationCenter.default - .postConfigFileChangeDetectionNotice() - NotificationCenter.default - .post(Notification(name: .configFileChange)) + UserNotificationCenter.shared.postConfigFileChangeDetectionNotice() + NotificationCenter.default.post(Notification(name: .configFileChange)) break } } diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index 02de1e778..61952d527 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -409,7 +409,7 @@ extension MenuItemFactory { ApiRequest.updateAllProviders(for: type) { Logger.log("\(s) \($0) failed") let info = $0 == 0 ? "Success" : "\($0) failed" - NSUserNotificationCenter.default.post(title: s, info: info) + UserNotificationCenter.shared.post(title: s, info: info) recreateProxyMenuItems() } } @@ -423,7 +423,7 @@ extension MenuItemFactory { ApiRequest.updateProvider(for: type, name: name) { let info = $0 ? "Success" : "Failed" Logger.log("\(log) info") - NSUserNotificationCenter.default.post(title: log, info: info) + UserNotificationCenter.shared.post(title: log, info: info) recreateProxyMenuItems() } } diff --git a/ClashX/General/Managers/RemoteConfigManager.swift b/ClashX/General/Managers/RemoteConfigManager.swift index 4e28f552b..81bb26161 100755 --- a/ClashX/General/Managers/RemoteConfigManager.swift +++ b/ClashX/General/Managers/RemoteConfigManager.swift @@ -112,8 +112,7 @@ class RemoteConfigManager { if let error = error { // Fail if showNotification { - NSUserNotificationCenter.default - .post(title: NSLocalizedString("Remote Config Update Fail", comment: ""), + UserNotificationCenter.shared.post(title: NSLocalizedString("Remote Config Update Fail", comment: ""), info: "\(config.name): \(error)") } @@ -121,8 +120,7 @@ class RemoteConfigManager { // Success if showNotification { let info = "\(config.name): \(NSLocalizedString("Succeed!", comment: ""))" - NSUserNotificationCenter.default - .post(title: NSLocalizedString("Remote Config Update", comment: ""), info: info) + UserNotificationCenter.shared.post(title: NSLocalizedString("Remote Config Update", comment: ""), info: info) } AppDelegate.shared.updateConfig(showNotification: false) } diff --git a/ClashX/General/Managers/SystemProxyManager.swift b/ClashX/General/Managers/SystemProxyManager.swift index 771c1373c..760e77b97 100644 --- a/ClashX/General/Managers/SystemProxyManager.swift +++ b/ClashX/General/Managers/SystemProxyManager.swift @@ -30,9 +30,7 @@ class SystemProxyManager: NSObject { Logger.log("saveProxy", level: .debug) helper?.getCurrentProxySetting { [weak self] info in Logger.log("saveProxy done", level: .debug) - if let info = info as? [String: Any] { - self?.savedProxyInfo = info - } + self?.savedProxyInfo = info } } diff --git a/ClashX/General/Utils/ClashStatusTool.swift b/ClashX/General/Utils/ClashStatusTool.swift index 89ad01de0..c5d2721c7 100644 --- a/ClashX/General/Utils/ClashStatusTool.swift +++ b/ClashX/General/Utils/ClashStatusTool.swift @@ -22,7 +22,7 @@ class ClashStatusTool { DispatchQueue.main.async { let ret = alert.runModal() if ret == .alertSecondButtonReturn { - NSWorkspace.shared.openFile(Paths.localConfigPath(for: "config")) + NSWorkspace.shared.openFilePath(Paths.localConfigPath(for: "config")) } NSApp.terminate(nil) } diff --git a/ClashX/General/Utils/Command.swift b/ClashX/General/Utils/Command.swift index 528bb5f8d..ca503e9f2 100644 --- a/ClashX/General/Utils/Command.swift +++ b/ClashX/General/Utils/Command.swift @@ -26,7 +26,7 @@ struct Command { task.waitUntilExit() let outdata = outpipe.fileHandleForReading.readDataToEndOfFile() - if var string = String(data: outdata, encoding: .utf8) { + if let string = String(data: outdata, encoding: .utf8) { output = string.trimmingCharacters(in: .newlines) } return output diff --git a/ClashX/ViewControllers/AboutViewController.swift b/ClashX/ViewControllers/AboutViewController.swift index 2a2f33269..a2c4e7912 100644 --- a/ClashX/ViewControllers/AboutViewController.swift +++ b/ClashX/ViewControllers/AboutViewController.swift @@ -35,7 +35,6 @@ class AboutViewController: NSViewController { let version = AppVersionUtil.currentVersion let build = AppVersionUtil.currentBuild - let isBeta = AppVersionUtil.isBeta ? " Beta" : "" versionLabel.stringValue = "Version: \(version) (\(build))" coreVersionLabel.stringValue = "Meta Core: \(clashCoreVersion)" diff --git a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift index 8f71fb186..6a5e43a49 100644 --- a/ClashX/ViewControllers/Settings/DebugSettingViewController.swift +++ b/ClashX/ViewControllers/Settings/DebugSettingViewController.swift @@ -25,11 +25,11 @@ class DebugSettingViewController: NSViewController { } @IBAction func actionOpenLogFolder(_ sender: Any) { - NSWorkspace.shared.openFile(Logger.shared.logFolder()) + NSWorkspace.shared.openFilePath(Logger.shared.logFolder()) } @IBAction func actionOpenLocalConfig(_ sender: Any) { - NSWorkspace.shared.openFile(kConfigFolderPath) + NSWorkspace.shared.openFilePath(kConfigFolderPath) } @IBAction func actionOpenIcloudConfig(_ sender: Any) { diff --git a/ClashX/ViewControllers/Settings/MetaPrefsViewController.swift b/ClashX/ViewControllers/Settings/MetaPrefsViewController.swift index 5841c9719..86ec6edd6 100644 --- a/ClashX/ViewControllers/Settings/MetaPrefsViewController.swift +++ b/ClashX/ViewControllers/Settings/MetaPrefsViewController.swift @@ -86,23 +86,31 @@ class MetaPrefsViewController: NSViewController { sender.isEnabled = false updateProgressIndicator.isHidden = false updateProgressIndicator.startAnimation(nil) - AlphaMetaDownloader.alphaAsset().then { - AlphaMetaDownloader.checkVersion($0) - }.then { - AlphaMetaDownloader.downloadCore($0) - }.then { - AlphaMetaDownloader.replaceCore($0) - }.done { - self.updateAlphaVersion($0) - let msg = NSLocalizedString("Version: ", comment: "") + $0 - NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: msg) - }.ensure { - sender.isEnabled = true - self.updateProgressIndicator.isHidden = true - self.updateProgressIndicator.stopAnimation(nil) - }.catch { - let error = $0 as? AlphaMetaDownloader.errors - NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: error?.des() ?? "") + + let dl = AlphaMetaDownloader.self + + Task { + do { + let asset = try await dl.alphaAsset() + let ver = try dl.checkVersion(asset) + let data = try await dl.downloadCore(ver) + let newVer = try dl.replaceCore(data) + + await MainActor.run { + self.updateAlphaVersion(newVer) + let msg = NSLocalizedString("Version: ", comment: "") + newVer + UserNotificationCenter.shared.post(title: "Clash Meta Core", info: msg) + } + } catch { + let error = error as? AlphaMetaDownloader.errors + UserNotificationCenter.shared.post(title: "Clash Meta Core", info: error?.des() ?? "") + } + + await MainActor.run { + sender.isEnabled = true + self.updateProgressIndicator.isHidden = true + self.updateProgressIndicator.stopAnimation(nil) + } } }