Files
e-party-iOS/yana/MVVM/LoginPage.swift
edwinQQQ 8b4eb9cb7e feat: 更新API相关逻辑及视图结构
- 在Info.plist中新增API签名密钥配置。
- 将Splash视图替换为SplashV2,优化启动逻辑和用户体验。
- 更新API请求中的User-Agent逻辑,使用UserAgentProvider提供的动态值。
- 在APILogger中添加敏感信息脱敏处理,增强安全性。
- 新增CreateFeedPage视图,支持用户发布动态功能。
- 更新MainPage和Splash视图的导航逻辑,整合统一的AppRoute管理。
- 移除冗余的SplashFeature视图,提升代码整洁性和可维护性。
2025-09-17 16:37:21 +08:00

219 lines
6.3 KiB
Swift

import SwiftUI
// MARK: - Login ViewModel
@MainActor
class LoginViewModel: ObservableObject {
// MARK: - Published Properties
@Published var showIDLogin: Bool = false
@Published var showEmailLogin: Bool = false
@Published var showLanguageSettings: Bool = false
@Published var showUserAgreement: Bool = false
@Published var showPrivacyPolicy: Bool = false
@Published var isAgreementAccepted: Bool = true //
@Published var showAgreementAlert: Bool = false
@Published var isAnyLoginCompleted: Bool = false
// MARK: - Callbacks
var onLoginSuccess: (() -> Void)?
private var hasSentSuccess: Bool = false
// MARK: - Public Methods
func onIDLoginTapped() {
if isAgreementAccepted {
showIDLogin = true
} else {
showAgreementAlert = true
}
}
func onEmailLoginTapped() {
if isAgreementAccepted {
showEmailLogin = true
} else {
showAgreementAlert = true
}
}
func onLanguageSettingsTapped() {
showLanguageSettings = true
}
func onUserAgreementTapped() {
showUserAgreement = true
}
func onPrivacyPolicyTapped() {
showPrivacyPolicy = true
}
func onLoginCompleted() {
guard !hasSentSuccess else { return }
isAnyLoginCompleted = true
showIDLogin = false
showEmailLogin = false
hasSentSuccess = true
onLoginSuccess?()
}
func onBackFromIDLogin() {
showIDLogin = false
}
func onBackFromEmailLogin() {
showEmailLogin = false
}
}
// MARK: - Login View
struct LoginPage: View {
@StateObject private var viewModel = LoginViewModel()
let onLoginSuccess: () -> Void
var body: some View {
GeometryReader { geometry in
ZStack {
backgroundView
VStack(spacing: 0) {
Image("top")
.resizable()
.aspectRatio(375/400, contentMode: .fit)
.frame(maxWidth: .infinity)
HStack {
Text(LocalizedString("login.app_title", comment: ""))
.font(FontManager.adaptedFont(.bayonRegular, designSize: 56, for: geometry.size.width))
.foregroundColor(.white)
.padding(.leading, 20)
Spacer()
}
.padding(.bottom, 20)
Spacer()
bottomSection
}
// -
languageSettingsButton
.position(x: geometry.size.width - 40, y: 60)
APILoadingEffectView()
}
}
.ignoresSafeArea()
.navigationBarHidden(true)
.navigationDestination(isPresented: $viewModel.showIDLogin) {
IDLoginPage(
onBack: {
viewModel.onBackFromIDLogin()
},
onLoginSuccess: {
viewModel.onLoginCompleted()
}
)
.navigationBarHidden(true)
}
.navigationDestination(isPresented: $viewModel.showEmailLogin) {
EMailLoginPage(
onBack: {
viewModel.onBackFromEmailLogin()
},
onLoginSuccess: {
viewModel.onLoginCompleted()
}
)
.navigationBarHidden(true)
}
.sheet(isPresented: $viewModel.showLanguageSettings) {
LanguageSettingsView(isPresented: $viewModel.showLanguageSettings)
}
.webView(
isPresented: $viewModel.showUserAgreement,
url: APIConfiguration.webURL(for: .userAgreement)
)
.webView(
isPresented: $viewModel.showPrivacyPolicy,
url: APIConfiguration.webURL(for: .privacyPolicy)
)
.alert(LocalizedString("login.agreement_alert_title", comment: ""), isPresented: $viewModel.showAgreementAlert) {
Button(LocalizedString("login.agreement_alert_confirm", comment: "")) { }
} message: {
Text(LocalizedString("login.agreement_alert_message", comment: ""))
}
.onAppear {
viewModel.onLoginSuccess = onLoginSuccess
}
}
// MARK: -
private var backgroundView: some View {
LoginBackgroundView()
}
private var bottomSection: some View {
VStack(spacing: 20) {
loginButtons
userAgreementComponent
}
.padding(.horizontal, 28)
.padding(.bottom, 48)
}
private var loginButtons: some View {
VStack(spacing: 20) {
LoginButton(
iconName: "person.circle",
iconColor: .blue,
title: LocalizedString("login.id_login", comment: ""),
action: {
viewModel.onIDLoginTapped()
}
)
LoginButton(
iconName: "envelope",
iconColor: .green,
title: LocalizedString("login.email_login", comment: ""),
action: {
viewModel.onEmailLoginTapped()
}
)
}
}
private var languageSettingsButton: some View {
Button(action: {
viewModel.onLanguageSettingsTapped()
}) {
Image(systemName: "globe")
.font(.system(size: 20))
.foregroundColor(.white.opacity(0.8))
}
}
private var userAgreementComponent: some View {
UserAgreementComponent(
isAgreed: $viewModel.isAgreementAccepted,
onAgreementTap: {
Task { @MainActor in
viewModel.onUserAgreementTapped()
}
},
onPolicyTap: {
Task { @MainActor in
viewModel.onPrivacyPolicyTapped()
}
}
)
.frame(height: 40)
.padding(.horizontal, -20)
}
}
#Preview {
LoginPage(onLoginSuccess: {})
}