add delete user from blacklist
This commit is contained in:
		
							parent
							
								
									43a5d8193d
								
							
						
					
					
						commit
						2eabbd59c3
					
				@ -3,6 +3,7 @@ import Foundation
 | 
			
		||||
enum BlockedUsersServiceError: LocalizedError {
 | 
			
		||||
    case unexpectedStatus(String)
 | 
			
		||||
    case decoding(debugDescription: String)
 | 
			
		||||
    case encoding(String)
 | 
			
		||||
 | 
			
		||||
    var errorDescription: String? {
 | 
			
		||||
        switch self {
 | 
			
		||||
@ -12,6 +13,8 @@ enum BlockedUsersServiceError: LocalizedError {
 | 
			
		||||
            return AppConfig.DEBUG
 | 
			
		||||
                ? debugDescription
 | 
			
		||||
                : 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 {
 | 
			
		||||
        let container = try decoder.singleValueContainer()
 | 
			
		||||
        let string = try container.decode(String.self)
 | 
			
		||||
@ -170,3 +227,7 @@ final class BlockedUsersService {
 | 
			
		||||
        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" : {
 | 
			
		||||
@ -1011,6 +1011,9 @@
 | 
			
		||||
    "Не удалось сохранить изменения профиля." : {
 | 
			
		||||
      "comment" : "Profile update unexpected status"
 | 
			
		||||
    },
 | 
			
		||||
    "Не удалось удалить пользователя из списка." : {
 | 
			
		||||
      "comment" : "Blocked users delete unexpected status"
 | 
			
		||||
    },
 | 
			
		||||
    "Неверный запрос (400)." : {
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
@ -1323,7 +1326,7 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Ошибка" : {
 | 
			
		||||
      "comment" : "Profile update error title",
 | 
			
		||||
      "comment" : "Common error title\nProfile update error title",
 | 
			
		||||
      "localizations" : {
 | 
			
		||||
        "en" : {
 | 
			
		||||
          "stringUnit" : {
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,10 @@ struct BlockedUsersView: View {
 | 
			
		||||
    @State private var blockedUsers: [BlockedUser] = []
 | 
			
		||||
    @State private var isLoading = false
 | 
			
		||||
    @State private var loadError: String?
 | 
			
		||||
    @State private var showAddBlockedUserAlert = false
 | 
			
		||||
    @State private var pendingUnblock: BlockedUser?
 | 
			
		||||
    @State private var showUnblockConfirmation = false
 | 
			
		||||
    @State private var removingUserIds: Set<UUID> = []
 | 
			
		||||
    @State private var activeAlert: ActiveAlert?
 | 
			
		||||
 | 
			
		||||
    private let blockedUsersService = BlockedUsersService()
 | 
			
		||||
 | 
			
		||||
@ -49,6 +50,7 @@ struct BlockedUsersView: View {
 | 
			
		||||
                            } label: {
 | 
			
		||||
                                Label(NSLocalizedString("Разблокировать", comment: ""), systemImage: "person.crop.circle.badge.xmark")
 | 
			
		||||
                            }
 | 
			
		||||
                            .disabled(removingUserIds.contains(user.id))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@ -59,7 +61,7 @@ struct BlockedUsersView: View {
 | 
			
		||||
        .toolbar {
 | 
			
		||||
            ToolbarItem(placement: .navigationBarTrailing) {
 | 
			
		||||
                Button {
 | 
			
		||||
                    showAddBlockedUserAlert = true
 | 
			
		||||
                    activeAlert = .addPlaceholder
 | 
			
		||||
                } label: {
 | 
			
		||||
                    Image(systemName: "plus")
 | 
			
		||||
                }
 | 
			
		||||
@ -71,10 +73,21 @@ struct BlockedUsersView: View {
 | 
			
		||||
        .refreshable {
 | 
			
		||||
            await loadBlockedUsers()
 | 
			
		||||
        }
 | 
			
		||||
        .alert(NSLocalizedString("Скоро", comment: "Add blocked user placeholder title"), isPresented: $showAddBlockedUserAlert) {
 | 
			
		||||
            Button(NSLocalizedString("OK", comment: "Common OK"), role: .cancel) {}
 | 
			
		||||
        } message: {
 | 
			
		||||
            Text(NSLocalizedString("Добавление новых блокировок появится позже.", comment: "Add blocked user placeholder message"))
 | 
			
		||||
        .alert(item: $activeAlert) { alert in
 | 
			
		||||
            switch alert {
 | 
			
		||||
            case .addPlaceholder:
 | 
			
		||||
                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(
 | 
			
		||||
            NSLocalizedString("Удалить из заблокированных?", comment: "Unblock confirmation title"),
 | 
			
		||||
@ -82,11 +95,15 @@ struct BlockedUsersView: View {
 | 
			
		||||
            presenting: pendingUnblock
 | 
			
		||||
        ) { user in
 | 
			
		||||
            Button(NSLocalizedString("Разблокировать", comment: "Unblock confirmation action"), role: .destructive) {
 | 
			
		||||
                unblock(user)
 | 
			
		||||
                pendingUnblock = nil
 | 
			
		||||
                showUnblockConfirmation = false
 | 
			
		||||
                Task {
 | 
			
		||||
                    await unblock(user)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
 | 
			
		||||
                pendingUnblock = nil
 | 
			
		||||
                showUnblockConfirmation = false
 | 
			
		||||
            }
 | 
			
		||||
        } message: { user in
 | 
			
		||||
            Text(String(format: NSLocalizedString("Пользователь \"%1$@\" будет удалён из списка заблокированных.", comment: "Unblock confirmation message"), user.displayName))
 | 
			
		||||
@ -141,17 +158,27 @@ struct BlockedUsersView: View {
 | 
			
		||||
            blockedUsers = payloads.map(BlockedUser.init)
 | 
			
		||||
        } catch {
 | 
			
		||||
            loadError = error.localizedDescription
 | 
			
		||||
            if AppConfig.DEBUG {
 | 
			
		||||
                print("[BlockedUsersView] load blocked users failed: \(error)")
 | 
			
		||||
            }
 | 
			
		||||
            activeAlert = .error(message: error.localizedDescription)
 | 
			
		||||
            if AppConfig.DEBUG { print("[BlockedUsersView] load blocked users failed: \(error)") }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        isLoading = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func unblock(_ user: BlockedUser) {
 | 
			
		||||
        // TODO: implement unblock logic when backend is ready
 | 
			
		||||
        blockedUsers.removeAll { $0.id == user.id }
 | 
			
		||||
    @MainActor
 | 
			
		||||
    private func unblock(_ user: BlockedUser) async {
 | 
			
		||||
        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