mirror of
https://github.com/vale981/tdesktop
synced 2025-03-05 17:51:41 -05:00
Confirm 2sv recovery email by code.
This commit is contained in:
parent
93678a07a8
commit
be3e43e6cb
15 changed files with 441 additions and 108 deletions
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() };
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ public:
|
|||
|
||||
void setupPassword();
|
||||
void cancelPasswordSubmit();
|
||||
void validateRecoveryEmail();
|
||||
|
||||
bool canAddScan(FileType type) const;
|
||||
void uploadScan(FileType type, QByteArray &&content);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -67,7 +67,6 @@ private:
|
|||
|
||||
not_null<Ui::VerticalLayout*> _inner;
|
||||
base::unique_qptr<Ui::RpWidget> _about;
|
||||
base::unique_qptr<Ui::RpWidget> _button;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue