Confirm 2sv recovery email by code.

This commit is contained in:
John Preston 2018-11-07 13:39:31 +04:00
parent 93678a07a8
commit be3e43e6cb
15 changed files with 441 additions and 108 deletions

View File

@ -342,6 +342,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_sessions_about" = "Control your sessions on other devices.";
"lng_settings_passcode_disable" = "Disable passcode";
"lng_settings_password_disable" = "Disable cloud password";
"lng_settings_password_abort" = "Abort two-step verification setup";
"lng_settings_password_reenter_email" = "Re-enter recovery email";
"lng_settings_about_bio" = "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco";
"lng_settings_name_label" = "Name";
"lng_settings_username_label" = "Username";
@ -449,7 +451,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passcode_logout" = "Log out";
"lng_passcode_need_unblock" = "You need to unlock me first.";
"lng_cloud_password_waiting" = "Confirmation link sent to {email}...";
"lng_cloud_password_waiting_code" = "Confirmation code sent to {email}...";
"lng_cloud_password_confirm" = "Confirm recovery email";
"lng_cloud_password_change" = "Change cloud password";
"lng_cloud_password_create" = "Cloud password";
"lng_cloud_password_remove" = "Remove cloud password";
@ -475,6 +478,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cloud_password_wrong" = "Wrong cloud password";
"lng_cloud_password_is_same" = "Password was not changed";
"lng_cloud_password_passport_losing" = "Warning! All data saved in your Telegram Passport will be lost!";
"lng_cloud_password_resend" = "Resend code";
"lng_cloud_password_resent" = "Code was resent.";
"lng_connection_auto_connecting" = "Default (connecting...)";
"lng_connection_auto" = "Default ({transport} used)";
@ -1602,7 +1607,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_create_password" = "Please create a password which will be used\nto encrypt your personal data.";
"lng_passport_about_password" = "This password will also be required whenever\nyou log in to a new device.";
"lng_passport_password_create" = "Create a password";
"lng_passport_link_sent" = "A confirmation link was sent to your email\n{email}";
"lng_passport_email_validate" = "Validate";
"lng_passport_code_sent" = "A confirmation code was sent to\n{email}";
"lng_passport_stop_password_sure" = "Are you sure you want to cancel setting up your password?";
"lng_passport_password_placeholder" = "Your password";
"lng_passport_next" = "Next";

View File

@ -5038,16 +5038,7 @@ void ApiWrap::reloadPasswordState() {
}
void ApiWrap::clearUnconfirmedPassword() {
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings(
MTP_flags(
MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_passwordKdfAlgoUnknown(), // new_algo
MTP_bytes(QByteArray()), // new_password_hash
MTP_string(QString()), // hint
MTP_string(QString()), // email
MTPSecureSecretSettings())
_passwordRequestId = request(MTPaccount_CancelPasswordEmail(
)).done([=](const MTPBool &result) {
_passwordRequestId = 0;
reloadPasswordState();

View File

@ -118,12 +118,15 @@ public:
_boxClosingStream.fire({});
}
void setDelegate(BoxContentDelegate *newDelegate) {
void setDelegate(not_null<BoxContentDelegate*> newDelegate) {
_delegate = newDelegate;
_preparing = true;
prepare();
finishPrepare();
}
not_null<BoxContentDelegate*> getDelegate() const {
return _delegate;
}
public slots:
void onScrollToY(int top, int bottom = -1);
@ -187,10 +190,6 @@ protected:
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
not_null<BoxContentDelegate*> getDelegate() const {
return _delegate;
}
private slots:
void onScroll();
void onInnerResize();

View File

@ -10,12 +10,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/bytes.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "boxes/confirm_phone_box.h"
#include "mainwindow.h"
#include "auth_session.h"
#include "storage/localstorage.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "passport/passport_encryption.h"
#include "passport/passport_panel_edit_contact.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
namespace {
} // namespace
PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
: _turningOff(turningOff)
@ -64,6 +75,10 @@ rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
return _passwordReloadNeeded.events();
}
rpl::producer<> PasscodeBox::clearUnconfirmedPassword() const {
return _clearUnconfirmedPassword.events();
}
bool PasscodeBox::currentlyHave() const {
return _cloudPwd ? (!!_curRequest) : Global::LocalPasscode();
}
@ -266,20 +281,96 @@ void PasscodeBox::setPasswordFail(const RPCError &error) {
void PasscodeBox::setPasswordFail(
const QByteArray &newPasswordBytes,
const QString &email,
const RPCError &error) {
if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
const auto prefix = qstr("EMAIL_UNCONFIRMED_");
if (error.type().startsWith(prefix)) {
const auto codeLength = error.type().mid(prefix.size()).toInt();
closeReplacedBy();
_setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_almost)),
LayerOption::CloseOther);
validateEmail(email, codeLength, newPasswordBytes);
} else {
setPasswordFail(error);
}
}
void PasscodeBox::validateEmail(
const QString &email,
int codeLength,
const QByteArray &newPasswordBytes) {
const auto errors = std::make_shared<rpl::event_stream<QString>>();
const auto resent = std::make_shared<rpl::event_stream<QString>>();
const auto set = std::make_shared<bool>(false);
const auto submit = [=](QString code) {
if (_setRequest) {
return;
}
_setRequest = request(MTPaccount_ConfirmPasswordEmail(
MTP_string(code)
)).done([=](const MTPBool &result) {
*set = true;
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
_setRequest = 0;
if (MTP::isFloodError(error)) {
errors->fire(lang(lng_flood_error));
} else if (error.type() == qstr("CODE_INVALID")) {
errors->fire(lang(lng_signin_wrong_code));
} else if (error.type() == qstr("EMAIL_HASH_EXPIRED")) {
const auto weak = make_weak(this);
_clearUnconfirmedPassword.fire({});
if (weak) {
auto box = Box<InformBox>(
Lang::Hard::EmailConfirmationExpired());
weak->getDelegate()->show(
std::move(box),
LayerOption::CloseOther);
}
} else {
errors->fire(Lang::Hard::ServerError());
}
}).handleFloodErrors().send();
};
const auto resend = [=] {
if (_setRequest) {
return;
}
_setRequest = request(MTPaccount_ResendPasswordEmail(
)).done([=](const MTPBool &result) {
_setRequest = 0;
resent->fire(lang(lng_cloud_password_resent));
}).fail([=](const RPCError &error) {
_setRequest = 0;
errors->fire(Lang::Hard::ServerError());
}).send();
};
const auto box = getDelegate()->show(
Passport::VerifyEmailBox(
email,
codeLength,
submit,
resend,
errors->events(),
resent->events()),
LayerOption::KeepOther);
box->setCloseByOutsideClick(false);
box->setCloseByEscape(false);
box->boxClosing(
) | rpl::filter([=] {
return !*set;
}) | start_with_next([=, weak = make_weak(this)] {
if (weak) {
weak->_clearUnconfirmedPassword.fire({});
}
if (weak) {
weak->closeBox();
}
}, box->lifetime());
}
void PasscodeBox::handleSrpIdInvalid() {
const auto now = getms(true);
if (_lastSrpIdInvalidTime > 0
@ -474,7 +565,7 @@ void PasscodeBox::sendClearCloudPassword(
)).done([=](const MTPBool &result) {
setPasswordDone({});
}).handleFloodErrors().fail([=](const RPCError &error) mutable {
setPasswordFail({}, error);
setPasswordFail({}, QString(), error);
}).send();
}
@ -505,7 +596,7 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
setPasswordFail(newPasswordBytes, error);
setPasswordFail(newPasswordBytes, email, error);
}).send();
}
@ -655,7 +746,7 @@ void PasscodeBox::sendChangeCloudPassword(
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).handleFloodErrors().fail([=](const RPCError &error) {
setPasswordFail(newPasswordBytes, error);
setPasswordFail(newPasswordBytes, QString(), error);
}).send();
}
@ -890,3 +981,81 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
_recoverCode->setFocus();
return false;
}
RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern) {
const auto errors = std::make_shared<rpl::event_stream<QString>>();
const auto resent = std::make_shared<rpl::event_stream<QString>>();
const auto requestId = std::make_shared<mtpRequestId>(0);
const auto weak = std::make_shared<QPointer<BoxContent>>();
const auto reloads = std::make_shared<rpl::event_stream<>>();
const auto cancels = std::make_shared<rpl::event_stream<>>();
const auto submit = [=](QString code) {
if (*requestId) {
return;
}
const auto done = [=](const MTPBool &result) {
*requestId = 0;
reloads->fire({});
if (*weak) {
(*weak)->getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_was_set)),
LayerOption::CloseOther);
}
};
const auto fail = [=](const RPCError &error) {
const auto skip = MTP::isDefaultHandledError(error)
&& !MTP::isFloodError(error);
if (skip) {
return false;
}
*requestId = 0;
if (MTP::isFloodError(error)) {
errors->fire(lang(lng_flood_error));
} else if (error.type() == qstr("CODE_INVALID")) {
errors->fire(lang(lng_signin_wrong_code));
} else if (error.type() == qstr("EMAIL_HASH_EXPIRED")) {
cancels->fire({});
if (*weak) {
auto box = Box<InformBox>(
Lang::Hard::EmailConfirmationExpired());
(*weak)->getDelegate()->show(
std::move(box),
LayerOption::CloseOther);
}
} else {
errors->fire(Lang::Hard::ServerError());
}
return true;
};
*requestId = MTP::send(
MTPaccount_ConfirmPasswordEmail(MTP_string(code)),
rpcDone(done),
rpcFail(fail));
};
const auto resend = [=] {
if (*requestId) {
return;
}
*requestId = MTP::send(MTPaccount_ResendPasswordEmail(
), rpcDone([=](const MTPBool &result) {
*requestId = 0;
resent->fire(lang(lng_cloud_password_resent));
}), rpcFail([=](const RPCError &error) {
*requestId = 0;
errors->fire(Lang::Hard::ServerError());
return true;
}));
};
auto box = Passport::VerifyEmailBox(
pattern,
0,
submit,
resend,
errors->events(),
resent->events());
*weak = box.data();
return { std::move(box), reloads->events(), cancels->events() };
}

View File

@ -32,6 +32,7 @@ public:
rpl::producer<QByteArray> newPasswordSet() const;
rpl::producer<> passwordReloadNeeded() const;
rpl::producer<> clearUnconfirmedPassword() const;
protected:
void prepare() override;
@ -59,7 +60,12 @@ private:
void setPasswordFail(const RPCError &error);
void setPasswordFail(
const QByteArray &newPasswordBytes,
const QString &email,
const RPCError &error);
void validateEmail(
const QString &email,
int codeLength,
const QByteArray &newPasswordBytes);
void recoverStarted(const MTPauth_PasswordRecovery &result);
void recoverStartFail(const RPCError &error);
@ -90,9 +96,6 @@ private:
const Core::CloudPasswordResult &check,
const QString &newPassword,
Fn<void()> callback);
void resetSecretAndChangePassword(
const bytes::vector &oldPasswordHash,
const QString &newPassword);
void sendClearCloudPassword(const QString &oldPassword);
void sendClearCloudPassword(const Core::CloudPasswordResult &check);
@ -134,6 +137,7 @@ private:
rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<> _passwordReloadNeeded;
rpl::event_stream<> _clearUnconfirmedPassword;
};
@ -173,3 +177,10 @@ private:
rpl::event_stream<> _recoveryExpired;
};
struct RecoveryEmailValidation {
object_ptr<BoxContent> box;
rpl::producer<> reloadRequests;
rpl::producer<> cancelRequests;
};
RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern);

View File

@ -62,5 +62,9 @@ inline QString UnknownSecureScanError() {
return qsl("Unknown scan read error.");
}
inline QString EmailConfirmationExpired() {
return qsl("This email confirmation has expired. Please setup two-step verification once again.");
}
} // namespace Hard
} // namespace Lang

View File

@ -1016,15 +1016,7 @@ void FormController::cancelPassword() {
if (_passwordRequestId) {
return;
}
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings(
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_passwordKdfAlgoUnknown(), // new_algo
MTP_bytes(QByteArray()), // new_password_hash
MTP_string(QString()), // hint
MTP_string(QString()), // email
MTPSecureSecretSettings())
_passwordRequestId = request(MTPaccount_CancelPasswordEmail(
)).done([=](const MTPBool &result) {
_passwordRequestId = 0;
reloadPassword();

View File

@ -738,6 +738,11 @@ void PanelController::setupPassword() {
) | rpl::start_with_next([=] {
_form->reloadPassword();
}, box->lifetime());
box->clearUnconfirmedPassword(
) | rpl::start_with_next([=] {
_form->cancelPassword();
}, box->lifetime());
}
void PanelController::cancelPasswordSubmit() {
@ -748,6 +753,24 @@ void PanelController::cancelPasswordSubmit() {
[=] { if (*box) (*box)->closeBox(); _form->cancelPassword(); }));
}
void PanelController::validateRecoveryEmail() {
auto validation = ConfirmRecoveryEmail(unconfirmedEmailPattern());
std::move(
validation.reloadRequests
) | rpl::start_with_next([=] {
_form->reloadPassword();
}, validation.box->lifetime());
std::move(
validation.cancelRequests
) | rpl::start_with_next([=] {
_form->cancelPassword();
}, validation.box->lifetime());
show(std::move(validation.box));
}
bool PanelController::canAddScan(FileType type) const {
Expects(_editScope != nullptr);
Expects(_editDocument != nullptr);
@ -1335,12 +1358,15 @@ void PanelController::processVerificationNeeded(
text,
value->verification.codeLength,
[=](const QString &code) { _form->verify(value, code); },
nullptr, // resend
rpl::duplicate(
update
) | rpl::map([=](not_null<const Value*> field) {
return field->verification.error;
}) | rpl::distinct_until_changed()));
}) | rpl::distinct_until_changed(),
rpl::never<QString>()));
} else {
Unexpected("Type in processVerificationNeeded.");
}

View File

@ -95,6 +95,7 @@ public:
void setupPassword();
void cancelPasswordSubmit();
void validateRecoveryEmail();
bool canAddScan(FileType type) const;
void uploadScan(FileType type, QByteArray &&content);

View File

@ -35,8 +35,10 @@ public:
const QString &text,
int codeLength,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
rpl::producer<QString> error);
rpl::producer<QString> error,
rpl::producer<QString> resent);
void setInnerFocus() override;
@ -48,13 +50,15 @@ private:
const QString &text,
int codeLength,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
rpl::producer<QString> error);
rpl::producer<QString> error,
rpl::producer<QString> resent);
QString _title;
Fn<void()> _submit;
QPointer<SentCodeField> _code;
int _height = 0;
QPointer<Ui::VerticalLayout> _content;
};
@ -64,44 +68,94 @@ VerifyBox::VerifyBox(
const QString &text,
int codeLength,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
rpl::producer<QString> error)
rpl::producer<QString> error,
rpl::producer<QString> resent)
: _title(title) {
setupControls(
text,
codeLength,
submit,
resend,
std::move(call),
std::move(error));
std::move(error),
std::move(resent));
}
void VerifyBox::setupControls(
const QString &text,
int codeLength,
Fn<void(QString code)> submit,
Fn<void()> resend,
rpl::producer<QString> call,
rpl::producer<QString> error) {
const auto description = Ui::CreateChild<Ui::FlatLabel>(
this,
text,
Ui::FlatLabel::InitType::Simple,
st::boxLabel);
_code = Ui::CreateChild<SentCodeField>(
this,
st::defaultInputField,
langFactory(lng_change_phone_code_title));
rpl::producer<QString> error,
rpl::producer<QString> resent) {
_content = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto problem = Ui::CreateChild<Ui::FadeWrap<Ui::FlatLabel>>(
this,
const auto small = style::margins(
st::boxPadding.left(),
0,
st::boxPadding.right(),
st::boxPadding.bottom());
const auto description = _content->add(
object_ptr<Ui::FlatLabel>(
this,
QString(),
_content,
text,
Ui::FlatLabel::InitType::Simple,
st::passportVerifyErrorLabel));
const auto waiter = Ui::CreateChild<Ui::FlatLabel>(
this,
std::move(call),
st::boxDividerLabel);
st::boxLabel),
small);
_code = _content->add(
object_ptr<SentCodeField>(
_content,
st::defaultInputField,
langFactory(lng_change_phone_code_title)),
small);
const auto problem = _content->add(
object_ptr<Ui::FadeWrap<Ui::FlatLabel>>(
_content,
object_ptr<Ui::FlatLabel>(
_content,
QString(),
Ui::FlatLabel::InitType::Simple,
st::passportVerifyErrorLabel)),
small);
const auto waiter = _content->add(
object_ptr<Ui::FlatLabel>(
_content,
std::move(call),
st::boxDividerLabel),
small);
if (resend) {
auto link = TextWithEntities{ lang(lng_cloud_password_resend) };
link.entities.push_back(EntityInText(
EntityInTextCustomUrl,
0,
link.text.size(),
QString("internal:resend")));
const auto label = _content->add(
object_ptr<Ui::FlatLabel>(
_content,
rpl::single(
link
) | rpl::then(rpl::duplicate(
resent
) | rpl::map([](const QString &value) {
return TextWithEntities{ value };
})),
st::boxDividerLabel),
small);
std::move(
resent
) | rpl::start_with_next([=] {
_content->resizeToWidth(st::boxWidth);
}, _content->lifetime());
label->setClickHandlerFilter([=](auto&&...) {
resend();
return false;
});
}
std::move(
error
) | rpl::start_with_next([=](const QString &error) {
@ -109,29 +163,12 @@ void VerifyBox::setupControls(
problem->hide(anim::type::normal);
} else {
problem->entity()->setText(error);
_content->resizeToWidth(st::boxWidth);
problem->show(anim::type::normal);
_code->showError();
}
}, lifetime());
auto y = 0;
const auto innerWidth = st::boxWidth
- st::boxPadding.left()
- st::boxPadding.right();
description->resizeToWidth(innerWidth);
description->moveToLeft(st::boxPadding.left(), y);
y += description->height() + st::boxPadding.bottom();
_code->resizeToWidth(innerWidth);
_code->moveToLeft(st::boxPadding.left(), y);
y += _code->height() + st::boxPadding.bottom();
problem->resizeToWidth(innerWidth);
problem->moveToLeft(st::boxPadding.left(), y);
y += problem->height() + st::boxPadding.top();
waiter->resizeToWidth(innerWidth);
waiter->moveToLeft(st::boxPadding.left(), y);
y += waiter->height() + st::boxPadding.bottom();
_height = y;
_submit = [=] {
submit(_code->getLastText());
};
@ -155,7 +192,11 @@ void VerifyBox::prepare() {
addButton(langFactory(lng_change_phone_new_submit), _submit);
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setDimensions(st::boxWidth, _height);
_content->resizeToWidth(st::boxWidth);
_content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWidth, height);
}, _content->lifetime());
}
} // namespace
@ -364,22 +405,28 @@ object_ptr<BoxContent> VerifyPhoneBox(
lng_passport_confirm_phone(lt_phone, App::formatPhone(phone)),
codeLength,
submit,
nullptr,
std::move(call),
std::move(error));
std::move(error),
rpl::never<QString>());
}
object_ptr<BoxContent> VerifyEmailBox(
const QString &email,
int codeLength,
Fn<void(QString code)> submit,
rpl::producer<QString> error) {
Fn<void()> resend,
rpl::producer<QString> error,
rpl::producer<QString> resent) {
return Box<VerifyBox>(
lang(lng_passport_email_title),
lng_passport_confirm_email(lt_email, email),
codeLength,
submit,
resend,
rpl::single(QString()),
std::move(error));
std::move(error),
std::move(resent));
}
} // namespace Passport

View File

@ -83,6 +83,8 @@ object_ptr<BoxContent> VerifyEmailBox(
const QString &email,
int codeLength,
Fn<void(QString code)> submit,
rpl::producer<QString> error);
Fn<void()> resend,
rpl::producer<QString> error,
rpl::producer<QString> resent);
} // namespace Passport

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/special_buttons.h"
#include "boxes/passcode_box.h"
#include "lang/lang_keys.h"
#include "info/profile/info_profile_icon.h"
#include "styles/style_passport.h"
@ -226,29 +227,49 @@ void PanelNoPassword::refreshBottom() {
_inner,
(pattern.isEmpty()
? lang(lng_passport_about_password)
: lng_passport_link_sent(lt_email, pattern)),
: lng_passport_code_sent(lt_email, pattern)),
Ui::FlatLabel::InitType::Simple,
st::passportPasswordSetupLabel)),
st::passportFormAbout2Padding)->entity());
const auto button = _inner->add(
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
_inner,
object_ptr<Ui::RoundButton>(
_inner,
langFactory(pattern.isEmpty()
? lng_passport_password_create
: lng_cancel),
st::defaultBoxButton)));
if (pattern.isEmpty()) {
const auto button = _inner->add(
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
_inner,
object_ptr<Ui::RoundButton>(
_inner,
langFactory(lng_passport_password_create),
st::defaultBoxButton)));
button->entity()->addClickHandler([=] {
_controller->setupPassword();
});
} else {
button->entity()->addClickHandler([=] {
const auto container = _inner->add(
object_ptr<Ui::FixedHeightWidget>(
_inner,
st::defaultBoxButton.height));
const auto cancel = Ui::CreateChild<Ui::RoundButton>(
container,
langFactory(lng_cancel),
st::defaultBoxButton);
cancel->addClickHandler([=] {
_controller->cancelPasswordSubmit();
});
const auto validate = Ui::CreateChild<Ui::RoundButton>(
container,
langFactory(lng_passport_email_validate),
st::defaultBoxButton);
validate->addClickHandler([=] {
_controller->validateRecoveryEmail();
});
container->widthValue(
) | rpl::start_with_next([=](int width) {
const auto both = cancel->width()
+ validate->width()
+ st::boxLittleSkip;
cancel->moveToLeft((width - both) / 2, 0, width);
validate->moveToRight((width - both) / 2, 0, width);
}, container->lifetime());
}
_button.reset(button);
}
} // namespace Passport

View File

@ -67,7 +67,6 @@ private:
not_null<Ui::VerticalLayout*> _inner;
base::unique_qptr<Ui::RpWidget> _about;
base::unique_qptr<Ui::RpWidget> _button;
};

View File

@ -17,6 +17,10 @@ settingsSectionButton: InfoProfileButton(infoProfileButton) {
settingsButton: InfoProfileButton(settingsSectionButton) {
padding: margins(22px, 10px, 22px, 8px);
}
settingsAttentionButton: InfoProfileButton(settingsButton) {
textFg: attentionButtonFg;
textFgOver: attentionButtonFgOver;
}
settingsSectionSkip: 9px;
settingsSectionIconLeft: 22px;
settingsSeparatorPadding: margins(22px, infoProfileSkip, 0px, infoProfileSkip);

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/self_destruction_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "calls/calls_instance.h"
@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "apiwrap.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h"
namespace Settings {
namespace {
@ -242,12 +244,18 @@ void EditCloudPassword() {
current->notEmptyPassport,
current->hint,
current->newSecureSecret));
rpl::merge(
box->newPasswordSet() | rpl::map([] { return rpl::empty_value(); }),
box->passwordReloadNeeded()
) | rpl::start_with_next([=] {
Auth().api().reloadPasswordState();
}, box->lifetime());
box->clearUnconfirmedPassword(
) | rpl::start_with_next([=] {
Auth().api().clearUnconfirmedPassword();
}, box->lifetime());
}
void RemoveCloudPassword() {
@ -266,6 +274,7 @@ void RemoveCloudPassword() {
current->hint,
current->newSecureSecret,
true));
rpl::merge(
box->newPasswordSet(
) | rpl::map([] { return rpl::empty_value(); }),
@ -273,15 +282,21 @@ void RemoveCloudPassword() {
) | rpl::start_with_next([=] {
Auth().api().reloadPasswordState();
}, box->lifetime());
box->clearUnconfirmedPassword(
) | rpl::start_with_next([=] {
Auth().api().clearUnconfirmedPassword();
}, box->lifetime());
}
void SetupCloudPassword(not_null<Ui::VerticalLayout*> container) {
using namespace rpl::mappers;
using State = Core::CloudPasswordState;
AddDivider(container);
AddSkip(container);
AddSubsectionTitle(container, lng_settings_password_title);
using State = Core::CloudPasswordState;
auto has = rpl::single(
false
) | rpl::then(Auth().api().passwordState(
@ -301,7 +316,7 @@ void SetupCloudPassword(not_null<Ui::VerticalLayout*> container) {
) | rpl::filter([](const QString &pattern) {
return !pattern.isEmpty();
}) | rpl::map([](const QString &pattern) {
return lng_cloud_password_waiting(lt_email, pattern);
return lng_cloud_password_waiting_code(lt_email, pattern);
}));
auto unconfirmed = rpl::single(
true
@ -346,7 +361,7 @@ void SetupCloudPassword(not_null<Ui::VerticalLayout*> container) {
container,
std::move(text),
st::settingsButton)));
change->toggleOn(std::move(
change->toggleOn(rpl::duplicate(
unconfirmed
) | rpl::map([](bool unconfirmed) {
return !unconfirmed;
@ -357,6 +372,40 @@ void SetupCloudPassword(not_null<Ui::VerticalLayout*> container) {
}
});
const auto confirm = container->add(
object_ptr<Ui::SlideWrap<Button>>(
container,
object_ptr<Button>(
container,
Lang::Viewer(lng_cloud_password_confirm),
st::settingsButton)));
confirm->toggleOn(rpl::duplicate(
unconfirmed
))->setDuration(0);
confirm->entity()->addClickHandler([] {
const auto state = Auth().api().passwordStateCurrent();
auto validation = ConfirmRecoveryEmail(state->unconfirmedPattern);
std::move(
validation.reloadRequests
) | rpl::start_with_next([] {
Auth().api().reloadPasswordState();
}, validation.box->lifetime());
std::move(
validation.cancelRequests
) | rpl::start_with_next([] {
Auth().api().clearUnconfirmedPassword();
}, validation.box->lifetime());
Ui::show(std::move(validation.box));
});
const auto remove = [] {
if (CheckEditCloudPassword()) {
RemoveCloudPassword();
}
};
const auto disable = container->add(
object_ptr<Ui::SlideWrap<Button>>(
container,
@ -364,12 +413,24 @@ void SetupCloudPassword(not_null<Ui::VerticalLayout*> container) {
container,
Lang::Viewer(lng_settings_password_disable),
st::settingsButton)));
disable->toggleOn(base::duplicate(has));
disable->entity()->addClickHandler([] {
if (CheckEditCloudPassword()) {
RemoveCloudPassword();
}
});
disable->toggleOn(rpl::combine(
rpl::duplicate(has),
rpl::duplicate(unconfirmed),
_1 && !_2));
disable->entity()->addClickHandler(remove);
const auto abort = container->add(
object_ptr<Ui::SlideWrap<Button>>(
container,
object_ptr<Button>(
container,
Lang::Viewer(lng_settings_password_abort),
st::settingsAttentionButton)));
abort->toggleOn(rpl::combine(
rpl::duplicate(has),
rpl::duplicate(unconfirmed),
_1 && _2));
abort->entity()->addClickHandler(remove);
const auto reloadOnActivation = [=](Qt::ApplicationState state) {
if (label->toggled() && state == Qt::ApplicationActive) {