完善注册流程
This commit is contained in:
parent
0f98a356b7
commit
9727fd1096
@ -34,12 +34,19 @@ struct SDLAPIClient {
|
|||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
let (data, _) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
if let response = String(bytes: data, encoding: .utf8) {
|
if let response = String(bytes: data, encoding: .utf8) {
|
||||||
NSLog("response is: \(response)")
|
NSLog("url: \(path), response is: \(response)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let apiResponse = try JSONDecoder().decode(SDLAPIResponse<T>.self, from: data)
|
let apiResponse = try JSONDecoder().decode(SDLAPIResponse<T>.self, from: data)
|
||||||
if apiResponse.code == 0, let data = apiResponse.data {
|
// code = 0 表示成功
|
||||||
return data
|
if apiResponse.code == 0 {
|
||||||
|
if let data = apiResponse.data {
|
||||||
|
return data
|
||||||
|
} else if let data = apiResponse.message as? T {
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
throw SDLAPIError(code: 0, message: "数据格式错误")
|
||||||
|
}
|
||||||
} else if let message = apiResponse.message {
|
} else if let message = apiResponse.message {
|
||||||
throw SDLAPIError(code: apiResponse.code, message: message)
|
throw SDLAPIError(code: apiResponse.code, message: message)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
98
punchnet/Views/Privacy/PrivacyDetailView.swift
Normal file
98
punchnet/Views/Privacy/PrivacyDetailView.swift
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// PrivacyDetailView.swift
|
||||||
|
// punchnet
|
||||||
|
//
|
||||||
|
// Created by 安礼成 on 2026/3/20.
|
||||||
|
//
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - 2. 隐私政策主容器
|
||||||
|
struct PrivacyDetailView: View {
|
||||||
|
@Environment(\.dismiss) var dismiss
|
||||||
|
@AppStorage("hasAcceptedPrivacy") var hasAcceptedPrivacy: Bool = false
|
||||||
|
@Binding var showPrivacy: Bool
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
@State private var loadingProgress: Double = 0.0
|
||||||
|
@State private var isPageLoading: Bool = true
|
||||||
|
|
||||||
|
let privacyURL = URL(string: "https://www.baidu.com")! // 替换为真实地址
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
// MARK: 自定义导航栏
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
Text("隐私政策与服务条款")
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
Text("由 PunchNet 加密传输")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if isPageLoading {
|
||||||
|
ProgressView()
|
||||||
|
.controlSize(.small)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(action: { dismiss() }) {
|
||||||
|
Image(systemName: "xmark.circle.fill")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.font(.title3)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(.ultraThinMaterial)
|
||||||
|
|
||||||
|
// MARK: 线性进度条
|
||||||
|
if isPageLoading {
|
||||||
|
ProgressView(value: loadingProgress, total: 1.0)
|
||||||
|
.progressViewStyle(.linear)
|
||||||
|
.tint(.blue)
|
||||||
|
.frame(height: 2)
|
||||||
|
.transition(.opacity)
|
||||||
|
} else {
|
||||||
|
Divider().frame(height: 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: WebView 内容
|
||||||
|
PunchNetWebView(url: privacyURL, progress: $loadingProgress, isLoading: $isPageLoading)
|
||||||
|
.background(Color(NSColor.windowBackgroundColor))
|
||||||
|
|
||||||
|
// MARK: 底部同意栏
|
||||||
|
VStack(spacing: 12) {
|
||||||
|
Divider()
|
||||||
|
HStack {
|
||||||
|
Text("继续使用即表示您同意我们的全部条款。")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button("拒绝") {
|
||||||
|
self.showPrivacy = false
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
NSApplication.shared.terminate(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
|
||||||
|
Button("同意并继续") {
|
||||||
|
hasAcceptedPrivacy = true
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.tint(.blue)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 16)
|
||||||
|
}
|
||||||
|
.background(.ultraThinMaterial)
|
||||||
|
}
|
||||||
|
.frame(minWidth: 600, minHeight: 700)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
punchnet/Views/Privacy/PunchNetWebView.swift
Normal file
63
punchnet/Views/Privacy/PunchNetWebView.swift
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// PunchNetWebView.swift
|
||||||
|
// punchnet
|
||||||
|
//
|
||||||
|
// Created by 安礼成 on 2026/3/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
// MARK: - 1. 核心 WebView 包装器 (带进度监听)
|
||||||
|
struct PunchNetWebView: NSViewRepresentable {
|
||||||
|
let url: URL
|
||||||
|
@Binding var progress: Double
|
||||||
|
@Binding var isLoading: Bool
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> WKWebView {
|
||||||
|
let webView = WKWebView()
|
||||||
|
webView.navigationDelegate = context.coordinator
|
||||||
|
|
||||||
|
// 添加进度监听 (KVO)
|
||||||
|
context.coordinator.observation = webView.observe(\.estimatedProgress, options: [.new]) { wv, _ in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.progress = wv.estimatedProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = URLRequest(url: url)
|
||||||
|
webView.load(request)
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSView(_ nsView: WKWebView, context: Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 协调器处理加载状态
|
||||||
|
class Coordinator: NSObject, WKNavigationDelegate {
|
||||||
|
var parent: PunchNetWebView
|
||||||
|
var observation: NSKeyValueObservation?
|
||||||
|
|
||||||
|
init(_ parent: PunchNetWebView) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
|
DispatchQueue.main.async { self.parent.isLoading = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
self.parent.isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ class RegisterModel {
|
|||||||
case requestVerifyCode(username: String?)
|
case requestVerifyCode(username: String?)
|
||||||
case submitVerifyCode(username: String, sessionId: Int)
|
case submitVerifyCode(username: String, sessionId: Int)
|
||||||
case setPassword(sessionId: Int)
|
case setPassword(sessionId: Int)
|
||||||
|
case success
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册会话信息
|
// 注册会话信息
|
||||||
@ -44,7 +45,7 @@ class RegisterModel {
|
|||||||
return try await SDLAPIClient.doPost(path: "/register/sendVerfiyCode", params: params, as: RegisterSession.self)
|
return try await SDLAPIClient.doPost(path: "/register/sendVerfiyCode", params: params, as: RegisterSession.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func submitVerifyCode(sessionId: Int, verifyCode: Int) async throws -> String {
|
func submitVerifyCode(sessionId: Int, verifyCode: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"session_id": sessionId,
|
"session_id": sessionId,
|
||||||
"code": verifyCode,
|
"code": verifyCode,
|
||||||
|
|||||||
@ -25,6 +25,8 @@ struct RegisterRootView: View {
|
|||||||
RegisterSubmitVerifyCodeView(username: username, sessionId: sessionId)
|
RegisterSubmitVerifyCodeView(username: username, sessionId: sessionId)
|
||||||
case .setPassword(let sessionId):
|
case .setPassword(let sessionId):
|
||||||
RegisterSetPasswordView(sessionId: sessionId)
|
RegisterSetPasswordView(sessionId: sessionId)
|
||||||
|
case .success:
|
||||||
|
RegisterSuccessView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.transition(.asymmetric(
|
.transition(.asymmetric(
|
||||||
@ -126,9 +128,12 @@ struct RegisterRequestVerifyCodeView: View {
|
|||||||
self.registerModel.stage = .submitVerifyCode(username: username, sessionId: registerSession.sessionId)
|
self.registerModel.stage = .submitVerifyCode(username: username, sessionId: registerSession.sessionId)
|
||||||
self.registerModel.transitionEdge = .trailing
|
self.registerModel.transitionEdge = .trailing
|
||||||
}
|
}
|
||||||
} catch {
|
} catch let err as SDLAPIError {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = err.message
|
||||||
|
} catch let err {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.localizedDescription
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
@ -161,7 +166,7 @@ struct RegisterSubmitVerifyCodeView: View {
|
|||||||
|
|
||||||
// 判断验证码是否正确
|
// 判断验证码是否正确
|
||||||
var validInputCode: Bool {
|
var validInputCode: Bool {
|
||||||
return !self.code.isEmpty && self.code.count == 4 && self.code.allSatisfy {$0.isNumber}
|
return !self.code.isEmpty && self.code.count == 6 && self.code.allSatisfy {$0.isNumber}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -169,7 +174,7 @@ struct RegisterSubmitVerifyCodeView: View {
|
|||||||
headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)")
|
headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)")
|
||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 16) {
|
VStack(alignment: .trailing, spacing: 16) {
|
||||||
PunchTextField(icon: "envelope.badge", placeholder: "输入 4 位验证码", text: $code)
|
PunchTextField(icon: "envelope.badge", placeholder: "输入 6 位验证码", text: $code)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
self.resendVerifyCodeAction()
|
self.resendVerifyCodeAction()
|
||||||
@ -250,15 +255,19 @@ struct RegisterSubmitVerifyCodeView: View {
|
|||||||
self.isProcessing = true
|
self.isProcessing = true
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
do {
|
do {
|
||||||
_ = try await self.registerModel.submitVerifyCode(sessionId: sessionId, verifyCode: Int(self.code) ?? 0)
|
_ = try await self.registerModel.submitVerifyCode(sessionId: sessionId, verifyCode: self.code)
|
||||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
self.registerModel.stage = .setPassword(sessionId: sessionId)
|
self.registerModel.stage = .setPassword(sessionId: sessionId)
|
||||||
self.registerModel.transitionEdge = .trailing
|
self.registerModel.transitionEdge = .trailing
|
||||||
}
|
}
|
||||||
} catch {
|
} catch let err as SDLAPIError {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = err.message
|
||||||
|
} catch let err {
|
||||||
|
self.showAlert = true
|
||||||
|
self.errorMessage = err.localizedDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isProcessing = false
|
self.isProcessing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,8 +346,13 @@ struct RegisterSetPasswordView: View {
|
|||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
do {
|
do {
|
||||||
_ = try await self.registerModel.register(sessionId: sessionId, password: self.password)
|
_ = try await self.registerModel.register(sessionId: sessionId, password: self.password)
|
||||||
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
|
self.registerModel.stage = .success
|
||||||
|
self.registerModel.transitionEdge = .trailing
|
||||||
|
}
|
||||||
|
} catch let err as SDLAPIError {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = "注册成功"
|
self.errorMessage = err.message
|
||||||
} catch {
|
} catch {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = "注册失败,重稍后重试"
|
self.errorMessage = "注册失败,重稍后重试"
|
||||||
@ -349,6 +363,88 @@ struct RegisterSetPasswordView: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: 第四步 注册成功
|
||||||
|
struct RegisterSuccessView: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss // 获取关闭窗口的能力
|
||||||
|
|
||||||
|
// MARK: - 动画状态
|
||||||
|
@State private var animateIcon: Bool = false // 用于呼吸灯效果
|
||||||
|
@State private var animateText: Bool = false // 用于文本延迟浮现
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 32) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// MARK: - 成功动画图标 (带呼吸灯效果)
|
||||||
|
ZStack {
|
||||||
|
// 外层呼吸圈
|
||||||
|
Circle()
|
||||||
|
.fill(Color.green.opacity(0.1))
|
||||||
|
.frame(width: 100, height: 100)
|
||||||
|
// 呼吸逻辑:缩放和透明度变化
|
||||||
|
.scaleEffect(animateIcon ? 1.1 : 0.95)
|
||||||
|
.opacity(animateIcon ? 0.8 : 1.0)
|
||||||
|
|
||||||
|
// 内层 Checkmark
|
||||||
|
Image(systemName: "checkmark.seal.fill")
|
||||||
|
.font(.system(size: 56))
|
||||||
|
.foregroundStyle(.green.gradient)
|
||||||
|
// Checkmark 本身做轻微缩放
|
||||||
|
.scaleEffect(animateIcon ? 1.05 : 1.0)
|
||||||
|
}
|
||||||
|
// 整个图标组的弹出动画
|
||||||
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||||
|
|
||||||
|
// MARK: - 文本和按钮
|
||||||
|
VStack(spacing: 32) {
|
||||||
|
VStack(spacing: 12) {
|
||||||
|
Text("注册成功")
|
||||||
|
.font(.title.bold())
|
||||||
|
|
||||||
|
Text("您的 PunchNet 账号已就绪。\n现在可以登录并开始构建您的私有网络了。")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
// 关闭当前注册窗口
|
||||||
|
dismiss()
|
||||||
|
}) {
|
||||||
|
Text("立即开始使用")
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.frame(width: 200)
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.controlSize(.large)
|
||||||
|
.tint(.green)
|
||||||
|
}
|
||||||
|
// 文本和按钮比图标晚一点出现,形成层次感
|
||||||
|
.opacity(animateText ? 1.0 : 0.0)
|
||||||
|
.offset(y: animateText ? 0 : 20)
|
||||||
|
}
|
||||||
|
.padding(40)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background(Color(NSColor.windowBackgroundColor)) // 确保背景干净
|
||||||
|
.onAppear {
|
||||||
|
self.startAnimations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 动画逻辑
|
||||||
|
private func startAnimations() {
|
||||||
|
// 1. 图标呼吸灯:无限循环,缓入缓出
|
||||||
|
withAnimation(.easeInOut(duration: 1.5).repeatForever(autoreverses: true)) {
|
||||||
|
animateIcon = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 文本延迟弹出:延迟 0.4 秒,使用弹簧效果
|
||||||
|
withAnimation(.spring(duration: 0.6, bounce: 0.3).delay(0.4)) {
|
||||||
|
animateText = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - 辅助视图
|
// MARK: - 辅助视图
|
||||||
extension View {
|
extension View {
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class ResetPasswordModel {
|
|||||||
return try await SDLAPIClient.doPost(path: "/password/sendVerfiyCode", params: params, as: ResetPasswordSession.self)
|
return try await SDLAPIClient.doPost(path: "/password/sendVerfiyCode", params: params, as: ResetPasswordSession.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func submitVerifyCode(sessionId: Int, verifyCode: Int) async throws -> String {
|
func submitVerifyCode(sessionId: Int, verifyCode: String) async throws -> String {
|
||||||
var params: [String: Any] = [
|
var params: [String: Any] = [
|
||||||
"session_id": sessionId,
|
"session_id": sessionId,
|
||||||
"code": verifyCode,
|
"code": verifyCode,
|
||||||
|
|||||||
@ -109,7 +109,7 @@ struct SubmitVerifyCodeView: View {
|
|||||||
@State private var errorMessage = ""
|
@State private var errorMessage = ""
|
||||||
|
|
||||||
var validInputCode: Bool {
|
var validInputCode: Bool {
|
||||||
return !self.code.isEmpty && self.code.count == 4 && self.code.allSatisfy {$0.isNumber}
|
return !self.code.isEmpty && self.code.count == 6 && self.code.allSatisfy {$0.isNumber}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -117,7 +117,7 @@ struct SubmitVerifyCodeView: View {
|
|||||||
headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)")
|
headerSection(title: "身份验证", subtitle: "验证码已发送至 \(username)")
|
||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 16) {
|
VStack(alignment: .trailing, spacing: 16) {
|
||||||
PunchTextField(icon: "envelope.badge", placeholder: "输入 4 位验证码", text: $code)
|
PunchTextField(icon: "envelope.badge", placeholder: "输入 6 位验证码", text: $code)
|
||||||
|
|
||||||
Button(isResendEnabled ? "重新获取" : "重新获取 (\(remainingSeconds)s)") {
|
Button(isResendEnabled ? "重新获取" : "重新获取 (\(remainingSeconds)s)") {
|
||||||
self.resendAction()
|
self.resendAction()
|
||||||
@ -183,13 +183,14 @@ struct SubmitVerifyCodeView: View {
|
|||||||
self.isProcessing = true
|
self.isProcessing = true
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
do {
|
do {
|
||||||
let result = try await resetPasswordModel.submitVerifyCode(sessionId: sessionId, verifyCode: Int(code) ?? 0)
|
let result = try await resetPasswordModel.submitVerifyCode(sessionId: sessionId, verifyCode: code)
|
||||||
|
NSLog("reset password submit verify code result: \(result)")
|
||||||
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
|
||||||
self.resetPasswordModel.stage = .resetPassword(username: username, sessionId: sessionId)
|
self.resetPasswordModel.stage = .resetPassword(username: username, sessionId: sessionId)
|
||||||
self.resetPasswordModel.transitionEdge = .trailing
|
self.resetPasswordModel.transitionEdge = .trailing
|
||||||
}
|
}
|
||||||
} catch {
|
} catch let err as SDLAPIError {
|
||||||
self.errorMessage = error.localizedDescription
|
self.errorMessage = err.message
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
}
|
}
|
||||||
self.isProcessing = false
|
self.isProcessing = false
|
||||||
|
|||||||
@ -33,6 +33,9 @@ struct punchnetApp: App {
|
|||||||
private var noticeServer: UDPNoticeCenterServer
|
private var noticeServer: UDPNoticeCenterServer
|
||||||
@State private var appContext: AppContext
|
@State private var appContext: AppContext
|
||||||
@State private var userContext = UserContext()
|
@State private var userContext = UserContext()
|
||||||
|
@AppStorage("hasAcceptedPrivacy") var hasAcceptedPrivacy: Bool = false
|
||||||
|
// UI 控制状态:是否显示弹窗
|
||||||
|
@State private var showPrivacy: Bool = false
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.noticeServer = UDPNoticeCenterServer()
|
self.noticeServer = UDPNoticeCenterServer()
|
||||||
@ -42,10 +45,19 @@ struct punchnetApp: App {
|
|||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup(id: "main") {
|
WindowGroup(id: "main") {
|
||||||
RootView()
|
//RootView()
|
||||||
|
//RegisterRootView()
|
||||||
|
RegisterSuccessView()
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.environment(self.appContext)
|
.environment(self.appContext)
|
||||||
.environment(self.userContext)
|
.environment(self.userContext)
|
||||||
|
.onAppear {
|
||||||
|
self.showPrivacy = !hasAcceptedPrivacy
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showPrivacy) {
|
||||||
|
PrivacyDetailView(showPrivacy: $showPrivacy)
|
||||||
|
//.interactiveDismissDisabled() // 强制阅读
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// .commands {
|
// .commands {
|
||||||
// CommandGroup(replacing: .appInfo) {
|
// CommandGroup(replacing: .appInfo) {
|
||||||
@ -86,24 +98,24 @@ struct punchnetApp: App {
|
|||||||
}
|
}
|
||||||
.windowResizability(.contentSize)
|
.windowResizability(.contentSize)
|
||||||
.defaultPosition(.center)
|
.defaultPosition(.center)
|
||||||
|
//
|
||||||
MenuBarExtra("punchnet", image: "logo_32") {
|
// MenuBarExtra("punchnet", image: "logo_32") {
|
||||||
VStack {
|
// VStack {
|
||||||
Button(action: {
|
// Button(action: {
|
||||||
self.menuClick()
|
// self.menuClick()
|
||||||
}, label: {
|
// }, label: {
|
||||||
Text("启动")
|
// Text("启动")
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
Divider()
|
// Divider()
|
||||||
|
//
|
||||||
Button(action: {
|
// Button(action: {
|
||||||
NSApplication.shared.terminate(nil)
|
// NSApplication.shared.terminate(nil)
|
||||||
}, label: { Text("退出") })
|
// }, label: { Text("退出") })
|
||||||
|
//
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
.menuBarExtraStyle(.menu)
|
// .menuBarExtraStyle(.menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func menuClick() {
|
private func menuClick() {
|
||||||
@ -120,9 +132,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||||
|
print("call me applicationShouldTerminate")
|
||||||
Task {
|
Task {
|
||||||
try await VPNManager.shared.disableVpn()
|
do {
|
||||||
DispatchQueue.main.async {
|
try await VPNManager.shared.disableVpn()
|
||||||
|
} catch let err {
|
||||||
|
print("退出时关闭 VPN 失败: \(err)")
|
||||||
|
}
|
||||||
|
|
||||||
|
print("call me here termi")
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
sender.reply(toApplicationShouldTerminate: true)
|
sender.reply(toApplicationShouldTerminate: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user