简化环境逻辑
This commit is contained in:
parent
1ffea953bf
commit
c8c37954ce
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user