diff --git a/wake/Utils/APIConfig.swift b/wake/Utils/APIConfig.swift index 5257f1b..5b8265b 100644 --- a/wake/Utils/APIConfig.swift +++ b/wake/Utils/APIConfig.swift @@ -3,7 +3,7 @@ import Foundation /// API 配置信息 public enum APIConfig { /// API 基础 URL - public static let baseURL = "https://api.memorywake.com/api/v1" + public static let baseURL = "https://api-dev.memorywake.com:31274/api/v1" /// 认证 token - 从 Keychain 中获取 public static var authToken: String { diff --git a/wake/View/Components/UserProfileModal.swift b/wake/View/Components/UserProfileModal.swift index d9970cb..60b8610 100644 --- a/wake/View/Components/UserProfileModal.swift +++ b/wake/View/Components/UserProfileModal.swift @@ -1,40 +1,117 @@ import SwiftUI +// User profile model +struct UserProfile: Codable { + let userId: String + let nickname: String + let avatarUrl: String? + let account: String + let email: String + + enum CodingKeys: String, CodingKey { + case userId = "user_id" + case nickname + case avatarUrl = "avatar_file_url" + case account + case email + } +} + +// API Response wrapper +struct APIResponse: Codable { + let code: Int + let data: T +} + struct UserProfileModal: View { @Binding var showModal: Bool @Binding var showSettings: Bool + @State private var userProfile: UserProfile? + @State private var isLoading = false + @State private var errorMessage: String? + @State private var isCopied = false var body: some View { - // Modal content with transparent background VStack(spacing: 20) { Spacer() .frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0) - // 用户信息区域 + + if isLoading { + ProgressView() + .padding() + } else if let error = errorMessage { + Text(error) + .foregroundColor(.red) + .padding() + } + HStack(alignment: .center, spacing: 16) { - Image(systemName: "person.circle.fill") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 60, height: 60) - .foregroundColor(.blue) - .clipShape(Circle()) + if let avatarUrl = userProfile?.avatarUrl, !avatarUrl.isEmpty, let url = URL(string: avatarUrl) { + AsyncImage(url: url) { phase in + switch phase { + case .success(let image): + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 60, height: 60) + .clipShape(Circle()) + default: + Image(systemName: "person.circle.fill") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 60, height: 60) + .foregroundColor(.blue) + } + } + } else { + Image(systemName: "person.circle.fill") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 60, height: 60) + .foregroundColor(.blue) + } VStack(alignment: .leading, spacing: 10) { - Text("Name") + Text(userProfile?.nickname ?? "Name") .font(Typography.font(for: .body)) .fontWeight(.bold) .foregroundColor(.themeTextMessageMain) HStack(spacing: 4) { - Text("ID: 12345678") + Text("ID: \(userProfile?.userId ?? "")") .font(.system(size: 14)) .foregroundColor(.themeTextMessageMain) + .lineLimit(1) + .truncationMode(.tail) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: 120) Button(action: { - UIPasteboard.general.string = "12345678" + print("Copy ID button tapped") + UIPasteboard.general.string = userProfile?.userId + print("Copied to clipboard:", userProfile?.userId ?? "nil") + withAnimation { + isCopied = true + // Reset after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + withAnimation { + isCopied = false + print("Reset copy button state") + } + } + } }) { - Image(systemName: "doc.on.doc") - .font(.system(size: 12)) - .foregroundColor(.themeTextMessageMain) + if isCopied { + Image(systemName: "checkmark") + .foregroundColor(.themePrimary) + } else { + Image(systemName: "doc.on.doc") + } } + .font(.system(size: 12)) + .foregroundColor(.themeTextMessageMain) + .animation(.easeInOut, value: isCopied) + .contentShape(Rectangle()) // Make the entire button area tappable + .frame(width: 24, height: 24) // Ensure minimum touch target size } } @@ -52,8 +129,8 @@ struct UserProfileModal: View { ZStack(alignment: .center) { // SVG背景 - 确保铺满整个区域 SVGImage(svgName: "Pioneer") + .scaledToFill() .frame(maxWidth: .infinity, maxHeight: .infinity) - .scaledToFill() // 内容区域 VStack(alignment: .leading, spacing: 6) { @@ -80,8 +157,8 @@ struct UserProfileModal: View { } .frame(height: 112) .frame(maxWidth: .infinity) + .cornerRadius(16) .clipped() // 确保内容不会超出边界 - VStack(spacing: 12) { // upload @@ -102,9 +179,9 @@ struct UserProfileModal: View { } .padding() .cornerRadius(10) - .contentShape(Rectangle()) // 使整个区域可点击 + .contentShape(Rectangle()) } - .buttonStyle(PlainButtonStyle()) // 移除按钮默认样式 + .buttonStyle(PlainButtonStyle()) // memories Button(action: { @@ -124,9 +201,9 @@ struct UserProfileModal: View { } .padding() .cornerRadius(10) - .contentShape(Rectangle()) // 使整个区域可点击 + .contentShape(Rectangle()) } - .buttonStyle(PlainButtonStyle()) // 移除按钮默认样式 + .buttonStyle(PlainButtonStyle()) // Box Button(action: { @@ -146,9 +223,9 @@ struct UserProfileModal: View { } .padding() .cornerRadius(10) - .contentShape(Rectangle()) // 使整个区域可点击 + .contentShape(Rectangle()) } - .buttonStyle(PlainButtonStyle()) // 移除按钮默认样式 + .buttonStyle(PlainButtonStyle()) // setting Button(action: { @@ -170,10 +247,9 @@ struct UserProfileModal: View { } .padding() .cornerRadius(10) - .contentShape(Rectangle()) // 使整个区域可点击 + .contentShape(Rectangle()) } - .buttonStyle(PlainButtonStyle()) // 移除按钮默认样式 - + .buttonStyle(PlainButtonStyle()) } .frame(maxWidth: .infinity) .padding() @@ -189,6 +265,32 @@ struct UserProfileModal: View { .background(Color.themeTextWhiteSecondary) .cornerRadius(20, corners: [.topRight, .bottomRight]) .edgesIgnoringSafeArea(.all) + .onAppear { + fetchUserInfo() + } + } + + private func fetchUserInfo() { + isLoading = true + errorMessage = nil + + NetworkService.shared.get( + path: "/iam/user-info", + parameters: nil + ) { (result: Result, NetworkError>) in + DispatchQueue.main.async { + isLoading = false + + switch result { + case .success(let response): + self.userProfile = response.data + print("✅ Successfully fetched user info:", response.data) + case .failure(let error): + self.errorMessage = error.localizedDescription + print("❌ Failed to fetch user info:", error) + } + } + } } }