mirror of
https://github.com/bolucat/Archive.git
synced 2025-12-24 13:28:37 +08:00
Update On Mon Jul 1 20:30:44 CEST 2024
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Uncomment the next line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :osx, '10.14'
|
||||
platform :osx, '11.0'
|
||||
|
||||
target 'V2rayU' do
|
||||
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
|
||||
@@ -14,7 +14,6 @@ target 'V2rayU' do
|
||||
pod 'SwiftyJSON'
|
||||
# master branch
|
||||
pod 'Preferences', :git => 'https://github.com/sindresorhus/Preferences.git'
|
||||
pod 'Sparkle' ,'~> 2.0'
|
||||
pod 'QRCoder'
|
||||
pod 'MASShortcut'
|
||||
pod 'Swifter'
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
6618372E23E9BF74000F7410 /* ToastWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6618372C23E9BF73000F7410 /* ToastWindow.xib */; };
|
||||
66193A8623EE45B200289B6A /* PreferenceRouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66193A8523EE45B200289B6A /* PreferenceRouting.swift */; };
|
||||
66193A8923EE46BC00289B6A /* PreferenceRouting.xib in Resources */ = {isa = PBXBuildFile; fileRef = 66193A8723EE46BC00289B6A /* PreferenceRouting.xib */; };
|
||||
6633A43F2C0A120000C54CA5 /* Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6633A43E2C0A120000C54CA5 /* Sparkle.swift */; };
|
||||
663F040625ED4B2C00687600 /* V2rayLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663F040525ED4B2C00687600 /* V2rayLaunch.swift */; };
|
||||
664BAC472C2DB0E100654FB7 /* V2rayRouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */; };
|
||||
664BAC492C314CD600654FB7 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BAC482C314CD600654FB7 /* AppVersion.swift */; };
|
||||
664EB375216C9A5E00B6AE0D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB374216C9A5E00B6AE0D /* AppDelegate.swift */; };
|
||||
664EB377216C9A5F00B6AE0D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664EB376216C9A5F00B6AE0D /* Assets.xcassets */; };
|
||||
664EB392216CA9E800B6AE0D /* ConfigWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB390216CA9E800B6AE0D /* ConfigWindow.swift */; };
|
||||
@@ -121,11 +121,11 @@
|
||||
6625848D2AB745E700DFDC1E /* sign.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sign.sh; sourceTree = "<group>"; };
|
||||
6625848E2AB746D500DFDC1E /* appdmg.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = appdmg.sh; sourceTree = "<group>"; };
|
||||
662F0ACE2AB720C700884C17 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ../en.lproj/PreferenceGeneral.strings; sourceTree = "<group>"; };
|
||||
6633A43E2C0A120000C54CA5 /* Sparkle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sparkle.swift; sourceTree = "<group>"; };
|
||||
663F040525ED4B2C00687600 /* V2rayLaunch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = V2rayLaunch.swift; sourceTree = "<group>"; };
|
||||
664666A021CBD6C60094F0B7 /* libPods-V2rayUTool.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libPods-V2rayUTool.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
664B95DD217062A500DBC941 /* Alamofire */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Alamofire; path = Pods/Alamofire; sourceTree = "<group>"; };
|
||||
664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2rayRouting.swift; sourceTree = "<group>"; };
|
||||
664BAC482C314CD600654FB7 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||
664EB371216C9A5E00B6AE0D /* V2rayU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2rayU.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
664EB374216C9A5E00B6AE0D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
664EB376216C9A5F00B6AE0D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -267,6 +267,7 @@
|
||||
6608D9B82182BBAC00A0E0DD /* v2ray */,
|
||||
664EB376216C9A5F00B6AE0D /* Assets.xcassets */,
|
||||
664EB374216C9A5E00B6AE0D /* AppDelegate.swift */,
|
||||
664BAC482C314CD600654FB7 /* AppVersion.swift */,
|
||||
66ACB19F21757D5B005B5881 /* MainMenu.swift */,
|
||||
664EB378216C9A5F00B6AE0D /* MainMenu.xib */,
|
||||
664EB390216CA9E800B6AE0D /* ConfigWindow.swift */,
|
||||
@@ -287,7 +288,6 @@
|
||||
6D6DF7F662C35646734A352E /* install.sh */,
|
||||
6D6DF11F0983AFCBEF1E347B /* Uri.swift */,
|
||||
6D6DF79424C83391C205CB9C /* Share.swift */,
|
||||
6633A43E2C0A120000C54CA5 /* Sparkle.swift */,
|
||||
);
|
||||
path = V2rayU;
|
||||
sourceTree = "<group>";
|
||||
@@ -556,6 +556,7 @@
|
||||
6608D9DA2182C69D00A0E0DD /* v2rayStruct.swift in Sources */,
|
||||
66F07CF9236D79540088A4AE /* Ping.swift in Sources */,
|
||||
664EB392216CA9E800B6AE0D /* ConfigWindow.swift in Sources */,
|
||||
664BAC492C314CD600654FB7 /* AppVersion.swift in Sources */,
|
||||
66193A8623EE45B200289B6A /* PreferenceRouting.swift in Sources */,
|
||||
6D6DF8BFC33F97E9AFCA5A4B /* V2rayConfig.swift in Sources */,
|
||||
6D6DF6F065067CD879201FF9 /* Import.swift in Sources */,
|
||||
@@ -568,7 +569,6 @@
|
||||
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */,
|
||||
6D6DFC1D9432F2A60D2156E2 /* PreferenceDns.swift in Sources */,
|
||||
6D6DF670FFB063EE2360776C /* Uri.swift in Sources */,
|
||||
6633A43F2C0A120000C54CA5 /* Sparkle.swift in Sources */,
|
||||
6D6DF41FA8F7C7B020AC4115 /* Share.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -768,7 +768,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -827,7 +827,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
@@ -849,7 +849,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 4.2.0;
|
||||
CURRENT_PROJECT_VERSION = 4.0.0;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_ONLY_ACTIVE_RESOURCES = YES;
|
||||
@@ -859,8 +859,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 4.2.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -883,7 +883,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 4.2.0;
|
||||
CURRENT_PROJECT_VERSION = 4.0.0;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_ONLY_ACTIVE_RESOURCES = YES;
|
||||
@@ -893,8 +893,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 4.2.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 4.0.0;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -915,7 +915,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
@@ -930,7 +930,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
|
||||
@@ -10,12 +10,10 @@ import Cocoa
|
||||
import ServiceManagement
|
||||
import MASShortcut
|
||||
import Preferences
|
||||
import Sparkle
|
||||
import FirebaseCore
|
||||
|
||||
let launcherAppIdentifier = "net.yanue.V2rayU.Launcher"
|
||||
let appVersion = getAppVersion()
|
||||
let V2rayUpdater = V2rayUpdaterController()
|
||||
|
||||
let NOTIFY_TOGGLE_RUNNING_SHORTCUT = Notification.Name(rawValue: "NOTIFY_TOGGLE_RUNNING_SHORTCUT")
|
||||
let NOTIFY_SWITCH_PROXY_MODE_SHORTCUT = Notification.Name(rawValue: "NOTIFY_SWITCH_PROXY_MODE_SHORTCUT")
|
||||
@@ -42,6 +40,9 @@ let preferencesWindowController = PreferencesWindowController(
|
||||
]
|
||||
)
|
||||
|
||||
let langStr = Locale.current.languageCode
|
||||
let isMainland = langStr == "zh-CN" || langStr == "zh" || langStr == "zh-Hans" || langStr == "zh-Hant"
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
// bar menu
|
||||
|
||||
614
v2rayu/V2rayU/AppVersion.swift
Normal file
614
v2rayu/V2rayU/AppVersion.swift
Normal file
@@ -0,0 +1,614 @@
|
||||
//
|
||||
// AppVersion.swift
|
||||
// V2rayU
|
||||
//
|
||||
// Created by yanue on 2024/6/30.
|
||||
// Copyright © 2024 yanue. All rights reserved.
|
||||
//
|
||||
import SwiftUI
|
||||
import ServiceManagement
|
||||
|
||||
// 手动实现检查版本下载更新 UI.
|
||||
// 基于 SwiftUI + NSWindowController 实现
|
||||
// 参考 UI: Sparkle(https://github.com/sparkle-project/Sparkle)
|
||||
// 基于 https://github.com/yanue/V2rayU/releases 进行版本检查
|
||||
|
||||
struct GithubRelease: Codable {
|
||||
let id: Int
|
||||
let tagName: String
|
||||
let name: String
|
||||
let draft: Bool
|
||||
let prerelease: Bool
|
||||
let publishedAt: Date // 2024-06-30T09:00:00Z, 用于排序
|
||||
let assets: [GithubAsset]
|
||||
let body: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case tagName = "tag_name"
|
||||
case name
|
||||
case draft
|
||||
case prerelease
|
||||
case publishedAt = "published_at"
|
||||
case assets
|
||||
case body
|
||||
}
|
||||
}
|
||||
|
||||
struct GithubAsset: Codable {
|
||||
let name: String
|
||||
let browserDownloadUrl: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name
|
||||
case browserDownloadUrl = "browser_download_url"
|
||||
}
|
||||
}
|
||||
|
||||
let V2rayUpdater = AppCheckController()
|
||||
|
||||
// AppCheckController - 检查新版本页面
|
||||
|
||||
class AppCheckController: NSWindowController {
|
||||
// Declare the contentView as a property to avoid using self before super.init
|
||||
private var contentView: NSHostingView<ContentView>!
|
||||
var bindData = BindData()
|
||||
|
||||
// Initialize the view and window
|
||||
init() {
|
||||
// Initialize the content view with a placeholder closure
|
||||
let contentView = NSHostingView(rootView: ContentView(
|
||||
bindData: bindData,
|
||||
closeWindow: {}
|
||||
))
|
||||
|
||||
// Create the window with specified dimensions and styles
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
|
||||
styleMask: [.titled, .closable, .resizable],
|
||||
backing: .buffered, defer: false)
|
||||
window.title = "Check V2rayU"
|
||||
window.contentView = contentView
|
||||
|
||||
// Call the super init with the created window
|
||||
super.init(window: window)
|
||||
|
||||
// Update the contentView with the actual closure after super.init
|
||||
contentView.rootView = ContentView(
|
||||
bindData: bindData,
|
||||
closeWindow: closeWindow
|
||||
)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
}
|
||||
|
||||
func checkForUpdates(showWindow: Bool = false) {
|
||||
if showWindow {
|
||||
DispatchQueue.main.async {
|
||||
self.window?.orderFrontRegardless()
|
||||
self.window?.center()
|
||||
self.window?.makeKeyAndOrderFront(nil)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
} else {
|
||||
// close window
|
||||
DispatchQueue.main.async {
|
||||
self.window?.close()
|
||||
}
|
||||
}
|
||||
guard let url = URL(string: "https://api.github.com/repos/yanue/V2rayU/releases") else {
|
||||
return
|
||||
}
|
||||
print("checkForUpdates: \(url)")
|
||||
let checkTask = URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
if let error = error {
|
||||
print("Error fetching release: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
print("No data returned")
|
||||
return
|
||||
}
|
||||
|
||||
print("checkForUpdates: \n \(data)")
|
||||
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
decoder.dateDecodingStrategy = .iso8601 // 解析日期
|
||||
|
||||
// try decode data
|
||||
let data: [GithubRelease] = try decoder.decode([GithubRelease].self, from: data)
|
||||
|
||||
// 按日期倒序排序
|
||||
let sortedData = data.sorted { $0.publishedAt > $1.publishedAt }
|
||||
|
||||
// 取第一个
|
||||
if let release = sortedData.first {
|
||||
print("release: \(release.tagName)")
|
||||
DispatchQueue.main.async {
|
||||
let releaseVersion = release.tagName.replacingOccurrences(of: "v", with: "").replacingOccurrences(of: "V", with: "").trimmingCharacters(in: .whitespaces) // v4.1.0 => 4.1.0
|
||||
// get old version
|
||||
let appVer = appVersion.versionToInt()
|
||||
let releaseVer = releaseVersion.versionToInt()
|
||||
|
||||
// new version is bigger than old version
|
||||
if appVer.lexicographicallyPrecedes(releaseVer) {
|
||||
// 点击菜单栏检查新版本,不过滤
|
||||
if !showWindow {
|
||||
// 如果用户选择跳过版本更新, 则不显示新版本详情页面
|
||||
if let skipVersion = UserDefaults.standard.string(forKey: "skipAppVersion") {
|
||||
if skipVersion == release.tagName {
|
||||
print("Skip version: \(skipVersion)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// 显示新版本详情页面
|
||||
let versionController = AppVersionController()
|
||||
versionController.show(release: release)
|
||||
// close window
|
||||
self.closeWindow()
|
||||
} else {
|
||||
var title = "You are up to date!"
|
||||
var toast = "V2rayU \(appVersion) is currently the newest version available."
|
||||
if isMainland {
|
||||
title = "当前已经是最新版了"
|
||||
toast = "V2rayU \(appVersion) 已经是当前最新版了.";
|
||||
}
|
||||
// open dialog
|
||||
alertDialog(title: title, message: toast)
|
||||
// close window
|
||||
self.closeWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Error decoding JSON: \(error)")
|
||||
DispatchQueue.main.async {
|
||||
// update progress text
|
||||
self.bindData.progressText = "Check failed: \(error)"
|
||||
var title = "Check failed!"
|
||||
var toast = "\(error)"
|
||||
if isMainland {
|
||||
title = "检查失败"
|
||||
toast = "\(error)";
|
||||
}
|
||||
// open dialog
|
||||
alertDialog(title: title, message: toast)
|
||||
// sleep 2s
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
// close window
|
||||
self.closeWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkTask.resume()
|
||||
}
|
||||
|
||||
func closeWindow() {
|
||||
DispatchQueue.main.async {
|
||||
self.window?.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BindData: ObservableObject {
|
||||
@Published var progressText = "check for updates..."
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var bindData: BindData
|
||||
|
||||
var closeWindow: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
HStack {
|
||||
Image("V2rayU")
|
||||
.resizable()
|
||||
.frame(width: 64, height: 64)
|
||||
.cornerRadius(8)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
ProgressView(bindData.progressText) .progressViewStyle(LinearProgressViewStyle()).padding(.horizontal)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
closeWindow()
|
||||
}) {
|
||||
Text("Cancel").font(.body)
|
||||
}
|
||||
.padding(.trailing, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AppVersionController - 新版本详情页面
|
||||
|
||||
class AppVersionController: NSWindowController {
|
||||
var bindData = BindData()
|
||||
private var contentView: NSHostingView<ContentView>!
|
||||
private var release: GithubRelease!
|
||||
|
||||
init() {
|
||||
let contentView = NSHostingView(rootView: ContentView(
|
||||
bindData: bindData,
|
||||
skipAction: { print("Skip action") },
|
||||
installAction: { print("Install action") }
|
||||
))
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 500, height: 300),
|
||||
styleMask: [.titled, .closable, .resizable],
|
||||
backing: .buffered, defer: false)
|
||||
window.title = "Software Update"
|
||||
window.contentView = contentView
|
||||
|
||||
super.init(window: window)
|
||||
|
||||
// Update the contentView with the actual closure after super.init
|
||||
contentView.rootView = ContentView(
|
||||
bindData: bindData,
|
||||
skipAction: self.skipAction,
|
||||
installAction: self.installAction
|
||||
)
|
||||
}
|
||||
|
||||
func show(release: GithubRelease) {
|
||||
bindData.title = "A new version of V2rayU is available!"
|
||||
bindData.description = "V2rayU \(appVersion) is now available—you have \(release.tagName). Would you like to download it now?"
|
||||
bindData.releaseNotes = release.name + "\n" + release.body
|
||||
self.release = release
|
||||
print("bindData.releaseNotes", bindData.releaseNotes)
|
||||
// bring window to front
|
||||
window?.orderFrontRegardless()
|
||||
// center position
|
||||
window?.center()
|
||||
// make window key
|
||||
window?.makeKeyAndOrderFront(nil)
|
||||
// activate app
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
}
|
||||
|
||||
// 安装新版本
|
||||
func installAction() {
|
||||
DispatchQueue.main.async {
|
||||
// 显示下载页面
|
||||
let downloadController = AppDownloadController()
|
||||
downloadController.show(release: self.release)
|
||||
// 关闭窗口
|
||||
self.window?.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func skipAction() {
|
||||
print("Skip action")
|
||||
// UserDefaults 记录是否跳过版本更新
|
||||
UserDefaults.standard.set(release.tagName, forKey: "skipAppVersion")
|
||||
// 关闭窗口
|
||||
DispatchQueue.main.async {
|
||||
self.window?.close()
|
||||
}
|
||||
}
|
||||
|
||||
class BindData: ObservableObject {
|
||||
@Published var title = "A new version of V2rayU App is available!"
|
||||
@Published var description = ""
|
||||
@Published var releaseNotes = """
|
||||
"""
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var bindData: BindData
|
||||
var skipAction: () -> Void
|
||||
var installAction: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
// use AppIcon.appiconset to Image
|
||||
Image("V2rayU")
|
||||
.resizable()
|
||||
.frame(width: 64, height: 64)
|
||||
.padding(.top, 20)
|
||||
.padding(.leading, 20)
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(bindData.title)
|
||||
.font(.headline)
|
||||
.padding(.top, 20)
|
||||
|
||||
Text(bindData.description)
|
||||
.padding(.trailing, 20)
|
||||
|
||||
Text("Release Notes:")
|
||||
.font(.headline)
|
||||
.bold()
|
||||
.padding(.top, 20)
|
||||
|
||||
HStack {
|
||||
|
||||
// 文字可选中
|
||||
TextEditor(text: $bindData.releaseNotes)
|
||||
.lineSpacing(6) // 行间距
|
||||
.frame(height: 120)
|
||||
.border(Color.gray, width: 1) // 黑色边框,宽度为 2
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
|
||||
Spacer(minLength: 20) // 右边 margin 40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Button("Skip This Version") {
|
||||
skipAction()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Button("Install Update") {
|
||||
installAction()
|
||||
}
|
||||
.keyboardShortcut(.defaultAction)
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
.frame(width: 500, height: 300)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AppDownloadController - 下载安装页面
|
||||
|
||||
class AppDownloadController: NSWindowController, URLSessionDownloadDelegate {
|
||||
private var contentView: NSHostingView<ContentView>!
|
||||
var bindData = BindData()
|
||||
private var downloadTask: URLSessionDownloadTask?
|
||||
private var destinationURL: URL?
|
||||
|
||||
init() {
|
||||
let contentView = NSHostingView(rootView: ContentView(
|
||||
bindData: bindData,
|
||||
cancelDownload: {},
|
||||
doInstall: {}
|
||||
))
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
|
||||
styleMask: [.titled, .closable, .resizable],
|
||||
backing: .buffered, defer: false)
|
||||
window.title = "Download V2rayU"
|
||||
window.contentView = contentView
|
||||
super.init(window: window)
|
||||
|
||||
// Update the contentView with the actual closure after super.init
|
||||
contentView.rootView = ContentView(
|
||||
bindData: bindData,
|
||||
cancelDownload: cancelDownload,
|
||||
doInstall: doInstall
|
||||
)
|
||||
self.contentView = contentView
|
||||
}
|
||||
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
}
|
||||
|
||||
func show(release: GithubRelease) {
|
||||
DispatchQueue.main.async {
|
||||
self.window?.orderFrontRegardless()
|
||||
self.window?.center()
|
||||
self.window?.makeKeyAndOrderFront(nil)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
download(release: release)
|
||||
}
|
||||
|
||||
func download(release: GithubRelease) {
|
||||
DispatchQueue.main.async {
|
||||
if let asset = release.assets.first {
|
||||
self.bindData.dmgUrl = asset.browserDownloadUrl
|
||||
print("download: \(self.bindData.dmgUrl)")
|
||||
self.startDownload()
|
||||
} else {
|
||||
self.bindData.progressText = "No dmg asset found"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startDownload() {
|
||||
guard let url = URL(string: bindData.dmgUrl) else {
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = true
|
||||
self.bindData.progressText = "Invalid dmg url"
|
||||
}
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = true
|
||||
self.bindData.progress = 0.0
|
||||
self.bindData.progressText = "Downloading..."
|
||||
}
|
||||
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
|
||||
downloadTask = urlSession.downloadTask(with: url)
|
||||
downloadTask?.resume()
|
||||
|
||||
}
|
||||
|
||||
private func cancelDownload() {
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = false
|
||||
self.bindData.progress = 0.0
|
||||
self.bindData.progressText = "Download canceled"
|
||||
self.downloadTask?.cancel()
|
||||
print("Download canceled")
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
self.window?.close()
|
||||
}
|
||||
}
|
||||
|
||||
func doInstall() {
|
||||
DispatchQueue.main.async {
|
||||
if let destinationURL = self.destinationURL {
|
||||
// open downloaded dmg
|
||||
NSWorkspace.shared.open(destinationURL)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
// close window
|
||||
self.window?.close()
|
||||
NSApplication.shared.terminate(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("Installing V2rayU")
|
||||
}
|
||||
|
||||
// ---------------------- ui 相关 --------------------------------
|
||||
// MARK: - 下载进度数据
|
||||
class BindData: ObservableObject {
|
||||
@Published var progressText = "Downloading..."
|
||||
@Published var dmgUrl: String = ""
|
||||
@Published var progress: Float = 0.0
|
||||
@Published var isDownloading: Bool = false
|
||||
}
|
||||
|
||||
// MARK: - 下载进度视图
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var bindData: BindData
|
||||
var cancelDownload: () -> Void
|
||||
var doInstall: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
VStack(spacing: 20) {
|
||||
HStack {
|
||||
Image("V2rayU")
|
||||
.resizable()
|
||||
.frame(width: 64, height: 64)
|
||||
.cornerRadius(8)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
ProgressView(value: bindData.progress, total: 100) {
|
||||
Text(bindData.progressText)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
if bindData.isDownloading {
|
||||
Button(action: {
|
||||
cancelDownload()
|
||||
}) {
|
||||
Text("Cancel").font(.body)
|
||||
}
|
||||
} else {
|
||||
Button(action: {
|
||||
doInstall()
|
||||
}) {
|
||||
Text("Install V2rayU").font(.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 下载相关 --------------------------------
|
||||
|
||||
// MARK: - URLSessionDownloadDelegate
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||
let fileManager = FileManager.default
|
||||
let downloadsDirectory = fileManager.urls(for: .downloadsDirectory, in: .userDomainMask).first!
|
||||
destinationURL = downloadsDirectory.appendingPathComponent(downloadTask.response?.suggestedFilename ?? "V2rayU-macOS.dmg")
|
||||
|
||||
do {
|
||||
try fileManager.moveItem(at: location, to: destinationURL!)
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = false
|
||||
self.bindData.progress = 100.0
|
||||
self.bindData.progressText = "Download Completed"
|
||||
}
|
||||
print("Download finished: \(destinationURL!)")
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = false
|
||||
self.bindData.progressText = "File move error: \(error.localizedDescription)"
|
||||
var title = "Download failed!"
|
||||
var toast = "\(error)"
|
||||
if isMainland {
|
||||
title = "移动文件失败"
|
||||
toast = "\(error)";
|
||||
}
|
||||
// open dialog
|
||||
alertDialog(title: title, message: toast)
|
||||
}
|
||||
print("File move error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite) * 100
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
if let error = error {
|
||||
DispatchQueue.main.async {
|
||||
self.bindData.isDownloading = false
|
||||
self.bindData.progressText = "Download Failed: \(error.localizedDescription)"
|
||||
}
|
||||
var title = "Download failed!"
|
||||
var toast = "\(error)"
|
||||
if isMainland {
|
||||
title = "下载文件失败"
|
||||
toast = "\(error)";
|
||||
}
|
||||
// open dialog
|
||||
alertDialog(title: title, message: toast)
|
||||
print("Download error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "icon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "icon@3x 1.png",
|
||||
"idiom" : "universal",
|
||||
"filename" : "icon@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
v2rayu/V2rayU/Assets.xcassets/IconOn.imageset/icon@3x 1.png
vendored
Normal file
BIN
v2rayu/V2rayU/Assets.xcassets/IconOn.imageset/icon@3x 1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 750 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/1024.png
vendored
Normal file
BIN
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/1024.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
BIN
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/128.png
vendored
Normal file
BIN
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/128.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
18
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/Contents.json
vendored
Normal file
18
v2rayu/V2rayU/Assets.xcassets/V2rayU.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
|
||||
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -19,7 +19,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="700" height="360"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YO4-aZ-Rwa">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YO4-aZ-Rwa">
|
||||
<rect key="frame" x="309" y="272" width="62" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="V2rayU" id="09i-pj-m0K">
|
||||
@@ -28,7 +28,7 @@
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5SZ-ig-pGf">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5SZ-ig-pGf">
|
||||
<rect key="frame" x="266" y="184" width="24" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="🌍" id="K46-M8-1zq">
|
||||
@@ -37,7 +37,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Te4-BT-hGI">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Te4-BT-hGI">
|
||||
<rect key="frame" x="286" y="189" width="152" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="github.com/yanue/v2rayU" id="Qby-y7-vli">
|
||||
@@ -46,7 +46,7 @@
|
||||
<color key="backgroundColor" name="systemIndigoColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e05-pp-m5L">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e05-pp-m5L">
|
||||
<rect key="frame" x="288" y="166" width="122" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="yanue@outlook.com" id="Guc-nt-FeY">
|
||||
@@ -55,7 +55,7 @@
|
||||
<color key="backgroundColor" name="systemIndigoColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wuh-jp-WUO">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wuh-jp-WUO">
|
||||
<rect key="frame" x="266" y="162" width="24" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="📬" id="OUg-qH-sDe">
|
||||
@@ -64,7 +64,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oNz-CM-Geb">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oNz-CM-Geb">
|
||||
<rect key="frame" x="267" y="138" width="24" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="🔗" id="rUl-Lc-b4S">
|
||||
@@ -73,7 +73,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hji-yO-Zff">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hji-yO-Zff">
|
||||
<rect key="frame" x="287" y="143" width="146" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="https://github.com/XTLS" id="Sw8-ch-1M9">
|
||||
@@ -82,7 +82,7 @@
|
||||
<color key="backgroundColor" name="systemIndigoColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="huF-fy-g3j">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="huF-fy-g3j">
|
||||
<rect key="frame" x="262" y="92" width="156" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright © 2021 yanue.net" id="bCA-au-e3o">
|
||||
@@ -91,7 +91,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gws-ge-QLa">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gws-ge-QLa">
|
||||
<rect key="frame" x="264" y="69" width="156" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="charWrapping" enabled="NO" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="center" title="based on Xray-core v1.4.2" usesSingleLineMode="YES" bezelStyle="round" id="8bU-0Q-dTD">
|
||||
@@ -100,7 +100,7 @@
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GfP-Xl-Rdf">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GfP-Xl-Rdf">
|
||||
<rect key="frame" x="306" y="251" width="73" height="13"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Version 1.0" id="gsf-1V-bbM">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -18,13 +18,13 @@
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" visibleAtLaunch="NO" animationBehavior="default" id="kRK-Pd-y69" customClass="NSPanel">
|
||||
<windowStyleMask key="styleMask" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
|
||||
<rect key="contentRect" x="139" y="81" width="300" height="100"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1125"/>
|
||||
<value key="minSize" type="size" width="200" height="100"/>
|
||||
<view key="contentView" id="KRY-zc-8By" userLabel="Panel View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="162" translatesAutoresizingMaskIntoConstraints="NO" id="scG-hz-bja" userLabel="Is Name">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="162" translatesAutoresizingMaskIntoConstraints="NO" id="scG-hz-bja" userLabel="Is Name">
|
||||
<rect key="frame" x="24" y="33" width="258" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="Label" usesSingleLineMode="YES" id="eQL-ri-Y7Z">
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Alamofire
|
||||
|
||||
var v2rayConfig: V2rayConfig = V2rayConfig()
|
||||
|
||||
@@ -645,22 +644,6 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
|
||||
|
||||
if let importUri = ImportUri.importUri(uri: uri, checkExist: false) {
|
||||
self.saveImport(importUri: importUri)
|
||||
} else {
|
||||
// download json file
|
||||
Alamofire.request(jsonUrl.stringValue).responseString { DataResponse in
|
||||
if (DataResponse.error != nil) {
|
||||
DispatchQueue.main.async{
|
||||
self.errTip.stringValue = "error: " + DataResponse.error.debugDescription
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if DataResponse.value != nil {
|
||||
DispatchQueue.main.async{
|
||||
self.configText.string = v2rayConfig.formatJson(json: DataResponse.value ?? text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string></string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<true/>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
@@ -47,14 +51,10 @@
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 yanue. All rights reserved.</string>
|
||||
<string>Copyright © 2024 yanue. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://v2rayu-61f76.web.app/appcast.xml</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>PW8pDnr5VZkmC93gZjUDlHI8gkJSspPoDU3DdhsMkps</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
import Cocoa
|
||||
import ServiceManagement
|
||||
import Sparkle
|
||||
import Alamofire
|
||||
|
||||
let menuController = (NSApplication.shared.delegate as? AppDelegate)?.statusMenu.delegate as! MenuController
|
||||
|
||||
@@ -335,8 +333,7 @@ class MenuController: NSObject, NSMenuDelegate {
|
||||
}
|
||||
|
||||
@IBAction func checkForUpdate(_ sender: NSMenuItem) {
|
||||
// need set SUFeedURL into plist
|
||||
V2rayUpdater.checkForUpdates()
|
||||
V2rayUpdater.checkForUpdates(showWindow: true)
|
||||
}
|
||||
|
||||
@IBAction func generateQrcode(_ sender: NSMenuItem) {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
// Copyright © 2019 yanue. All rights reserved.
|
||||
//
|
||||
|
||||
import Alamofire
|
||||
import SwiftyJSON
|
||||
|
||||
// ping and choose fastest v2ray
|
||||
|
||||
@@ -68,7 +68,6 @@ final class PreferenceGeneralViewController: NSViewController, PreferencePane {
|
||||
}
|
||||
|
||||
@IBAction func checkVersion(_ sender: NSButton) {
|
||||
// need set SUFeedURL into plist
|
||||
V2rayUpdater.checkForUpdates()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
// Copyright © 2018 yanue. All rights reserved.
|
||||
//
|
||||
|
||||
import Alamofire
|
||||
import Cocoa
|
||||
import Preferences
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
import Cocoa
|
||||
import Preferences
|
||||
import Alamofire
|
||||
import SwiftyJSON
|
||||
|
||||
final class PreferenceSubscribeViewController: NSViewController, PreferencePane, NSTabViewDelegate {
|
||||
|
||||
@@ -23,7 +23,7 @@ class V2rayUpdaterController: NSObject, SPUUpdaterDelegate {
|
||||
|
||||
func checkForUpdates() {
|
||||
// check version by github release
|
||||
checkV2rayUVersion()
|
||||
// checkV2rayUVersion()
|
||||
// check by sparkle
|
||||
fetchAppcast(from: primaryFeedURL) { success in
|
||||
// 主线程
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Alamofire
|
||||
|
||||
extension UserDefaults {
|
||||
enum KEY: String {
|
||||
|
||||
@@ -428,58 +428,3 @@ class V2rayLaunch: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkV2rayUVersion() {
|
||||
// 当前版本检测
|
||||
Alamofire.request("https://api.github.com/repos/yanue/V2rayU/releases/latest").responseJSON { response in
|
||||
//to get status code
|
||||
if let status = response.response?.statusCode {
|
||||
if status != 200 {
|
||||
NSLog("error with response status: ", status)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//to get JSON return value
|
||||
if let result = response.result.value {
|
||||
guard let JSON = result as? NSDictionary else {
|
||||
NSLog("error: no tag_name")
|
||||
return
|
||||
}
|
||||
|
||||
// get tag_name (version)
|
||||
guard let tag_name = JSON["tag_name"] else {
|
||||
NSLog("error: no tag_name")
|
||||
return
|
||||
}
|
||||
|
||||
// get prerelease and draft
|
||||
guard let prerelease = JSON["prerelease"], let draft = JSON["draft"] else {
|
||||
// get
|
||||
NSLog("error: get prerelease or draft")
|
||||
return
|
||||
}
|
||||
|
||||
// not pre release or draft
|
||||
if prerelease as! Bool == true || draft as! Bool == true {
|
||||
NSLog("this release is a prerelease or draft")
|
||||
return
|
||||
}
|
||||
|
||||
let newVer = (tag_name as! String)
|
||||
// get old version
|
||||
let oldVer = appVersion.replacingOccurrences(of: "v", with: "").versionToInt()
|
||||
let curVer = newVer.replacingOccurrences(of: "v", with: "").versionToInt()
|
||||
|
||||
// compare with [Int]
|
||||
DispatchQueue.main.async {
|
||||
if oldVer.lexicographicallyPrecedes(curVer) {
|
||||
menuController.newVersionItem.isHidden = false
|
||||
menuController.newVersionItem.title = "has new version " + newVer
|
||||
} else {
|
||||
menuController.newVersionItem.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Alamofire
|
||||
import SwiftyJSON
|
||||
import Yams
|
||||
|
||||
|
||||
Reference in New Issue
Block a user