186 lines
6.3 KiB
Swift
186 lines
6.3 KiB
Swift
import SwiftUI
|
|
|
|
struct ContactEditInfo {
|
|
let userId: String
|
|
let login: String?
|
|
let fullName: String?
|
|
let customName: String?
|
|
let avatarFileId: String?
|
|
|
|
init(userId: String, login: String?, fullName: String?, customName: String?, avatarFileId: String?) {
|
|
self.userId = userId
|
|
self.login = login
|
|
self.fullName = fullName
|
|
self.customName = customName
|
|
self.avatarFileId = avatarFileId
|
|
}
|
|
|
|
init(userId: UUID, login: String?, fullName: String?, customName: String?, avatarFileId: String?) {
|
|
self.init(userId: userId.uuidString, login: login, fullName: fullName, customName: customName, avatarFileId: avatarFileId)
|
|
}
|
|
|
|
init(profile: ChatProfile) {
|
|
self.init(
|
|
userId: profile.userId,
|
|
login: profile.login,
|
|
fullName: profile.fullName,
|
|
customName: profile.customName,
|
|
avatarFileId: profile.avatars?.current?.fileId
|
|
)
|
|
}
|
|
|
|
init(payload: ContactPayload) {
|
|
self.init(
|
|
userId: payload.userId,
|
|
login: payload.login,
|
|
fullName: payload.fullName,
|
|
customName: payload.customName,
|
|
avatarFileId: nil
|
|
)
|
|
}
|
|
|
|
var preferredName: String {
|
|
if let custom = customName?.trimmedNonEmpty {
|
|
return custom
|
|
}
|
|
if let full = fullName?.trimmedNonEmpty {
|
|
return full
|
|
}
|
|
if let login, !login.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
return "@\(login)"
|
|
}
|
|
return NSLocalizedString("Неизвестный пользователь", comment: "Message profile fallback title")
|
|
}
|
|
}
|
|
|
|
struct ContactEditView: View {
|
|
let contact: ContactEditInfo
|
|
|
|
@State private var displayName: String
|
|
@State private var originalDisplayName: String
|
|
@State private var activeAlert: ContactEditAlert?
|
|
|
|
init(contact: ContactEditInfo) {
|
|
self.contact = contact
|
|
let initialName = contact.preferredName
|
|
_displayName = State(initialValue: initialName)
|
|
_originalDisplayName = State(initialValue: initialName)
|
|
}
|
|
|
|
var body: some View {
|
|
Form {
|
|
avatarSection
|
|
|
|
Section(header: Text(NSLocalizedString("Публичная информация", comment: "Profile info section title"))) {
|
|
TextField(NSLocalizedString("Отображаемое имя", comment: "Display name field placeholder"), text: $displayName)
|
|
}
|
|
}
|
|
.navigationTitle(NSLocalizedString("Контакт", comment: "Contact edit title"))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button(NSLocalizedString("Сохранить", comment: "Contact edit save button")) {
|
|
handleSaveTap()
|
|
}
|
|
.disabled(!hasChanges)
|
|
}
|
|
}
|
|
.alert(item: $activeAlert) { item in
|
|
Alert(
|
|
title: Text(item.title),
|
|
message: Text(item.message),
|
|
dismissButton: .default(Text(NSLocalizedString("Понятно", comment: "Placeholder alert dismiss")))
|
|
)
|
|
}
|
|
}
|
|
|
|
private var avatarSection: some View {
|
|
Section {
|
|
HStack {
|
|
Spacer()
|
|
VStack(spacing: 8) {
|
|
avatarView
|
|
.frame(width: 120, height: 120)
|
|
.clipShape(Circle())
|
|
|
|
Button(NSLocalizedString("Изменить фото", comment: "Edit avatar button title")) {
|
|
showAvatarUnavailableAlert()
|
|
}
|
|
}
|
|
Spacer()
|
|
}
|
|
}
|
|
.listRowBackground(Color(UIColor.systemGroupedBackground))
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var avatarView: some View {
|
|
if let url = avatarURL,
|
|
let fileId = contact.avatarFileId {
|
|
CachedAvatarView(url: url, fileId: fileId, userId: contact.userId) {
|
|
avatarPlaceholder
|
|
}
|
|
.aspectRatio(contentMode: .fill)
|
|
} else {
|
|
avatarPlaceholder
|
|
}
|
|
}
|
|
|
|
private var avatarPlaceholder: some View {
|
|
Circle()
|
|
.fill(Color.accentColor.opacity(0.15))
|
|
.overlay(
|
|
Text(avatarInitial)
|
|
.font(.system(size: 48, weight: .semibold))
|
|
.foregroundColor(.accentColor)
|
|
)
|
|
}
|
|
|
|
private var avatarInitial: String {
|
|
let trimmedName = displayName.trimmedNonEmpty ?? contact.preferredName
|
|
if let first = trimmedName.trimmingCharacters(in: .whitespacesAndNewlines).first {
|
|
return String(first).uppercased()
|
|
}
|
|
if let login = contact.login?.trimmingCharacters(in: .whitespacesAndNewlines), !login.isEmpty {
|
|
return String(login.prefix(1)).uppercased()
|
|
}
|
|
return "?"
|
|
}
|
|
|
|
private var avatarURL: URL? {
|
|
guard let fileId = contact.avatarFileId else { return nil }
|
|
return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(contact.userId)?file_id=\(fileId)")
|
|
}
|
|
|
|
private var hasChanges: Bool {
|
|
displayName.trimmingCharacters(in: .whitespacesAndNewlines) != originalDisplayName.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
|
|
private func showAvatarUnavailableAlert() {
|
|
activeAlert = ContactEditAlert(
|
|
title: NSLocalizedString("Изменение фото недоступно", comment: "Contact edit avatar unavailable title"),
|
|
message: NSLocalizedString("Мы пока не можем обновить фото контакта.", comment: "Contact edit avatar unavailable message")
|
|
)
|
|
}
|
|
|
|
private func handleSaveTap() {
|
|
activeAlert = ContactEditAlert(
|
|
title: NSLocalizedString("Скоро", comment: "Common soon title"),
|
|
message: NSLocalizedString("Редактирование контакта появится позже.", comment: "Message profile edit contact alert message")
|
|
)
|
|
}
|
|
}
|
|
|
|
private struct ContactEditAlert: Identifiable {
|
|
let id = UUID()
|
|
let title: String
|
|
let message: String
|
|
}
|
|
|
|
private extension String {
|
|
var trimmedNonEmpty: String? {
|
|
let value = trimmingCharacters(in: .whitespacesAndNewlines)
|
|
return value.isEmpty ? nil : value
|
|
}
|
|
}
|