patch security setting
This commit is contained in:
parent
e3cf374893
commit
107318ef21
@ -90,7 +90,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Email" : {
|
"Email" : {
|
||||||
"comment" : "Заголовок экрана настроек email\nРаздел настроек безопасности для email"
|
"comment" : "Заголовок экрана настроек email"
|
||||||
},
|
},
|
||||||
"Email не подтверждён. Подтвердите, чтобы активировать дополнительные проверки." : {
|
"Email не подтверждён. Подтвердите, чтобы активировать дополнительные проверки." : {
|
||||||
"comment" : "Описание необходимости подтверждения email"
|
"comment" : "Описание необходимости подтверждения email"
|
||||||
@ -226,9 +226,6 @@
|
|||||||
"Аудио" : {
|
"Аудио" : {
|
||||||
"comment" : "Audio message placeholder"
|
"comment" : "Audio message placeholder"
|
||||||
},
|
},
|
||||||
"Аутентификация" : {
|
|
||||||
"comment" : "Раздел настроек безопасности для аутентификации"
|
|
||||||
},
|
|
||||||
"Без звука (скоро)" : {
|
"Без звука (скоро)" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -266,6 +263,9 @@
|
|||||||
"Введите код из приложения" : {
|
"Введите код из приложения" : {
|
||||||
"comment" : "Поле ввода кода 2FA"
|
"comment" : "Поле ввода кода 2FA"
|
||||||
},
|
},
|
||||||
|
"Введите пароль" : {
|
||||||
|
"comment" : "Поле ввода пароля на приложение"
|
||||||
|
},
|
||||||
"Веб" : {
|
"Веб" : {
|
||||||
"comment" : "Тип сессии — веб"
|
"comment" : "Тип сессии — веб"
|
||||||
},
|
},
|
||||||
@ -363,6 +363,9 @@
|
|||||||
"Всего сессий" : {
|
"Всего сессий" : {
|
||||||
"comment" : "Сводка по количеству сессий"
|
"comment" : "Сводка по количеству сессий"
|
||||||
},
|
},
|
||||||
|
"Вход и защита аккаунта (заглушка)" : {
|
||||||
|
"comment" : "Раздел настроек безопасности для аутентификации"
|
||||||
|
},
|
||||||
"Вы" : {
|
"Вы" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -611,6 +614,9 @@
|
|||||||
"Защита входа" : {
|
"Защита входа" : {
|
||||||
"comment" : "Раздел защиты входа через email"
|
"comment" : "Раздел защиты входа через email"
|
||||||
},
|
},
|
||||||
|
"Защита приложением будет добавлена в будущих обновлениях." : {
|
||||||
|
"comment" : "Сообщение заглушки пароля на приложение"
|
||||||
|
},
|
||||||
"Здесь не будут чаты" : {
|
"Здесь не будут чаты" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1017,6 +1023,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Настоящая защита приложения появится позже. Пока вы можете ознакомится с макетом." : {
|
||||||
|
"comment" : "Описание заглушки для пароля на приложение"
|
||||||
|
},
|
||||||
"Настройка приложения" : {
|
"Настройка приложения" : {
|
||||||
"comment" : "Раздел инструкций подключения"
|
"comment" : "Раздел инструкций подключения"
|
||||||
},
|
},
|
||||||
@ -1580,7 +1589,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Пароли не совпадают" : {
|
"Пароли не совпадают" : {
|
||||||
"comment" : "Пароли не совпадают",
|
"comment" : "Заголовок ошибки несовпадения паролей\nПароли не совпадают",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -1633,6 +1642,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Пароль на приложение" : {
|
||||||
|
"comment" : "Заголовок экрана пароля на приложение\nПереход к настройкам пароля на приложение"
|
||||||
|
},
|
||||||
"Пароль не удовлетворяет требованиям" : {
|
"Пароль не удовлетворяет требованиям" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1654,12 +1666,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Пароль-приложение" : {
|
||||||
|
"comment" : "Раздел формы установки пароля на приложение"
|
||||||
|
},
|
||||||
"Первый вход: %@" : {
|
"Первый вход: %@" : {
|
||||||
"comment" : "Дата первого входа в сессию"
|
"comment" : "Дата первого входа в сессию"
|
||||||
},
|
},
|
||||||
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
|
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
|
||||||
"comment" : "FAQ answer: reset password"
|
"comment" : "FAQ answer: reset password"
|
||||||
},
|
},
|
||||||
|
"Повторите пароль" : {
|
||||||
|
"comment" : "Поле подтверждения пароля на приложение"
|
||||||
|
},
|
||||||
"Повторить" : {
|
"Повторить" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1869,6 +1887,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Приватность и контроль" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Приватные чаты" : {
|
"Приватные чаты" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1974,6 +1995,9 @@
|
|||||||
"Проверочный код" : {
|
"Проверочный код" : {
|
||||||
"comment" : "Раздел верификации 2FA"
|
"comment" : "Раздел верификации 2FA"
|
||||||
},
|
},
|
||||||
|
"Проверьте ввод и попробуйте снова." : {
|
||||||
|
"comment" : "Сообщение ошибки несовпадения паролей"
|
||||||
|
},
|
||||||
"Проверьте данные и повторите попытку." : {
|
"Проверьте данные и повторите попытку." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -2325,6 +2349,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Сохранить пароль" : {
|
||||||
|
"comment" : "Кнопка сохранения пароля на приложение"
|
||||||
|
},
|
||||||
"Спасибо! Мы получили ваш отзыв" : {
|
"Спасибо! Мы получили ваш отзыв" : {
|
||||||
"comment" : "feedback: success title",
|
"comment" : "feedback: success title",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class LoginViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum OnboardingDestination: Equatable {
|
enum OnboardingDestination: Equatable {
|
||||||
case twoFactor
|
case securitySettings
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DefaultsKeys {
|
private enum DefaultsKeys {
|
||||||
@ -132,7 +132,7 @@ class LoginViewModel: ObservableObject {
|
|||||||
self?.isLoggedIn = true // 👈 переключаем на главный экран после автологина
|
self?.isLoggedIn = true // 👈 переключаем на главный экран после автологина
|
||||||
self?.loadStoredUser()
|
self?.loadStoredUser()
|
||||||
self?.socketService.connectForCurrentUser()
|
self?.socketService.connectForCurrentUser()
|
||||||
self?.onboardingDestination = .twoFactor
|
self?.onboardingDestination = .securitySettings
|
||||||
} else {
|
} else {
|
||||||
self?.socketService.disconnect()
|
self?.socketService.disconnect()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ struct MainView: View {
|
|||||||
@State private var isQrPresented = false
|
@State private var isQrPresented = false
|
||||||
@State private var deepLinkChatItem: PrivateChatListItem?
|
@State private var deepLinkChatItem: PrivateChatListItem?
|
||||||
@State private var isDeepLinkChatActive = false
|
@State private var isDeepLinkChatActive = false
|
||||||
@State private var hasTriggeredTwoFactorOnboarding = false
|
@State private var hasTriggeredSecuritySettingsOnboarding = false
|
||||||
|
|
||||||
private var tabTitle: String {
|
private var tabTitle: String {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
@ -224,13 +224,13 @@ private extension MainView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleTwoFactorOnboardingIfNeeded() {
|
func handleTwoFactorOnboardingIfNeeded() {
|
||||||
guard viewModel.onboardingDestination == .twoFactor else {
|
guard viewModel.onboardingDestination == .securitySettings else {
|
||||||
hasTriggeredTwoFactorOnboarding = false
|
hasTriggeredSecuritySettingsOnboarding = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !hasTriggeredTwoFactorOnboarding else { return }
|
guard !hasTriggeredSecuritySettingsOnboarding else { return }
|
||||||
hasTriggeredTwoFactorOnboarding = true
|
hasTriggeredSecuritySettingsOnboarding = true
|
||||||
|
|
||||||
if isMessengerModeEnabled {
|
if isMessengerModeEnabled {
|
||||||
if selectedTab != 5 {
|
if selectedTab != 5 {
|
||||||
|
|||||||
82
yobble/Views/Tab/Settings/Security/AppLockSettingsView.swift
Normal file
82
yobble/Views/Tab/Settings/Security/AppLockSettingsView.swift
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct AppLockSettingsView: View {
|
||||||
|
@State private var desiredPassword: String = ""
|
||||||
|
@State private var confirmationPassword: String = ""
|
||||||
|
@State private var activeAlert: AppLockAlert?
|
||||||
|
@FocusState private var focusedField: Field?
|
||||||
|
|
||||||
|
private enum Field: Hashable {
|
||||||
|
case desired
|
||||||
|
case confirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section(header: Text(NSLocalizedString("Пароль-приложение", comment: "Раздел формы установки пароля на приложение"))) {
|
||||||
|
SecureField(NSLocalizedString("Введите пароль", comment: "Поле ввода пароля на приложение"), text: $desiredPassword)
|
||||||
|
.focused($focusedField, equals: .desired)
|
||||||
|
|
||||||
|
SecureField(NSLocalizedString("Повторите пароль", comment: "Поле подтверждения пароля на приложение"), text: $confirmationPassword)
|
||||||
|
.focused($focusedField, equals: .confirmation)
|
||||||
|
|
||||||
|
Button(NSLocalizedString("Сохранить пароль", comment: "Кнопка сохранения пароля на приложение")) {
|
||||||
|
handleSaveTapped()
|
||||||
|
}
|
||||||
|
.disabled(desiredPassword.isEmpty || confirmationPassword.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Text(NSLocalizedString("Настоящая защита приложения появится позже. Пока вы можете ознакомится с макетом.", comment: "Описание заглушки для пароля на приложение"))
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(NSLocalizedString("Пароль на приложение", comment: "Заголовок экрана пароля на приложение"))
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.onAppear {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||||
|
focusedField = .desired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert(item: $activeAlert) { alert in
|
||||||
|
Alert(
|
||||||
|
title: Text(alert.title),
|
||||||
|
message: Text(alert.message),
|
||||||
|
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Общий текст кнопки OK")))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleSaveTapped() {
|
||||||
|
guard !desiredPassword.isEmpty, desiredPassword == confirmationPassword else {
|
||||||
|
activeAlert = AppLockAlert(
|
||||||
|
title: NSLocalizedString("Пароли не совпадают", comment: "Заголовок ошибки несовпадения паролей"),
|
||||||
|
message: NSLocalizedString("Проверьте ввод и попробуйте снова.", comment: "Сообщение ошибки несовпадения паролей"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activeAlert = AppLockAlert(
|
||||||
|
title: NSLocalizedString("Скоро", comment: "Заголовок заглушки"),
|
||||||
|
message: NSLocalizedString("Защита приложением будет добавлена в будущих обновлениях.", comment: "Сообщение заглушки пароля на приложение")
|
||||||
|
)
|
||||||
|
desiredPassword.removeAll()
|
||||||
|
confirmationPassword.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct AppLockAlert: Identifiable {
|
||||||
|
let id = UUID()
|
||||||
|
let title: String
|
||||||
|
let message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
struct AppLockSettingsView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
AppLockSettingsView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -4,37 +4,57 @@ struct SecuritySettingsView: View {
|
|||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@State private var isTwoFactorActive = false
|
@State private var isTwoFactorActive = false
|
||||||
@State private var isEmailSettingsActive = false
|
@State private var isEmailSettingsActive = false
|
||||||
|
@State private var isAppLockActive = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section(header: Text(NSLocalizedString("Аутентификация", comment: "Раздел настроек безопасности для аутентификации"))) {
|
Section(header: Text(NSLocalizedString("Вход и защита аккаунта (заглушка)", comment: "Раздел настроек безопасности для аутентификации"))) {
|
||||||
NavigationLink(isActive: $isTwoFactorActive) {
|
NavigationLink(isActive: $isTwoFactorActive) {
|
||||||
TwoFactorAuthView()
|
TwoFactorAuthView()
|
||||||
} label: {
|
} label: {
|
||||||
Label(NSLocalizedString("Двухфакторная аутентификация", comment: "Переход к настройкам двухфакторной аутентификации"), systemImage: "lock.shield")
|
Label(NSLocalizedString("Двухфакторная аутентификация", comment: "Переход к настройкам двухфакторной аутентификации"), systemImage: "lock.shield")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text(NSLocalizedString("Email", comment: "Раздел настроек безопасности для email"))) {
|
|
||||||
NavigationLink(isActive: $isEmailSettingsActive) {
|
NavigationLink(isActive: $isEmailSettingsActive) {
|
||||||
EmailSecuritySettingsView()
|
EmailSecuritySettingsView()
|
||||||
} label: {
|
} label: {
|
||||||
Label(NSLocalizedString("Настройки email", comment: "Переход к настройкам безопасности email"), systemImage: "envelope")
|
Label(NSLocalizedString("Настройки email", comment: "Переход к настройкам безопасности email"), systemImage: "envelope")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationLink(isActive: $isAppLockActive) {
|
||||||
|
AppLockSettingsView()
|
||||||
|
} label: {
|
||||||
|
Label(NSLocalizedString("Пароль на приложение", comment: "Переход к настройкам пароля на приложение"), systemImage: "lock.square")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(header: Text(NSLocalizedString("Приватность и контроль", comment: ""))) {
|
||||||
|
|
||||||
|
NavigationLink(destination: EditPrivacyView()) {
|
||||||
|
Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill")
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: ChangePasswordView()) {
|
||||||
|
Label(NSLocalizedString("Сменить пароль", comment: ""), systemImage: "key")
|
||||||
|
}
|
||||||
|
NavigationLink(destination: ActiveSessionsView()) {
|
||||||
|
Label(NSLocalizedString("Активные сессии", comment: ""), systemImage: "iphone")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
.listStyle(.insetGrouped)
|
||||||
.navigationTitle(NSLocalizedString("Безопасность", comment: "Заголовок экрана настроек безопасности"))
|
.navigationTitle(NSLocalizedString("Безопасность", comment: "Заголовок экрана настроек безопасности"))
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.onAppear { handleTwoFactorOnboardingIfNeeded() }
|
.onAppear { handleSecuritySettingsOnboardingIfNeeded() }
|
||||||
.onChange(of: viewModel.onboardingDestination) { _ in
|
.onChange(of: viewModel.onboardingDestination) { _ in
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
handleSecuritySettingsOnboardingIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleTwoFactorOnboardingIfNeeded() {
|
private func handleSecuritySettingsOnboardingIfNeeded() {
|
||||||
guard viewModel.onboardingDestination == .twoFactor else { return }
|
guard viewModel.onboardingDestination == .securitySettings else { return }
|
||||||
isTwoFactorActive = true
|
// isSecuritySettingsActive = true
|
||||||
viewModel.onboardingDestination = nil
|
viewModel.onboardingDestination = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,9 +18,9 @@ struct SettingsView: View {
|
|||||||
// NavigationLink(destination: EditProfileView()) {
|
// NavigationLink(destination: EditProfileView()) {
|
||||||
// Label("Мой профиль", systemImage: "person.crop.circle")
|
// Label("Мой профиль", systemImage: "person.crop.circle")
|
||||||
// }
|
// }
|
||||||
|
|
||||||
NavigationLink(destination: EditPrivacyView()) {
|
NavigationLink(destination: EditProfileView()) {
|
||||||
Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill")
|
Label(NSLocalizedString("Редактировать профиль", comment: ""), systemImage: "person.crop.circle")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(destination: BlockedUsersView()) {
|
NavigationLink(destination: BlockedUsersView()) {
|
||||||
@ -30,17 +30,21 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
// MARK: - Безопасность
|
// MARK: - Безопасность
|
||||||
Section(header: Text(NSLocalizedString("Безопасность", comment: ""))) {
|
Section(header: Text(NSLocalizedString("Безопасность", comment: ""))) {
|
||||||
|
NavigationLink(destination: EditPrivacyView()) {
|
||||||
|
Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill")
|
||||||
|
}
|
||||||
|
|
||||||
NavigationLink(destination: ChangePasswordView()) {
|
NavigationLink(destination: ChangePasswordView()) {
|
||||||
Label(NSLocalizedString("Сменить пароль", comment: ""), systemImage: "key")
|
Label(NSLocalizedString("Сменить пароль", comment: ""), systemImage: "key")
|
||||||
}
|
}
|
||||||
|
NavigationLink(destination: ActiveSessionsView()) {
|
||||||
|
Label(NSLocalizedString("Активные сессии", comment: ""), systemImage: "iphone")
|
||||||
|
}
|
||||||
NavigationLink(isActive: $isSecurityActive) {
|
NavigationLink(isActive: $isSecurityActive) {
|
||||||
SecuritySettingsView(viewModel: viewModel)
|
SecuritySettingsView(viewModel: viewModel)
|
||||||
} label: {
|
} label: {
|
||||||
Label(NSLocalizedString("Безопасность", comment: ""), systemImage: "lock.shield")
|
Label(NSLocalizedString("Безопасность", comment: ""), systemImage: "lock.shield")
|
||||||
}
|
}
|
||||||
NavigationLink(destination: ActiveSessionsView()) {
|
|
||||||
Label(NSLocalizedString("Активные сессии", comment: ""), systemImage: "iphone")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Приложение
|
// MARK: - Приложение
|
||||||
@ -177,7 +181,7 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
private extension SettingsView {
|
private extension SettingsView {
|
||||||
func handleTwoFactorOnboardingIfNeeded() {
|
func handleTwoFactorOnboardingIfNeeded() {
|
||||||
guard viewModel.onboardingDestination == .twoFactor else { return }
|
guard viewModel.onboardingDestination == .securitySettings else { return }
|
||||||
isSecurityActive = true
|
isSecurityActive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user