diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 1eb0d76e2..3be5f9145 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1076,6 +1076,8 @@ PRIVATE settings/cloud_password/settings_cloud_password_common.h settings/cloud_password/settings_cloud_password_email.cpp settings/cloud_password/settings_cloud_password_email.h + settings/cloud_password/settings_cloud_password_email_confirm.cpp + settings/cloud_password/settings_cloud_password_email_confirm.h settings/cloud_password/settings_cloud_password_hint.cpp settings/cloud_password/settings_cloud_password_hint.h settings/cloud_password/settings_cloud_password_input.cpp diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 80e569939..f3150f277 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "info/info_memento.h" #include "info/info_top_bar.h" +#include "settings/cloud_password/settings_cloud_password_email_confirm.h" #include "settings/settings_chat.h" #include "settings/settings_main.h" #include "ui/widgets/discrete_sliders.h" @@ -395,7 +396,9 @@ void WrapWidget::createTopBar() { addTopBarMenuButton(); addProfileCallsButton(); } else if (section.type() == Section::Type::Settings - && (section.settingsType() == ::Settings::Main::Id() + && (section.settingsType() + == ::Settings::CloudPasswordEmailConfirmId() + || section.settingsType() == ::Settings::Main::Id() || section.settingsType() == ::Settings::Chat::Id())) { addTopBarMenuButton(); } else if (section.type() == Section::Type::Downloads) { diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp index 432e537dc..80565d969 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #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_confirm.h" #include "settings/cloud_password/settings_cloud_password_manage.h" #include "ui/boxes/confirm_box.h" #include "ui/widgets/buttons.h" @@ -80,6 +81,7 @@ void Email::setupContent() { auto data = stepData(); data.unconfirmedEmailLengthCode = d.unconfirmedEmailLengthCode; setStepData(std::move(data)); + showOther(CloudPasswordEmailConfirmId()); }, [=](const QString &error) { _requestLifetime.destroy(); diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp new file mode 100644 index 000000000..6aab3a73c --- /dev/null +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.cpp @@ -0,0 +1,220 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "settings/cloud_password/settings_cloud_password_email_confirm.h" + +#include "api/api_cloud_password.h" +#include "core/core_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_hint.h" +#include "settings/cloud_password/settings_cloud_password_input.h" +#include "settings/cloud_password/settings_cloud_password_manage.h" +#include "settings/cloud_password/settings_cloud_password_start.h" +#include "ui/boxes/confirm_box.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/sent_code_field.h" +#include "ui/wrap/padding_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "window/window_session_controller.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" +#include "styles/style_settings.h" + +namespace Settings { +namespace CloudPassword { + +class EmailConfirm : public TypedAbstractStep { +public: + using TypedAbstractStep::TypedAbstractStep; + + [[nodiscard]] rpl::producer title() override; + void setupContent(); + + [[nodiscard]] rpl::producer> removeFromStack() override; + +private: + rpl::lifetime _requestLifetime; + +}; + +rpl::producer EmailConfirm::title() { + return tr::lng_settings_cloud_password_email_title(); +} + +rpl::producer> EmailConfirm::removeFromStack() { + return rpl::single(std::vector{ + CloudPasswordStartId(), + CloudPasswordInputId(), + CloudPasswordHintId(), + CloudPasswordEmailId(), + CloudPasswordEmailConfirmId(), + CloudPasswordManageId(), + }); +} + +void EmailConfirm::setupContent() { + const auto content = Ui::CreateChild(this); + auto currentStepData = stepData(); + const auto currentStepDataCodeLength = base::take( + currentStepData.unconfirmedEmailLengthCode); + // If we go back from Email Confirm to Privacy Settings + // we should forget the current password. + const auto currentPassword = base::take(currentStepData.currentPassword); + const auto typedPassword = base::take(currentStepData.password); + setStepData(currentStepData); + + const auto state = cloudPassword().stateCurrent(); + if (!state) { + setStepData(StepData()); + showBack(); + return; + } + cloudPassword().state( + ) | rpl::start_with_next([=](const Core::CloudPasswordState &state) { + if (!_requestLifetime && state.unconfirmedPattern.isEmpty()) { + setStepData(StepData()); + showBack(); + } + }, lifetime()); + + SetupHeader( + content, + u"cloud_password/email"_q, + showFinishes(), + tr::lng_cloud_password_confirm(), + rpl::single( + tr::lng_cloud_password_waiting_code( + tr::now, + lt_email, + state->unconfirmedPattern))); + + AddSkip(content, st::settingLocalPasscodeDescriptionBottomSkip); + + const auto wrap = content->add( + object_ptr>( + content, + object_ptr( + content, + st::settingLocalPasscodeInputField, + tr::lng_change_phone_code_title()))); + const auto newInput = wrap->entity(); + + const auto error = AddError(content, nullptr); + QObject::connect(newInput, &Ui::InputField::changed, [=] { + error->hide(); + }); + AddSkipInsteadOfField(content); + + const auto resendInfo = Ui::CreateChild( + error->parentWidget(), + tr::lng_cloud_password_resent(tr::now), + st::changePhoneLabel); + resendInfo->hide(); + error->geometryValue( + ) | rpl::start_with_next([=](const QRect &r) { + resendInfo->setGeometry(r); + }, resendInfo->lifetime()); + error->shownValue( + ) | rpl::start_with_next([=](bool shown) { + if (shown) { + resendInfo->hide(); + } + }, resendInfo->lifetime()); + + const auto resend = Ui::CreateChild( + this, + tr::lng_cloud_password_resend(tr::now)); + wrap->geometryValue( + ) | rpl::start_with_next([=](QRect r) { + r.translate(wrap->entity()->pos().x(), 0); + resend->moveToLeft(r.x(), r.y() + r.height() + st::passcodeTextLine); + }, resend->lifetime()); + resend->setClickedCallback([=] { + if (_requestLifetime) { + return; + } + _requestLifetime = cloudPassword().resendEmailCode( + ) | rpl::start_with_error_done([=](const QString &type) { + _requestLifetime.destroy(); + + error->show(); + error->setText(Lang::Hard::ServerError()); + }, [=] { + _requestLifetime.destroy(); + + error->hide(); + resendInfo->show(); + newInput->hideError(); + }); + }); + + const auto button = AddDoneButton( + content, + tr::lng_settings_cloud_password_email_confirm()); + button->setClickedCallback([=] { + const auto newText = newInput->getDigitsOnly(); + if (newText.isEmpty()) { + newInput->setFocus(); + newInput->showError(); + } else if (!_requestLifetime) { + _requestLifetime = cloudPassword().confirmEmail( + 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)); + } else if (type == u"CODE_INVALID"_q) { + error->setText(tr::lng_signin_wrong_code(tr::now)); + } else if (type == u"EMAIL_HASH_EXPIRED"_q) { + // Show box? + error->setText(Lang::Hard::EmailConfirmationExpired()); + } else { + error->setText(Lang::Hard::ServerError()); + } + }, [=] { + _requestLifetime.destroy(); + + auto empty = StepData(); + const auto anyPassword = currentPassword.isEmpty() + ? typedPassword + : currentPassword; + empty.currentPassword = anyPassword; + setStepData(std::move(empty)); + // If we don't have the current password + // Then we should go to Privacy Settings. + if (anyPassword.isEmpty()) { + showBack(); + } else { + showOther(CloudPasswordManageId()); + } + }); + } + }); + + const auto submit = [=] { button->clicked({}, Qt::LeftButton); }; + newInput->setAutoSubmit(currentStepDataCodeLength, submit); + QObject::connect(newInput, &Ui::InputField::submitted, submit); + + setFocusCallback([=] { newInput->setFocus(); }); + + Ui::ResizeFitChild(this, content); +} + +} // namespace CloudPassword + +Type CloudPasswordEmailConfirmId() { + return CloudPassword::EmailConfirm::Id(); +} + +} // namespace Settings diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.h new file mode 100644 index 000000000..aee154192 --- /dev/null +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email_confirm.h @@ -0,0 +1,17 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "settings/settings_type.h" + +namespace Settings { + +Type CloudPasswordEmailConfirmId(); + +} // namespace Settings + diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp index 7e950eb08..984cd1258 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_manage.cpp @@ -12,6 +12,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_email.h" #include "settings/cloud_password/settings_cloud_password_hint.h" #include "settings/cloud_password/settings_cloud_password_input.h" @@ -102,6 +103,7 @@ rpl::producer> Manage::removeFromStack() { CloudPasswordInputId(), CloudPasswordHintId(), CloudPasswordEmailId(), + CloudPasswordEmailConfirmId(), CloudPasswordManageId(), }); } diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp index 220bf526a..7ec5a5b21 100644 --- a/Telegram/SourceFiles/settings/settings_common.cpp +++ b/Telegram/SourceFiles/settings/settings_common.cpp @@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_common.h" +#include "apiwrap.h" +#include "api/api_cloud_password.h" +#include "settings/cloud_password/settings_cloud_password_email_confirm.h" #include "settings/settings_chat.h" #include "settings/settings_advanced.h" #include "settings/settings_information.h" @@ -17,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_calls.h" #include "settings/settings_experimental.h" #include "core/application.h" +#include "core/core_cloud_password.h" #include "ui/wrap/padding_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/widgets/labels.h" @@ -296,6 +300,17 @@ void FillMenu( tr::lng_settings_bg_theme_create(tr::now), [=] { window->show(Box(Window::Theme::CreateBox, window)); }, &st::menuIconChangeColors); + } else if (type == CloudPasswordEmailConfirmId()) { + const auto api = &controller->session().api(); + if (const auto state = api->cloudPassword().stateCurrent()) { + if (state->unconfirmedPattern.isEmpty()) { + return; + } + } + addAction( + tr::lng_settings_password_abort(tr::now), + [=] { api->cloudPassword().clearUnconfirmedPassword(); }, + &st::menuIconCancel); } else { const auto &list = Core::App().domain().accounts(); if (list.size() < ::Main::Domain::kMaxAccounts) {