ios_app/Shared/Views/login/RegistrationView.swift
2025-07-12 02:41:46 +03:00

214 lines
8.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// RegistrationView.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct RegistrationView: View {
@ObservedObject var viewModel: LoginViewModel
@Environment(\.presentationMode) private var presentationMode
@State private var username: String = ""
@State private var password: String = ""
@State private var confirmPassword: String = ""
@State private var inviteCode: String = ""
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
@State private var isLoading: Bool = false
@State private var showError: Bool = false
@State private var errorMessage: String = ""
private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$"
return username.range(of: pattern, options: .regularExpression) != nil
}
private var isPasswordValid: Bool {
password.count >= 8 && password.count <= 128
}
private var isConfirmPasswordValid: Bool {
confirmPassword == password && !confirmPassword.isEmpty
}
private var isFormValid: Bool {
isUsernameValid && isPasswordValid && isConfirmPasswordValid
}
var body: some View {
NavigationView {
ScrollView {
ZStack {
Color.clear
.contentShape(Rectangle())
.onTapGesture {
hideKeyboard()
}
VStack(alignment: .leading, spacing: 16) {
Group {
HStack {
TextField(NSLocalizedString("RegistrationView_login", comment: "Логин"), text: $username)
.autocapitalization(.none)
.disableAutocorrection(true)
Spacer()
if !username.isEmpty {
Image(systemName: isUsernameValid ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isUsernameValid ? .green : .red)
}
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: username) { newValue in
if newValue.count > 32 {
username = String(newValue.prefix(32))
}
}
if !isUsernameValid && !username.isEmpty {
Text(NSLocalizedString("RegistrationView_error_username_invalid", comment: "Неверный логин"))
.foregroundColor(.red)
.font(.caption)
}
HStack {
SecureField(NSLocalizedString("RegistrationView_password", comment: "Пароль"), text: $password)
.autocapitalization(.none)
Spacer()
if !password.isEmpty {
Image(systemName: isPasswordValid ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isPasswordValid ? .green : .red)
}
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.onChange(of: password) { newValue in
if newValue.count > 32 {
password = String(newValue.prefix(32))
}
}
if !isPasswordValid && !password.isEmpty {
Text(NSLocalizedString("RegistrationView_error_password_invalid", comment: "Пароль должен быть от 6 до 32 символов"))
.foregroundColor(.red)
.font(.caption)
}
HStack {
SecureField(NSLocalizedString("RegistrationView_confirm_password", comment: "Подтверждение пароля"), text: $confirmPassword)
.autocapitalization(.none)
Spacer()
if !confirmPassword.isEmpty {
Image(systemName: isConfirmPasswordValid ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isConfirmPasswordValid ? .green : .red)
}
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.onChange(of: confirmPassword) { newValue in
if newValue.count > 32 {
confirmPassword = String(newValue.prefix(32))
}
}
if !isConfirmPasswordValid && !confirmPassword.isEmpty {
Text(NSLocalizedString("RegistrationView_error_confirm_password_invalid", comment: "Пароли не совпадают"))
.foregroundColor(.red)
.font(.caption)
}
TextField(NSLocalizedString("RegistrationView_invite", comment: "Инвайт-код"), text: $inviteCode)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
}
Button(action: registerUser) {
if isLoading {
ProgressView()
.padding()
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.6))
.cornerRadius(8)
} else {
Text(NSLocalizedString("RegistrationView_button_register", comment: "Зарегистрироваться"))
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(isFormValid ? Color.blue : Color.gray.opacity(0.6))
.cornerRadius(8)
}
}
.disabled(!isFormValid)
.padding(.bottom)
}
.padding()
}
.navigationBarItems(trailing:
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Text(NSLocalizedString("RegistrationView_close", comment: "Закрыть"))
}
)
.navigationTitle(Text(NSLocalizedString("RegistrationView_title", comment: "Регистрация")))
.alert(isPresented: $showError) {
Alert(
title: Text(NSLocalizedString("RegistrationView_error", comment: "Ошибка")),
message: Text(errorMessage),
dismissButton: .default(Text(NSLocalizedString("ok", comment: "")))
)
}
}
.onTapGesture {
hideKeyboard()
}
}
.onTapGesture {
hideKeyboard()
}
}
private func registerUser() {
isLoading = true
errorMessage = ""
viewModel.registerUser(username: username, password: password, invite: inviteCode.isEmpty ? nil : inviteCode) { success, message in
isLoading = false
if success {
presentationMode.wrappedValue.dismiss()
} else {
errorMessage = message ?? "Неизвестная ошибка."
showError = true
}
}
}
private func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = LoginViewModel()
viewModel.isLoading = false // чтобы убрать спиннер
return RegistrationView(viewModel: viewModel)
}
}