Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
yanue committed Dec 27, 2024
1 parent b9237ef commit 9d8ca1a
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 127 deletions.
4 changes: 4 additions & 0 deletions V2rayU.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
662CC44E2D1C4D38006E8450 /* SubViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662CC44B2D1C4D38006E8450 /* SubViewModel.swift */; };
662CC4502D1C4D46006E8450 /* Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662CC44F2D1C4D46006E8450 /* Scanner.swift */; };
66A078BC2D008A3700490469 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 66A078BB2D008A3700490469 /* SwiftyBeaver */; };
66F66F832D1E450000E77C64 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F66F822D1E450000E77C64 /* AppState.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -151,6 +152,7 @@
662CC44F2D1C4D46006E8450 /* Scanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scanner.swift; sourceTree = "<group>"; };
66CBCED72C63B16400313949 /* V2rayU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2rayU.app; sourceTree = BUILT_PRODUCTS_DIR; };
66CBCEE32C63B16500313949 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
66F66F822D1E450000E77C64 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -355,6 +357,7 @@
isa = PBXGroup;
children = (
662CC3BF2D1BED9B006E8450 /* App.swift */,
66F66F822D1E450000E77C64 /* AppState.swift */,
662CC3C02D1BED9B006E8450 /* AppDelegate.swift */,
662CC3C12D1BED9B006E8450 /* AppVersion.swift */,
662CC3C22D1BED9B006E8450 /* Assets.xcassets */,
Expand Down Expand Up @@ -514,6 +517,7 @@
662CC4292D1BED9B006E8450 /* ToastWindow.swift in Sources */,
662CC42A2D1BED9B006E8450 /* V2rayOutbound.swift in Sources */,
662CC44C2D1C4D38006E8450 /* ProfileViewModel.swift in Sources */,
66F66F832D1E450000E77C64 /* AppState.swift in Sources */,
662CC44D2D1C4D38006E8450 /* RoutingViewModel.swift in Sources */,
662CC44E2D1C4D38006E8450 /* SubViewModel.swift in Sources */,
662CC42B2D1BED9B006E8450 /* ProfileForm.swift in Sources */,
Expand Down
14 changes: 9 additions & 5 deletions V2rayU/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import SwiftUI

@main
struct V2rayUApp: App {
@State var windowController: NSWindowController?
@State var aboutWindowController: NSWindowController?
@StateObject var languageManager = LanguageManager()
@StateObject var themeManager = ThemeManager()

init() {
// 已设置 Application is agent (UIElement) 为 YES
// 初始化
Expand All @@ -13,14 +18,13 @@ struct V2rayUApp: App {
print("NSHomeDirectory()",NSHomeDirectory())
print("userHomeDirectory",userHomeDirectory)
V2rayLaunch.checkInstall()
V2rayLaunch.runTun2Socks()
V2rayLaunch.runAtStart()
// V2rayLaunch.runTun2Socks()
}



var body: some Scene {
// 显示 MenuBar
MenuBarExtra("V2rayU", image: appState.shared.runMode.icon) {
MenuBarExtra("V2rayU", image: AppState.shared.runMode.icon) {
AppMenuView(openContentViewWindow: openContentViewWindow)
}.menuBarExtraStyle(.window) // 重点,按窗口显示
.environment(\.locale, languageManager.currentLocale) // 设置 Environment 的 locale
Expand All @@ -34,7 +38,7 @@ struct V2rayUApp: App {
// let contentView = ConfigView(item: item)
let contentView = ContentView()
.environment(\.locale, languageManager.currentLocale) // 设置 Environment 的 locale
.environmentObject(appState.shared)
.environmentObject(AppState.shared)
let hostingController = NSHostingController(rootView: contentView)

let window = NSWindow(contentViewController: hostingController)
Expand Down
1 change: 1 addition & 0 deletions V2rayU/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let userHomeDirectory = FileManager.default.homeDirectoryForCurrentUser.path
@MainActor let windowDelegate = WindowDelegate()

class AppDelegate: NSObject, NSApplicationDelegate {

func applicationDidFinishLaunching(_ aNotification: Notification) {
print("Application did finish launching.")

Expand Down
20 changes: 5 additions & 15 deletions V2rayU/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enum RunMode: String {
var icon: String {
switch self {
case .global:
return "IconG"
return "IconOn"
case .off:
return "IconOff"
case .manual:
Expand All @@ -20,24 +20,14 @@ enum RunMode: String {
}
}

class AppState: ObservableObject {
@MainActor
final class AppState: ObservableObject {
static let shared = AppState() // 单例实例

@Published var runMode: RunMode = .off
@Published var windowController: NSWindowController?
@Published var aboutWindowController: NSWindowController?
@Published var languageManager = LanguageManager()
@Published var themeManager = ThemeManager()

// 使用 Completion Handler 的异步设置
func setRunMode(mode: RunMode, completion: @escaping () -> Void) {
DispatchQueue.global().async {
DispatchQueue.main.async {
self.runMode = mode
UserDefaults.set(forKey: .runMode, value: mode.rawValue)
completion()
}
}
func setRunMode(mode: RunMode) async {
self.runMode = mode
}

private var _runningProfile: String = UserDefaults.get(forKey: .runningProfile) ?? ""
Expand Down
7 changes: 4 additions & 3 deletions V2rayU/Database/Models/ProfileModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
// 公共属性
@Published var uuid: String // 唯一标识
@Published var remark: String // 备注
@Published var speed: Int = -1 // 速度
@Published var sort: Int = 0 // 排序
@Published var `protocol`: V2rayProtocolOutbound // 协议
@Published var network: V2rayStreamNetwork = .tcp // 网络: tcp, kcp, ws, domainsocket, xhttp, h2, grpc, quic
@Published var security: V2rayStreamSecurity = .none // 底层传输安全加密方式: none, tls, reality
Expand Down Expand Up @@ -42,14 +44,14 @@ class ProfileModel: ObservableObject, Identifiable, Codable {

// 对应编码的 `CodingKeys` 枚举
enum CodingKeys: String, CodingKey {
case uuid, `protocol`, subid, address, port, password, alterId, encryption, network, remark,
headerType, host, path, security, allowInsecure, flow, sni, alpn, fingerprint, publicKey, shortId, spiderX
case uuid,remark,speed,sort,`protocol`, subid, address, port, password, alterId, encryption, network,headerType, host, path, security, allowInsecure, flow, sni, alpn, fingerprint, publicKey, shortId, spiderX
}

// 需要手动实现 `init(from:)` 和 `encode(to:)`,如果你使用自定义类型时
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
uuid = try container.decode(String.self, forKey: .uuid)
remark = try container.decode(String.self, forKey: .remark)
speed = try container.decode(Int.self, forKey: .speed)
sort = try container.decode(Int.self, forKey: .sort)
`protocol` = try container.decode(V2rayProtocolOutbound.self, forKey: .protocol)
Expand All @@ -61,7 +63,6 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
password = try container.decode(String.self, forKey: .password)
alterId = try container.decode(Int.self, forKey: .alterId)
encryption = try container.decode(String.self, forKey: .encryption)
remark = try container.decode(String.self, forKey: .remark)
headerType = try container.decode(V2rayHeaderType.self, forKey: .headerType)
host = try container.decode(String.self, forKey: .host)
path = try container.decode(String.self, forKey: .path)
Expand Down
44 changes: 28 additions & 16 deletions V2rayU/Database/ViewModels/ProfileViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ class ProfileViewModel: ObservableObject {
print("getList error: \(error)")
}
}

func delete(uuid: String) {
Self.delete(uuid: uuid)
self.getList()
}

func upsert(item: ProfileModel) {
Self.upsert(item: item)
self.getList()
}

/// Mark: - Static

static func all() -> [ProfileModel] {
do {
Expand All @@ -37,13 +49,13 @@ class ProfileViewModel: ObservableObject {
}

// 获取当前正在运行配置
static func getRunning() -> RoutingModel? {
var item: RoutingModel?
static func getRunning() -> ProfileModel? {
var item: ProfileModel?
// 获取当前运行配置
let runningProfile = UserDefaults.get(forKey: .runningProfile) ?? ""
if !runningProfile.isEmpty {
// 根据uuid获取配置
item = ProfileViewModel().fetchOne(uuid: runningProfile)
item = ProfileViewModel.fetchOne(uuid: runningProfile)
}
if item == nil {
// 没有配置,获取速度最快的配置
Expand All @@ -52,47 +64,47 @@ class ProfileViewModel: ObservableObject {
return item
}

static func getFastOne() -> RoutingModel? {
static func getFastOne() -> ProfileModel? {
do {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
return try RoutingModel.filter().orderBy(RoutingModel.Columns.speed, .desc).fetchOne(db)
return try ProfileModel.order(ProfileModel.Columns.speed.desc).fetchOne(db)
}
} catch {
print("getOne error: \(error)")
print("getFastOne error: \(error)")
return nil
}
}

func fetchOne(uuid: String) throws -> ProfileModel {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
guard let model = try ProfileModel.filter(ProfileModel.Columns.uuid == uuid).fetchOne(db) else {
throw NSError(domain: "ProfileViewModel", code: 404, userInfo: [NSLocalizedDescriptionKey: "ProfileModel not found for uuid: \(uuid)"])
static func fetchOne(uuid: String) -> ProfileModel? {
do {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
return try ProfileModel.filter(ProfileModel.Columns.uuid == uuid).fetchOne(db)
}
return model
} catch {
print("fetchOne error: \(error)")
return nil
}
}

func delete(uuid: String) {
static func delete(uuid: String) {
do {
let dbWriter = AppDatabase.shared.dbWriter
try dbWriter.write { db in
try ProfileModel.filter(ProfileModel.Columns.uuid == uuid).deleteAll(db)
}
getList()
} catch {
print("delete error: \(error)")
}
}

func upsert(item: ProfileModel) {
static func upsert(item: ProfileModel) {
do {
let dbWriter = AppDatabase.shared.dbWriter
try dbWriter.write { db in
try item.save(db)
}
getList()
} catch {
print("upsert error: \(error)")
}
Expand Down
86 changes: 21 additions & 65 deletions V2rayU/Database/ViewModels/RoutingViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,10 @@ import Combine
import GRDB
import Foundation

let RoutingRuleGlobal = "routing.global"
let RoutingRuleLAN = "routing.lan"
let RoutingRuleCn = "routing.cn"
let RoutingRuleLANAndCn = "routing.lanAndCn"

let defaultRuleCn = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal, "🌏 全局"),
(RoutingRuleLAN, "🌏 绕过局域网"),
(RoutingRuleCn, "🌏 绕过中国大陆"),
(RoutingRuleLANAndCn, "🌏 绕过局域网和中国大陆"),
])

let defaultRuleEn = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal, "🌏 Global"),
(RoutingRuleLAN, "🌏 Bypassing the LAN Address"),
(RoutingRuleCn, "🌏 Bypassing mainland address"),
(RoutingRuleLANAndCn, "🌏 Bypassing LAN and mainland address"),
])

let defaultRules = Dictionary(uniqueKeysWithValues: [
(RoutingRuleGlobal, RoutingModel(name: RoutingRuleGlobal, remark: "")),
(RoutingRuleLAN, RoutingModel(name: RoutingRuleLAN, remark: "")),
(RoutingRuleCn, RoutingModel(name: RoutingRuleCn, remark: "")),
(RoutingRuleLANAndCn, RoutingModel(name: RoutingRuleLANAndCn, remark: "")),
])

class RoutingViewModel: ObservableObject {

@Published var list: [RoutingModel] = []

func getList() {
do {
let dbReader = AppDatabase.shared.reader
Expand All @@ -49,7 +24,19 @@ class RoutingViewModel: ObservableObject {
print("getList error: \(error)")
}
}


func delete(uuid: String) {
Self.delete(uuid: uuid)
self.getList()
}

func upsert(item: RoutingModel) {
Self.upsert(item: item)
self.getList()
}

/// Mark: - Static

static func all() -> [RoutingModel] {
do {
let dbReader = AppDatabase.shared.reader
Expand All @@ -61,37 +48,8 @@ class RoutingViewModel: ObservableObject {
return []
}
}

// 获取正在运行路由规则, 优先级: 用户选择 > 默认规则
static func getRunning() -> V2rayRouting {
// 查询当前使用的规则
let runningRouting = UserDefaults.get(forKey: .runningRouting)
// 查询所有规则
let all = RoutingViewModel.all()
// 如果没有规则,则创建默认规则
if all.count == 0 {
for (_, item) in defaultRules {
RoutingViewModel.upsert(item)
// 添加到 all
all.append(item)
}
}
for item in all {
// 如果匹配到选中的规则,则返回
if item.uuid == runningRouting {
let handler = RoutingHandler(from: item)
return handler.getRouting()
}
}
let defaultRouting = defaultRules[RoutingRuleLANAndCn]!
// 如果没有匹配到选中的规则,则返回默认规则
let handler = RoutingHandler(from: defaultRouting)
// 设置默认规则
UserDefaults.set(forKey: .runningRouting, value: defaultRouting.uuid)
return handler.getRouting()
}

func fetchOne(uuid: String) throws -> RoutingModel {

static func fetchOne(uuid: String) throws -> RoutingModel {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
guard let model = try RoutingModel.filter(RoutingModel.Columns.uuid == uuid).fetchOne(db) else {
Expand All @@ -100,26 +58,24 @@ class RoutingViewModel: ObservableObject {
return model
}
}

func delete(uuid: String) {
static func delete(uuid: String) {
do {
let dbWriter = AppDatabase.shared.dbWriter
try dbWriter.write { db in
try RoutingModel.filter(RoutingModel.Columns.uuid == uuid).deleteAll(db)
}
getList()
} catch {
print("delete error: \(error)")
}
}

func upsert(item: RoutingModel) {
static func upsert(item: RoutingModel) {
do {
let dbWriter = AppDatabase.shared.dbWriter
try dbWriter.write { db in
try item.save(db)
}
getList()
} catch {
print("upsert error: \(error)")
}
Expand Down
Loading

0 comments on commit 9d8ca1a

Please sign in to comment.