add delete user from blacklist
This commit is contained in:
parent
43a5d8193d
commit
2eabbd59c3
@ -3,6 +3,7 @@ import Foundation
|
|||||||
enum BlockedUsersServiceError: LocalizedError {
|
enum BlockedUsersServiceError: LocalizedError {
|
||||||
case unexpectedStatus(String)
|
case unexpectedStatus(String)
|
||||||
case decoding(debugDescription: String)
|
case decoding(debugDescription: String)
|
||||||
|
case encoding(String)
|
||||||
|
|
||||||
var errorDescription: String? {
|
var errorDescription: String? {
|
||||||
switch self {
|
switch self {
|
||||||
@ -12,6 +13,8 @@ enum BlockedUsersServiceError: LocalizedError {
|
|||||||
return AppConfig.DEBUG
|
return AppConfig.DEBUG
|
||||||
? debugDescription
|
? debugDescription
|
||||||
: NSLocalizedString("Не удалось загрузить список.", comment: "Blocked users service decoding error")
|
: NSLocalizedString("Не удалось загрузить список.", comment: "Blocked users service decoding error")
|
||||||
|
case .encoding(let message):
|
||||||
|
return message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,6 +81,60 @@ final class BlockedUsersService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func remove(userId: UUID, completion: @escaping (Result<String, Error>) -> Void) {
|
||||||
|
let request = BlockedUserDeleteRequest(userId: userId)
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.keyEncodingStrategy = .convertToSnakeCase
|
||||||
|
|
||||||
|
guard let body = try? encoder.encode(request) else {
|
||||||
|
let message = NSLocalizedString("Не удалось подготовить данные запроса.", comment: "Blocked users delete encoding error")
|
||||||
|
completion(.failure(BlockedUsersServiceError.encoding(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client.request(
|
||||||
|
path: "/v1/user/blacklist/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: "Blocked users delete unexpected status")
|
||||||
|
completion(.failure(BlockedUsersServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.success(apiResponse.data.message))
|
||||||
|
} catch {
|
||||||
|
let debugMessage = Self.describeDecodingError(error: error, data: response.data)
|
||||||
|
if AppConfig.DEBUG {
|
||||||
|
print("[BlockedUsersService] decode delete response failed: \(debugMessage)")
|
||||||
|
}
|
||||||
|
completion(.failure(BlockedUsersServiceError.decoding(debugDescription: debugMessage)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
if case let NetworkError.server(_, data) = error,
|
||||||
|
let data,
|
||||||
|
let message = Self.errorMessage(from: data) {
|
||||||
|
completion(.failure(BlockedUsersServiceError.unexpectedStatus(message)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(userId: UUID) async throws -> String {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
remove(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)
|
||||||
@ -170,3 +227,7 @@ final class BlockedUsersService {
|
|||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct BlockedUserDeleteRequest: Encodable {
|
||||||
|
let userId: UUID
|
||||||
|
}
|
||||||
|
|||||||
@ -993,7 +993,7 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
"Не удалось подготовить данные запроса." : {
|
"Не удалось подготовить данные запроса." : {
|
||||||
"comment" : "Profile update encoding error"
|
"comment" : "Blocked users delete encoding error\nProfile update encoding error"
|
||||||
},
|
},
|
||||||
"Не удалось сериализовать данные запроса." : {
|
"Не удалось сериализовать данные запроса." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1011,6 +1011,9 @@
|
|||||||
"Не удалось сохранить изменения профиля." : {
|
"Не удалось сохранить изменения профиля." : {
|
||||||
"comment" : "Profile update unexpected status"
|
"comment" : "Profile update unexpected status"
|
||||||
},
|
},
|
||||||
|
"Не удалось удалить пользователя из списка." : {
|
||||||
|
"comment" : "Blocked users delete unexpected status"
|
||||||
|
},
|
||||||
"Неверный запрос (400)." : {
|
"Неверный запрос (400)." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1323,7 +1326,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Ошибка" : {
|
"Ошибка" : {
|
||||||
"comment" : "Profile update error title",
|
"comment" : "Common error title\nProfile update error title",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
|||||||
@ -4,9 +4,10 @@ struct BlockedUsersView: View {
|
|||||||
@State private var blockedUsers: [BlockedUser] = []
|
@State private var blockedUsers: [BlockedUser] = []
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
@State private var loadError: String?
|
@State private var loadError: String?
|
||||||
@State private var showAddBlockedUserAlert = false
|
|
||||||
@State private var pendingUnblock: BlockedUser?
|
@State private var pendingUnblock: BlockedUser?
|
||||||
@State private var showUnblockConfirmation = false
|
@State private var showUnblockConfirmation = false
|
||||||
|
@State private var removingUserIds: Set<UUID> = []
|
||||||
|
@State private var activeAlert: ActiveAlert?
|
||||||
|
|
||||||
private let blockedUsersService = BlockedUsersService()
|
private let blockedUsersService = BlockedUsersService()
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ struct BlockedUsersView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label(NSLocalizedString("Разблокировать", comment: ""), systemImage: "person.crop.circle.badge.xmark")
|
Label(NSLocalizedString("Разблокировать", comment: ""), systemImage: "person.crop.circle.badge.xmark")
|
||||||
}
|
}
|
||||||
|
.disabled(removingUserIds.contains(user.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +61,7 @@ struct BlockedUsersView: View {
|
|||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
showAddBlockedUserAlert = true
|
activeAlert = .addPlaceholder
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
}
|
}
|
||||||
@ -71,10 +73,21 @@ struct BlockedUsersView: View {
|
|||||||
.refreshable {
|
.refreshable {
|
||||||
await loadBlockedUsers()
|
await loadBlockedUsers()
|
||||||
}
|
}
|
||||||
.alert(NSLocalizedString("Скоро", comment: "Add blocked user placeholder title"), isPresented: $showAddBlockedUserAlert) {
|
.alert(item: $activeAlert) { alert in
|
||||||
Button(NSLocalizedString("OK", comment: "Common OK"), role: .cancel) {}
|
switch alert {
|
||||||
} message: {
|
case .addPlaceholder:
|
||||||
Text(NSLocalizedString("Добавление новых блокировок появится позже.", comment: "Add blocked user placeholder message"))
|
return Alert(
|
||||||
|
title: Text(NSLocalizedString("Скоро", comment: "Add blocked user placeholder title")),
|
||||||
|
message: Text(NSLocalizedString("Добавление новых блокировок появится позже.", comment: "Add blocked user placeholder message")),
|
||||||
|
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
|
||||||
|
)
|
||||||
|
case .error(_, let message):
|
||||||
|
return Alert(
|
||||||
|
title: Text(NSLocalizedString("Ошибка", comment: "Common error title")),
|
||||||
|
message: Text(message),
|
||||||
|
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.confirmationDialog(
|
.confirmationDialog(
|
||||||
NSLocalizedString("Удалить из заблокированных?", comment: "Unblock confirmation title"),
|
NSLocalizedString("Удалить из заблокированных?", comment: "Unblock confirmation title"),
|
||||||
@ -82,11 +95,15 @@ struct BlockedUsersView: View {
|
|||||||
presenting: pendingUnblock
|
presenting: pendingUnblock
|
||||||
) { user in
|
) { user in
|
||||||
Button(NSLocalizedString("Разблокировать", comment: "Unblock confirmation action"), role: .destructive) {
|
Button(NSLocalizedString("Разблокировать", comment: "Unblock confirmation action"), role: .destructive) {
|
||||||
unblock(user)
|
|
||||||
pendingUnblock = nil
|
pendingUnblock = nil
|
||||||
|
showUnblockConfirmation = false
|
||||||
|
Task {
|
||||||
|
await unblock(user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
|
Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
|
||||||
pendingUnblock = nil
|
pendingUnblock = nil
|
||||||
|
showUnblockConfirmation = false
|
||||||
}
|
}
|
||||||
} message: { user in
|
} message: { user in
|
||||||
Text(String(format: NSLocalizedString("Пользователь \"%1$@\" будет удалён из списка заблокированных.", comment: "Unblock confirmation message"), user.displayName))
|
Text(String(format: NSLocalizedString("Пользователь \"%1$@\" будет удалён из списка заблокированных.", comment: "Unblock confirmation message"), user.displayName))
|
||||||
@ -141,17 +158,27 @@ struct BlockedUsersView: View {
|
|||||||
blockedUsers = payloads.map(BlockedUser.init)
|
blockedUsers = payloads.map(BlockedUser.init)
|
||||||
} catch {
|
} catch {
|
||||||
loadError = error.localizedDescription
|
loadError = error.localizedDescription
|
||||||
if AppConfig.DEBUG {
|
activeAlert = .error(message: error.localizedDescription)
|
||||||
print("[BlockedUsersView] load blocked users failed: \(error)")
|
if AppConfig.DEBUG { print("[BlockedUsersView] load blocked users failed: \(error)") }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unblock(_ user: BlockedUser) {
|
@MainActor
|
||||||
// TODO: implement unblock logic when backend is ready
|
private func unblock(_ user: BlockedUser) async {
|
||||||
blockedUsers.removeAll { $0.id == user.id }
|
guard !removingUserIds.contains(user.id) else { return }
|
||||||
|
|
||||||
|
removingUserIds.insert(user.id)
|
||||||
|
defer { removingUserIds.remove(user.id) }
|
||||||
|
|
||||||
|
do {
|
||||||
|
_ = try await blockedUsersService.remove(userId: user.id)
|
||||||
|
blockedUsers.removeAll { $0.id == user.id }
|
||||||
|
} catch {
|
||||||
|
activeAlert = .error(message: error.localizedDescription)
|
||||||
|
if AppConfig.DEBUG { print("[BlockedUsersView] unblock failed: \(error)") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,3 +233,17 @@ private struct BlockedUser: Identifiable, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum ActiveAlert: Identifiable {
|
||||||
|
case addPlaceholder
|
||||||
|
case error(id: UUID = UUID(), message: String)
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
switch self {
|
||||||
|
case .addPlaceholder:
|
||||||
|
return "addPlaceholder"
|
||||||
|
case .error(let id, _):
|
||||||
|
return id.uuidString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user