From 8cc44b06cfaac479e2e5f759145bc1383d68c61f Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 7 Oct 2025 00:28:48 +0300 Subject: [PATCH] fix theme --- yobble/Resources/Localizable.xcstrings | 15 ++-- yobble/Services/ThemeManager.swift | 4 +- yobble/Services/ThemeOption.swift | 74 +++++++++++++++++ yobble/Views/Login/LoginView.swift | 46 +++++++--- yobble/Views/Login/RegistrationView.swift | 4 +- .../Tab/Settings/ChangePasswordView.swift | 80 ++++++++++++++++++ yobble/Views/Tab/Settings/SettingsView.swift | 83 ++----------------- 7 files changed, 209 insertions(+), 97 deletions(-) create mode 100644 yobble/Services/ThemeOption.swift create mode 100644 yobble/Views/Tab/Settings/ChangePasswordView.swift diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index 7a2af57..06226fa 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -173,9 +173,6 @@ }, "Заглушка: Обратная связь" : { - }, - "Заглушка: Сменить пароль" : { - }, "Заглушка: Хранилище данных" : { @@ -354,9 +351,6 @@ } } } - }, - "Мой профиль" : { - }, "Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : { @@ -431,6 +425,9 @@ }, "Нет сообщений" : { + }, + "Новый пароль" : { + "comment" : "Новый пароль" }, "О приложении" : { @@ -523,9 +520,6 @@ }, "Произошла ошибка." : { - }, - "Профиль" : { - }, "Публичная информация" : { @@ -576,6 +570,9 @@ }, "Спасибо!" : { + }, + "Старый пароль" : { + "comment" : "Старый пароль" }, "Темы" : { diff --git a/yobble/Services/ThemeManager.swift b/yobble/Services/ThemeManager.swift index 4f0ac8d..9a2f4be 100644 --- a/yobble/Services/ThemeManager.swift +++ b/yobble/Services/ThemeManager.swift @@ -4,9 +4,9 @@ import SwiftUI enum Theme: String, CaseIterable { case system = "System" case light = "Light" - case oledDark = "Oled" + case oledDark = "Oleg" // case dark = "Dark" // TODO - + var colorScheme: ColorScheme? { switch self { case .system: diff --git a/yobble/Services/ThemeOption.swift b/yobble/Services/ThemeOption.swift new file mode 100644 index 0000000..f9da019 --- /dev/null +++ b/yobble/Services/ThemeOption.swift @@ -0,0 +1,74 @@ +import Foundation + +enum ThemeOption: String, CaseIterable, Identifiable { + case system + case oledDark + case lightTest + case dark + case custom + + var id: String { rawValue } + + var title: String { + switch self { + case .system: + return "Системная" + case .oledDark: + return "OLEG тёмный" + case .dark: + return "Тёмная" + case .lightTest: + return "Светлая" + case .custom: + return "Кастомная" + } + } + + var note: String? { + switch self { + case .lightTest: + return "Тестовая версия" + case .dark, .custom: + return "Недоступна" + default: + return nil + } + } + + var isEnabled: Bool { + switch self { + case .dark, .custom: + return false + default: + return true + } + } + + var mappedTheme: Theme? { + switch self { + case .system: + return .system + case .oledDark: + return .oledDark + case .lightTest: + return .light + case .dark, .custom: + return nil + } + } + + static var ordered: [ThemeOption] { + [.system, .oledDark, .lightTest, .dark, .custom] + } + + static func option(for theme: Theme) -> ThemeOption { + switch theme { + case .system: + return .system + case .light: + return .lightTest + case .oledDark: + return .oledDark + } + } +} diff --git a/yobble/Views/Login/LoginView.swift b/yobble/Views/Login/LoginView.swift index 6152854..2b2ecc4 100644 --- a/yobble/Views/Login/LoginView.swift +++ b/yobble/Views/Login/LoginView.swift @@ -11,6 +11,7 @@ struct LoginView: View { @ObservedObject var viewModel: LoginViewModel @EnvironmentObject private var themeManager: ThemeManager @Environment(\.colorScheme) private var colorScheme + private let themeOptions = ThemeOption.ordered @State private var isShowingRegistration = false @FocusState private var focusedField: Field? @@ -46,7 +47,15 @@ struct LoginView: View { .padding() } Spacer() - Button(action: toggleTheme) { + Menu { + ForEach(themeOptions) { option in + Button(action: { selectTheme(option) }) { + themeMenuContent(for: option) + .opacity(option.isEnabled ? 1.0 : 0.5) + } + .disabled(!option.isEnabled) + } + } label: { Image(systemName: themeIconName) .padding() } @@ -152,11 +161,6 @@ struct LoginView: View { } } } - - - - - private var themeIconName: String { switch themeManager.theme { case .system: @@ -168,14 +172,36 @@ struct LoginView: View { } } - private func toggleTheme() { - themeManager.toggleTheme(from: colorScheme) - } - private func openLanguageSettings() { guard let url = URL(string: UIApplication.openSettingsURLString) else { return } UIApplication.shared.open(url) } + + private var selectedThemeOption: ThemeOption { + ThemeOption.option(for: themeManager.theme) + } + + private func themeMenuContent(for option: ThemeOption) -> some View { + let isSelected = option == selectedThemeOption + + return HStack(spacing: 8) { + Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") + .foregroundColor(isSelected ? .accentColor : .secondary) + VStack(alignment: .leading, spacing: 2) { + Text(option.title) + if let note = option.note { + Text(note) + .font(.caption) + .foregroundColor(.secondary) + } + } + } + } + + private func selectTheme(_ option: ThemeOption) { + guard let mappedTheme = option.mappedTheme else { return } + themeManager.setTheme(mappedTheme) + } } diff --git a/yobble/Views/Login/RegistrationView.swift b/yobble/Views/Login/RegistrationView.swift index c3393cc..9eac315 100644 --- a/yobble/Views/Login/RegistrationView.swift +++ b/yobble/Views/Login/RegistrationView.swift @@ -101,8 +101,8 @@ struct RegistrationView: View { .cornerRadius(8) .autocapitalization(.none) .onChange(of: password) { newValue in - if newValue.count > 32 { - password = String(newValue.prefix(32)) + if newValue.count > 128 { + password = String(newValue.prefix(128)) } } diff --git a/yobble/Views/Tab/Settings/ChangePasswordView.swift b/yobble/Views/Tab/Settings/ChangePasswordView.swift new file mode 100644 index 0000000..709e4d5 --- /dev/null +++ b/yobble/Views/Tab/Settings/ChangePasswordView.swift @@ -0,0 +1,80 @@ +import SwiftUI + + + +struct ChangePasswordView: View { + @State private var oldPassword = "" + @State private var newPassword = "" + @State private var confirmPassword = "" + + private var isOldPasswordValid: Bool { + return oldPassword.count >= 8 && oldPassword.count <= 128 + } + + private var isOldPasswordSame: Bool { + return oldPassword == newPassword + } + + private var isNewPasswordValid: Bool { + return newPassword.count >= 8 && newPassword.count <= 128 + } + + private var isPasswordConfirmValid: Bool { + return newPassword == confirmPassword + } + + var body: some View { + Form { + Section { + + HStack { + SecureField(NSLocalizedString("Старый пароль", comment: "Старый пароль"), text: $oldPassword) + .autocapitalization(.none) + + if !oldPassword.isEmpty { + Image(systemName: isOldPasswordValid ? "checkmark.circle" : "xmark.circle") + .foregroundColor(isOldPasswordValid ? .green : .red) + } + } + + HStack { + SecureField(NSLocalizedString("Новый пароль", comment: "Новый пароль"), text: $newPassword) + .autocapitalization(.none) + + if !newPassword.isEmpty { + let isAllValid = isNewPasswordValid && !isOldPasswordSame + + Image(systemName: isAllValid ? "checkmark.circle" : "xmark.circle") + .foregroundColor(isAllValid ? .green : .red) + } + } + + HStack { + SecureField(NSLocalizedString("Подтверждение пароля", comment: "Подтверждение пароля"), text: $confirmPassword) + .autocapitalization(.none) + + if !confirmPassword.isEmpty { + Image(systemName: isPasswordConfirmValid ? "checkmark.circle" : "xmark.circle") + .foregroundColor(isPasswordConfirmValid ? .green : .red) + } + } + } + + var isButtonEnabled: Bool { + isPasswordConfirmValid && !isOldPasswordSame && isNewPasswordValid && isOldPasswordValid + } + + Button(action: { + // Действие для сохранения профиля + print("oldPassword: \(oldPassword)") + print("newPassword: \(newPassword)") + print("confirmPassword: \(confirmPassword)") + }) { + Text(NSLocalizedString("Применить", comment: "")) + .background(isButtonEnabled ? Color.blue : Color.gray) + } + .disabled(!isButtonEnabled) + } + .navigationTitle(NSLocalizedString("Изменение пароля", comment: "")) + } +} diff --git a/yobble/Views/Tab/Settings/SettingsView.swift b/yobble/Views/Tab/Settings/SettingsView.swift index cdab14e..70b4ff4 100644 --- a/yobble/Views/Tab/Settings/SettingsView.swift +++ b/yobble/Views/Tab/Settings/SettingsView.swift @@ -5,89 +5,24 @@ struct SettingsView: View { @EnvironmentObject private var themeManager: ThemeManager @AppStorage("isDarkMode") private var isDarkMode: Bool = true @State private var isThemeExpanded = false - private let themeOptions: [ThemeOption] = [.system, .dark, .oledDark, .lightTest, .custom] - - private enum ThemeOption: String, CaseIterable, Identifiable { - case system - case dark - case oledDark - case lightTest - case custom - - var id: String { rawValue } - - var title: String { - switch self { - case .system: - return "Системная" - case .oledDark: - return "OLED тёмная" - case .lightTest: - return "Светлая" - case .custom: - return "Кастомная" - case .dark: - return "Тёмная" - } - } - - var note: String? { - switch self { - case .lightTest: - return "Тестовая версия" - case .custom, .dark: - return "Недоступна" - default: - return nil - } - } - - var isEnabled: Bool { - switch self { - case .custom, .dark: - return false - default: - return true - } - } - - var mappedTheme: Theme? { - switch self { - case .system: - return .system - case .lightTest: - return .light - case .oledDark: - return .oledDark - case .custom, .dark: - return nil - } - } - } + private let themeOptions = ThemeOption.ordered private var selectedThemeOption: ThemeOption { - switch themeManager.theme { - case .system: - return .system - case .light: - return .lightTest - case .oledDark: - return .oledDark - } + ThemeOption.option(for: themeManager.theme) } var body: some View { Form { - // MARK: - Профиль - Section(header: Text("Профиль")) { - NavigationLink(destination: EditProfileView()) { - Label("Мой профиль", systemImage: "person.crop.circle") - } - } +// // MARK: - Профиль +// Section(header: Text("Профиль")) { +// NavigationLink(destination: EditProfileView()) { +// Label("Мой профиль", systemImage: "person.crop.circle") +// } +// } // MARK: - Безопасность Section(header: Text("Безопасность")) { - NavigationLink(destination: Text("Заглушка: Сменить пароль")) { + NavigationLink(destination: ChangePasswordView()) { Label("Сменить пароль", systemImage: "key") } NavigationLink(destination: Text("Заглушка: Двухфакторная аутентификация")) {