diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6df699362f..5b6ab61acf 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b06445d133..50dbf919e3 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -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(); diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h index 8bc9d5d35e..6e07fc9a8e 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.h +++ b/Telegram/SourceFiles/boxes/abstract_box.h @@ -118,12 +118,15 @@ public: _boxClosingStream.fire({}); } - void setDelegate(BoxContentDelegate *newDelegate) { + void setDelegate(not_null newDelegate) { _delegate = newDelegate; _preparing = true; prepare(); finishPrepare(); } + not_null 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 getDelegate() const { - return _delegate; - } - private slots: void onScroll(); void onInnerResize(); diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 637c1aeb11..d0d70a9100 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -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(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>(); + const auto resent = std::make_shared>(); + const auto set = std::make_shared(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( + 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>(); + const auto resent = std::make_shared>(); + const auto requestId = std::make_shared(0); + const auto weak = std::make_shared>(); + const auto reloads = std::make_shared>(); + const auto cancels = std::make_shared>(); + + const auto submit = [=](QString code) { + if (*requestId) { + return; + } + const auto done = [=](const MTPBool &result) { + *requestId = 0; + reloads->fire({}); + if (*weak) { + (*weak)->getDelegate()->show( + Box(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( + 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() }; +} diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 918b459b5c..ef4381247b 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -32,6 +32,7 @@ public: rpl::producer 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 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 _newPasswordSet; rpl::event_stream<> _passwordReloadNeeded; + rpl::event_stream<> _clearUnconfirmedPassword; }; @@ -173,3 +177,10 @@ private: rpl::event_stream<> _recoveryExpired; }; + +struct RecoveryEmailValidation { + object_ptr box; + rpl::producer<> reloadRequests; + rpl::producer<> cancelRequests; +}; +RecoveryEmailValidation ConfirmRecoveryEmail(const QString &pattern); diff --git a/Telegram/SourceFiles/lang/lang_hardcoded.h b/Telegram/SourceFiles/lang/lang_hardcoded.h index 7ca7ebcc5d..07b4bd9dc3 100644 --- a/Telegram/SourceFiles/lang/lang_hardcoded.h +++ b/Telegram/SourceFiles/lang/lang_hardcoded.h @@ -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 diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 0d5140b9bf..58d67e5002 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -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(); diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 5518ddb62e..8339130c6c 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -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 field) { return field->verification.error; - }) | rpl::distinct_until_changed())); + }) | rpl::distinct_until_changed(), + + rpl::never())); } else { Unexpected("Type in processVerificationNeeded."); } diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index 92e7a6c9f5..7f0e2ce415 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -95,6 +95,7 @@ public: void setupPassword(); void cancelPasswordSubmit(); + void validateRecoveryEmail(); bool canAddScan(FileType type) const; void uploadScan(FileType type, QByteArray &&content); diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp index 9794ed41b5..3c9be95cb4 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp @@ -35,8 +35,10 @@ public: const QString &text, int codeLength, Fn submit, + Fn resend, rpl::producer call, - rpl::producer error); + rpl::producer error, + rpl::producer resent); void setInnerFocus() override; @@ -48,13 +50,15 @@ private: const QString &text, int codeLength, Fn submit, + Fn resend, rpl::producer call, - rpl::producer error); + rpl::producer error, + rpl::producer resent); QString _title; Fn _submit; QPointer _code; - int _height = 0; + QPointer _content; }; @@ -64,44 +68,94 @@ VerifyBox::VerifyBox( const QString &text, int codeLength, Fn submit, + Fn resend, rpl::producer call, - rpl::producer error) + rpl::producer error, + rpl::producer 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 submit, + Fn resend, rpl::producer call, - rpl::producer error) { - const auto description = Ui::CreateChild( - this, - text, - Ui::FlatLabel::InitType::Simple, - st::boxLabel); - _code = Ui::CreateChild( - this, - st::defaultInputField, - langFactory(lng_change_phone_code_title)); + rpl::producer error, + rpl::producer resent) { + _content = Ui::CreateChild(this); - const auto problem = Ui::CreateChild>( - this, + const auto small = style::margins( + st::boxPadding.left(), + 0, + st::boxPadding.right(), + st::boxPadding.bottom()); + const auto description = _content->add( object_ptr( - this, - QString(), + _content, + text, Ui::FlatLabel::InitType::Simple, - st::passportVerifyErrorLabel)); - const auto waiter = Ui::CreateChild( - this, - std::move(call), - st::boxDividerLabel); + st::boxLabel), + small); + _code = _content->add( + object_ptr( + _content, + st::defaultInputField, + langFactory(lng_change_phone_code_title)), + small); + + const auto problem = _content->add( + object_ptr>( + _content, + object_ptr( + _content, + QString(), + Ui::FlatLabel::InitType::Simple, + st::passportVerifyErrorLabel)), + small); + const auto waiter = _content->add( + object_ptr( + _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( + _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 VerifyPhoneBox( lng_passport_confirm_phone(lt_phone, App::formatPhone(phone)), codeLength, submit, + nullptr, std::move(call), - std::move(error)); + std::move(error), + rpl::never()); } object_ptr VerifyEmailBox( const QString &email, int codeLength, Fn submit, - rpl::producer error) { + Fn resend, + rpl::producer error, + rpl::producer resent) { return Box( 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 diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h index e5658b4ec0..d1f8dc46af 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h @@ -83,6 +83,8 @@ object_ptr VerifyEmailBox( const QString &email, int codeLength, Fn submit, - rpl::producer error); + Fn resend, + rpl::producer error, + rpl::producer resent); } // namespace Passport diff --git a/Telegram/SourceFiles/passport/passport_panel_password.cpp b/Telegram/SourceFiles/passport/passport_panel_password.cpp index 3cc5958e58..2484bdb3fc 100644 --- a/Telegram/SourceFiles/passport/passport_panel_password.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_password.cpp @@ -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>( - _inner, - object_ptr( - _inner, - langFactory(pattern.isEmpty() - ? lng_passport_password_create - : lng_cancel), - st::defaultBoxButton))); if (pattern.isEmpty()) { + const auto button = _inner->add( + object_ptr>( + _inner, + object_ptr( + _inner, + langFactory(lng_passport_password_create), + st::defaultBoxButton))); button->entity()->addClickHandler([=] { _controller->setupPassword(); }); } else { - button->entity()->addClickHandler([=] { + const auto container = _inner->add( + object_ptr( + _inner, + st::defaultBoxButton.height)); + const auto cancel = Ui::CreateChild( + container, + langFactory(lng_cancel), + st::defaultBoxButton); + cancel->addClickHandler([=] { _controller->cancelPasswordSubmit(); }); + const auto validate = Ui::CreateChild( + 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 diff --git a/Telegram/SourceFiles/passport/passport_panel_password.h b/Telegram/SourceFiles/passport/passport_panel_password.h index 17566181b7..1de899762b 100644 --- a/Telegram/SourceFiles/passport/passport_panel_password.h +++ b/Telegram/SourceFiles/passport/passport_panel_password.h @@ -67,7 +67,6 @@ private: not_null _inner; base::unique_qptr _about; - base::unique_qptr _button; }; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 319550244b..1e7948c88d 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -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); diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 07cc747086..86dce2b29d 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -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 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 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 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 container) { } }); + const auto confirm = container->add( + object_ptr>( + container, + object_ptr