add update contact
This commit is contained in:
parent
bb08452ff9
commit
997ddea9c4
@ -44,6 +44,11 @@ private struct ContactDeleteRequestPayload: Encodable {
|
|||||||
let userId: UUID
|
let userId: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct ContactUpdateRequestPayload: Encodable {
|
||||||
|
let userId: UUID
|
||||||
|
let customName: String?
|
||||||
|
}
|
||||||
|
|
||||||
final class ContactsService {
|
final class ContactsService {
|
||||||
private let client: NetworkClient
|
private let client: NetworkClient
|
||||||
private let decoder: JSONDecoder
|
private let decoder: JSONDecoder
|
||||||
@ -215,6 +220,58 @@ final class ContactsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateContact(userId: UUID, customName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
let request = ContactUpdateRequestPayload(userId: userId, customName: customName)
|
||||||
|
|
||||||
|
guard let body = try? encoder.encode(request) else {
|
||||||
|
let message = NSLocalizedString("Не удалось подготовить данные запроса.", comment: "Contacts service encoding error")
|
||||||
|
completion(.failure(ContactsServiceError.encoding(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client.request(
|
||||||
|
path: "/v1/user/contact/update",
|
||||||
|
method: .patch,
|
||||||
|
body: body,
|
||||||
|
requiresAuth: true
|
||||||
|
) { [decoder] result in
|
||||||
|
switch result {
|
||||||
|
case .success(let response):
|
||||||
|
do {
|
||||||
|
let apiResponse = try decoder.decode(APIResponse<MessagePayload>.self, from: response.data)
|
||||||
|
guard apiResponse.status == "fine" else {
|
||||||
|
let message = apiResponse.detail ?? NSLocalizedString("Не удалось обновить контакт.", comment: "Contacts service update unexpected status")
|
||||||
|
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.success(()))
|
||||||
|
} catch {
|
||||||
|
let debugMessage = Self.describeDecodingError(error: error, data: response.data)
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[ContactsService] decode contact update failed: \(debugMessage)")
|
||||||
|
}
|
||||||
|
completion(.failure(ContactsServiceError.decoding(debugDescription: debugMessage)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
if case let NetworkError.server(_, data) = error,
|
||||||
|
let data,
|
||||||
|
let message = Self.errorMessage(from: data) {
|
||||||
|
completion(.failure(ContactsServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateContact(userId: UUID, customName: String?) async throws {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
updateContact(userId: userId, customName: customName) { result in
|
||||||
|
continuation.resume(with: result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
private static func decodeDate(from decoder: Decoder) throws -> Date {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
let string = try container.decode(String.self)
|
let string = try container.decode(String.self)
|
||||||
|
|||||||
@ -853,9 +853,12 @@
|
|||||||
},
|
},
|
||||||
"Имя в чате" : {
|
"Имя в чате" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Имя контакта должно быть короче 32 символов." : {
|
||||||
|
"comment" : "Contact edit name too long message"
|
||||||
},
|
},
|
||||||
"Имя не может быть пустым." : {
|
"Имя не может быть пустым." : {
|
||||||
"comment" : "Contact add empty name error"
|
"comment" : "Contact add empty name error\nContact edit empty name error"
|
||||||
},
|
},
|
||||||
"Имя, логин и статус — как в профиле Telegram." : {
|
"Имя, логин и статус — как в профиле Telegram." : {
|
||||||
"comment" : "Message profile about description"
|
"comment" : "Message profile about description"
|
||||||
@ -1410,6 +1413,9 @@
|
|||||||
"Не удалось обновить аватар." : {
|
"Не удалось обновить аватар." : {
|
||||||
"comment" : "Avatar upload unexpected status"
|
"comment" : "Avatar upload unexpected status"
|
||||||
},
|
},
|
||||||
|
"Не удалось обновить контакт." : {
|
||||||
|
"comment" : "Contacts service update unexpected status"
|
||||||
|
},
|
||||||
"Не удалось обновить пароль." : {
|
"Не удалось обновить пароль." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1445,7 +1451,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Не удалось определить контакт." : {
|
"Не удалось определить контакт." : {
|
||||||
"comment" : "Contact delete invalid user id error"
|
"comment" : "Contact delete invalid user id error\nContact edit invalid user id error"
|
||||||
},
|
},
|
||||||
"Не удалось определить пользователя для блокировки." : {
|
"Не удалось определить пользователя для блокировки." : {
|
||||||
"comment" : "Message profile missing user id error"
|
"comment" : "Message profile missing user id error"
|
||||||
@ -2599,6 +2605,7 @@
|
|||||||
},
|
},
|
||||||
"Редактирование контакта появится позже." : {
|
"Редактирование контакта появится позже." : {
|
||||||
"comment" : "Message profile edit contact alert message",
|
"comment" : "Message profile edit contact alert message",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2744,7 +2751,7 @@
|
|||||||
"comment" : "Кнопка копирования кода восстановления"
|
"comment" : "Кнопка копирования кода восстановления"
|
||||||
},
|
},
|
||||||
"Скоро" : {
|
"Скоро" : {
|
||||||
"comment" : "Add blocked user placeholder title\nCommon soon title\nContacts placeholder title\nЗаголовок заглушки"
|
"comment" : "Add blocked user placeholder title\nContacts placeholder title\nЗаголовок заглушки"
|
||||||
},
|
},
|
||||||
"Скоро можно будет искать сообщения, ссылки и файлы в этом чате." : {
|
"Скоро можно будет искать сообщения, ссылки и файлы в этом чате." : {
|
||||||
"comment" : "Message profile search action description"
|
"comment" : "Message profile search action description"
|
||||||
@ -2754,6 +2761,9 @@
|
|||||||
},
|
},
|
||||||
"Скрыть" : {
|
"Скрыть" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Слишком длинное имя" : {
|
||||||
|
"comment" : "Contact edit name too long title"
|
||||||
},
|
},
|
||||||
"Слишком много запросов." : {
|
"Слишком много запросов." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -48,9 +48,15 @@ struct MessageProfileView: View {
|
|||||||
if canEditContact {
|
if canEditContact {
|
||||||
if let profile = currentChatProfile {
|
if let profile = currentChatProfile {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
ContactEditView(contact: ContactEditInfo(profile: profile)) {
|
ContactEditView(
|
||||||
|
contact: ContactEditInfo(profile: profile),
|
||||||
|
onContactDeleted: {
|
||||||
handleContactDeleted()
|
handleContactDeleted()
|
||||||
|
},
|
||||||
|
onContactUpdated: { newName in
|
||||||
|
handleContactUpdated(newName)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
} label: {
|
} label: {
|
||||||
Text(NSLocalizedString("Изменить", comment: "Message profile edit contact button"))
|
Text(NSLocalizedString("Изменить", comment: "Message profile edit contact button"))
|
||||||
}
|
}
|
||||||
@ -598,6 +604,29 @@ struct MessageProfileView: View {
|
|||||||
chatProfile = updatedProfile
|
chatProfile = updatedProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func handleContactUpdated(_ newName: String) {
|
||||||
|
guard let profile = currentChatProfile else { return }
|
||||||
|
|
||||||
|
let updatedProfile = ChatProfile(
|
||||||
|
userId: profile.userId,
|
||||||
|
login: profile.login,
|
||||||
|
fullName: profile.fullName,
|
||||||
|
customName: newName,
|
||||||
|
bio: profile.bio,
|
||||||
|
lastSeen: profile.lastSeen,
|
||||||
|
createdAt: profile.createdAt,
|
||||||
|
avatars: profile.avatars,
|
||||||
|
stories: profile.stories,
|
||||||
|
permissions: profile.permissions,
|
||||||
|
profilePermissions: profile.profilePermissions,
|
||||||
|
relationship: profile.relationship,
|
||||||
|
rating: profile.rating,
|
||||||
|
isOfficial: profile.isOfficial
|
||||||
|
)
|
||||||
|
|
||||||
|
chatProfile = updatedProfile
|
||||||
|
}
|
||||||
|
|
||||||
private func handleContactDeleted() {
|
private func handleContactDeleted() {
|
||||||
guard let profile = currentChatProfile else { return }
|
guard let profile = currentChatProfile else { return }
|
||||||
|
|
||||||
|
|||||||
@ -56,18 +56,25 @@ struct ContactEditInfo {
|
|||||||
struct ContactEditView: View {
|
struct ContactEditView: View {
|
||||||
let contact: ContactEditInfo
|
let contact: ContactEditInfo
|
||||||
let onContactDeleted: (() -> Void)?
|
let onContactDeleted: (() -> Void)?
|
||||||
|
let onContactUpdated: ((String) -> Void)?
|
||||||
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
private let contactsService = ContactsService()
|
private let contactsService = ContactsService()
|
||||||
|
|
||||||
@State private var displayName: String
|
@State private var displayName: String
|
||||||
@State private var activeAlert: ContactEditAlert?
|
@State private var activeAlert: ContactEditAlert?
|
||||||
|
@State private var isSaving = false
|
||||||
@State private var isDeleting = false
|
@State private var isDeleting = false
|
||||||
@State private var showDeleteConfirmation = false
|
@State private var showDeleteConfirmation = false
|
||||||
|
|
||||||
init(contact: ContactEditInfo, onContactDeleted: (() -> Void)? = nil) {
|
init(
|
||||||
|
contact: ContactEditInfo,
|
||||||
|
onContactDeleted: (() -> Void)? = nil,
|
||||||
|
onContactUpdated: ((String) -> Void)? = nil
|
||||||
|
) {
|
||||||
self.contact = contact
|
self.contact = contact
|
||||||
self.onContactDeleted = onContactDeleted
|
self.onContactDeleted = onContactDeleted
|
||||||
|
self.onContactUpdated = onContactUpdated
|
||||||
let initialName = contact.preferredName
|
let initialName = contact.preferredName
|
||||||
_displayName = State(initialValue: initialName)
|
_displayName = State(initialValue: initialName)
|
||||||
}
|
}
|
||||||
@ -78,6 +85,7 @@ struct ContactEditView: View {
|
|||||||
|
|
||||||
Section(header: Text(NSLocalizedString("Публичная информация", comment: "Profile info section title"))) {
|
Section(header: Text(NSLocalizedString("Публичная информация", comment: "Profile info section title"))) {
|
||||||
TextField(NSLocalizedString("Отображаемое имя", comment: "Display name field placeholder"), text: $displayName)
|
TextField(NSLocalizedString("Отображаемое имя", comment: "Display name field placeholder"), text: $displayName)
|
||||||
|
.disabled(isSaving || isDeleting)
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
@ -93,10 +101,14 @@ struct ContactEditView: View {
|
|||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
|
if isSaving {
|
||||||
|
ProgressView()
|
||||||
|
} else {
|
||||||
Button(NSLocalizedString("Сохранить", comment: "Contact edit save button")) {
|
Button(NSLocalizedString("Сохранить", comment: "Contact edit save button")) {
|
||||||
handleSaveTap()
|
handleSaveTap()
|
||||||
}
|
}
|
||||||
.disabled(!hasChanges)
|
.disabled(!hasChanges || isDeleting)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert(item: $activeAlert) { item in
|
.alert(item: $activeAlert) { item in
|
||||||
@ -216,10 +228,53 @@ struct ContactEditView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleSaveTap() {
|
private func handleSaveTap() {
|
||||||
|
guard !isSaving, !isDeleting else { return }
|
||||||
|
|
||||||
|
guard let userId = UUID(uuidString: contact.userId) else {
|
||||||
activeAlert = ContactEditAlert(
|
activeAlert = ContactEditAlert(
|
||||||
title: NSLocalizedString("Скоро", comment: "Common soon title"),
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
message: NSLocalizedString("Редактирование контакта появится позже.", comment: "Message profile edit contact alert message")
|
message: NSLocalizedString("Не удалось определить контакт.", comment: "Contact edit invalid user id error")
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let trimmed = displayName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !trimmed.isEmpty else {
|
||||||
|
activeAlert = ContactEditAlert(
|
||||||
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
message: NSLocalizedString("Имя не может быть пустым.", comment: "Contact edit empty name error")
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if trimmed.count > 32 {
|
||||||
|
activeAlert = ContactEditAlert(
|
||||||
|
title: NSLocalizedString("Слишком длинное имя", comment: "Contact edit name too long title"),
|
||||||
|
message: NSLocalizedString("Имя контакта должно быть короче 32 символов.", comment: "Contact edit name too long message")
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isSaving = true
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await contactsService.updateContact(userId: userId, customName: trimmed)
|
||||||
|
await MainActor.run {
|
||||||
|
isSaving = false
|
||||||
|
onContactUpdated?(trimmed)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
isSaving = false
|
||||||
|
activeAlert = ContactEditAlert(
|
||||||
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
message: error.localizedDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleDeleteTap() {
|
private func handleDeleteTap() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user