diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 9149029ae..e9d46d619 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1074,6 +1074,8 @@ PRIVATE
profile/profile_cover_drop_area.h
settings/cloud_password/settings_cloud_password_common.cpp
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_hint.cpp
settings/cloud_password/settings_cloud_password_hint.h
settings/cloud_password/settings_cloud_password_input.cpp
diff --git a/Telegram/Resources/animations/cloud_password/email.tgs b/Telegram/Resources/animations/cloud_password/email.tgs
new file mode 100644
index 000000000..c8aa2e7b3
Binary files /dev/null and b/Telegram/Resources/animations/cloud_password/email.tgs differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 600a48a4c..7228bbccc 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -562,6 +562,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_cloud_password_manage_email_change" = "Change Recovery Email";
"lng_settings_cloud_password_manage_password_change" = "Change Password";
"lng_settings_cloud_password_skip_hint" = "Skip hint";
+"lng_settings_cloud_password_save" = "Save and Finish";
+"lng_settings_cloud_password_email_confirm" = "Confirm and Finish";
"lng_clear_payment_info_title" = "Clear payment info";
"lng_clear_payment_info_sure" = "Are you sure you want to clear your payment and shipping info?";
@@ -705,7 +707,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cloud_password_hint" = "Enter Password Hint";
"lng_cloud_password_change_hint" = "Enter new password hint";
"lng_cloud_password_bad" = "Password and hint cannot be the same.";
-"lng_cloud_password_email" = "Enter recovery email";
+"lng_cloud_password_email" = "Enter Email";
"lng_cloud_password_bad_email" = "Incorrect email, please try other.";
"lng_cloud_password_about" = "This password will be asked when you log in on a new device in addition to the SMS code.";
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nadd a password recovery email?\n\nIf you forget your password, you will\nlose access to your Telegram account.";
diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc
index f8590c6bb..4197f473f 100644
--- a/Telegram/Resources/qrc/telegram/animations.qrc
+++ b/Telegram/Resources/qrc/telegram/animations.qrc
@@ -7,5 +7,6 @@
../../animations/cloud_password/intro.tgs
../../animations/cloud_password/password_input.tgs
../../animations/cloud_password/hint.tgs
+ ../../animations/cloud_password/email.tgs
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
index c1414a27a..7fba2db1e 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.cpp
@@ -7,13 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "settings/cloud_password/settings_cloud_password_common.h"
+#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
+#include "main/main_session.h"
#include "settings/settings_common.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.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"
@@ -223,6 +226,10 @@ not_null AbstractStep::controller() const {
return _controller;
}
+Api::CloudPassword &AbstractStep::cloudPassword() {
+ return _controller->session().api().cloudPassword();
+}
+
void AbstractStep::showBack() {
_showBack.fire({});
}
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
index cd226079d..ded3b1b5b 100644
--- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h
@@ -20,11 +20,18 @@ class RoundButton;
class VerticalLayout;
} // namespace Ui
+namespace Api {
+class CloudPassword;
+} // namespace Api
+
namespace Settings::CloudPassword {
struct StepData {
+ QString currentPassword;
QString password;
QString hint;
+ QString email;
+ int unconfirmedEmailLengthCode;
};
void SetupHeader(
@@ -96,6 +103,7 @@ public:
protected:
[[nodiscard]] not_null controller() const;
+ [[nodiscard]] Api::CloudPassword &cloudPassword();
void showBack();
void showOther(Type type);
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp
new file mode 100644
index 000000000..c225e56df
--- /dev/null
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.cpp
@@ -0,0 +1,159 @@
+/*
+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.h"
+
+#include "api/api_cloud_password.h"
+#include "lang/lang_keys.h"
+#include "settings/cloud_password/settings_cloud_password_common.h"
+#include "ui/boxes/confirm_box.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/input_fields.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 Email : public TypedAbstractStep {
+public:
+ using TypedAbstractStep::TypedAbstractStep;
+
+ [[nodiscard]] rpl::producer title() override;
+ void setupContent();
+
+private:
+ rpl::lifetime _requestLifetime;
+
+};
+
+rpl::producer Email::title() {
+ return tr::lng_settings_cloud_password_email_title();
+}
+
+void Email::setupContent() {
+ const auto content = Ui::CreateChild(this);
+ auto currentStepData = stepData();
+ const auto currentStepDataEmail = base::take(currentStepData.email);
+ setStepData(currentStepData);
+
+ SetupHeader(
+ content,
+ u"cloud_password/email"_q,
+ showFinishes(),
+ tr::lng_settings_cloud_password_email_subtitle(),
+ tr::lng_settings_cloud_password_email_about());
+
+ AddSkip(content, st::settingLocalPasscodeDescriptionBottomSkip);
+
+ const auto wrap = AddWrappedField(
+ content,
+ tr::lng_cloud_password_email(),
+ currentStepDataEmail);
+ const auto newInput = wrap->entity();
+ AddSkipInsteadOfField(content);
+ AddSkipInsteadOfError(content);
+
+ const auto send = [=](Fn close) {
+ Expects(!_requestLifetime);
+
+ const auto data = stepData();
+
+ _requestLifetime = cloudPassword().set(
+ data.currentPassword,
+ data.password,
+ data.hint,
+ !data.email.isEmpty(),
+ data.email
+ ) | rpl::start_with_next_error_done([=](Api::CloudPassword::SetOk d) {
+ _requestLifetime.destroy();
+
+ auto data = stepData();
+ data.unconfirmedEmailLengthCode = d.unconfirmedEmailLengthCode;
+ setStepData(std::move(data));
+ }, [=](const QString &error) {
+ _requestLifetime.destroy();
+
+ }, [=] {
+ _requestLifetime.destroy();
+
+ auto empty = StepData();
+ empty.currentPassword = stepData().password;
+ setStepData(std::move(empty));
+ });
+
+ if (close) {
+ _requestLifetime.add(close);
+ }
+ };
+
+ const auto confirm = [=](const QString &email) {
+ if (_requestLifetime) {
+ return;
+ }
+
+ auto data = stepData();
+ data.email = email;
+ setStepData(std::move(data));
+
+ if (!email.isEmpty()) {
+ send(nullptr);
+ return;
+ }
+
+ controller()->show(Ui::MakeConfirmBox({
+ .text = { tr::lng_cloud_password_about_recover() },
+ .confirmed = crl::guard(this, send),
+ .confirmText = tr::lng_cloud_password_skip_email(),
+ .confirmStyle = &st::attentionBoxButton,
+ }));
+ };
+
+ const auto skip = Ui::CreateChild(
+ this,
+ tr::lng_cloud_password_skip_email(tr::now));
+ wrap->geometryValue(
+ ) | rpl::start_with_next([=](QRect r) {
+ r.translate(wrap->entity()->pos().x(), 0);
+ skip->moveToLeft(r.x(), r.y() + r.height() + st::passcodeTextLine);
+ }, skip->lifetime());
+ skip->setClickedCallback([=] {
+ confirm(QString());
+ });
+
+ const auto button = AddDoneButton(
+ content,
+ tr::lng_settings_cloud_password_save());
+ button->setClickedCallback([=] {
+ const auto newText = newInput->getLastText();
+ if (newText.isEmpty()) {
+ newInput->setFocus();
+ newInput->showError();
+ } else {
+ confirm(newText);
+ }
+ });
+
+ const auto submit = [=] { button->clicked({}, Qt::LeftButton); };
+ QObject::connect(newInput, &Ui::InputField::submitted, submit);
+
+ setFocusCallback([=] { newInput->setFocus(); });
+
+ Ui::ResizeFitChild(this, content);
+}
+
+} // namespace CloudPassword
+
+Type CloudPasswordEmailId() {
+ return CloudPassword::Email::Id();
+}
+
+} // namespace Settings
diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.h
new file mode 100644
index 000000000..c018138a9
--- /dev/null
+++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_email.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 CloudPasswordEmailId();
+
+} // namespace Settings
+