fix flow
This commit is contained in:
parent
aca4bf1ec2
commit
883f9d7f64
@ -15,6 +15,17 @@ class AppContext {
|
|||||||
// 调用 "/connect" 之后的网络信息
|
// 调用 "/connect" 之后的网络信息
|
||||||
var networkContext: NetworkContext?
|
var networkContext: NetworkContext?
|
||||||
|
|
||||||
|
// 当前的场景
|
||||||
|
enum AppScene {
|
||||||
|
case login
|
||||||
|
case logined
|
||||||
|
case register
|
||||||
|
case resetPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前app所处的场景
|
||||||
|
var appScene: AppScene = .login
|
||||||
|
|
||||||
init(noticePort: Int) {
|
init(noticePort: Int) {
|
||||||
self.noticePort = noticePort
|
self.noticePort = noticePort
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,12 +86,16 @@ struct LoginView: View {
|
|||||||
// MARK: - 账户登录组件
|
// MARK: - 账户登录组件
|
||||||
struct LoginAccountView: View {
|
struct LoginAccountView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
@Environment(\.openWindow) private var openWindow
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
|
||||||
@State private var username = ""
|
@State private var username = ""
|
||||||
@State private var password = ""
|
@State private var password = ""
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
|
|
||||||
|
// 错误提示
|
||||||
|
@State private var showAlert: Bool = false
|
||||||
|
@State private var errorMessage: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
@ -103,14 +107,18 @@ struct LoginAccountView: View {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button("注册") {
|
Button("注册") {
|
||||||
openWindow(id: "register")
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
|
self.appContext.appScene = .register
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.link)
|
.buttonStyle(.link)
|
||||||
.font(.system(size: 11))
|
.font(.system(size: 11))
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
Button("忘记密码?") {
|
Button("忘记密码?") {
|
||||||
openWindow(id: "resetPassword")
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
|
self.appContext.appScene = .resetPassword
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.link)
|
.buttonStyle(.link)
|
||||||
.font(.system(size: 11))
|
.font(.system(size: 11))
|
||||||
@ -121,7 +129,11 @@ struct LoginAccountView: View {
|
|||||||
.frame(width: 280)
|
.frame(width: 280)
|
||||||
|
|
||||||
// 蓝色主按钮
|
// 蓝色主按钮
|
||||||
Button(action: { self.login() }) {
|
Button(action: {
|
||||||
|
Task { @MainActor in
|
||||||
|
await self.login()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
@ -140,6 +152,9 @@ struct LoginAccountView: View {
|
|||||||
.keyboardShortcut(.defaultAction) // 绑定回车键
|
.keyboardShortcut(.defaultAction) // 绑定回车键
|
||||||
.disabled(username.isEmpty || password.isEmpty || isLoading)
|
.disabled(username.isEmpty || password.isEmpty || isLoading)
|
||||||
}
|
}
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() {
|
if let (cacheUsername, cachePassword) = self.userContext.loadCacheUsernameAndPassword() {
|
||||||
self.username = cacheUsername
|
self.username = cacheUsername
|
||||||
@ -148,11 +163,20 @@ struct LoginAccountView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func login() {
|
private func login() async {
|
||||||
isLoading = true
|
self.isLoading = true
|
||||||
Task {
|
defer {
|
||||||
try? await userContext.loginWithAccountAndPassword(username: username, password: password)
|
self.isLoading = false
|
||||||
isLoading = false
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
_ = try await userContext.loginWithAccountAndPassword(username: username, password: password)
|
||||||
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
|
self.appContext.appScene = .logined
|
||||||
|
}
|
||||||
|
} catch let err {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.localizedDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,16 +184,24 @@ struct LoginAccountView: View {
|
|||||||
// MARK: - 密钥登录组件
|
// MARK: - 密钥登录组件
|
||||||
struct LoginTokenView: View {
|
struct LoginTokenView: View {
|
||||||
@Environment(UserContext.self) var userContext: UserContext
|
@Environment(UserContext.self) var userContext: UserContext
|
||||||
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
|
||||||
@State private var token = ""
|
@State private var token = ""
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
|
|
||||||
|
// 错误提示
|
||||||
|
@State private var showAlert: Bool = false
|
||||||
|
@State private var errorMessage: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
CustomTextField(title: "请输入认证密钥 (Token)", text: $token, icon: "key.fill")
|
CustomTextField(title: "请输入认证密钥 (Token)", text: $token, icon: "key.fill")
|
||||||
.frame(width: 280)
|
.frame(width: 280)
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
self.login()
|
Task { @MainActor in
|
||||||
|
await self.login()
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("验证并连接")
|
Text("验证并连接")
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
@ -180,6 +212,9 @@ struct LoginTokenView: View {
|
|||||||
.frame(width: 280)
|
.frame(width: 280)
|
||||||
.disabled(token.isEmpty || isLoading)
|
.disabled(token.isEmpty || isLoading)
|
||||||
}
|
}
|
||||||
|
.alert(isPresented: $showAlert) {
|
||||||
|
Alert(title: Text("提示"), message: Text(self.errorMessage))
|
||||||
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let cacheToken = self.userContext.loadCacheToken() {
|
if let cacheToken = self.userContext.loadCacheToken() {
|
||||||
self.token = cacheToken
|
self.token = cacheToken
|
||||||
@ -187,11 +222,20 @@ struct LoginTokenView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func login() {
|
private func login() async {
|
||||||
isLoading = true
|
self.isLoading = true
|
||||||
Task {
|
defer {
|
||||||
try? await userContext.loginWithToken(token: token)
|
self.isLoading = false
|
||||||
isLoading = false
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
_ = try await userContext.loginWithToken(token: token)
|
||||||
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
|
self.appContext.appScene = .logined
|
||||||
|
}
|
||||||
|
} catch let err {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.localizedDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ struct RegisterRootView: View {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
.environment(registerModel)
|
.environment(registerModel)
|
||||||
|
.frame(width: 500, height: 400)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ struct ResetPasswordRootView: View {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
.environment(resetPasswordModel)
|
.environment(resetPasswordModel)
|
||||||
|
.frame(width: 500, height: 400)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,18 +9,29 @@ import SwiftUI
|
|||||||
|
|
||||||
struct RootView: View {
|
struct RootView: View {
|
||||||
@Environment(UserContext.self) var userContext
|
@Environment(UserContext.self) var userContext
|
||||||
|
@Environment(AppContext.self) var appContext: AppContext
|
||||||
|
|
||||||
@State private var updateManager = AppUpdateManager.shared
|
@State private var updateManager = AppUpdateManager.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// 主要界面
|
// 主要界面
|
||||||
Group {
|
Group {
|
||||||
if userContext.isLogined {
|
switch appContext.appScene {
|
||||||
NetworkView()
|
case .login:
|
||||||
} else {
|
|
||||||
LoginView()
|
LoginView()
|
||||||
|
case .logined:
|
||||||
|
NetworkView()
|
||||||
|
case .register:
|
||||||
|
RegisterRootView()
|
||||||
|
case .resetPassword:
|
||||||
|
ResetPasswordRootView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.transition(.asymmetric(
|
||||||
|
insertion: .move(edge: .trailing).combined(with: .opacity),
|
||||||
|
removal: .move(edge: .trailing).combined(with: .opacity)
|
||||||
|
))
|
||||||
|
|
||||||
// 自动更新遮罩
|
// 自动更新遮罩
|
||||||
if updateManager.showUpdateOverlay, let info = updateManager.updateInfo {
|
if updateManager.showUpdateOverlay, let info = updateManager.updateInfo {
|
||||||
|
|||||||
@ -18,7 +18,7 @@ struct SettingsAccountView: View {
|
|||||||
sectionHeader(title: "账户安全", icon: "shield.lefthalf.filled")
|
sectionHeader(title: "账户安全", icon: "shield.lefthalf.filled")
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if userContext.isLogined, let loginCredit = userContext.loginCredit {
|
if let loginCredit = userContext.loginCredit {
|
||||||
switch loginCredit {
|
switch loginCredit {
|
||||||
case .token(let token):
|
case .token(let token):
|
||||||
TokenCreditView(token: token)
|
TokenCreditView(token: token)
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import Observation
|
|||||||
|
|
||||||
@Observable
|
@Observable
|
||||||
class UserContext {
|
class UserContext {
|
||||||
var isLogined: Bool = false
|
|
||||||
var loginCredit: Credit?
|
var loginCredit: Credit?
|
||||||
var networkSession: NetworkSession?
|
var networkSession: NetworkSession?
|
||||||
|
|
||||||
@ -63,7 +62,7 @@ class UserContext {
|
|||||||
]
|
]
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func loginWithAccountAndPassword(username: String, password: String) async throws {
|
func loginWithAccountAndPassword(username: String, password: String) async throws -> Bool {
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
@ -74,16 +73,17 @@ class UserContext {
|
|||||||
|
|
||||||
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/login", params: params, as: NetworkSession.self)
|
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/login", params: params, as: NetworkSession.self)
|
||||||
self.loginCredit = .accountAndPasword(account: username, password: password)
|
self.loginCredit = .accountAndPasword(account: username, password: password)
|
||||||
self.isLogined = true
|
|
||||||
|
|
||||||
// 将数据缓存到keychain
|
// 将数据缓存到keychain
|
||||||
if let data = "\(username):\(password)".data(using: .utf8) {
|
if let data = "\(username):\(password)".data(using: .utf8) {
|
||||||
try KeychainStore.shared.save(data, account: "accountAndPasword")
|
try KeychainStore.shared.save(data, account: "accountAndPasword")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
func loginWithToken(token: String) async throws {
|
func loginWithToken(token: String) async throws -> Bool {
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"token": token,
|
"token": token,
|
||||||
"system": SystemConfig.systemInfo,
|
"system": SystemConfig.systemInfo,
|
||||||
@ -93,42 +93,43 @@ class UserContext {
|
|||||||
|
|
||||||
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/token", params: params, as: NetworkSession.self)
|
self.networkSession = try await SDLAPIClient.doPost(path: "/auth/token", params: params, as: NetworkSession.self)
|
||||||
self.loginCredit = .token(token: token)
|
self.loginCredit = .token(token: token)
|
||||||
self.isLogined = true
|
|
||||||
|
|
||||||
// 将数据缓存到keychain
|
// 将数据缓存到keychain
|
||||||
if let data = token.data(using: .utf8) {
|
if let data = token.data(using: .utf8) {
|
||||||
try KeychainStore.shared.save(data, account: "token")
|
try KeychainStore.shared.save(data, account: "token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendVerifyCode(username: String) async throws -> String {
|
// func sendVerifyCode(username: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
// var params: [String: Any] = [
|
||||||
"username": username
|
// "username": username
|
||||||
]
|
// ]
|
||||||
params.merge(baseParams) {$1}
|
// params.merge(baseParams) {$1}
|
||||||
|
//
|
||||||
return try await SDLAPIClient.doPost(path: "/auth/sendVerifyCode", params: params, as: String.self)
|
// return try await SDLAPIClient.doPost(path: "/auth/sendVerifyCode", params: params, as: String.self)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func submitVerifyCode(username: String, verifyCode: String) async throws -> String {
|
// func submitVerifyCode(username: String, verifyCode: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
// var params: [String: Any] = [
|
||||||
"username": username,
|
// "username": username,
|
||||||
"verify_code": verifyCode,
|
// "verify_code": verifyCode,
|
||||||
]
|
// ]
|
||||||
params.merge(baseParams) {$1}
|
// params.merge(baseParams) {$1}
|
||||||
|
//
|
||||||
return try await SDLAPIClient.doPost(path: "/auth/submitVerifyCode", params: params, as: String.self)
|
// return try await SDLAPIClient.doPost(path: "/auth/submitVerifyCode", params: params, as: String.self)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func resetPassword(username: String, password: String) async throws -> String {
|
// func resetPassword(username: String, password: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
// var params: [String: Any] = [
|
||||||
"username": username,
|
// "username": username,
|
||||||
"password": password,
|
// "password": password,
|
||||||
]
|
// ]
|
||||||
params.merge(baseParams) {$1}
|
// params.merge(baseParams) {$1}
|
||||||
|
//
|
||||||
return try await SDLAPIClient.doPost(path: "/auth/resetPassword", params: params, as: String.self)
|
// return try await SDLAPIClient.doPost(path: "/auth/resetPassword", params: params, as: String.self)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func loadCacheToken() -> String? {
|
func loadCacheToken() -> String? {
|
||||||
if let data = try? KeychainStore.shared.load(account: "token") {
|
if let data = try? KeychainStore.shared.load(account: "token") {
|
||||||
@ -151,7 +152,6 @@ class UserContext {
|
|||||||
// 退出登陆
|
// 退出登陆
|
||||||
func logout() async throws {
|
func logout() async throws {
|
||||||
try await VPNManager.shared.disableVpn()
|
try await VPNManager.shared.disableVpn()
|
||||||
self.isLogined = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,7 @@ struct punchnetApp: App {
|
|||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup(id: "main") {
|
WindowGroup(id: "main") {
|
||||||
// RootView()
|
RootView()
|
||||||
SettingsView()
|
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.environment(self.appContext)
|
.environment(self.appContext)
|
||||||
.environment(self.userContext)
|
.environment(self.userContext)
|
||||||
@ -80,23 +79,23 @@ struct punchnetApp: App {
|
|||||||
.windowResizability(.contentSize)
|
.windowResizability(.contentSize)
|
||||||
.defaultPosition(.center)
|
.defaultPosition(.center)
|
||||||
|
|
||||||
Window("重置密码", id: "resetPassword") {
|
// Window("重置密码", id: "resetPassword") {
|
||||||
ResetPasswordRootView()
|
// ResetPasswordRootView()
|
||||||
.environment(self.userContext)
|
// .environment(self.userContext)
|
||||||
.environment(self.appContext)
|
// .environment(self.appContext)
|
||||||
.frame(width: 500, height: 400)
|
// .frame(width: 500, height: 400)
|
||||||
}
|
// }
|
||||||
.windowResizability(.contentSize)
|
// .windowResizability(.contentSize)
|
||||||
.defaultPosition(.center)
|
// .defaultPosition(.center)
|
||||||
|
//
|
||||||
Window("注册", id: "register") {
|
// Window("注册", id: "register") {
|
||||||
RegisterRootView()
|
// RegisterRootView()
|
||||||
.environment(self.userContext)
|
// .environment(self.userContext)
|
||||||
.environment(self.appContext)
|
// .environment(self.appContext)
|
||||||
.frame(width: 500, height: 400)
|
// .frame(width: 500, height: 400)
|
||||||
}
|
// }
|
||||||
.windowResizability(.contentSize)
|
// .windowResizability(.contentSize)
|
||||||
.defaultPosition(.center)
|
// .defaultPosition(.center)
|
||||||
//
|
//
|
||||||
// MenuBarExtra("punchnet", image: "logo_32") {
|
// MenuBarExtra("punchnet", image: "logo_32") {
|
||||||
// VStack {
|
// VStack {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user