add change password
This commit is contained in:
parent
ab5218f02a
commit
a588a33338
@ -25,3 +25,7 @@ struct ErrorResponse: Decodable {
|
||||
let data: ErrorPayload?
|
||||
let detail: String?
|
||||
}
|
||||
|
||||
struct MessagePayload: Decodable {
|
||||
let message: String
|
||||
}
|
||||
|
||||
@ -126,6 +126,43 @@ final class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
func changePassword(oldPassword: String, newPassword: String, completion: @escaping (Bool, String?) -> Void) {
|
||||
let payload = ChangePasswordRequestPayload(old_password: oldPassword, new_password: newPassword)
|
||||
guard let body = try? JSONEncoder().encode(payload) else {
|
||||
completion(false, NSLocalizedString("Не удалось сериализовать данные запроса.", comment: ""))
|
||||
return
|
||||
}
|
||||
|
||||
NetworkClient.shared.request(
|
||||
path: "/v1/auth/password/change",
|
||||
method: .post,
|
||||
body: body,
|
||||
requiresAuth: true
|
||||
) { result in
|
||||
switch result {
|
||||
case .success(let response):
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
let apiResponse = try decoder.decode(APIResponse<MessagePayload>.self, from: response.data)
|
||||
|
||||
guard apiResponse.status == "fine" else {
|
||||
let message = apiResponse.detail ?? NSLocalizedString("Не удалось обновить пароль.", comment: "")
|
||||
completion(false, message)
|
||||
return
|
||||
}
|
||||
|
||||
completion(true, apiResponse.data.message)
|
||||
} catch {
|
||||
completion(false, NSLocalizedString("Не удалось обработать ответ сервера.", comment: ""))
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
let message = self.changePasswordErrorMessage(for: error)
|
||||
completion(false, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logoutCurrentUser(completion: @escaping (Bool, String?) -> Void) {
|
||||
guard let currentUser = UserDefaults.standard.string(forKey: "currentUser") else {
|
||||
completion(false, "Не найден текущий пользователь.")
|
||||
@ -237,6 +274,47 @@ final class AuthService {
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
private func changePasswordErrorMessage(for error: NetworkError) -> String {
|
||||
switch error {
|
||||
case .network(let err):
|
||||
return String(format: NSLocalizedString("Ошибка сети: %@", comment: ""), err.localizedDescription)
|
||||
case .server(let statusCode, let data):
|
||||
if let message = extractMessage(from: data) {
|
||||
return message
|
||||
}
|
||||
|
||||
switch statusCode {
|
||||
case 401:
|
||||
return NSLocalizedString("Необходимо авторизоваться заново.", comment: "")
|
||||
case 403:
|
||||
return NSLocalizedString("Старый пароль указан неверно или совпадает с новым.", comment: "")
|
||||
case 422:
|
||||
return NSLocalizedString("Проверьте данные и повторите попытку.", comment: "")
|
||||
case 429:
|
||||
return NSLocalizedString("Слишком много попыток. Попробуйте позже.", comment: "")
|
||||
default:
|
||||
return String(format: NSLocalizedString("Ошибка сервера: %@", comment: ""), "\(statusCode)")
|
||||
}
|
||||
case .unauthorized:
|
||||
return NSLocalizedString("Необходимо авторизоваться заново.", comment: "")
|
||||
case .invalidURL, .noResponse:
|
||||
return NSLocalizedString("Некорректный ответ от сервера.", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
private func extractMessage(from data: Data?) -> String? {
|
||||
guard let data else { return nil }
|
||||
if let response = try? JSONDecoder().decode(ErrorResponse.self, from: data) {
|
||||
if let message = response.data?.message, !message.isEmpty {
|
||||
return message
|
||||
}
|
||||
if let detail = response.detail, !detail.isEmpty {
|
||||
return detail
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private struct LoginRequest: Encodable {
|
||||
@ -249,3 +327,8 @@ private struct RegisterRequest: Encodable {
|
||||
let password: String
|
||||
let invite: String?
|
||||
}
|
||||
|
||||
private struct ChangePasswordRequestPayload: Encodable {
|
||||
let old_password: String
|
||||
let new_password: String
|
||||
}
|
||||
|
||||
@ -380,6 +380,9 @@
|
||||
},
|
||||
"Не удалось загрузить чаты." : {
|
||||
|
||||
},
|
||||
"Не удалось обновить пароль." : {
|
||||
|
||||
},
|
||||
"Не удалось обработать данные чатов." : {
|
||||
|
||||
@ -422,6 +425,9 @@
|
||||
},
|
||||
"Некорректный ответ от сервера." : {
|
||||
|
||||
},
|
||||
"Необходимо авторизоваться заново." : {
|
||||
|
||||
},
|
||||
"Нет аккаунта? Регистрация" : {
|
||||
"comment" : "Регистрация"
|
||||
@ -449,6 +455,9 @@
|
||||
},
|
||||
"Отправляем..." : {
|
||||
|
||||
},
|
||||
"Ошибка" : {
|
||||
|
||||
},
|
||||
"Ошибка авторизации" : {
|
||||
|
||||
@ -473,12 +482,21 @@
|
||||
},
|
||||
"Пароли не совпадают" : {
|
||||
"comment" : "Пароли не совпадают"
|
||||
},
|
||||
"Пароли не совпадают." : {
|
||||
|
||||
},
|
||||
"Пароль" : {
|
||||
"comment" : "Пароль"
|
||||
},
|
||||
"Пароль должен быть от 8 до 128 символов" : {
|
||||
"comment" : "Пароль должен быть от 6 до 32 символов"
|
||||
},
|
||||
"Пароль обновлен" : {
|
||||
|
||||
},
|
||||
"Пароль успешно обновлен." : {
|
||||
|
||||
},
|
||||
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
|
||||
"comment" : "FAQ answer: reset password"
|
||||
@ -520,6 +538,9 @@
|
||||
},
|
||||
"Применить" : {
|
||||
|
||||
},
|
||||
"Проверьте данные и повторите попытку." : {
|
||||
|
||||
},
|
||||
"Произошла ошибка." : {
|
||||
|
||||
@ -564,6 +585,9 @@
|
||||
},
|
||||
"Слишком много запросов." : {
|
||||
|
||||
},
|
||||
"Слишком много попыток. Попробуйте позже." : {
|
||||
|
||||
},
|
||||
"Сменить пароль" : {
|
||||
|
||||
@ -576,9 +600,15 @@
|
||||
},
|
||||
"Старый пароль" : {
|
||||
"comment" : "Старый пароль"
|
||||
},
|
||||
"Старый пароль указан неверно или совпадает с новым." : {
|
||||
|
||||
},
|
||||
"Темы" : {
|
||||
|
||||
},
|
||||
"Ты шо ебанутый? А ниче тот факт что новый пароль должен отличаться от старого." : {
|
||||
|
||||
},
|
||||
"Уведомления" : {
|
||||
|
||||
|
||||
34
yobble/ViewModels/ChangePasswordViewModel.swift
Normal file
34
yobble/ViewModels/ChangePasswordViewModel.swift
Normal file
@ -0,0 +1,34 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
final class ChangePasswordViewModel: ObservableObject {
|
||||
@Published private(set) var isLoading: Bool = false
|
||||
@Published var successMessage: String?
|
||||
@Published var errorMessage: String?
|
||||
|
||||
private let authService: AuthService
|
||||
|
||||
init(authService: AuthService = AuthService()) {
|
||||
self.authService = authService
|
||||
}
|
||||
|
||||
func changePassword(oldPassword: String, newPassword: String) {
|
||||
guard !isLoading else { return }
|
||||
|
||||
isLoading = true
|
||||
successMessage = nil
|
||||
errorMessage = nil
|
||||
|
||||
authService.changePassword(oldPassword: oldPassword, newPassword: newPassword) { [weak self] success, message in
|
||||
guard let self else { return }
|
||||
DispatchQueue.main.async {
|
||||
self.isLoading = false
|
||||
if success {
|
||||
self.successMessage = message ?? NSLocalizedString("Пароль успешно обновлен.", comment: "")
|
||||
} else {
|
||||
self.errorMessage = message ?? NSLocalizedString("Не удалось обновить пароль.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +1,33 @@
|
||||
import SwiftUI
|
||||
|
||||
|
||||
|
||||
struct ChangePasswordView: View {
|
||||
@StateObject private var viewModel = ChangePasswordViewModel()
|
||||
@State private var oldPassword = ""
|
||||
@State private var newPassword = ""
|
||||
@State private var confirmPassword = ""
|
||||
@State private var isOldPasswordVisible = false
|
||||
@State private var isNewPasswordVisible = false
|
||||
@State private var isConfirmPasswordVisible = false
|
||||
@State private var alertData: AlertData?
|
||||
|
||||
private var isOldPasswordValid: Bool {
|
||||
return oldPassword.count >= 8 && oldPassword.count <= 128
|
||||
oldPassword.count >= 8 && oldPassword.count <= 128
|
||||
}
|
||||
|
||||
private var isOldPasswordSame: Bool {
|
||||
return oldPassword == newPassword
|
||||
oldPassword == newPassword
|
||||
}
|
||||
|
||||
private var isNewPasswordValid: Bool {
|
||||
return newPassword.count >= 8 && newPassword.count <= 128
|
||||
newPassword.count >= 8 && newPassword.count <= 128
|
||||
}
|
||||
|
||||
private var isPasswordConfirmValid: Bool {
|
||||
return newPassword == confirmPassword
|
||||
newPassword == confirmPassword
|
||||
}
|
||||
|
||||
private var isButtonEnabled: Bool {
|
||||
isPasswordConfirmValid && !isOldPasswordSame && isNewPasswordValid && isOldPasswordValid && !viewModel.isLoading
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@ -91,6 +95,11 @@ struct ChangePasswordView: View {
|
||||
.foregroundColor(isAllValid ? .green : .red)
|
||||
}
|
||||
}
|
||||
if isOldPasswordSame && !newPassword.isEmpty {
|
||||
Text(NSLocalizedString("Ты шо ебанутый? А ниче тот факт что новый пароль должен отличаться от старого.", comment: ""))
|
||||
.font(.caption)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
|
||||
HStack {
|
||||
if isConfirmPasswordVisible {
|
||||
@ -121,21 +130,27 @@ struct ChangePasswordView: View {
|
||||
.foregroundColor(isPasswordConfirmValid ? .green : .red)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if !confirmPassword.isEmpty && !isPasswordConfirmValid {
|
||||
Text(NSLocalizedString("Пароли не совпадают.", comment: ""))
|
||||
.font(.caption)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
|
||||
var isButtonEnabled: Bool {
|
||||
isPasswordConfirmValid && !isOldPasswordSame && isNewPasswordValid && isOldPasswordValid
|
||||
|
||||
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
// Действие для сохранения профиля
|
||||
print("oldPassword: \(oldPassword)")
|
||||
print("newPassword: \(newPassword)")
|
||||
print("confirmPassword: \(confirmPassword)")
|
||||
viewModel.changePassword(oldPassword: oldPassword, newPassword: newPassword)
|
||||
}) {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray.opacity(0.6))
|
||||
.cornerRadius(8)
|
||||
} else {
|
||||
Text(NSLocalizedString("Применить", comment: ""))
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
@ -143,11 +158,51 @@ struct ChangePasswordView: View {
|
||||
.background(isButtonEnabled ? Color.blue : Color.gray)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
.disabled(!isButtonEnabled)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.listRowInsets(EdgeInsets())
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.navigationTitle(NSLocalizedString("Изменение пароля", comment: ""))
|
||||
.onChange(of: viewModel.successMessage) { message in
|
||||
guard let message else { return }
|
||||
alertData = AlertData(kind: .success, message: message)
|
||||
}
|
||||
.onChange(of: viewModel.errorMessage) { message in
|
||||
guard let message else { return }
|
||||
alertData = AlertData(kind: .error, message: message)
|
||||
}
|
||||
.alert(item: $alertData) { data in
|
||||
Alert(
|
||||
title: Text(data.kind == .success
|
||||
? NSLocalizedString("Пароль обновлен", comment: "")
|
||||
: NSLocalizedString("Ошибка", comment: "")),
|
||||
message: Text(data.message),
|
||||
dismissButton: .default(Text(NSLocalizedString("OK", comment: ""))) {
|
||||
switch data.kind {
|
||||
case .success:
|
||||
oldPassword = ""
|
||||
newPassword = ""
|
||||
confirmPassword = ""
|
||||
viewModel.successMessage = nil
|
||||
case .error:
|
||||
viewModel.errorMessage = nil
|
||||
}
|
||||
alertData = nil
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AlertData: Identifiable {
|
||||
enum Kind {
|
||||
case success
|
||||
case error
|
||||
}
|
||||
|
||||
let id = UUID()
|
||||
let kind: Kind
|
||||
let message: String
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user