Skip to content

Commit

Permalink
some change
Browse files Browse the repository at this point in the history
  • Loading branch information
yanue committed Dec 26, 2024
1 parent 0d808ad commit b9237ef
Show file tree
Hide file tree
Showing 13 changed files with 548 additions and 115 deletions.
11 changes: 2 additions & 9 deletions V2rayU/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import SwiftUI

@main
struct V2rayUApp: App {
@State private var windowController: NSWindowController?
@State private var aboutWindowController: NSWindowController?
@StateObject private var languageManager = LanguageManager()
@StateObject private var themeManager = ThemeManager()
@State private var forceUpdate = false // 添加一个 dummy 变量

init() {
// 已设置 Application is agent (UIElement) 为 YES
// 初始化
Expand All @@ -26,7 +20,7 @@ struct V2rayUApp: App {

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

let window = NSWindow(contentViewController: hostingController)
Expand Down
66 changes: 66 additions & 0 deletions V2rayU/AppState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import SwiftUI

enum RunMode: String {
case global
case off
case manual
case tunnel

var icon: String {
switch self {
case .global:
return "IconG"
case .off:
return "IconOff"
case .manual:
return "IconM"
case .tunnel:
return "IconT"
}
}
}

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()
}
}
}

private var _runningProfile: String = UserDefaults.get(forKey: .runningProfile) ?? ""
var runningProfile: String {
get {
_runningProfile
}
set {
_runningProfile = newValue
UserDefaults.standard.set(newValue, forKey: "runningProfile")
objectWillChange.send() // 通知更新
}
}

private var _runningRouting: String = UserDefaults.get(forKey: .runningRouting) ?? ""
var runningRouting: String {
get {
_runningRouting
}
set {
_runningRouting = newValue
UserDefaults.standard.set(newValue, forKey: "runningRouting")
objectWillChange.send() // 通知更新
}
}
}
6 changes: 4 additions & 2 deletions V2rayU/Base/UserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ extension UserDefaults {
// pacPort
case localPacPort

// routing selected rule
case routingSelectedRule
// selected routing uuid
case runningRouting
// selected profile uuid
case runningProfile
}

static func setBool(forKey key: KEY, value: Bool) {
Expand Down
22 changes: 17 additions & 5 deletions V2rayU/Database/Models/ProfileModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
uuid = try container.decode(String.self, forKey: .uuid)
speed = try container.decode(Int.self, forKey: .speed)
sort = try container.decode(Int.self, forKey: .sort)
`protocol` = try container.decode(V2rayProtocolOutbound.self, forKey: .protocol)
network = try container.decode(V2rayStreamNetwork.self, forKey: .network)
security = try container.decode(V2rayStreamSecurity.self, forKey: .security)
Expand All @@ -76,6 +78,9 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(uuid, forKey: .uuid)
try container.encode(remark, forKey: .remark)
try container.encode(speed, forKey: .speed)
try container.encode(sort, forKey: .sort)
try container.encode(`protocol`, forKey: .protocol)
try container.encode(network, forKey: .network)
try container.encode(security, forKey: .security)
Expand All @@ -85,7 +90,6 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
try container.encode(password, forKey: .password)
try container.encode(alterId, forKey: .alterId)
try container.encode(encryption, forKey: .encryption)
try container.encode(remark, forKey: .remark)
try container.encode(headerType, forKey: .headerType)
try container.encode(host, forKey: .host)
try container.encode(path, forKey: .path)
Expand All @@ -103,6 +107,8 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
init(
uuid: String = UUID().uuidString,
remark: String,
speed: Int = -1,
sort: Int = 0,
protocol: V2rayProtocolOutbound,
address: String = "",
port: Int = 0,
Expand All @@ -124,14 +130,17 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
shortId: String = "",
spiderX: String = ""
) {
self.uuid = uuid
self.speed = speed
self.sort = sort
self.remark = remark
self.protocol = `protocol`
self.address = address
self.port = port
self.password = password
self.alterId = alterId
self.encryption = encryption
self.network = network
self.remark = remark
self.headerType = headerType
self.host = host
self.path = path
Expand All @@ -145,7 +154,6 @@ class ProfileModel: ObservableObject, Identifiable, Codable {
self.publicKey = publicKey
self.shortId = shortId
self.spiderX = spiderX
self.uuid = uuid
}
}

Expand All @@ -168,6 +176,9 @@ extension ProfileModel: TableRecord, FetchableRecord, PersistableRecord {
// 定义数据库列
enum Columns {
static let uuid = Column(CodingKeys.uuid)
static let remark = Column(CodingKeys.remark)
static let speed = Column(CodingKeys.speed)
static let sort = Column(CodingKeys.sort)
static let `protocol` = Column(CodingKeys.protocol)
static let subid = Column(CodingKeys.subid)
static let address = Column(CodingKeys.address)
Expand All @@ -176,7 +187,6 @@ extension ProfileModel: TableRecord, FetchableRecord, PersistableRecord {
static let alterId = Column(CodingKeys.alterId)
static let encryption = Column(CodingKeys.encryption)
static let network = Column(CodingKeys.network)
static let remark = Column(CodingKeys.remark)
static let headerType = Column(CodingKeys.headerType)
static let host = Column(CodingKeys.host)
static let path = Column(CodingKeys.path)
Expand All @@ -197,6 +207,9 @@ extension ProfileModel: TableRecord, FetchableRecord, PersistableRecord {
migrator.registerMigration("createProfileTable") { db in
try db.create(table: ProfileModel.databaseTableName) { t in
t.column(ProfileModel.Columns.uuid.name, .text).notNull().primaryKey()
t.column(ProfileModel.Columns.remark.name, .text).notNull()
t.column(ProfileModel.Columns.speed.name, .integer).notNull()
t.column(ProfileModel.Columns.sort.name, .integer).notNull()
t.column(ProfileModel.Columns.protocol.name, .text).notNull()
t.column(ProfileModel.Columns.subid.name, .text)
t.column(ProfileModel.Columns.address.name, .text).notNull()
Expand All @@ -205,7 +218,6 @@ extension ProfileModel: TableRecord, FetchableRecord, PersistableRecord {
t.column(ProfileModel.Columns.alterId.name, .integer)
t.column(ProfileModel.Columns.encryption.name, .text)
t.column(ProfileModel.Columns.network.name, .text)
t.column(ProfileModel.Columns.remark.name, .text)
t.column(ProfileModel.Columns.headerType.name, .text)
t.column(ProfileModel.Columns.host.name, .text)
t.column(ProfileModel.Columns.path.name, .text)
Expand Down
28 changes: 28 additions & 0 deletions V2rayU/Database/ViewModels/ProfileViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,34 @@ class ProfileViewModel: ObservableObject {
}
}

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

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

func fetchOne(uuid: String) throws -> ProfileModel {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
Expand Down
66 changes: 66 additions & 0 deletions V2rayU/Database/ViewModels/RoutingViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ 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] = []
Expand All @@ -25,6 +50,47 @@ class RoutingViewModel: ObservableObject {
}
}

static func all() -> [RoutingModel] {
do {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
return try RoutingModel.fetchAll(db)
}
} catch {
print("getList error: \(error)")
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 {
let dbReader = AppDatabase.shared.reader
return try dbReader.read { db in
Expand Down
Loading

0 comments on commit b9237ef

Please sign in to comment.