feat: 用户信息页面

This commit is contained in:
jinyaqiu 2025-08-19 13:59:03 +08:00
parent 4f92f52bb7
commit 119b671838
7 changed files with 263 additions and 51 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -23,6 +23,7 @@ enum TypographyStyle {
case subtitle //
case caption //
case footnote //
case small //
}
// MARK: -
@ -41,13 +42,14 @@ struct Typography {
///
private static let styleConfig: [TypographyStyle: TypographyConfig] = [
.headline: TypographyConfig(size: 24, weight: .bold, textStyle: .headline),
.title: TypographyConfig(size: 20, weight: .semibold, textStyle: .title2),
.body: TypographyConfig(size: 16, weight: .regular, textStyle: .body),
.subtitle: TypographyConfig(size: 14, weight: .medium, textStyle: .subheadline),
.caption: TypographyConfig(size: 12, weight: .light, textStyle: .caption1),
.footnote: TypographyConfig(size: 11, weight: .regular, textStyle: .footnote)
]
.headline: TypographyConfig(size: 24, weight: .bold, textStyle: .headline),
.title: TypographyConfig(size: 20, weight: .semibold, textStyle: .title2),
.body: TypographyConfig(size: 16, weight: .regular, textStyle: .body),
.subtitle: TypographyConfig(size: 14, weight: .medium, textStyle: .subheadline),
.caption: TypographyConfig(size: 12, weight: .light, textStyle: .caption1),
.footnote: TypographyConfig(size: 11, weight: .regular, textStyle: .footnote),
.small: TypographyConfig(size: 10, weight: .regular, textStyle: .headline)
]
// MARK: -

View File

View File

@ -7,60 +7,67 @@ import CryptoKit
struct LoginView: View {
// MARK: - Properties
@Environment(\.dismiss) private var dismiss
@State private var isLoading = false
@State private var showError = false
@State private var errorMessage = ""
@State private var currentNonce: String?
@State private var isLoggedIn = false
// MARK: - Body
var body: some View {
ZStack {
// Background
Color(red: 1.0, green: 0.67, blue: 0.15)
.edgesIgnoringSafeArea(.all)
VStack(alignment: .leading, spacing: 4) {
Text("Hi, I'm MeMo!")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 24)
.padding(.top, 44)
NavigationStack {
ZStack {
// Background
Color(red: 1.0, green: 0.67, blue: 0.15)
.edgesIgnoringSafeArea(.all)
Text("Welcome~")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 24)
.padding(.bottom, 20)
VStack(alignment: .leading, spacing: 4) {
Text("Hi, I'm MeMo!")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 24)
.padding(.top, 44)
Text("Welcome~")
.font(.largeTitle)
.fontWeight(.semibold)
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 24)
.padding(.bottom, 20)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
Spacer()
VStack(spacing: 16) {
Spacer()
signInButton()
termsAndPrivacyView()
}
.padding()
.alert(isPresented: $showError) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("OK"))
)
}
if isLoading {
loadingView()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
VStack(spacing: 16) {
Spacer()
signInButton()
termsAndPrivacyView()
}
.padding()
.alert(isPresented: $showError) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("OK"))
)
}
if isLoading {
loadingView()
.navigationBarHidden(true)
.fullScreenCover(isPresented: $isLoggedIn) {
NavigationStack {
UserInfo()
}
}
}
.navigationBarHidden(true)
}
// MARK: - Views
@ -140,6 +147,9 @@ struct LoginView: View {
private func handleAppleSignIn(result: Result<ASAuthorization, Error>) {
print("🔵 [Apple Sign In] 开始处理登录结果...")
DispatchQueue.main.async {
self.isLoggedIn = true
}
switch result {
case .success(let authResults):
print("✅ [Apple Sign In] 登录授权成功")
@ -242,9 +252,9 @@ struct LoginView: View {
// MARK: - Helpers
private func handleSuccessfulAuthentication() {
print("✅ [Auth] 登录成功,准备关闭登录页面...")
print("✅ [Auth] 登录成功,准备跳转到用户信息页面...")
DispatchQueue.main.async {
self.dismiss()
self.isLoggedIn = true
}
}
@ -257,7 +267,10 @@ struct LoginView: View {
private func handleAuthenticationError(_ error: AFError) {
let errorMessage = error.localizedDescription
print("❌ [Auth] 认证错误: \(errorMessage)")
showError(message: "登录失败: \(errorMessage)")
DispatchQueue.main.async {
self.isLoggedIn = false
self.showError(message: "登录失败: \(errorMessage)")
}
}
private func showError(message: String) {

View File

@ -0,0 +1,146 @@
import SwiftUI
struct UserInfo: View {
@Environment(\.dismiss) private var dismiss
// Sample user data - replace with your actual data model
@State private var userName = "MeMo"
@State private var userEmail = "memo@example.com"
@State private var notificationsEnabled = true
@State private var darkModeEnabled = false
@State private var showLogoutAlert = false
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 20) {
Text("Choose a photo as your avatar, and we'll generate a video mystery box for you.")
.font(Typography.font(for: .small))
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 10)
.background(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.0, green: 0.97, blue: 0.87),
.white,
Color(red: 1.0, green: 0.97, blue: 0.84)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
.padding(.vertical, 10)
Spacer()
VStack(spacing: 20) {
// Title
Text("Add Your Avatar")
.font(Typography.font(for: .title))
.frame(maxWidth: .infinity, alignment: .center)
// Avatar
ZStack {
SVGImage(svgName: "Avatar")
.frame(width: 225, height: 225)
// Fallback in case SVG fails to load
Image(systemName: "person.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 225, height: 225)
.foregroundColor(.gray)
.opacity(0.3)
}
.padding(.vertical, 20)
// Buttons
Button(action: {
// Action for first button
}) {
Text("Upload from Gallery")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color(red: 1.0, green: 0.973, blue: 0.871))
)
}
Button(action: {
// Action for second button
}) {
Text("Take a Photo")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color(red: 1.0, green: 0.973, blue: 0.871))
)
}
}
.padding()
.background(Color(.white))
.cornerRadius(20)
Spacer()
Button(action: {
// Action for next button
}) {
Text("Next")
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 25)
.fill(Color(red: 1.0, green: 0.714, blue: 0.271))
)
}
}
.padding()
.navigationTitle("Complete Your Profile")
.navigationBarTitleDisplayMode(.inline)
.background(Color(red: 0.98, green: 0.98, blue: 0.98)) // #FAFAFA
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
dismiss()
}) {
Image(systemName: "chevron.left")
.foregroundColor(.blue)
}
}
}
}
}
// MARK: - Settings Row View
struct SettingsRow: View {
let icon: String
let title: String
let color: Color
var body: some View {
HStack {
Image(systemName: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.padding(6)
.background(color.opacity(0.1))
.foregroundColor(color)
.cornerRadius(6)
Text(title)
.padding(.leading, 5)
}
.padding(.vertical, 4)
}
}
// MARK: - Preview
struct UserInfo_Previews: PreviewProvider {
static var previews: some View {
UserInfo()
}
}

View File

@ -0,0 +1,35 @@
import SwiftUI
import WebKit
struct SVGImage: UIViewRepresentable {
let svgName: String
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.isOpaque = false
webView.backgroundColor = .clear
webView.scrollView.isScrollEnabled = false
// SVG
if let url = Bundle.main.url(forResource: svgName, withExtension: "svg") {
print("SVG URL found: \(url.path)")
let request = URLRequest(url: url)
webView.load(request)
} else {
print("Error: Failed to find SVG file with name: \(svgName).svg in main bundle")
// SVG
if let resourceURL = Bundle.main.resourceURL,
let files = try? FileManager.default.contentsOfDirectory(at: resourceURL, includingPropertiesForKeys: nil) {
let svgFiles = files.filter { $0.pathExtension.lowercased() == "svg" }
print("Available SVG files: \(svgFiles)")
}
}
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {}
}
// Usage:
// SVGImage(svgName: "Avatar")