burger menu

This commit is contained in:
cheykrym 2025-10-05 06:00:54 +03:00
parent 5d74af5597
commit 102826ac8a
8 changed files with 46 additions and 27 deletions

View File

@ -7,7 +7,8 @@ struct TopBarView: View {
@Binding var selectedAccount: String @Binding var selectedAccount: String
// @Binding var sheetType: ProfileTab.SheetType? // @Binding var sheetType: ProfileTab.SheetType?
var accounts: [String] var accounts: [String]
var viewModel: LoginViewModel // var viewModel: LoginViewModel
@ObservedObject var viewModel: LoginViewModel
// Привязка для управления боковым меню // Привязка для управления боковым меню
@Binding var isSideMenuPresented: Bool @Binding var isSideMenuPresented: Bool
@ -45,7 +46,7 @@ struct TopBarView: View {
Spacer() Spacer()
Button(action: { }) { Button(action: { }) {
HStack(spacing: 4) { HStack(spacing: 4) {
Text(selectedAccount) Text("@\(viewModel.username)")
.font(.headline) .font(.headline)
.foregroundColor(.primary) .foregroundColor(.primary)
Image(systemName: "chevron.down") Image(systemName: "chevron.down")

View File

@ -104,7 +104,6 @@ class AuthService {
// Сохраняем токены в Keychain // Сохраняем токены в Keychain
KeychainService.shared.save(loginResponse.access_token, forKey: "access_token", service: username) KeychainService.shared.save(loginResponse.access_token, forKey: "access_token", service: username)
KeychainService.shared.save(loginResponse.refresh_token, forKey: "refresh_token", service: username) KeychainService.shared.save(loginResponse.refresh_token, forKey: "refresh_token", service: username)
print("loginResponse.user_id \(loginResponse.user_id)")
KeychainService.shared.save(loginResponse.user_id, forKey: "userId", service: username) KeychainService.shared.save(loginResponse.user_id, forKey: "userId", service: username)
UserDefaults.standard.set(username, forKey: "currentUser") UserDefaults.standard.set(username, forKey: "currentUser")

View File

@ -1,6 +1,9 @@
{ {
"sourceLanguage" : "en", "sourceLanguage" : "en",
"strings" : { "strings" : {
"@%@" : {
},
"@yourusername" : { "@yourusername" : {
}, },

View File

@ -25,7 +25,7 @@ class LoginViewModel: ObservableObject {
} }
init() { init() {
loadStoredUser() // loadStoredUser()
// Запускаем автологин // Запускаем автологин
autoLogin() autoLogin()
@ -113,6 +113,6 @@ class LoginViewModel: ObservableObject {
username = defaults.string(forKey: DefaultsKeys.currentUser) ?? "" username = defaults.string(forKey: DefaultsKeys.currentUser) ?? ""
userId = KeychainService.shared.get(forKey: DefaultsKeys.userId, service: username) ?? "" userId = KeychainService.shared.get(forKey: DefaultsKeys.userId, service: username) ?? ""
print("username: \(username) | userId: \(userId)") if AppConfig.DEBUG{ print("username: \(username) | userId: \(userId)")}
} }
} }

View File

@ -13,6 +13,12 @@ struct LoginView: View {
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
@State private var isShowingRegistration = false @State private var isShowingRegistration = false
@FocusState private var focusedField: Field?
private enum Field: Hashable {
case username
case password
}
private var isUsernameValid: Bool { private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$" let pattern = "^[A-Za-z0-9_]{3,32}$"
@ -29,7 +35,7 @@ struct LoginView: View {
Color.clear // чтобы поймать тап Color.clear // чтобы поймать тап
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { .onTapGesture {
hideKeyboard() focusedField = nil
} }
VStack { VStack {
@ -46,8 +52,8 @@ struct LoginView: View {
} }
} }
.onTapGesture { .onTapGesture {
hideKeyboard() focusedField = nil
} }
Spacer() Spacer()
@ -57,6 +63,7 @@ struct LoginView: View {
.cornerRadius(8) .cornerRadius(8)
.autocapitalization(.none) .autocapitalization(.none)
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($focusedField, equals: .username)
.onChange(of: viewModel.username) { newValue in .onChange(of: viewModel.username) { newValue in
if newValue.count > 32 { if newValue.count > 32 {
viewModel.username = String(newValue.prefix(32)) viewModel.username = String(newValue.prefix(32))
@ -76,6 +83,7 @@ struct LoginView: View {
.background(Color(.secondarySystemBackground)) .background(Color(.secondarySystemBackground))
.cornerRadius(8) .cornerRadius(8)
.autocapitalization(.none) .autocapitalization(.none)
.focused($focusedField, equals: .password)
.onChange(of: viewModel.password) { newValue in .onChange(of: viewModel.password) { newValue in
if newValue.count > 32 { if newValue.count > 32 {
viewModel.password = String(newValue.prefix(32)) viewModel.password = String(newValue.prefix(32))
@ -140,8 +148,8 @@ struct LoginView: View {
) )
} }
.onTapGesture { .onTapGesture {
hideKeyboard() focusedField = nil
} }
} }
} }
@ -169,10 +177,6 @@ struct LoginView: View {
UIApplication.shared.open(url) UIApplication.shared.open(url)
} }
private func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
} }
struct LoginView_Previews: PreviewProvider { struct LoginView_Previews: PreviewProvider {

View File

@ -22,6 +22,15 @@ struct RegistrationView: View {
@State private var showError: Bool = false @State private var showError: Bool = false
@State private var errorMessage: String = "" @State private var errorMessage: String = ""
@FocusState private var focusedField: Field?
private enum Field: Hashable {
case username
case password
case confirmPassword
case invite
}
private var isUsernameValid: Bool { private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$" let pattern = "^[A-Za-z0-9_]{3,32}$"
return username.range(of: pattern, options: .regularExpression) != nil return username.range(of: pattern, options: .regularExpression) != nil
@ -45,7 +54,7 @@ struct RegistrationView: View {
ZStack(alignment: .top) { ZStack(alignment: .top) {
Color.clear Color.clear
.contentShape(Rectangle()) .contentShape(Rectangle())
.onTapGesture { hideKeyboard() } .onTapGesture { focusedField = nil }
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
Group { Group {
@ -53,6 +62,7 @@ struct RegistrationView: View {
TextField(NSLocalizedString("Логин", comment: "Логин"), text: $username) TextField(NSLocalizedString("Логин", comment: "Логин"), text: $username)
.autocapitalization(.none) .autocapitalization(.none)
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($focusedField, equals: .username)
Spacer() Spacer()
if !username.isEmpty { if !username.isEmpty {
Image(systemName: isUsernameValid ? "checkmark.circle" : "xmark.circle") Image(systemName: isUsernameValid ? "checkmark.circle" : "xmark.circle")
@ -79,6 +89,7 @@ struct RegistrationView: View {
HStack { HStack {
SecureField(NSLocalizedString("Пароль", comment: "Пароль"), text: $password) SecureField(NSLocalizedString("Пароль", comment: "Пароль"), text: $password)
.autocapitalization(.none) .autocapitalization(.none)
.focused($focusedField, equals: .password)
Spacer() Spacer()
if !password.isEmpty { if !password.isEmpty {
Image(systemName: isPasswordValid ? "checkmark.circle" : "xmark.circle") Image(systemName: isPasswordValid ? "checkmark.circle" : "xmark.circle")
@ -104,6 +115,7 @@ struct RegistrationView: View {
HStack { HStack {
SecureField(NSLocalizedString("Подтверждение пароля", comment: "Подтверждение пароля"), text: $confirmPassword) SecureField(NSLocalizedString("Подтверждение пароля", comment: "Подтверждение пароля"), text: $confirmPassword)
.autocapitalization(.none) .autocapitalization(.none)
.focused($focusedField, equals: .confirmPassword)
Spacer() Spacer()
if !confirmPassword.isEmpty { if !confirmPassword.isEmpty {
Image(systemName: isConfirmPasswordValid ? "checkmark.circle" : "xmark.circle") Image(systemName: isConfirmPasswordValid ? "checkmark.circle" : "xmark.circle")
@ -132,6 +144,7 @@ struct RegistrationView: View {
.cornerRadius(8) .cornerRadius(8)
.autocapitalization(.none) .autocapitalization(.none)
.disableAutocorrection(true) .disableAutocorrection(true)
.focused($focusedField, equals: .invite)
} }
Button(action: registerUser) { Button(action: registerUser) {
@ -189,14 +202,10 @@ struct RegistrationView: View {
} }
private func dismissSheet() { private func dismissSheet() {
hideKeyboard() focusedField = nil
isPresented = false isPresented = false
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
} }
private func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
} }

View File

@ -79,7 +79,7 @@ struct MainView: View {
.allowsHitTesting(menuOffset > 0) .allowsHitTesting(menuOffset > 0)
// Боковое меню // Боковое меню
SideMenuView(isPresented: $isSideMenuPresented) SideMenuView(viewModel: viewModel, isPresented: $isSideMenuPresented)
.frame(width: menuWidth) .frame(width: menuWidth)
.offset(x: -menuWidth + menuOffset) // Новая логика смещения .offset(x: -menuWidth + menuOffset) // Новая логика смещения
.ignoresSafeArea(edges: .vertical) .ignoresSafeArea(edges: .vertical)

View File

@ -62,6 +62,7 @@ struct SideMenuFooterButton: View {
// --- MAIN VIEW --- // --- MAIN VIEW ---
struct SideMenuView: View { struct SideMenuView: View {
@ObservedObject var viewModel: LoginViewModel
@EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var themeManager: ThemeManager
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@Binding var isPresented: Bool @Binding var isPresented: Bool
@ -132,10 +133,10 @@ struct SideMenuView: View {
}) { }) {
HStack { HStack {
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text("Your Name") Text("@\(viewModel.username)")
.font(.title3).bold() .font(.title3).bold()
Text("@yourusername") // Text("@yourusername")
.font(.footnote) // .font(.footnote)
} }
.foregroundColor(.primary) .foregroundColor(.primary)
@ -172,8 +173,9 @@ struct SideMenuView: View {
} }
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(account.name).font(.footnote).bold() // Smaller text Text(account.username).font(.footnote).bold() // Smaller text
Text(account.username).font(.caption2) // Smaller text // Text(account.name).font(.footnote).bold() // Smaller text
// Text(account.username).font(.caption2) // Smaller text
} }
.foregroundColor(.primary) .foregroundColor(.primary)
} }
@ -253,7 +255,8 @@ struct SideMenuView: View {
struct SideMenuView_Previews: PreviewProvider { struct SideMenuView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
SideMenuView(isPresented: .constant(true)) let mockViewModel = LoginViewModel()
SideMenuView(viewModel: mockViewModel, isPresented: .constant(true))
.environmentObject(ThemeManager()) .environmentObject(ThemeManager())
} }
} }