fix theme
This commit is contained in:
parent
d502a059b9
commit
8cc44b06cf
@ -173,9 +173,6 @@
|
|||||||
},
|
},
|
||||||
"Заглушка: Обратная связь" : {
|
"Заглушка: Обратная связь" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Заглушка: Сменить пароль" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Заглушка: Хранилище данных" : {
|
"Заглушка: Хранилище данных" : {
|
||||||
|
|
||||||
@ -354,9 +351,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Мой профиль" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : {
|
"Мы планируем заменить вкладку. Поделитесь, что бы вы хотели видеть здесь чаще всего." : {
|
||||||
|
|
||||||
@ -431,6 +425,9 @@
|
|||||||
},
|
},
|
||||||
"Нет сообщений" : {
|
"Нет сообщений" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Новый пароль" : {
|
||||||
|
"comment" : "Новый пароль"
|
||||||
},
|
},
|
||||||
"О приложении" : {
|
"О приложении" : {
|
||||||
|
|
||||||
@ -523,9 +520,6 @@
|
|||||||
},
|
},
|
||||||
"Произошла ошибка." : {
|
"Произошла ошибка." : {
|
||||||
|
|
||||||
},
|
|
||||||
"Профиль" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Публичная информация" : {
|
"Публичная информация" : {
|
||||||
|
|
||||||
@ -576,6 +570,9 @@
|
|||||||
},
|
},
|
||||||
"Спасибо!" : {
|
"Спасибо!" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Старый пароль" : {
|
||||||
|
"comment" : "Старый пароль"
|
||||||
},
|
},
|
||||||
"Темы" : {
|
"Темы" : {
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import SwiftUI
|
|||||||
enum Theme: String, CaseIterable {
|
enum Theme: String, CaseIterable {
|
||||||
case system = "System"
|
case system = "System"
|
||||||
case light = "Light"
|
case light = "Light"
|
||||||
case oledDark = "Oled"
|
case oledDark = "Oleg"
|
||||||
// case dark = "Dark" // TODO
|
// case dark = "Dark" // TODO
|
||||||
|
|
||||||
var colorScheme: ColorScheme? {
|
var colorScheme: ColorScheme? {
|
||||||
|
|||||||
74
yobble/Services/ThemeOption.swift
Normal file
74
yobble/Services/ThemeOption.swift
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ struct LoginView: View {
|
|||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@EnvironmentObject private var themeManager: ThemeManager
|
@EnvironmentObject private var themeManager: ThemeManager
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
|
private let themeOptions = ThemeOption.ordered
|
||||||
|
|
||||||
@State private var isShowingRegistration = false
|
@State private var isShowingRegistration = false
|
||||||
@FocusState private var focusedField: Field?
|
@FocusState private var focusedField: Field?
|
||||||
@ -46,7 +47,15 @@ struct LoginView: View {
|
|||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
Spacer()
|
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)
|
Image(systemName: themeIconName)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@ -152,11 +161,6 @@ struct LoginView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private var themeIconName: String {
|
private var themeIconName: String {
|
||||||
switch themeManager.theme {
|
switch themeManager.theme {
|
||||||
case .system:
|
case .system:
|
||||||
@ -168,15 +172,37 @@ struct LoginView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toggleTheme() {
|
|
||||||
themeManager.toggleTheme(from: colorScheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func openLanguageSettings() {
|
private func openLanguageSettings() {
|
||||||
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||||
UIApplication.shared.open(url)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoginView_Previews: PreviewProvider {
|
struct LoginView_Previews: PreviewProvider {
|
||||||
|
|||||||
@ -101,8 +101,8 @@ struct RegistrationView: View {
|
|||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.onChange(of: password) { newValue in
|
.onChange(of: password) { newValue in
|
||||||
if newValue.count > 32 {
|
if newValue.count > 128 {
|
||||||
password = String(newValue.prefix(32))
|
password = String(newValue.prefix(128))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
80
yobble/Views/Tab/Settings/ChangePasswordView.swift
Normal file
80
yobble/Views/Tab/Settings/ChangePasswordView.swift
Normal file
@ -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: ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,89 +5,24 @@ struct SettingsView: View {
|
|||||||
@EnvironmentObject private var themeManager: ThemeManager
|
@EnvironmentObject private var themeManager: ThemeManager
|
||||||
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
|
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
|
||||||
@State private var isThemeExpanded = false
|
@State private var isThemeExpanded = false
|
||||||
private let themeOptions: [ThemeOption] = [.system, .dark, .oledDark, .lightTest, .custom]
|
private let themeOptions = ThemeOption.ordered
|
||||||
|
|
||||||
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 var selectedThemeOption: ThemeOption {
|
private var selectedThemeOption: ThemeOption {
|
||||||
switch themeManager.theme {
|
ThemeOption.option(for: themeManager.theme)
|
||||||
case .system:
|
|
||||||
return .system
|
|
||||||
case .light:
|
|
||||||
return .lightTest
|
|
||||||
case .oledDark:
|
|
||||||
return .oledDark
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
// MARK: - Профиль
|
// // MARK: - Профиль
|
||||||
Section(header: Text("Профиль")) {
|
// Section(header: Text("Профиль")) {
|
||||||
NavigationLink(destination: EditProfileView()) {
|
// NavigationLink(destination: EditProfileView()) {
|
||||||
Label("Мой профиль", systemImage: "person.crop.circle")
|
// Label("Мой профиль", systemImage: "person.crop.circle")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// MARK: - Безопасность
|
// MARK: - Безопасность
|
||||||
Section(header: Text("Безопасность")) {
|
Section(header: Text("Безопасность")) {
|
||||||
NavigationLink(destination: Text("Заглушка: Сменить пароль")) {
|
NavigationLink(destination: ChangePasswordView()) {
|
||||||
Label("Сменить пароль", systemImage: "key")
|
Label("Сменить пароль", systemImage: "key")
|
||||||
}
|
}
|
||||||
NavigationLink(destination: Text("Заглушка: Двухфакторная аутентификация")) {
|
NavigationLink(destination: Text("Заглушка: Двухфакторная аутентификация")) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user