From 031525e7e37bddce3f1ced0f8a1f7080fb4b33e3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 9 May 2022 02:19:51 +0300 Subject: [PATCH] Added ability to recover cloud password with email from settings. --- .../settings_cloud_password_common.h | 7 ++ .../settings_cloud_password_email_confirm.cpp | 59 ++++++++++++++-- .../settings_cloud_password_hint.cpp | 40 +++++++++-- .../settings_cloud_password_input.cpp | 68 ++++++++++++++++++- 4 files changed, 162 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h index 6d742fa433..8a32b7a834 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h @@ -34,6 +34,13 @@ struct StepData { QString email; int unconfirmedEmailLengthCode; bool setOnlyRecoveryEmail = false; + + struct ProcessRecover { + bool setNewPassword = false; + QString checkedCode; + QString emailPattern; + }; + ProcessRecover processRecover; }; void SetupHeader( diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp index 22219bab7d..36edfd9598 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp @@ -67,6 +67,8 @@ void EmailConfirm::setupContent() { // we should forget the current password. const auto currentPassword = base::take(currentStepData.currentPassword); const auto typedPassword = base::take(currentStepData.password); + const auto recoverEmailPattern = base::take( + currentStepData.processRecover.emailPattern); setStepData(currentStepData); const auto state = cloudPassword().stateCurrent(); @@ -77,7 +79,9 @@ void EmailConfirm::setupContent() { } cloudPassword().state( ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) { - if (!_requestLifetime && state.unconfirmedPattern.isEmpty()) { + if (!_requestLifetime + && state.unconfirmedPattern.isEmpty() + && recoverEmailPattern.isEmpty()) { setStepData(StepData()); showBack(); } @@ -87,12 +91,16 @@ void EmailConfirm::setupContent() { content, u"cloud_password/email"_q, showFinishes(), - tr::lng_cloud_password_confirm(), + state->unconfirmedPattern.isEmpty() + ? tr::lng_settings_cloud_password_email_recovery_subtitle() + : tr::lng_cloud_password_confirm(), rpl::single( tr::lng_cloud_password_waiting_code( tr::now, lt_email, - state->unconfirmedPattern))); + state->unconfirmedPattern.isEmpty() + ? recoverEmailPattern + : state->unconfirmedPattern))); AddSkip(content, st::settingLocalPasscodeDescriptionBottomSkip); @@ -147,16 +155,19 @@ void EmailConfirm::setupContent() { newInput->hideError(); }); }); + resend->setVisible(recoverEmailPattern.isEmpty()); const auto button = AddDoneButton( content, - tr::lng_settings_cloud_password_email_confirm()); + recoverEmailPattern.isEmpty() + ? tr::lng_settings_cloud_password_email_confirm() + : tr::lng_passcode_check_button()); button->setClickedCallback([=] { const auto newText = newInput->getDigitsOnly(); if (newText.isEmpty()) { newInput->setFocus(); newInput->showError(); - } else if (!_requestLifetime) { + } else if (!_requestLifetime && recoverEmailPattern.isEmpty()) { _requestLifetime = cloudPassword().confirmEmail( newText ) | rpl::start_with_error_done([=](const QString &type) { @@ -193,6 +204,44 @@ void EmailConfirm::setupContent() { showOther(CloudPasswordManageId()); } }); + } else if (!_requestLifetime) { + _requestLifetime = cloudPassword().checkRecoveryEmailAddressCode( + newText + ) | rpl::start_with_error_done([=](const QString &type) { + _requestLifetime.destroy(); + + newInput->setFocus(); + newInput->showError(); + error->show(); + + if (MTP::IsFloodError(type)) { + error->setText(tr::lng_flood_error(tr::now)); + return; + } + + if (type == u"PASSWORD_RECOVERY_NA"_q) { + setStepData(StepData()); + showBack(); + } else if (type == u"PASSWORD_RECOVERY_EXPIRED"_q) { + setStepData(StepData()); + showBack(); + } else if (type == u"CODE_INVALID"_q) { + error->setText(tr::lng_signin_wrong_code(tr::now)); + } else { + error->setText(Logs::DebugEnabled() + // internal server error + ? type + : Lang::Hard::ServerError()); + } + }, [=] { + _requestLifetime.destroy(); + + auto empty = StepData(); + empty.processRecover.checkedCode = newText; + empty.processRecover.setNewPassword = true; + setStepData(std::move(empty)); + showOther(CloudPasswordInputId()); + }); } }); diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_hint.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_hint.cpp index fe72f42951..60b44d609c 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_hint.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_hint.cpp @@ -7,9 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/cloud_password/settings_cloud_password_hint.h" +#include "api/api_cloud_password.h" #include "lang/lang_keys.h" #include "settings/cloud_password/settings_cloud_password_common.h" #include "settings/cloud_password/settings_cloud_password_email.h" +#include "settings/cloud_password/settings_cloud_password_manage.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" @@ -28,6 +30,9 @@ public: [[nodiscard]] rpl::producer title() override; void setupContent(); +private: + rpl::lifetime _requestLifetime; + }; rpl::producer Hint::title() { @@ -61,10 +66,37 @@ void Hint::setupContent() { AddSkipInsteadOfField(content); const auto save = [=](const QString &hint) { - auto data = stepData(); - data.hint = hint; - setStepData(std::move(data)); - showOther(CloudPasswordEmailId()); + if (currentStepData.processRecover.setNewPassword) { + if (_requestLifetime) { + return; + } + _requestLifetime = cloudPassword().recoverPassword( + currentStepData.processRecover.checkedCode, + currentStepData.password, + hint + ) | rpl::start_with_error_done([=](const QString &type) { + _requestLifetime.destroy(); + + error->show(); + if (MTP::IsFloodError(type)) { + error->setText(tr::lng_flood_error(tr::now)); + } else { + error->setText(Lang::Hard::ServerError()); + } + }, [=] { + _requestLifetime.destroy(); + + auto empty = StepData(); + empty.currentPassword = stepData().password; + setStepData(std::move(empty)); + showOther(CloudPasswordManageId()); + }); + } else { + auto data = stepData(); + data.hint = hint; + setStepData(std::move(data)); + showOther(CloudPasswordEmailId()); + } }; AddLinkButton( diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp index 1152ac1237..880de6afc8 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_input.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "lottie/lottie_icon.h" #include "settings/cloud_password/settings_cloud_password_common.h" +#include "settings/cloud_password/settings_cloud_password_email_confirm.h" #include "settings/cloud_password/settings_cloud_password_hint.h" #include "settings/cloud_password/settings_cloud_password_manage.h" #include "ui/widgets/buttons.h" @@ -68,11 +69,18 @@ public: [[nodiscard]] rpl::producer title() override; void setupContent(); + [[nodiscard]] rpl::producer> removeFromStack() override; + private: + rpl::variable> _removesFromStack; rpl::lifetime _requestLifetime; }; +rpl::producer> Input::removeFromStack() { + return _removesFromStack.value(); +} + rpl::producer Input::title() { return tr::lng_settings_cloud_password_password_title(); } @@ -81,11 +89,22 @@ void Input::setupContent() { const auto content = Ui::CreateChild(this); auto currentStepData = stepData(); const auto currentStepDataPassword = base::take(currentStepData.password); + const auto currentStepProcessRecover = base::take( + currentStepData.processRecover); setStepData(currentStepData); const auto currentState = cloudPassword().stateCurrent(); - const auto hasPassword = currentState ? currentState->hasPassword : false; - const auto isCheck = stepData().currentPassword.isEmpty() && hasPassword; + const auto hasPassword = !currentStepProcessRecover.setNewPassword + && (currentState ? currentState->hasPassword : false); + const auto isCheck = currentStepData.currentPassword.isEmpty() + && hasPassword + && !currentStepProcessRecover.setNewPassword; + + if (currentStepProcessRecover.setNewPassword) { + _removesFromStack = std::vector{ + CloudPasswordEmailConfirmId() + }; + } const auto icon = CreateInteractiveLottieIcon( content, @@ -131,6 +150,8 @@ void Input::setupContent() { } if (isCheck) { + AddSkipInsteadOfField(content); + const auto hint = currentState ? currentState->hint : QString(); const auto hintInfo = Ui::CreateChild( error->parentWidget(), @@ -149,6 +170,44 @@ void Input::setupContent() { hintInfo->setVisible(!hint.isEmpty()); } }, hintInfo->lifetime()); + + const auto recover = Ui::CreateChild( + content, + tr::lng_signin_recover(tr::now)); + + rpl::merge( + content->geometryValue(), + newInput->geometryValue() + ) | rpl::start_with_next([=] { + const auto topLeft = newInput->mapTo(content, newInput->pos()); + recover->moveToLeft( + newInput->pos().x(), + topLeft.y() + newInput->height() + st::passcodeTextLine); + }, recover->lifetime()); + recover->setClickedCallback([=] { + if (_requestLifetime) { + return; + } + _requestLifetime = cloudPassword().requestPasswordRecovery( + ) | rpl::start_with_next_error([=](const QString &pattern) { + _requestLifetime.destroy(); + + auto data = stepData(); + data.processRecover = currentStepProcessRecover; + data.processRecover.emailPattern = pattern; + setStepData(std::move(data)); + showOther(CloudPasswordEmailConfirmId()); + }, [=](const QString &type) { + _requestLifetime.destroy(); + + error->show(); + if (MTP::IsFloodError(type)) { + error->setText(tr::lng_flood_error(tr::now)); + } else { + error->setText(Lang::Hard::ServerError()); + } + }); + }); } if (!newInput->text().isEmpty()) { @@ -168,7 +227,9 @@ void Input::setupContent() { newInput->showError(); newInput->selectAll(); error->show(); - if (type == u"PASSWORD_HASH_INVALID"_q + if (MTP::IsFloodError(type)) { + error->setText(tr::lng_flood_error(tr::now)); + } else if (type == u"PASSWORD_HASH_INVALID"_q || type == u"SRP_PASSWORD_CHANGED"_q) { error->setText(tr::lng_cloud_password_wrong(tr::now)); } else { @@ -206,6 +267,7 @@ void Input::setupContent() { checkPassword(newText); } else { auto data = stepData(); + data.processRecover = currentStepProcessRecover; data.password = newText; setStepData(std::move(data)); showOther(CloudPasswordHintId());