Compare commits
No commits in common. "b466864350345257a9b719e2cd8770f29ef59aa1" and "58c841b5c79564e540d11a2c91e48395efec3631" have entirely different histories.
b466864350
...
58c841b5c7
@ -61,12 +61,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2FA включена" : {
|
|
||||||
"comment" : "Заголовок уведомления об успешной активации 2FA"
|
|
||||||
},
|
|
||||||
"2FA отключена" : {
|
|
||||||
"comment" : "Заголовок уведомления об отключении 2FA"
|
|
||||||
},
|
|
||||||
"Companion ID" : {
|
"Companion ID" : {
|
||||||
"comment" : "Search placeholder companion title"
|
"comment" : "Search placeholder companion title"
|
||||||
},
|
},
|
||||||
@ -89,12 +83,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Email" : {
|
|
||||||
"comment" : "Заголовок экрана настроек email"
|
|
||||||
},
|
|
||||||
"Email не подтверждён. Подтвердите, чтобы активировать дополнительные проверки." : {
|
|
||||||
"comment" : "Описание необходимости подтверждения email"
|
|
||||||
},
|
|
||||||
"Fun Fest" : {
|
"Fun Fest" : {
|
||||||
"comment" : "Fun Fest",
|
"comment" : "Fun Fest",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -230,7 +218,6 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
"Безопасность" : {
|
"Безопасность" : {
|
||||||
"comment" : "Заголовок экрана настроек безопасности",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -260,12 +247,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Введите код из приложения" : {
|
|
||||||
"comment" : "Поле ввода кода 2FA"
|
|
||||||
},
|
|
||||||
"Введите пароль" : {
|
|
||||||
"comment" : "Поле ввода пароля на приложение"
|
|
||||||
},
|
|
||||||
"Веб" : {
|
"Веб" : {
|
||||||
"comment" : "Тип сессии — веб"
|
"comment" : "Тип сессии — веб"
|
||||||
},
|
},
|
||||||
@ -304,12 +285,6 @@
|
|||||||
},
|
},
|
||||||
"Включено" : {
|
"Включено" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Включить" : {
|
|
||||||
"comment" : "Кнопка подтверждения включения 2FA"
|
|
||||||
},
|
|
||||||
"Включить 2FA" : {
|
|
||||||
"comment" : "Тоггл активации 2FA"
|
|
||||||
},
|
},
|
||||||
"Включить автоудаление аккаунта" : {
|
"Включить автоудаление аккаунта" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -321,9 +296,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Включить двухфакторную аутентификацию?" : {
|
|
||||||
"comment" : "Заголовок подтверждения включения 2FA"
|
|
||||||
},
|
|
||||||
"Вложение" : {
|
"Вложение" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -363,9 +335,6 @@
|
|||||||
"Всего сессий" : {
|
"Всего сессий" : {
|
||||||
"comment" : "Сводка по количеству сессий"
|
"comment" : "Сводка по количеству сессий"
|
||||||
},
|
},
|
||||||
"Вход и защита аккаунта (заглушка)" : {
|
|
||||||
"comment" : "Раздел настроек безопасности для аутентификации"
|
|
||||||
},
|
|
||||||
"Вы" : {
|
"Вы" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -376,18 +345,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Вы всегда можете отключить двухфакторную защиту, но мы рекомендуем оставлять её включённой для безопасности." : {
|
|
||||||
"comment" : "Рекомендация оставить 2FA включенной"
|
|
||||||
},
|
|
||||||
"Вы выйдете из выбранной сессии." : {
|
"Вы выйдете из выбранной сессии." : {
|
||||||
"comment" : "Описание подтверждения завершения конкретной сессии"
|
"comment" : "Описание подтверждения завершения конкретной сессии"
|
||||||
},
|
},
|
||||||
"Вы выйдете со всех устройств, кроме текущего." : {
|
"Вы выйдете со всех устройств, кроме текущего." : {
|
||||||
"comment" : "Описание подтверждения завершения сессий"
|
"comment" : "Описание подтверждения завершения сессий"
|
||||||
},
|
},
|
||||||
"Вы можете включить защиту снова в любой момент." : {
|
|
||||||
"comment" : "Сообщение после отключения 2FA"
|
|
||||||
},
|
|
||||||
"Выберите оценку — это поможет нам понять настроение." : {
|
"Выберите оценку — это поможет нам понять настроение." : {
|
||||||
"comment" : "feedback: rating hint",
|
"comment" : "feedback: rating hint",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -440,7 +403,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Двухфакторная аутентификация" : {
|
"Двухфакторная аутентификация" : {
|
||||||
"comment" : "Заголовок экрана 2FA\nПереход к настройкам двухфакторной аутентификации",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -450,9 +412,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Двухфакторная аутентификация настроена." : {
|
|
||||||
"comment" : "Сообщение после успешного подтверждения кода 2FA"
|
|
||||||
},
|
|
||||||
"Десктоп" : {
|
"Десктоп" : {
|
||||||
"comment" : "Тип сессии — десктоп"
|
"comment" : "Тип сессии — десктоп"
|
||||||
},
|
},
|
||||||
@ -473,9 +432,6 @@
|
|||||||
"Добавьте контакты, чтобы быстрее выходить на связь." : {
|
"Добавьте контакты, чтобы быстрее выходить на связь." : {
|
||||||
"comment" : "Contacts empty state subtitle"
|
"comment" : "Contacts empty state subtitle"
|
||||||
},
|
},
|
||||||
"Добавьте новый аккаунт в приложении аутентификации и введите следующий ключ:" : {
|
|
||||||
"comment" : "Инструкция по добавлению ключа 2FA"
|
|
||||||
},
|
|
||||||
"Другие устройства (%d)" : {
|
"Другие устройства (%d)" : {
|
||||||
"comment" : "Заголовок секции других устройств с количеством"
|
"comment" : "Заголовок секции других устройств с количеством"
|
||||||
},
|
},
|
||||||
@ -518,6 +474,9 @@
|
|||||||
},
|
},
|
||||||
"Заглушка: Push-уведомления" : {
|
"Заглушка: Push-уведомления" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Заглушка: Двухфакторная аутентификация" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Заглушка: Другие настройки" : {
|
"Заглушка: Другие настройки" : {
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@ -611,12 +570,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Защита входа" : {
|
|
||||||
"comment" : "Раздел защиты входа через email"
|
|
||||||
},
|
|
||||||
"Защита приложением будет добавлена в будущих обновлениях." : {
|
|
||||||
"comment" : "Сообщение заглушки пароля на приложение"
|
|
||||||
},
|
|
||||||
"Здесь не будут чаты" : {
|
"Здесь не будут чаты" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -630,9 +583,6 @@
|
|||||||
"Здесь появится информация о собеседнике и существующих чатах." : {
|
"Здесь появится информация о собеседнике и существующих чатах." : {
|
||||||
"comment" : "Search placeholder description"
|
"comment" : "Search placeholder description"
|
||||||
},
|
},
|
||||||
"Значение сохранено в буфере обмена." : {
|
|
||||||
"comment" : "Сообщение после копирования"
|
|
||||||
},
|
|
||||||
"Идея" : {
|
"Идея" : {
|
||||||
"comment" : "feedback category: idea",
|
"comment" : "feedback category: idea",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -713,12 +663,6 @@
|
|||||||
"Код дружбы" : {
|
"Код дружбы" : {
|
||||||
"comment" : "Friend code badge"
|
"comment" : "Friend code badge"
|
||||||
},
|
},
|
||||||
"Код принят" : {
|
|
||||||
"comment" : "Заголовок успешного подтверждения кода 2FA"
|
|
||||||
},
|
|
||||||
"Коды восстановления" : {
|
|
||||||
"comment" : "Раздел кодов восстановления 2FA"
|
|
||||||
},
|
|
||||||
"Контактов пока нет" : {
|
"Контактов пока нет" : {
|
||||||
"comment" : "Contacts empty state title"
|
"comment" : "Contacts empty state title"
|
||||||
},
|
},
|
||||||
@ -872,9 +816,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Мессенджер-режим сейчас проработан примерно на 50%." : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Мессенджер-режим сейчас проработан примерно на 60%." : {
|
"Мессенджер-режим сейчас проработан примерно на 60%." : {
|
||||||
|
|
||||||
@ -926,12 +867,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Мы отправим код подтверждения на привязанный email каждый раз при входе." : {
|
|
||||||
"comment" : "Описание работы кодов при входе"
|
|
||||||
},
|
|
||||||
"Мы отправим письмо, как только функция будет готова." : {
|
|
||||||
"comment" : "Сообщение при недоступной отправке письма"
|
|
||||||
},
|
|
||||||
"Мы постараемся всё исправить. Напишите, что смутило." : {
|
"Мы постараемся всё исправить. Напишите, что смутило." : {
|
||||||
"comment" : "feedback: rating description 2",
|
"comment" : "feedback: rating description 2",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1023,12 +958,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Настоящая защита приложения появится позже. Пока вы можете ознакомится с макетом." : {
|
|
||||||
"comment" : "Описание заглушки для пароля на приложение"
|
|
||||||
},
|
|
||||||
"Настройка приложения" : {
|
|
||||||
"comment" : "Раздел инструкций подключения"
|
|
||||||
},
|
|
||||||
"Настройки" : {
|
"Настройки" : {
|
||||||
"comment" : "Settings",
|
"comment" : "Settings",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1040,9 +969,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Настройки email" : {
|
|
||||||
"comment" : "Переход к настройкам безопасности email"
|
|
||||||
},
|
|
||||||
"Настройки приватности" : {
|
"Настройки приватности" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1186,9 +1112,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Неверный код" : {
|
|
||||||
"comment" : "Заголовок ошибки неправильного кода 2FA"
|
|
||||||
},
|
|
||||||
"Неверный код приглашения." : {
|
"Неверный код приглашения." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1440,12 +1363,6 @@
|
|||||||
},
|
},
|
||||||
"Основной режим находится в ранней разработке (около 10%)." : {
|
"Основной режим находится в ранней разработке (около 10%)." : {
|
||||||
|
|
||||||
},
|
|
||||||
"Отключить" : {
|
|
||||||
"comment" : "Кнопка подтверждения отключения 2FA"
|
|
||||||
},
|
|
||||||
"Отключить двухфакторную аутентификацию?" : {
|
|
||||||
"comment" : "Заголовок подтверждения отключения 2FA"
|
|
||||||
},
|
},
|
||||||
"Открыть правила" : {
|
"Открыть правила" : {
|
||||||
|
|
||||||
@ -1467,9 +1384,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Отправить письмо подтверждения" : {
|
|
||||||
"comment" : "Кнопка отправки письма подтверждения"
|
|
||||||
},
|
|
||||||
"Отправляем..." : {
|
"Отправляем..." : {
|
||||||
"comment" : "feedback: sending state",
|
"comment" : "feedback: sending state",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1589,7 +1503,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Пароли не совпадают" : {
|
"Пароли не совпадают" : {
|
||||||
"comment" : "Заголовок ошибки несовпадения паролей\nПароли не совпадают",
|
"comment" : "Пароли не совпадают",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -1642,9 +1556,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Пароль на приложение" : {
|
|
||||||
"comment" : "Заголовок экрана пароля на приложение\nПереход к настройкам пароля на приложение"
|
|
||||||
},
|
|
||||||
"Пароль не удовлетворяет требованиям" : {
|
"Пароль не удовлетворяет требованиям" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1666,18 +1577,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Пароль-приложение" : {
|
|
||||||
"comment" : "Раздел формы установки пароля на приложение"
|
|
||||||
},
|
|
||||||
"Первый вход: %@" : {
|
"Первый вход: %@" : {
|
||||||
"comment" : "Дата первого входа в сессию"
|
"comment" : "Дата первого входа в сессию"
|
||||||
},
|
},
|
||||||
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
|
"Перейдите в раздел \"Настройки > Сменить пароль\" и следуйте инструкциям." : {
|
||||||
"comment" : "FAQ answer: reset password"
|
"comment" : "FAQ answer: reset password"
|
||||||
},
|
},
|
||||||
"Повторите пароль" : {
|
|
||||||
"comment" : "Поле подтверждения пароля на приложение"
|
|
||||||
},
|
|
||||||
"Повторить" : {
|
"Повторить" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1725,12 +1630,6 @@
|
|||||||
},
|
},
|
||||||
"Подключение" : {
|
"Подключение" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Подтвердить" : {
|
|
||||||
"comment" : "Кнопка подтверждения кода 2FA"
|
|
||||||
},
|
|
||||||
"Подтверждение email" : {
|
|
||||||
"comment" : "Раздел подтверждения email"
|
|
||||||
},
|
},
|
||||||
"Подтверждение пароля" : {
|
"Подтверждение пароля" : {
|
||||||
"comment" : "Подтверждение пароля",
|
"comment" : "Подтверждение пароля",
|
||||||
@ -1803,9 +1702,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Получать коды на email при входе" : {
|
|
||||||
"comment" : "Переключатель отправки кодов при входе"
|
|
||||||
},
|
|
||||||
"Получить ответ от команды" : {
|
"Получить ответ от команды" : {
|
||||||
"comment" : "feedback: contact toggle",
|
"comment" : "feedback: contact toggle",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1887,9 +1783,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"Приватность и контроль" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Приватные чаты" : {
|
"Приватные чаты" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1992,12 +1885,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Проверочный код" : {
|
|
||||||
"comment" : "Раздел верификации 2FA"
|
|
||||||
},
|
|
||||||
"Проверьте ввод и попробуйте снова." : {
|
|
||||||
"comment" : "Сообщение ошибки несовпадения паролей"
|
|
||||||
},
|
|
||||||
"Проверьте данные и повторите попытку." : {
|
"Проверьте данные и повторите попытку." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -2008,9 +1895,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Проверьте цифры и попробуйте снова." : {
|
|
||||||
"comment" : "Описание ошибки неверного кода 2FA"
|
|
||||||
},
|
|
||||||
"Произошла неизвестная ошибка." : {
|
"Произошла неизвестная ошибка." : {
|
||||||
"comment" : "Search unknown error"
|
"comment" : "Search unknown error"
|
||||||
},
|
},
|
||||||
@ -2208,9 +2092,6 @@
|
|||||||
"Связаться с разработчиками" : {
|
"Связаться с разработчиками" : {
|
||||||
"comment" : "FAQ: contact developers link"
|
"comment" : "FAQ: contact developers link"
|
||||||
},
|
},
|
||||||
"Сгенерируйте резервные коды и сохраните их в надежном месте." : {
|
|
||||||
"comment" : "Подсказка о необходимости генерации кодов"
|
|
||||||
},
|
|
||||||
"Сервер вернул ошибку (%d)." : {
|
"Сервер вернул ошибку (%d)." : {
|
||||||
"comment" : "Search error server status"
|
"comment" : "Search error server status"
|
||||||
},
|
},
|
||||||
@ -2259,20 +2140,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Скопировано" : {
|
|
||||||
"comment" : "Заголовок уведомления о копировании"
|
|
||||||
},
|
|
||||||
"Скопировать" : {
|
"Скопировать" : {
|
||||||
"comment" : "Search placeholder copy"
|
"comment" : "Search placeholder copy"
|
||||||
},
|
},
|
||||||
"Скопировать ключ" : {
|
|
||||||
"comment" : "Кнопка копирования секретного ключа"
|
|
||||||
},
|
|
||||||
"Скопировать код" : {
|
|
||||||
"comment" : "Кнопка копирования кода восстановления"
|
|
||||||
},
|
|
||||||
"Скоро" : {
|
"Скоро" : {
|
||||||
"comment" : "Add blocked user placeholder title\nContacts placeholder title\nЗаголовок заглушки"
|
"comment" : "Add blocked user placeholder title\nContacts placeholder title"
|
||||||
},
|
},
|
||||||
"Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : {
|
"Скоро появится мини-игра, где можно заработать очки для кастомизации профиля. Следите за обновлениями!" : {
|
||||||
"comment" : "Concept tab placeholder description"
|
"comment" : "Concept tab placeholder description"
|
||||||
@ -2315,9 +2187,6 @@
|
|||||||
},
|
},
|
||||||
"Согласиться с правилами" : {
|
"Согласиться с правилами" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Создать новые коды" : {
|
|
||||||
"comment" : "Кнопка генерации резервных кодов"
|
|
||||||
},
|
},
|
||||||
"Сообщение" : {
|
"Сообщение" : {
|
||||||
|
|
||||||
@ -2336,9 +2205,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Сохраните секретный ключ и введите код из приложения, чтобы завершить настройку." : {
|
|
||||||
"comment" : "Сообщение после активации 2FA"
|
|
||||||
},
|
|
||||||
"Сохранить изменения" : {
|
"Сохранить изменения" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -2349,9 +2215,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Сохранить пароль" : {
|
|
||||||
"comment" : "Кнопка сохранения пароля на приложение"
|
|
||||||
},
|
|
||||||
"Спасибо! Мы получили ваш отзыв" : {
|
"Спасибо! Мы получили ваш отзыв" : {
|
||||||
"comment" : "feedback: success title",
|
"comment" : "feedback: success title",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -2395,9 +2258,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Статус защиты" : {
|
|
||||||
"comment" : "Раздел состояния 2FA"
|
|
||||||
},
|
|
||||||
"Стикеры" : {
|
"Стикеры" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -2503,9 +2363,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Функция пока недоступна." : {
|
|
||||||
"comment" : "Сообщение заглушки"
|
|
||||||
},
|
|
||||||
"Центр авторов" : {
|
"Центр авторов" : {
|
||||||
"comment" : "Creator Center",
|
"comment" : "Creator Center",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -22,7 +22,6 @@ class LoginViewModel: ObservableObject {
|
|||||||
@Published var isLoadingTerms: Bool = false
|
@Published var isLoadingTerms: Bool = false
|
||||||
@Published var termsContent: String = ""
|
@Published var termsContent: String = ""
|
||||||
@Published var termsErrorMessage: String?
|
@Published var termsErrorMessage: String?
|
||||||
@Published var onboardingDestination: OnboardingDestination?
|
|
||||||
|
|
||||||
private let authService = AuthService()
|
private let authService = AuthService()
|
||||||
private let socketService = SocketService.shared
|
private let socketService = SocketService.shared
|
||||||
@ -33,10 +32,6 @@ class LoginViewModel: ObservableObject {
|
|||||||
case loading
|
case loading
|
||||||
}
|
}
|
||||||
|
|
||||||
enum OnboardingDestination: Equatable {
|
|
||||||
case securitySettings
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum DefaultsKeys {
|
private enum DefaultsKeys {
|
||||||
static let currentUser = "currentUser"
|
static let currentUser = "currentUser"
|
||||||
static let userId = "userId"
|
static let userId = "userId"
|
||||||
@ -132,7 +127,6 @@ class LoginViewModel: ObservableObject {
|
|||||||
self?.isLoggedIn = true // 👈 переключаем на главный экран после автологина
|
self?.isLoggedIn = true // 👈 переключаем на главный экран после автологина
|
||||||
self?.loadStoredUser()
|
self?.loadStoredUser()
|
||||||
self?.socketService.connectForCurrentUser()
|
self?.socketService.connectForCurrentUser()
|
||||||
self?.onboardingDestination = .securitySettings
|
|
||||||
} else {
|
} else {
|
||||||
self?.socketService.disconnect()
|
self?.socketService.disconnect()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,6 @@ 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 hasTriggeredSecuritySettingsOnboarding = false
|
|
||||||
|
|
||||||
private var tabTitle: String {
|
private var tabTitle: String {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
@ -173,14 +172,9 @@ struct MainView: View {
|
|||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
enforceTabSelectionForMessengerMode()
|
enforceTabSelectionForMessengerMode()
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
|
||||||
}
|
}
|
||||||
.onChange(of: isMessengerModeEnabled) { _ in
|
.onChange(of: isMessengerModeEnabled) { _ in
|
||||||
enforceTabSelectionForMessengerMode()
|
enforceTabSelectionForMessengerMode()
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
|
||||||
}
|
|
||||||
.onChange(of: viewModel.onboardingDestination) { _ in
|
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
|
||||||
}
|
}
|
||||||
.onChange(of: messageCenter.pendingNavigation?.id) { _ in
|
.onChange(of: messageCenter.pendingNavigation?.id) { _ in
|
||||||
guard !AppConfig.PRESENT_CHAT_AS_SHEET,
|
guard !AppConfig.PRESENT_CHAT_AS_SHEET,
|
||||||
@ -223,29 +217,6 @@ private extension MainView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTwoFactorOnboardingIfNeeded() {
|
|
||||||
guard viewModel.onboardingDestination == .securitySettings else {
|
|
||||||
hasTriggeredSecuritySettingsOnboarding = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard !hasTriggeredSecuritySettingsOnboarding else { return }
|
|
||||||
hasTriggeredSecuritySettingsOnboarding = true
|
|
||||||
|
|
||||||
if isMessengerModeEnabled {
|
|
||||||
if selectedTab != 5 {
|
|
||||||
selectedTab = 5
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if selectedTab != 3 {
|
|
||||||
selectedTab = 3
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isSettingsPresented = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var deepLinkNavigationLink: some View {
|
var deepLinkNavigationLink: some View {
|
||||||
NavigationLink(
|
NavigationLink(
|
||||||
destination: deepLinkChatDestination,
|
destination: deepLinkChatDestination,
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
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
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct EmailSecuritySettingsView: View {
|
|
||||||
@State private var isLoginCodesEnabled = false
|
|
||||||
@State private var activeAlert: EmailSecurityAlert?
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Form {
|
|
||||||
Section(header: Text(NSLocalizedString("Защита входа", comment: "Раздел защиты входа через email"))) {
|
|
||||||
Toggle(NSLocalizedString("Получать коды на email при входе", comment: "Переключатель отправки кодов при входе"), isOn: Binding(
|
|
||||||
get: { isLoginCodesEnabled },
|
|
||||||
set: { _ in
|
|
||||||
activeAlert = EmailSecurityAlert(
|
|
||||||
title: NSLocalizedString("Скоро", comment: "Заголовок заглушки"),
|
|
||||||
message: NSLocalizedString("Функция пока недоступна.", comment: "Сообщение заглушки")
|
|
||||||
)
|
|
||||||
isLoginCodesEnabled = false
|
|
||||||
}
|
|
||||||
))
|
|
||||||
|
|
||||||
Text(NSLocalizedString("Мы отправим код подтверждения на привязанный email каждый раз при входе.", comment: "Описание работы кодов при входе"))
|
|
||||||
.font(.footnote)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text(NSLocalizedString("Подтверждение email", comment: "Раздел подтверждения email"))) {
|
|
||||||
Text(NSLocalizedString("Email не подтверждён. Подтвердите, чтобы активировать дополнительные проверки.", comment: "Описание необходимости подтверждения email"))
|
|
||||||
.font(.callout)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
|
|
||||||
Button(NSLocalizedString("Отправить письмо подтверждения", comment: "Кнопка отправки письма подтверждения")) {
|
|
||||||
activeAlert = EmailSecurityAlert(
|
|
||||||
title: NSLocalizedString("Скоро", comment: "Заголовок заглушки"),
|
|
||||||
message: NSLocalizedString("Мы отправим письмо, как только функция будет готова.", comment: "Сообщение при недоступной отправке письма")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle(NSLocalizedString("Email", comment: "Заголовок экрана настроек email"))
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.alert(item: $activeAlert) { alert in
|
|
||||||
Alert(
|
|
||||||
title: Text(alert.title),
|
|
||||||
message: Text(alert.message),
|
|
||||||
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Общий текст кнопки OK")))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct EmailSecurityAlert: Identifiable {
|
|
||||||
let id = UUID()
|
|
||||||
let title: String
|
|
||||||
let message: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
struct EmailSecuritySettingsView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
NavigationView {
|
|
||||||
EmailSecuritySettingsView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
#if canImport(UIKit)
|
|
||||||
import UIKit
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct TwoFactorAuthView: View {
|
|
||||||
@State private var isTwoFactorEnabled = false
|
|
||||||
@State private var showEnableConfirmation = false
|
|
||||||
@State private var showDisableConfirmation = false
|
|
||||||
@State private var secretKey: String = TwoFactorAuthView.generateSecret()
|
|
||||||
@State private var verificationCode: String = ""
|
|
||||||
@State private var backupCodes: [String] = []
|
|
||||||
@State private var activeAlert: TwoFactorAlert?
|
|
||||||
@FocusState private var isCodeFieldFocused: Bool
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
List {
|
|
||||||
Section(header: Text(NSLocalizedString("Статус защиты", comment: "Раздел состояния 2FA"))) {
|
|
||||||
Toggle(isOn: Binding(
|
|
||||||
get: { isTwoFactorEnabled },
|
|
||||||
set: { handleToggleChange($0) }
|
|
||||||
)) {
|
|
||||||
Label(NSLocalizedString("Включить 2FA", comment: "Тоггл активации 2FA"), systemImage: "lock.shield")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTwoFactorEnabled {
|
|
||||||
Section(header: Text(NSLocalizedString("Настройка приложения", comment: "Раздел инструкций подключения"))) {
|
|
||||||
Text(NSLocalizedString("Добавьте новый аккаунт в приложении аутентификации и введите следующий ключ:", comment: "Инструкция по добавлению ключа 2FA"))
|
|
||||||
.font(.callout)
|
|
||||||
keyRow
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text(NSLocalizedString("Проверочный код", comment: "Раздел верификации 2FA"))) {
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
|
||||||
TextField(NSLocalizedString("Введите код из приложения", comment: "Поле ввода кода 2FA"), text: $verificationCode)
|
|
||||||
.keyboardType(.numberPad)
|
|
||||||
.focused($isCodeFieldFocused)
|
|
||||||
.onChange(of: verificationCode) { newValue in
|
|
||||||
verificationCode = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: verifyCode) {
|
|
||||||
Text(NSLocalizedString("Подтвердить", comment: "Кнопка подтверждения кода 2FA"))
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.buttonStyle(.borderedProminent)
|
|
||||||
.disabled(verificationCode.isEmpty)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text(NSLocalizedString("Коды восстановления", comment: "Раздел кодов восстановления 2FA"))) {
|
|
||||||
if backupCodes.isEmpty {
|
|
||||||
Text(NSLocalizedString("Сгенерируйте резервные коды и сохраните их в надежном месте.", comment: "Подсказка о необходимости генерации кодов"))
|
|
||||||
.font(.callout)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
} else {
|
|
||||||
ForEach(backupCodes, id: \.self) { code in
|
|
||||||
HStack {
|
|
||||||
Text(code)
|
|
||||||
.font(.system(.body, design: .monospaced))
|
|
||||||
Spacer()
|
|
||||||
Button(action: { copyToPasteboard(code) }) {
|
|
||||||
Image(systemName: "doc.on.doc")
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
.accessibilityLabel(NSLocalizedString("Скопировать код", comment: "Кнопка копирования кода восстановления"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: generateBackupCodes) {
|
|
||||||
Label(NSLocalizedString("Создать новые коды", comment: "Кнопка генерации резервных кодов"), systemImage: "arrow.clockwise")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(footer: Text(NSLocalizedString("Вы всегда можете отключить двухфакторную защиту, но мы рекомендуем оставлять её включённой для безопасности.", comment: "Рекомендация оставить 2FA включенной"))) {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.listStyle(.insetGrouped)
|
|
||||||
.navigationTitle(NSLocalizedString("Двухфакторная аутентификация", comment: "Заголовок экрана 2FA"))
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.alert(item: $activeAlert) { alert in
|
|
||||||
Alert(
|
|
||||||
title: Text(alert.title),
|
|
||||||
message: Text(alert.message),
|
|
||||||
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Общий текст кнопки OK")))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.confirmationDialog(
|
|
||||||
NSLocalizedString("Включить двухфакторную аутентификацию?", comment: "Заголовок подтверждения включения 2FA"),
|
|
||||||
isPresented: $showEnableConfirmation,
|
|
||||||
titleVisibility: .visible
|
|
||||||
) {
|
|
||||||
Button(NSLocalizedString("Включить", comment: "Кнопка подтверждения включения 2FA"), role: .destructive) {
|
|
||||||
enableTwoFactor()
|
|
||||||
}
|
|
||||||
Button(NSLocalizedString("Отмена", comment: "Общий текст кнопки отмены"), role: .cancel) {}
|
|
||||||
}
|
|
||||||
.confirmationDialog(
|
|
||||||
NSLocalizedString("Отключить двухфакторную аутентификацию?", comment: "Заголовок подтверждения отключения 2FA"),
|
|
||||||
isPresented: $showDisableConfirmation,
|
|
||||||
titleVisibility: .visible
|
|
||||||
) {
|
|
||||||
Button(NSLocalizedString("Отключить", comment: "Кнопка подтверждения отключения 2FA"), role: .destructive) {
|
|
||||||
disableTwoFactor()
|
|
||||||
}
|
|
||||||
Button(NSLocalizedString("Отмена", comment: "Общий текст кнопки отмены"), role: .cancel) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension TwoFactorAuthView {
|
|
||||||
var keyRow: some View {
|
|
||||||
HStack(alignment: .center, spacing: 12) {
|
|
||||||
Text(secretKey)
|
|
||||||
.font(.system(.body, design: .monospaced))
|
|
||||||
.textSelection(.enabled)
|
|
||||||
Spacer()
|
|
||||||
Button(action: { copyToPasteboard(secretKey) }) {
|
|
||||||
Image(systemName: "doc.on.doc")
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
.accessibilityLabel(NSLocalizedString("Скопировать ключ", comment: "Кнопка копирования секретного ключа"))
|
|
||||||
}
|
|
||||||
.padding(8)
|
|
||||||
.background(Color(UIColor.secondarySystemBackground))
|
|
||||||
.cornerRadius(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleToggleChange(_ newValue: Bool) {
|
|
||||||
if newValue {
|
|
||||||
showEnableConfirmation = true
|
|
||||||
} else {
|
|
||||||
showDisableConfirmation = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableTwoFactor() {
|
|
||||||
isTwoFactorEnabled = true
|
|
||||||
showEnableConfirmation = false
|
|
||||||
secretKey = Self.generateSecret()
|
|
||||||
verificationCode = ""
|
|
||||||
generateBackupCodes()
|
|
||||||
activeAlert = TwoFactorAlert(
|
|
||||||
title: NSLocalizedString("2FA включена", comment: "Заголовок уведомления об успешной активации 2FA"),
|
|
||||||
message: NSLocalizedString("Сохраните секретный ключ и введите код из приложения, чтобы завершить настройку.", comment: "Сообщение после активации 2FA")
|
|
||||||
)
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
||||||
isCodeFieldFocused = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disableTwoFactor() {
|
|
||||||
isTwoFactorEnabled = false
|
|
||||||
showDisableConfirmation = false
|
|
||||||
verificationCode = ""
|
|
||||||
backupCodes.removeAll()
|
|
||||||
activeAlert = TwoFactorAlert(
|
|
||||||
title: NSLocalizedString("2FA отключена", comment: "Заголовок уведомления об отключении 2FA"),
|
|
||||||
message: NSLocalizedString("Вы можете включить защиту снова в любой момент.", comment: "Сообщение после отключения 2FA")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyCode() {
|
|
||||||
let normalized = verificationCode.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
guard normalized.count == 6, normalized.allSatisfy(\.isNumber) else {
|
|
||||||
activeAlert = TwoFactorAlert(
|
|
||||||
title: NSLocalizedString("Неверный код", comment: "Заголовок ошибки неправильного кода 2FA"),
|
|
||||||
message: NSLocalizedString("Проверьте цифры и попробуйте снова.", comment: "Описание ошибки неверного кода 2FA")
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
verificationCode = ""
|
|
||||||
activeAlert = TwoFactorAlert(
|
|
||||||
title: NSLocalizedString("Код принят", comment: "Заголовок успешного подтверждения кода 2FA"),
|
|
||||||
message: NSLocalizedString("Двухфакторная аутентификация настроена.", comment: "Сообщение после успешного подтверждения кода 2FA")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateBackupCodes() {
|
|
||||||
backupCodes = Self.generateBackupCodes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyToPasteboard(_ value: String) {
|
|
||||||
#if canImport(UIKit)
|
|
||||||
UIPasteboard.general.string = value
|
|
||||||
#endif
|
|
||||||
activeAlert = TwoFactorAlert(
|
|
||||||
title: NSLocalizedString("Скопировано", comment: "Заголовок уведомления о копировании"),
|
|
||||||
message: NSLocalizedString("Значение сохранено в буфере обмена.", comment: "Сообщение после копирования")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func generateSecret() -> String {
|
|
||||||
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
|
|
||||||
return String((0..<16).compactMap { _ in alphabet.randomElement() })
|
|
||||||
}
|
|
||||||
|
|
||||||
static func generateBackupCodes(count: Int = 8) -> [String] {
|
|
||||||
let alphabet = Array("ABCDEFGHJKLMNPQRSTUVWXYZ23456789")
|
|
||||||
return (0..<count).map { _ in
|
|
||||||
String((0..<8).compactMap { _ in alphabet.randomElement() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct TwoFactorAlert: Identifiable {
|
|
||||||
let id = UUID()
|
|
||||||
let title: String
|
|
||||||
let message: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
struct TwoFactorAuthView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
NavigationView {
|
|
||||||
TwoFactorAuthView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct SecuritySettingsView: View {
|
|
||||||
@ObservedObject var viewModel: LoginViewModel
|
|
||||||
@State private var isTwoFactorActive = false
|
|
||||||
@State private var isEmailSettingsActive = false
|
|
||||||
@State private var isAppLockActive = false
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
List {
|
|
||||||
Section(header: Text(NSLocalizedString("Вход и защита аккаунта (заглушка)", comment: "Раздел настроек безопасности для аутентификации"))) {
|
|
||||||
NavigationLink(isActive: $isTwoFactorActive) {
|
|
||||||
TwoFactorAuthView()
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Двухфакторная аутентификация", comment: "Переход к настройкам двухфакторной аутентификации"), systemImage: "lock.shield")
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(isActive: $isEmailSettingsActive) {
|
|
||||||
EmailSecuritySettingsView()
|
|
||||||
} label: {
|
|
||||||
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)
|
|
||||||
.navigationTitle(NSLocalizedString("Безопасность", comment: "Заголовок экрана настроек безопасности"))
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
// .onAppear { handleSecuritySettingsOnboardingIfNeeded() }
|
|
||||||
// .onChange(of: viewModel.onboardingDestination) { _ in
|
|
||||||
// handleSecuritySettingsOnboardingIfNeeded()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// private func handleSecuritySettingsOnboardingIfNeeded() {
|
|
||||||
// guard viewModel.onboardingDestination == .securitySettings else { return }
|
|
||||||
// guard !isTwoFactorActive else {
|
|
||||||
// viewModel.onboardingDestination = nil
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
// isTwoFactorActive = true
|
|
||||||
// viewModel.onboardingDestination = nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
struct SecuritySettingsView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
NavigationView {
|
|
||||||
SecuritySettingsView(viewModel: LoginViewModel())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -4,7 +4,6 @@ struct SettingsView: View {
|
|||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@EnvironmentObject private var themeManager: ThemeManager
|
@EnvironmentObject private var themeManager: ThemeManager
|
||||||
@State private var isThemeExpanded = false
|
@State private var isThemeExpanded = false
|
||||||
@State private var isSecurityActive = false
|
|
||||||
private let themeOptions = ThemeOption.ordered
|
private let themeOptions = ThemeOption.ordered
|
||||||
|
|
||||||
private var selectedThemeOption: ThemeOption {
|
private var selectedThemeOption: ThemeOption {
|
||||||
@ -18,9 +17,9 @@ struct SettingsView: View {
|
|||||||
// NavigationLink(destination: EditProfileView()) {
|
// NavigationLink(destination: EditProfileView()) {
|
||||||
// Label("Мой профиль", systemImage: "person.crop.circle")
|
// Label("Мой профиль", systemImage: "person.crop.circle")
|
||||||
// }
|
// }
|
||||||
|
|
||||||
NavigationLink(destination: EditProfileView()) {
|
NavigationLink(destination: EditPrivacyView()) {
|
||||||
Label(NSLocalizedString("Редактировать профиль", comment: ""), systemImage: "person.crop.circle")
|
Label(NSLocalizedString("Конфиденциальность", comment: ""), systemImage: "lock.fill")
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(destination: BlockedUsersView()) {
|
NavigationLink(destination: BlockedUsersView()) {
|
||||||
@ -30,21 +29,15 @@ 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: Text("Заглушка: Двухфакторная аутентификация")) {
|
||||||
|
Label("Двухфакторная аутентификация", systemImage: "lock.shield")
|
||||||
|
}
|
||||||
NavigationLink(destination: ActiveSessionsView()) {
|
NavigationLink(destination: ActiveSessionsView()) {
|
||||||
Label(NSLocalizedString("Активные сессии", comment: ""), systemImage: "iphone")
|
Label(NSLocalizedString("Активные сессии", comment: ""), systemImage: "iphone")
|
||||||
}
|
}
|
||||||
NavigationLink(isActive: $isSecurityActive) {
|
|
||||||
SecuritySettingsView(viewModel: viewModel)
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Безопасность", comment: ""), systemImage: "lock.shield")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Приложение
|
// MARK: - Приложение
|
||||||
@ -132,12 +125,6 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Настройки")
|
.navigationTitle("Настройки")
|
||||||
.onAppear {
|
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
|
||||||
}
|
|
||||||
.onChange(of: viewModel.onboardingDestination) { _ in
|
|
||||||
handleTwoFactorOnboardingIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openLanguageSettings() {
|
private func openLanguageSettings() {
|
||||||
@ -178,13 +165,3 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SettingsView {
|
|
||||||
func handleTwoFactorOnboardingIfNeeded() {
|
|
||||||
guard viewModel.onboardingDestination == .securitySettings else { return }
|
|
||||||
guard !isSecurityActive else { return }
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isSecurityActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user