ios_app/Shared/Views/LoginView.swift
2025-06-11 07:54:16 +03:00

171 lines
6.2 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.

//
// LoginView.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct LoginView: View {
@ObservedObject var viewModel: LoginViewModel
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
@State private var isShowingRegistration = false
private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$"
return viewModel.username.range(of: pattern, options: .regularExpression) != nil
}
private var isPasswordValid: Bool {
return viewModel.password.count >= 6 && viewModel.password.count <= 32
}
var body: some View {
ZStack {
Color.clear // чтобы поймать тап
.contentShape(Rectangle())
.onTapGesture {
hideKeyboard()
}
VStack {
HStack {
Button(action: openLanguageSettings) {
Text("🌍")
.padding()
}
Spacer()
Button(action: toggleTheme) {
Image(systemName: isDarkMode ? "moon.fill" : "sun.max.fill")
.padding()
}
}
.onTapGesture {
hideKeyboard()
}
Spacer()
TextField(NSLocalizedString("LoginView_login", comment: ""), text: $viewModel.username)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: viewModel.username) { newValue in
if newValue.count > 32 {
viewModel.username = String(newValue.prefix(32))
}
}
// Показываем ошибку для логина
if !isUsernameValid && !viewModel.username.isEmpty {
Text(NSLocalizedString("LoginView_error_username_invalid", comment: "Неверный логин"))
.foregroundColor(.red)
.font(.caption)
}
// Показываем поле пароля (даже если оно невалидное) только если логин корректен
if isUsernameValid || !viewModel.password.isEmpty {
SecureField(NSLocalizedString("LoginView_password", comment: ""), text: $viewModel.password)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.onChange(of: viewModel.password) { newValue in
if newValue.count > 32 {
viewModel.password = String(newValue.prefix(32))
}
}
// Показываем ошибку для пароля
if !isPasswordValid && !viewModel.password.isEmpty {
Text(NSLocalizedString("LoginView_error_password_invalid", comment: "Неверный пароль"))
.foregroundColor(.red)
.font(.caption)
}
}
if isUsernameValid && isPasswordValid {
Button(action: {
viewModel.login()
}) {
if viewModel.isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.padding()
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.6))
.cornerRadius(8)
} else {
Text(NSLocalizedString("LoginView_button_login", comment: ""))
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
}
.disabled(viewModel.isLoading || !isUsernameValid || !isPasswordValid)
}
Spacer()
// Кнопка регистрации
Button(action: {
isShowingRegistration = true
}) {
Text(NSLocalizedString("LoginView_button_register", comment: "Регистрация"))
.foregroundColor(.blue)
}
.padding(.top, 10)
.sheet(isPresented: $isShowingRegistration) {
RegistrationView(viewModel: viewModel)
}
}
.padding()
.alert(isPresented: $viewModel.showError) {
Alert(
title: Text(NSLocalizedString("LoginView_error", comment: "")),
message: Text(viewModel.errorMessage),
dismissButton: .default(Text(NSLocalizedString("ok", comment: "")))
)
}
.onTapGesture {
hideKeyboard()
}
}
}
private func toggleTheme() {
isDarkMode.toggle()
}
private func openLanguageSettings() {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url)
}
private func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = LoginViewModel()
viewModel.isLoading = false // чтобы убрать спиннер
return LoginView(viewModel: viewModel)
}
}