
- 在Info.plist中新增API签名密钥配置。 - 将Splash视图替换为SplashV2,优化启动逻辑和用户体验。 - 更新API请求中的User-Agent逻辑,使用UserAgentProvider提供的动态值。 - 在APILogger中添加敏感信息脱敏处理,增强安全性。 - 新增CreateFeedPage视图,支持用户发布动态功能。 - 更新MainPage和Splash视图的导航逻辑,整合统一的AppRoute管理。 - 移除冗余的SplashFeature视图,提升代码整洁性和可维护性。
219 lines
6.3 KiB
Swift
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: {})
|
|
} |