add edit contact
This commit is contained in:
parent
951ee3f5f5
commit
b3617a58d4
@ -809,6 +809,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Изменение фото недоступно" : {
|
||||
"comment" : "Contact edit avatar unavailable title",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Photo change unavailable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Изменить" : {
|
||||
"comment" : "Message profile edit contact button",
|
||||
"localizations" : {
|
||||
@ -827,7 +838,15 @@
|
||||
|
||||
},
|
||||
"Изменить фото" : {
|
||||
|
||||
"comment" : "Edit avatar button title",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Change photo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Изображение" : {
|
||||
"comment" : "Image message placeholder"
|
||||
@ -905,6 +924,17 @@
|
||||
"Коды восстановления" : {
|
||||
"comment" : "Раздел кодов восстановления 2FA"
|
||||
},
|
||||
"Контакт" : {
|
||||
"comment" : "Contact edit title",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Contact"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Контактов пока нет" : {
|
||||
"comment" : "Contacts empty state title"
|
||||
},
|
||||
@ -1140,6 +1170,17 @@
|
||||
"Мы отправим письмо, как только функция будет готова." : {
|
||||
"comment" : "Сообщение при недоступной отправке письма"
|
||||
},
|
||||
"Мы пока не можем обновить фото контакта." : {
|
||||
"comment" : "Contact edit avatar unavailable message",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "We can’t update the contact photo yet."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Мы постараемся всё исправить. Напишите, что смутило." : {
|
||||
"comment" : "feedback: rating description 2",
|
||||
"localizations" : {
|
||||
@ -1302,6 +1343,17 @@
|
||||
"Не удалось завершить сессию." : {
|
||||
"comment" : "Sessions service revoke unexpected status"
|
||||
},
|
||||
"Не удалось загрузить данные контакта." : {
|
||||
"comment" : "Contact edit missing profile message",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Failed to load contact data."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Не удалось загрузить историю чата." : {
|
||||
|
||||
},
|
||||
@ -1723,7 +1775,7 @@
|
||||
"comment" : "Common cancel\nОбщий текст кнопки отмены"
|
||||
},
|
||||
"Отображаемое имя" : {
|
||||
|
||||
"comment" : "Display name field placeholder"
|
||||
},
|
||||
"Отправить код ещё раз" : {
|
||||
|
||||
@ -2407,7 +2459,7 @@
|
||||
|
||||
},
|
||||
"Публичная информация" : {
|
||||
|
||||
"comment" : "Profile info section title"
|
||||
},
|
||||
"Публичное имя" : {
|
||||
|
||||
@ -2524,16 +2576,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Редактировать профиль" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Edit profile"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Редактирование контакта появится позже." : {
|
||||
"comment" : "Message profile edit contact alert message",
|
||||
"localizations" : {
|
||||
@ -2545,6 +2587,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Редактировать профиль" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Edit profile"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Редактор контактов скоро появится. Мы сохраним имя, телефон и заметку." : {
|
||||
"comment" : "Message profile add contact alert message"
|
||||
},
|
||||
@ -2671,7 +2723,7 @@
|
||||
"comment" : "Кнопка копирования кода восстановления"
|
||||
},
|
||||
"Скоро" : {
|
||||
"comment" : "Add blocked user placeholder title\nContacts placeholder title\nЗаголовок заглушки"
|
||||
"comment" : "Add blocked user placeholder title\nCommon soon title\nContacts placeholder title\nЗаголовок заглушки"
|
||||
},
|
||||
"Скоро можно будет искать сообщения, ссылки и файлы в этом чате." : {
|
||||
"comment" : "Message profile search action description"
|
||||
@ -2753,6 +2805,17 @@
|
||||
"Сохраните секретный ключ и введите код из приложения, чтобы завершить настройку." : {
|
||||
"comment" : "Сообщение после активации 2FA"
|
||||
},
|
||||
"Сохранить" : {
|
||||
"comment" : "Contact edit save button",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Save"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Сохранить изменения" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
|
||||
@ -46,8 +46,16 @@ struct MessageProfileView: View {
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
if canEditContact {
|
||||
Button(NSLocalizedString("Изменить", comment: "Message profile edit contact button")) {
|
||||
handleEditContactTap()
|
||||
if let profile = currentChatProfile {
|
||||
NavigationLink {
|
||||
ContactEditView(contact: ContactEditInfo(profile: profile))
|
||||
} label: {
|
||||
Text(NSLocalizedString("Изменить", comment: "Message profile edit contact button"))
|
||||
}
|
||||
} else {
|
||||
Button(NSLocalizedString("Изменить", comment: "Message profile edit contact button")) {
|
||||
handleEditContactTap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -537,8 +545,8 @@ struct MessageProfileView: View {
|
||||
|
||||
private func handleEditContactTap() {
|
||||
showPlaceholderAction(
|
||||
title: NSLocalizedString("Изменить контакт", comment: "Contacts context action edit"),
|
||||
message: NSLocalizedString("Редактирование контакта появится позже.", comment: "Message profile edit contact alert message")
|
||||
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||
message: NSLocalizedString("Не удалось загрузить данные контакта.", comment: "Contact edit missing profile message")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
185
yobble/Views/Contacts/ContactEditView.swift
Normal file
185
yobble/Views/Contacts/ContactEditView.swift
Normal file
@ -0,0 +1,185 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user