add delete contact
This commit is contained in:
parent
a1446ec8bf
commit
bb08452ff9
@ -40,6 +40,10 @@ private struct ContactCreateRequestPayload: Encodable {
|
|||||||
let customName: String?
|
let customName: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct ContactDeleteRequestPayload: Encodable {
|
||||||
|
let userId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
final class ContactsService {
|
final class ContactsService {
|
||||||
private let client: NetworkClient
|
private let client: NetworkClient
|
||||||
private let decoder: JSONDecoder
|
private let decoder: JSONDecoder
|
||||||
@ -159,6 +163,58 @@ final class ContactsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeContact(userId: UUID, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
let request = ContactDeleteRequestPayload(userId: userId)
|
||||||
|
|
||||||
|
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/remove",
|
||||||
|
method: .delete,
|
||||||
|
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 delete 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 delete 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 removeContact(userId: UUID) async throws {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
removeContact(userId: userId) { 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)
|
||||||
|
|||||||
@ -938,6 +938,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Контакт \"%1$@\" будет удалён из списка." : {
|
||||||
|
"comment" : "Contact delete confirmation message"
|
||||||
|
},
|
||||||
"Контактов пока нет" : {
|
"Контактов пока нет" : {
|
||||||
"comment" : "Contacts empty state title"
|
"comment" : "Contacts empty state title"
|
||||||
},
|
},
|
||||||
@ -1441,6 +1444,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Не удалось определить контакт." : {
|
||||||
|
"comment" : "Contact delete invalid user id error"
|
||||||
|
},
|
||||||
"Не удалось определить пользователя для блокировки." : {
|
"Не удалось определить пользователя для блокировки." : {
|
||||||
"comment" : "Message profile missing user id error"
|
"comment" : "Message profile missing user id error"
|
||||||
},
|
},
|
||||||
@ -1484,6 +1490,9 @@
|
|||||||
"Не удалось сохранить изменения профиля." : {
|
"Не удалось сохранить изменения профиля." : {
|
||||||
"comment" : "Profile update unexpected status"
|
"comment" : "Profile update unexpected status"
|
||||||
},
|
},
|
||||||
|
"Не удалось удалить контакт." : {
|
||||||
|
"comment" : "Contacts service delete unexpected status"
|
||||||
|
},
|
||||||
"Не удалось удалить пользователя из списка." : {
|
"Не удалось удалить пользователя из списка." : {
|
||||||
"comment" : "Blocked users delete unexpected status"
|
"comment" : "Blocked users delete unexpected status"
|
||||||
},
|
},
|
||||||
@ -2998,6 +3007,7 @@
|
|||||||
},
|
},
|
||||||
"Удаление контакта появится позже." : {
|
"Удаление контакта появится позже." : {
|
||||||
"comment" : "Contact edit delete placeholder message",
|
"comment" : "Contact edit delete placeholder message",
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -3007,12 +3017,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Удалить" : {
|
||||||
|
"comment" : "Contact delete confirm action"
|
||||||
|
},
|
||||||
"Удалить из заблокированных?" : {
|
"Удалить из заблокированных?" : {
|
||||||
"comment" : "Unblock confirmation title"
|
"comment" : "Unblock confirmation title"
|
||||||
},
|
},
|
||||||
"Удалить контакт" : {
|
"Удалить контакт" : {
|
||||||
"comment" : "Contact edit delete action\nContacts context action delete"
|
"comment" : "Contact edit delete action\nContacts context action delete"
|
||||||
},
|
},
|
||||||
|
"Удалить контакт?" : {
|
||||||
|
"comment" : "Contact delete confirmation title"
|
||||||
|
},
|
||||||
"Удалить фото" : {
|
"Удалить фото" : {
|
||||||
"comment" : "Avatar delete"
|
"comment" : "Avatar delete"
|
||||||
},
|
},
|
||||||
@ -3026,6 +3042,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Удаляем..." : {
|
||||||
|
"comment" : "Contact delete in progress"
|
||||||
|
},
|
||||||
"Удалять аккаунт через %lld дн." : {
|
"Удалять аккаунт через %lld дн." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
|
|||||||
@ -48,7 +48,9 @@ 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)) {
|
||||||
|
handleContactDeleted()
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Text(NSLocalizedString("Изменить", comment: "Message profile edit contact button"))
|
Text(NSLocalizedString("Изменить", comment: "Message profile edit contact button"))
|
||||||
}
|
}
|
||||||
@ -596,6 +598,37 @@ struct MessageProfileView: View {
|
|||||||
chatProfile = updatedProfile
|
chatProfile = updatedProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func handleContactDeleted() {
|
||||||
|
guard let profile = currentChatProfile else { return }
|
||||||
|
|
||||||
|
let existingRelationship = profile.relationship
|
||||||
|
let updatedRelationship = RelationshipStatus(
|
||||||
|
isTargetInContactsOfCurrentUser: false,
|
||||||
|
isCurrentUserInContactsOfTarget: existingRelationship?.isCurrentUserInContactsOfTarget ?? false,
|
||||||
|
isTargetUserBlockedByCurrentUser: existingRelationship?.isTargetUserBlockedByCurrentUser ?? false,
|
||||||
|
isCurrentUserInBlacklistOfTarget: existingRelationship?.isCurrentUserInBlacklistOfTarget ?? false
|
||||||
|
)
|
||||||
|
|
||||||
|
let updatedProfile = ChatProfile(
|
||||||
|
userId: profile.userId,
|
||||||
|
login: profile.login,
|
||||||
|
fullName: profile.fullName,
|
||||||
|
customName: nil,
|
||||||
|
bio: profile.bio,
|
||||||
|
lastSeen: profile.lastSeen,
|
||||||
|
createdAt: profile.createdAt,
|
||||||
|
avatars: profile.avatars,
|
||||||
|
stories: profile.stories,
|
||||||
|
permissions: profile.permissions,
|
||||||
|
profilePermissions: profile.profilePermissions,
|
||||||
|
relationship: updatedRelationship,
|
||||||
|
rating: profile.rating,
|
||||||
|
isOfficial: profile.isOfficial
|
||||||
|
)
|
||||||
|
|
||||||
|
chatProfile = updatedProfile
|
||||||
|
}
|
||||||
|
|
||||||
private func handleEditContactTap() {
|
private func handleEditContactTap() {
|
||||||
showPlaceholderAction(
|
showPlaceholderAction(
|
||||||
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
|||||||
@ -55,12 +55,19 @@ struct ContactEditInfo {
|
|||||||
|
|
||||||
struct ContactEditView: View {
|
struct ContactEditView: View {
|
||||||
let contact: ContactEditInfo
|
let contact: ContactEditInfo
|
||||||
|
let onContactDeleted: (() -> Void)?
|
||||||
|
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
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 isDeleting = false
|
||||||
|
@State private var showDeleteConfirmation = false
|
||||||
|
|
||||||
init(contact: ContactEditInfo) {
|
init(contact: ContactEditInfo, onContactDeleted: (() -> Void)? = nil) {
|
||||||
self.contact = contact
|
self.contact = contact
|
||||||
|
self.onContactDeleted = onContactDeleted
|
||||||
let initialName = contact.preferredName
|
let initialName = contact.preferredName
|
||||||
_displayName = State(initialValue: initialName)
|
_displayName = State(initialValue: initialName)
|
||||||
}
|
}
|
||||||
@ -77,9 +84,9 @@ struct ContactEditView: View {
|
|||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
handleDeleteTap()
|
handleDeleteTap()
|
||||||
} label: {
|
} label: {
|
||||||
Text(NSLocalizedString("Удалить контакт", comment: "Contact edit delete action"))
|
deleteButtonLabel
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
}
|
||||||
|
.disabled(isDeleting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(NSLocalizedString("Контакт", comment: "Contact edit title"))
|
.navigationTitle(NSLocalizedString("Контакт", comment: "Contact edit title"))
|
||||||
@ -99,6 +106,37 @@ struct ContactEditView: View {
|
|||||||
dismissButton: .default(Text(NSLocalizedString("Понятно", comment: "Placeholder alert dismiss")))
|
dismissButton: .default(Text(NSLocalizedString("Понятно", comment: "Placeholder alert dismiss")))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.confirmationDialog(
|
||||||
|
NSLocalizedString("Удалить контакт?", comment: "Contact delete confirmation title"),
|
||||||
|
isPresented: $showDeleteConfirmation,
|
||||||
|
titleVisibility: .visible
|
||||||
|
) {
|
||||||
|
Button(NSLocalizedString("Удалить", comment: "Contact delete confirm action"), role: .destructive) {
|
||||||
|
confirmDelete()
|
||||||
|
}
|
||||||
|
Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
|
||||||
|
showDeleteConfirmation = false
|
||||||
|
}
|
||||||
|
} message: {
|
||||||
|
Text(String(
|
||||||
|
format: NSLocalizedString("Контакт \"%1$@\" будет удалён из списка.", comment: "Contact delete confirmation message"),
|
||||||
|
contact.preferredName
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var deleteButtonLabel: some View {
|
||||||
|
if isDeleting {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
ProgressView()
|
||||||
|
Text(NSLocalizedString("Удаляем...", comment: "Contact delete in progress"))
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
} else {
|
||||||
|
Text(NSLocalizedString("Удалить контакт", comment: "Contact edit delete action"))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var avatarSection: some View {
|
private var avatarSection: some View {
|
||||||
@ -185,10 +223,41 @@ struct ContactEditView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleDeleteTap() {
|
private func handleDeleteTap() {
|
||||||
activeAlert = ContactEditAlert(
|
guard !isDeleting else { return }
|
||||||
title: NSLocalizedString("Скоро", comment: "Common soon title"),
|
showDeleteConfirmation = true
|
||||||
message: NSLocalizedString("Удаление контакта появится позже.", comment: "Contact edit delete placeholder message")
|
}
|
||||||
)
|
|
||||||
|
private func confirmDelete() {
|
||||||
|
guard !isDeleting else { return }
|
||||||
|
guard let userId = UUID(uuidString: contact.userId) else {
|
||||||
|
activeAlert = ContactEditAlert(
|
||||||
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
message: NSLocalizedString("Не удалось определить контакт.", comment: "Contact delete invalid user id error")
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isDeleting = true
|
||||||
|
showDeleteConfirmation = false
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await contactsService.removeContact(userId: userId)
|
||||||
|
await MainActor.run {
|
||||||
|
isDeleting = false
|
||||||
|
onContactDeleted?()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
isDeleting = false
|
||||||
|
activeAlert = ContactEditAlert(
|
||||||
|
title: NSLocalizedString("Ошибка", comment: "Common error title"),
|
||||||
|
message: error.localizedDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user