diff --git a/punchnet/Views/AppContext.swift b/punchnet/Views/AppContext.swift index f1c3de0..28e4351 100644 --- a/punchnet/Views/AppContext.swift +++ b/punchnet/Views/AppContext.swift @@ -15,6 +15,17 @@ class AppContext { // 调用 "/connect" 之后的网络信息 var networkContext: NetworkContext? + var loginCredit: Credit? + var networkSession: SDLAPIClient.NetworkSession? + + // 当前app所处的场景 + var appScene: AppScene = .login(username: nil) + + enum Credit { + case token(token: String) + case accountAndPasword(account: String, password: String) + } + // 当前的场景 enum AppScene: Equatable { case login(username: String?) @@ -23,11 +34,51 @@ class AppContext { case resetPassword } - // 当前app所处的场景 - var appScene: AppScene = .login(username: nil) - init(noticePort: Int) { self.noticePort = noticePort } + + func loginWith(credit: Credit) async throws -> Bool { + switch credit { + case .token(let token): + self.networkSession = try await SDLAPIClient.loginWithToken(token: token) + // 将数据缓存到keychain + if let data = token.data(using: .utf8) { + try KeychainStore.shared.save(data, account: "token") + } + case .accountAndPasword(let username, let password): + self.networkSession = try await SDLAPIClient.loginWithAccountAndPassword(username: username, password: password) + // 将数据缓存到keychain + if let data = "\(username):\(password)".data(using: .utf8) { + try KeychainStore.shared.save(data, account: "accountAndPasword") + } + } + self.loginCredit = credit + + return true + } + + func loadCacheToken() -> String? { + if let data = try? KeychainStore.shared.load(account: "token") { + return String(data: data, encoding: .utf8) + } + return nil + } + + func loadCacheUsernameAndPassword() -> (String, String)? { + if let data = try? KeychainStore.shared.load(account: "accountAndPasword"), + let str = String(data: data, encoding: .utf8) { + let parts = str.split(separator: ":") + if parts.count == 2 { + return (String(parts[0]), String(parts[1])) + } + } + return nil + } + + // 退出登陆 + func logout() async throws { + try await VPNManager.shared.disableVpn() + } } diff --git a/punchnet/Views/Login/LoginView.swift b/punchnet/Views/Login/LoginView.swift index b0299c5..1f1daa4 100644 --- a/punchnet/Views/Login/LoginView.swift +++ b/punchnet/Views/Login/LoginView.swift @@ -9,7 +9,6 @@ import Observation // MARK: - 主容器视图 struct LoginView: View { - @Environment(UserContext.self) var userContext: UserContext @State private var authMethod: AuthMethod = .account var username: String? @@ -87,7 +86,6 @@ struct LoginView: View { // MARK: - 账户登录组件 struct LoginAccountView: View { - @Environment(UserContext.self) var userContext: UserContext @Environment(AppContext.self) var appContext: AppContext @State var username: String = "" @@ -158,7 +156,7 @@ struct LoginAccountView: View { Alert(title: Text("提示"), message: Text(self.errorMessage)) } .onAppear { - if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() { + if let (cacheUsername, cachePassword) = self.appContext.loadCacheUsernameAndPassword() { self.username = cacheUsername self.password = cachePassword } @@ -172,7 +170,7 @@ struct LoginAccountView: View { } do { - _ = try await userContext.loginWith(credit: .accountAndPasword(account: username, password: password)) + _ = try await appContext.loginWith(credit: .accountAndPasword(account: username, password: password)) withAnimation(.spring(duration: 0.6, bounce: 0.2)) { self.appContext.appScene = .logined } @@ -188,7 +186,6 @@ struct LoginAccountView: View { // MARK: - 密钥登录组件 struct LoginTokenView: View { - @Environment(UserContext.self) var userContext: UserContext @Environment(AppContext.self) var appContext: AppContext @State private var token = "" @@ -221,7 +218,7 @@ struct LoginTokenView: View { Alert(title: Text("提示"), message: Text(self.errorMessage)) } .onAppear { - if let cacheToken = self.userContext.loadCacheToken() { + if let cacheToken = self.appContext.loadCacheToken() { self.token = cacheToken } } @@ -234,7 +231,7 @@ struct LoginTokenView: View { } do { - _ = try await userContext.loginWith(credit: .token(token: token)) + _ = try await appContext.loginWith(credit: .token(token: token)) withAnimation(.spring(duration: 0.6, bounce: 0.2)) { self.appContext.appScene = .logined } @@ -312,5 +309,5 @@ struct VisualEffectView: NSViewRepresentable { #Preview { LoginView() - .environment(UserContext()) + .environment(AppContext(noticePort: 0)) } diff --git a/punchnet/Views/Network/NetworkView.swift b/punchnet/Views/Network/NetworkView.swift index c31f4f3..2a8ab9d 100644 --- a/punchnet/Views/Network/NetworkView.swift +++ b/punchnet/Views/Network/NetworkView.swift @@ -11,7 +11,6 @@ enum ConnectState { // MARK: - 主网络视图 struct NetworkView: View { - @Environment(UserContext.self) var userContext @Environment(AppContext.self) var appContext @Environment(\.openWindow) private var openWindow @@ -68,7 +67,7 @@ extension NetworkView { statusIndicator VStack(alignment: .leading, spacing: 2) { - Text(userContext.networkSession?.networkName ?? "未连接网络") + Text(appContext.networkSession?.networkName ?? "未连接网络") .font(.system(size: 14, weight: .semibold)) if connectState == .connected { @@ -186,7 +185,7 @@ extension NetworkView { isConnecting = true Task { do { - guard let session = userContext.networkSession else { + guard let session = appContext.networkSession else { return } @@ -269,7 +268,8 @@ struct NetworkNodeHeadView: View { } struct NetworkNodeDetailView: View { - @Environment(UserContext.self) var userContext + @Environment(AppContext.self) private var appContext: AppContext + var node: Node @State private var resources: [Resource] = [] @State private var isLoading = false @@ -310,7 +310,7 @@ struct NetworkNodeDetailView: View { } private func loadNodeResources(id: Int) async { - guard let session = userContext.networkSession else { + guard let session = appContext.networkSession else { return } diff --git a/punchnet/Views/RootView.swift b/punchnet/Views/RootView.swift index 56ff052..352183e 100644 --- a/punchnet/Views/RootView.swift +++ b/punchnet/Views/RootView.swift @@ -8,7 +8,6 @@ import SwiftUI struct RootView: View { - @Environment(UserContext.self) var userContext @Environment(AppContext.self) var appContext: AppContext @State private var updateManager = AppUpdateManager.shared diff --git a/punchnet/Views/Settings/SettingsAccountView.swift b/punchnet/Views/Settings/SettingsAccountView.swift index 60b5262..5093d90 100644 --- a/punchnet/Views/Settings/SettingsAccountView.swift +++ b/punchnet/Views/Settings/SettingsAccountView.swift @@ -7,7 +7,7 @@ import SwiftUI struct SettingsAccountView: View { - @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext @Environment(\.openWindow) var openWindow @Environment(\.openURL) var openURL @@ -18,7 +18,7 @@ struct SettingsAccountView: View { sectionHeader(title: "账户安全", icon: "shield.lefthalf.filled") VStack(spacing: 0) { - if let loginCredit = userContext.loginCredit { + if let loginCredit = appContext.loginCredit { switch loginCredit { case .token(let token): TokenCreditView(token: token) @@ -115,7 +115,7 @@ extension SettingsAccountView { } struct AccountCreditView: View { - @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext let username: String var body: some View { @@ -128,7 +128,7 @@ extension SettingsAccountView { Button("退出登录") { Task { @MainActor in - try await userContext.logout() + try await appContext.logout() } } .buttonStyle(.bordered) @@ -139,14 +139,14 @@ extension SettingsAccountView { } struct TokenCreditView: View { - @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext let token: String var body: some View { AccountRow(icon: "key.horizontal.fill", title: "Token 登录", subtitle: token, actions: AnyView( Button("退出登录") { Task { @MainActor in - try await userContext.logout() + try await appContext.logout() } } .buttonStyle(.bordered) diff --git a/punchnet/Views/Settings/SettingsNetworkView.swift b/punchnet/Views/Settings/SettingsNetworkView.swift index fa1362f..a1093aa 100644 --- a/punchnet/Views/Settings/SettingsNetworkView.swift +++ b/punchnet/Views/Settings/SettingsNetworkView.swift @@ -7,7 +7,7 @@ import SwiftUI struct SettingsNetworkView: View { - @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext @Environment(\.openURL) var openURL @State private var selectedExitNode: SDLAPIClient.NetworkSession.ExitNode? @@ -19,7 +19,7 @@ struct SettingsNetworkView: View { // MARK: - 网络部分 sectionHeader(title: "网络配置", icon: "network") - if let networkSession = userContext.networkSession { + if let networkSession = appContext.networkSession { VStack(spacing: 16) { HStack { VStack(alignment: .leading, spacing: 4) { @@ -113,7 +113,7 @@ struct SettingsNetworkView: View { .frame(maxWidth: 600, alignment: .leading) } .onAppear { - self.selectedExitNode = self.userContext.networkSession?.exitNodes.first + self.selectedExitNode = self.appContext.networkSession?.exitNodes.first } } diff --git a/punchnet/Views/Settings/SettingsUserIssueView.swift b/punchnet/Views/Settings/SettingsUserIssueView.swift index 167f44f..f63eed9 100644 --- a/punchnet/Views/Settings/SettingsUserIssueView.swift +++ b/punchnet/Views/Settings/SettingsUserIssueView.swift @@ -8,7 +8,7 @@ import SwiftUI // MARK: - 用户反馈页面 (完整逻辑版) struct SettingsUserIssueView: View { - @Environment(UserContext.self) var userContext: UserContext + @Environment(AppContext.self) var appContext: AppContext // 表单状态 @State private var account: String = "" @@ -129,7 +129,7 @@ struct SettingsUserIssueView: View { } let params: [String: Any] = [ - "access_token": self.userContext.networkSession?.accessToken ?? "", + "access_token": self.appContext.networkSession?.accessToken ?? "", "contact": self.account, "platform": SystemConfig.systemInfo, "content": self.text, diff --git a/punchnet/Views/UserContext.swift b/punchnet/Views/UserContext.swift deleted file mode 100644 index e0c7407..0000000 --- a/punchnet/Views/UserContext.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// LoginState.swift -// punchnet -// -// Created by 安礼成 on 2026/1/16. -// - -import Foundation -import Observation - -@Observable -class UserContext { - var loginCredit: Credit? - var networkSession: SDLAPIClient.NetworkSession? - - enum Credit { - case token(token: String) - case accountAndPasword(account: String, password: String) - } - - func loginWith(credit: Credit) async throws -> Bool { - switch credit { - case .token(let token): - self.networkSession = try await SDLAPIClient.loginWithToken(token: token) - // 将数据缓存到keychain - if let data = token.data(using: .utf8) { - try KeychainStore.shared.save(data, account: "token") - } - case .accountAndPasword(let username, let password): - self.networkSession = try await SDLAPIClient.loginWithAccountAndPassword(username: username, password: password) - // 将数据缓存到keychain - if let data = "\(username):\(password)".data(using: .utf8) { - try KeychainStore.shared.save(data, account: "accountAndPasword") - } - } - self.loginCredit = credit - - return true - } - - func loadCacheToken() -> String? { - if let data = try? KeychainStore.shared.load(account: "token") { - return String(data: data, encoding: .utf8) - } - return nil - } - - func loadCacheUsernameAndPassword() -> (String, String)? { - if let data = try? KeychainStore.shared.load(account: "accountAndPasword"), - let str = String(data: data, encoding: .utf8) { - let parts = str.split(separator: ":") - if parts.count == 2 { - return (String(parts[0]), String(parts[1])) - } - } - return nil - } - - // 退出登陆 - func logout() async throws { - try await VPNManager.shared.disableVpn() - } - -} diff --git a/punchnet/punchnetApp.swift b/punchnet/punchnetApp.swift index 06f442a..cbf8082 100644 --- a/punchnet/punchnetApp.swift +++ b/punchnet/punchnetApp.swift @@ -32,7 +32,6 @@ struct punchnetApp: App { private var noticeServer: UDPNoticeCenterServer @State private var appContext: AppContext - @State private var userContext = UserContext() @AppStorage("hasAcceptedPrivacy") var hasAcceptedPrivacy: Bool = false // UI 控制状态:是否显示弹窗 @State private var showPrivacy: Bool = false @@ -48,7 +47,6 @@ struct punchnetApp: App { RootView() .navigationTitle("") .environment(self.appContext) - .environment(self.userContext) .onAppear { self.showPrivacy = !hasAcceptedPrivacy } @@ -72,7 +70,6 @@ struct punchnetApp: App { Window("设置", id: "settings") { SettingsView() - .environment(self.userContext) .environment(self.appContext) .frame(width: 750, height: 500) }