Added ability to recover cloud password with email from settings.

This commit is contained in:
23rd 2022-05-09 02:19:51 +03:00
parent 4d5cb6398e
commit 031525e7e3
4 changed files with 162 additions and 12 deletions

View File

@ -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(

View File

@ -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());
});
}
});

View File

@ -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<QString> title() override;
void setupContent();
private:
rpl::lifetime _requestLifetime;
};
rpl::producer<QString> 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(

View File

@ -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<QString> title() override;
void setupContent();
[[nodiscard]] rpl::producer<std::vector<Type>> removeFromStack() override;
private:
rpl::variable<std::vector<Type>> _removesFromStack;
rpl::lifetime _requestLifetime;
};
rpl::producer<std::vector<Type>> Input::removeFromStack() {
return _removesFromStack.value();
}
rpl::producer<QString> Input::title() {
return tr::lng_settings_cloud_password_password_title();
}
@ -81,11 +89,22 @@ void Input::setupContent() {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(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<Type>{
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<Ui::FlatLabel>(
error->parentWidget(),
@ -149,6 +170,44 @@ void Input::setupContent() {
hintInfo->setVisible(!hint.isEmpty());
}
}, hintInfo->lifetime());
const auto recover = Ui::CreateChild<Ui::LinkButton>(
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());