ios_app_v2/yobble/Views/Tab/Settings/EditProfileView.swift
2025-12-09 23:45:59 +03:00

176 lines
6.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
struct EditProfileView: View {
// State for form fields
@State private var displayName = ""
@State private var description = ""
// State for profile data and avatar
@State private var profile: ProfileDataPayload?
@State private var avatarImage: UIImage?
@State private var showImagePicker = false
// State for loading and errors
@State private var isLoading = false
@State private var alertMessage: String?
@State private var showAlert = false
private let profileService = ProfileService()
private let descriptionLimit = 1024
var body: some View {
ZStack {
Form {
Section {
HStack {
Spacer()
VStack {
if let image = avatarImage {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(width: 120, height: 120)
.clipShape(Circle())
} else if let profile = profile,
let fileId = profile.avatars?.current?.fileId,
let url = avatarUrl(for: profile, fileId: fileId) {
CachedAvatarView(url: url, fileId: fileId, userId: profile.userId.uuidString) {
avatarPlaceholder
}
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 120)
.clipShape(Circle())
} else {
avatarPlaceholder
}
Button("Изменить фото") {
showImagePicker = true
}
.padding(.top, 8)
}
Spacer()
}
}
.listRowBackground(Color(UIColor.systemGroupedBackground))
Section(header: Text("Публичная информация")) {
TextField("Отображаемое имя", text: $displayName)
VStack(alignment: .leading, spacing: 5) {
Text("Описание")
.font(.caption)
.foregroundColor(.secondary)
TextEditor(text: $description)
.frame(height: 150)
.onChange(of: description) { newValue in
if newValue.count > descriptionLimit {
description = String(newValue.prefix(descriptionLimit))
}
}
HStack {
Spacer()
Text("\(description.count) / \(descriptionLimit)")
.font(.caption)
.foregroundColor(description.count > descriptionLimit ? .red : .secondary)
}
}
}
Button(action: {
// Действие для сохранения профиля
print("DisplayName: \(displayName)")
print("Description: \(description)")
}) {
Text("Применить")
}
}
.navigationTitle("Редактировать профиль")
.onAppear(perform: loadProfile)
.sheet(isPresented: $showImagePicker) {
ImagePicker(image: $avatarImage)
}
.alert("Ошибка", isPresented: $showAlert, presenting: alertMessage) { _ in
Button("OK") {}
} message: { message in
Text(message)
}
if isLoading {
Color.black.opacity(0.4).ignoresSafeArea()
ProgressView("Загрузка...")
.padding()
.background(Color.secondary.colorInvert())
.cornerRadius(10)
}
}
}
private var avatarPlaceholder: some View {
Circle()
.fill(Color.secondary.opacity(0.2))
.frame(width: 120, height: 120)
.overlay(
Image(systemName: "person.fill")
.font(.system(size: 60))
.foregroundColor(.gray)
)
}
private func avatarUrl(for profile: ProfileDataPayload, fileId: String) -> URL? {
return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(profile.userId)?file_id=\(fileId)")
}
private func loadProfile() {
isLoading = true
Task {
do {
let profile = try await profileService.fetchMyProfile()
await MainActor.run {
self.profile = profile
self.displayName = profile.fullName ?? ""
self.description = profile.bio ?? ""
self.isLoading = false
}
} catch {
await MainActor.run {
self.alertMessage = error.localizedDescription
self.showAlert = true
self.isLoading = false
}
}
}
}
}
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
@Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let uiImage = info[.originalImage] as? UIImage {
parent.image = uiImage
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}